From 12790b0daf867185816d8cf07f4bf55def57486c Mon Sep 17 00:00:00 2001 From: Ian Menezes Date: Mon, 12 Feb 2024 14:31:50 +0100 Subject: [PATCH] See #ANT-958 Fixed after merge --- src/andromede/simulation/optimization.py | 62 +++++++++++++++++------- tests/andromede/test_xpansion.py | 10 ++-- 2 files changed, 51 insertions(+), 21 deletions(-) diff --git a/src/andromede/simulation/optimization.py b/src/andromede/simulation/optimization.py index 05a57b73..93aa7fd9 100644 --- a/src/andromede/simulation/optimization.py +++ b/src/andromede/simulation/optimization.py @@ -237,10 +237,14 @@ def get_values(self, expression: ExpressionNode) -> TimestepValueProvider: ) def add_variable( - self, block_timestep: int, scenario: int, variable: lp.Variable + self, + block_timestep: int, + scenario: int, + model_var_name: str, + variable: lp.Variable, ) -> None: self.opt_context.register_component_variable( - block_timestep, scenario, self.component.id, variable.name(), variable + block_timestep, scenario, self.component.id, model_var_name, variable ) def get_variable( @@ -363,8 +367,6 @@ def get_component_variable( if variable_structure.scenario == False: scenario = 0 - variable_name = f"{component_id}_{variable_name}_{block_timestep}_{scenario}" - return self._component_variables[ TimestepComponentVariableKey( component_id, variable_name, block_timestep, scenario @@ -490,8 +492,12 @@ def _create_objective( component_context: ComponentContext, objective_contribution: ExpressionNode, ) -> None: + instantiated_expr = _instantiate_model_expression( + objective_contribution, component.id, opt_context + ) # We have already checked in the model creation that the objective contribution is neither indexed by time nor by scenario - linear_expr = component_context.linearize_expression(0, 0, objective_contribution) + linear_expr = component_context.linearize_expression(0, 0, instantiated_expr) + obj: lp.Objective = solver.Objective() for term in linear_expr.terms.values(): # TODO : How to handle the scenario operator in a general manner ? @@ -688,13 +694,16 @@ def __init__( self._create_constraints() self._create_objectives() - def _register_connection_fields_definitions(self) -> None: for cnx in self.context.network.connections: for field_name in list(cnx.master_port.keys()): master_port = cnx.master_port[field_name] - port_definition = master_port.component.model.port_fields_definitions.get( - PortFieldId(port_name=master_port.port_id, field_name=field_name.name) + port_definition = ( + master_port.component.model.port_fields_definitions.get( + PortFieldId( + port_name=master_port.port_id, field_name=field_name.name + ) + ) ) expression_node = port_definition.definition # type: ignore instantiated_expression = add_component_context( @@ -713,7 +722,6 @@ def _register_connection_fields_definitions(self) -> None: expression=instantiated_expression, ) - def _create_variables(self) -> None: for component in self.context.network.all_components: component_context = self.context.get_component_context(component) @@ -754,13 +762,35 @@ def _create_variables(self) -> None: ).get_value(block_timestep, scenario) # TODO: Add BoolVar or IntVar if the variable is specified to be integer or bool - solver_var = self.solver.NumVar(lower_bound, upper_bound, model_var.name) - component_context.add_variable(block_timestep, scenario, solver_var) - + # Externally, for the Solver, this variable will have a full name + # Internally, it will be indexed by a structure that into account + # the component id, variable name, timestep and scenario separately + solver_var = self.solver.NumVar( + lower_bound, + upper_bound, + f"{component.id}_{model_var.name}_{block_timestep}_{scenario}", + ) + component_context.add_variable( + block_timestep, scenario, model_var.name, solver_var + ) def _create_constraints(self) -> None: for component in self.context.network.all_components: for constraint in component.model.get_all_constraints(): + if ( + self.type == OptimizationProblem.Type.xpansion_master + and constraint.context == ProblemContext.operational + ): + # Xpansion Master Problem only takes investment constraints + continue + + elif ( + self.type == OptimizationProblem.Type.xpansion_subproblem + and constraint.context == ProblemContext.investment + ): + # Xpansion SubProblems only take operational constraints + continue + instantiated_expr = _instantiate_model_expression( constraint.expression, component.id, self.context ) @@ -770,8 +800,9 @@ def _create_constraints(self) -> None: instantiated_ub = _instantiate_model_expression( constraint.upper_bound, component.id, self.context ) + instantiated_constraint = Constraint( - name=constraint.name, + name=f"{component.id}_{constraint.name}", expression=instantiated_expr, lower_bound=instantiated_lb, upper_bound=instantiated_ub, @@ -782,7 +813,6 @@ def _create_constraints(self) -> None: instantiated_constraint, ) - def _create_objectives(self) -> None: for component in self.context.network.all_components: component_context = self.context.get_component_context(component) @@ -966,9 +996,7 @@ def export_xpansion_problem(problems: List["OptimizationProblem"]) -> bool: # If candidate was identified in master problem_to_candidates[problem.name][variable] = len(seen_variables) - 1 - if not write_to_file( - f"{problem.name}.mps", problem_mps_str, "outputs/lp" - ): + if not write_to_file(f"{problem.name}.mps", problem_mps_str, "outputs/lp"): return False # === write structure.txt file === diff --git a/tests/andromede/test_xpansion.py b/tests/andromede/test_xpansion.py index 56381718..32996bef 100644 --- a/tests/andromede/test_xpansion.py +++ b/tests/andromede/test_xpansion.py @@ -127,6 +127,12 @@ def wind_cluster_candidate() -> Model: ), ], ports=[ModelPort(port_type=BALANCE_PORT_TYPE, port_name="balance_port")], + port_fields_definitions=[ + PortFieldDefinition( + port_field=PortFieldId("balance_port", "flow"), + definition=var("generation"), + ) + ], constraints=[ Constraint( name="Max generation", expression=var("generation") <= var("p_max") @@ -136,10 +142,6 @@ def wind_cluster_candidate() -> Model: expression=var("p_max") == param("p_max_per_unit") * var("nb_units"), context=INVESTMENT, ), - Constraint( - name="Balance contribution", - expression=port_field("balance_port", "flow") == var("generation"), - ), ], objective_operational_contribution=(param("op_cost") * var("generation")) .sum()