From 75f3c1e584a4048be31ebc538d5a4c77d58dd204 Mon Sep 17 00:00:00 2001 From: cpschau Date: Thu, 12 Dec 2024 16:58:24 +0100 Subject: [PATCH 1/3] fix: myopic geothermal district heating --- scripts/add_brownfield.py | 1 + scripts/build_existing_heating_distribution.py | 5 ++++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/scripts/add_brownfield.py b/scripts/add_brownfield.py index 1e53adca0..dea819698 100644 --- a/scripts/add_brownfield.py +++ b/scripts/add_brownfield.py @@ -244,6 +244,7 @@ def update_heat_pump_efficiency(n: pypsa.Network, n_p: pypsa.Network, year: int) # get names of heat pumps in previous iteration heat_pump_idx_previous_iteration = n_p.links.index[ n_p.links.index.str.contains("heat pump") + & n_p.links.index.str[:-4].isin(n.links_t.efficiency.columns.str[:-4]) ] # construct names of same-technology heat pumps in the current iteration corresponding_idx_this_iteration = heat_pump_idx_previous_iteration.str[:-4] + str( diff --git a/scripts/build_existing_heating_distribution.py b/scripts/build_existing_heating_distribution.py index 2cc43bafb..cbcabf46b 100644 --- a/scripts/build_existing_heating_distribution.py +++ b/scripts/build_existing_heating_distribution.py @@ -143,7 +143,10 @@ def build_existing_heating(): ] += nodal_heat_name_tech[(f"{sector} rural", "air heat pump")] nodal_heat_name_tech[(f"{sector} rural", "air heat pump")] = 0.0 - nodal_heat_name_tech[("urban central", "ground heat pump")] = 0.0 + for heat_pump_source in snakemake.params.sector["heat_pump_sources"][ + "urban central" + ]: + nodal_heat_name_tech[("urban central", f"{heat_pump_source} heat pump")] = 0.0 nodal_heat_name_tech.to_csv(snakemake.output.existing_heating_distribution) From 429d226cc764d89e78b266ce995ee260a68585ef Mon Sep 17 00:00:00 2001 From: cpschau Date: Mon, 6 Jan 2025 12:16:33 +0100 Subject: [PATCH 2/3] docs: add comments, docstring, and release note; style: strip year variable instead of string slicing --- doc/release_notes.rst | 2 ++ scripts/add_brownfield.py | 16 ++++++----- .../build_existing_heating_distribution.py | 27 +++++++++++++------ 3 files changed, 31 insertions(+), 14 deletions(-) diff --git a/doc/release_notes.rst b/doc/release_notes.rst index 749b96b0b..6a08f365c 100644 --- a/doc/release_notes.rst +++ b/doc/release_notes.rst @@ -11,6 +11,8 @@ Release Notes Upcoming Release ================ +* Bugfix: Adjusted existing heating data in ``build_existing_heating_distribution`` and the indexing of existing heat pumps for the COP correction in ``add_brownfield`` to make the myopic code work with the geothermal district heating feature. + * 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). diff --git a/scripts/add_brownfield.py b/scripts/add_brownfield.py index dea819698..993ecd787 100644 --- a/scripts/add_brownfield.py +++ b/scripts/add_brownfield.py @@ -241,10 +241,14 @@ def update_heat_pump_efficiency(n: pypsa.Network, n_p: pypsa.Network, year: int) This function updates the efficiency in place and does not return a value. """ - # get names of heat pumps in previous iteration + # get names of heat pumps in previous iteration that cannot be replaced by direct utilisation in this iteration heat_pump_idx_previous_iteration = n_p.links.index[ n_p.links.index.str.contains("heat pump") - & n_p.links.index.str[:-4].isin(n.links_t.efficiency.columns.str[:-4]) + & n_p.links.index.str[:-4].isin( + n.links_t.efficiency.columns.str.rstrip( # sources that can be directly used are no longer represented by heat pumps in the dynamic efficiency dataframe + str(year) + ) + ) ] # construct names of same-technology heat pumps in the current iteration corresponding_idx_this_iteration = heat_pump_idx_previous_iteration.str[:-4] + str( @@ -262,11 +266,11 @@ def update_heat_pump_efficiency(n: pypsa.Network, n_p: pypsa.Network, year: int) snakemake = mock_snakemake( "add_brownfield", - clusters="37", + clusters="39", opts="", - ll="v1.0", - sector_opts="168H-T-H-B-I-dist1", - planning_horizons=2030, + ll="vopt", + sector_opts="", + planning_horizons=2050, ) configure_logging(snakemake) diff --git a/scripts/build_existing_heating_distribution.py b/scripts/build_existing_heating_distribution.py index cbcabf46b..eafe467a6 100644 --- a/scripts/build_existing_heating_distribution.py +++ b/scripts/build_existing_heating_distribution.py @@ -47,14 +47,23 @@ def build_existing_heating(): - # retrieve existing heating capacities - - # Add existing heating capacities, data comes from the study - # "Mapping and analyses of the current and future (2020 - 2030) - # heating/cooling fuel deployment (fossil/renewables) " - # https://energy.ec.europa.eu/publications/mapping-and-analyses-current-and-future-2020-2030-heatingcooling-fuel-deployment-fossilrenewables-1_en - # file: "WP2_DataAnnex_1_BuildingTechs_ForPublication_201603.xls" -> "existing_heating_raw.csv". - # data is for buildings only (i.e. NOT district heating) and represents the year 2012 + """ + Retrieve and clean existing heating capacities for the myopic code. + Data comes from the study "Mapping and analyses of the current and + future (2020 - 2030) heating/cooling fuel deployment (fossil/renewables)". + + Source + ------ + https://energy.ec.europa.eu/publications/mapping-and-analyses-current-and-future-2020-2030-heatingcooling-fuel-deployment-fossilrenewables-1_en + + File + ---- + "WP2_DataAnnex_1_BuildingTechs_ForPublication_201603.xls" -> "existing_heating_raw.csv". + + Notes + ----- + Data is for buildings only (i.e. NOT district heating) and represents the year 2012. + """ # TODO start from original file existing_heating = pd.read_csv( @@ -143,6 +152,8 @@ def build_existing_heating(): ] += nodal_heat_name_tech[(f"{sector} rural", "air heat pump")] nodal_heat_name_tech[(f"{sector} rural", "air heat pump")] = 0.0 + # add large-scale heat pump sources as columns for district heating with 0 capacity + for heat_pump_source in snakemake.params.sector["heat_pump_sources"][ "urban central" ]: From 078a292cb9144a5db2a4b9c5312aa3f0b6569822 Mon Sep 17 00:00:00 2001 From: cpschau Date: Tue, 7 Jan 2025 13:58:04 +0100 Subject: [PATCH 3/3] change efficiency2 for heat pumps with 3 buses --- scripts/add_brownfield.py | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/scripts/add_brownfield.py b/scripts/add_brownfield.py index 993ecd787..c62d3ecc7 100644 --- a/scripts/add_brownfield.py +++ b/scripts/add_brownfield.py @@ -259,6 +259,17 @@ def update_heat_pump_efficiency(n: pypsa.Network, n_p: pypsa.Network, year: int) n.links_t["efficiency"].loc[:, corresponding_idx_this_iteration].values ) + # Change efficiency2 for heat pumps that use an explicitly modelled heat source + previous_iteration_columns = heat_pump_idx_previous_iteration.intersection( + n_p.links_t["efficiency2"].columns + ) + current_iteration_columns = corresponding_idx_this_iteration.intersection( + n.links_t["efficiency2"].columns + ) + n_p.links_t["efficiency2"].loc[:, previous_iteration_columns] = ( + n.links_t["efficiency2"].loc[:, current_iteration_columns].values + ) + if __name__ == "__main__": if "snakemake" not in globals():