Skip to content

Commit da685c3

Browse files
author
cpschau
committed
Merge remote-tracking branch 'origin/main' into refine_dh_areas
2 parents e5abe49 + c373db2 commit da685c3

File tree

3 files changed

+40
-104
lines changed

3 files changed

+40
-104
lines changed

README.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,14 @@
11
# PyPSA-DE - Hochaufgelöstes, sektorengekoppeltes Modell des deutschen Energiesystems
22

3-
PyPSA-DE ist ein sektorengekoppeltes Energiesystem-Modell auf Basis der Toolbox [PyPSA](https://github.com/PyPSA/pypsa) und des europäischen Modells [PyPSA-Eur](https://github.com/PyPSA/pypsa-eur). Der PyPSA-DE Workflow modelliert das deutsche Energiesystem mit deutschlandspezifischen Datensätzen (MaStR, Netzentwicklungsplan,...) im Verbund mit den direkten Stromnachbarn sowie Spanien und Italien. Der Ausbau und der Betrieb von Kraftwerken, des Strom- und Wasserstoffübertragunsnetzes und die Energieversorgung aller Sektoren werden dann in einem linearen Optimierungsproblem gelöst, mit hoher zeitlicher und räumlicher Auflösung. PyPSA-DE wurde im Rahmen des Kopernikus-Projekts [Ariadne](https://ariadneprojekt.de/) entwickelt in dem Szenarien für ein klimaneutrales Deutschland untersucht werden, und spielt eine zentrale Rolle im [Ariadne Szenarienreport](https://ariadneprojekt.de/publikation/report-szenarien-zur-klimaneutralitat-2045/), als Leitmodell für den [Sektor Energiewirtschaft und Infrastruktur](https://ariadneprojekt.de/publikation/report-szenarien-zur-klimaneutralitat-2045/#6-sektorale-perspektive-energiewirtschaft) und als eines von drei Gesamtsystemmodellen. Die Ergebnisse aus der Modellierung mit PyPSA-DE werden auch im [Ariadne-Webinar zu den Kernaussagen des Berichts](https://youtu.be/UL3KAH7e0zs) ([Folien](https://ariadneprojekt.de/media/2025/03/Ariadne_Szen2025_Webinar_Folien_Kernaussagen.pdf)) und im [Ariadne-Webinar zur Energiewirtschaft](https://youtu.be/FcmHBL1MKQA) ([Folien](https://ariadneprojekt.de/media/2025/03/Ariadne_Szen2025_Webinar_Folien_Energiewirtschaft.pdf)) vorgestellt
3+
PyPSA-DE ist ein sektorengekoppeltes Energiesystem-Modell auf Basis der Toolbox [PyPSA](https://github.com/PyPSA/pypsa) und des europäischen Modells [PyPSA-Eur](https://github.com/PyPSA/pypsa-eur). Der PyPSA-DE Workflow modelliert das deutsche Energiesystem mit deutschlandspezifischen Datensätzen (MaStR, Netzentwicklungsplan,...) im Verbund mit den direkten Stromnachbarn sowie Spanien und Italien. Der Ausbau und der Betrieb von Kraftwerken, des Strom- und Wasserstoffübertragunsnetzes und die Energieversorgung aller Sektoren werden dann in einem linearen Optimierungsproblem gelöst, mit hoher zeitlicher und räumlicher Auflösung. PyPSA-DE wurde im Rahmen des Kopernikus-Projekts [Ariadne](https://ariadneprojekt.de/) entwickelt in dem Szenarien für ein klimaneutrales Deutschland untersucht werden, und spielt eine zentrale Rolle im [Ariadne Szenarienreport](https://ariadneprojekt.de/publikation/report-szenarien-zur-klimaneutralitat-2045/), als Leitmodell für den [Sektor Energiewirtschaft und Infrastruktur](https://ariadneprojekt.de/publikation/report-szenarien-zur-klimaneutralitat-2045/#6-sektorale-perspektive-energiewirtschaft) und als eines von drei Gesamtsystemmodellen. Die Ergebnisse aus der Modellierung mit PyPSA-DE werden auch im [Ariadne-Webinar zu den Kernaussagen des Berichts](https://youtu.be/UL3KAH7e0zs) ([Folien](https://ariadneprojekt.de/media/2025/03/Ariadne_Szen2025_Webinar_Folien_Kernaussagen.pdf)) und im [Ariadne-Webinar zur Energiewirtschaft](https://youtu.be/FcmHBL1MKQA) ([Folien](https://ariadneprojekt.de/media/2025/03/Ariadne_Szen2025_Webinar_Folien_Energiewirtschaft.pdf)) vorgestellt. Die [gelösten Netzwerke](https://zenodo.org/records/15096970) sind auf zenodo verfügbar.
44

55
# PyPSA-DE - High resolution, sector-coupled model of the German Energy System
66

7-
PyPSA-DE is a sector-coupled energy system model based on the toolbox [PyPSA](https://github.com/PyPSA/pypsa) and the European model [PyPSA-Eur](https://github.com/PyPSA/pypsa-eur). It solves a linear optimization problem to simulate the electricty and hydrogen transmission networks, as well as supply, demand and storage in all sectors of the energy system in Germany and its neighboring countries, as well as Italy and Spain, with high spatial and temporal resolution. PyPSA-DE was developed in the context of the Kopernikus-Projekt [Ariadne](https://ariadneprojekt.de/en/), which studies scenarios of a carbon-neutral German economcy, and plays a decisive role in the [Ariadne Szenarienreport](https://ariadneprojekt.de/publikation/report-szenarien-zur-klimaneutralitat-2045/), as reference model for the [energy and infrastructure sectors](https://ariadneprojekt.de/publikation/report-szenarien-zur-klimaneutralitat-2045/#6-sektorale-perspektive-energiewirtschaft). The results of modeling with PyPSA-DE are also presented in the [Ariadne-Webinar on the core messages of the report](https://youtu.be/UL3KAH7e0zs) ([slides](https://ariadneprojekt.de/media/2025/03/Ariadne_Szen2025_Webinar_Folien_Kernaussagen.pdf)) and in the [Ariadne-Webinar on the energy sector](https://youtu.be/FcmHBL1MKQA) ([slides](https://ariadneprojekt.de/media/2025/03/Ariadne_Szen2025_Webinar_Folien_Energiewirtschaft.pdf)).
7+
PyPSA-DE is a sector-coupled energy system model based on the toolbox [PyPSA](https://github.com/PyPSA/pypsa) and the European model [PyPSA-Eur](https://github.com/PyPSA/pypsa-eur). It solves a linear optimization problem to simulate the electricty and hydrogen transmission networks, as well as supply, demand and storage in all sectors of the energy system in Germany and its neighboring countries, as well as Italy and Spain, with high spatial and temporal resolution. PyPSA-DE was developed in the context of the Kopernikus-Projekt [Ariadne](https://ariadneprojekt.de/en/), which studies scenarios of a carbon-neutral German economcy, and plays a decisive role in the [Ariadne Szenarienreport](https://ariadneprojekt.de/publikation/report-szenarien-zur-klimaneutralitat-2045/), as reference model for the [energy and infrastructure sectors](https://ariadneprojekt.de/publikation/report-szenarien-zur-klimaneutralitat-2045/#6-sektorale-perspektive-energiewirtschaft). The results of modeling with PyPSA-DE are also presented in the [Ariadne-Webinar on the core messages of the report](https://youtu.be/UL3KAH7e0zs) ([slides](https://ariadneprojekt.de/media/2025/03/Ariadne_Szen2025_Webinar_Folien_Kernaussagen.pdf)) and in the [Ariadne-Webinar on the energy sector](https://youtu.be/FcmHBL1MKQA) ([slides](https://ariadneprojekt.de/media/2025/03/Ariadne_Szen2025_Webinar_Folien_Energiewirtschaft.pdf)). The [solved networks](https://zenodo.org/records/15096970) are available on zenodo.
88

99
This repository contains the entire scientific project, including data sources and code. The philosophy behind this repository is that no intermediary results are included, but all results are computed from raw data and code.
1010

11-
[`<img src="https://ariadneprojekt.de/media/2025/03/Abb-7.3.png" width="400"/>`](https://ariadneprojekt.de/media/2025/03/Abb-7.3.png)
11+
[<img src="https://ariadneprojekt.de/media/2025/03/Abb-7.3.png" width="400"/>](https://ariadneprojekt.de/media/2025/03/Abb-7.3.png)
1212

1313
## Getting ready
1414

config/config.de.yaml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -314,7 +314,6 @@ sector:
314314
biomass_spatial: true
315315
#relax so no infeasibility in 2050 with no land transport demand
316316
min_part_load_fischer_tropsch: 0.
317-
regional_methanol_demand: true #set to true if regional CO2 constraints needed
318317
regional_oil_demand: true #set to true if regional CO2 constraints needed
319318
regional_coal_demand: true #set to true if regional CO2 constraints needed
320319
gas_network: false

scripts/pypsa-de/plot_ariadne_report.py

Lines changed: 37 additions & 100 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44

55
sys.path.insert(0, os.path.abspath(os.path.dirname(__file__)))
66
from itertools import compress, islice
7-
from multiprocessing import Pool
87

98
import cartopy
109
import cartopy.crs as ccrs
@@ -376,8 +375,8 @@ def plot_nodal_elec_balance(
376375
tech_colors,
377376
savepath,
378377
carriers=["AC", "low voltage"],
379-
start_date="2019-01-01 00:00:00",
380-
end_date="2019-12-31 00:00:00",
378+
start_date="01-01 00:00:00",
379+
end_date="12-31 23:00:00",
381380
regions=["DE"],
382381
model_run="Model run",
383382
c1_groups=c1_groups,
@@ -414,6 +413,9 @@ def plot_nodal_elec_balance(
414413
)
415414
return
416415

416+
start_date = str(network.generators_t.p.index[0])[:4] + "-" + start_date
417+
end_date = str(network.generators_t.p.index[-1])[:4] + "-" + end_date
418+
417419
period = network.generators_t.p.index[
418420
(network.generators_t.p.index >= start_date)
419421
& (network.generators_t.p.index <= end_date)
@@ -535,11 +537,14 @@ def plot_nodal_elec_balance(
535537
df_pos[import_label] = (
536538
df["Electricity trade"].where(df["Electricity trade"] > 0).fillna(0)
537539
)
540+
except KeyError:
541+
print("Skipping Electricity import because it is too small")
542+
try:
538543
df_neg[export_label] = (
539544
df["Electricity trade"].where(df["Electricity trade"] < 0).fillna(0)
540545
)
541546
except KeyError:
542-
print("Skipping Electricity trade because it is too small")
547+
print("Skipping Electricity export because it is too small")
543548
df_pos = df_pos.drop(columns=["Electricity trade"], errors="ignore")
544549
df_pos = df_pos.rename(columns={"urban central H2 CHP": "H2 CHP"})
545550
df_pos["other"] = df_pos.drop(columns=preferred_order_pos, errors="ignore").sum(
@@ -714,8 +719,8 @@ def plot_nodal_heat_balance(
714719
tech_colors,
715720
savepath,
716721
carriers=["AC", "low voltage"],
717-
start_date="2019-01-01 00:00:00",
718-
end_date="2019-12-31 00:00:00",
722+
start_date="01-01 00:00:00",
723+
end_date="12-31 23:00:00",
719724
regions=["DE"],
720725
model_run="Model run",
721726
c1_groups=c1_groups,
@@ -732,6 +737,9 @@ def plot_nodal_heat_balance(
732737
ylabel="total electricity balance [GW]",
733738
title="Electricity balance",
734739
):
740+
start_date = str(network.generators_t.p.index[0])[:4] + "-" + start_date
741+
end_date = str(network.generators_t.p.index[-1])[:4] + "-" + end_date
742+
735743
carriers = carriers
736744
loads = loads
737745
start_date = start_date
@@ -992,13 +1000,16 @@ def plot_storage(
9921000
tech_colors,
9931001
savepath,
9941002
model_run="Model run",
995-
start_date="2019-01-01 00:00:00",
996-
end_date="2019-12-31 00",
1003+
start_date="01-01 00:00:00",
1004+
end_date="12-31 23:00:00",
9971005
regions=["DE"],
9981006
):
9991007
# State of charge [per unit of max] (all stores and storage units)
10001008
# Ratio of total generation of max state of charge
10011009

1010+
start_date = str(network.generators_t.p.index[0])[:4] + "-" + start_date
1011+
end_date = str(network.generators_t.p.index[-1])[:4] + "-" + end_date
1012+
10021013
n = network
10031014
n.remove("Link", n.links.index[n.links.index.str[:2] != "DE"])
10041015
n.remove("Store", n.stores.index[n.stores.index.str[:2] != "DE"])
@@ -1520,6 +1531,7 @@ def plot_elec_prices_spatial(
15201531
2035: 4.27 + 27.51,
15211532
2040: 5.60 + 27.51,
15221533
2045: 6.53 + 27.51,
1534+
2050: 0.0, # dummy value to make the function work for 2050
15231535
}
15241536
)
15251537
nep_annuität = pd.Series(
@@ -1530,6 +1542,7 @@ def plot_elec_prices_spatial(
15301542
2035: 13.05 + 27.51,
15311543
2040: 15.39 + 27.51,
15321544
2045: 15.82 + 27.51,
1545+
2050: 0.0, # dummy value to make the function work for 2050
15331546
}
15341547
)
15351548
electricity_demand = exported_variables.loc["Demand|Electricity"].iloc[0, :] / 1000
@@ -2888,8 +2901,8 @@ def plot_h2_trade(
28882901
network=network,
28892902
nodal_balance=balance,
28902903
tech_colors=tech_colors,
2891-
start_date="2019-01-01 00:00:00",
2892-
end_date="2019-12-31 00:00:00",
2904+
start_date="01-01 00:00:00",
2905+
end_date="12-31 23:00:00",
28932906
savepath=f"{snakemake.output.elec_balances}/elec-all-year-DE-{year}.pdf",
28942907
model_run=snakemake.wildcards.run,
28952908
resample="D",
@@ -2907,8 +2920,8 @@ def plot_h2_trade(
29072920
network=network,
29082921
nodal_balance=balance,
29092922
tech_colors=tech_colors,
2910-
start_date="2019-01-01 00:00:00",
2911-
end_date="2019-01-31 00:00:00",
2923+
start_date="01-01 00:00:00",
2924+
end_date="01-31 23:00:00",
29122925
savepath=f"{snakemake.output.elec_balances}/elec-Jan-DE-{year}.pdf",
29132926
model_run=snakemake.wildcards.run,
29142927
german_carriers=True,
@@ -2923,8 +2936,8 @@ def plot_h2_trade(
29232936
network=network,
29242937
nodal_balance=balance,
29252938
tech_colors=tech_colors,
2926-
start_date="2019-01-01 00:00:00",
2927-
end_date="2019-01-31 00:00:00",
2939+
start_date="01-01 00:00:00",
2940+
end_date="01-31 23:00:00",
29282941
savepath=f"{snakemake.output.elec_balances}/elec-Jan-DE-{year}.pdf",
29292942
model_run=snakemake.wildcards.run,
29302943
german_carriers=False,
@@ -2939,8 +2952,8 @@ def plot_h2_trade(
29392952
network=network,
29402953
nodal_balance=balance,
29412954
tech_colors=tech_colors,
2942-
start_date="2019-05-01 00:00:00",
2943-
end_date="2019-05-31 00:00:00",
2955+
start_date="05-01 00:00:00",
2956+
end_date="05-31 23:00:00",
29442957
savepath=f"{snakemake.output.elec_balances}/elec-May-DE-{year}.pdf",
29452958
model_run=snakemake.wildcards.run,
29462959
german_carriers=True,
@@ -2958,8 +2971,8 @@ def plot_h2_trade(
29582971
network=network,
29592972
nodal_balance=balance,
29602973
tech_colors=tech_colors,
2961-
start_date="2019-01-01 00:00:00",
2962-
end_date="2019-12-31 00:00:00",
2974+
start_date="01-01 00:00:00",
2975+
end_date="12-31 23:00:00",
29632976
savepath=f"{snakemake.output.heat_balances}/heat-all-year-DE-{carriers}-{year}.pdf",
29642977
model_run=snakemake.wildcards.run,
29652978
resample="D",
@@ -2978,8 +2991,8 @@ def plot_h2_trade(
29782991
network=network,
29792992
nodal_balance=balance,
29802993
tech_colors=tech_colors,
2981-
start_date="2019-01-01 00:00:00",
2982-
end_date="2019-01-31 00:00:00",
2994+
start_date="01-01 00:00:00",
2995+
end_date="01-31 23:00:00",
29832996
savepath=f"{snakemake.output.heat_balances}/heat-Jan-DE-{carriers}-{year}.pdf",
29842997
model_run=snakemake.wildcards.run,
29852998
plot_lmps=False,
@@ -2995,8 +3008,8 @@ def plot_h2_trade(
29953008
network=network,
29963009
nodal_balance=balance,
29973010
tech_colors=tech_colors,
2998-
start_date="2019-05-01 00:00:00",
2999-
end_date="2019-05-31 00:00:00",
3011+
start_date="05-01 00:00:00",
3012+
end_date="05-31 23:00:00",
30003013
savepath=f"{snakemake.output.heat_balances}/heat-May-DE-{carriers}-{year}.pdf",
30013014
model_run=snakemake.wildcards.run,
30023015
plot_lmps=False,
@@ -3013,8 +3026,8 @@ def plot_h2_trade(
30133026
plot_storage(
30143027
network=network,
30153028
tech_colors=tech_colors,
3016-
start_date="2019-01-01 00:00:00",
3017-
end_date="2019-12-31 00:00:00",
3029+
start_date="01-01 00:00:00",
3030+
end_date="12-31 23:00:00",
30183031
savepath=f"{snakemake.output.results}/storage-DE-{year}.pdf",
30193032
model_run=snakemake.wildcards.run,
30203033
)
@@ -3162,79 +3175,3 @@ def plot_h2_trade(
31623175
tech_colors,
31633176
savepath=f"{snakemake.output.elec_transmission}/elec-trade-DE.pdf",
31643177
)
3165-
3166-
## nodal balances general (might not be very robust)
3167-
logger.info("Plotting nodal balances")
3168-
plt.style.use(["bmh", snakemake.input.rc])
3169-
3170-
year = 2045
3171-
network = networks[planning_horizons.index(year)].copy()
3172-
n = network
3173-
3174-
months = pd.date_range(freq="ME", **snakemake.config["snapshots"]).map(
3175-
lambda x: x.strftime("%Y-%m")
3176-
)
3177-
3178-
# only DE
3179-
ct = "DE"
3180-
buses = n.buses.index[(n.buses.index.str[:2] == ct)].drop("DE")
3181-
balance = (
3182-
n.statistics.energy_balance(
3183-
aggregate_time=False,
3184-
groupby=["bus", "carrier", "bus_carrier"],
3185-
)
3186-
.loc[:, buses, :, :]
3187-
.droplevel("bus")
3188-
)
3189-
3190-
n.carriers.color.update(snakemake.config["plotting"]["tech_colors"])
3191-
n.carriers.color.update(tech_colors)
3192-
colors = n.carriers.color.rename(n.carriers.nice_name)
3193-
# replace empty values TODO add empty values with colors to plotting config
3194-
colors[colors.values == ""] = "lightgrey"
3195-
3196-
# wrap in function for multiprocessing
3197-
def process_group(group, carriers, balance, months, colors):
3198-
if not isinstance(carriers, list):
3199-
carriers = [carriers]
3200-
3201-
mask = balance.index.get_level_values("bus_carrier").isin(carriers)
3202-
df = balance[mask].groupby("carrier").sum().div(1e3).T
3203-
3204-
# daily resolution for each carrier
3205-
plot_energy_balance_timeseries(
3206-
df,
3207-
resample="D",
3208-
ylabel=group,
3209-
colors=colors,
3210-
threshold=THRESHOLD,
3211-
dir=dir,
3212-
)
3213-
3214-
# monthly resolution for each carrier
3215-
plot_energy_balance_timeseries(
3216-
df,
3217-
resample="M",
3218-
ylabel=group,
3219-
colors=colors,
3220-
threshold=THRESHOLD,
3221-
dir=dir,
3222-
)
3223-
3224-
# native resolution for each month and carrier
3225-
for month in months:
3226-
plot_energy_balance_timeseries(
3227-
df,
3228-
time=month,
3229-
ylabel=group,
3230-
colors=colors,
3231-
threshold=THRESHOLD,
3232-
dir=dir,
3233-
)
3234-
3235-
args = [
3236-
(group, carriers, balance, months, colors)
3237-
for group, carriers in CARRIER_GROUPS.items()
3238-
]
3239-
with Pool(processes=snakemake.threads) as pool:
3240-
pool.starmap(process_group, args)

0 commit comments

Comments
 (0)