From f38681f13462187dc83eb78c093bd0ccfa5899fb Mon Sep 17 00:00:00 2001 From: Tom Brown Date: Mon, 29 Jan 2024 10:02:05 +0100 Subject: [PATCH 1/6] correctly source the existing heating technologies for buildings The source URL has changed. It represents the year 2012 and is only for buildings, not district heating. So the capacities for urban central are now set to zero from this source. --- doc/configtables/licenses-sector.csv | 2 +- scripts/add_existing_baseyear.py | 12 +++------ .../build_existing_heating_distribution.py | 27 +++++++++++-------- 3 files changed, 20 insertions(+), 21 deletions(-) diff --git a/doc/configtables/licenses-sector.csv b/doc/configtables/licenses-sector.csv index a297b2f6..7f20b5a6 100644 --- a/doc/configtables/licenses-sector.csv +++ b/doc/configtables/licenses-sector.csv @@ -10,7 +10,7 @@ BASt emobility statistics,emobility/,unknown,http://www.bast.de/DE/Verkehrstechn BDEW heating profile,heat_load_profile_BDEW.csv,unknown,https://github.com/oemof/demandlib heating profiles for Aarhus,heat_load_profile_DK_AdamJensen.csv,unknown,Adam Jensen MA thesis at Aarhus University co2 budgets,co2_budget.csv,CC BY 4.0,https://arxiv.org/abs/2004.11009 -existing heating potentials,existing_infrastructure/existing_heating_raw.csv,unknown,https://ec.europa.eu/energy/studies/mapping-and-analyses-current-and-future-2020-2030-heatingcooling-fuel-deployment_en?redir=1 +existing heating potentials,existing_infrastructure/existing_heating_raw.csv,unknown,https://energy.ec.europa.eu/publications/mapping-and-analyses-current-and-future-2020-2030-heatingcooling-fuel-deployment-fossilrenewables-1_en IRENA existing VRE capacities,existing_infrastructure/{solar|onwind|offwind}_capcity_IRENA.csv,unknown,https://www.irena.org/Statistics/Download-Data USGS ammonia production,myb1-2017-nitro.xls,unknown,https://www.usgs.gov/centers/nmic/nitrogen-statistics-and-information hydrogen salt cavern potentials,h2_salt_caverns_GWh_per_sqkm.geojson,CC BY 4.0,https://doi.org/10.1016/j.ijhydene.2019.12.161 https://doi.org/10.20944/preprints201910.0187.v1 diff --git a/scripts/add_existing_baseyear.py b/scripts/add_existing_baseyear.py index 4f476b4d..3934079e 100644 --- a/scripts/add_existing_baseyear.py +++ b/scripts/add_existing_baseyear.py @@ -402,16 +402,10 @@ def add_heating_capacities_installed_before_baseyear( """ logger.debug(f"Adding heating capacities installed before {baseyear}") - # 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://ec.europa.eu/energy/studies/mapping-and-analyses-current-and-future-2020-2030-heatingcooling-fuel-deployment_en?redir=1 - # file: "WP2_DataAnnex_1_BuildingTechs_ForPublication_201603.xls" -> "existing_heating_raw.csv". - # TODO start from original file + existing_heating = pd.read_csv(snakemake.input.existing_heating_distribution, + header=[0,1], + index_col=0) - existing_heating = pd.read_csv( - snakemake.input.existing_heating_distribution, header=[0, 1], index_col=0 - ) techs = existing_heating.columns.get_level_values(1).unique() diff --git a/scripts/build_existing_heating_distribution.py b/scripts/build_existing_heating_distribution.py index 67993c29..12bfd4da 100644 --- a/scripts/build_existing_heating_distribution.py +++ b/scripts/build_existing_heating_distribution.py @@ -16,9 +16,17 @@ cc = coco.CountryConverter() def build_existing_heating(): # retrieve existing heating capacities - existing_heating = pd.read_csv( - snakemake.input.existing_heating, index_col=0, header=0 - ) + # 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 + # TODO start from original file + + existing_heating = pd.read_csv(snakemake.input.existing_heating, + index_col=0, + header=0) # data for Albania, Montenegro and Macedonia not included in database existing_heating.loc["Albania"] = np.nan @@ -67,17 +75,14 @@ def build_existing_heating(): nodal_sectoral_totals.sum(axis=1), axis=0 ) - nodal_heat_name_fraction = pd.DataFrame(dtype=float) + nodal_heat_name_fraction = pd.DataFrame(index=district_heat_info.index, + dtype=float) - nodal_heat_name_fraction["urban central"] = dist_fraction + nodal_heat_name_fraction["urban central"] = 0. for sector in sectors: - nodal_heat_name_fraction[f"{sector} rural"] = nodal_sectoral_fraction[ - sector - ] * (1 - urban_fraction) - nodal_heat_name_fraction[f"{sector} urban decentral"] = nodal_sectoral_fraction[ - sector - ] * (urban_fraction - dist_fraction) + nodal_heat_name_fraction[f"{sector} rural"] = nodal_sectoral_fraction[sector]*(1 - urban_fraction) + nodal_heat_name_fraction[f"{sector} urban decentral"] = nodal_sectoral_fraction[sector]*urban_fraction nodal_heat_name_tech = pd.concat( { From d7f3f16a9bf5a8228b7d28157f292260c3aaff40 Mon Sep 17 00:00:00 2001 From: Fabian Neumann Date: Wed, 7 Feb 2024 17:15:08 +0100 Subject: [PATCH 2/6] autoformat --- scripts/add_existing_baseyear.py | 7 +++---- .../build_existing_heating_distribution.py | 19 +++++++++++-------- 2 files changed, 14 insertions(+), 12 deletions(-) diff --git a/scripts/add_existing_baseyear.py b/scripts/add_existing_baseyear.py index 3934079e..e8683e16 100644 --- a/scripts/add_existing_baseyear.py +++ b/scripts/add_existing_baseyear.py @@ -402,10 +402,9 @@ def add_heating_capacities_installed_before_baseyear( """ logger.debug(f"Adding heating capacities installed before {baseyear}") - existing_heating = pd.read_csv(snakemake.input.existing_heating_distribution, - header=[0,1], - index_col=0) - + existing_heating = pd.read_csv( + snakemake.input.existing_heating_distribution, header=[0, 1], index_col=0 + ) techs = existing_heating.columns.get_level_values(1).unique() diff --git a/scripts/build_existing_heating_distribution.py b/scripts/build_existing_heating_distribution.py index 12bfd4da..78518597 100644 --- a/scripts/build_existing_heating_distribution.py +++ b/scripts/build_existing_heating_distribution.py @@ -24,9 +24,9 @@ def build_existing_heating(): # 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(snakemake.input.existing_heating, - index_col=0, - header=0) + existing_heating = pd.read_csv( + snakemake.input.existing_heating, index_col=0, header=0 + ) # data for Albania, Montenegro and Macedonia not included in database existing_heating.loc["Albania"] = np.nan @@ -75,14 +75,17 @@ def build_existing_heating(): nodal_sectoral_totals.sum(axis=1), axis=0 ) - nodal_heat_name_fraction = pd.DataFrame(index=district_heat_info.index, - dtype=float) + nodal_heat_name_fraction = pd.DataFrame(index=district_heat_info.index, dtype=float) - nodal_heat_name_fraction["urban central"] = 0. + nodal_heat_name_fraction["urban central"] = 0.0 for sector in sectors: - nodal_heat_name_fraction[f"{sector} rural"] = nodal_sectoral_fraction[sector]*(1 - urban_fraction) - nodal_heat_name_fraction[f"{sector} urban decentral"] = nodal_sectoral_fraction[sector]*urban_fraction + nodal_heat_name_fraction[f"{sector} rural"] = nodal_sectoral_fraction[ + sector + ] * (1 - urban_fraction) + nodal_heat_name_fraction[f"{sector} urban decentral"] = ( + nodal_sectoral_fraction[sector] * urban_fraction + ) nodal_heat_name_tech = pd.concat( { From 6d94439bbb9b650dc6d3c8d1da3064ee5affd813 Mon Sep 17 00:00:00 2001 From: Tom Brown Date: Mon, 29 Jan 2024 11:25:09 +0100 Subject: [PATCH 3/6] for existing heating use new default_heating_lifetime This is because old costs default (25) is longer than all heating technologies (20). Script was distributing across 25 years, then throwing out boilers older than 20 years, an inconsistent behaviour. Now existing boilers are smoothly distributed across 20 years. --- config/config.default.yaml | 1 + scripts/add_existing_baseyear.py | 10 ++-------- 2 files changed, 3 insertions(+), 8 deletions(-) diff --git a/config/config.default.yaml b/config/config.default.yaml index 93b0568e..633bed70 100644 --- a/config/config.default.yaml +++ b/config/config.default.yaml @@ -357,6 +357,7 @@ existing_capacities: grouping_years_power: [1980, 1985, 1990, 1995, 2000, 2005, 2010, 2015, 2020, 2025, 2030] grouping_years_heat: [1980, 1985, 1990, 1995, 2000, 2005, 2010, 2015, 2019] # these should not extend 2020 threshold_capacity: 10 + default_heating_lifetime: 20 conventional_carriers: - lignite - coal diff --git a/scripts/add_existing_baseyear.py b/scripts/add_existing_baseyear.py index e8683e16..13527fad 100644 --- a/scripts/add_existing_baseyear.py +++ b/scripts/add_existing_baseyear.py @@ -429,7 +429,7 @@ def add_heating_capacities_installed_before_baseyear( if int(grouping_year) + default_lifetime <= int(baseyear): continue - # installation is assumed to be linear for the past 25 years (default lifetime) + # installation is assumed to be linear for the past default_lifetime years ratio = (int(grouping_year) - int(grouping_years[i - 1])) / default_lifetime n.madd( @@ -536,12 +536,6 @@ def add_heating_capacities_installed_before_baseyear( ], ) - # drop assets which are at the end of their lifetime - links_i = n.links[(n.links.build_year + n.links.lifetime <= baseyear)].index - logger.info("Removing following links because at end of their lifetime:") - logger.info(links_i) - n.mremove("Link", links_i) - if __name__ == "__main__": if "snakemake" not in globals(): @@ -598,7 +592,7 @@ if __name__ == "__main__": .to_pandas() .reindex(index=n.snapshots) ) - default_lifetime = snakemake.params.costs["fill_values"]["lifetime"] + default_lifetime = snakemake.params.existing_capacities["default_heating_lifetime"] add_heating_capacities_installed_before_baseyear( n, baseyear, From 92d00a0c83d6ec912d7c34eeffea8da67bee18ea Mon Sep 17 00:00:00 2001 From: Tom Brown Date: Mon, 29 Jan 2024 12:22:54 +0100 Subject: [PATCH 4/6] add new default to overdimension heating in individual buildings This allows them to cover heat demand peaks e.g. 10% higher than those in the data. The disadvantage of manipulating the costs is that the capacity is then not quite right. This way at least the costs are right. Doing it properly would require introducing artificial peaks, but this creates new problems (e.g. what is going on with wind/solar/other demand). --- config/config.default.yaml | 1 + scripts/add_existing_baseyear.py | 4 +++- scripts/prepare_sector_network.py | 22 ++++++++++++++++------ 3 files changed, 20 insertions(+), 7 deletions(-) diff --git a/config/config.default.yaml b/config/config.default.yaml index 633bed70..b24f2ceb 100644 --- a/config/config.default.yaml +++ b/config/config.default.yaml @@ -483,6 +483,7 @@ sector: resistive_heaters: true oil_boilers: false biomass_boiler: true + overdimension_individual_heating: 1.1 #to cover demand peaks bigger than data chp: true micro_chp: false solar_thermal: true diff --git a/scripts/add_existing_baseyear.py b/scripts/add_existing_baseyear.py index 13527fad..c0d37a5b 100644 --- a/scripts/add_existing_baseyear.py +++ b/scripts/add_existing_baseyear.py @@ -592,7 +592,9 @@ if __name__ == "__main__": .to_pandas() .reindex(index=n.snapshots) ) - default_lifetime = snakemake.params.existing_capacities["default_heating_lifetime"] + default_lifetime = snakemake.params.existing_capacities[ + "default_heating_lifetime" + ] add_heating_capacities_installed_before_baseyear( n, baseyear, diff --git a/scripts/prepare_sector_network.py b/scripts/prepare_sector_network.py index 394a67f8..eb24d9cf 100755 --- a/scripts/prepare_sector_network.py +++ b/scripts/prepare_sector_network.py @@ -1680,6 +1680,8 @@ def add_heat(n, costs): heat_demand = build_heat_demand(n) + overdim_factor = options["overdimension_individual_heating"] + district_heat_info = pd.read_csv(snakemake.input.district_heat_share, index_col=0) dist_fraction = district_heat_info["district fraction of node"] urban_fraction = district_heat_info["urban fraction"] @@ -1814,7 +1816,8 @@ def add_heat(n, costs): carrier=f"{name} {heat_pump_type} heat pump", efficiency=efficiency, capital_cost=costs.at[costs_name, "efficiency"] - * costs.at[costs_name, "fixed"], + * costs.at[costs_name, "fixed"] + * overdim_factor, p_nom_extendable=True, lifetime=costs.at[costs_name, "lifetime"], ) @@ -1883,7 +1886,9 @@ def add_heat(n, costs): bus1=nodes + f" {name} heat", carrier=name + " resistive heater", efficiency=costs.at[key, "efficiency"], - capital_cost=costs.at[key, "efficiency"] * costs.at[key, "fixed"], + capital_cost=costs.at[key, "efficiency"] + * costs.at[key, "fixed"] + * overdim_factor, p_nom_extendable=True, lifetime=costs.at[key, "lifetime"], ) @@ -1901,7 +1906,9 @@ def add_heat(n, costs): carrier=name + " gas boiler", efficiency=costs.at[key, "efficiency"], efficiency2=costs.at["gas", "CO2 intensity"], - capital_cost=costs.at[key, "efficiency"] * costs.at[key, "fixed"], + capital_cost=costs.at[key, "efficiency"] + * costs.at[key, "fixed"] + * overdim_factor, lifetime=costs.at[key, "lifetime"], ) @@ -1915,7 +1922,8 @@ def add_heat(n, costs): bus=nodes + f" {name} heat", carrier=name + " solar thermal", p_nom_extendable=True, - capital_cost=costs.at[name_type + " solar thermal", "fixed"], + capital_cost=costs.at[name_type + " solar thermal", "fixed"] + * overdim_factor, p_max_pu=solar_thermal[nodes], lifetime=costs.at[name_type + " solar thermal", "lifetime"], ) @@ -2348,7 +2356,8 @@ def add_biomass(n, costs): carrier=name + " biomass boiler", efficiency=costs.at["biomass boiler", "efficiency"], capital_cost=costs.at["biomass boiler", "efficiency"] - * costs.at["biomass boiler", "fixed"], + * costs.at["biomass boiler", "fixed"] + * options["overdimension_individual_heating"], marginal_cost=costs.at["biomass boiler", "pelletizing cost"], lifetime=costs.at["biomass boiler", "lifetime"], ) @@ -2806,7 +2815,8 @@ def add_industry(n, costs): efficiency=costs.at["decentral oil boiler", "efficiency"], efficiency2=costs.at["oil", "CO2 intensity"], capital_cost=costs.at["decentral oil boiler", "efficiency"] - * costs.at["decentral oil boiler", "fixed"], + * costs.at["decentral oil boiler", "fixed"] + * options["overdimension_individual_heating"], lifetime=costs.at["decentral oil boiler", "lifetime"], ) From 7ad5dcc3eea0bed4bcc66acae6f8a6c3bb5ed5bd Mon Sep 17 00:00:00 2001 From: Fabian Neumann Date: Wed, 7 Feb 2024 17:25:09 +0100 Subject: [PATCH 5/6] add release notes and documentation --- doc/configtables/existing_capacities.csv | 1 + doc/configtables/sector.csv | 1 + doc/release_notes.rst | 13 +++++++++++++ 3 files changed, 15 insertions(+) diff --git a/doc/configtables/existing_capacities.csv b/doc/configtables/existing_capacities.csv index 87519193..eacae35b 100644 --- a/doc/configtables/existing_capacities.csv +++ b/doc/configtables/existing_capacities.csv @@ -3,4 +3,5 @@ grouping_years_power ,--,A list of years,Intervals to group existing capacities grouping_years_heat ,--,A list of years below 2020,Intervals to group existing capacities for heat threshold_capacity ,MW,float,Capacities generators and links of below threshold are removed during add_existing_capacities +default_heating_lifetime ,years,int,Default lifetime for heating technologies conventional_carriers ,--,"Any subset of {uranium, coal, lignite, oil} ",List of conventional power plants to include in the sectoral network diff --git a/doc/configtables/sector.csv b/doc/configtables/sector.csv index 338cf34e..d8cc3288 100644 --- a/doc/configtables/sector.csv +++ b/doc/configtables/sector.csv @@ -66,6 +66,7 @@ boilers,--,"{true, false}",Add option for transforming gas into heat using gas b resistive_heaters,--,"{true, false}",Add option for transforming electricity into heat using resistive heaters (independently from gas boilers) oil_boilers,--,"{true, false}",Add option for transforming oil into heat using boilers biomass_boiler,--,"{true, false}",Add option for transforming biomass into heat using boilers +overdimension_individual_heating,--,"float",Add option for overdimensioning individual heating systems by a certain factor. This allows them to cover heat demand peaks e.g. 10% higher than those in the data with a setting of 1.1. chp,--,"{true, false}",Add option for using Combined Heat and Power (CHP) micro_chp,--,"{true, false}",Add option for using Combined Heat and Power (CHP) for decentral areas. solar_thermal,--,"{true, false}",Add option for using solar thermal to generate heat. diff --git a/doc/release_notes.rst b/doc/release_notes.rst index b1511dd7..fea45d74 100644 --- a/doc/release_notes.rst +++ b/doc/release_notes.rst @@ -10,6 +10,19 @@ Release Notes Upcoming Release ================ +* Add new default to overdimension heating in individual buildings. This allows + them to cover heat demand peaks e.g. 10% higher than those in the data. The + disadvantage of manipulating the costs is that the capacity is then not quite + right. This way at least the costs are right. + +* Add option to specify to set a default heating lifetime for existing heating + (``existing_capacities: default_heating_lifetime:``). + +* Correctly source the existing heating technologies for buildings since the + source URL has changed. It represents the year 2012 and is only for + buildings, not district heating. So the capacities for urban central are now + set to zero from this source. + * Remove long-deprecated function ``attach_extendable_generators`` in :mod:`add_electricity`. * The filtering of power plants in the ``config.default.yaml`` has been updated regarding phased-out power plants in 2023. From 3e411fe79d9a8d707053ae9b37fd6c17cd81ee0d Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Wed, 7 Feb 2024 16:27:18 +0000 Subject: [PATCH 6/6] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- 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 fea45d74..ee7bd64b 100644 --- a/doc/release_notes.rst +++ b/doc/release_notes.rst @@ -16,7 +16,7 @@ Upcoming Release right. This way at least the costs are right. * Add option to specify to set a default heating lifetime for existing heating - (``existing_capacities: default_heating_lifetime:``). + (``existing_capacities: default_heating_lifetime:``). * Correctly source the existing heating technologies for buildings since the source URL has changed. It represents the year 2012 and is only for