Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
23bacad
improve gas price 2025
lindnemi Jan 12, 2026
0ec4aeb
limit ttes capacity before 2025, and no ptes availability before 2035
lindnemi Jan 12, 2026
d1849ce
improve emission data for 2025 and 2030
lindnemi Jan 12, 2026
e0030aa
improve RES capacity constraints for 2025 and 2030
lindnemi Jan 12, 2026
3370d84
allow a small amount of derivatives to be imported (mostly MeOH)
lindnemi Sep 1, 2025
006524e
decrease land_transport electric share towards pypsa-eur defaults
lindnemi Jan 14, 2026
dbbd64c
take more configs from config.default.yaml
lindnemi Jan 14, 2026
8df4e51
disable some config changes in pypsa-eur
lindnemi Jan 12, 2026
4a6444d
limit cross-border AC flows
lindnemi Jan 14, 2026
557dd4c
rename script
lindnemi Jan 14, 2026
b6bea90
add UBA industry data and processing, activate it in 2025; activate U…
lindnemi Jan 14, 2026
a71ee0a
improve uba mobility data pipeline and technology occurence definition
lindnemi Jan 14, 2026
caa10eb
Merge branch 'merge-upstream-26-01-05' into improvements-from-2030-study
lindnemi Jan 15, 2026
6a0aeee
national co2 budget: apply domestic factor for shipping
lindnemi Jan 15, 2026
e27d602
address pylint errors
lindnemi Jan 15, 2026
917dcf6
update assumptions for nonco2 in 2020 and 2025
lindnemi Jan 15, 2026
6bc8371
add an entry for Bus: to first_tech_occurence to avoid empty buses
lindnemi Jan 16, 2026
75e2dd3
fix custom_costs scaling
lindnemi Jan 29, 2026
0db8d83
avoid code duplication
lindnemi Jan 29, 2026
5471b7d
minor fix
lindnemi Jan 29, 2026
32b2d01
Merge branch 'main' into improvements-from-2030-study
lindnemi Jan 30, 2026
db2876c
add a little documentation of changes
lindnemi Jan 30, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 0 additions & 6 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -28,12 +28,6 @@ gurobi.log
# Allowlist a small subset under data/pypsa-de.
# Important: parent directories must be un-ignored for negations to take effect.
!/data/pypsa-de/
/data/pypsa-de/*
!/data/pypsa-de/custom_costs_nep_2021.csv
!/data/pypsa-de/custom_costs_nep_2023.csv
!/data/pypsa-de/offshore_connection_points.csv
!/data/pypsa-de/wasserstoff_kernnetz/
!/data/pypsa-de/wasserstoff_kernnetz/**
/cutouts
/tmp
doc/_build
Expand Down
8 changes: 8 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,12 @@
# Changelog
- added 2 delay years to the offshore NEP, s.t. capacity in 2030 is ~20GW
- improved script name `modify_industry_demand` -> `modify_industry_production`
- excluded international shipping from DE-specific CO2 emisisons
- improved land transport shares, and some RES constraints
- disabled ptes before 2035
- add function to limit cross border electricity flows, to comply with the 0.7 security margin and the 0.7 EU trade capacity goals
- Added an option to source industry energy demand from UBA MWMS (Projektionsbericht 2025) for the years 2025-2035
- renamed some scripts
- Upstream: PyPSA-Eur adopted a new functionality for overwriting costs. PyPSA-DE follows this convention now. As a consequence, the `costs:horizon:optimist/mean/pessimist` is no longer available. `Mean` will be provided exclusively from now on. Also, the costs assumptions for onwind turbines changed sligthly. Furthermore, "costs:NEP_year:2021/2023" is no longer available, instead one of the custom_cost files for these NEP years provided in the `data/pypsa-de` folder has to be specified.
- The `ariadne-data` folder has been moved and renamed to `data/pypsa-de` to conform with the syntax of `scripts/pypsa-de`
- Bugfix: Enforce stricter power import limit to avoid that import from one country compensate from exports to another
Expand Down
22 changes: 18 additions & 4 deletions Snakefile
Original file line number Diff line number Diff line change
Expand Up @@ -654,6 +654,13 @@ rule modify_prenetwork:
bev_charge_rate=config_provider("sector", "bev_charge_rate"),
bev_energy=config_provider("sector", "bev_energy"),
bev_dsm_availability=config_provider("sector", "bev_dsm_availability"),
uba_for_industry=config_provider("pypsa-de", "uba_for_industry", "enable"),
scale_industry_non_energy=config_provider(
"pypsa-de", "uba_for_industry", "scale_industry_non_energy"
),
limit_cross_border_flows_ac=config_provider(
"pypsa-de", "limit_cross_border_flows_ac"
),
input:
network=resources(
"networks/base_s_{clusters}_{opts}_{sector_opts}_{planning_horizons}_brownfield.nc"
Expand All @@ -675,13 +682,20 @@ rule modify_prenetwork:
industrial_demand=resources(
"industrial_energy_demand_base_s_{clusters}_{planning_horizons}.csv"
),
industrial_production_per_country_tomorrow=resources(
"industrial_production_per_country_tomorrow_{planning_horizons}-modified.csv"
),
industry_sector_ratios=resources(
"industry_sector_ratios_{planning_horizons}.csv"
),
pop_weighted_energy_totals=resources(
"pop_weighted_energy_totals_s_{clusters}.csv"
),
shipping_demand=resources("shipping_demand_s_{clusters}.csv"),
regions_onshore=resources("regions_onshore_base_s_{clusters}.geojson"),
regions_offshore=resources("regions_offshore_base_s_{clusters}.geojson"),
offshore_connection_points="data/pypsa-de/offshore_connection_points.csv",
new_industrial_energy_demand="data/pypsa-de/UBA_Projektionsbericht2025_Abbildung31_MWMS.csv",
output:
network=resources(
"networks/base_s_{clusters}_{opts}_{sector_opts}_{planning_horizons}_final.nc"
Expand All @@ -695,7 +709,7 @@ rule modify_prenetwork:
"scripts/pypsa-de/modify_prenetwork.py"


ruleorder: modify_industry_demand > build_industrial_production_per_country_tomorrow
ruleorder: modify_industry_production > build_industrial_production_per_country_tomorrow


rule modify_existing_heating:
Expand Down Expand Up @@ -740,7 +754,7 @@ rule build_existing_chp_de:
"scripts/pypsa-de/build_existing_chp_de.py"


rule modify_industry_demand:
rule modify_industry_production:
params:
reference_scenario=config_provider("pypsa-de", "reference_scenario"),
input:
Expand All @@ -755,9 +769,9 @@ rule modify_industry_demand:
resources:
mem_mb=1000,
log:
logs("modify_industry_demand_{planning_horizons}.log"),
logs("modify_industry_production_{planning_horizons}.log"),
script:
"scripts/pypsa-de/modify_industry_demand.py"
"scripts/pypsa-de/modify_industry_production.py"


rule build_wasserstoff_kernnetz:
Expand Down
108 changes: 49 additions & 59 deletions config/config.de.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: 20251017_improve_power_limits
prefix: 20260114_limit_cross_border_flows
name:
# - ExPol
- KN2045_Mix
Expand Down Expand Up @@ -36,7 +36,20 @@ pypsa-de:
reference_scenario: KN2045_Mix
region: Deutschland
ageb_for_mobility: true # In 2020 use AGEB data for final energy demand and KBA for vehicles
uba_for_mobility: false # For 2025–2035 use MWMS scenario from UBA Projektionsbericht 2025
uba_for_mobility: # Available for 2025–2035; uses MWMS scenario from UBA Projektionsbericht 2025
- 2025
uba_for_industry: # Available for 2025–2035; uses MWMS scenario from UBA Projektionsbericht 2025
scale_non_energy: false # Scale non-energy demand directly proportional to energy demand
enable: # Allowed values are "false" or a subset of [2025, 2030, 2035]
- 2025
limit_cross_border_flows_ac: # relevant if only one node per country is used
2020: 0.4
2025: 0.4
2030: 0.45
2035: 0.49 # = 0.7 * 0.7 <=> security margin * CEP target
2040: 0.49
2045: 0.49
2050: 0.49

# docs in https://pypsa-eur.readthedocs.io/en/latest/configuration.html#foresight
foresight: myopic
Expand Down Expand Up @@ -261,7 +274,12 @@ first_technology_occurrence:
H2 pipeline: 2025
H2 Electrolysis: 2025
H2 pipeline retrofitted: 2025

urban central water pits charger: 2035
urban central water pits discharger: 2035
Store:
urban central water pits: 2035
Bus:
urban central water pits: 2035
costs:
custom_cost_fn: data/pypsa-de/custom_costs_nep_2023.csv
transmission: "overhead" # either overhead line ("overhead") or underground cable ("underground")
Expand Down Expand Up @@ -336,44 +354,30 @@ sector:
2045: 0.36
2050: 0.43
# For Germany these settings get overwritten in build_exogenous_mobility_data
# Essentially all PyPSA-EUR settings but with a little bit of hydrogen to allow for the later overwrites
land_transport_fuel_cell_share:
2020: 0.01
2025: 0.01
2030: 0.02
2035: 0.03
2040: 0.03
2045: 0.03
2050: 0.03
2020: 0.001
2025: 0.001
2030: 0.001
2035: 0.001
2040: 0.001
2045: 0.001
2050: 0.001
land_transport_electric_share:
2020: 0.05
2025: 0.15
2030: 0.3
2035: 0.45
2040: 0.72
2045: 0.87
2050: 0.97
2050: 0.999
land_transport_ice_share:
2020: 0.94
2025: 0.84
2030: 0.68
2035: 0.52
2040: 0.25
2045: 0.1
2050: 0.0
2020: 0.999
2025: 0.949
2030: 0.799
2035: 0.549
2040: 0.299
2045: 0.149

# docs in https://pypsa-eur.readthedocs.io/en/latest/configuration.html#industry
industry:
steam_biomass_fraction: 0.4
steam_hydrogen_fraction: 0.3
steam_electricity_fraction: 0.3
St_primary_fraction:
2020: 0.6
2025: 0.55
2030: 0.5
2035: 0.45
2040: 0.4
2045: 0.35
2050: 0.3
DRI_fraction:
2020: 0
2025: 0
Expand All @@ -394,22 +398,6 @@ industry:
2040: 0.6
2045: 0.5
2050: 0.4
HVC_mechanical_recycling_fraction:
2020: 0.12
2025: 0.15
2030: 0.18
2035: 0.21
2040: 0.24
2045: 0.27
2050: 0.30
HVC_chemical_recycling_fraction:
2020: 0.0
2025: 0.0
2030: 0.04
2035: 0.08
2040: 0.12
2045: 0.16
2050: 0.20
# 15 Mt HVC production (from IDEES) -> 6 Mt Plastikabfälle,
# To substitute for other waste, assume all Plastikabfall is used energetically whereas in reality ~40% is recycled
# see https://github.com/PyPSA/pypsa-ariadne/pull/292
Expand Down Expand Up @@ -468,7 +456,7 @@ solving:
onwind:
DE:
2020: 54.5
2025: 69
2025: 68 # # Abb. 4_9 https://www.agora-energiewende.de/fileadmin/Projekte/2025/2025-28_DE_JAW25/A-EW_391_Die_Energiewende_in_Deutschland_Stand_der_Dinge_2025_WEB.pdf
2030: 115 # EEG2023 Ziel für 2030
2035: 160 # EEG2023 Ziel für 2040
2040: 250
Expand All @@ -478,15 +466,15 @@ solving:
DE:
2020: 7.8
2025: 11.3
2030: 29.3 # uba Projektionsbericht and NEP without delayed BalWin 3
2035: 50 # Planned projects until 2035 (offshore_connection_points.csv) -1.3 GW for potential delays
2030: 24 # very optimistic upper ceiling to "Mittelfristprognose zur deutschlandweiten Stromerzeugung"
2035: 50 # Planned projects until 2035 (offshore_connection_points.csv) -1.3 GW for potential delays
2040: 65 # Planned projects until 2040 -1.5 GW for potential retirments
2045: 70
2050: 70
solar:
DE:
2020: 53.7
2025: 110 # EEG2023; assumes for 2026: 128 GW, assuming a fair share reached by end of 2025
2025: 119 # Abb. 4_9 https://www.agora-energiewende.de/fileadmin/Projekte/2025/2025-28_DE_JAW25/A-EW_391_Die_Energiewende_in_Deutschland_Stand_der_Dinge_2025_WEB.pdf
2030: 235 # PV Ziel 2030 + 20 GW
2035: 400
2040: 800
Expand All @@ -502,6 +490,9 @@ solving:
2040: 50000
2045: 80000
2050: 80000
urban central water tanks:
DE:
2025: 120 # GWh, https://www.hamburg-institut.com/wp-content/uploads/2023/12/Referenzblatt_SysGF-1.pdf
Link:
methanolisation:
DE:
Expand Down Expand Up @@ -530,21 +521,20 @@ solving:
Generator:
onwind:
DE:
2025: 67 # Abb. 4_9 https://www.agora-energiewende.de/fileadmin/Projekte/2025/2025-28_DE_JAW25/A-EW_391_Die_Energiewende_in_Deutschland_Stand_der_Dinge_2025_WEB.pdf
2030: 99 # Wind-an-Land Law 2028
2035: 115 # Wind-an-Land Law 2030
2040: 157 # target 2035
2045: 160 # target 2040
offwind:
DE:
2030: 22.5 # 75% Wind-auf-See Law
2030: 18 # Mittelfristprognose zur deutschlandweiten Stromerzeugung
2035: 35
2040: 42
2045: 50
solar:
DE:
# EEG2023; Ziel for 2024: 88 GW and for 2026: 128 GW,
# assuming at least 1/3 of difference reached in 2025
2025: 101
2025: 118 # Abb. 4_9 https://www.agora-energiewende.de/fileadmin/Projekte/2025/2025-28_DE_JAW25/A-EW_391_Die_Energiewende_in_Deutschland_Stand_der_Dinge_2025_WEB.pdf
Link:
H2 Electrolysis:
DE:
Expand Down Expand Up @@ -601,8 +591,8 @@ solving:
# boundary condition lower?
DE:
2020: 0
2025: 0
2030: 10
2025: 6
2030: 20
2035: 105
2040: 200
2045: 300
Expand Down Expand Up @@ -720,7 +710,7 @@ onshore_nep_force:
offshore_nep_force:
cutin_year: 2025
cutout_year: 2030
delay_years: 0
delay_years: 2

scale_capacity:
2020:
Expand Down
6 changes: 6 additions & 0 deletions data/pypsa-de/UBA_Projektionsbericht2025_Abbildung31_MWMS.csv
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
carrier,2025,2030,2035
fossil,324,258,191
industry electricity,211,234,249
solid biomass for industry,31,35,31
H2 for industry,0,6,42
low-temperature heat for industry,48,59,63
2 changes: 1 addition & 1 deletion data/pypsa-de/custom_costs_nep_2021.csv
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ all,hydrogen storage underground,investment,0.55,EUR/kWh,Langfristszenarien Szen
2020,coal,fuel,5.7048,EUR2020/MWh,Ariadne,"$2020 = 0.8775 EUR2020, 1t = 8.06 MWh"
2020,decentral air-sourced heat pump,investment,1685,EUR2020/kW_th,https://ariadneprojekt.de/media/2024/01/Ariadne-Analyse_HeizkostenEmissionenGebaeude_Januar2024.pdf https://www.enpal.de/waermepumpe/kosten/ https://www.bdew.de/media/documents/BDEW-HKV_Altbau.pdf
2020,decentral ground-sourced heat pump,investment,2774,EUR2020/kW_th,https://ariadneprojekt.de/media/2024/01/Ariadne-Analyse_HeizkostenEmissionenGebaeude_Januar2024.pdf https://www.enpal.de/waermepumpe/kosten/ https://www.bdew.de/media/documents/BDEW-HKV_Altbau.pdf
2025,gas,fuel,40,EUR/MWh_th,Ariadne,
2025,gas,fuel,30,EUR2020/MWh_th,Ariadne,Näherungsweise Durchschnittspreis 2025 (Annahme 36€) korrigiert für Inflation (Annahme 20%)
2025,oil,fuel,32.9876,EUR2020/MWh,Ariadne,"$2020 = 0.8775 EUR2020, 1bbl = 1.6998MWh"
2025,coal,fuel,10.6694,EUR2020/MWh,Ariadne,"$2020 = 0.8775 EUR2020, 1t = 8.06 MWh"
2025,decentral air-sourced heat pump,investment,1604,EUR2020/kW_th,https://ariadneprojekt.de/media/2024/01/Ariadne-Analyse_HeizkostenEmissionenGebaeude_Januar2024.pdf https://www.enpal.de/waermepumpe/kosten/ https://www.bdew.de/media/documents/BDEW-HKV_Altbau.pdf and cost reduction from DEA
Expand Down
2 changes: 1 addition & 1 deletion data/pypsa-de/custom_costs_nep_2023.csv
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ all,hydrogen storage underground,investment,0.55,EUR/kWh,Langfristszenarien Szen
2020,coal,fuel,5.7048,EUR2020/MWh,Ariadne,"$2020 = 0.8775 EUR2020, 1t = 8.06 MWh"
2020,decentral air-sourced heat pump,investment,1685,EUR2020/kW_th,https://ariadneprojekt.de/media/2024/01/Ariadne-Analyse_HeizkostenEmissionenGebaeude_Januar2024.pdf https://www.enpal.de/waermepumpe/kosten/ https://www.bdew.de/media/documents/BDEW-HKV_Altbau.pdf
2020,decentral ground-sourced heat pump,investment,2774,EUR2020/kW_th,https://ariadneprojekt.de/media/2024/01/Ariadne-Analyse_HeizkostenEmissionenGebaeude_Januar2024.pdf https://www.enpal.de/waermepumpe/kosten/ https://www.bdew.de/media/documents/BDEW-HKV_Altbau.pdf
2025,gas,fuel,40,EUR/MWh_th,Ariadne,
2025,gas,fuel,30,EUR2020/MWh_th,Ariadne,Näherungsweise Durchschnittspreis 2025 (Annahme 36€) korrigiert für Inflation (Annahme 20%)
2025,oil,fuel,32.9876,EUR2020/MWh,Ariadne,"$2020 = 0.8775 EUR2020, 1bbl = 1.6998MWh"
2025,coal,fuel,10.6694,EUR2020/MWh,Ariadne,"$2020 = 0.8775 EUR2020, 1t = 8.06 MWh"
2025,decentral air-sourced heat pump,investment,1604,EUR2020/kW_th,https://ariadneprojekt.de/media/2024/01/Ariadne-Analyse_HeizkostenEmissionenGebaeude_Januar2024.pdf https://www.enpal.de/waermepumpe/kosten/ https://www.bdew.de/media/documents/BDEW-HKV_Altbau.pdf and cost reduction from DEA
Expand Down
20 changes: 14 additions & 6 deletions scripts/process_cost_data.py
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,16 @@ def prepare_costs(
DataFrame containing the prepared cost data

"""

def _convert_to_MW(cost_df: pd.DataFrame) -> pd.DataFrame:
# correct units to MW and EUR
cost_df.loc[cost_df.unit.str.contains("/kW"), "value"] *= 1e3
cost_df.loc[cost_df.unit.str.contains("/GW"), "value"] /= 1e3

cost_df.unit = cost_df.unit.str.replace("/kW", "/MW")
cost_df.unit = cost_df.unit.str.replace("/GW", "/MW")
return cost_df

# Load custom costs and categorize into two sets:
# - Raw attributes: overwritten before cost preparation
# - Prepared attributes: overwritten after cost preparation
Expand All @@ -115,9 +125,12 @@ def prepare_costs(
index_col=["technology", "parameter"],
).query("planning_horizon in [@planning_horizon, 'all']")

custom_costs = _convert_to_MW(custom_costs)

custom_costs = custom_costs.drop("planning_horizon", axis=1).value.unstack(
level=1
)

prepared_attrs = ["marginal_cost", "capital_cost"]
raw_attrs = list(set(custom_costs.columns) - set(prepared_attrs))
custom_raw = custom_costs[raw_attrs].dropna(axis=0, how="all")
Expand All @@ -128,12 +141,7 @@ def prepare_costs(
if key in config:
config["overwrites"][key] = config[key]

# correct units to MW and EUR
costs.loc[costs.unit.str.contains("/kW"), "value"] *= 1e3
costs.loc[costs.unit.str.contains("/GW"), "value"] /= 1e3

costs.unit = costs.unit.str.replace("/kW", "/MW")
costs.unit = costs.unit.str.replace("/GW", "/MW")
costs = _convert_to_MW(costs)

# min_count=1 is important to generate NaNs which are then filled by fillna
costs = costs.value.unstack(level=1).groupby("technology").sum(min_count=1)
Expand Down
Loading
Loading