Skip to content

Commit 1e85e80

Browse files
authored
feat: pass solving.options through to CBA solves (#708)
* refac: move cba-specific solving options from cba.solving.options to cba.solving and add validator * feat: pass through `include_objective_constant` * refac: use `collect_kwargs` in solve_cba_network * refac: use `collect_kwargs` in solve_cba_msv_extraction * chore: reorder pixi command * [github-actions.ci] Update locked envs * chore: change run name back to all * feat: add `gurobi-simplex` solver option * fix: update ``config/test/config.cyears.yaml`` to have latest config options and settings * chore: add comment about `ScaleFlag` * docs: add #708 to doc/release_notes * fix: pass new horizon and overlap settings to prepare_rolling_horizon * chore: fix typo * feat: change `include_objective_constant` default to False * refac: change `mock_snakemake` and add comment about rolling horizon and overlap * chore: edit description of cba solving options * docs: edit release note description * feat: add back gurobi logger setting * fix: call on `all_kwargs` instead of `kwargs` * chore: reference correct code in comments * refac: use `cf_solving.get(...)` * docs: edit release note description for #708 --------- Co-authored-by: measrainsey <21201137+measrainsey@users.noreply.github.com>
1 parent 4854af0 commit 1e85e80

12 files changed

Lines changed: 220 additions & 61 deletions

config/config.default.yaml

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1252,6 +1252,8 @@ solving:
12521252
seed: 123
12531253
custom_extra_functionality: ../data/custom_extra_functionality.py
12541254
io_api:
1255+
include_objective_constant: false
1256+
assign_all_duals: false
12551257
track_iterations: false
12561258
min_iterations: 2
12571259
max_iterations: 3
@@ -1315,6 +1317,16 @@ solving:
13151317
AggFill: 0
13161318
PreDual: 0
13171319
GURO_PAR_BARDENSETHRESH: 200
1320+
"gurobi-simplex":
1321+
threads: 32
1322+
method: 1
1323+
NodeMethod: 1
1324+
Seed: 123
1325+
AggFill: 0
1326+
PreDual: 0
1327+
FeasibilityTol: 1.0e-05
1328+
OptimalityTol: 1.0e-05
1329+
ScaleFlag: -1
13181330
"gurobi-numeric-focus":
13191331
NumericFocus: 3
13201332
method: 2
@@ -1603,17 +1615,18 @@ cba:
16031615
resolution: false
16041616
resample_method: ffill
16051617
solving:
1618+
options: {}
16061619
solver:
16071620
name: highs
16081621
options: "highs-simplex"
16091622
solver_options: {}
16101623
solving:
16111624
options:
1612-
horizon: 168
1613-
overlap: 1
16141625
load_shedding:
16151626
enable: true
16161627
io_api: direct
1628+
horizon: 168
1629+
overlap: 1
16171630
solver:
16181631
name: highs
16191632
options: "highs-simplex"

config/config.tyndp.yaml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -466,10 +466,10 @@ cba:
466466

467467
# Rolling horizon solving settings
468468
solving:
469+
horizon: 168
470+
overlap: 1
469471
options:
470472
noisy_costs: false
471-
horizon: 168
472-
overlap: 1
473473
load_shedding:
474474
enable: true
475475
apply_to_all_carriers: true

config/schema.default.json

Lines changed: 83 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -584,6 +584,11 @@
584584
"solving": {
585585
"description": "Configuration for `cba.msv_extraction.solving` settings.",
586586
"properties": {
587+
"options": {
588+
"additionalProperties": true,
589+
"description": "Solving option overrides for MSV extraction. Uses the same option names as the top-level `solving.options` section.",
590+
"type": "object"
591+
},
587592
"solver": {
588593
"additionalProperties": {
589594
"type": "string"
@@ -608,9 +613,19 @@
608613
"properties": {
609614
"options": {
610615
"additionalProperties": true,
611-
"description": "Solving options for rolling horizon dispatch.",
616+
"description": "Solving option overrides for rolling horizon CBA dispatch. Uses the same option names as the top-level `solving.options`.",
612617
"type": "object"
613618
},
619+
"horizon": {
620+
"default": 168,
621+
"description": "Number of snapshots to consider in each rolling-horizon window for CBA project solves.",
622+
"type": "integer"
623+
},
624+
"overlap": {
625+
"default": 1,
626+
"description": "Number of snapshots to overlap between rolling-horizon windows for CBA project solves.",
627+
"type": "integer"
628+
},
614629
"solver": {
615630
"additionalProperties": {
616631
"type": "string"
@@ -5952,6 +5967,16 @@
59525967
"default": null,
59535968
"description": "Passed to linopy and determines the API used to communicate with the solver. With the `'lp'` and `'mps'` options linopy passes a file to the solver; with the `'direct'` option (only supported for HIGHS and Gurobi) linopy uses an in-memory python API resulting in better performance."
59545969
},
5970+
"include_objective_constant": {
5971+
"default": false,
5972+
"description": "Passed to linopy model creation. When false, objective constants are not represented as variables, which can improve numerical conditioning.",
5973+
"type": "boolean"
5974+
},
5975+
"assign_all_duals": {
5976+
"default": false,
5977+
"description": "Assign all dual variables returned by the solver to the network.",
5978+
"type": "boolean"
5979+
},
59555980
"track_iterations": {
59565981
"default": false,
59575982
"description": "Flag whether to store the intermediate branch capacities and objective function values are recorded for each iteration in `network.lines['s_nom_opt_X']` (where `X` labels the iteration)",
@@ -6578,6 +6603,11 @@
65786603
"solving": {
65796604
"description": "Configuration for `cba.msv_extraction.solving` settings.",
65806605
"properties": {
6606+
"options": {
6607+
"additionalProperties": true,
6608+
"description": "Solving option overrides for MSV extraction. Uses the same option names as the top-level `solving.options` section.",
6609+
"type": "object"
6610+
},
65816611
"solver": {
65826612
"additionalProperties": {
65836613
"type": "string"
@@ -6600,6 +6630,11 @@
66006630
"_CbaMsvSolvingConfig": {
66016631
"description": "Configuration for `cba.msv_extraction.solving` settings.",
66026632
"properties": {
6633+
"options": {
6634+
"additionalProperties": true,
6635+
"description": "Solving option overrides for MSV extraction. Uses the same option names as the top-level `solving.options` section.",
6636+
"type": "object"
6637+
},
66036638
"solver": {
66046639
"additionalProperties": {
66056640
"type": "string"
@@ -6637,9 +6672,19 @@
66376672
"properties": {
66386673
"options": {
66396674
"additionalProperties": true,
6640-
"description": "Solving options for rolling horizon dispatch.",
6675+
"description": "Solving option overrides for rolling horizon CBA dispatch. Uses the same option names as the top-level `solving.options`.",
66416676
"type": "object"
66426677
},
6678+
"horizon": {
6679+
"default": 168,
6680+
"description": "Number of snapshots to consider in each rolling-horizon window for CBA project solves.",
6681+
"type": "integer"
6682+
},
6683+
"overlap": {
6684+
"default": 1,
6685+
"description": "Number of snapshots to overlap between rolling-horizon windows for CBA project solves.",
6686+
"type": "integer"
6687+
},
66436688
"solver": {
66446689
"additionalProperties": {
66456690
"type": "string"
@@ -8882,6 +8927,16 @@
88828927
"default": null,
88838928
"description": "Passed to linopy and determines the API used to communicate with the solver. With the `'lp'` and `'mps'` options linopy passes a file to the solver; with the `'direct'` option (only supported for HIGHS and Gurobi) linopy uses an in-memory python API resulting in better performance."
88848929
},
8930+
"include_objective_constant": {
8931+
"default": false,
8932+
"description": "Passed to linopy model creation. When false, objective constants are not represented as variables, which can improve numerical conditioning.",
8933+
"type": "boolean"
8934+
},
8935+
"assign_all_duals": {
8936+
"default": false,
8937+
"description": "Assign all dual variables returned by the solver to the network.",
8938+
"type": "boolean"
8939+
},
88858940
"track_iterations": {
88868941
"default": false,
88878942
"description": "Flag whether to store the intermediate branch capacities and objective function values are recorded for each iteration in `network.lines['s_nom_opt_X']` (where `X` labels the iteration)",
@@ -13688,6 +13743,16 @@
1368813743
"default": null,
1368913744
"description": "Passed to linopy and determines the API used to communicate with the solver. With the `'lp'` and `'mps'` options linopy passes a file to the solver; with the `'direct'` option (only supported for HIGHS and Gurobi) linopy uses an in-memory python API resulting in better performance."
1369013745
},
13746+
"include_objective_constant": {
13747+
"default": false,
13748+
"description": "Passed to linopy model creation. When false, objective constants are not represented as variables, which can improve numerical conditioning.",
13749+
"type": "boolean"
13750+
},
13751+
"assign_all_duals": {
13752+
"default": false,
13753+
"description": "Assign all dual variables returned by the solver to the network.",
13754+
"type": "boolean"
13755+
},
1369113756
"track_iterations": {
1369213757
"default": false,
1369313758
"description": "Flag whether to store the intermediate branch capacities and objective function values are recorded for each iteration in `network.lines['s_nom_opt_X']` (where `X` labels the iteration)",
@@ -15454,6 +15519,11 @@
1545415519
"solving": {
1545515520
"description": "Configuration for `cba.msv_extraction.solving` settings.",
1545615521
"properties": {
15522+
"options": {
15523+
"additionalProperties": true,
15524+
"description": "Solving option overrides for MSV extraction. Uses the same option names as the top-level `solving.options` section.",
15525+
"type": "object"
15526+
},
1545715527
"solver": {
1545815528
"additionalProperties": {
1545915529
"type": "string"
@@ -15478,9 +15548,19 @@
1547815548
"properties": {
1547915549
"options": {
1548015550
"additionalProperties": true,
15481-
"description": "Solving options for rolling horizon dispatch.",
15551+
"description": "Solving option overrides for rolling horizon CBA dispatch. Uses the same option names as the top-level `solving.options`.",
1548215552
"type": "object"
1548315553
},
15554+
"horizon": {
15555+
"default": 168,
15556+
"description": "Number of snapshots to consider in each rolling-horizon window for CBA project solves.",
15557+
"type": "integer"
15558+
},
15559+
"overlap": {
15560+
"default": 1,
15561+
"description": "Number of snapshots to overlap between rolling-horizon windows for CBA project solves.",
15562+
"type": "integer"
15563+
},
1548415564
"solver": {
1548515565
"additionalProperties": {
1548615566
"type": "string"

config/test/config.cyears.yaml

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ run:
1616
foresight: myopic
1717

1818
tyndp_scenario: NT
19+
data_config: # change to 'tyndp' to use tyndp-archive where available
1920

2021
launch_explorer: false
2122

@@ -136,7 +137,6 @@ electricity:
136137
- Electrolyser
137138
- Battery
138139
- DSR
139-
- DSR
140140

141141
transmission_limit: v1.0
142142

@@ -188,6 +188,7 @@ load:
188188
- 2050
189189
manual_adjustments: false
190190
supplement_synthetic: false
191+
patch_demand_with_mm: true
191192

192193
pypsa_eur:
193194
Bus:
@@ -313,6 +314,7 @@ sector:
313314
- H2
314315
offshore_hubs_tyndp:
315316
enable: true
317+
patch_crossborder_with_mm: true
316318
connect_isolated: true
317319
max_capacity:
318320
DC_OH: 10
@@ -474,10 +476,10 @@ cba:
474476

475477
# Rolling horizon solving settings
476478
solving:
479+
horizon: 168
480+
overlap: 1
477481
options:
478482
noisy_costs: false
479-
horizon: 168
480-
overlap: 1
481483
load_shedding:
482484
enable: true
483485
apply_to_all_carriers: true

config/test/config.tyndp.yaml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -474,10 +474,10 @@ cba:
474474

475475
# Rolling horizon solving settings
476476
solving:
477+
horizon: 168
478+
overlap: 1
477479
options:
478480
noisy_costs: false
479-
horizon: 168
480-
overlap: 1
481481
load_shedding:
482482
enable: true
483483
apply_to_all_carriers: true

doc/release_notes.rst

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,12 @@ Upcoming Open-TYNDP Release
2727

2828
* Use only electricity-sourced emissions for and and change signs of B2 and B4 indicators (https://github.com/open-energy-transition/open-tyndp/pull/709).
2929

30+
* Pass through and validate default solving options for CBA solve (https://github.com/open-energy-transition/open-tyndp/pull/708):
31+
32+
- Make breaking change by moving ``cba.solving.options.horizon`` and ``cba.solving.options.overlap`` to ``cba.solving.horizon`` and ``cba.solving.overlap``, respectively.
33+
- Add ``include_objective_constant`` and ``assign_all_duals`` to solving config validator.
34+
- Add ``gurobi-simplex`` as solver option.
35+
3036
**Bugfixes and Compatibility**
3137

3238
**Documentation**

scripts/cba/prepare_rolling_horizon.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -356,7 +356,7 @@ def fix_reservoir_soc_at_boundaries(
356356

357357
# Fix reservoir state of charge at window boundaries from perfect foresight
358358
soc_boundary_carriers = snakemake.params.get("soc_boundary_carriers", [])
359-
cba_solving = snakemake.config.get("cba", {}).get("solving", {}).get("options", {})
359+
cba_solving = snakemake.config.get("cba", {}).get("solving", {})
360360
fix_reservoir_soc_at_boundaries(
361361
n,
362362
n_msv,

scripts/cba/solve_cba_msv_extraction.py

Lines changed: 17 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@
3232
set_scenario_config,
3333
update_config_from_wildcards,
3434
)
35-
from scripts.solve_network import prepare_network
35+
from scripts.solve_network import collect_kwargs, prepare_network
3636
from scripts.temporal_aggregation import set_temporal_aggregation
3737

3838
logger = logging.getLogger(__name__)
@@ -64,11 +64,11 @@
6464

6565
# Merge CBA-specific solving overrides into the global solving config
6666
solving = copy.deepcopy(snakemake.params.get("solving", {}))
67-
update_config(solving, snakemake.params.get("cba_solving", {}))
67+
cba_solving = snakemake.params.get("cba_solving", {})
68+
update_config(solving, cba_solving)
6869

6970
solver_name = solving.get("solver", {}).get("name", "highs")
7071
solver_options_key = solving.get("solver", {}).get("options", "highs-default")
71-
solver_options = solving.get("solver_options", {}).get(solver_options_key, {})
7272
solver_log = getattr(snakemake.log, "solver", None)
7373

7474
# Prepare network (e.g., load shedding setup)
@@ -101,13 +101,22 @@
101101
with memory_logger(
102102
filename=getattr(snakemake.log, "memory", None), interval=30
103103
) as mem:
104-
n.optimize.create_model()
105-
status, termination_condition = n.optimize.solve_model(
106-
solver_name=solver_name,
107-
solver_options=solver_options,
108-
assign_all_duals=True,
104+
model_kwargs, solve_kwargs = collect_kwargs(
105+
snakemake.config,
106+
solving,
107+
snakemake.wildcards.get("planning_horizons", None),
109108
log_fn=solver_log,
109+
mode="single",
110110
)
111+
if cba_solving.get("options", {}).get("assign_all_duals") is False:
112+
raise ValueError(
113+
"`cba.msv_extraction.solving.options.assign_all_duals` cannot be "
114+
"false because MSV extraction requires dual values."
115+
)
116+
solve_kwargs["assign_all_duals"] = True
117+
118+
n.optimize.create_model(**model_kwargs)
119+
status, termination_condition = n.optimize.solve_model(**solve_kwargs)
111120

112121
if status != "ok":
113122
logger.error(f"Extraction solve failed: {termination_condition}")

0 commit comments

Comments
 (0)