diff --git a/etrago/tools/calc_results.py b/etrago/tools/calc_results.py index fca593a9..bf1edac8 100755 --- a/etrago/tools/calc_results.py +++ b/etrago/tools/calc_results.py @@ -22,25 +22,27 @@ calc_results.py defines methods to calculate results of eTraGo """ import os -if 'READTHEDOCS' not in os.environ: - import time + +if "READTHEDOCS" not in os.environ: import logging import pandas as pd - import numpy as np logger = logging.getLogger(__name__) -__copyright__ = ("Flensburg University of Applied Sciences, " - "Europa-Universität Flensburg, " - "Centre for Sustainable Energy Systems, " - "DLR-Institute for Networked Energy Systems") +__copyright__ = ( + "Flensburg University of Applied Sciences, " + "Europa-Universität Flensburg, " + "Centre for Sustainable Energy Systems, " + "DLR-Institute for Networked Energy Systems" +) __license__ = "GNU Affero General Public License Version 3 (AGPL-3.0)" __author__ = "ulfmueller, s3pp, wolfbunke, mariusves, lukasol" def _calc_storage_expansion(self): - """ Function that calulates storage expansion in MW + """Function that calulates storage expansion in MW + Returns ------- @@ -48,14 +50,18 @@ def _calc_storage_expansion(self): storage expansion in MW """ - return (self.network.storage_units.p_nom_opt - - self.network.storage_units.p_nom_min - )[self.network.storage_units.p_nom_extendable]\ - .groupby(self.network.storage_units.carrier).sum() + return ( + ( + self.network.storage_units.p_nom_opt + - self.network.storage_units.p_nom_min + )[self.network.storage_units.p_nom_extendable] + .groupby(self.network.storage_units.carrier) + .sum() + ) def _calc_store_expansion(self): - """ Function that calulates store expansion in MW + """Function that calulates store expansion in MW Returns ------- @@ -63,13 +69,13 @@ def _calc_store_expansion(self): store expansion in MW """ - return (self.network.stores.e_nom_opt - - self.network.stores.e_nom_min - )[self.network.stores.e_nom_extendable] + return (self.network.stores.e_nom_opt - self.network.stores.e_nom_min)[ + self.network.stores.e_nom_extendable + ] def _calc_sectorcoupling_link_expansion(self): - """ Function that calulates expansion of sectorcoupling links in MW + """Function that calulates expansion of sectorcoupling links in MW Returns ------- @@ -81,21 +87,21 @@ def _calc_sectorcoupling_link_expansion(self): links = [0, 0, 0, 0] - l1 = ext_links[ext_links.carrier=='H2_to_power'] - l2 = ext_links[ext_links.carrier=='power_to_H2'] - l3 = ext_links[ext_links.carrier=='H2_to_CH4'] - l4 = ext_links[ext_links.carrier=='CH4_to_H2'] + l1 = ext_links[ext_links.carrier == "H2_to_power"] + l2 = ext_links[ext_links.carrier == "power_to_H2"] + l3 = ext_links[ext_links.carrier == "H2_to_CH4"] + l4 = ext_links[ext_links.carrier == "CH4_to_H2"] - links[0] = (l1.p_nom_opt-l1.p_nom_min).sum() - links[1] = (l2.p_nom_opt-l2.p_nom_min).sum() - links[2] = (l3.p_nom_opt-l3.p_nom_min).sum() - links[3] = (l4.p_nom_opt-l4.p_nom_min).sum() + links[0] = (l1.p_nom_opt - l1.p_nom_min).sum() + links[1] = (l2.p_nom_opt - l2.p_nom_min).sum() + links[2] = (l3.p_nom_opt - l3.p_nom_min).sum() + links[3] = (l4.p_nom_opt - l4.p_nom_min).sum() return links def _calc_network_expansion(self): - """ Function that calulates electrical network expansion in MW + """Function that calulates electrical network expansion in MW Returns ------- @@ -106,21 +112,20 @@ def _calc_network_expansion(self): network = self.network - lines = (network.lines.s_nom_opt - - network.lines.s_nom_min - )[network.lines.s_nom_extendable] + lines = (network.lines.s_nom_opt - network.lines.s_nom_min)[ + network.lines.s_nom_extendable + ] ext_links = network.links[network.links.p_nom_extendable] - ext_dc_lines = ext_links[ext_links.carrier=='DC'] + ext_dc_lines = ext_links[ext_links.carrier == "DC"] - dc_links = (ext_dc_lines.p_nom_opt - - ext_dc_lines.p_nom_min) + dc_links = ext_dc_lines.p_nom_opt - ext_dc_lines.p_nom_min return lines, dc_links def calc_investment_cost(self): - """ Function that calulates overall annualized investment costs. + """Function that calulates overall annualized investment costs. Returns ------- @@ -141,30 +146,40 @@ def calc_investment_cost(self): ext_lines = network.lines[network.lines.s_nom_extendable] ext_trafos = network.transformers[network.transformers.s_nom_extendable] ext_links = network.links[network.links.p_nom_extendable] - ext_dc_lines = ext_links[ext_links.carrier=='DC'] + ext_dc_lines = ext_links[ext_links.carrier == "DC"] if not ext_lines.empty: - network_costs[0] = ((ext_lines.s_nom_opt-ext_lines.s_nom_min - )*ext_lines.capital_cost).sum() + network_costs[0] = ( + (ext_lines.s_nom_opt - ext_lines.s_nom_min) + * ext_lines.capital_cost + ).sum() if not ext_trafos.empty: - network_costs[0] = network_costs[0]+(( - ext_trafos.s_nom_opt-ext_trafos.s_nom - )*ext_trafos.capital_cost).sum() + network_costs[0] = ( + network_costs[0] + + ( + (ext_trafos.s_nom_opt - ext_trafos.s_nom) + * ext_trafos.capital_cost + ).sum() + ) if not ext_dc_lines.empty: - network_costs[1] = ((ext_dc_lines.p_nom_opt-ext_dc_lines.p_nom_min - )*ext_dc_lines.capital_cost).sum() + network_costs[1] = ( + (ext_dc_lines.p_nom_opt - ext_dc_lines.p_nom_min) + * ext_dc_lines.capital_cost + ).sum() # links in other sectors / coupling different sectors link_costs = 0 - ext_links = ext_links[ext_links.carrier!='DC'] + ext_links = ext_links[ext_links.carrier != "DC"] if not ext_links.empty: - link_costs = ((ext_links.p_nom_opt-ext_links.p_nom_min - )*ext_links.capital_cost).sum() + link_costs = ( + (ext_links.p_nom_opt - ext_links.p_nom_min) + * ext_links.capital_cost + ).sum() # storage and store costs @@ -174,12 +189,10 @@ def calc_investment_cost(self): ext_store = network.stores[network.stores.e_nom_extendable] if not ext_storage.empty: - sto_costs[0] = (ext_storage.p_nom_opt* - ext_storage.capital_cost).sum() + sto_costs[0] = (ext_storage.p_nom_opt * ext_storage.capital_cost).sum() if not ext_store.empty: - sto_costs[1] = (ext_store.e_nom_opt* - ext_store.capital_cost).sum() + sto_costs[1] = (ext_store.e_nom_opt * ext_store.capital_cost).sum() return network_costs, link_costs, sto_costs @@ -196,121 +209,565 @@ def calc_marginal_cost(self): """ network = self.network - gen = network.generators_t.p.mul( - network.snapshot_weightings.objective, axis=0).sum(axis=0).mul( - network.generators.marginal_cost).sum() - link = abs(network.links_t.p0).mul( - network.snapshot_weightings.objective, axis=0).sum(axis=0).mul( - network.links.marginal_cost).sum() - stor = network.storage_units_t.p.mul( - network.snapshot_weightings.objective, axis=0).sum(axis=0).mul( - network.storage_units.marginal_cost).sum() + gen = ( + network.generators_t.p.mul( + network.snapshot_weightings.objective, axis=0 + ) + .sum(axis=0) + .mul(network.generators.marginal_cost) + .sum() + ) + link = ( + abs(network.links_t.p0) + .mul(network.snapshot_weightings.objective, axis=0) + .sum(axis=0) + .mul(network.links.marginal_cost) + .sum() + ) + stor = ( + network.storage_units_t.p.mul( + network.snapshot_weightings.objective, axis=0 + ) + .sum(axis=0) + .mul(network.storage_units.marginal_cost) + .sum() + ) marginal_cost = gen + link + stor return marginal_cost -def calc_etrago_results(self): - """ Function that calculates main results of grid optimization - and adds them to Etrago object. +def german_network(self): + """Cut out all network components in Germany Returns ------- - None. + new_network : pypsa.Network + Network with all components in Germany """ - self.results = pd.DataFrame(columns=['unit', 'value'], - index=['annual system costs', - 'annual investment costs', - 'annual marginal costs', - 'annual electrical grid investment costs', - 'annual ac grid investment costs', - 'annual dc grid investment costs', - 'annual links investment costs', - 'annual storage+store investment costs', - 'annual electrical storage investment costs', - 'annual store investment costs', - 'battery storage expansion', - 'store expansion', - 'H2 store expansion', - 'CH4 store expansion', - 'heat store expansion', - 'storage+store expansion', - 'fuel cell links expansion', - 'electrolyzer links expansion', - 'methanisation links expansion', - 'Steam Methane Reformation links expansion', - 'abs. electrical grid expansion', - 'abs. electrical ac grid expansion', - 'abs. electrical dc grid expansion', - 'rel. electrical ac grid expansion', - 'rel. electrical dc grid expansion']) - - self.results.unit[self.results.index.str.contains('cost')] = 'EUR/a' - self.results.unit[self.results.index.str.contains('expansion')] = 'MW' - self.results.unit[self.results.index.str.contains('rel.')] = 'p.u.' + keep_cntr = ["DE"] + new_idx = self.network.buses[ + self.network.buses.country.isin(keep_cntr) + ].index + + new_network = self.network.copy() + + # drop components of other countries + new_network.mremove( + "Bus", new_network.buses[~new_network.buses.index.isin(new_idx)].index + ) + + new_network.mremove( + "Line", + new_network.lines[ + ~new_network.lines.index.isin( + new_network.lines[ + ( + new_network.lines.bus0.isin(new_idx) + & new_network.lines.bus1.isin(new_idx) + ) + ].index + ) + ].index, + ) + new_network.mremove( + "Link", + new_network.links[ + ~new_network.links.index.isin( + new_network.links[ + ( + new_network.links.bus0.isin(new_idx) + & new_network.links.bus1.isin(new_idx) + ) + ].index + ) + ].index, + ) + + new_network.mremove( + "Transformer", + new_network.transformers[ + ~new_network.transformers.index.isin( + new_network.transformers[ + ( + new_network.transformers.bus0.isin(new_idx) + & new_network.transformers.bus1.isin(new_idx) + ) + ].index + ) + ].index, + ) + + new_network.mremove( + "Generator", + new_network.generators[ + ~new_network.generators.index.isin( + new_network.generators[ + new_network.generators.bus.isin(new_idx) + ].index + ) + ].index, + ) + + new_network.mremove( + "Load", + new_network.loads[ + ~new_network.loads.index.isin( + new_network.loads[new_network.loads.bus.isin(new_idx)].index + ) + ].index, + ) + + new_network.mremove( + "Store", + new_network.stores[ + ~new_network.stores.index.isin( + new_network.stores[new_network.stores.bus.isin(new_idx)].index + ) + ].index, + ) + + new_network.mremove( + "StorageUnit", + new_network.storage_units[ + ~new_network.storage_units.index.isin( + new_network.storage_units[ + new_network.storage_units.bus.isin(new_idx) + ].index + ) + ].index, + ) + + return new_network + + +def system_costs_germany(self): + """Calculte system costs for Germany - # system costs + Returns + ------- + marginal_cost : float + Marginal costs for dispatch in Germany + invest_cost : float + Annualized investment costs for components in Germany + import_costs : float + Costs for energy imported to Germany minus costs for exports + + """ + + network_de = self.german_network() + + marginal_cost = 0 + invest_cost = 0 + + for c in network_de.iterate_components(): + if c.name in ["Store"]: + value = "e" + elif c.name in ["Line", "Transformer"]: + value = "s" + else: + value = "p" + if c.name in network_de.one_port_components: + if "marginal_cost" in c.df.columns: + marginal_cost += ( + c.pnl.p.mul(c.df.marginal_cost) + .mul(network_de.snapshot_weightings.generators, axis=0) + .sum() + .sum() + ) + + else: + if "marginal_cost" in c.df.columns: + marginal_cost += ( + c.pnl.p0.mul(c.df.marginal_cost) + .mul(network_de.snapshot_weightings.generators, axis=0) + .sum() + .sum() + ) + if c.name not in [ + "Bus", + "Load", + "LineType", + "TransformerType", + "Carrier", + ]: + invest_cost += ( + ( + c.df[c.df[f"{value}_nom_extendable"]][f"{value}_nom_opt"] + - c.df[c.df[f"{value}_nom_extendable"]][f"{value}_nom_min"] + ) + * c.df[c.df[f"{value}_nom_extendable"]]["capital_cost"] + ).sum() + + # import and its costs + links_export = self.network.links[ + ( + self.network.links.bus0.isin(network_de.buses.index.values) + & ~(self.network.links.bus1.isin(network_de.buses.index.values)) + ) + ] + + export_positive = ( + self.network.links_t.p0[links_export.index] + .clip(lower=0) + .mul(self.network.snapshot_weightings.generators, axis=0) + .mul( + self.network.buses_t.marginal_price[links_export.bus1].values, + ) + .sum() + .sum() + ) + + export_negative = ( + self.network.links_t.p0[links_export.index] + .clip(upper=0) + .mul(self.network.snapshot_weightings.generators, axis=0) + .mul( + self.network.buses_t.marginal_price[links_export.bus1].values, + ) + .mul(-1) + .sum() + .sum() + ) + + links_import = self.network.links[ + ( + self.network.links.bus1.isin(network_de.buses.index.values) + & ~(self.network.links.bus0.isin(network_de.buses.index.values)) + ) + ] + + import_positive = ( + self.network.links_t.p0[links_import.index] + .clip(lower=0) + .mul(self.network.snapshot_weightings.generators, axis=0) + .mul( + self.network.buses_t.marginal_price[links_import.bus1].values, + ) + .sum() + .sum() + ) + + import_negative = ( + self.network.links_t.p0[links_import.index] + .clip(upper=0) + .mul(self.network.snapshot_weightings.generators, axis=0) + .mul( + self.network.buses_t.marginal_price[links_import.bus1].values, + ) + .mul(-1) + .sum() + .sum() + ) + + import_costs = ( + export_negative + import_positive - export_positive - import_negative + ) + + return marginal_cost, invest_cost, import_costs + + +def ac_export(self): + """Calculate electricity exports and imports over AC lines + + Returns + ------- + float + Electricity export (if negative: import) from Germany + + """ + de_buses = self.network.buses[self.network.buses.country == "DE"] + for_buses = self.network.buses[self.network.buses.country != "DE"] + exp = self.network.lines[ + (self.network.lines.bus0.isin(de_buses.index)) + & (self.network.lines.bus1.isin(for_buses.index)) + ] + imp = self.network.lines[ + (self.network.lines.bus1.isin(de_buses.index)) + & (self.network.lines.bus0.isin(for_buses.index)) + ] + + return ( + self.network.lines_t.p0[exp.index] + .sum(axis=1) + .mul(self.network.snapshot_weightings.generators) + .sum() + + self.network.lines_t.p1[imp.index] + .sum(axis=1) + .mul(self.network.snapshot_weightings.generators) + .sum() + ) + + +def ac_export_per_country(self): + """Calculate electricity exports and imports over AC lines per country + + Returns + ------- + float + Electricity export (if negative: import) from Germany in TWh + + """ + de_buses = self.network.buses[self.network.buses.country == "DE"] + + for_buses = self.network.buses[self.network.buses.country != "DE"] + + result = pd.Series(index=for_buses.country.unique()) + + for c in for_buses.country.unique(): + exp = self.network.lines[ + (self.network.lines.bus0.isin(de_buses.index)) + & ( + self.network.lines.bus1.isin( + for_buses[for_buses.country == c].index + ) + ) + ] + imp = self.network.lines[ + (self.network.lines.bus1.isin(de_buses.index)) + & ( + self.network.lines.bus0.isin( + for_buses[for_buses.country == c].index + ) + ) + ] + + result[c] = ( + self.network.lines_t.p0[exp.index] + .sum(axis=1) + .mul(self.network.snapshot_weightings.generators) + .sum() + + self.network.lines_t.p1[imp.index] + .sum(axis=1) + .mul(self.network.snapshot_weightings.generators) + .sum() + ) * 1e-6 + + return result + + +def dc_export(self): + """Calculate electricity exports and imports over DC lines + + Returns + ------- + float + Electricity export (if negative: import) from Germany + + """ + de_buses = self.network.buses[self.network.buses.country == "DE"] + for_buses = self.network.buses[self.network.buses.country != "DE"] + exp = self.network.links[ + (self.network.links.carrier == "DC") + & (self.network.links.bus0.isin(de_buses.index)) + & (self.network.links.bus1.isin(for_buses.index)) + ] + imp = self.network.links[ + (self.network.links.carrier == "DC") + & (self.network.links.bus1.isin(de_buses.index)) + & (self.network.links.bus0.isin(for_buses.index)) + ] + return ( + self.network.links_t.p0[exp.index] + .sum(axis=1) + .mul(self.network.snapshot_weightings.generators) + .sum() + + self.network.links_t.p1[imp.index] + .sum(axis=1) + .mul(self.network.snapshot_weightings.generators) + .sum() + ) + + +def dc_export_per_country(self): + """Calculate electricity exports and imports over DC lines per country + + Returns + ------- + float + Electricity export (if negative: import) from Germany in TWh + + """ + de_buses = self.network.buses[self.network.buses.country == "DE"] + + for_buses = self.network.buses[self.network.buses.country != "DE"] + + result = pd.Series(index=for_buses.country.unique()) + + for c in for_buses.country.unique(): + exp = self.network.links[ + (self.network.links.carrier == "DC") + & (self.network.links.bus0.isin(de_buses.index)) + & ( + self.network.links.bus1.isin( + for_buses[for_buses.country == c].index + ) + ) + ] + imp = self.network.links[ + (self.network.links.carrier == "DC") + & (self.network.links.bus1.isin(de_buses.index)) + & ( + self.network.links.bus0.isin( + for_buses[for_buses.country == c].index + ) + ) + ] + + result[c] = ( + self.network.links_t.p0[exp.index] + .sum(axis=1) + .mul(self.network.snapshot_weightings.generators) + .sum() + + self.network.links_t.p1[imp.index] + .sum(axis=1) + .mul(self.network.snapshot_weightings.generators) + .sum() + ) * 1e-6 + + return result - self.results.value['annual ac grid investment costs'] = calc_investment_cost(self)[0][0] - self.results.value['annual dc grid investment costs'] = calc_investment_cost(self)[0][1] - self.results.value['annual electrical grid investment costs'] = sum(calc_investment_cost(self)[0]) - self.results.value['annual links investment costs'] = calc_investment_cost(self)[1] +def calc_etrago_results(self): + """Function that calculates main results of grid optimization + and adds them to Etrago object. - self.results.value['annual electrical storage investment costs'] = calc_investment_cost(self)[2][0] - self.results.value['annual store investment costs'] = calc_investment_cost(self)[2][1] - self.results.value['annual storage+store investment costs'] = sum(calc_investment_cost(self)[2]) + Returns + ------- + None. + """ + self.results = pd.DataFrame( + columns=["unit", "value"], + index=[ + "annual system costs", + "annual investment costs", + "annual marginal costs", + "annual electrical grid investment costs", + "annual ac grid investment costs", + "annual dc grid investment costs", + "annual links investment costs", + "annual storage+store investment costs", + "annual electrical storage investment costs", + "annual store investment costs", + "battery storage expansion", + "store expansion", + "H2 store expansion", + "CH4 store expansion", + "heat store expansion", + "storage+store expansion", + "fuel cell links expansion", + "electrolyzer links expansion", + "methanisation links expansion", + "Steam Methane Reformation links expansion", + "abs. electrical grid expansion", + "abs. electrical ac grid expansion", + "abs. electrical dc grid expansion", + "rel. electrical ac grid expansion", + "rel. electrical dc grid expansion", + ], + ) + + self.results.unit[self.results.index.str.contains("cost")] = "EUR/a" + self.results.unit[self.results.index.str.contains("expansion")] = "MW" + self.results.unit[self.results.index.str.contains("rel.")] = "p.u." - self.results.value['annual investment costs'] = \ - sum(calc_investment_cost(self)[0]) + calc_investment_cost(self)[1] + sum(calc_investment_cost(self)[2]) - self.results.value['annual marginal costs'] = calc_marginal_cost(self) + # system costs - self.results.value['annual system costs'] = \ - self.results.value['annual investment costs'] + self.results.value['annual marginal costs'] + self.results.value[ + "annual ac grid investment costs" + ] = calc_investment_cost(self)[0][0] + self.results.value[ + "annual dc grid investment costs" + ] = calc_investment_cost(self)[0][1] + self.results.value["annual electrical grid investment costs"] = sum( + calc_investment_cost(self)[0] + ) + + self.results.value["annual links investment costs"] = calc_investment_cost( + self + )[1] + + self.results.value[ + "annual electrical storage investment costs" + ] = calc_investment_cost(self)[2][0] + self.results.value["annual store investment costs"] = calc_investment_cost( + self + )[2][1] + self.results.value["annual storage+store investment costs"] = sum( + calc_investment_cost(self)[2] + ) + + self.results.value["annual investment costs"] = ( + sum(calc_investment_cost(self)[0]) + + calc_investment_cost(self)[1] + + sum(calc_investment_cost(self)[2]) + ) + self.results.value["annual marginal costs"] = calc_marginal_cost(self) + + self.results.value["annual system costs"] = ( + self.results.value["annual investment costs"] + + self.results.value["annual marginal costs"] + ) # storage and store expansion network = self.network if not network.storage_units[network.storage_units.p_nom_extendable].empty: - - self.results.value['battery storage expansion'] = \ - _calc_storage_expansion(self).sum() + self.results.value[ + "battery storage expansion" + ] = _calc_storage_expansion(self).sum() store = _calc_store_expansion(self) - self.results.value['store expansion'] = store.sum() - self.results.value['H2 store expansion'] = \ - store[store.index.str.contains('H2')].sum() - self.results.value['CH4 store expansion'] = \ - store[store.index.str.contains('CH4')].sum() - self.results.value['heat store expansion'] = \ - store[store.index.str.contains('heat')].sum() - - self.results.value['storage+store expansion'] = \ - self.results.value['battery storage expansion'] + self.results.value['store expansion'] + self.results.value["store expansion"] = store.sum() + self.results.value["H2 store expansion"] = store[ + store.index.str.contains("H2") + ].sum() + self.results.value["CH4 store expansion"] = store[ + store.index.str.contains("CH4") + ].sum() + self.results.value["heat store expansion"] = store[ + store.index.str.contains("heat") + ].sum() + + self.results.value["storage+store expansion"] = ( + self.results.value["battery storage expansion"] + + self.results.value["store expansion"] + ) # links expansion if not network.links[network.links.p_nom_extendable].empty: - links = _calc_sectorcoupling_link_expansion(self) - self.results.value['fuel cell links expansion'] = links[0] - self.results.value['electrolyzer links expansion'] = links[1] - self.results.value['methanisation links expansion'] = links[2] - self.results.value['Steam Methane Reformation links expansion'] = links[3] + self.results.value["fuel cell links expansion"] = links[0] + self.results.value["electrolyzer links expansion"] = links[1] + self.results.value["methanisation links expansion"] = links[2] + self.results.value[ + "Steam Methane Reformation links expansion" + ] = links[3] # grid expansion if not network.lines[network.lines.s_nom_extendable].empty: - - self.results.value['abs. electrical ac grid expansion'] = _calc_network_expansion(self)[0].sum() - self.results.value['abs. electrical dc grid expansion'] = _calc_network_expansion(self)[1].sum() - self.results.value['abs. electrical grid expansion'] = self.results.value['abs. electrical ac grid expansion'] + self.results.value['abs. electrical dc grid expansion'] + self.results.value[ + "abs. electrical ac grid expansion" + ] = _calc_network_expansion(self)[0].sum() + self.results.value[ + "abs. electrical dc grid expansion" + ] = _calc_network_expansion(self)[1].sum() + self.results.value["abs. electrical grid expansion"] = ( + self.results.value["abs. electrical ac grid expansion"] + + self.results.value["abs. electrical dc grid expansion"] + ) ext_lines = network.lines[network.lines.s_nom_extendable] ext_links = network.links[network.links.p_nom_extendable] - ext_dc_lines = ext_links[ext_links.carrier=='DC'] - - self.results.value['rel. electrical ac grid expansion'] = (_calc_network_expansion(self)[0].sum() / ext_lines.s_nom.sum()) - self.results.value['rel. electrical dc grid expansion'] = (_calc_network_expansion(self)[1].sum() / ext_dc_lines.p_nom.sum()) \ No newline at end of file + ext_dc_lines = ext_links[ext_links.carrier == "DC"] + + self.results.value["rel. electrical ac grid expansion"] = ( + _calc_network_expansion(self)[0].sum() / ext_lines.s_nom.sum() + ) + self.results.value["rel. electrical dc grid expansion"] = ( + _calc_network_expansion(self)[1].sum() / ext_dc_lines.p_nom.sum() + ) diff --git a/etrago/tools/network.py b/etrago/tools/network.py index 385bd7a1..95dbf3f3 100644 --- a/etrago/tools/network.py +++ b/etrago/tools/network.py @@ -37,7 +37,15 @@ from etrago.cluster.electrical import ehv_clustering, run_spatial_clustering from etrago.cluster.gas import run_spatial_clustering_gas from etrago.cluster.snapshot import skip_snapshots, snapshot_clustering -from etrago.tools.calc_results import calc_etrago_results +from etrago.tools.calc_results import ( + ac_export, + ac_export_per_country, + calc_etrago_results, + dc_export, + dc_export_per_country, + german_network, + system_costs_germany, +) from etrago.tools.execute import ( dispatch_disaggregation, lopf, @@ -64,6 +72,7 @@ plot_h2_summary, plot_heat_loads, plot_heat_summary, + shifted_energy, ) from etrago.tools.utilities import ( add_missing_components, @@ -253,14 +262,26 @@ def __init__( calc_results = calc_etrago_results + calc_ac_export = ac_export + + calc_ac_export_per_country = ac_export_per_country + + calc_dc_export = dc_export + + calc_dc_export_per_country = dc_export_per_country + export_to_csv = export_to_csv filter_links_by_carrier = filter_links_by_carrier + german_network = german_network + set_line_costs = set_line_costs set_trafo_costs = set_trafo_costs + system_costs_germany = system_costs_germany + drop_sectors = drop_sectors buses_by_country = buses_by_country @@ -301,6 +322,8 @@ def __init__( manual_fixes_datamodel = manual_fixes_datamodel + shifted_energy = shifted_energy + def dc_lines(self): return self.filter_links_by_carrier("DC", like=False) diff --git a/etrago/tools/plot.py b/etrago/tools/plot.py index d3ae2068..72197de7 100644 --- a/etrago/tools/plot.py +++ b/etrago/tools/plot.py @@ -135,6 +135,7 @@ def coloring(): "power_to_H2": "cyan", "H2_overground": "cyan", "H2_underground": "cyan", + "H2": "cyan", "dsm-cts": "dodgerblue", "dsm-ind-osm": "dodgerblue", "dsm-ind-sites": "dodgerblue", @@ -155,6 +156,7 @@ def coloring(): "H2_to_CH4": "seagreen", "central_heat_store_charger": "firebrick", "central_heat_store": "firebrick", + "heat": "firebrick", "rural_heat_store_charger": "salmon", "rural_heat_store": "salmon", "central_heat_store_discharger": "firebrick", @@ -701,6 +703,7 @@ def curtailment(network, carrier="solar", filename=None): """ Plot curtailment of selected carrier + Parameters ---------- network : PyPSA network container @@ -710,6 +713,7 @@ def curtailment(network, carrier="solar", filename=None): filename: str or None Save figure in this direction + Returns ------- Plot @@ -1357,6 +1361,7 @@ def storage_soc_sorted(network, filename=None): ---------- network : PyPSA network container Holds topology of grid including results from powerflow analysis + filename : path to folder Returns @@ -1503,6 +1508,7 @@ def calc_ac_loading(network, timesteps): def calc_dc_loading(network, timesteps): """Calculates loading of DC-lines + Parameters ---------- network : :class:`pypsa.Network @@ -3329,3 +3335,184 @@ def plot_heat_summary(self, t_resolution="20H", stacked=True, save_path=False): if save_path: plt.savefig(save_path, dpi=300) + + +def shifted_energy(self, carrier, buses): + """Calulate shifted energy for a specific carrier + + Parameters + ---------- + carrier : str + Name of energy carrier + buses : list + List of considered bus indices + + Returns + ------- + shifted : pandas.Series + Shifted energy per time step + + """ + + buses = self.network.links[ + self.network.links.bus0.isin( + self.network.buses[ + (self.network.buses.carrier == "AC") + & (self.network.buses.index.isin(buses)) + ].index + ) + & self.network.links.bus1.isin( + self.network.buses[ + self.network.buses.carrier.str.contains(carrier) + ].index + ) + ].bus1.unique() + + supply = self.network.links_t.p1[ + self.network.links[ + (self.network.links.bus1.isin(buses)) + & ~(self.network.links.carrier.str.contains("charger")) + ].index + ].mul(-1).sum(axis=1) + ( + self.network.generators_t.p[ + self.network.generators[ + self.network.generators.bus.isin(buses) + ].index + ].sum(axis=1) + ) + + demand = self.network.loads_t.p[ + self.network.loads[self.network.loads.bus.isin(buses)].index + ].sum(axis=1) + ( + self.network.links_t.p0[ + self.network.links[ + (self.network.links.bus0.isin(buses)) + & ~(self.network.links.carrier.str.contains("charger")) + ].index + ].sum(axis=1) + ) + + shifted = supply - demand + return shifted + + +def flexibility_duration_curve(etrago, etrago_lowflex, filename=None): + """Plot duration curves of flexibility options + + Parameters + ---------- + etrago : Etrago + Object including network with flexibility options + etrago_lowflex : Etrago + Object including network with less flexibility options + filename : str, optional + Name of file to save plot. The default is None. + + Returns + ------- + None. + + """ + colors = coloring() + + value = "p" + + df = pd.DataFrame() + + dsm_stores = etrago.network.stores[ + etrago.network.stores.carrier.str.contains("dsm") + ] + df["dsm_positive"] = ( + etrago.network.stores_t[value][dsm_stores.index] + .clip(lower=0) + .sum(axis=1) + ) + df["dsm_negative"] = ( + etrago.network.stores_t[value][dsm_stores.index] + .clip(upper=0) + .sum(axis=1) + ) + + emob_static = etrago_lowflex.network.loads[ + etrago_lowflex.network.loads.carrier == "land transport EV" + ] + + emob_static_t = etrago_lowflex.network.loads_t.p_set[emob_static.index] + + emob_static_t = emob_static_t.loc[:, emob_static.index] + + emob_static_t.columns = emob_static.bus.values + + emob_flex = etrago.network.links[ + etrago.network.links.carrier.str.contains("BEV") + ] + + emob_flex_t = etrago.network.links_t.p0[emob_flex.index] + + emob_flex_t = emob_flex_t.loc[:, emob_flex.index] + + emob_flex_t.columns = emob_flex.bus0.values + + emob_flex_t - emob_static_t + df["BEV charger_positive"] = ( + (emob_flex_t - emob_static_t).clip(lower=0).sum(axis=1) + ) + df["BEV charger_negative"] = ( + (emob_flex_t - emob_static_t).clip(upper=0).sum(axis=1) + ) + + heat_stores = etrago.network.stores[ + etrago.network.stores.carrier.str.contains("heat") + ] + df["heat_positive"] = ( + etrago.network.stores_t[value][heat_stores.index] + .clip(lower=0) + .sum(axis=1) + ) + df["heat_negative"] = ( + etrago.network.stores_t[value][heat_stores.index] + .clip(upper=0) + .sum(axis=1) + ) + + h2_stores = etrago.network.stores[ + etrago.network.stores.carrier.str.contains("H2") + ] + df["H2_positive"] = ( + etrago.network.stores_t[value][h2_stores.index] + .clip(lower=0) + .sum(axis=1) + ) + df["H2_negative"] = ( + etrago.network.stores_t[value][h2_stores.index] + .clip(upper=0) + .sum(axis=1) + ) + + fig, ax = plt.subplots(figsize=(15, 8)) + for c in df.columns: + result = pd.Series(dtype=float) + color = colors[c.split("_")[0]] + for p in range(0, 100): + result[p * df[c].abs().max() * np.sign(df[c].sum()) / 100] = ( + df[c][df[c].abs() > p * 0.01 * df[c].abs().max()].size + / df[c].size + ) * 100 + + data_to_plot = pd.DataFrame( + index=result.values, data=result.index * 1e-3 + ) + data_to_plot.columns = [c.split("_")[0]] + data_to_plot.plot(ax=ax, color=color, linewidth=3.0) + plt.axhline(y=0.0, color="grey", linestyle="dotted") + ax.set_xlim(0, 80) + ax.set_xlabel("time in %") + ax.set_ylabel("flexibility usage in GW") + + handles, labels = plt.gca().get_legend_handles_labels() + by_label = dict(zip(labels, handles)) + plt.legend(by_label.values(), by_label.keys()) + + if filename: + fig.savefig(filename, dpi=600) + plt.close() diff --git a/noxfile.py b/noxfile.py index 9764d83d..7fabe5f7 100644 --- a/noxfile.py +++ b/noxfile.py @@ -5,6 +5,7 @@ cleaned = [ "etrago/cluster/disaggregation.py", + "etrago/tools/calc_results.py", "etrago/tools/network.py", "etrago/tools/utilities.py", "noxfile.py",