From 540985164f4aee296a93baa18145d65a24621a05 Mon Sep 17 00:00:00 2001 From: Tom Brown Date: Thu, 2 Aug 2018 16:14:38 +0200 Subject: [PATCH] Plot summary; take account of snapshot weightings correctly --- config.yaml | 128 +++++++++++++++------------ scripts/make_summary.py | 10 +-- scripts/plot_summary.py | 185 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 264 insertions(+), 59 deletions(-) create mode 100644 scripts/plot_summary.py diff --git a/config.yaml b/config.yaml index f4454096..e0c9f7b6 100644 --- a/config.yaml +++ b/config.yaml @@ -180,6 +180,13 @@ plotting: linewidth_factor: 3.e+3 # 1.e+3 #3.e+3 costs_max: 800 + costs_threshold: 1 + + + energy_max: 15000. + energy_min: -10000. + energy_threshold: 50. + vre_techs: ["onwind", "offwind", "solar", "ror"] conv_techs: ["OCGT", "CCGT", "Nuclear", "Coal"] @@ -193,60 +200,73 @@ plotting: "central CHP electric", "central gas boiler"] heat_generators: ["gas boiler", "central gas boiler", "solar thermal collector", "central solar thermal collector"] tech_colors: - onwind: "xkcd:azure" - offwind: "blue" - hydro: "g" - ror: "lightgreen" - PHS: "g" - hydro+PHS: "g" - solar: "yellow" - OCGT: "brown" - OCGT marginal: "sandybrown" - OCGT-heat: "orange" - central gas boiler: "orange" - gas boiler: "orange" - gas boilers: "orange" - gas boiler marginal: "orange" - gas: "brown" - lines: "k" - AC line: "k" - AC-AC: "k" - transmission lines: "k" - H2: "m" - hydrogen storage: "m" - battery: "slategray" - battery storage: "slategray" - CAES: "lightgray" - nuclear: "r" - nuclear marginal: "r" - coal: "k" - coal marginal: "k" - lignite: "grey" - lignite marginal: "grey" - CCGT: "orange" - CCGT marginal: "orange" - diesel: "darkred" - diesel marginal: "darkred" - heat pumps: "green" - heat pump: "green" - central heat pump: "green" - resistive heater: "pink" - central resistive heater: "pink" - Sabatier: "turquoise" - water tanks: "w" - CHP: "r" - CHP heat: "r" - CHP electric: "r" - central CHP heat: "r" - central CHP electric: "r" - Pumped storage: "g" - Ambient: "k" - AC load: "b" - Heat load: "r" - Li ion load: "grey" - heat: "r" - Li ion: "grey" - district heating: "#CC4E5C" + "onwind" : "b" + "onshore wind" : "b" + 'offwind' : "c" + 'offshore wind' : "c" + "hydro" : "#3B5323" + "hydro reservoir" : "#3B5323" + "ror" : "#78AB46" + "run of river" : "#78AB46" + 'hydroelectricity' : '#006400' + 'solar' : "y" + 'solar PV' : "y" + 'solar thermal' : 'coral' + "OCGT" : "wheat" + "OCGT marginal" : "sandybrown" + "OCGT-heat" : "orange" + "gas boiler" : "orange" + "gas boilers" : "orange" + "gas boiler marginal" : "orange" + "gas" : "brown" + "natural gas" : "brown" + "lines" : "k" + "transmission lines" : "k" + "H2" : "m" + "hydrogen storage" : "m" + "battery" : "slategray" + "battery storage" : "slategray" + "Nuclear" : "r" + "Nuclear marginal" : "r" + "Coal" : "k" + "Coal marginal" : "k" + "Lignite" : "grey" + "Lignite marginal" : "grey" + "CCGT" : "orange" + "CCGT marginal" : "orange" + "heat pumps" : "#76EE00" + "heat pump" : "#76EE00" + "air heat pump" : "#76EE00" + "ground heat pump" : "#40AA00" + "resistive heater" : "pink" + "Sabatier" : "#FF1493" + "methanation" : "#FF1493" + "helmeth" : "#7D0552" + "helmeth" : "#7D0552" + "DAC" : "#E74C3C" + "nuclear" : "#303030" + "water tanks" : "#BBBBBB" + "hot water storage" : "#BBBBBB" + "hot water charging" : "#BBBBBB" + "hot water discharging" : "#999999" + "CHP" : "r" + "CHP heat" : "r" + "CHP electric" : "r" + "PHS" : "g" + "Ambient" : "k" + "Electric load" : "b" + "Heat load" : "r" + "Transport load" : "grey" + "heat" : "r" + "Li ion" : "grey" + "district heating" : "#CC4E5C" + "retrofitting" : "purple" + "building retrofitting" : "purple" + "BEV charger" : "grey" + "V2G" : "grey" + "transport" : "grey" + "electricity" : "k" + "transport fuel cell" : "#AAAAAA" nice_names: # OCGT: "Gas" # OCGT marginal: "Gas (marginal)" diff --git a/scripts/make_summary.py b/scripts/make_summary.py index f8de9db1..50e0b3d3 100644 --- a/scripts/make_summary.py +++ b/scripts/make_summary.py @@ -50,15 +50,15 @@ def calculate_costs(n,label,costs): costs.loc[idx[c.list_name,"capital",list(capital_costs_grouped.index)],label] = capital_costs_grouped.values if c.name == "Link": - p = c.pnl.p0.sum() + p = c.pnl.p0.multiply(n.snapshot_weightings,axis=0).sum() elif c.name == "Line": continue elif c.name == "StorageUnit": - p_all = c.pnl.p.copy() + p_all = c.pnl.p.multiply(n.snapshot_weightings,axis=0) p_all[p_all < 0.] = 0. p = p_all.sum() else: - p = c.pnl.p.sum() + p = c.pnl.p.multiply(n.snapshot_weightings,axis=0).sum() marginal_costs = p*c.df.marginal_cost @@ -97,9 +97,9 @@ def calculate_energy(n,label,energy): for c in n.iterate_components(n.one_port_components|n.branch_components): if c.name in n.one_port_components: - c_energies = c.pnl.p.sum().multiply(c.df.sign).groupby(c.df.carrier).sum() + c_energies = c.pnl.p.multiply(n.snapshot_weightings,axis=0).sum().multiply(c.df.sign).groupby(c.df.carrier).sum() else: - c_energies = (-c.pnl.p1.sum() - c.pnl.p0.sum()).groupby(c.df.carrier).sum() + c_energies = (-c.pnl.p1.multiply(n.snapshot_weightings,axis=0).sum() - c.pnl.p0.multiply(n.snapshot_weightings,axis=0).sum()).groupby(c.df.carrier).sum() energy = energy.reindex(energy.index|pd.MultiIndex.from_product([[c.list_name],c_energies.index])) diff --git a/scripts/plot_summary.py b/scripts/plot_summary.py new file mode 100644 index 00000000..83461f15 --- /dev/null +++ b/scripts/plot_summary.py @@ -0,0 +1,185 @@ + + + +import pandas as pd + +#allow plotting without Xwindows +import matplotlib +matplotlib.use('Agg') + +import matplotlib.pyplot as plt + + + +#consolidate and rename +def rename_techs(label): + if label[:8] == "central ": + label = label[8:] + if label[:6] == "urban ": + label = label[6:] + + if "retrofitting" in label: + label = "building retrofitting" + if "H2" in label: + label = "hydrogen storage" + if "CHP" in label: + label = "CHP" + if "water tank" in label: + label = "water tanks" + if label=="water tanks": + label = "hot water storage" + if "gas" in label and label != "gas boiler": + label = "natural gas" + if "solar thermal" in label: + label = "solar thermal" + if label == "solar": + label = "solar PV" + if label == "heat pump": + label = "air heat pump" + if label == "Sabatier": + label = "methanation" + if label == "offwind": + label = "offshore wind" + if label == "onwind": + label = "onshore wind" + if label == "ror": + label = "hydroelectricity" + if label == "hydro": + label = "hydroelectricity" + if label == "PHS": + label = "hydroelectricity" + if label == "co2 Store": + label = "DAC" + if "battery" in label: + label = "battery storage" + + return label + + +preferred_order = pd.Index(["transmission lines","hydroelectricity","hydro reservoir","run of river","pumped hydro storage","onshore wind","offshore wind","solar PV","solar thermal","building retrofitting","ground heat pump","air heat pump","resistive heater","CHP","OCGT","gas boiler","gas","natural gas","methanation","hydrogen storage","battery storage","hot water storage"]) + +def plot_costs(): + + + cost_df = pd.read_csv(snakemake.input.costs,index_col=list(range(3)),header=[0,1,2]) + + + df = cost_df.groupby(cost_df.index.get_level_values(2)).sum() + + #convert to billions + df = df/1e9 + + df = df.groupby(df.index.map(rename_techs)).sum() + + to_drop = df.index[df.max(axis=1) < snakemake.config['plotting']['costs_threshold']] + + print("dropping") + + print(df.loc[to_drop]) + + df = df.drop(to_drop) + + print(df.sum()) + + new_index = (preferred_order&df.index).append(df.index.difference(preferred_order)) + + new_columns = df.sum().sort_values().index + + fig, ax = plt.subplots() + fig.set_size_inches((12,8)) + + df.loc[new_index,new_columns].T.plot(kind="bar",ax=ax,stacked=True,color=[snakemake.config['plotting']['tech_colors'][i] for i in new_index]) + + + handles,labels = ax.get_legend_handles_labels() + + handles.reverse() + labels.reverse() + + ax.set_ylim([0,snakemake.config['plotting']['costs_max']]) + + ax.set_ylabel("System Cost [EUR billion per year]") + + ax.set_xlabel("") + + ax.grid(axis="y") + + ax.legend(handles,labels,ncol=4,loc="upper left") + + + fig.tight_layout() + + fig.savefig(snakemake.output.costs,transparent=True) + + +def plot_energy(): + + energy_df = pd.read_csv(snakemake.input.energy,index_col=list(range(2)),header=[0,1,2]) + + df = energy_df.groupby(energy_df.index.get_level_values(1)).sum() + + #convert MWh to TWh + df = df/1e6 + + df = df.groupby(df.index.map(rename_techs)).sum() + + to_drop = df.index[df.abs().max(axis=1) < snakemake.config['plotting']['energy_threshold']] + + print("dropping") + + print(df.loc[to_drop]) + + df = df.drop(to_drop) + + print(df.sum()) + + new_index = (preferred_order&df.index).append(df.index.difference(preferred_order)) + + new_columns = df.columns.sort_values() + + fig, ax = plt.subplots() + fig.set_size_inches((12,8)) + + df.loc[new_index,new_columns].T.plot(kind="bar",ax=ax,stacked=True,color=[snakemake.config['plotting']['tech_colors'][i] for i in new_index]) + + + handles,labels = ax.get_legend_handles_labels() + + handles.reverse() + labels.reverse() + + ax.set_ylim([snakemake.config['plotting']['energy_min'],snakemake.config['plotting']['energy_max']]) + + ax.set_ylabel("Energy [TWh/a]") + + ax.set_xlabel("") + + ax.grid(axis="y") + + ax.legend(handles,labels,ncol=4,loc="upper left") + + + fig.tight_layout() + + fig.savefig(snakemake.output.energy,transparent=True) + + +if __name__ == "__main__": + # Detect running outside of snakemake and mock snakemake for testing + if 'snakemake' not in globals(): + from vresutils import Dict + import yaml + snakemake = Dict() + with open('config.yaml') as f: + snakemake.config = yaml.load(f) + snakemake.input = Dict() + snakemake.output = Dict() + name = "37-lv" + + for item in ["costs","energy"]: + snakemake.input[item] = snakemake.config['summary_dir'] + '/{name}/csvs/{item}.csv'.format(name=name,item=item) + snakemake.output[item] = snakemake.config['summary_dir'] + '/{name}/graphs/{item}.pdf'.format(name=name,item=item) + + plot_costs() + + plot_energy()