Skip to content

odys.domain.entities.base

Base classes for all energy entities: EnergyEntity (shared name field and frozen Pydantic configuration) and Asset (entities the user owns, the only ones allowed in an AssetPortfolio).

Base classes for energy system entities.

This module defines EnergyEntity, the base of every named thing in the energy system, and Asset, the base of entities the user owns and operates.

Asset

Bases: EnergyEntity, ABC

Base class for entities the user owns and operates.

Only assets can be placed in an AssetPortfolio. Markets are entities but not assets, and are passed to EnergySystem separately.

Source code in src/odys/domain/entities/base.py
77
78
79
80
81
82
class Asset(EnergyEntity, ABC):
    """Base class for entities the user owns and operates.

    Only assets can be placed in an `AssetPortfolio`. Markets are entities
    but not assets, and are passed to `EnergySystem` separately.
    """

EnergyEntity

Bases: BaseModel, ABC

Base class for energy system entities.

An entity is any named thing in the energy system: assets the user owns, and external counterparts such as energy markets.

The capability queries (max_supply, min_demand, max_energy_supply and validate_horizon) let system-level validation treat every entity alike. Each defaults to "none"; entity types that supply power, demand power or depend on the horizon override them.

Source code in src/odys/domain/entities/base.py
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
class EnergyEntity(BaseModel, ABC):  # pyright: ignore[reportUnsafeMultipleInheritance]
    """Base class for energy system entities.

    An entity is any named thing in the energy system: assets the user owns,
    and external counterparts such as energy markets.

    The capability queries (`max_supply`, `min_demand`, `max_energy_supply`
    and `validate_horizon`) let system-level validation treat every entity
    alike. Each defaults to "none"; entity types that supply power, demand
    power or depend on the horizon override them.
    """

    model_config = ConfigDict(
        frozen=True,
        extra="forbid",
    )

    name: str

    def max_supply(self, conditions: OperatingConditions) -> tuple[float, ...]:
        """Return the most power this entity can inject at each timestep, in MW.

        Args:
            conditions: The horizon and this entity's profile values in one scenario.

        Returns:
            One value per timestep. The default is zero.
        """
        return (0.0,) * conditions.horizon.number_of_steps

    def min_demand(self, conditions: OperatingConditions) -> tuple[float, ...]:
        """Return the least power this entity must withdraw at each timestep, in MW.

        Args:
            conditions: The horizon and this entity's profile values in one scenario.

        Returns:
            One value per timestep. The default is zero.
        """
        return (0.0,) * conditions.horizon.number_of_steps

    def max_energy_supply(self, conditions: OperatingConditions) -> float:
        """Return the most energy this entity can inject over the horizon, in MWh.

        Args:
            conditions: The horizon and this entity's profile values in one scenario.

        Returns:
            The default is `max_supply` summed over the horizon, times the step length in hours.
        """
        return sum(self.max_supply(conditions)) * conditions.horizon.hours_per_step

    def validate_horizon(self, horizon: Horizon) -> None:
        """Check that this entity fits the optimization horizon. The default accepts any horizon.

        Args:
            horizon: The time grid of the optimization.

        Raises:
            OdysValidationError: If the entity does not fit the horizon.
        """

max_energy_supply(conditions)

Return the most energy this entity can inject over the horizon, in MWh.

Parameters:

Name Type Description Default
conditions OperatingConditions

The horizon and this entity's profile values in one scenario.

required

Returns:

Type Description
float

The default is max_supply summed over the horizon, times the step length in hours.

Source code in src/odys/domain/entities/base.py
55
56
57
58
59
60
61
62
63
64
def max_energy_supply(self, conditions: OperatingConditions) -> float:
    """Return the most energy this entity can inject over the horizon, in MWh.

    Args:
        conditions: The horizon and this entity's profile values in one scenario.

    Returns:
        The default is `max_supply` summed over the horizon, times the step length in hours.
    """
    return sum(self.max_supply(conditions)) * conditions.horizon.hours_per_step

max_supply(conditions)

Return the most power this entity can inject at each timestep, in MW.

Parameters:

Name Type Description Default
conditions OperatingConditions

The horizon and this entity's profile values in one scenario.

required

Returns:

Type Description
tuple[float, ...]

One value per timestep. The default is zero.

Source code in src/odys/domain/entities/base.py
33
34
35
36
37
38
39
40
41
42
def max_supply(self, conditions: OperatingConditions) -> tuple[float, ...]:
    """Return the most power this entity can inject at each timestep, in MW.

    Args:
        conditions: The horizon and this entity's profile values in one scenario.

    Returns:
        One value per timestep. The default is zero.
    """
    return (0.0,) * conditions.horizon.number_of_steps

min_demand(conditions)

Return the least power this entity must withdraw at each timestep, in MW.

Parameters:

Name Type Description Default
conditions OperatingConditions

The horizon and this entity's profile values in one scenario.

required

Returns:

Type Description
tuple[float, ...]

One value per timestep. The default is zero.

Source code in src/odys/domain/entities/base.py
44
45
46
47
48
49
50
51
52
53
def min_demand(self, conditions: OperatingConditions) -> tuple[float, ...]:
    """Return the least power this entity must withdraw at each timestep, in MW.

    Args:
        conditions: The horizon and this entity's profile values in one scenario.

    Returns:
        One value per timestep. The default is zero.
    """
    return (0.0,) * conditions.horizon.number_of_steps

validate_horizon(horizon)

Check that this entity fits the optimization horizon. The default accepts any horizon.

Parameters:

Name Type Description Default
horizon Horizon

The time grid of the optimization.

required

Raises:

Type Description
OdysValidationError

If the entity does not fit the horizon.

Source code in src/odys/domain/entities/base.py
66
67
68
69
70
71
72
73
74
def validate_horizon(self, horizon: Horizon) -> None:
    """Check that this entity fits the optimization horizon. The default accepts any horizon.

    Args:
        horizon: The time grid of the optimization.

    Raises:
        OdysValidationError: If the entity does not fit the horizon.
    """