diff --git a/CHANGELOG.md b/CHANGELOG.md index c22e96d02..ae6725702 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,6 @@ # Changelog +- Longer lifetime (40 years) is only applied to existing gas CHPs, not new ones. Added a new config entry `existing_capacities:fill_value_gas_chp_lifetime` +- Bugfix: gas CHPs are extendable again - Simplified scenarion definition and made `Mix` the default scenario - 0.3: workflow is all public now, no longer requires credentials to internal data - Allowing myopic optimization until 2050 diff --git a/config/config.de.yaml b/config/config.de.yaml index 97aaa3327..22bfcc13d 100644 --- a/config/config.de.yaml +++ b/config/config.de.yaml @@ -4,7 +4,7 @@ # docs in https://pypsa-eur.readthedocs.io/en/latest/configuration.html#run run: - prefix: 20250514_dhsubnodes + prefix: 20253006_fix_chp_lifetime name: # - ExPol - KN2045_Mix @@ -70,6 +70,7 @@ scenario: existing_capacities: grouping_years_power: [1920, 1950, 1955, 1960, 1965, 1970, 1975, 1980, 1985, 1990, 1995, 2000, 2005, 2010, 2015, 2020] grouping_years_heat: [1980, 1985, 1990, 1995, 2000, 2005, 2010, 2015, 2019] # heat grouping years >= baseyear will be ignored + fill_value_gas_chp_lifetime: 40 # if no explicit lifetime is given use 40 years. The number was chosen s.t. the existing capacities in 2020 match with statistics. # docs in https://pypsa-eur.readthedocs.io/en/latest/configuration.html#countries @@ -275,8 +276,6 @@ costs: # docs in https://pypsa-eur.readthedocs.io/en/latest/configuration.html#sector sector: - chp: - enable: false # This should only deactivate the CHPs from prepare_sector_network, not those from add_existing_baseyear v2g: false solar_thermal: false district_heating: diff --git a/scripts/add_existing_baseyear.py b/scripts/add_existing_baseyear.py index d10829868..b7917414f 100644 --- a/scripts/add_existing_baseyear.py +++ b/scripts/add_existing_baseyear.py @@ -510,6 +510,10 @@ def add_chp_plants(n, grouping_years, costs, baseyear): chp["lifetime"] = (chp.DateOut - chp["grouping_year"] + 1).fillna( snakemake.params.costs["fill_values"]["lifetime"] ) + chp.loc[chp.Fueltype == "gas", "lifetime"] = ( + chp.DateOut - chp["grouping_year"] + 1 + ).fillna(snakemake.params.existing_capacities["fill_value_gas_chp_lifetime"]) + chp = chp.loc[ chp.grouping_year + chp.lifetime > baseyear ] # in add_brownfield this is build_year + lifetime <= baseyear @@ -572,6 +576,12 @@ def add_chp_plants(n, grouping_years, costs, baseyear): aggfunc=lambda x: np.average(x, weights=mastr_chp.loc[x.index, "p_nom"]), ) + mastr_chp_lifetime = mastr_chp.pivot_table( + index=["grouping_year", "Fueltype"], + columns="bus", + values="lifetime", + aggfunc=lambda x: np.average(x, weights=mastr_chp.loc[x.index, "p_nom"]), + ) mastr_chp_p_nom = mastr_chp.pivot_table( index=["grouping_year", "Fueltype"], columns="bus", @@ -589,12 +599,13 @@ def add_chp_plants(n, grouping_years, costs, baseyear): # add everything as Link for grouping_year, generator in mastr_chp_p_nom.index: # capacity is the capacity in MW at each node for this - p_nom = mastr_chp_p_nom.loc[grouping_year, generator].dropna() + p_nom = mastr_chp_p_nom.loc[grouping_year, generator] threshold = snakemake.params.existing_capacities["threshold_capacity"] p_nom = p_nom[p_nom > threshold] efficiency_power = mastr_chp_efficiency_power.loc[grouping_year, generator] efficiency_heat = mastr_chp_efficiency_heat.loc[grouping_year, generator] + lifetime = mastr_chp_lifetime.loc[grouping_year, generator] for bus in p_nom.index: # check if link already exists and set p_nom_min and efficiency @@ -636,11 +647,11 @@ def add_chp_plants(n, grouping_years, costs, baseyear): overnight_cost=costs.at[key, "investment"] * costs.at[key, "efficiency"], marginal_cost=costs.at[key, "VOM"], - efficiency=efficiency_power.dropna().loc[bus], - efficiency2=efficiency_heat.dropna().loc[bus], + efficiency=efficiency_power.loc[bus], + efficiency2=efficiency_heat.loc[bus], efficiency3=costs.at[generator, "CO2 intensity"], build_year=grouping_year, - lifetime=costs.at[key, "lifetime"], + lifetime=lifetime.loc[bus], ) else: key = "central solid biomass CHP" @@ -661,7 +672,7 @@ def add_chp_plants(n, grouping_years, costs, baseyear): efficiency=efficiency_power.loc[bus], efficiency2=efficiency_heat.loc[bus], build_year=grouping_year, - lifetime=costs.at[key, "lifetime"], + lifetime=lifetime.loc[bus], ) # CHPs that are not from MaStR @@ -671,10 +682,17 @@ def add_chp_plants(n, grouping_years, costs, baseyear): values="Capacity", aggfunc="sum", ) + chp_nodal_lifetime = chp.pivot_table( + index=["grouping_year", "Fueltype"], + columns="bus", + values="lifetime", + aggfunc=lambda x: np.average(x, weights=chp.loc[x.index, "Capacity"]), + ) for grouping_year, generator in chp_nodal_p_nom.index: - p_nom = chp_nodal_p_nom.loc[grouping_year, generator].dropna() + p_nom = chp_nodal_p_nom.loc[grouping_year, generator] threshold = snakemake.params.existing_capacities["threshold_capacity"] p_nom = p_nom[p_nom > threshold] + lifetime = chp_nodal_lifetime.loc[grouping_year, generator] for bus in p_nom.index: # check if link already exists and set p_nom_min and efficiency @@ -726,7 +744,7 @@ def add_chp_plants(n, grouping_years, costs, baseyear): efficiency2=costs.at[key, "efficiency"] / costs.at[key, "c_b"], efficiency3=costs.at[generator, "CO2 intensity"], build_year=grouping_year, - lifetime=costs.at[key, "lifetime"], + lifetime=lifetime.loc[bus], ) else: key = "central solid biomass CHP" @@ -747,7 +765,7 @@ def add_chp_plants(n, grouping_years, costs, baseyear): efficiency=costs.at[key, "efficiency"], efficiency2=costs.at[key, "efficiency-heat"], build_year=grouping_year, - lifetime=costs.at[key, "lifetime"], + lifetime=lifetime.loc[bus], ) @@ -1085,9 +1103,8 @@ def add_heating_capacities_installed_before_baseyear( snakemake = mock_snakemake( "add_existing_baseyear", - configfiles=["config/test/config.dach.yaml"], - clusters="5", - ll="v1.5", + clusters="27", + ll="vopt", opts="", sector_opts="none", planning_horizons="2020", diff --git a/scripts/pypsa-de/modify_cost_data.py b/scripts/pypsa-de/modify_cost_data.py index d76c76012..822780789 100644 --- a/scripts/pypsa-de/modify_cost_data.py +++ b/scripts/pypsa-de/modify_cost_data.py @@ -140,12 +140,6 @@ def carbon_component_fossils(costs, co2_price): f"Scaling central water tank storage investment costs to KEA Technikkatalog: {costs.loc['central water tank storage', 'investment'].value} {costs.loc['central water tank storage', 'investment'].unit}." ) - # increase central gas CHP lifetime to 40 years - costs.at[("central gas CHP", "lifetime"), "value"] = 40 - logger.info( - f"Setting lifetime of central gas CHP to {costs.at[('central gas CHP', 'lifetime'), 'value']} {costs.at[('central gas CHP', 'lifetime'), 'unit']}." - ) - # decrease Fischer-Tropsch efficiency costs.at[("Fischer-Tropsch", "efficiency"), "value"] = ( 1 / costs.at[("Fischer-Tropsch", "hydrogen-input"), "value"]