diff --git a/Makefile b/Makefile index bf87579d6..6fb98eb5d 100755 --- a/Makefile +++ b/Makefile @@ -54,11 +54,11 @@ install-pinned-macos: _conda_check # Run default tests test: set -e - snakemake solve_elec_networks --configfile config/test/config.electricity.yaml --rerun-triggers=mtime - snakemake --configfile config/test/config.overnight.yaml --rerun-triggers=mtime - snakemake --configfile config/test/config.myopic.yaml --rerun-triggers=mtime - snakemake make_summary_perfect --configfile config/test/config.perfect.yaml --rerun-triggers=mtime - snakemake --configfile config/test/config.scenarios.yaml --rerun-triggers=mtime -n + snakemake solve_elec_networks --configfile config/test/config.electricity.yaml + snakemake --configfile config/test/config.overnight.yaml + snakemake --configfile config/test/config.myopic.yaml + snakemake make_summary_perfect --configfile config/test/config.perfect.yaml + snakemake --configfile config/test/config.scenarios.yaml -n echo "All tests completed successfully." unit-test: @@ -66,11 +66,11 @@ unit-test: # Cleans all output files from tests clean-tests: - snakemake solve_elec_networks --configfile config/test/config.electricity.yaml --rerun-triggers=mtime --delete-all-output - snakemake --configfile config/test/config.overnight.yaml --rerun-triggers=mtime --delete-all-output - snakemake --configfile config/test/config.myopic.yaml --rerun-triggers=mtime --delete-all-output - snakemake make_summary_perfect --configfile config/test/config.perfect.yaml --rerun-triggers=mtime --delete-all-output - snakemake --configfile config/test/config.scenarios.yaml --rerun-triggers=mtime -n --delete-all-output + snakemake solve_elec_networks --configfile config/test/config.electricity.yaml --delete-all-output + snakemake --configfile config/test/config.overnight.yaml --delete-all-output + snakemake --configfile config/test/config.myopic.yaml --delete-all-output + snakemake make_summary_perfect --configfile config/test/config.perfect.yaml --delete-all-output + snakemake --configfile config/test/config.scenarios.yaml -n --delete-all-output # Removes all created files except for large cutout files (similar to fresh clone) reset: diff --git a/config/config.default.yaml b/config/config.default.yaml index 87f3cb737..ffebff647 100644 --- a/config/config.default.yaml +++ b/config/config.default.yaml @@ -1012,6 +1012,12 @@ solving: cbc-default: {} # Used in CI glpk-default: {} # Used in CI + check_objective: + enable: false + expected_value: None + atol: 1_000_000 + rtol: 0.01 + mem_mb: 30000 #memory in MB; 20 GB enough for 50+B+I+H2; 100 GB for 181+B+I+H2 runtime: 6h #runtime in humanfriendly style https://humanfriendly.readthedocs.io/en/latest/ diff --git a/config/test/config.electricity.yaml b/config/test/config.electricity.yaml index 9f891f4b0..e52703047 100644 --- a/config/test/config.electricity.yaml +++ b/config/test/config.electricity.yaml @@ -82,6 +82,9 @@ solving: name: highs options: highs-default + check_objective: + enable: true + expected_value: 3.8120188094e+07 plotting: map: diff --git a/config/test/config.overnight.yaml b/config/test/config.overnight.yaml index c9d379930..d15770b85 100644 --- a/config/test/config.overnight.yaml +++ b/config/test/config.overnight.yaml @@ -84,6 +84,10 @@ solving: options: highs-default mem: 4000 + check_objective: + enable: true + expected_value: 7.0847670388e+08 + plotting: map: boundaries: diff --git a/config/test/config.perfect.yaml b/config/test/config.perfect.yaml index a5fa6e1fb..867cb6634 100644 --- a/config/test/config.perfect.yaml +++ b/config/test/config.perfect.yaml @@ -89,6 +89,10 @@ solving: options: highs-default mem: 4000 + check_objective: + enable: true + expected_value: 1.4427662256e+10 + plotting: map: boundaries: diff --git a/scripts/solve_network.py b/scripts/solve_network.py index bce09c64e..5867f9d4a 100644 --- a/scripts/solve_network.py +++ b/scripts/solve_network.py @@ -51,6 +51,10 @@ pypsa.pf.logger.setLevel(logging.WARNING) +class ObjectiveValueError(Exception): + pass + + def add_land_use_constraint_perfect(n): """ Add global constraints for tech capacity limit. @@ -986,6 +990,19 @@ def extra_functionality(n, snapshots): custom_extra_functionality(n, snapshots, snakemake) +def check_objective_value(n, solving): + check_objective = solving["check_objective"] + if check_objective["enable"]: + atol = check_objective["atol"] + rtol = check_objective["rtol"] + expected_value = check_objective["expected_value"] + if not np.isclose(n.objective, expected_value, atol=atol, rtol=rtol): + raise ObjectiveValueError( + f"Objective value {n.objective} differs from expected value " + f"{expected_value} by more than {atol}." + ) + + def solve_network(n, config, params, solving, **kwargs): set_of_options = solving["solver"]["options"] cf_solving = solving["options"] @@ -1034,10 +1051,13 @@ def solve_network(n, config, params, solving, **kwargs): **kwargs ) - if status != "ok" and not rolling_horizon: - logger.warning( - f"Solving status '{status}' with termination condition '{condition}'" - ) + if not rolling_horizon: + if status != "ok": + logger.warning( + f"Solving status '{status}' with termination condition '{condition}'" + ) + check_objective_value(n, solving) + if "infeasible" in condition: labels = n.model.compute_infeasibilities() logger.info(f"Labels:\n{labels}")