diff --git a/config/config.yaml b/config/config.yaml index 52f57520..713449c9 100644 --- a/config/config.yaml +++ b/config/config.yaml @@ -4,7 +4,7 @@ # docs in https://pypsa-eur.readthedocs.io/en/latest/configuration.html#run run: - prefix: 20241120-correct-wind + prefix: 20241121-fix-missing-gas-capa name: # - CurrentPolicies @@ -655,10 +655,11 @@ co2_price_add_on_fossils: 2020: 25 2025: 60 -must_run_biogas: - enable: false - p_min_pu: 0.6 - regions: ['DE'] +must_run: + 2020: + DE: + lignite: 0.4 + # biogas: 0.6 transmission_projects: new_link_capacity: keep #keep or zero @@ -672,3 +673,9 @@ onshore_nep_force: offshore_nep_force: cutin_year: 2025 cutout_year: 2030 + +scale_capacity: + 2020: + DE: + CCGT: 10000 + "urban central gas CHP": 22000 diff --git a/workflow/Snakefile b/workflow/Snakefile index 78a3d664..918b2a59 100644 --- a/workflow/Snakefile +++ b/workflow/Snakefile @@ -235,7 +235,7 @@ rule modify_prenetwork: "sector", "H2_retrofit_capacity_per_CH4" ), transmission_costs=config_provider("costs", "transmission"), - biogas_must_run=config_provider("must_run_biogas"), + must_run=config_provider("must_run"), clustering=config_provider("clustering", "temporal", "resolution_sector"), H2_plants=config_provider("electricity", "H2_plants_DE"), land_transport_electric_share=config_provider( @@ -249,6 +249,7 @@ rule modify_prenetwork: shipping_oil_efficiency=config_provider("sector", "shipping_oil_efficiency"), shipping_methanol_share=config_provider("sector", "shipping_methanol_share"), mwh_meoh_per_tco2=config_provider("sector", "MWh_MeOH_per_tCO2"), + scale_capacity=config_provider("scale_capacity"), input: costs_modifications="ariadne-data/costs_{planning_horizons}-modifications.csv", network=RESULTS diff --git a/workflow/scripts/modify_cost_data.py b/workflow/scripts/modify_cost_data.py index c31df697..deb9c16a 100644 --- a/workflow/scripts/modify_cost_data.py +++ b/workflow/scripts/modify_cost_data.py @@ -146,4 +146,10 @@ 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"]}." + ) + costs.to_csv(snakemake.output[0]) diff --git a/workflow/scripts/modify_prenetwork.py b/workflow/scripts/modify_prenetwork.py index 0a12aec5..7e491a64 100644 --- a/workflow/scripts/modify_prenetwork.py +++ b/workflow/scripts/modify_prenetwork.py @@ -768,17 +768,23 @@ def transmission_costs_from_modified_cost_data(n, costs, transmission): n.links.loc[dc_b, "overnight_cost"] = overnight_cost -def must_run_biogas(n, p_min_pu, regions): +def must_run(n, params): """ - Set p_min_pu for biogas generators to the specified value. + Set p_min_pu for links to the specified value. """ - logger.info( - f"Must-run condition enabled: Setting p_min_pu = {p_min_pu} for biogas generators." - ) - links_i = n.links[ - (n.links.carrier == "biogas") & (n.links.bus0.str.startswith(tuple(regions))) - ].index - n.links.loc[links_i, "p_min_pu"] = p_min_pu + investment_year = int(snakemake.wildcards.planning_horizons) + if investment_year in params.keys(): + for region in params[investment_year].keys(): + for carrier in params[investment_year][region].keys(): + p_min_pu = params[investment_year][region][carrier] + logger.info( + f"Must-run condition enabled: Setting p_min_pu = {p_min_pu} for {carrier} in year {investment_year} and region {region}." + ) + + links_i = n.links[ + (n.links.carrier == carrier) & n.links.index.str.contains(region) + ].index + n.links.loc[links_i, "p_min_pu"] = p_min_pu def aladin_mobility_demand(n): @@ -1168,6 +1174,63 @@ def drop_duplicate_transmission_projects(n): n.remove("Line", to_drop) +def scale_capacity(n, scaling): + """ + Scale the output capacity of energy system links based on predefined scaling limits. + + Parameters: + - n: The network/model object representing the energy system. + - scaling: A dictionary with scaling limits structured as + {year: {region: {carrier: limit}}}. + """ + investment_year = int(snakemake.wildcards.planning_horizons) + if investment_year in scaling.keys(): + for region in scaling[investment_year].keys(): + for carrier in scaling[investment_year][region].keys(): + limit = scaling[investment_year][region][carrier] + logger.info( + f"Scaling output capacity (bus1) of {carrier} in region {region} to {limit} MW" + ) + + links_i = n.links[ + (n.links.carrier == carrier) & n.links.index.str.contains(region) + ].index + + installed_cap = n.links.loc[links_i].eval("p_nom * efficiency").sum() + if installed_cap == 0: + logger.warning( + f"No installed capacity for {carrier} in region {region}. Skipping adjustment." + ) + continue + + diff_cap = limit - installed_cap + avg_efficiency = n.links.loc[links_i, "efficiency"].mean() + if avg_efficiency == 0 or np.isnan(avg_efficiency): + logger.warning( + f"Invalid average efficiency for {carrier} in region {region}. Skipping adjustment." + ) + continue + + diff_cap_0 = diff_cap / avg_efficiency + p_nom_sum = n.links.loc[links_i, "p_nom"].sum() + if p_nom_sum == 0: + logger.warning( + f"Zero total p_nom for {carrier} in region {region}. Skipping adjustment." + ) + continue + + scaling_factors = n.links.loc[links_i].eval("p_nom / @p_nom_sum") + n.links.loc[links_i, "p_nom"] += scaling_factors * diff_cap_0 + + links_i_current = n.links.loc[links_i][ + (n.links.loc[links_i].p_nom_min != 0) + & n.links.loc[links_i].p_nom_extendable + ].index + n.links.loc[links_i_current, "p_nom_min"] = n.links.loc[ + links_i_current, "p_nom" + ] + + if __name__ == "__main__": if "snakemake" not in globals(): import os @@ -1184,7 +1247,7 @@ def drop_duplicate_transmission_projects(n): opts="", ll="vopt", sector_opts="none", - planning_horizons="2025", + planning_horizons="2020", run="KN2045_Bal_v4", ) @@ -1241,12 +1304,8 @@ def drop_duplicate_transmission_projects(n): snakemake.params.transmission_costs, ) - if snakemake.params.biogas_must_run["enable"]: - must_run_biogas( - n, - snakemake.params.biogas_must_run["p_min_pu"], - snakemake.params.biogas_must_run["regions"], - ) + if snakemake.params.must_run is not None: + must_run(n, snakemake.params.must_run) if snakemake.params.H2_plants["enable"]: if snakemake.params.H2_plants["start"] <= int( @@ -1266,4 +1325,6 @@ def drop_duplicate_transmission_projects(n): force_connection_nep_offshore(n, current_year) + scale_capacity(n, snakemake.params.scale_capacity) + n.export_to_netcdf(snakemake.output.network)