From 5b29d431f158ba394c58036145acbadd23fbc88e Mon Sep 17 00:00:00 2001 From: Tom Kaehler Date: Wed, 4 Dec 2024 13:52:54 +0100 Subject: [PATCH 1/9] Add PTES and change stores to storage units to implement an energy-to-power ratio --- config/config.default.yaml | 2 + scripts/prepare_sector_network.py | 162 +++++++++++++++++++++++------- 2 files changed, 130 insertions(+), 34 deletions(-) diff --git a/config/config.default.yaml b/config/config.default.yaml index 64197dbbe..eb5243844 100644 --- a/config/config.default.yaml +++ b/config/config.default.yaml @@ -1215,6 +1215,8 @@ plotting: residential urban decentral water tanks discharger: '#baac9e' services rural water tanks discharger: '#bbc2b8' services urban decentral water tanks discharger: '#bdd8d3' + water pits: '#cc826a' + urban central hot water pits: '#d96f4c' # heat demand Heat load: '#cc1f1f' heat: '#cc1f1f' diff --git a/scripts/prepare_sector_network.py b/scripts/prepare_sector_network.py index 1709e8201..2139881e5 100755 --- a/scripts/prepare_sector_network.py +++ b/scripts/prepare_sector_network.py @@ -2264,47 +2264,30 @@ def add_heat( ) if options["tes"]: - n.add("Carrier", f"{heat_system} water tanks") - - n.add( - "Bus", - nodes + f" {heat_system} water tanks", - location=nodes, - carrier=f"{heat_system} water tanks", - unit="MWh_th", - ) - n.add( - "Link", - nodes + f" {heat_system} water tanks charger", - bus0=nodes + f" {heat_system} heat", - bus1=nodes + f" {heat_system} water tanks", - efficiency=costs.at["water tank charger", "efficiency"], - carrier=f"{heat_system} water tanks charger", - p_nom_extendable=True, - ) - - n.add( - "Link", - nodes + f" {heat_system} water tanks discharger", - bus0=nodes + f" {heat_system} water tanks", - bus1=nodes + f" {heat_system} heat", - carrier=f"{heat_system} water tanks discharger", - efficiency=costs.at["water tank discharger", "efficiency"], - p_nom_extendable=True, - ) + n.add("Carrier", f"{heat_system} water tanks") tes_time_constant_days = options["tes_tau"][ heat_system.central_or_decentral ] +# n.add( +# "Bus", +# nodes + f" {heat_system} water tanks", +# location=nodes, +# carrier=f"{heat_system} water tanks", +# unit="MWh_th", +# ) + n.add( - "Store", + "StorageUnit", nodes + f" {heat_system} water tanks", - bus=nodes + f" {heat_system} water tanks", - e_cyclic=True, - e_nom_extendable=True, + bus=nodes + f" {heat_system} heat", carrier=f"{heat_system} water tanks", + efficiency_store=costs.at["water tank charger", "efficiency"], + max_hours=costs.at["central water tank storage", "energy to power ratio"], + efficiency_dispatch=costs.at["water tank discharger", "efficiency"], + p_nom_extendable=True, standing_loss=1 - np.exp(-1 / 24 / tes_time_constant_days), capital_cost=costs.at[ heat_system.central_or_decentral + " water tank storage", "fixed" @@ -2312,8 +2295,115 @@ def add_heat( lifetime=costs.at[ heat_system.central_or_decentral + " water tank storage", "lifetime" ], + e_nom_extendable=True, + e_cyclic=True, ) +# n.add( +# "Link", +# nodes + f" {heat_system} water tanks charger", +# bus0=nodes + f" {heat_system} heat", +# bus1=nodes + f" {heat_system} water tanks", +# efficiency=costs.at["water tank charger", "efficiency"], +# carrier=f"{heat_system} water tanks charger", +# p_nom_extendable=True, +# ) + +# n.add( +# "Link", +# nodes + f" {heat_system} water tanks discharger", +# bus0=nodes + f" {heat_system} water tanks", +# bus1=nodes + f" {heat_system} heat", +# carrier=f"{heat_system} water tanks discharger", +# efficiency=costs.at["water tank discharger", "efficiency"], +# p_nom_extendable=True, +# ) + + +# n.add( +# "Store", +# nodes + f" {heat_system} water tanks", +# bus=nodes + f" {heat_system} water tanks", +# e_cyclic=True, +# e_nom_extendable=True, +# carrier=f"{heat_system} water tanks", +# standing_loss=1 - np.exp(-1 / 24 / tes_time_constant_days), +# capital_cost=costs.at[ +# heat_system.central_or_decentral + " water tank storage", "fixed" +# ], +# lifetime=costs.at[ +# heat_system.central_or_decentral + " water tank storage", "lifetime" +# ], +# ) + + if heat_system == HeatSystem.URBAN_CENTRAL: + + n.add("Carrier", f"{heat_system} water pits") + +# n.add( +# "Bus", +# nodes + f" {heat_system} water pits", +# location=nodes, +# carrier=f"{heat_system} water pits", +# unit="MWh_th", +# ) + + n.add( + "StorageUnit", + nodes + f" {heat_system} water pits", + bus=nodes + f" {heat_system} heat", + carrier=f"{heat_system} water pits", + efficiency_store=costs.at["water pit charger", "efficiency"], + max_hours=costs.at["central water pit storage", "energy to power ratio"], + efficiency_dispatch=costs.at["water pit discharger", "efficiency"], + p_nom_extendable=True, + standing_loss=1 - np.exp(-1 / 24 / tes_time_constant_days), + capital_cost=costs.at[ + "central water pit storage", "fixed" + ], + lifetime=costs.at[ + "central water pit storage", "lifetime" + ], + e_nom_extendable=True, + e_cyclic=True, + ) + +# n.add( +# "Link", +# nodes + f" {heat_system} water pits charger", +# bus0=nodes + f" {heat_system} heat", +# bus1=nodes + f" {heat_system} water pits", +# efficiency=costs.at["water pit charger", "efficiency"], +# carrier=f"{heat_system} water pits charger", +# p_nom_extendable=True, +# ) + +# n.add( +# "Link", +# nodes + f" {heat_system} water pits discharger", +# bus0=nodes + f" {heat_system} water pits", +# bus1=nodes + f" {heat_system} heat", +# carrier=f"{heat_system} water pits discharger", +# efficiency=costs.at["water pit discharger", "efficiency"], +# p_nom_extendable=True, +# ) + +# n.add( +# "Store", +# nodes + f" {heat_system} water pits", +# bus=nodes + f" {heat_system} water pits", +# e_cyclic=True, +# e_nom_extendable=True, +# carrier=f"{heat_system} water pits", +# standing_loss=1 - np.exp(-1 / 24 / tes_time_constant_days), +# capital_cost=costs.at[ +# "central water pit storage", "fixed" +# ], +# lifetime=costs.at[ +# "central water pit storage", "lifetime" +# ], +# ) + if options["resistive_heaters"]: key = f"{heat_system.central_or_decentral} resistive heater" @@ -4254,7 +4344,7 @@ def define_clustering(attributes, aggregate_dict): return agg logger.info("Cluster residential and service heat buses.") - components = ["Bus", "Carrier", "Generator", "Link", "Load", "Store"] + components = ["Bus", "Carrier", "Generator", "Link", "Load", "Store", "StorageUnit"] for c in n.iterate_components(components): df = c.df @@ -4601,10 +4691,14 @@ def add_enhanced_geothermal(n, egs_potentials, egs_overlap, costs): if "snakemake" not in globals(): from _helpers import mock_snakemake +# dies am Ende auf keinen Fall pushen, dirty work!!! + import os + os.chdir(os.path.dirname(os.path.realpath(__file__))) + snakemake = mock_snakemake( "prepare_sector_network", opts="", - clusters="38", + clusters="6", ll="vopt", sector_opts="", planning_horizons="2030", From 8ea2e96daaa58f53bd93743122fe4c784f173299 Mon Sep 17 00:00:00 2001 From: Tom Kaehler Date: Wed, 4 Dec 2024 14:02:35 +0100 Subject: [PATCH 2/9] cleaned up unnecessary code --- scripts/prepare_sector_network.py | 93 ------------------------------- 1 file changed, 93 deletions(-) diff --git a/scripts/prepare_sector_network.py b/scripts/prepare_sector_network.py index 2139881e5..5df766559 100755 --- a/scripts/prepare_sector_network.py +++ b/scripts/prepare_sector_network.py @@ -2271,14 +2271,6 @@ def add_heat( heat_system.central_or_decentral ] -# n.add( -# "Bus", -# nodes + f" {heat_system} water tanks", -# location=nodes, -# carrier=f"{heat_system} water tanks", -# unit="MWh_th", -# ) - n.add( "StorageUnit", nodes + f" {heat_system} water tanks", @@ -2299,55 +2291,10 @@ def add_heat( e_cyclic=True, ) -# n.add( -# "Link", -# nodes + f" {heat_system} water tanks charger", -# bus0=nodes + f" {heat_system} heat", -# bus1=nodes + f" {heat_system} water tanks", -# efficiency=costs.at["water tank charger", "efficiency"], -# carrier=f"{heat_system} water tanks charger", -# p_nom_extendable=True, -# ) - -# n.add( -# "Link", -# nodes + f" {heat_system} water tanks discharger", -# bus0=nodes + f" {heat_system} water tanks", -# bus1=nodes + f" {heat_system} heat", -# carrier=f"{heat_system} water tanks discharger", -# efficiency=costs.at["water tank discharger", "efficiency"], -# p_nom_extendable=True, -# ) - - -# n.add( -# "Store", -# nodes + f" {heat_system} water tanks", -# bus=nodes + f" {heat_system} water tanks", -# e_cyclic=True, -# e_nom_extendable=True, -# carrier=f"{heat_system} water tanks", -# standing_loss=1 - np.exp(-1 / 24 / tes_time_constant_days), -# capital_cost=costs.at[ -# heat_system.central_or_decentral + " water tank storage", "fixed" -# ], -# lifetime=costs.at[ -# heat_system.central_or_decentral + " water tank storage", "lifetime" -# ], -# ) - if heat_system == HeatSystem.URBAN_CENTRAL: n.add("Carrier", f"{heat_system} water pits") -# n.add( -# "Bus", -# nodes + f" {heat_system} water pits", -# location=nodes, -# carrier=f"{heat_system} water pits", -# unit="MWh_th", -# ) - n.add( "StorageUnit", nodes + f" {heat_system} water pits", @@ -2368,42 +2315,6 @@ def add_heat( e_cyclic=True, ) -# n.add( -# "Link", -# nodes + f" {heat_system} water pits charger", -# bus0=nodes + f" {heat_system} heat", -# bus1=nodes + f" {heat_system} water pits", -# efficiency=costs.at["water pit charger", "efficiency"], -# carrier=f"{heat_system} water pits charger", -# p_nom_extendable=True, -# ) - -# n.add( -# "Link", -# nodes + f" {heat_system} water pits discharger", -# bus0=nodes + f" {heat_system} water pits", -# bus1=nodes + f" {heat_system} heat", -# carrier=f"{heat_system} water pits discharger", -# efficiency=costs.at["water pit discharger", "efficiency"], -# p_nom_extendable=True, -# ) - -# n.add( -# "Store", -# nodes + f" {heat_system} water pits", -# bus=nodes + f" {heat_system} water pits", -# e_cyclic=True, -# e_nom_extendable=True, -# carrier=f"{heat_system} water pits", -# standing_loss=1 - np.exp(-1 / 24 / tes_time_constant_days), -# capital_cost=costs.at[ -# "central water pit storage", "fixed" -# ], -# lifetime=costs.at[ -# "central water pit storage", "lifetime" -# ], -# ) - if options["resistive_heaters"]: key = f"{heat_system.central_or_decentral} resistive heater" @@ -4691,10 +4602,6 @@ def add_enhanced_geothermal(n, egs_potentials, egs_overlap, costs): if "snakemake" not in globals(): from _helpers import mock_snakemake -# dies am Ende auf keinen Fall pushen, dirty work!!! - import os - os.chdir(os.path.dirname(os.path.realpath(__file__))) - snakemake = mock_snakemake( "prepare_sector_network", opts="", From fc26291e93f45e7663ec8d85674353c83d9219e1 Mon Sep 17 00:00:00 2001 From: Tom Kaehler Date: Thu, 5 Dec 2024 09:27:25 +0100 Subject: [PATCH 3/9] Align Snakemake defaults in prepare_sector_network with main script --- scripts/prepare_sector_network.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/prepare_sector_network.py b/scripts/prepare_sector_network.py index 5df766559..d273e2e2e 100755 --- a/scripts/prepare_sector_network.py +++ b/scripts/prepare_sector_network.py @@ -4605,7 +4605,7 @@ def add_enhanced_geothermal(n, egs_potentials, egs_overlap, costs): snakemake = mock_snakemake( "prepare_sector_network", opts="", - clusters="6", + clusters="38", ll="vopt", sector_opts="", planning_horizons="2030", From f3bfdee3113d2d104d15daaced58f41a16a2405a Mon Sep 17 00:00:00 2001 From: Tom Kaehler Date: Thu, 5 Dec 2024 09:35:05 +0100 Subject: [PATCH 4/9] Add release note for PTES and storage unit updates in prepare_sector_network --- doc/release_notes.rst | 2 ++ 1 file changed, 2 insertions(+) diff --git a/doc/release_notes.rst b/doc/release_notes.rst index 749b96b0b..e8d23dec1 100644 --- a/doc/release_notes.rst +++ b/doc/release_notes.rst @@ -11,6 +11,8 @@ Release Notes Upcoming Release ================ +* Added PTES and transitioned stores to storage units to implement an energy-to-power ratio in ``prepare_sector_network``. + * Feature: Introduce geothermal district heating (direct utilisation and heat pumps). Potentials are based on `Manz et al. 2024: Spatial analysis of renewable and excess heat potentials for climate-neutral district heating in Europe `. * Feature: Allow CHPs to use different fuel sources such as gas, oil, coal, and methanol. Note that the cost assumptions are based on a gas CHP (except for solid biomass-fired CHP). From b4e4e70c94311c21a787f02c86c37aea5e83e31e Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Thu, 5 Dec 2024 09:15:50 +0000 Subject: [PATCH 5/9] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- scripts/prepare_sector_network.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/scripts/prepare_sector_network.py b/scripts/prepare_sector_network.py index d273e2e2e..27b9a5963 100755 --- a/scripts/prepare_sector_network.py +++ b/scripts/prepare_sector_network.py @@ -2277,7 +2277,9 @@ def add_heat( bus=nodes + f" {heat_system} heat", carrier=f"{heat_system} water tanks", efficiency_store=costs.at["water tank charger", "efficiency"], - max_hours=costs.at["central water tank storage", "energy to power ratio"], + max_hours=costs.at[ + "central water tank storage", "energy to power ratio" + ], efficiency_dispatch=costs.at["water tank discharger", "efficiency"], p_nom_extendable=True, standing_loss=1 - np.exp(-1 / 24 / tes_time_constant_days), @@ -2301,16 +2303,14 @@ def add_heat( bus=nodes + f" {heat_system} heat", carrier=f"{heat_system} water pits", efficiency_store=costs.at["water pit charger", "efficiency"], - max_hours=costs.at["central water pit storage", "energy to power ratio"], + max_hours=costs.at[ + "central water pit storage", "energy to power ratio" + ], efficiency_dispatch=costs.at["water pit discharger", "efficiency"], p_nom_extendable=True, standing_loss=1 - np.exp(-1 / 24 / tes_time_constant_days), - capital_cost=costs.at[ - "central water pit storage", "fixed" - ], - lifetime=costs.at[ - "central water pit storage", "lifetime" - ], + capital_cost=costs.at["central water pit storage", "fixed"], + lifetime=costs.at["central water pit storage", "lifetime"], e_nom_extendable=True, e_cyclic=True, ) From 85c944582e74bbc7c251b29716c752aa5fad8f2e Mon Sep 17 00:00:00 2001 From: Tom Kaehler Date: Tue, 17 Dec 2024 17:58:59 +0100 Subject: [PATCH 6/9] Improved distinction between decentral and central water tanks --- scripts/prepare_sector_network.py | 34 +++++++++++++++++-------------- 1 file changed, 19 insertions(+), 15 deletions(-) diff --git a/scripts/prepare_sector_network.py b/scripts/prepare_sector_network.py index 27b9a5963..e576d4170 100755 --- a/scripts/prepare_sector_network.py +++ b/scripts/prepare_sector_network.py @@ -2276,11 +2276,15 @@ def add_heat( nodes + f" {heat_system} water tanks", bus=nodes + f" {heat_system} heat", carrier=f"{heat_system} water tanks", - efficiency_store=costs.at["water tank charger", "efficiency"], + efficiency_store=costs.at[ + heat_system.central_or_decentral + " water tank charger", "efficiency" + ], max_hours=costs.at[ - "central water tank storage", "energy to power ratio" + heat_system.central_or_decentral + " water tank storage", "energy to power ratio" + ], + efficiency_dispatch=costs.at[ + heat_system.central_or_decentral + " water tank discharger", "efficiency" ], - efficiency_dispatch=costs.at["water tank discharger", "efficiency"], p_nom_extendable=True, standing_loss=1 - np.exp(-1 / 24 / tes_time_constant_days), capital_cost=costs.at[ @@ -2289,8 +2293,7 @@ def add_heat( lifetime=costs.at[ heat_system.central_or_decentral + " water tank storage", "lifetime" ], - e_nom_extendable=True, - e_cyclic=True, + cyclic_state_of_charge=True, ) if heat_system == HeatSystem.URBAN_CENTRAL: @@ -2302,17 +2305,18 @@ def add_heat( nodes + f" {heat_system} water pits", bus=nodes + f" {heat_system} heat", carrier=f"{heat_system} water pits", - efficiency_store=costs.at["water pit charger", "efficiency"], - max_hours=costs.at[ - "central water pit storage", "energy to power ratio" - ], - efficiency_dispatch=costs.at["water pit discharger", "efficiency"], + efficiency_store=costs.at["central water pit charger", "efficiency"], + max_hours=costs.at["central water pit storage", "energy to power ratio"], + efficiency_dispatch=costs.at["central water pit discharger", "efficiency"], p_nom_extendable=True, standing_loss=1 - np.exp(-1 / 24 / tes_time_constant_days), - capital_cost=costs.at["central water pit storage", "fixed"], - lifetime=costs.at["central water pit storage", "lifetime"], - e_nom_extendable=True, - e_cyclic=True, + capital_cost=costs.at[ + "central water pit storage", "fixed" + ], + lifetime=costs.at[ + "central water pit storage", "lifetime" + ], + cyclic_state_of_charge=True, ) if options["resistive_heaters"]: @@ -4605,7 +4609,7 @@ def add_enhanced_geothermal(n, egs_potentials, egs_overlap, costs): snakemake = mock_snakemake( "prepare_sector_network", opts="", - clusters="38", + clusters="6", ll="vopt", sector_opts="", planning_horizons="2030", From 194761f349add91b29f6a17c9db73f0256c09213 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Tue, 17 Dec 2024 17:17:15 +0000 Subject: [PATCH 7/9] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- scripts/prepare_sector_network.py | 29 +++++++++++++++++------------ 1 file changed, 17 insertions(+), 12 deletions(-) diff --git a/scripts/prepare_sector_network.py b/scripts/prepare_sector_network.py index e576d4170..23322d9d4 100755 --- a/scripts/prepare_sector_network.py +++ b/scripts/prepare_sector_network.py @@ -2277,13 +2277,16 @@ def add_heat( bus=nodes + f" {heat_system} heat", carrier=f"{heat_system} water tanks", efficiency_store=costs.at[ - heat_system.central_or_decentral + " water tank charger", "efficiency" + heat_system.central_or_decentral + " water tank charger", + "efficiency", ], max_hours=costs.at[ - heat_system.central_or_decentral + " water tank storage", "energy to power ratio" + heat_system.central_or_decentral + " water tank storage", + "energy to power ratio", ], efficiency_dispatch=costs.at[ - heat_system.central_or_decentral + " water tank discharger", "efficiency" + heat_system.central_or_decentral + " water tank discharger", + "efficiency", ], p_nom_extendable=True, standing_loss=1 - np.exp(-1 / 24 / tes_time_constant_days), @@ -2305,17 +2308,19 @@ def add_heat( nodes + f" {heat_system} water pits", bus=nodes + f" {heat_system} heat", carrier=f"{heat_system} water pits", - efficiency_store=costs.at["central water pit charger", "efficiency"], - max_hours=costs.at["central water pit storage", "energy to power ratio"], - efficiency_dispatch=costs.at["central water pit discharger", "efficiency"], - p_nom_extendable=True, - standing_loss=1 - np.exp(-1 / 24 / tes_time_constant_days), - capital_cost=costs.at[ - "central water pit storage", "fixed" + efficiency_store=costs.at[ + "central water pit charger", "efficiency" + ], + max_hours=costs.at[ + "central water pit storage", "energy to power ratio" ], - lifetime=costs.at[ - "central water pit storage", "lifetime" + efficiency_dispatch=costs.at[ + "central water pit discharger", "efficiency" ], + p_nom_extendable=True, + standing_loss=1 - np.exp(-1 / 24 / tes_time_constant_days), + capital_cost=costs.at["central water pit storage", "fixed"], + lifetime=costs.at["central water pit storage", "lifetime"], cyclic_state_of_charge=True, ) From ce80d90bd92d41e481348fdf23a7ae427190d530 Mon Sep 17 00:00:00 2001 From: Tom Kaehler Date: Tue, 17 Dec 2024 18:26:01 +0100 Subject: [PATCH 8/9] small change of code --- scripts/prepare_sector_network.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/prepare_sector_network.py b/scripts/prepare_sector_network.py index 23322d9d4..d9cc57a2d 100755 --- a/scripts/prepare_sector_network.py +++ b/scripts/prepare_sector_network.py @@ -4614,7 +4614,7 @@ def add_enhanced_geothermal(n, egs_potentials, egs_overlap, costs): snakemake = mock_snakemake( "prepare_sector_network", opts="", - clusters="6", + clusters="38", ll="vopt", sector_opts="", planning_horizons="2030", From c8b7d903999b78c81fbe137a039f15dcb4c8e97c Mon Sep 17 00:00:00 2001 From: Tom Kaehler Date: Tue, 17 Dec 2024 18:29:54 +0100 Subject: [PATCH 9/9] small changes at docs/release_notes --- doc/release_notes.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/release_notes.rst b/doc/release_notes.rst index e8d23dec1..d13a85795 100644 --- a/doc/release_notes.rst +++ b/doc/release_notes.rst @@ -11,7 +11,7 @@ Release Notes Upcoming Release ================ -* Added PTES and transitioned stores to storage units to implement an energy-to-power ratio in ``prepare_sector_network``. +* Added PTES and transitioned LTES stores to storage units to implement an energy-to-power ratio in ``prepare_sector_network``. * Feature: Introduce geothermal district heating (direct utilisation and heat pumps). Potentials are based on `Manz et al. 2024: Spatial analysis of renewable and excess heat potentials for climate-neutral district heating in Europe `.