Source code for energia.components.commodities.commodity

"""Commodity Base Class"""

from __future__ import annotations

from collections.abc import Mapping
from typing import TYPE_CHECKING, Self

from ..._core._component import _Component
from ...modeling.parameters.conversion import Conversion

if TYPE_CHECKING:
    from ..measure.unit import Unit


[docs] class Commodity(_Component, Mapping): """ A commodity, can be a material, chemical, energy, etc. :param label: Label of the commodity, used for plotting. Defaults to None. :type label: str, optional :param name: Name of the commodity, used for indexing. Defaults to None. :type name: str, optional :param basis: Unit basis of the commodity. Defaults to None. :type basis: Unit, optional :ivar model: The model to which the component belongs. :vartype model: Model :ivar name: Set when the component is assigned as a Model attribute. :vartype name: str :ivar constraints: List of constraints associated with the component. :vartype constraints: list[str] :ivar domains: List of domains associated with the component. :vartype domains: list[Domain] :ivar aspects: Aspects associated with the component with domains. :vartype aspects: dict[Aspect, list[Domain]] :ivar conversions: List of conversions associated with the commodity. Defaults to []. :vartype conversions: list[Conversion] :ivar insitu: If the commodity only exists insitu, i.e., does not scale any domains :vartype insitu: bool, optional """ def __init__( self, basis: Unit | None = None, label: str = "", citations: str = "", **kwargs ): self.basis = basis _Component.__init__(self, label=label, citations=citations, **kwargs) # list of conversions associated with the commodity self.conversions: list[Conversion] = [] # flag indicates whether the commodity is produced and expended insitu only self.insitu = False @property def balance(self) -> dict[Conversion | Self, int | float]: """Conversion""" # if no conversion is set, return 1.0 if not self.conversions: return {self: 1.0} # if there is a conversion, return its parameter in all tasks its # associated with # TODO: mute when ready to set conversions on commodity # return {task: task.balance[self] for task in self.conversions} # TODO: mute when ready to set conversions on commodity # def __setattr__(self, name, value): # if isinstance(value, Conversion): # # update conversions # self.conversions.append(value) # super().__setattr__(name, value) def __mul__(self, other: int | float) -> Conversion: # multiplying a number with a resources gives conversion # math operations with conversions form the balance in tasks return Conversion.from_balance({self: other}) def __rmul__(self, other: int | float) -> Conversion: # reverse multiplication return self * other def __add__(self, other: Conversion | Commodity) -> Conversion: return Conversion.from_balance({self: 1, **other}) def __neg__(self) -> Conversion: # just multiply by -1 return self * -1 def __sub__(self, other: Conversion | Self): if isinstance(other, Commodity): # if another resource is subtracted # give it the parameter -1 return self + -1 * other def _negate(par): if isinstance(par, (int, float)): return -1 * par return [-i for i in par] if isinstance(other, Conversion): # if another conversion is subtracted, update the balance return Conversion.from_balance( { self: 1, **{res: _negate(par) for res, par in other.items()}, } ) def __truediv__(self, other: int | float): # treat division as multiplication by the inverse return self * (1 / other) def __eq__(self, other: Conversion | Self): if isinstance(other, Conversion): conv = self + other # set itself as base conv.resource = self return conv if isinstance(other, int | float): conv = Conversion(resource=self, hold=other) return conv return super().__eq__(other) def __getitem__(self, _): return 1 def __iter__(self): return iter([self]) def __len__(self): return 1