2023-07-05 09:07:36 +00:00
|
|
|
#!/usr/bin/env python3
|
|
|
|
# -*- coding: utf-8 -*-
|
2024-02-19 15:21:48 +00:00
|
|
|
# SPDX-FileCopyrightText: : 2017-2024 The PyPSA-Eur Authors
|
2023-07-05 09:07:36 +00:00
|
|
|
#
|
|
|
|
# SPDX-License-Identifier: MIT
|
|
|
|
|
|
|
|
import matplotlib.pyplot as plt
|
|
|
|
import pandas as pd
|
|
|
|
import pypsa
|
|
|
|
import seaborn as sns
|
2023-08-15 13:02:41 +00:00
|
|
|
from _helpers import configure_logging, set_scenario_config
|
2023-07-05 09:07:36 +00:00
|
|
|
from pypsa.statistics import get_bus_and_carrier
|
|
|
|
|
|
|
|
sns.set_theme("paper", style="whitegrid")
|
|
|
|
|
|
|
|
carrier_groups = {
|
|
|
|
"Offshore Wind (AC)": "Offshore Wind",
|
|
|
|
"Offshore Wind (DC)": "Offshore Wind",
|
|
|
|
"Open-Cycle Gas": "Gas",
|
|
|
|
"Combined-Cycle Gas": "Gas",
|
|
|
|
"Reservoir & Dam": "Hydro",
|
|
|
|
"Pumped Hydro Storage": "Hydro",
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if __name__ == "__main__":
|
|
|
|
if "snakemake" not in globals():
|
|
|
|
from _helpers import mock_snakemake
|
|
|
|
|
|
|
|
snakemake = mock_snakemake(
|
2023-07-11 12:25:32 +00:00
|
|
|
"plot_validation_electricity_production",
|
|
|
|
opts="Ept",
|
2023-07-13 16:36:14 +00:00
|
|
|
clusters="37c",
|
2023-07-05 09:07:36 +00:00
|
|
|
ll="v1.0",
|
|
|
|
)
|
|
|
|
configure_logging(snakemake)
|
2023-08-15 13:02:41 +00:00
|
|
|
set_scenario_config(snakemake)
|
2023-07-05 09:07:36 +00:00
|
|
|
|
|
|
|
n = pypsa.Network(snakemake.input.network)
|
|
|
|
n.loads.carrier = "load"
|
|
|
|
|
|
|
|
historic = pd.read_csv(
|
|
|
|
snakemake.input.electricity_production,
|
|
|
|
index_col=0,
|
|
|
|
header=[0, 1],
|
|
|
|
parse_dates=True,
|
|
|
|
)
|
2024-01-18 17:04:22 +00:00
|
|
|
subset_technologies = ["Geothermal", "Nuclear", "Biomass", "Lignite", "Oil", "Coal"]
|
|
|
|
lowercase_technologies = [
|
|
|
|
technology.lower() if technology in subset_technologies else technology
|
|
|
|
for technology in historic.columns.levels[1]
|
|
|
|
]
|
|
|
|
historic.columns = historic.columns.set_levels(lowercase_technologies, level=1)
|
2023-07-05 09:07:36 +00:00
|
|
|
|
|
|
|
colors = n.carriers.set_index("nice_name").color.where(
|
|
|
|
lambda s: s != "", "lightgrey"
|
|
|
|
)
|
|
|
|
colors["Offshore Wind"] = colors["Offshore Wind (AC)"]
|
|
|
|
colors["Gas"] = colors["Combined-Cycle Gas"]
|
|
|
|
colors["Hydro"] = colors["Reservoir & Dam"]
|
|
|
|
colors["Other"] = "lightgray"
|
|
|
|
|
|
|
|
if len(historic.index) > len(n.snapshots):
|
|
|
|
historic = historic.resample(n.snapshots.inferred_freq).mean().loc[n.snapshots]
|
|
|
|
|
|
|
|
optimized = n.statistics.dispatch(
|
|
|
|
groupby=get_bus_and_carrier, aggregate_time=False
|
|
|
|
).T
|
|
|
|
optimized = optimized[["Generator", "StorageUnit"]].droplevel(0, axis=1)
|
|
|
|
optimized = optimized.rename(columns=n.buses.country, level=0)
|
|
|
|
optimized = optimized.rename(columns=carrier_groups, level=1)
|
2024-07-29 09:51:20 +00:00
|
|
|
optimized = optimized.T.groupby(level=[0, 1]).sum().T
|
2023-07-05 09:07:36 +00:00
|
|
|
|
|
|
|
data = pd.concat([historic, optimized], keys=["Historic", "Optimized"], axis=1)
|
|
|
|
data.columns.names = ["Kind", "Country", "Carrier"]
|
|
|
|
data = data.mul(n.snapshot_weightings.generators, axis=0)
|
|
|
|
|
2023-07-11 12:25:32 +00:00
|
|
|
# total production per carrier
|
2023-07-05 09:07:36 +00:00
|
|
|
fig, ax = plt.subplots(figsize=(6, 6))
|
|
|
|
|
|
|
|
df = data.groupby(level=["Kind", "Carrier"], axis=1).sum().sum().unstack().T
|
|
|
|
df = df / 1e6 # TWh
|
|
|
|
df.plot.barh(ax=ax, xlabel="Electricity Production [TWh]", ylabel="")
|
|
|
|
ax.grid(axis="y")
|
|
|
|
fig.savefig(snakemake.output.production_bar, bbox_inches="tight")
|
|
|
|
|
2023-07-11 12:25:32 +00:00
|
|
|
# highest diffs
|
2023-07-05 09:07:36 +00:00
|
|
|
|
|
|
|
fig, ax = plt.subplots(figsize=(6, 10))
|
|
|
|
|
|
|
|
df = data.sum() / 1e6 # TWh
|
|
|
|
df = df["Optimized"] - df["Historic"]
|
|
|
|
df = df.dropna().sort_values()
|
|
|
|
df = pd.concat([df.iloc[:5], df.iloc[-5:]])
|
|
|
|
c = colors[df.index.get_level_values(1)]
|
|
|
|
df.plot.barh(
|
|
|
|
xlabel="Optimized Production - Historic Production [TWh]", ax=ax, color=c.values
|
|
|
|
)
|
|
|
|
ax.set_title("Strongest Deviations")
|
|
|
|
ax.grid(axis="y")
|
|
|
|
fig.savefig(snakemake.output.production_deviation_bar, bbox_inches="tight")
|
|
|
|
|
2023-07-11 12:25:32 +00:00
|
|
|
# seasonal operation
|
2023-07-05 09:07:36 +00:00
|
|
|
|
|
|
|
fig, axes = plt.subplots(3, 1, figsize=(9, 9))
|
|
|
|
|
|
|
|
df = (
|
|
|
|
data.groupby(level=["Kind", "Carrier"], axis=1)
|
|
|
|
.sum()
|
|
|
|
.resample("1W")
|
|
|
|
.mean()
|
|
|
|
.clip(lower=0)
|
|
|
|
)
|
|
|
|
df = df / 1e3
|
|
|
|
|
|
|
|
order = (
|
|
|
|
(df["Historic"].diff().abs().sum() / df["Historic"].sum()).sort_values().index
|
|
|
|
)
|
|
|
|
c = colors[order]
|
|
|
|
optimized = df["Optimized"].reindex(order, axis=1, level=1)
|
|
|
|
historical = df["Historic"].reindex(order, axis=1, level=1)
|
|
|
|
|
|
|
|
kwargs = dict(color=c, legend=False, ylabel="Production [GW]", xlabel="")
|
|
|
|
|
|
|
|
optimized.plot.area(ax=axes[0], **kwargs, title="Optimized")
|
|
|
|
historical.plot.area(ax=axes[1], **kwargs, title="Historic")
|
|
|
|
|
2023-07-13 16:36:14 +00:00
|
|
|
diff = optimized - historical
|
2023-07-05 09:07:36 +00:00
|
|
|
diff.clip(lower=0).plot.area(
|
|
|
|
ax=axes[2], **kwargs, title="$\Delta$ (Optimized - Historic)"
|
|
|
|
)
|
|
|
|
lim = axes[2].get_ylim()[1]
|
|
|
|
diff.clip(upper=0).plot.area(ax=axes[2], **kwargs)
|
|
|
|
axes[2].set_ylim(bottom=-lim, top=lim)
|
|
|
|
|
|
|
|
h, l = axes[0].get_legend_handles_labels()
|
|
|
|
fig.legend(
|
|
|
|
h[::-1],
|
|
|
|
l[::-1],
|
|
|
|
loc="center left",
|
|
|
|
bbox_to_anchor=(1, 0.5),
|
|
|
|
ncol=1,
|
|
|
|
frameon=False,
|
|
|
|
labelspacing=1,
|
|
|
|
)
|
|
|
|
fig.savefig(snakemake.output.seasonal_operation_area, bbox_inches="tight")
|
|
|
|
|
|
|
|
# touch file
|
|
|
|
with open(snakemake.output.plots_touch, "a"):
|
|
|
|
pass
|