Merge pull request #220 from PyPSA/map-proj

improved legends, EqualEarth projection, w/wo retrofit compatibility
This commit is contained in:
Fabian Neumann 2022-12-28 15:46:29 +01:00 committed by GitHub
commit 2be8b52a1e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 280 additions and 159 deletions

View File

@ -516,7 +516,8 @@ rule prepare_sector_network:
rule plot_network: rule plot_network:
input: input:
overrides="data/override_component_attrs", overrides="data/override_component_attrs",
network=RDIR + "/postnetworks/elec_s{simpl}_{clusters}_lv{lv}_{opts}_{sector_opts}_{planning_horizons}.nc" network=RDIR + "/postnetworks/elec_s{simpl}_{clusters}_lv{lv}_{opts}_{sector_opts}_{planning_horizons}.nc",
regions=pypsaeur('resources/regions_onshore_elec_s{simpl}_{clusters}.geojson')
output: output:
map=RDIR + "/maps/elec_s{simpl}_{clusters}_lv{lv}_{opts}_{sector_opts}-costs-all_{planning_horizons}.pdf", map=RDIR + "/maps/elec_s{simpl}_{clusters}_lv{lv}_{opts}_{sector_opts}-costs-all_{planning_horizons}.pdf",
today=RDIR + "/maps/elec_s{simpl}_{clusters}_lv{lv}_{opts}_{sector_opts}_{planning_horizons}-today.pdf" today=RDIR + "/maps/elec_s{simpl}_{clusters}_lv{lv}_{opts}_{sector_opts}_{planning_horizons}-today.pdf"

View File

@ -401,7 +401,7 @@ plotting:
boundaries: [-11, 30, 34, 71] boundaries: [-11, 30, 34, 71]
color_geomap: color_geomap:
ocean: white ocean: white
land: whitesmoke land: white
eu_node_location: eu_node_location:
x: -5.5 x: -5.5
y: 46. y: 46.

View File

@ -86,6 +86,8 @@ incorporates retrofitting options to hydrogen.
* Shipping demand now defaults to (synthetic) oil rather than liquefied hydrogen until 2050. * Shipping demand now defaults to (synthetic) oil rather than liquefied hydrogen until 2050.
* Improved network plots including better legends, hydrogen retrofitting network display, and change to EqualEarth projection.
**Bugfixes** **Bugfixes**
* The CO2 sequestration limit implemented as GlobalConstraint (introduced in the previous version) * The CO2 sequestration limit implemented as GlobalConstraint (introduced in the previous version)

View File

@ -1,4 +1,3 @@
backend: Agg
font.family: sans-serif font.family: sans-serif
font.sans-serif: Ubuntu, DejaVu Sans font.sans-serif: Ubuntu, DejaVu Sans
image.cmap: viridis image.cmap: viridis

View File

@ -1,18 +1,17 @@
import pypsa import pypsa
import numpy as np
import pandas as pd import pandas as pd
import geopandas as gpd
import matplotlib.pyplot as plt import matplotlib.pyplot as plt
import cartopy.crs as ccrs import cartopy.crs as ccrs
from matplotlib.legend_handler import HandlerPatch from pypsa.plot import add_legend_circles, add_legend_patches, add_legend_lines
from matplotlib.patches import Circle, Ellipse
from make_summary import assign_carriers from make_summary import assign_carriers
from plot_summary import rename_techs, preferred_order from plot_summary import rename_techs, preferred_order
from helper import override_component_attrs from helper import override_component_attrs
plt.style.use('ggplot') plt.style.use(['ggplot', "matplotlibrc"])
def rename_techs_tyndp(tech): def rename_techs_tyndp(tech):
@ -27,8 +26,8 @@ def rename_techs_tyndp(tech):
return "ammonia" return "ammonia"
elif tech in ["OCGT", "CHP", "gas boiler", "H2 Fuel Cell"]: elif tech in ["OCGT", "CHP", "gas boiler", "H2 Fuel Cell"]:
return "gas-to-power/heat" return "gas-to-power/heat"
elif "solar" in tech: # elif "solar" in tech:
return "solar" # return "solar"
elif tech in ["Fischer-Tropsch", "methanolisation"]: elif tech in ["Fischer-Tropsch", "methanolisation"]:
return "power-to-liquid" return "power-to-liquid"
elif "offshore wind" in tech: elif "offshore wind" in tech:
@ -39,36 +38,6 @@ def rename_techs_tyndp(tech):
return tech return tech
def make_handler_map_to_scale_circles_as_in(ax, dont_resize_actively=False):
fig = ax.get_figure()
def axes2pt():
return np.diff(ax.transData.transform([(0, 0), (1, 1)]), axis=0)[0] * (72. / fig.dpi)
ellipses = []
if not dont_resize_actively:
def update_width_height(event):
dist = axes2pt()
for e, radius in ellipses:
e.width, e.height = 2. * radius * dist
fig.canvas.mpl_connect('resize_event', update_width_height)
ax.callbacks.connect('xlim_changed', update_width_height)
ax.callbacks.connect('ylim_changed', update_width_height)
def legend_circle_handler(legend, orig_handle, xdescent, ydescent,
width, height, fontsize):
w, h = 2. * orig_handle.get_radius() * axes2pt()
e = Ellipse(xy=(0.5 * width - 0.5 * xdescent, 0.5 *
height - 0.5 * ydescent), width=w, height=w)
ellipses.append((e, orig_handle.get_radius()))
return e
return {Circle: HandlerPatch(patch_func=legend_circle_handler)}
def make_legend_circles_for(sizes, scale=1.0, **kw):
return [Circle((0, 0), radius=(s / scale)**0.5, **kw) for s in sizes]
def assign_location(n): def assign_location(n):
for c in n.iterate_components(n.one_port_components | n.branch_components): for c in n.iterate_components(n.one_port_components | n.branch_components):
ifind = pd.Series(c.df.index.str.find(" ", start=4), c.df.index) ifind = pd.Series(c.df.index.str.find(" ", start=4), c.df.index)
@ -80,7 +49,9 @@ def assign_location(n):
def plot_map(network, components=["links", "stores", "storage_units", "generators"], def plot_map(network, components=["links", "stores", "storage_units", "generators"],
bus_size_factor=1.7e10, transmission=False): bus_size_factor=1.7e10, transmission=False, with_legend=True):
tech_colors = snakemake.config['plotting']['tech_colors']
n = network.copy() n = network.copy()
assign_location(n) assign_location(n)
@ -111,7 +82,7 @@ def plot_map(network, components=["links", "stores", "storage_units", "generator
costs = costs[new_columns] costs = costs[new_columns]
for item in new_columns: for item in new_columns:
if item not in snakemake.config['plotting']['tech_colors']: if item not in tech_colors:
print("Warning!",item,"not in config/plotting/tech_colors") print("Warning!",item,"not in config/plotting/tech_colors")
costs = costs.stack() # .sort_index() costs = costs.stack() # .sort_index()
@ -133,34 +104,39 @@ def plot_map(network, components=["links", "stores", "storage_units", "generator
# make sure they are removed from index # make sure they are removed from index
costs.index = pd.MultiIndex.from_tuples(costs.index.values) costs.index = pd.MultiIndex.from_tuples(costs.index.values)
threshold = 100e6 # 100 mEUR/a
carriers = costs.groupby(level=1).sum()
carriers = carriers.where(carriers > threshold).dropna()
carriers = list(carriers.index)
# PDF has minimum width, so set these to zero # PDF has minimum width, so set these to zero
line_lower_threshold = 500. line_lower_threshold = 500.
line_upper_threshold = 1e4 line_upper_threshold = 1e4
linewidth_factor = 2e3 linewidth_factor = 4e3
ac_color = "gray" ac_color = "rosybrown"
dc_color = "m" dc_color = "darkseagreen"
if snakemake.wildcards["lv"] == "1.0": if snakemake.wildcards["lv"] == "1.0":
# should be zero # should be zero
line_widths = n.lines.s_nom_opt - n.lines.s_nom line_widths = n.lines.s_nom_opt - n.lines.s_nom
link_widths = n.links.p_nom_opt - n.links.p_nom link_widths = n.links.p_nom_opt - n.links.p_nom
title = "Transmission reinforcement" title = "added grid"
if transmission: if transmission:
line_widths = n.lines.s_nom_opt line_widths = n.lines.s_nom_opt
link_widths = n.links.p_nom_opt link_widths = n.links.p_nom_opt
linewidth_factor = 2e3 linewidth_factor = 2e3
line_lower_threshold = 0. line_lower_threshold = 0.
title = "Today's transmission" title = "current grid"
else: else:
line_widths = n.lines.s_nom_opt - n.lines.s_nom_min line_widths = n.lines.s_nom_opt - n.lines.s_nom_min
link_widths = n.links.p_nom_opt - n.links.p_nom_min link_widths = n.links.p_nom_opt - n.links.p_nom_min
title = "Transmission reinforcement" title = "added grid"
if transmission: if transmission:
line_widths = n.lines.s_nom_opt line_widths = n.lines.s_nom_opt
link_widths = n.links.p_nom_opt link_widths = n.links.p_nom_opt
title = "Total transmission" title = "total grid"
line_widths[line_widths < line_lower_threshold] = 0. line_widths[line_widths < line_lower_threshold] = 0.
link_widths[link_widths < line_lower_threshold] = 0. link_widths[link_widths < line_lower_threshold] = 0.
@ -168,12 +144,12 @@ def plot_map(network, components=["links", "stores", "storage_units", "generator
line_widths[line_widths > line_upper_threshold] = line_upper_threshold line_widths[line_widths > line_upper_threshold] = line_upper_threshold
link_widths[link_widths > line_upper_threshold] = line_upper_threshold link_widths[link_widths > line_upper_threshold] = line_upper_threshold
fig, ax = plt.subplots(subplot_kw={"projection": ccrs.PlateCarree()}) fig, ax = plt.subplots(subplot_kw={"projection": ccrs.EqualEarth()})
fig.set_size_inches(7, 6) fig.set_size_inches(7, 6)
n.plot( n.plot(
bus_sizes=costs / bus_size_factor, bus_sizes=costs / bus_size_factor,
bus_colors=snakemake.config['plotting']['tech_colors'], bus_colors=tech_colors,
line_colors=ac_color, line_colors=ac_color,
link_colors=dc_color, link_colors=dc_color,
line_widths=line_widths / linewidth_factor, line_widths=line_widths / linewidth_factor,
@ -181,45 +157,66 @@ def plot_map(network, components=["links", "stores", "storage_units", "generator
ax=ax, **map_opts ax=ax, **map_opts
) )
handles = make_legend_circles_for( sizes = [20, 10, 5]
[5e9, 1e9], labels = [f"{s} bEUR/a" for s in sizes]
scale=bus_size_factor, sizes = [s/bus_size_factor*1e9 for s in sizes]
facecolor="gray"
)
labels = ["{} bEUR/a".format(s) for s in (5, 1)] legend_kw = dict(
l2 = ax.legend(
handles, labels,
loc="upper left", loc="upper left",
bbox_to_anchor=(0.01, 1.01), bbox_to_anchor=(0.01, 1.06),
labelspacing=1.0, labelspacing=0.8,
frameon=False, frameon=False,
title='System cost', handletextpad=0,
handler_map=make_handler_map_to_scale_circles_as_in(ax) title='system cost',
) )
ax.add_artist(l2) add_legend_circles(
ax,
sizes,
labels,
srid=n.srid,
patch_kw=dict(facecolor="lightgrey"),
legend_kw=legend_kw
)
handles = [] sizes = [10, 5]
labels = [] labels = [f"{s} GW" for s in sizes]
scale = 1e3 / linewidth_factor
sizes = [s*scale for s in sizes]
for s in (10, 5): legend_kw = dict(
handles.append(plt.Line2D([0], [0], color=ac_color,
linewidth=s * 1e3 / linewidth_factor))
labels.append("{} GW".format(s))
l1_1 = ax.legend(
handles, labels,
loc="upper left", loc="upper left",
bbox_to_anchor=(0.22, 1.01), bbox_to_anchor=(0.27, 1.06),
frameon=False, frameon=False,
labelspacing=0.8, labelspacing=0.8,
handletextpad=1.5, handletextpad=1,
title=title title=title
) )
ax.add_artist(l1_1) add_legend_lines(
ax,
sizes,
labels,
patch_kw=dict(color='lightgrey'),
legend_kw=legend_kw
)
legend_kw = dict(
bbox_to_anchor=(1.52, 1.04),
frameon=False,
)
if with_legend:
colors = [tech_colors[c] for c in carriers] + [ac_color, dc_color]
labels = carriers + ["HVAC line", "HVDC link"]
add_legend_patches(
ax,
colors,
labels,
legend_kw=legend_kw,
)
fig.savefig( fig.savefig(
snakemake.output.map, snakemake.output.map,
@ -248,7 +245,7 @@ def group_pipes(df, drop_direction=False):
return pipe_capacity return pipe_capacity
def plot_h2_map(network): def plot_h2_map(network, regions):
n = network.copy() n = network.copy()
if "H2 pipeline" not in n.links.carrier.unique(): if "H2 pipeline" not in n.links.carrier.unique():
@ -256,6 +253,10 @@ def plot_h2_map(network):
assign_location(n) assign_location(n)
h2_storage = n.stores.query("carrier == 'H2'")
regions["H2"] = h2_storage.rename(index=h2_storage.bus.map(n.buses.location)).e_nom_opt.div(1e6) # TWh
regions["H2"] = regions["H2"].where(regions["H2"] > 0.1)
bus_size_factor = 1e5 bus_size_factor = 1e5
linewidth_factor = 1e4 linewidth_factor = 1e4
# MW below which not drawn # MW below which not drawn
@ -264,7 +265,9 @@ def plot_h2_map(network):
# Drop non-electric buses so they don't clutter the plot # Drop non-electric buses so they don't clutter the plot
n.buses.drop(n.buses.index[n.buses.carrier != "AC"], inplace=True) n.buses.drop(n.buses.index[n.buses.carrier != "AC"], inplace=True)
elec = n.links[n.links.carrier.isin(["H2 Electrolysis", "H2 Fuel Cell"])].index carriers = ["H2 Electrolysis", "H2 Fuel Cell"]
elec = n.links[n.links.carrier.isin(carriers)].index
bus_sizes = n.links.loc[elec,"p_nom_opt"].groupby([n.links["bus0"], n.links.carrier]).sum() / bus_size_factor bus_sizes = n.links.loc[elec,"p_nom_opt"].groupby([n.links["bus0"], n.links.carrier]).sum() / bus_size_factor
@ -275,14 +278,44 @@ def plot_h2_map(network):
h2_new = n.links.loc[n.links.carrier=="H2 pipeline"] h2_new = n.links.loc[n.links.carrier=="H2 pipeline"]
h2_retro = n.links.loc[n.links.carrier=='H2 pipeline retrofitted'] h2_retro = n.links.loc[n.links.carrier=='H2 pipeline retrofitted']
if snakemake.config['foresight'] == 'myopic':
# sum capacitiy for pipelines from different investment periods # sum capacitiy for pipelines from different investment periods
h2_new = group_pipes(h2_new) h2_new = group_pipes(h2_new)
h2_retro = group_pipes(h2_retro, drop_direction=True).reindex(h2_new.index).fillna(0) h2_retro = group_pipes(h2_retro, drop_direction=True).reindex(h2_new.index).fillna(0)
if not h2_retro.empty:
positive_order = h2_retro.bus0 < h2_retro.bus1
h2_retro_p = h2_retro[positive_order]
swap_buses = {"bus0": "bus1", "bus1": "bus0"}
h2_retro_n = h2_retro[~positive_order].rename(columns=swap_buses)
h2_retro = pd.concat([h2_retro_p, h2_retro_n])
h2_retro["index_orig"] = h2_retro.index
h2_retro.index = h2_retro.apply(
lambda x: f"H2 pipeline {x.bus0.replace(' H2', '')} -> {x.bus1.replace(' H2', '')}",
axis=1
)
retro_w_new_i = h2_retro.index.intersection(h2_new.index)
h2_retro_w_new = h2_retro.loc[retro_w_new_i]
retro_wo_new_i = h2_retro.index.difference(h2_new.index)
h2_retro_wo_new = h2_retro.loc[retro_wo_new_i]
h2_retro_wo_new.index = h2_retro_wo_new.index_orig
to_concat = [h2_new, h2_retro_w_new, h2_retro_wo_new]
h2_total = pd.concat(to_concat).p_nom_opt.groupby(level=0).sum()
else:
h2_total = h2_new
link_widths_total = h2_total / linewidth_factor
n.links.rename(index=lambda x: x.split("-2")[0], inplace=True) n.links.rename(index=lambda x: x.split("-2")[0], inplace=True)
n.links = n.links.groupby(level=0).first() n.links = n.links.groupby(level=0).first()
link_widths_total = (h2_new + h2_retro) / linewidth_factor
link_widths_total = link_widths_total.reindex(n.links.index).fillna(0.) link_widths_total = link_widths_total.reindex(n.links.index).fillna(0.)
link_widths_total[n.links.p_nom_opt < line_lower_threshold] = 0. link_widths_total[n.links.p_nom_opt < line_lower_threshold] = 0.
@ -293,15 +326,27 @@ def plot_h2_map(network):
n.links.bus0 = n.links.bus0.str.replace(" H2", "") n.links.bus0 = n.links.bus0.str.replace(" H2", "")
n.links.bus1 = n.links.bus1.str.replace(" H2", "") n.links.bus1 = n.links.bus1.str.replace(" H2", "")
proj = ccrs.EqualEarth()
regions = regions.to_crs(proj.proj4_init)
fig, ax = plt.subplots( fig, ax = plt.subplots(
figsize=(7, 6), figsize=(7, 6),
subplot_kw={"projection": ccrs.PlateCarree()} subplot_kw={"projection": proj}
) )
color_h2_pipe = '#b3f3f4'
color_retrofit = '#499a9c'
bus_colors = {
"H2 Electrolysis": "#ff29d9",
"H2 Fuel Cell": '#805394'
}
n.plot( n.plot(
geomap=True,
bus_sizes=bus_sizes, bus_sizes=bus_sizes,
bus_colors=snakemake.config['plotting']['tech_colors'], bus_colors=bus_colors,
link_colors='#a2f0f2', link_colors=color_h2_pipe,
link_widths=link_widths_total, link_widths=link_widths_total,
branch_components=["Link"], branch_components=["Link"],
ax=ax, ax=ax,
@ -309,53 +354,88 @@ def plot_h2_map(network):
) )
n.plot( n.plot(
geomap=True,
bus_sizes=0, bus_sizes=0,
link_colors='#72d3d6', link_colors=color_retrofit,
link_widths=link_widths_retro, link_widths=link_widths_retro,
branch_components=["Link"], branch_components=["Link"],
ax=ax, ax=ax,
**map_opts color_geomap=False,
boundaries=map_opts["boundaries"]
) )
handles = make_legend_circles_for( regions.plot(
[50000, 10000], ax=ax,
scale=bus_size_factor, column="H2",
facecolor='grey' cmap='Blues',
linewidths=0,
legend=True,
vmax=10,
vmin=0,
legend_kwds={
"label": "Hydrogen Storage [TWh]",
"shrink": 0.7,
"extend": "max",
},
) )
labels = ["{} GW".format(s) for s in (50, 10)] sizes = [50, 10]
labels = [f"{s} GW" for s in sizes]
sizes = [s/bus_size_factor*1e3 for s in sizes]
l2 = ax.legend( legend_kw = dict(
handles, labels,
loc="upper left", loc="upper left",
bbox_to_anchor=(-0.03, 1.01), bbox_to_anchor=(0, 1),
labelspacing=1.0, labelspacing=0.8,
handletextpad=0,
frameon=False, frameon=False,
title='Electrolyzer capacity',
handler_map=make_handler_map_to_scale_circles_as_in(ax)
) )
ax.add_artist(l2) add_legend_circles(ax, sizes, labels,
srid=n.srid,
patch_kw=dict(facecolor='lightgrey'),
legend_kw=legend_kw
)
handles = [] sizes = [30, 10]
labels = [] labels = [f"{s} GW" for s in sizes]
scale = 1e3 / linewidth_factor
sizes = [s*scale for s in sizes]
for s in (50, 10): legend_kw = dict(
handles.append(plt.Line2D([0], [0], color="grey",
linewidth=s * 1e3 / linewidth_factor))
labels.append("{} GW".format(s))
l1_1 = ax.legend(
handles, labels,
loc="upper left", loc="upper left",
bbox_to_anchor=(0.28, 1.01), bbox_to_anchor=(0.23, 1),
frameon=False, frameon=False,
labelspacing=0.8, labelspacing=0.8,
handletextpad=1.5, handletextpad=1,
title='H2 pipeline capacity'
) )
ax.add_artist(l1_1) add_legend_lines(
ax,
sizes,
labels,
patch_kw=dict(color='lightgrey'),
legend_kw=legend_kw,
)
colors = [bus_colors[c] for c in carriers] + [color_h2_pipe, color_retrofit]
labels = carriers + ["H2 pipeline (total)", "H2 pipeline (repurposed)"]
legend_kw = dict(
loc="upper left",
bbox_to_anchor=(0, 1.13),
ncol=2,
frameon=False,
)
add_legend_patches(
ax,
colors,
labels,
legend_kw=legend_kw
)
ax.set_facecolor("white")
fig.savefig( fig.savefig(
snakemake.output.map.replace("-costs-all","-h2_network"), snakemake.output.map.replace("-costs-all","-h2_network"),
@ -415,26 +495,32 @@ def plot_ch4_map(network):
link_widths_used = max_usage / linewidth_factor link_widths_used = max_usage / linewidth_factor
link_widths_used[max_usage < line_lower_threshold] = 0. link_widths_used[max_usage < line_lower_threshold] = 0.
link_color_used = n.links.carrier.map({"gas pipeline": "#f08080", tech_colors = snakemake.config['plotting']['tech_colors']
"gas pipeline new": "#c46868"})
pipe_colors = {
"gas pipeline": "#f08080",
"gas pipeline new": "#c46868",
"gas pipeline (in 2020)": 'lightgrey',
"gas pipeline (available)": '#e8d1d1',
}
link_color_used = n.links.carrier.map(pipe_colors)
n.links.bus0 = n.links.bus0.str.replace(" gas", "") n.links.bus0 = n.links.bus0.str.replace(" gas", "")
n.links.bus1 = n.links.bus1.str.replace(" gas", "") n.links.bus1 = n.links.bus1.str.replace(" gas", "")
tech_colors = snakemake.config['plotting']['tech_colors']
bus_colors = { bus_colors = {
"fossil gas": tech_colors["fossil gas"], "fossil gas": tech_colors["fossil gas"],
"methanation": tech_colors["methanation"], "methanation": tech_colors["methanation"],
"biogas": "seagreen" "biogas": "seagreen"
} }
fig, ax = plt.subplots(figsize=(7,6), subplot_kw={"projection": ccrs.PlateCarree()}) fig, ax = plt.subplots(figsize=(7,6), subplot_kw={"projection": ccrs.EqualEarth()})
n.plot( n.plot(
bus_sizes=bus_sizes, bus_sizes=bus_sizes,
bus_colors=bus_colors, bus_colors=bus_colors,
link_colors='lightgrey', link_colors=pipe_colors['gas pipeline (in 2020)'],
link_widths=link_widths_orig, link_widths=link_widths_orig,
branch_components=["Link"], branch_components=["Link"],
ax=ax, ax=ax,
@ -444,10 +530,11 @@ def plot_ch4_map(network):
n.plot( n.plot(
ax=ax, ax=ax,
bus_sizes=0., bus_sizes=0.,
link_colors='#e8d1d1', link_colors=pipe_colors['gas pipeline (available)'],
link_widths=link_widths_rem, link_widths=link_widths_rem,
branch_components=["Link"], branch_components=["Link"],
**map_opts color_geomap=False,
boundaries=map_opts["boundaries"]
) )
n.plot( n.plot(
@ -456,46 +543,76 @@ def plot_ch4_map(network):
link_colors=link_color_used, link_colors=link_color_used,
link_widths=link_widths_used, link_widths=link_widths_used,
branch_components=["Link"], branch_components=["Link"],
**map_opts color_geomap=False,
boundaries=map_opts["boundaries"]
) )
handles = make_legend_circles_for( sizes = [100, 10]
[10e6, 100e6], labels = [f"{s} TWh" for s in sizes]
scale=bus_size_factor, sizes = [s/bus_size_factor*1e6 for s in sizes]
facecolor='grey'
)
labels = ["{} TWh".format(s) for s in (10, 100)]
l2 = ax.legend( legend_kw = dict(
handles, labels,
loc="upper left", loc="upper left",
bbox_to_anchor=(-0.03, 1.01), bbox_to_anchor=(0, 1.03),
labelspacing=1.0, labelspacing=0.8,
frameon=False, frameon=False,
title='gas generation', handletextpad=1,
handler_map=make_handler_map_to_scale_circles_as_in(ax) title='gas sources',
) )
ax.add_artist(l2) add_legend_circles(
ax,
sizes,
labels,
srid=n.srid,
patch_kw=dict(facecolor='lightgrey'),
legend_kw=legend_kw,
)
handles = [] sizes = [50, 10]
labels = [] labels = [f"{s} GW" for s in sizes]
scale = 1e3 / linewidth_factor
sizes = [s*scale for s in sizes]
for s in (50, 10): legend_kw = dict(
handles.append(plt.Line2D([0], [0], color="grey", linewidth=s * 1e3 / linewidth_factor))
labels.append("{} GW".format(s))
l1_1 = ax.legend(
handles, labels,
loc="upper left", loc="upper left",
bbox_to_anchor=(0.28, 1.01), bbox_to_anchor=(0.25, 1.03),
frameon=False, frameon=False,
labelspacing=0.8, labelspacing=0.8,
handletextpad=1.5, handletextpad=1,
title='gas pipeline used capacity' title='gas pipeline'
) )
ax.add_artist(l1_1) add_legend_lines(
ax,
sizes,
labels,
patch_kw=dict(color='lightgrey'),
legend_kw=legend_kw,
)
colors = list(pipe_colors.values()) + list(bus_colors.values())
labels = list(pipe_colors.keys()) + list(bus_colors.keys())
# legend on the side
# legend_kw = dict(
# bbox_to_anchor=(1.47, 1.04),
# frameon=False,
# )
legend_kw = dict(
loc='upper left',
bbox_to_anchor=(0, 1.24),
ncol=2,
frameon=False,
)
add_legend_patches(
ax,
colors,
labels,
legend_kw=legend_kw,
)
fig.savefig( fig.savefig(
snakemake.output.map.replace("-costs-all","-ch4_network"), snakemake.output.map.replace("-costs-all","-ch4_network"),
@ -513,15 +630,15 @@ def plot_map_without(network):
fig, ax = plt.subplots( fig, ax = plt.subplots(
figsize=(7, 6), figsize=(7, 6),
subplot_kw={"projection": ccrs.PlateCarree()} subplot_kw={"projection": ccrs.EqualEarth()}
) )
# PDF has minimum width, so set these to zero # PDF has minimum width, so set these to zero
line_lower_threshold = 200. line_lower_threshold = 200.
line_upper_threshold = 1e4 line_upper_threshold = 1e4
linewidth_factor = 2e3 linewidth_factor = 3e3
ac_color = "gray" ac_color = "rosybrown"
dc_color = "m" dc_color = "darkseagreen"
# hack because impossible to drop buses... # hack because impossible to drop buses...
if "EU gas" in n.buses.index: if "EU gas" in n.buses.index:
@ -560,7 +677,7 @@ def plot_map_without(network):
for s in (10, 5): for s in (10, 5):
handles.append(plt.Line2D([0], [0], color=ac_color, handles.append(plt.Line2D([0], [0], color=ac_color,
linewidth=s * 1e3 / linewidth_factor)) linewidth=s * 1e3 / linewidth_factor))
labels.append("{} GW".format(s)) labels.append(f"{s} GW")
l1_1 = ax.legend(handles, labels, l1_1 = ax.legend(handles, labels,
loc="upper left", bbox_to_anchor=(0.05, 1.01), loc="upper left", bbox_to_anchor=(0.05, 1.01),
frameon=False, frameon=False,
@ -710,25 +827,27 @@ if __name__ == "__main__":
snakemake = mock_snakemake( snakemake = mock_snakemake(
'plot_network', 'plot_network',
simpl='', simpl='',
clusters="45", clusters="181",
lv=1.0, lv='opt',
opts='', opts='',
sector_opts='168H-T-H-B-I-A-solar+p3-dist1', sector_opts='Co2L0-730H-T-H-B-I-A-solar+p3-linemaxext10',
planning_horizons="2050", planning_horizons="2050",
) )
overrides = override_component_attrs(snakemake.input.overrides) overrides = override_component_attrs(snakemake.input.overrides)
n = pypsa.Network(snakemake.input.network, override_component_attrs=overrides) n = pypsa.Network(snakemake.input.network, override_component_attrs=overrides)
regions = gpd.read_file(snakemake.input.regions).set_index("name")
map_opts = snakemake.config['plotting']['map'] map_opts = snakemake.config['plotting']['map']
plot_map(n, plot_map(n,
components=["generators", "links", "stores", "storage_units"], components=["generators", "links", "stores", "storage_units"],
bus_size_factor=1.5e10, bus_size_factor=2e10,
transmission=False transmission=False
) )
plot_h2_map(n) plot_h2_map(n, regions)
plot_ch4_map(n) plot_ch4_map(n)
plot_map_without(n) plot_map_without(n)