Skip to content

Commit

Permalink
See #ANT-958 Fixes after PR review
Browse files Browse the repository at this point in the history
  • Loading branch information
ianmnz committed Feb 19, 2024
1 parent b30b673 commit debb29a
Show file tree
Hide file tree
Showing 4 changed files with 42 additions and 42 deletions.
9 changes: 3 additions & 6 deletions src/andromede/model/parameter.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
from dataclasses import dataclass

from andromede.expression.indexing_structure import IndexingStructure
from andromede.model.common import ValueType, ProblemContext
from andromede.model.common import ValueType


@dataclass(frozen=True)
Expand All @@ -27,20 +27,17 @@ class Parameter:
name: str
type: ValueType
structure: IndexingStructure
context: ProblemContext


def int_parameter(
name: str,
structure: IndexingStructure = IndexingStructure(True, True),
context: ProblemContext = ProblemContext.operational,
) -> Parameter:
return Parameter(name, ValueType.INTEGER, structure, context)
return Parameter(name, ValueType.INTEGER, structure)


def float_parameter(
name: str,
structure: IndexingStructure = IndexingStructure(True, True),
context: ProblemContext = ProblemContext.operational,
) -> Parameter:
return Parameter(name, ValueType.FLOAT, structure, context)
return Parameter(name, ValueType.FLOAT, structure)
32 changes: 15 additions & 17 deletions src/andromede/simulation/optimization.py
Original file line number Diff line number Diff line change
Expand Up @@ -280,13 +280,13 @@ class BlockBorderManagement(Enum):
class SolverVariableInfo:
"""
Helper class for constructing the structure file
for Bender solver. It keeps track of the corresponding
for Benders solver. It keeps track of the corresponding
column of the variable in the MPS format as well as if it is
present in the objective function or not
"""

name: str
column: int
column_id: int
is_in_objective: bool


Expand Down Expand Up @@ -667,16 +667,14 @@ class OptimizationProblem:
class Type(Enum):
"""
Class to specify the type of the created problem:
- simulator: Creates a Antares Simulator problem with only operational variables and constraints
- xpansion_master: Creates a Xpansion master problem only
- xpansion_subproblem: Creates Xpansion sub-problems only
- xpansion_merged: Creates a merged Xpansion master/subproblem
- master: Creates a Xpansion master problem with investment variables and constraints only
- subproblem: Creates Xpansion sub-problems with operational variables and constraints only
- merged: Creates a Antares Simulator/ Xpansion problem with both
"""

simulator = 0
xpansion_merged = 1
xpansion_master = 2
xpansion_subproblem = 3
merged = 0
master = 1
subproblem = 2

name: str
solver: lp.Solver
Expand All @@ -688,7 +686,7 @@ def __init__(
name: str,
solver: lp.Solver,
opt_context: OptimizationContext,
opt_type: Type = Type.simulator,
opt_type: Type = Type.merged,
) -> None:
self.name = name
self.solver = solver
Expand Down Expand Up @@ -735,7 +733,7 @@ def _create_variables(self) -> None:

for model_var in model.variables.values():
if (
self.type == OptimizationProblem.Type.xpansion_master
self.type == OptimizationProblem.Type.master
and model_var.context == ProblemContext.operational
):
# Xpansion Master Problem only takes investment variables and parameters
Expand Down Expand Up @@ -784,14 +782,14 @@ 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
self.type == OptimizationProblem.Type.master
and constraint.context == ProblemContext.operational
):
# Xpansion Master Problem only takes investment constraints
continue

elif (
self.type == OptimizationProblem.Type.xpansion_subproblem
self.type == OptimizationProblem.Type.subproblem
and constraint.context == ProblemContext.investment
):
# Xpansion SubProblems only take operational constraints
Expand Down Expand Up @@ -825,7 +823,7 @@ def _create_objectives(self) -> None:
model = component.model

if (
self.type != OptimizationProblem.Type.xpansion_master
self.type != OptimizationProblem.Type.master
and model.objective_operational_contribution is not None
):
# Xpansion SubProblems only take the operational contribution
Expand All @@ -838,7 +836,7 @@ def _create_objectives(self) -> None:
)

if (
self.type != OptimizationProblem.Type.xpansion_subproblem
self.type != OptimizationProblem.Type.subproblem
and model.objective_investment_contribution is not None
):
# Xpansion Master Problem only takes the investment contribution
Expand Down Expand Up @@ -866,7 +864,7 @@ def build_problem(
problem_name: str = "optimization_problem",
border_management: BlockBorderManagement = BlockBorderManagement.CYCLE,
solver_id: str = "GLOP",
problem_type: OptimizationProblem.Type = OptimizationProblem.Type.simulator,
problem_type: OptimizationProblem.Type = OptimizationProblem.Type.merged,
) -> OptimizationProblem:
"""
Entry point to build the optimization problem for a time period.
Expand Down
11 changes: 6 additions & 5 deletions src/andromede/simulation/xpansion.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@

"""
The xpansion module extends the optimization module
with Bender solver related functions
with Benders solver related functions
"""

import json
Expand Down Expand Up @@ -65,7 +65,7 @@ def export_structure(self) -> str:
if solver_var_info.is_in_objective:
problem_to_candidates["master"][
solver_var_info.name
] = solver_var_info.column
] = solver_var_info.column_id
candidates.add(solver_var_info.name)

for problem in self.subproblems:
Expand All @@ -76,7 +76,7 @@ def export_structure(self) -> str:
# If candidate was identified in master
problem_to_candidates[problem.name][
solver_var_info.name
] = solver_var_info.column
] = solver_var_info.column_id

structure_str = ""
for problem_name, candidate_to_index in problem_to_candidates.items():
Expand Down Expand Up @@ -155,6 +155,7 @@ def run(
# TODO Maybe a more robust check and/or return value?
# For now, it won't look anywhere else because a new
# architecture should be discussed
print(root_dir + "/bin/benders executable not found. Returning True")
return True

os.chdir(path)
Expand Down Expand Up @@ -193,7 +194,7 @@ def build_xpansion_problem(
problem_name="master",
border_management=border_management,
solver_id=solver_id,
problem_type=OptimizationProblem.Type.xpansion_master,
problem_type=OptimizationProblem.Type.master,
)

# Xpansion Sub-problems
Expand All @@ -205,7 +206,7 @@ def build_xpansion_problem(
problem_name="subproblem",
border_management=border_management,
solver_id=solver_id,
problem_type=OptimizationProblem.Type.xpansion_subproblem,
problem_type=OptimizationProblem.Type.subproblem,
)

return XpansionProblem(master, [subproblem])
32 changes: 18 additions & 14 deletions tests/andromede/test_xpansion.py
Original file line number Diff line number Diff line change
Expand Up @@ -59,9 +59,9 @@
INVESTMENT = ProblemContext.investment
OPERATIONAL = ProblemContext.operational

XPANSION_MASTER = OptimizationProblem.Type.xpansion_master
XPANSION_SUBPBL = OptimizationProblem.Type.xpansion_subproblem
XPANSION_MERGED = OptimizationProblem.Type.xpansion_merged
MASTER = OptimizationProblem.Type.master
SUBPBL = OptimizationProblem.Type.subproblem
MERGED = OptimizationProblem.Type.merged


@pytest.fixture
Expand All @@ -70,7 +70,7 @@ def thermal_candidate() -> Model:
id="GEN",
parameters=[
float_parameter("op_cost", CONSTANT),
float_parameter("invest_cost", CONSTANT, INVESTMENT),
float_parameter("invest_cost", CONSTANT),
],
variables=[
float_variable("generation", lower_bound=literal(0)),
Expand Down Expand Up @@ -108,8 +108,8 @@ def wind_cluster_candidate() -> Model:
id="WIND_CLUSTER",
parameters=[
float_parameter("op_cost", CONSTANT),
float_parameter("invest_cost", CONSTANT, INVESTMENT),
float_parameter("p_max_per_unit", CONSTANT, INVESTMENT),
float_parameter("invest_cost", CONSTANT),
float_parameter("p_max_per_unit", CONSTANT),
],
variables=[
float_variable("generation", lower_bound=literal(0)),
Expand Down Expand Up @@ -221,12 +221,14 @@ def test_generation_xpansion_single_time_step_single_scenario(

scenarios = 1
problem = build_problem(
network, database, TimeBlock(1, [0]), scenarios, problem_type=XPANSION_MERGED
network, database, TimeBlock(1, [0]), scenarios, problem_type=MERGED
)
status = problem.solver.Solve()

assert status == problem.solver.OPTIMAL
assert problem.solver.Objective().Value() == 490 * 100 + 100 * 10 + 200 * 40
assert problem.solver.Objective().Value() == pytest.approx(
490 * 100 + 100 * 10 + 200 * 40
)

output = OutputValues(problem)
expected_output = OutputValues()
Expand Down Expand Up @@ -298,8 +300,8 @@ def test_two_candidates_xpansion_single_time_step_single_scenario(
status = problem.solver.Solve()

assert status == problem.solver.OPTIMAL
assert problem.solver.Objective().Value() == (45 * 200) + (490 * 100 + 10 * 100) + (
200 * 100 + 10 * 100
assert problem.solver.Objective().Value() == pytest.approx(
(45 * 200) + (490 * 100 + 10 * 100) + (200 * 100 + 10 * 100)
)

output = OutputValues(problem)
Expand All @@ -321,7 +323,7 @@ def test_model_export_xpansion_single_time_step_single_scenario(
) -> None:
"""
Same test as before but this time we separate master/subproblem and
export the problems in MPS format to be solved by the Bender solver in Xpansion
export the problems in MPS format to be solved by the Benders solver in Xpansion
"""

database = DataBase()
Expand Down Expand Up @@ -429,9 +431,11 @@ def test_generation_xpansion_two_time_steps_two_scenarios(
# assert (
# problem.solver.NumConstraints() == 3 * scenarios * horizon
# ) # Flow balance, Max generation for each cluster
assert problem.solver.Objective().Value() == 490 * 300 + 0.5 * (
10 * 300 + 10 * 300 + 40 * 200
) + 0.5 * (10 * 200 + 10 * 300 + 40 * 100)
assert problem.solver.Objective().Value() == pytest.approx(
490 * 300
+ 0.5 * (10 * 300 + 10 * 300 + 40 * 200)
+ 0.5 * (10 * 200 + 10 * 300 + 40 * 100)
)

output = OutputValues(problem)
expected_output = OutputValues()
Expand Down

0 comments on commit debb29a

Please sign in to comment.