Source code for energia.components.spatial.linkage

"""Linkage links Locations through Transits"""

from __future__ import annotations

from functools import cached_property
from operator import is_
from typing import TYPE_CHECKING, Self

from gana import I

from ..._core._x import _X

if TYPE_CHECKING:
    from ...dimensions.space import Space
    from .location import Location


[docs] class Linkage(_X): """ Linkage between two Locations. :param name: Name of the Linkage. Defaults to None. :type name: str, optional :param label: Label of the Linkage. Defaults to None. :type label: str, optional :param source: Source Location. :type source: Loc :param sink: Sink Location. :type sink: Loc :param dist: Distance between the two Locations. :type dist: float | Unit :param bi: Is the Linkage bidirectional? Defaults to False. :type bi: bool, optional :param auto: Is the Linkage automatically generated? Defaults to False. :type auto: bool, optional :ivar model: Model to which the Linkage belongs. :vartype model: Model :ivar space: Space to which the Linkage belongs. :vartype space: Space :ivar network: Network to which the Linkage belongs. :vartype network: Loc """ def __init__( self, source: Location, sink: Location, dist: float | None = None, bi: bool = False, auto: bool = False, label: str = "", citations: str = "", ): if is_(source, sink): # if the source and sink are the same, throw error raise ValueError(f"source and sink can't both be {source}") self.source = source self.sink = sink self.dist = dist self.bi = bi self.auto = auto _X.__init__(self, label=label, citations=citations) self.sib: Self | None = None self.hierarchy = 1 # default multiplier self.multiplier = dist or 1.0
[docs] @cached_property def space(self) -> Space: """Space to which the Location belongs""" return self.model.space
@property def network(self) -> Location: """Network to which the Location belongs""" return self.model.network @property def isnetwork(self) -> bool: """Is this the network of the model? Linkage can never be the network """ return False @property def isin(self) -> Location | None: """Location to which the Linkage belongs""" if self.source.isin: if self.source.isin == self.sink.isin: self.source.isin.has += (self,) return self.source.isin self.network.has += (self,) return self.network
[docs] def rev(self): """Reversed Link""" # locations are all uni directional # if bi is True, create another link for the other direction # and set self.bi to False if self.bi: self.bi = False return -self
def __neg__(self): # creates a new Link in the opposite direction if self.sib is not None: return self.sib if self.label: label = self.label + "(-)" self.label = self.label + "(+)" else: label = None _link = Linkage( source=self.sink, sink=self.source, dist=self.dist, label=label, ) if self.auto: _link.name = f"{self.sink.name}-{self.source.name}" else: _link.name = "-" + self.name _link.sib, self.sib = self, _link return _link def __eq__(self, other: int | float | Self) -> Self | bool: """Sets the distance of a linkage""" # sets the distance of a linkage # each linkage has a unique distance if isinstance(other, (int, float)): self.dist = other self.model = self.source.model if not self.name: self.name = rf"{self.source.name}_{self.sink.name}" setattr(self.model, self.name, self) return self if is_(self, other): return True return False
[docs] @cached_property def I(self) -> I: """gana index set (I)""" _index = I(self.name, tag=self.label or "") setattr(self.program, self.name, _index) return _index
[docs] def update_hierarchy(self, hierarchy: int = 0): """Updates the hierarchy of the locations""" self.hierarchy = hierarchy