Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add function to scale carrier capacity #285

Merged
merged 10 commits into from
Nov 22, 2024
17 changes: 12 additions & 5 deletions config/config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand All @@ -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
3 changes: 2 additions & 1 deletion workflow/Snakefile
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand All @@ -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
Expand Down
6 changes: 6 additions & 0 deletions workflow/scripts/modify_cost_data.py
Original file line number Diff line number Diff line change
Expand Up @@ -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])
93 changes: 77 additions & 16 deletions workflow/scripts/modify_prenetwork.py
Original file line number Diff line number Diff line change
Expand Up @@ -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):
Expand Down Expand Up @@ -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][
lindnemi marked this conversation as resolved.
Show resolved Hide resolved
(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
Expand All @@ -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",
)

Expand Down Expand Up @@ -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(
Expand All @@ -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)