Skip to content

odys.optimization.problem

Internal implementation detail. OptimizationProblem is the input of the model builder: the context, one formulation per entity type present and one per objective term. OptimizationProblem.assemble() builds the formulations, and EnergySystem.build_problem() calls it.

Everything needed to build the optimization model of one energy system.

OptimizationProblem

Bases: BaseModel

The input of the model builder for one energy system.

It holds the shared indexing, one formulation per entity type present in the system, and one formulation per objective term. Formulations keep the variables of the model they are built into, so a problem is built into one model only; call EnergySystem.build_problem() again for another model.

Source code in src/odys/optimization/problem.py
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
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
class OptimizationProblem(BaseModel):
    """The input of the model builder for one energy system.

    It holds the shared indexing, one formulation per entity type present in the
    system, and one formulation per objective term.
    Formulations keep the variables of the model they are built into, so a
    problem is built into one model only; call `EnergySystem.build_problem()`
    again for another model.
    """

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

    context: ModelContext
    formulations: tuple[Formulation, ...]
    objective_terms: tuple[ObjectiveTermFormulation, ...]

    @classmethod
    def assemble(
        cls,
        entities: Sequence[EnergyEntity],
        horizon: Horizon,
        scenario_set: ScenarioSet,
        condition_set: ConditionSet,
        objective: Objective,
    ) -> Self:
        """Build the shared indexing and the formulations of the entity types present and of the objective's terms.

        Entity formulations are built in `FORMULATIONS` order, each seeing the ones
        built before it; objective terms in `OBJECTIVE_TERM_FORMULATIONS` order.

        Args:
            entities: Every asset and market of the system.
            horizon: The time grid of the optimization.
            scenario_set: The scenarios of the problem.
            condition_set: The initial and final conditions of the run.
            objective: The objective to maximize.

        Returns:
            The problem, ready for the model builder.
        """
        context = ModelContext(horizon=horizon, scenario_set=scenario_set, condition_set=condition_set)
        all_entities = tuple(entities)
        formulations: tuple[Formulation, ...] = ()
        for formulation_type in FORMULATIONS:
            inputs = FormulationInputs(entities=all_entities, context=context, built=formulations)
            formulation = formulation_type.build(inputs)
            if formulation is not None:
                formulations = (*formulations, formulation)
        term_inputs = ObjectiveTermInputs(objective=objective, context=context, formulations=formulations)
        objective_terms = tuple(
            term for term_type in OBJECTIVE_TERM_FORMULATIONS if (term := term_type.build(term_inputs)) is not None
        )
        return cls(context=context, formulations=formulations, objective_terms=objective_terms)

    def formulation_of(self, kind: type[FormulationT]) -> FormulationT | None:
        """Return the formulation of the given type, if the system has entities of that type.

        Args:
            kind: The formulation type, such as `FlexibleLoadFormulation`.

        Returns:
            The formulation, or None if the system has no entity of that type.

        """
        return next((formulation for formulation in self.formulations if isinstance(formulation, kind)), None)

    def dispatches(self, solution: xr.Dataset) -> tuple[Dispatch, ...]:
        """Return the dispatch results of every entity type that has them, read from the solution of the model.

        Args:
            solution: The solution of the model built from this problem, by linopy variable name.
        """
        return tuple(
            dispatch for formulation in self.formulations if (dispatch := formulation.dispatch(solution)) is not None
        )

assemble(entities, horizon, scenario_set, condition_set, objective) classmethod

Build the shared indexing and the formulations of the entity types present and of the objective's terms.

Entity formulations are built in FORMULATIONS order, each seeing the ones built before it; objective terms in OBJECTIVE_TERM_FORMULATIONS order.

Parameters:

Name Type Description Default
entities Sequence[EnergyEntity]

Every asset and market of the system.

required
horizon Horizon

The time grid of the optimization.

required
scenario_set ScenarioSet

The scenarios of the problem.

required
condition_set ConditionSet

The initial and final conditions of the run.

required
objective Objective

The objective to maximize.

required

Returns:

Type Description
Self

The problem, ready for the model builder.

Source code in src/odys/optimization/problem.py
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
75
76
@classmethod
def assemble(
    cls,
    entities: Sequence[EnergyEntity],
    horizon: Horizon,
    scenario_set: ScenarioSet,
    condition_set: ConditionSet,
    objective: Objective,
) -> Self:
    """Build the shared indexing and the formulations of the entity types present and of the objective's terms.

    Entity formulations are built in `FORMULATIONS` order, each seeing the ones
    built before it; objective terms in `OBJECTIVE_TERM_FORMULATIONS` order.

    Args:
        entities: Every asset and market of the system.
        horizon: The time grid of the optimization.
        scenario_set: The scenarios of the problem.
        condition_set: The initial and final conditions of the run.
        objective: The objective to maximize.

    Returns:
        The problem, ready for the model builder.
    """
    context = ModelContext(horizon=horizon, scenario_set=scenario_set, condition_set=condition_set)
    all_entities = tuple(entities)
    formulations: tuple[Formulation, ...] = ()
    for formulation_type in FORMULATIONS:
        inputs = FormulationInputs(entities=all_entities, context=context, built=formulations)
        formulation = formulation_type.build(inputs)
        if formulation is not None:
            formulations = (*formulations, formulation)
    term_inputs = ObjectiveTermInputs(objective=objective, context=context, formulations=formulations)
    objective_terms = tuple(
        term for term_type in OBJECTIVE_TERM_FORMULATIONS if (term := term_type.build(term_inputs)) is not None
    )
    return cls(context=context, formulations=formulations, objective_terms=objective_terms)

dispatches(solution)

Return the dispatch results of every entity type that has them, read from the solution of the model.

Parameters:

Name Type Description Default
solution Dataset

The solution of the model built from this problem, by linopy variable name.

required
Source code in src/odys/optimization/problem.py
90
91
92
93
94
95
96
97
98
def dispatches(self, solution: xr.Dataset) -> tuple[Dispatch, ...]:
    """Return the dispatch results of every entity type that has them, read from the solution of the model.

    Args:
        solution: The solution of the model built from this problem, by linopy variable name.
    """
    return tuple(
        dispatch for formulation in self.formulations if (dispatch := formulation.dispatch(solution)) is not None
    )

formulation_of(kind)

Return the formulation of the given type, if the system has entities of that type.

Parameters:

Name Type Description Default
kind type[FormulationT]

The formulation type, such as FlexibleLoadFormulation.

required

Returns:

Type Description
FormulationT | None

The formulation, or None if the system has no entity of that type.

Source code in src/odys/optimization/problem.py
78
79
80
81
82
83
84
85
86
87
88
def formulation_of(self, kind: type[FormulationT]) -> FormulationT | None:
    """Return the formulation of the given type, if the system has entities of that type.

    Args:
        kind: The formulation type, such as `FlexibleLoadFormulation`.

    Returns:
        The formulation, or None if the system has no entity of that type.

    """
    return next((formulation for formulation in self.formulations if isinstance(formulation, kind)), None)