From be23b5c56a31e383003d3849a55bbdeffb6e4428 Mon Sep 17 00:00:00 2001 From: lisazeyen Date: Thu, 24 Aug 2023 15:32:23 +0200 Subject: [PATCH] add network plot for perfect --- rules/postprocess.smk | 12 +-- scripts/plot_network.py | 183 ++++++++++++++++++++++++++++++++++++---- 2 files changed, 174 insertions(+), 21 deletions(-) diff --git a/rules/postprocess.smk b/rules/postprocess.smk index ca1d6fac..32775220 100644 --- a/rules/postprocess.smk +++ b/rules/postprocess.smk @@ -44,16 +44,18 @@ if config["foresight"] == "perfect": + "postnetworks/elec_s{simpl}_{clusters}_l{ll}_{opts}_{sector_opts}_brownfield_all_years.nc", regions=RESOURCES + "regions_onshore_elec_s{simpl}_{clusters}.geojson", output: - map=RESULTS - + "maps/elec_s{simpl}_{clusters}_l{ll}_{opts}_{sector_opts}-costs-all_{year}.pdf", - today=RESULTS - + "maps/elec_s{simpl}_{clusters}_l{ll}_{opts}_{sector_opts}_{year}-today.pdf", + **{ + f"map_{year}": RESULTS + + "maps/elec_s{simpl}_{clusters}_l{ll}_{opts}_{sector_opts}-costs-all_" + + f"{year}.pdf" + for year in config["scenario"]["planning_horizons"] + }, threads: 2 resources: mem_mb=10000, benchmark: BENCHMARKS - + "postnetworks/elec_s{simpl}_{clusters}_l{ll}_{opts}_{sector_opts}_{year}_brownfield_all_years_benchmark.txt", + + "postnetworks/elec_s{simpl}_{clusters}_l{ll}_{opts}_{sector_opts}_brownfield_all_years_benchmark", conda: "../envs/environment.yaml" script: diff --git a/scripts/plot_network.py b/scripts/plot_network.py index ae1d0e0a..3203f5ab 100644 --- a/scripts/plot_network.py +++ b/scripts/plot_network.py @@ -24,7 +24,7 @@ from make_summary import assign_carriers from plot_summary import preferred_order, rename_techs from pypsa.plot import add_legend_circles, add_legend_lines, add_legend_patches -plt.style.use(["ggplot", "matplotlibrc"]) +plt.style.use(["ggplot"]) def rename_techs_tyndp(tech): @@ -913,6 +913,153 @@ def plot_series(network, carrier="AC", name="test"): ) +def plot_map_perfect(network, components=["Link", "Store", "StorageUnit", "Generator"], + bus_size_factor=1.7e10): + + n = network.copy() + assign_location(n) + # Drop non-electric buses so they don't clutter the plot + n.buses.drop(n.buses.index[n.buses.carrier != "AC"], inplace=True) + # investment periods + investments = n.snapshots.levels[0] + + costs = {} + for comp in components: + df_c = n.df(comp) + if df_c.empty: continue + df_c["nice_group"] = df_c.carrier.map(rename_techs_tyndp) + + attr = "e_nom_opt" if comp == "Store" else "p_nom_opt" + + active = pd.concat( + [ + n.get_active_assets(comp, inv_p).rename(inv_p) + for inv_p in investments + ], + axis=1, + ).astype(int) + capital_cost = n.df(comp)[attr] * n.df(comp).capital_cost + capital_cost_t = ((active.mul(capital_cost, axis=0)) + .groupby([n.df(comp).location, + n.df(comp).nice_group]).sum()) + + capital_cost_t.drop("load", level=1, inplace=True, errors="ignore") + + costs[comp] = capital_cost_t + + costs = pd.concat(costs).groupby(level=[1,2]).sum() + costs.drop(costs[costs.sum(axis=1)==0].index, inplace=True) + + new_columns = (preferred_order.intersection(costs.index.levels[1]) + .append(costs.index.levels[1].difference(preferred_order))) + costs = costs.reindex(new_columns, level=1) + + for item in new_columns: + if item not in snakemake.config['plotting']['tech_colors']: + print("Warning!",item,"not in config/plotting/tech_colors, assign random color") + snakemake.config['plotting']['tech_colors'] = "pink" + + n.links.drop(n.links.index[(n.links.carrier != "DC") & ( + n.links.carrier != "B2B")], inplace=True) + + # drop non-bus + to_drop = costs.index.levels[0].symmetric_difference(n.buses.index) + if len(to_drop) != 0: + print("dropping non-buses", to_drop) + costs.drop(to_drop, level=0, inplace=True, axis=0, errors="ignore") + + # make sure they are removed from index + costs.index = pd.MultiIndex.from_tuples(costs.index.values) + + # PDF has minimum width, so set these to zero + line_lower_threshold = 500. + line_upper_threshold = 1e4 + linewidth_factor = 2e3 + ac_color = "gray" + dc_color = "m" + + line_widths = n.lines.s_nom_opt + link_widths = n.links.p_nom_opt + linewidth_factor = 2e3 + line_lower_threshold = 0. + title = "Today's transmission" + + line_widths[line_widths < line_lower_threshold] = 0. + link_widths[link_widths < line_lower_threshold] = 0. + + line_widths[line_widths > line_upper_threshold] = line_upper_threshold + link_widths[link_widths > line_upper_threshold] = line_upper_threshold + + for year in costs.columns: + + fig, ax = plt.subplots(subplot_kw={"projection": ccrs.PlateCarree()}) + fig.set_size_inches(7, 6) + fig.suptitle(year) + + n.plot( + bus_sizes=costs[year] / bus_size_factor, + bus_colors=snakemake.config['plotting']['tech_colors'], + line_colors=ac_color, + link_colors=dc_color, + line_widths=line_widths / linewidth_factor, + link_widths=link_widths / linewidth_factor, + ax=ax, **map_opts + ) + + sizes = [20, 10, 5] + labels = [f"{s} bEUR/a" for s in sizes] + sizes = [s / bus_size_factor * 1e9 for s in sizes] + + legend_kw = dict( + loc="upper left", + bbox_to_anchor=(0.01, 1.06), + labelspacing=0.8, + frameon=False, + handletextpad=0, + title="system cost", + ) + + add_legend_circles( + ax, + sizes, + labels, + srid=n.srid, + patch_kw=dict(facecolor="lightgrey"), + legend_kw=legend_kw, + ) + + sizes = [10, 5] + labels = [f"{s} GW" for s in sizes] + scale = 1e3 / linewidth_factor + sizes = [s * scale for s in sizes] + + legend_kw = dict( + loc="upper left", + bbox_to_anchor=(0.27, 1.06), + frameon=False, + labelspacing=0.8, + handletextpad=1, + title=title, + ) + + 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, + ) + + + fig.savefig( + snakemake.output[f"map_{year}"], + transparent=True, + bbox_inches="tight" + ) + + +#%% if __name__ == "__main__": if "snakemake" not in globals(): from _helpers import mock_snakemake @@ -921,10 +1068,9 @@ if __name__ == "__main__": "plot_network", simpl="", opts="", - clusters="5", - ll="v1.5", - sector_opts="CO2L0-1H-T-H-B-I-A-solar+p3-dist1", - planning_horizons="2030", + clusters="37", + ll="v1.0", + sector_opts="4380H-T-H-B-I-A-solar+p3-dist1", ) logging.basicConfig(level=snakemake.config["logging"]["level"]) @@ -937,17 +1083,22 @@ if __name__ == "__main__": if map_opts["boundaries"] is None: map_opts["boundaries"] = regions.total_bounds[[0, 2, 1, 3]] + [-1, 1, -1, 1] - - plot_map( - n, - components=["generators", "links", "stores", "storage_units"], - bus_size_factor=2e10, - transmission=False, - ) - - plot_h2_map(n, regions) - plot_ch4_map(n) - plot_map_without(n) + + if snakemake.params["foresight"] == "perfect": + plot_map_perfect(n, + components=["Link", "Store", "StorageUnit", "Generator"], + bus_size_factor=2e10) + else: + plot_map( + n, + components=["generators", "links", "stores", "storage_units"], + bus_size_factor=2e10, + transmission=False, + ) + + plot_h2_map(n, regions) + plot_ch4_map(n) + plot_map_without(n) # plot_series(n, carrier="AC", name=suffix) # plot_series(n, carrier="heat", name=suffix)