From 8aff213e9bb67fde4af264de65254035058f1793 Mon Sep 17 00:00:00 2001 From: alonsoju95 Date: Thu, 8 Dec 2022 16:11:32 +0100 Subject: [PATCH 001/136] changes to plot v_nom --- etrago/tools/plot.py | 221 +++++++++++++++++++++++++++++++------------ 1 file changed, 162 insertions(+), 59 deletions(-) diff --git a/etrago/tools/plot.py b/etrago/tools/plot.py index be3cf6cb6..5c6ccafd0 100644 --- a/etrago/tools/plot.py +++ b/etrago/tools/plot.py @@ -80,7 +80,6 @@ def set_epsg_network(network): set_epsg_network.counter = set_epsg_network.counter + 1 - def plot_osm(x, y, zoom, alpha=0.4): """ Plots openstreetmap as background of network-plots @@ -108,7 +107,9 @@ def plot_osm(x, y, zoom, alpha=0.4): fig, ax = plt.subplots() plt.axis("off") - plotter = tilemapbase.Plotter(extent, tilemapbase.tiles.build_OSM(), zoom=zoom) + plotter = tilemapbase.Plotter( + extent, tilemapbase.tiles.build_OSM(), zoom=zoom + ) plotter.plot(ax, alpha=alpha) # ax.plot(x, y, "ro-") return fig, ax @@ -217,7 +218,9 @@ def plot_line_loading_diff(networkA, networkB, timestep=0, osm=False): set_epsg_network(networkB) plot_osm(osm["x"], osm["y"], osm["zoom"]) # new colormap to make sure 0% difference has the same color in every plot - def shiftedColorMap(cmap, start=0, midpoint=0.5, stop=1.0, name="shiftedcmap"): + def shiftedColorMap( + cmap, start=0, midpoint=0.5, stop=1.0, name="shiftedcmap" + ): """ Function to offset the "center" of a colormap. Useful for data with a negative min and positive max and you want the @@ -279,7 +282,9 @@ def shiftedColorMap(cmap, start=0, midpoint=0.5, stop=1.0, name="shiftedcmap"): ) loading_noswitches.columns = ["noswitch"] diff_network = loading_switches.join(loading_noswitches) - diff_network["noswitch"] = diff_network["noswitch"].fillna(diff_network["switch"]) + diff_network["noswitch"] = diff_network["noswitch"].fillna( + diff_network["switch"] + ) diff_network[networkA.snapshots[timestep]] = ( diff_network["switch"] - diff_network["noswitch"] ) @@ -293,10 +298,13 @@ def shiftedColorMap(cmap, start=0, midpoint=0.5, stop=1.0, name="shiftedcmap"): # plot network with difference in loading and shifted colormap loading = ( - diff_network.loc[:, networkA.snapshots[timestep]] / (networkA.lines.s_nom) + diff_network.loc[:, networkA.snapshots[timestep]] + / (networkA.lines.s_nom) ) * 100 midpoint = 1 - max(loading) / (max(loading) + abs(min(loading))) - shifted_cmap = shiftedColorMap(plt.cm.jet, midpoint=midpoint, name="shifted") + shifted_cmap = shiftedColorMap( + plt.cm.jet, midpoint=midpoint, name="shifted" + ) ll = networkA.plot( line_colors=loading, line_cmap=shifted_cmap, @@ -311,7 +319,9 @@ def shiftedColorMap(cmap, start=0, midpoint=0.5, stop=1.0, name="shiftedcmap"): cb.set_label("Difference in line loading in % of s_nom") -def network_expansion_diff(networkA, networkB, filename=None, boundaries=[], osm=False): +def network_expansion_diff( + networkA, networkB, filename=None, boundaries=[], osm=False +): """Plot relative network expansion derivation of AC- and DC-lines. Parameters @@ -341,11 +351,13 @@ def network_expansion_diff(networkA, networkB, filename=None, boundaries=[], osm cmap = plt.cm.jet extension_lines = 100 * ( - (networkA.lines.s_nom_opt - networkB.lines.s_nom_opt) / networkA.lines.s_nom_opt + (networkA.lines.s_nom_opt - networkB.lines.s_nom_opt) + / networkA.lines.s_nom_opt ) extension_links = 100 * ( - (networkA.links.p_nom_opt - networkB.links.p_nom_opt) / networkA.links.p_nom_opt + (networkA.links.p_nom_opt - networkB.links.p_nom_opt) + / networkA.links.p_nom_opt ) ll = networkA.plot( @@ -378,7 +390,9 @@ def network_expansion_diff(networkA, networkB, filename=None, boundaries=[], osm cb_Link.remove() - cb = plt.colorbar(ll[1], boundaries=v, ticks=v[0:101:10], fraction=0.046, pad=0.04) + cb = plt.colorbar( + ll[1], boundaries=v, ticks=v[0:101:10], fraction=0.046, pad=0.04 + ) cb.set_clim(vmin=boundaries[0], vmax=boundaries[1]) cb.set_label("line extension derivation in %") @@ -408,17 +422,29 @@ def plot_residual_load(network): ] * network.generators_t.p_max_pu[renewables.index].mul( network.snapshot_weightings, axis=0 ) - load = network.loads_t.p_set.mul(network.snapshot_weightings, axis=0).sum(axis=1) + load = network.loads_t.p_set.mul(network.snapshot_weightings, axis=0).sum( + axis=1 + ) all_renew = renewables_t.sum(axis=1) residual_load = load - all_renew plot = residual_load.plot( - title="Residual load", drawstyle="steps", lw=2, color="red", legend=False + title="Residual load", + drawstyle="steps", + lw=2, + color="red", + legend=False, ) plot.set_ylabel("MW") # sorted curve - sorted_residual_load = residual_load.sort_values(ascending=False).reset_index() + sorted_residual_load = residual_load.sort_values( + ascending=False + ).reset_index() plot1 = sorted_residual_load.plot( - title="Sorted residual load", drawstyle="steps", lw=2, color="red", legend=False + title="Sorted residual load", + drawstyle="steps", + lw=2, + color="red", + legend=False, ) plot1.set_ylabel("MW") @@ -454,10 +480,16 @@ def plot_stacked_gen(network, bus=None, resolution="GW", filename=None): pd.concat( [ network.generators_t.p[ - network.generators[network.generators.control != "Slack"].index + network.generators[ + network.generators.control != "Slack" + ].index ], - network.generators_t.p.mul(network.snapshot_weightings, axis=0)[ - network.generators[network.generators.control == "Slack"].index + network.generators_t.p.mul( + network.snapshot_weightings, axis=0 + )[ + network.generators[ + network.generators.control == "Slack" + ].index ] .iloc[:, 0] .apply(lambda x: x if x > 0 else 0), @@ -496,8 +528,12 @@ def plot_stacked_gen(network, bus=None, resolution="GW", filename=None): colors = [colors[col] for col in p_by_carrier.columns] if len(colors) == 1: colors = colors[0] - (p_by_carrier / reso_int).plot(kind="area", ax=ax, linewidth=0, color=colors) - (load / reso_int).plot(ax=ax, legend="load", lw=2, color="darkgrey", style="--") + (p_by_carrier / reso_int).plot( + kind="area", ax=ax, linewidth=0, color=colors + ) + (load / reso_int).plot( + ax=ax, legend="load", lw=2, color="darkgrey", style="--" + ) ax.legend(ncol=4, loc="upper left") ax.set_ylabel(resolution) @@ -543,11 +579,19 @@ def gen_by_c(network): gen = ( pd.concat( [ - network.generators_t.p.mul(etwork.snapshot_weightings, axis=0)[ - network.generators[network.generators.control != "Slack"].index + network.generators_t.p.mul( + etwork.snapshot_weightings, axis=0 + )[ + network.generators[ + network.generators.control != "Slack" + ].index ], - network.generators_t.p.mul(network.snapshot_weightings, axis=0)[ - network.generators[network.generators.control == "Slack"].index + network.generators_t.p.mul( + network.snapshot_weightings, axis=0 + )[ + network.generators[ + network.generators.control == "Slack" + ].index ] .iloc[:, 0] .apply(lambda x: x if x > 0 else 0), @@ -623,7 +667,9 @@ def plot_voltage(network, boundaries=[], osm=False): cb.set_clim(vmin=boundaries[0], vmax=boundaries[1]) cb.set_label("Voltage Magnitude per unit of v_nom") - network.plot(ax=ax, line_widths=pd.Series(0.5, network.lines.index), bus_sizes=0) + network.plot( + ax=ax, line_widths=pd.Series(0.5, network.lines.index), bus_sizes=0 + ) plt.show() @@ -650,7 +696,9 @@ def curtailment(network, carrier="solar", filename=None): network.generators.carrier, axis=1 ).sum() capacity = network.generators.groupby("carrier").sum().at[carrier, "p_nom"] - p_available = network.generators_t.p_max_pu.multiply(network.generators["p_nom"]) + p_available = network.generators_t.p_max_pu.multiply( + network.generators["p_nom"] + ) p_available_by_carrier = p_available.groupby( network.generators.carrier, axis=1 ).sum() @@ -672,7 +720,9 @@ def curtailment(network, carrier="solar", filename=None): p_df[[carrier + " dispatched", carrier + " curtailed"]].plot( kind="area", ax=ax, linewidth=3 ) - p_df[[carrier + " available", carrier + " capacity"]].plot(ax=ax, linewidth=3) + p_df[[carrier + " available", carrier + " capacity"]].plot( + ax=ax, linewidth=3 + ) ax.set_xlabel("") ax.set_ylabel("Power [MW]") @@ -708,15 +758,21 @@ def calc_dispatch_per_carrier(network, timesteps): ] dist = pd.Series( - index=pd.MultiIndex.from_tuples(index, names=["bus", "carrier"]), dtype=float + index=pd.MultiIndex.from_tuples(index, names=["bus", "carrier"]), + dtype=float, ) for i in dist.index: gens = network.generators[ - (network.generators.bus == i[0]) & (network.generators.carrier == i[1]) + (network.generators.bus == i[0]) + & (network.generators.carrier == i[1]) ].index dist[i] = ( - (network.generators_t.p[gens].transpose()[network.snapshots[timesteps]]) + ( + network.generators_t.p[gens].transpose()[ + network.snapshots[timesteps] + ] + ) .sum() .sum() ) @@ -739,11 +795,15 @@ def calc_storage_expansion_per_bus(network): """ - batteries = network.storage_units[network.storage_units.carrier == "battery"] + batteries = network.storage_units[ + network.storage_units.carrier == "battery" + ] h2_overground = network.stores[network.stores.carrier == "H2_overground"] h2_underground = network.stores[network.stores.carrier == "H2_underground"] rural_heat = network.stores[network.stores.carrier == "rural_heat_store"] - central_heat = network.stores[network.stores.carrier == "central_heat_store"] + central_heat = network.stores[ + network.stores.carrier == "central_heat_store" + ] # hydrogen = network.storage_units[network.storage_units.carrier == # 'extendable_hydrogen_storage'] battery_distribution = ( @@ -791,7 +851,8 @@ def calc_storage_expansion_per_bus(network): # index.extend([(idx, 'hydrogen_storage') for idx in network.buses.index]) dist = pd.Series( - index=pd.MultiIndex.from_tuples(index, names=["bus", "carrier"]), dtype=float + index=pd.MultiIndex.from_tuples(index, names=["bus", "carrier"]), + dtype=float, ) dist.iloc[ @@ -989,13 +1050,19 @@ def nodal_gen_dispatch( dispatch_network = ( network.generators_t.p[gens.index] .mul(network.snapshot_weightings.generators, axis=0) - .groupby([network.generators.bus, network.generators.carrier], axis=1) + .groupby( + [network.generators.bus, network.generators.carrier], + axis=1, + ) .sum() ) dispatch_networkB = ( networkB.generators_t.p[gens.index] .mul(networkB.snapshot_weightings.generators, axis=0) - .groupby([networkB.generators.bus, networkB.generators.carrier], axis=1) + .groupby( + [networkB.generators.bus, networkB.generators.carrier], + axis=1, + ) .sum() ) dispatch = dispatch_network - dispatch_networkB @@ -1025,7 +1092,9 @@ def nodal_gen_dispatch( dispatch = dispatch.abs() + 1e-9 else: dispatch = dispatch.sum(level=0) - colors = {s[0]: "green" if s[1] > 0 else "red" for s in dispatch.iteritems()} + colors = { + s[0]: "green" if s[1] > 0 else "red" for s in dispatch.iteritems() + } dispatch = dispatch.abs() subcolors = {"negative": "red", "positive": "green"} import cartopy.crs as ccrs @@ -1096,7 +1165,10 @@ def nodal_production_balance(network, timesteps, scaling=0.00001): residual_load = (gen - load).sum() bus_colors = pd.Series( - {s[0]: "green" if s[1] > 0 else "red" for s in residual_load.iteritems()} + { + s[0]: "green" if s[1] > 0 else "red" + for s in residual_load.iteritems() + } ) bus_sizes = residual_load.abs() * scaling @@ -1130,10 +1202,12 @@ def storage_p_soc(network, mean="1H", filename=None): ] cap_batt = ( - network.storage_units.max_hours[sbatt] * network.storage_units.p_nom_opt[sbatt] + network.storage_units.max_hours[sbatt] + * network.storage_units.p_nom_opt[sbatt] ).sum() cap_hydr = ( - network.storage_units.max_hours[shydr] * network.storage_units.p_nom_opt[shydr] + network.storage_units.max_hours[shydr] + * network.storage_units.p_nom_opt[shydr] ).sum() fig, ax = plt.subplots(1, 1) @@ -1228,7 +1302,6 @@ def storage_p_soc(network, mean="1H", filename=None): ax2.legend(loc=1) ax.set_title("Storage dispatch and state of charge") - if filename is None: plt.show() else: @@ -1262,10 +1335,12 @@ def storage_soc_sorted(network, filename=None): ] cap_batt = ( - network.storage_units.max_hours[sbatt] * network.storage_units.p_nom_opt[sbatt] + network.storage_units.max_hours[sbatt] + * network.storage_units.p_nom_opt[sbatt] ).sum() cap_hydr = ( - network.storage_units.max_hours[shydr] * network.storage_units.p_nom_opt[shydr] + network.storage_units.max_hours[shydr] + * network.storage_units.p_nom_opt[shydr] ).sum() fig, ax = plt.subplots(1, 1) @@ -1424,19 +1499,25 @@ def calc_dc_loading(network, timesteps): network.links.linked_to = network.links.linked_to.astype(str) # Set p_nom_max and line_loading for one directional links - link_load = network.links_t.p0[network.links.index[network.links.linked_to == "0"]] + link_load = network.links_t.p0[ + network.links.index[network.links.linked_to == "0"] + ] p_nom_opt_max = network.links.p_nom_opt[network.links.linked_to == "0"] # Set p_nom_max and line_loading for bidirectional links for i, row in network.links[network.links.linked_to != "0"].iterrows(): - load = pd.DataFrame(index=network.links_t.p0.index, columns=["to", "from"]) + load = pd.DataFrame( + index=network.links_t.p0.index, columns=["to", "from"] + ) load["to"] = network.links_t.p0[row["linked_to"]] load["from"] = network.links_t.p0[i] link_load[i] = load.abs().max(axis=1) p_nom_opt_max[i] = max( row.p_nom_opt, - network.links.p_nom_opt[network.links.index == row["linked_to"]].values[0], + network.links.p_nom_opt[ + network.links.index == row["linked_to"] + ].values[0], ) return ( @@ -1500,21 +1581,24 @@ def calc_network_expansion(network, method="abs", ext_min=0.1): network.lines = network.lines[ network.lines.s_nom_extendable & ( - (network.lines.s_nom_opt - network.lines.s_nom_min) / network.lines.s_nom + (network.lines.s_nom_opt - network.lines.s_nom_min) + / network.lines.s_nom >= ext_min ) ] network.links = network.links[ network.links.p_nom_extendable & ( - (network.links.p_nom_opt - network.links.p_nom_min) / network.links.p_nom + (network.links.p_nom_opt - network.links.p_nom_min) + / network.links.p_nom >= ext_min ) ] for i, row in network.links.iterrows(): linked = network.links[ - (row["bus1"] == network.links.bus0) & (row["bus0"] == network.links.bus1) + (row["bus1"] == network.links.bus0) + & (row["bus0"] == network.links.bus1) ] if not linked.empty: if row["p_nom_opt"] < linked.p_nom_opt.values[0]: @@ -1557,21 +1641,25 @@ def plot_background_grid(network, ax): """ + linkcarrier = pd.Series( + data=network.links.carrier, index=network.links.index + ) + + linkcarrier[linkcarrier != "DC"] = 0 + linkcarrier[linkcarrier == "DC"] = 0.3 + network.plot( ax=ax, line_colors="grey", link_colors="grey", bus_sizes=0, line_widths=0.5, - link_widths=0.3, # 0.55, + link_widths=linkcarrier, # 0.3, geomap=True, projection=ccrs.PlateCarree(), color_geomap=True, ) - network.plot(ax=ax, line_colors='grey', link_colors='grey', - bus_sizes=0, line_widths=0.5, link_widths=0.3,#0.55, - geomap=True, projection=ccrs.PlateCarree(), color_geomap=True) def plot_carrier(etrago, carrier_links, carrier_buses=[], osm=False): @@ -1721,7 +1809,9 @@ def plot_grid( + " to " + str(network.snapshots[timesteps[-1]]) ) - rep_snapshots = network.snapshot_weightings[network.snapshots[timesteps]].sum() + rep_snapshots = network.snapshot_weightings[ + network.snapshots[timesteps] + ].sum() line_colors = calc_ac_loading(network, timesteps).abs() / rep_snapshots link_colors = calc_dc_loading(network, timesteps).abs() / rep_snapshots label = "line loading in p.u." @@ -1740,8 +1830,10 @@ def plot_grid( elif line_colors == "v_nom": title = "Voltage levels" label = "v_nom in kV" + breakpoint() line_colors = network.lines.v_nom - link_colors = network.links.v_nom + link_colors = pd.Series(data=0, index=network.links.index) + plot_background_grid(network, ax) elif line_colors == "expansion_abs": title = "Network expansion" label = "network expansion in MW" @@ -1765,7 +1857,9 @@ def plot_grid( elif line_colors == "q_flow_max": title = "Maximmal reactive power flows" label = "flow in Mvar" - line_colors = abs(network.lines_t.q0.abs().max() / (network.lines.s_nom)) + line_colors = abs( + network.lines_t.q0.abs().max() / (network.lines.s_nom) + ) link_colors = pd.Series(data=0, index=network.links.index) else: logger.warning("line_color {} undefined".format(line_colors)) @@ -1863,7 +1957,9 @@ def plot_grid( handles = [positive, negative] else: for i in network.carriers.color.index: - patch = mpatches.Patch(color=network.carriers.color[i], label=i) + patch = mpatches.Patch( + color=network.carriers.color[i], label=i + ) handles.append(patch) l3 = plt.legend( @@ -1882,7 +1978,9 @@ def plot_grid( v = np.linspace(boundaries[0], boundaries[1], 101) # colorbar for line heatmap - cb = plt.colorbar(ll[1], boundaries=v, ticks=v[0:101:10], fraction=0.046, pad=0.04) + cb = plt.colorbar( + ll[1], boundaries=v, ticks=v[0:101:10], fraction=0.046, pad=0.04 + ) # Set legend label cb.set_label(label) @@ -1999,7 +2097,9 @@ def plot_clusters( ] ] map_buses = map_buses[map_buses["carrier"] == carrier] - map_buses["geom"] = map_buses.apply(lambda x: Point(x["x"], x["y"]), axis=1) + map_buses["geom"] = map_buses.apply( + lambda x: Point(x["x"], x["y"]), axis=1 + ) map_buses["cluster"] = map_buses.index.map(self.busmap["busmap"]) map_buses["cluster_geom"] = map_buses["cluster"].map(new_geom.geom) map_buses["line"] = map_buses.apply( @@ -2027,9 +2127,12 @@ def plot_clusters( .any() ): lines["geom"] = gpd.GeoSeries.from_wkt(lines["geom"]) - lines = gpd.GeoDataFrame(self.busmap["orig_network"].lines, geometry="geom") + lines = gpd.GeoDataFrame( + self.busmap["orig_network"].lines, geometry="geom" + ) lines = lines[ - lines["bus0"].isin(map_buses.index) & lines["bus1"].isin(map_buses.index) + lines["bus0"].isin(map_buses.index) + & lines["bus1"].isin(map_buses.index) ] lines["geom"] = lines.apply( lambda x: x["geom"] From 520bd62dc0797734c2991ed557d1f05ee6ec76b6 Mon Sep 17 00:00:00 2001 From: alonsoju95 Date: Thu, 8 Dec 2022 16:38:36 +0100 Subject: [PATCH 002/136] changes to plot v_nom --- etrago/tools/plot.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/etrago/tools/plot.py b/etrago/tools/plot.py index 5c6ccafd0..5a56c5892 100644 --- a/etrago/tools/plot.py +++ b/etrago/tools/plot.py @@ -1640,14 +1640,14 @@ def plot_background_grid(network, ax): None. """ - + breakpoint() linkcarrier = pd.Series( data=network.links.carrier, index=network.links.index ) linkcarrier[linkcarrier != "DC"] = 0 linkcarrier[linkcarrier == "DC"] = 0.3 - + linkcarrier = linkcarrier.astype(float) network.plot( ax=ax, line_colors="grey", From 92c83eed25a072da9a535504704be4287f60a8a0 Mon Sep 17 00:00:00 2001 From: alonsoju95 Date: Fri, 9 Dec 2022 11:30:10 +0100 Subject: [PATCH 003/136] changes to plot v_nom --- etrago/tools/plot.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/etrago/tools/plot.py b/etrago/tools/plot.py index 5a56c5892..ebbc65d9a 100644 --- a/etrago/tools/plot.py +++ b/etrago/tools/plot.py @@ -1640,7 +1640,7 @@ def plot_background_grid(network, ax): None. """ - breakpoint() + # breakpoint() linkcarrier = pd.Series( data=network.links.carrier, index=network.links.index ) @@ -1830,7 +1830,7 @@ def plot_grid( elif line_colors == "v_nom": title = "Voltage levels" label = "v_nom in kV" - breakpoint() + # breakpoint() line_colors = network.lines.v_nom link_colors = pd.Series(data=0, index=network.links.index) plot_background_grid(network, ax) From 520b36eaae8d50bdc878ae14f8884503e762e288 Mon Sep 17 00:00:00 2001 From: alonsoju95 Date: Fri, 6 Jan 2023 13:52:44 +0100 Subject: [PATCH 004/136] changes to calc_network_expansion to plot only DC links --- etrago/tools/plot.py | 32 ++++++++++++++++++++++++++++++-- 1 file changed, 30 insertions(+), 2 deletions(-) diff --git a/etrago/tools/plot.py b/etrago/tools/plot.py index ebbc65d9a..e1500feee 100644 --- a/etrago/tools/plot.py +++ b/etrago/tools/plot.py @@ -1577,6 +1577,7 @@ def calc_network_expansion(network, method="abs", ext_min=0.1): DC-line expansion """ + # ´breakpoint() all_network = network.copy() network.lines = network.lines[ network.lines.s_nom_extendable @@ -1603,7 +1604,7 @@ def calc_network_expansion(network, method="abs", ext_min=0.1): if not linked.empty: if row["p_nom_opt"] < linked.p_nom_opt.values[0]: network.links.p_nom_opt[i] = linked.p_nom_opt.values[0] - + breakpoint() if method == "rel": extension_lines = ( @@ -1612,15 +1613,41 @@ def calc_network_expansion(network, method="abs", ext_min=0.1): / network.lines.s_nom ) + # extension_links = ( + # 100 + # * (network.links.p_nom_opt - network.links.p_nom_min) + # / (network.links.p_nom) + # ) + + extension_links = pd.DataFrame( + data=network.links, index=network.links.index + ) + extension_links[extension_links.carrier != "DC"] = 0 extension_links = ( 100 * (network.links.p_nom_opt - network.links.p_nom_min) / (network.links.p_nom) ) + extension_links = extension_links.fillna(0) + # if method == "abs": + # extension_lines = network.lines.s_nom_opt - network.lines.s_nom_min + + # extension_links = network.links.p_nom_opt - network.links.p_nom_min + + # return all_network, extension_lines, extension_links + if method == "abs": extension_lines = network.lines.s_nom_opt - network.lines.s_nom_min + extension_links = pd.DataFrame( + data=network.links, index=network.links.index + ) + extension_links[extension_links.carrier != "DC"] = 0 extension_links = network.links.p_nom_opt - network.links.p_nom_min + # extension_links = ( + # network.links[(network.links.carrier == "DC")].p_nom_opt + # - network.links[(network.links.carrier == "DC")].p_nom_min + # ).set_index(network.links.index) return all_network, extension_lines, extension_links @@ -1830,11 +1857,11 @@ def plot_grid( elif line_colors == "v_nom": title = "Voltage levels" label = "v_nom in kV" - # breakpoint() line_colors = network.lines.v_nom link_colors = pd.Series(data=0, index=network.links.index) plot_background_grid(network, ax) elif line_colors == "expansion_abs": + breakpoint() title = "Network expansion" label = "network expansion in MW" all_network, line_colors, link_colors = calc_network_expansion( @@ -1845,6 +1872,7 @@ def plot_grid( line_widths = 0.5 + (line_colors / ext_width) link_widths = 0.5 + (link_colors / ext_width) elif line_colors == "expansion_rel": + breakpoint() title = "Network expansion" label = "network expansion in %" all_network, line_colors, link_colors = calc_network_expansion( From b85bd435abcae70e6406fd547b54d56ca3f28fd7 Mon Sep 17 00:00:00 2001 From: alonsoju95 Date: Fri, 6 Jan 2023 14:20:10 +0100 Subject: [PATCH 005/136] empty spaces erased --- etrago/tools/plot.py | 24 +++--------------------- 1 file changed, 3 insertions(+), 21 deletions(-) diff --git a/etrago/tools/plot.py b/etrago/tools/plot.py index e1500feee..1a6ec7623 100644 --- a/etrago/tools/plot.py +++ b/etrago/tools/plot.py @@ -1577,7 +1577,7 @@ def calc_network_expansion(network, method="abs", ext_min=0.1): DC-line expansion """ - # ´breakpoint() + all_network = network.copy() network.lines = network.lines[ network.lines.s_nom_extendable @@ -1604,7 +1604,7 @@ def calc_network_expansion(network, method="abs", ext_min=0.1): if not linked.empty: if row["p_nom_opt"] < linked.p_nom_opt.values[0]: network.links.p_nom_opt[i] = linked.p_nom_opt.values[0] - breakpoint() + if method == "rel": extension_lines = ( @@ -1613,12 +1613,6 @@ def calc_network_expansion(network, method="abs", ext_min=0.1): / network.lines.s_nom ) - # extension_links = ( - # 100 - # * (network.links.p_nom_opt - network.links.p_nom_min) - # / (network.links.p_nom) - # ) - extension_links = pd.DataFrame( data=network.links, index=network.links.index ) @@ -1629,12 +1623,6 @@ def calc_network_expansion(network, method="abs", ext_min=0.1): / (network.links.p_nom) ) extension_links = extension_links.fillna(0) - # if method == "abs": - # extension_lines = network.lines.s_nom_opt - network.lines.s_nom_min - - # extension_links = network.links.p_nom_opt - network.links.p_nom_min - - # return all_network, extension_lines, extension_links if method == "abs": extension_lines = network.lines.s_nom_opt - network.lines.s_nom_min @@ -1644,10 +1632,6 @@ def calc_network_expansion(network, method="abs", ext_min=0.1): ) extension_links[extension_links.carrier != "DC"] = 0 extension_links = network.links.p_nom_opt - network.links.p_nom_min - # extension_links = ( - # network.links[(network.links.carrier == "DC")].p_nom_opt - # - network.links[(network.links.carrier == "DC")].p_nom_min - # ).set_index(network.links.index) return all_network, extension_lines, extension_links @@ -1667,7 +1651,7 @@ def plot_background_grid(network, ax): None. """ - # breakpoint() + linkcarrier = pd.Series( data=network.links.carrier, index=network.links.index ) @@ -1861,7 +1845,6 @@ def plot_grid( link_colors = pd.Series(data=0, index=network.links.index) plot_background_grid(network, ax) elif line_colors == "expansion_abs": - breakpoint() title = "Network expansion" label = "network expansion in MW" all_network, line_colors, link_colors = calc_network_expansion( @@ -1872,7 +1855,6 @@ def plot_grid( line_widths = 0.5 + (line_colors / ext_width) link_widths = 0.5 + (link_colors / ext_width) elif line_colors == "expansion_rel": - breakpoint() title = "Network expansion" label = "network expansion in %" all_network, line_colors, link_colors = calc_network_expansion( From a7a17ab87fc6a82409e59d98d11e23b1fe1a94d9 Mon Sep 17 00:00:00 2001 From: alonsoju95 Date: Fri, 20 Jan 2023 15:17:18 +0100 Subject: [PATCH 006/136] expansion abs only plots AC buses --- etrago/tools/plot.py | 25 ++++++++++++++++++------- 1 file changed, 18 insertions(+), 7 deletions(-) diff --git a/etrago/tools/plot.py b/etrago/tools/plot.py index 1a6ec7623..663df1455 100644 --- a/etrago/tools/plot.py +++ b/etrago/tools/plot.py @@ -580,7 +580,7 @@ def gen_by_c(network): pd.concat( [ network.generators_t.p.mul( - etwork.snapshot_weightings, axis=0 + network.snapshot_weightings, axis=0 )[ network.generators[ network.generators.control != "Slack" @@ -1651,7 +1651,7 @@ def plot_background_grid(network, ax): None. """ - + breakpoint() linkcarrier = pd.Series( data=network.links.carrier, index=network.links.index ) @@ -1792,7 +1792,7 @@ def plot_grid( network = self.disaggregated_network.copy() else: network = self.network.copy() - + breakpoint() # Set colors for plotting plotting_colors(network) @@ -1875,7 +1875,7 @@ def plot_grid( logger.warning("line_color {} undefined".format(line_colors)) # Set bus colors - + breakpoint() if bus_colors == "nodal_production_balance": bus_scaling = bus_sizes bus_sizes, bus_colors = nodal_production_balance( @@ -1916,8 +1916,18 @@ def plot_grid( bus_legend = "PowerToH2" bus_unit = "TW" - else: - logger.warning("bus_color {} undefined".format(bus_colors)) + # else: + # logger.warning("bus_color {} undefined".format(bus_colors)) + elif bus_colors == "grey": + bus_scaling = bus_sizes + bus_unit = "" + bus_legend = "" + bus_sizes = pd.Series( + data=network.buses.carrier, index=network.buses.index + ) + bus_sizes[bus_sizes != "AC"] = 0 + bus_sizes[bus_sizes == "AC"] = 1 * bus_scaling + bus_scaling = bus_sizes ll = network.plot( line_colors=line_colors, @@ -1936,7 +1946,8 @@ def plot_grid( ) # legends for bus sizes and colors - if type(bus_sizes) != float: + if bus_colors != "grey": + # if type(bus_sizes) != float: handles = make_legend_circles_for( [bus_sizes.min(), bus_sizes.max()], scale=1, facecolor="gray" ) From 452045ba93f3233f15a7fe47d2d49fcc6063638f Mon Sep 17 00:00:00 2001 From: alonsoju95 Date: Wed, 25 Jan 2023 19:12:10 +0100 Subject: [PATCH 007/136] check v_nom --- etrago/tools/plot.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/etrago/tools/plot.py b/etrago/tools/plot.py index 663df1455..b96cc51ef 100644 --- a/etrago/tools/plot.py +++ b/etrago/tools/plot.py @@ -1651,7 +1651,7 @@ def plot_background_grid(network, ax): None. """ - breakpoint() + # breakpoint() linkcarrier = pd.Series( data=network.links.carrier, index=network.links.index ) @@ -1792,7 +1792,7 @@ def plot_grid( network = self.disaggregated_network.copy() else: network = self.network.copy() - breakpoint() + # breakpoint() # Set colors for plotting plotting_colors(network) @@ -1875,7 +1875,7 @@ def plot_grid( logger.warning("line_color {} undefined".format(line_colors)) # Set bus colors - breakpoint() + # breakpoint() if bus_colors == "nodal_production_balance": bus_scaling = bus_sizes bus_sizes, bus_colors = nodal_production_balance( @@ -1920,8 +1920,8 @@ def plot_grid( # logger.warning("bus_color {} undefined".format(bus_colors)) elif bus_colors == "grey": bus_scaling = bus_sizes - bus_unit = "" - bus_legend = "" + # bus_unit = "" + # bus_legend = "" bus_sizes = pd.Series( data=network.buses.carrier, index=network.buses.index ) @@ -2002,6 +2002,7 @@ def plot_grid( cb = plt.colorbar( ll[1], boundaries=v, ticks=v[0:101:10], fraction=0.046, pad=0.04 ) + # Set legend label cb.set_label(label) From 9e107aa6c0d86d181bbc2eed8255732afbcf19dc Mon Sep 17 00:00:00 2001 From: alonsoju95 Date: Mon, 13 Feb 2023 23:59:23 +0100 Subject: [PATCH 008/136] update --- etrago/tools/plot.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/etrago/tools/plot.py b/etrago/tools/plot.py index b96cc51ef..b8f0b6538 100644 --- a/etrago/tools/plot.py +++ b/etrago/tools/plot.py @@ -1811,7 +1811,7 @@ def plot_grid( fig, ax = plt.subplots( subplot_kw={"projection": ccrs.PlateCarree()}, figsize=(5, 5) ) - + # breakpoint() # Set line colors if line_colors == "line_loading": title = ( @@ -1901,6 +1901,7 @@ def plot_grid( bus_sizes = bus_scaling * calc_dispatch_per_carrier(network, timesteps) bus_legend = "Dispatch" bus_unit = "TW" + elif ( bus_colors == "PowerToH2" ): # PowerToH2 plots p_nom_opt of links with carrier=power to H2 From 15596aef3401a6c1ae7749967a969e7f9a5e5e77 Mon Sep 17 00:00:00 2001 From: alonsoju95 Date: Tue, 14 Feb 2023 12:36:48 +0100 Subject: [PATCH 009/136] update --- etrago/tools/plot.py | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/etrago/tools/plot.py b/etrago/tools/plot.py index b8f0b6538..a8e0a9bfc 100644 --- a/etrago/tools/plot.py +++ b/etrago/tools/plot.py @@ -1651,24 +1651,21 @@ def plot_background_grid(network, ax): None. """ - # breakpoint() - linkcarrier = pd.Series( - data=network.links.carrier, index=network.links.index - ) - linkcarrier[linkcarrier != "DC"] = 0 - linkcarrier[linkcarrier == "DC"] = 0.3 - linkcarrier = linkcarrier.astype(float) + link_widths = pd.Series(index=network.links.index, data=0) + link_widths.loc[network.links.carrier == "DC"] = 0.3 + network.plot( ax=ax, line_colors="grey", link_colors="grey", bus_sizes=0, line_widths=0.5, - link_widths=linkcarrier, # 0.3, + link_widths=link_widths, # 0.3, geomap=True, projection=ccrs.PlateCarree(), color_geomap=True, + # boundaries=[1.5, 16, 46.8, 58], ) @@ -1792,7 +1789,7 @@ def plot_grid( network = self.disaggregated_network.copy() else: network = self.network.copy() - # breakpoint() + # Set colors for plotting plotting_colors(network) @@ -1811,7 +1808,7 @@ def plot_grid( fig, ax = plt.subplots( subplot_kw={"projection": ccrs.PlateCarree()}, figsize=(5, 5) ) - # breakpoint() + # Set line colors if line_colors == "line_loading": title = ( @@ -1875,7 +1872,7 @@ def plot_grid( logger.warning("line_color {} undefined".format(line_colors)) # Set bus colors - # breakpoint() + if bus_colors == "nodal_production_balance": bus_scaling = bus_sizes bus_sizes, bus_colors = nodal_production_balance( From 910d0c26d26e427417a273822da5d4357a9069a2 Mon Sep 17 00:00:00 2001 From: ClaraBuettner Date: Fri, 3 Mar 2023 14:59:13 +0100 Subject: [PATCH 010/136] Add function to calculate system costs in Germany --- etrago/tools/calc_results.py | 157 +++++++++++++++++++++++++++++++++++ 1 file changed, 157 insertions(+) diff --git a/etrago/tools/calc_results.py b/etrago/tools/calc_results.py index b12dd79bf..17fe163a5 100755 --- a/etrago/tools/calc_results.py +++ b/etrago/tools/calc_results.py @@ -204,6 +204,163 @@ def calc_marginal_cost(self): marginal_cost = gen + link + stor return marginal_cost +def german_network(self): + """Cut out all network components in Germany + + Returns + ------- + new_network : pypsa.Network + Network with all components in Germany + + """ + 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 + + 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 not c.name 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 calc_etrago_results(self): """ Function that calculates main results of grid optimization and adds them to Etrago object. From 34b9cb39ec3305bfb4d57d000e0e0a29742b4724 Mon Sep 17 00:00:00 2001 From: ClaraBuettner Date: Fri, 3 Mar 2023 15:00:05 +0100 Subject: [PATCH 011/136] Apply black and isort --- etrago/tools/calc_results.py | 793 +++++++++++++++++++++-------------- 1 file changed, 471 insertions(+), 322 deletions(-) diff --git a/etrago/tools/calc_results.py b/etrago/tools/calc_results.py index 17fe163a5..c25289705 100755 --- a/etrago/tools/calc_results.py +++ b/etrago/tools/calc_results.py @@ -22,187 +22,221 @@ 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 time - import pandas as pd import numpy as np + import pandas as pd 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 - ------- - float - storage expansion in MW + Returns + ------- + float + 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 - ------- - float - store expansion in MW + Returns + ------- + float + 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 - ------- - float - link expansion in MW (differentiating between technologies) + Returns + ------- + float + link expansion in MW (differentiating between technologies) - """ - ext_links = self.network.links[self.network.links.p_nom_extendable] + """ + ext_links = self.network.links[self.network.links.p_nom_extendable] + + links = [0, 0, 0, 0] - 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 - return links def _calc_network_expansion(self): - """ Function that calulates electrical network expansion in MW + """Function that calulates electrical network expansion in MW - Returns - ------- - float - network expansion (AC lines and DC links) in MW + Returns + ------- + float + network expansion (AC lines and DC links) in MW - """ + """ - network = self.network + 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_links = network.links[network.links.p_nom_extendable] + 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 - return lines, dc_links def calc_investment_cost(self): - """ Function that calulates overall annualized investment costs. + """Function that calulates overall annualized investment costs. - Returns - ------- - network_costs : float - Investments in line expansion (AC+DC) - link_costs : float - Investments in sectorcoupling link expansion - stor_costs : float - Investments in storage and store expansion + Returns + ------- + network_costs : float + Investments in line expansion (AC+DC) + link_costs : float + Investments in sectorcoupling link expansion + stor_costs : float + Investments in storage and store expansion - """ - network = self.network + """ + network = self.network - # electrical grid: AC lines, DC lines + # electrical grid: AC lines, DC lines - network_costs = [0, 0] + network_costs = [0, 0] - 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_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"] + + if not ext_lines.empty: + network_costs[0] = ( + (ext_lines.s_nom_opt - ext_lines.s_nom_min) + * ext_lines.capital_cost + ).sum() - if not ext_lines.empty: - 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() + ) - 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() + 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() - 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() + # links in other sectors / coupling different sectors - # links in other sectors / coupling different sectors + link_costs = 0 - 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() - if not ext_links.empty: - link_costs = ((ext_links.p_nom_opt-ext_links.p_nom_min - )*ext_links.capital_cost).sum() + # storage and store costs - # storage and store costs + sto_costs = [0, 0] - sto_costs = [0, 0] + ext_storage = network.storage_units[network.storage_units.p_nom_extendable] + ext_store = network.stores[network.stores.e_nom_extendable] - ext_storage = network.storage_units[network.storage_units.p_nom_extendable] - 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() - if not ext_storage.empty: - 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() - if not ext_store.empty: - sto_costs[1] = (ext_store.e_nom_opt* - ext_store.capital_cost).sum() + return network_costs, link_costs, sto_costs - return network_costs, link_costs, sto_costs def calc_marginal_cost(self): - """ - Function that caluclates and returns marginal costs, considering - generation and link and storage dispatch costs - - Returns - ------- - marginal_cost : float - Annual marginal cost in EUR - - """ - 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() - marginal_cost = gen + link + stor - return marginal_cost + """ + Function that caluclates and returns marginal costs, considering + generation and link and storage dispatch costs + + Returns + ------- + marginal_cost : float + Annual marginal cost in EUR + + """ + 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() + ) + marginal_cost = gen + link + stor + return marginal_cost + def german_network(self): """Cut out all network components in Germany @@ -214,72 +248,98 @@ def german_network(self): """ keep_cntr = ["DE"] - new_idx = self.network.buses[self.network.buses.country.isin(keep_cntr)].index + 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) - + "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.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.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.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.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.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.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) - + 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 @@ -296,12 +356,12 @@ def system_costs_germany(self): 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" @@ -311,158 +371,247 @@ def system_costs_germany(self): 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() + 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 not c.name 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 - + marginal_cost += ( + c.pnl.p0.mul(c.df.marginal_cost) + .mul(network_de.snapshot_weightings.generators, axis=0) + .sum() + .sum() + ) + if not c.name 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 calc_etrago_results(self): - """ Function that calculates main results of grid optimization - and adds them to Etrago object. - - 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.' - - # system 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() - - 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'] - - # 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] - - # 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'] - - 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 + """Function that calculates main results of grid optimization + and adds them to Etrago object. + + 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." + + # system 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() + + 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"] + ) + + # 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] + + # 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"] + ) + + 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() + ) From 26bf6ccb2a1d25f4ce34db9e6ade55f985c054aa Mon Sep 17 00:00:00 2001 From: ClaraBuettner Date: Fri, 3 Mar 2023 15:08:13 +0100 Subject: [PATCH 012/136] Add plots for duration curves of flexibility options --- etrago/tools/network.py | 3 + etrago/tools/plot.py | 123 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 126 insertions(+) diff --git a/etrago/tools/network.py b/etrago/tools/network.py index d0d2a1330..936a6a228 100644 --- a/etrago/tools/network.py +++ b/etrago/tools/network.py @@ -50,6 +50,7 @@ from etrago.tools.plot import ( bev_flexibility_potential, demand_side_management, + flexibility_duration_curve, flexibility_usage, heat_stores, hydrogen_stores, @@ -269,6 +270,8 @@ def __init__( plot_heat_summary = plot_heat_summary + plot_flexibility_duration_curve = flexibility_duration_curve + plot_flexibility_usage = flexibility_usage demand_side_management = demand_side_management diff --git a/etrago/tools/plot.py b/etrago/tools/plot.py index 270366e03..953c3841e 100644 --- a/etrago/tools/plot.py +++ b/etrago/tools/plot.py @@ -3276,3 +3276,126 @@ 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(self, buses, filename=None): + """Plot duration curves of flexibility options + + Parameters + ---------- + buses : list + List of considered bus indices + filename : str, optional + Name of file to save plot. The default is None. + + Returns + ------- + None. + + """ + fig, ax = plt.subplots(figsize=(15, 8)) + df = pd.DataFrame(index=range(len(self.network.snapshots))) + + df["dsm"] = ( + self.demand_side_management( + buses=buses, + snapshots=range(len(self.network.snapshots)), + used=True, + ) + .e.sort_values() + .reset_index() + .e + ) + + df["mobility"] = ( + ( + self.bev_flexibility_potential( + buses=buses, + snapshots=range(len(self.network.snapshots)), + used=True, + ).e + - self.bev_flexibility_potential( + buses=buses, + snapshots=range(len(self.network.snapshots)), + used=True, + )[["e_max", "e_min"]].mean(axis=1) + ) + .sort_values() + .reset_index()[0] + ) + df["mobility"] + df["heat"] = ( + shifted_energy(self, "heat", buses).sort_values().reset_index()[0] + ) + + df["hydrogen_stores"] = ( + shifted_energy(self, "H2", buses).sort_values().reset_index()[0] + ) + + df.mul(1e-3).plot(ax=ax) + ax.set_ylabel("Usage in GWh") + plt.axhline(y=0.0, color="grey", linestyle="dotted") + + if filename is None: + plt.show() + else: + matplotlib.pylab.savefig(filename, dpi=400, bbox_inches="tight") + plt.close() From ad0ba1c34f123bcce09e8713f237d24c380f4eeb Mon Sep 17 00:00:00 2001 From: ClaraBuettner Date: Fri, 3 Mar 2023 15:13:56 +0100 Subject: [PATCH 013/136] Add functions to calculate exports --- etrago/tools/calc_results.py | 160 +++++++++++++++++++++++++++++++++++ etrago/tools/network.py | 16 +++- 2 files changed, 175 insertions(+), 1 deletion(-) diff --git a/etrago/tools/calc_results.py b/etrago/tools/calc_results.py index c25289705..2ef643c7a 100755 --- a/etrago/tools/calc_results.py +++ b/etrago/tools/calc_results.py @@ -469,6 +469,166 @@ def system_costs_germany(self): 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 + + """ + 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 + + """ + 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 + def calc_etrago_results(self): """Function that calculates main results of grid optimization and adds them to Etrago object. diff --git a/etrago/tools/network.py b/etrago/tools/network.py index 936a6a228..a80b4830f 100644 --- a/etrago/tools/network.py +++ b/etrago/tools/network.py @@ -34,7 +34,13 @@ 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 +) from etrago.tools.execute import ( dispatch_disaggregation, lopf, @@ -240,6 +246,14 @@ 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 From 37849b3b8d53c0c6bbfb1ed885488552398343a8 Mon Sep 17 00:00:00 2001 From: ClaraBuettner Date: Fri, 3 Mar 2023 15:22:07 +0100 Subject: [PATCH 014/136] Apply black and isort --- etrago/tools/network.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/etrago/tools/network.py b/etrago/tools/network.py index a80b4830f..2ed5aa5e9 100644 --- a/etrago/tools/network.py +++ b/etrago/tools/network.py @@ -39,7 +39,7 @@ ac_export_per_country, calc_etrago_results, dc_export, - dc_export_per_country + dc_export_per_country, ) from etrago.tools.execute import ( dispatch_disaggregation, From 30176e924009b3468688afcf9202c0d13a6e4565 Mon Sep 17 00:00:00 2001 From: CarlosEpia Date: Tue, 21 Mar 2023 09:02:30 +0100 Subject: [PATCH 015/136] fix line_loading plot in plot_grid --- etrago/tools/plot.py | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/etrago/tools/plot.py b/etrago/tools/plot.py index 8de936881..e66c60a18 100644 --- a/etrago/tools/plot.py +++ b/etrago/tools/plot.py @@ -1440,7 +1440,7 @@ def calc_ac_loading(network, timesteps): Returns ------- pandas.Series - ACC line loading in MVA + AC line loading in MVA """ loading_lines = ( @@ -2318,11 +2318,11 @@ def plot_grid( ].sum() line_colors = calc_ac_loading(network, timesteps).abs() / rep_snapshots link_colors = calc_dc_loading(network, timesteps).abs() / rep_snapshots - link_widths = 2 + pd.Series(1, index=link_colors.index) - link_widths[link_colors.index.isin(network.links[network.links.carrier!="DC"].index)] = 0 + link_widths = network.links.carrier + link_widths = link_widths.apply(lambda x: 2 if x == "DC" else 0) label = "line loading in p.u." #Only active flow direction is displayed! - flow = pd.Series(index=network.branches().index, dtype="float64") + flow = pd.Series(1, index=network.branches().index, dtype="float64") flow.iloc[flow.index.get_level_values("component") == "Line"] = ( mul_weighting(network, network.lines_t.p0) .loc[network.snapshots[timesteps]] @@ -2333,11 +2333,13 @@ def plot_grid( flow.iloc[flow.index.get_level_values("component") == "Link"] = ( calc_dc_loading(network, timesteps) / rep_snapshots ).values + flow = flow[(flow.index.get_level_values("component")=="Line")| - (flow.index.isin(link_widths[link_colors.index.isin( + (flow.index.isin(link_widths[link_widths.index.isin( network.links[network.links.carrier=="DC"].index) ].index, level=1))] - flow = flow.apply(lambda x: x+5 if x > 0 else x-5) + flow[flow<0] = -1 + flow[flow>0] = 1 elif line_colors == "v_nom": title = "Voltage levels" @@ -2461,7 +2463,7 @@ def plot_grid( title=title, geomap=False, ) - + # legends for bus sizes and colors if bus_colors != "grey": # if type(bus_sizes) != float: From 712cb1f96acbb9c5cc4d6410929cd6bf03812fa4 Mon Sep 17 00:00:00 2001 From: ClaraBuettner Date: Thu, 22 Dec 2022 13:45:10 +0100 Subject: [PATCH 016/136] Fix mul_weighting function --- etrago/tools/plot.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/etrago/tools/plot.py b/etrago/tools/plot.py index e66c60a18..e0628fc96 100644 --- a/etrago/tools/plot.py +++ b/etrago/tools/plot.py @@ -1423,7 +1423,8 @@ def mul_weighting(network, timeseries): timeseries considering snapshot_weightings """ - return timeseries.mul(network.snapshot_weightings["objective"], axis=0) + return timeseries.mul(network.snapshot_weightings.generators, axis=0) + def calc_ac_loading(network, timesteps): From ff3d9029c9cc49e1e73fea6eaa99355df4e4535e Mon Sep 17 00:00:00 2001 From: CarlosEpia Date: Tue, 21 Mar 2023 09:53:47 +0100 Subject: [PATCH 017/136] change boundaries for plots --- etrago/tools/plot.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/etrago/tools/plot.py b/etrago/tools/plot.py index e0628fc96..acc94a8ed 100644 --- a/etrago/tools/plot.py +++ b/etrago/tools/plot.py @@ -1671,6 +1671,7 @@ def plot_background_grid(network, ax): geomap=True, projection=ccrs.PlateCarree(), color_geomap=True, + boundaries = [1.5,16,46.8,58], ) else: network.plot( @@ -2433,7 +2434,7 @@ def plot_grid( bus_sizes[bus_sizes != "AC"] = 0 bus_sizes[bus_sizes == "AC"] = 1 * bus_scaling bus_scaling = bus_sizes - + if cartopy_present: ll = network.plot( line_colors=line_colors, @@ -2449,6 +2450,7 @@ def plot_grid( geomap=False, projection=ccrs.PlateCarree(), color_geomap=True, + boundaries = [1.5,16,46.8,58], ) else: ll = network.plot( @@ -2463,6 +2465,7 @@ def plot_grid( flow=flow, title=title, geomap=False, + boundaries = [1.5,16,46.8,58], ) # legends for bus sizes and colors From c87a1fbebe245a4808f1f2c2b511f362b7c4b678 Mon Sep 17 00:00:00 2001 From: ClaraBuettner Date: Thu, 22 Dec 2022 13:48:04 +0100 Subject: [PATCH 018/136] Add option to set legend entries --- etrago/tools/plot.py | 69 ++++++++++++++++++++++---------------------- 1 file changed, 35 insertions(+), 34 deletions(-) diff --git a/etrago/tools/plot.py b/etrago/tools/plot.py index acc94a8ed..7e4b364c8 100644 --- a/etrago/tools/plot.py +++ b/etrago/tools/plot.py @@ -2202,22 +2202,19 @@ def plot_carrier(network, carrier_links=["AC"], carrier_buses=["AC"]): ax.autoscale() -def plot_grid( - self, - line_colors, - bus_sizes=0.001, - bus_colors="grey", - timesteps=range(2), - osm=False, - boundaries=None, - filename=None, - disaggregated=False, - ext_min=0.1, - ext_width=False, -): - """Function that plots etrago.network and results for lines and buses - - +def plot_grid(self, + line_colors, + bus_sizes=0.001, + bus_colors='grey', + timesteps=range(2), + osm=False, + boundaries=None, + filename=None, + disaggregated=False, + ext_min=0.1, + ext_width=False, + legend_entries = 'all'): + """ Function that plots etrago.network and results for lines and buses Parameters ---------- @@ -2499,6 +2496,10 @@ def plot_grid( positive = mpatches.Patch(color="green", label="generation") negative = mpatches.Patch(color="red", label="consumption") handles = [positive, negative] + elif legend_entries != 'all': + for i in legend_entries: + patch = mpatches.Patch(color=network.carriers.color[i], label=i) + handles.append(patch) else: for i in network.carriers.color.index: patch = mpatches.Patch( @@ -2507,27 +2508,27 @@ def plot_grid( handles.append(patch) l3 = plt.legend( - handles=handles, loc="upper left", ncol=3, bbox_to_anchor=(-0.1, 0) + handles=handles, loc="upper left", + ncol=2, bbox_to_anchor=(0, 0) ) ax.add_artist(l3) - # Set fixed boundaries if selected in parameters - if not boundaries: - boundaries = [ - min(line_colors.min(), link_colors.min()), - max(line_colors.max(), link_colors.max()), - ] - - # Create ticks for legend - v = np.linspace(boundaries[0], boundaries[1], 101) - - # colorbar for line heatmap - cb = plt.colorbar( - ll[1], boundaries=v, ticks=v[0:101:10], fraction=0.046, pad=0.04 - ) - - # Set legend label - cb.set_label(label) + if type(line_colors) != str: + print(111111) + # Set fixed boundaries if selected in parameters + if not boundaries: + boundaries = [ + min(line_colors.min(), link_colors.min()), + max(line_colors.max(), link_colors.max()), + ] + + # Create ticks for legend + v = np.linspace(boundaries[0], boundaries[1], 101) + + # colorbar for line heatmap + cb = plt.colorbar(ll[2], boundaries=v, ticks=v[0:101:10], fraction=0.046, pad=0.04) + # Set legend label + cb.set_label(label) # Show plot or save to file if filename is None: From c1fee2ef7f75fac943340b1233275814e1b8950b Mon Sep 17 00:00:00 2001 From: ClaraBuettner Date: Thu, 22 Dec 2022 13:48:48 +0100 Subject: [PATCH 019/136] Add plot to show dynamic line rating usage --- etrago/tools/plot.py | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/etrago/tools/plot.py b/etrago/tools/plot.py index 7e4b364c8..b56556ff7 100644 --- a/etrago/tools/plot.py +++ b/etrago/tools/plot.py @@ -2373,6 +2373,28 @@ def plot_grid(self, network.lines_t.q0.abs().max() / (network.lines.s_nom) ) link_colors = pd.Series(data=0, index=network.links.index) + elif line_colors == "dlr": + title = "Dynamic line rating" + label = "MWh above nominal capacity" + plot_background_grid(network, ax) + line_loading = network.lines_t.p0.mul(1/network.lines.s_nom_opt) + dlr_usage = line_loading[line_loading.abs() > 1].fillna(0).mul( + network.snapshot_weightings.generators, axis = 0).abs().sum() + line_colors = dlr_usage + if ext_width != False: + line_widths = 0.5 + (line_colors / ext_width) + link_colors = pd.Series(data=0, index=network.links.index) + + elif line_colors == 'blue': + title = "" + label = "" + line_colors = 'blue' + link_colors = 'blue' + plot_background_grid(network, ax) + link_widths = 0 + line_widths = 0 + + else: logger.warning("line_color {} undefined".format(line_colors)) From edd0cf9c80cfdc54f1c878c67a35116001c72342 Mon Sep 17 00:00:00 2001 From: ClaraBuettner Date: Thu, 22 Dec 2022 13:49:18 +0100 Subject: [PATCH 020/136] Add plot for flexibility usage per node --- etrago/tools/plot.py | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/etrago/tools/plot.py b/etrago/tools/plot.py index b56556ff7..6eb901b4a 100644 --- a/etrago/tools/plot.py +++ b/etrago/tools/plot.py @@ -2425,7 +2425,23 @@ def plot_grid(self, bus_sizes = bus_scaling * calc_dispatch_per_carrier(network, timesteps) bus_legend = "Dispatch" bus_unit = "TW" - + elif bus_colors == 'flexibility_usage': + #import pdb; pdb.set_trace() + bus_scaling = bus_sizes + flex_links = network.links[network.links.carrier.isin([ + 'dsm', 'BEV charger', + #'central_heat_store_charger', + #'central_heat_store_discharger', + #'rural_heat_store_charger', + #'rural_heat_store_discharger' + ])] + flex_links["p0_sum"] = network.links_t.p0[flex_links.index].mul( + network.snapshot_weightings.generators, axis=0).abs().sum() + flex_links["p1_sum"] = network.links_t.p1[flex_links.index].mul( + network.snapshot_weightings.generators, axis=0).sum() + bus_sizes = bus_scaling * flex_links.groupby(['bus0', 'carrier']).p0_sum.sum() + bus_unit = "TWh" + bus_legend = "flexibility_usage" elif ( bus_colors == "PowerToH2" ): # PowerToH2 plots p_nom_opt of links with carrier=power to H2 From 4798efd19c7773cd2cf173e9ab1f04b4dba63a9b Mon Sep 17 00:00:00 2001 From: ClaraBuettner Date: Thu, 22 Dec 2022 13:49:37 +0100 Subject: [PATCH 021/136] Add h2 storage plot --- etrago/tools/plot.py | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/etrago/tools/plot.py b/etrago/tools/plot.py index 6eb901b4a..433a3c014 100644 --- a/etrago/tools/plot.py +++ b/etrago/tools/plot.py @@ -2442,6 +2442,14 @@ def plot_grid(self, bus_sizes = bus_scaling * flex_links.groupby(['bus0', 'carrier']).p0_sum.sum() bus_unit = "TWh" bus_legend = "flexibility_usage" + elif bus_colors == "h2_storage_expansion": + bus_scaling = bus_sizes + bus_sizes = bus_scaling * calc_storage_expansion_per_bus(network) + bus_sizes = bus_sizes.reset_index() + bus_sizes = bus_sizes[bus_sizes.carrier.str.contains('H2')] + bus_sizes.set_index(['bus','carrier'],inplace=True) + bus_legend = "Storage expansion" + bus_unit = "GW" elif ( bus_colors == "PowerToH2" ): # PowerToH2 plots p_nom_opt of links with carrier=power to H2 @@ -2456,9 +2464,6 @@ def plot_grid(self, bus_colors = coloring()["power_to_H2"] bus_legend = "PowerToH2" bus_unit = "TW" - - # else: - # logger.warning("bus_color {} undefined".format(bus_colors)) elif bus_colors == "grey": bus_scaling = bus_sizes # bus_unit = "" @@ -2469,6 +2474,8 @@ def plot_grid(self, bus_sizes[bus_sizes != "AC"] = 0 bus_sizes[bus_sizes == "AC"] = 1 * bus_scaling bus_scaling = bus_sizes + else: + logger.warning("bus_color {} undefined".format(bus_colors)) if cartopy_present: ll = network.plot( @@ -2503,6 +2510,7 @@ def plot_grid(self, boundaries = [1.5,16,46.8,58], ) + # legends for bus sizes and colors if bus_colors != "grey": # if type(bus_sizes) != float: From 0cdad076096e54a93bd296f5338c4c1524103a66 Mon Sep 17 00:00:00 2001 From: ClaraBuettner Date: Tue, 14 Feb 2023 08:53:01 +0100 Subject: [PATCH 022/136] Add color for 'heat' --- etrago/tools/plot.py | 1 + 1 file changed, 1 insertion(+) diff --git a/etrago/tools/plot.py b/etrago/tools/plot.py index 433a3c014..297f25fe7 100644 --- a/etrago/tools/plot.py +++ b/etrago/tools/plot.py @@ -153,6 +153,7 @@ def coloring(): "H2_grid": "green", "H2_saltcavern": "darkgreen", "central_heat_store": "firebrick", + "heat": "firebrick", "rural_heat_store": "salmon", "AC": "blue", "nuclear": "palegreen", From 49290e56c010cdf4e8078d4d05472521cd501856 Mon Sep 17 00:00:00 2001 From: CarlosEpia Date: Tue, 21 Mar 2023 10:14:54 +0100 Subject: [PATCH 023/136] Change boundaries of map --- etrago/tools/plot.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/etrago/tools/plot.py b/etrago/tools/plot.py index 297f25fe7..3b1fe2daf 100644 --- a/etrago/tools/plot.py +++ b/etrago/tools/plot.py @@ -1672,7 +1672,7 @@ def plot_background_grid(network, ax): geomap=True, projection=ccrs.PlateCarree(), color_geomap=True, - boundaries = [1.5,16,46.8,58], + boundaries=[-2.5, 16, 46.8, 58], ) else: network.plot( @@ -2493,7 +2493,7 @@ def plot_grid(self, geomap=False, projection=ccrs.PlateCarree(), color_geomap=True, - boundaries = [1.5,16,46.8,58], + boundaries=[-2.5, 16, 46.8, 58] ) else: ll = network.plot( @@ -2508,7 +2508,7 @@ def plot_grid(self, flow=flow, title=title, geomap=False, - boundaries = [1.5,16,46.8,58], + boundaries=[-2.5, 16, 46.8, 58] ) From fc5a3aea11d9e457fd6555614e0e2da14fdb96af Mon Sep 17 00:00:00 2001 From: ClaraBuettner Date: Tue, 14 Feb 2023 08:54:05 +0100 Subject: [PATCH 024/136] Add different legend sizes for stores --- etrago/tools/plot.py | 130 +++++++++++++++++++++++++------------------ 1 file changed, 75 insertions(+), 55 deletions(-) diff --git a/etrago/tools/plot.py b/etrago/tools/plot.py index 3b1fe2daf..bc3817616 100644 --- a/etrago/tools/plot.py +++ b/etrago/tools/plot.py @@ -2203,19 +2203,26 @@ def plot_carrier(network, carrier_links=["AC"], carrier_buses=["AC"]): ax.autoscale() -def plot_grid(self, - line_colors, - bus_sizes=0.001, - bus_colors='grey', - timesteps=range(2), - osm=False, - boundaries=None, - filename=None, - disaggregated=False, - ext_min=0.1, - ext_width=False, - legend_entries = 'all'): - """ Function that plots etrago.network and results for lines and buses +def plot_grid( + self, + line_colors, + bus_sizes=0.001, + bus_colors="grey", + timesteps=range(2), + osm=False, + boundaries=None, + filename=None, + disaggregated=False, + ext_min=0.1, + ext_width=False, + legend_entries="all", + scaling_store_expansion = { + "H2": 50, + "heat": 0.1, + "battery": 10, + } +): + """Function that plots etrago.network and results for lines and buses Parameters ---------- @@ -2411,6 +2418,13 @@ def plot_grid(self, elif bus_colors == "storage_expansion": bus_scaling = bus_sizes bus_sizes = bus_scaling * calc_storage_expansion_per_bus(network) + for store_carrier in ["H2", "heat", "battery"]: + bus_sizes[bus_sizes.index.get_level_values('carrier').str.contains(store_carrier)] *= scaling_store_expansion[store_carrier] + bus_legend = "Storage expansion" + bus_unit = "GW" + elif bus_colors == "h2_battery_storage_expansion": + bus_scaling = bus_sizes + bus_sizes = bus_scaling * calc_storage_expansion_per_bus(network, carriers=["battery", "H2_overground", "H2_underground"]) bus_legend = "Storage expansion" bus_unit = "GW" elif bus_colors == "storage_distribution": @@ -2480,8 +2494,8 @@ def plot_grid(self, if cartopy_present: ll = network.plot( - line_colors=line_colors, - link_colors=link_colors, + line_colors=line_colors.mul(1e-3), + link_colors=link_colors.mul(1e-3), line_cmap=plt.cm.jet, link_cmap=plt.cm.jet, bus_sizes=bus_sizes, @@ -2489,16 +2503,16 @@ def plot_grid(self, line_widths=line_widths, link_widths=link_widths, flow=flow, - title=title, + #title=title, geomap=False, projection=ccrs.PlateCarree(), color_geomap=True, - boundaries=[-2.5, 16, 46.8, 58] + boundaries=[-2.5, 16, 46.8, 58], ) else: - ll = network.plot( - line_colors=line_colors, - link_colors=link_colors, + ll =network.plot( + line_colors=line_colors.mul(1e-3), + link_colors=link_colors.mul(1e-3), line_cmap=plt.cm.jet, link_cmap=plt.cm.jet, bus_sizes=bus_sizes, @@ -2506,25 +2520,29 @@ def plot_grid(self, line_widths=line_widths, link_widths=link_widths, flow=flow, - title=title, + #title=title, geomap=False, - boundaries=[-2.5, 16, 46.8, 58] + boundaries=[-2.5, 16, 46.8, 58], ) # legends for bus sizes and colors - if bus_colors != "grey": - # if type(bus_sizes) != float: - handles = make_legend_circles_for( - [bus_sizes.min(), bus_sizes.max()], scale=1, facecolor="gray" - ) - labels = [ - ("{} " + bus_unit).format(s) - for s in ( - round(bus_sizes.min() / bus_scaling / 1000, 0), - round(bus_sizes.max() / bus_scaling / 1000, 0), - ) - ] + if type(bus_sizes) != float: + handles = [] + labels = [] + for i in legend_entries: + try: + max_value = bus_sizes[bus_sizes.index.get_level_values('carrier').str.contains(i)].max() + except: + max_value = bus_sizes.max() + handles.append(make_legend_circles_for( + [max_value], scale=1, facecolor=network.carriers.color[i], + )[0]) + + if scaling_store_expansion: + labels.append(f"{round(max_value / bus_scaling / scaling_store_expansion[i]/1000, 2)} GWh " + i) + else: + labels.append(f"{round(max_value / bus_scaling /1000, 2)} GWh " + i) l2 = ax.legend( handles, @@ -2535,33 +2553,35 @@ def plot_grid(self, framealpha=1.0, title=bus_legend, handler_map=make_handler_map_to_scale_circles_as_in(ax), + prop={'size': 8} ) ax.add_artist(l2) - handles = [] - if bus_legend == "Nodal production balance": - positive = mpatches.Patch(color="green", label="generation") - negative = mpatches.Patch(color="red", label="consumption") - handles = [positive, negative] - elif legend_entries != 'all': - for i in legend_entries: - patch = mpatches.Patch(color=network.carriers.color[i], label=i) - handles.append(patch) - else: - for i in network.carriers.color.index: - patch = mpatches.Patch( - color=network.carriers.color[i], label=i - ) - handles.append(patch) + plt.setp(l2.get_title(),fontsize='9') - l3 = plt.legend( - handles=handles, loc="upper left", - ncol=2, bbox_to_anchor=(0, 0) - ) - ax.add_artist(l3) + if not scaling_store_expansion: + + handles = [] + if bus_legend == "Nodal production balance": + positive = mpatches.Patch(color="green", label="generation") + negative = mpatches.Patch(color="red", label="consumption") + handles = [positive, negative] + elif legend_entries != "all": + for i in legend_entries: + patch = mpatches.Patch(color=network.carriers.color[i], label=i) + handles.append(patch) + else: + for i in network.carriers.color.index: + patch = mpatches.Patch(color=network.carriers.color[i], label=i) + handles.append(patch) + + l3 = plt.legend( + handles=handles, loc="upper left", ncol=2, bbox_to_anchor=(0, 0) + ) + ax.add_artist(l3) if type(line_colors) != str: - print(111111) + # Set fixed boundaries if selected in parameters if not boundaries: boundaries = [ From 1ace72e34d84d2a411d825448c311c9e4b9a27de Mon Sep 17 00:00:00 2001 From: ClaraBuettner Date: Tue, 14 Feb 2023 08:54:44 +0100 Subject: [PATCH 025/136] Delete unused lines --- etrago/tools/plot.py | 35 +++++++++++++++++++++-------------- 1 file changed, 21 insertions(+), 14 deletions(-) diff --git a/etrago/tools/plot.py b/etrago/tools/plot.py index bc3817616..0f0538ebc 100644 --- a/etrago/tools/plot.py +++ b/etrago/tools/plot.py @@ -2440,21 +2440,28 @@ def plot_grid( bus_sizes = bus_scaling * calc_dispatch_per_carrier(network, timesteps) bus_legend = "Dispatch" bus_unit = "TW" - elif bus_colors == 'flexibility_usage': - #import pdb; pdb.set_trace() + elif bus_colors == "flexibility_usage": bus_scaling = bus_sizes - flex_links = network.links[network.links.carrier.isin([ - 'dsm', 'BEV charger', - #'central_heat_store_charger', - #'central_heat_store_discharger', - #'rural_heat_store_charger', - #'rural_heat_store_discharger' - ])] - flex_links["p0_sum"] = network.links_t.p0[flex_links.index].mul( - network.snapshot_weightings.generators, axis=0).abs().sum() - flex_links["p1_sum"] = network.links_t.p1[flex_links.index].mul( - network.snapshot_weightings.generators, axis=0).sum() - bus_sizes = bus_scaling * flex_links.groupby(['bus0', 'carrier']).p0_sum.sum() + flex_links = network.links[ + network.links.carrier.isin( + [ + "dsm", + "BEV charger", + ] + ) + ] + flex_links["p0_sum"] = ( + network.links_t.p0[flex_links.index] + .mul(network.snapshot_weightings.generators, axis=0) + .abs() + .sum() + ) + flex_links["p1_sum"] = ( + network.links_t.p1[flex_links.index] + .mul(network.snapshot_weightings.generators, axis=0) + .sum() + ) + bus_sizes = bus_scaling * flex_links.groupby(["bus0", "carrier"]).p0_sum.sum() bus_unit = "TWh" bus_legend = "flexibility_usage" elif bus_colors == "h2_storage_expansion": From a4f665c48a8e483fc53c5095313f65d6a4c9e0d4 Mon Sep 17 00:00:00 2001 From: CarlosEpia Date: Tue, 21 Mar 2023 10:48:52 +0100 Subject: [PATCH 026/136] Scale line exapsnion to GVA --- etrago/tools/plot.py | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/etrago/tools/plot.py b/etrago/tools/plot.py index 0f0538ebc..1b22e6f8b 100644 --- a/etrago/tools/plot.py +++ b/etrago/tools/plot.py @@ -2499,10 +2499,13 @@ def plot_grid( else: logger.warning("bus_color {} undefined".format(bus_colors)) + if type(link_colors) != str: + link_colors=link_colors.mul(1e-3) + line_colors=line_colors.mul(1e-3) if cartopy_present: ll = network.plot( - line_colors=line_colors.mul(1e-3), - link_colors=link_colors.mul(1e-3), + line_colors=line_colors, + link_colors=link_colors, line_cmap=plt.cm.jet, link_cmap=plt.cm.jet, bus_sizes=bus_sizes, @@ -2518,8 +2521,8 @@ def plot_grid( ) else: ll =network.plot( - line_colors=line_colors.mul(1e-3), - link_colors=link_colors.mul(1e-3), + line_colors=line_colors, + link_colors=link_colors, line_cmap=plt.cm.jet, link_cmap=plt.cm.jet, bus_sizes=bus_sizes, From abbf70f93c60e222d69394d888cfbb26670e439b Mon Sep 17 00:00:00 2001 From: ClaraBuettner Date: Tue, 14 Feb 2023 08:55:29 +0100 Subject: [PATCH 027/136] Fix colorbar for all line collections --- etrago/tools/plot.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/etrago/tools/plot.py b/etrago/tools/plot.py index 1b22e6f8b..126fc6466 100644 --- a/etrago/tools/plot.py +++ b/etrago/tools/plot.py @@ -2601,9 +2601,13 @@ def plot_grid( # Create ticks for legend v = np.linspace(boundaries[0], boundaries[1], 101) - + for l_collection in ll: + l_collection.set_clim(boundaries[0], boundaries[1]) + # colorbar for line heatmap - cb = plt.colorbar(ll[2], boundaries=v, ticks=v[0:101:10], fraction=0.046, pad=0.04) + cb = plt.colorbar( + ll[1], values=v, ticks=v[0:101:10], fraction=0.028, pad=0.04, + ) # Set legend label cb.set_label(label) From dbcc32d8dfa30bfeb75f3613ef93e0dba3f1b56f Mon Sep 17 00:00:00 2001 From: CarlosEpia Date: Tue, 21 Mar 2023 16:07:53 +0100 Subject: [PATCH 028/136] fix line_loading plot in plot_grid --- etrago/tools/plot.py | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/etrago/tools/plot.py b/etrago/tools/plot.py index 126fc6466..57d3fcdb9 100644 --- a/etrago/tools/plot.py +++ b/etrago/tools/plot.py @@ -2326,8 +2326,10 @@ def plot_grid( line_colors = calc_ac_loading(network, timesteps).abs() / rep_snapshots link_colors = calc_dc_loading(network, timesteps).abs() / rep_snapshots link_widths = network.links.carrier - link_widths = link_widths.apply(lambda x: 2 if x == "DC" else 0) + link_widths = link_widths.apply(lambda x: 10 if x == "DC" else 0) + line_widths = 10 label = "line loading in p.u." + plot_background_grid(network, ax) #Only active flow direction is displayed! flow = pd.Series(1, index=network.branches().index, dtype="float64") flow.iloc[flow.index.get_level_values("component") == "Line"] = ( @@ -2364,6 +2366,8 @@ def plot_grid( if ext_width != False: line_widths = 0.5 + (line_colors / ext_width) link_widths = 0.5 + (link_colors / ext_width) + else: + link_widths = 2.0 elif line_colors == "expansion_rel": title = "Network expansion" label = "network expansion in %" @@ -2499,7 +2503,7 @@ def plot_grid( else: logger.warning("bus_color {} undefined".format(bus_colors)) - if type(link_colors) != str: + if not isinstance(link_colors, (pd.Series, float)): link_colors=link_colors.mul(1e-3) line_colors=line_colors.mul(1e-3) if cartopy_present: @@ -2537,7 +2541,7 @@ def plot_grid( # legends for bus sizes and colors - if type(bus_sizes) != float: + if not isinstance(bus_sizes, (pd.Series, float)): handles = [] labels = [] for i in legend_entries: @@ -2613,7 +2617,7 @@ def plot_grid( # Show plot or save to file if filename is None: - if type(bus_sizes) != float: + if not isinstance(bus_sizes, (pd.Series, float)): logger.warning("Legend of bus sizes will change when zooming") plt.show() else: From b95d32d1023cc25a4d66c31f8806cac767e50a93 Mon Sep 17 00:00:00 2001 From: CarlosEpia Date: Wed, 22 Mar 2023 12:00:07 +0100 Subject: [PATCH 029/136] fix plot nodal_production_balance --- etrago/tools/plot.py | 121 +++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 117 insertions(+), 4 deletions(-) diff --git a/etrago/tools/plot.py b/etrago/tools/plot.py index 57d3fcdb9..c8114524d 100644 --- a/etrago/tools/plot.py +++ b/etrago/tools/plot.py @@ -1153,7 +1153,114 @@ def nodal_production_balance(network, timesteps, scaling=0.00001): 'green' for producer and 'red' for consumer """ + def import_gen_from_links(network): + from pypsa.networkclustering import aggregategenerators + from etrago.cluster.spatial import strategies_generators + """ + create gas generators from links in order to not lose them when + dropping non-electric carriers + """ + # Discard all generators < 1kW + discard_gen = network.links[network.links["p_nom"] <= 0.001].index + network.links.drop(discard_gen, inplace=True) + for df in network.links_t: + if not network.links_t[df].empty: + network.links_t[df].drop( + columns=discard_gen.values, inplace=True, errors="ignore" + ) + + gas_to_add = network.links[ + network.links.carrier.isin( + [ + "central_gas_CHP", + "OCGT", + "H2_to_power", + "industrial_gas_CHP", + ] + ) + ].copy() + + # Drop generators from the links table + network.links.drop(gas_to_add.index, inplace=True) + gas_to_add.rename(columns={"bus1": "bus"}, inplace=True) + + # Create generators' names like in network.generators + gas_to_add["Generator"] = ( + gas_to_add["bus"] + " " + gas_to_add.index + gas_to_add["carrier"] + ) + gas_to_add_orig = gas_to_add.copy() + gas_to_add.set_index("Generator", drop=True, inplace=True) + gas_to_add = gas_to_add[ + gas_to_add.columns[ + gas_to_add.columns.isin(network.generators.columns) + ] + ] + + network.import_components_from_dataframe(gas_to_add, "Generator") + + # Dealing with generators_t + columns_new = network.links_t.p1.columns[ + network.links_t.p1.columns.isin(gas_to_add_orig.index) + ] + + new_gen_t = network.links_t.p1[columns_new] * -1 + new_gen_t.rename(columns=gas_to_add_orig["Generator"], inplace=True) + network.generators_t.p = network.generators_t.p.join(new_gen_t) + + # Drop generators from the links_t table + for df in network.links_t: + if not network.links_t[df].empty: + network.links_t[df].drop( + columns=gas_to_add_orig.index, + inplace=True, + errors="ignore", + ) + + # Group generators per bus if needed + if not ( + network.generators.groupby(["bus", "carrier"]).p_nom.count() == 1 + ).all(): + network.generators["weight"] = network.generators.p_nom + df, df_t = aggregategenerators( + network, + busmap=pd.Series( + index=network.buses.index, data=network.buses.index + ), + custom_strategies=strategies_generators(), + ) + + # Keep control arguments from generators + control = network.generators.groupby( + ["bus", "carrier"] + ).control.first() + control.index = ( + control.index.get_level_values(0) + + " " + + control.index.get_level_values(1) + ) + df.control = control + + # Drop non-aggregated generators + network.mremove("Generator", network.generators.index) + + # Insert aggregated generators and time series + network.import_components_from_dataframe(df, "Generator") + + for attr, data in df_t.items(): + if not data.empty: + network.import_series_from_dataframe( + data, "Generator", attr + ) + + return + + import_gen_from_links(network) + + ac_buses = network.buses[network.buses.carrier == "AC"].index + network.generators = network.generators[network.generators.bus.isin(ac_buses)] + network.generators_t.p = network.generators_t.p.loc[:,network.generators_t.p.columns.isin(network.generators.index)] + gen = ( mul_weighting(network, network.generators_t.p) .groupby(network.generators.bus, axis=1) @@ -1177,7 +1284,9 @@ def nodal_production_balance(network, timesteps, scaling=0.00001): ) bus_sizes = residual_load.abs() * scaling - + bus_sizes = pd.Series(data= bus_sizes, index = network.buses.index).fillna(0) + + bus_colors = pd.Series(data= bus_colors, index = network.buses.index).fillna("grey") return bus_sizes, bus_colors @@ -2379,12 +2488,13 @@ def plot_grid( line_widths = 0.5 + (line_colors / ext_width) link_widths = 0.5 + (link_colors / ext_width) elif line_colors == "q_flow_max": - title = "Maximmal reactive power flows" + title = "Maximum reactive power flows" label = "flow in Mvar" line_colors = abs( network.lines_t.q0.abs().max() / (network.lines.s_nom) ) link_colors = pd.Series(data=0, index=network.links.index) + plot_background_grid(network, ax) elif line_colors == "dlr": title = "Dynamic line rating" label = "MWh above nominal capacity" @@ -2507,6 +2617,9 @@ def plot_grid( link_colors=link_colors.mul(1e-3) line_colors=line_colors.mul(1e-3) if cartopy_present: + #breakpoint() + + ll = network.plot( line_colors=line_colors, link_colors=link_colors, @@ -2517,7 +2630,7 @@ def plot_grid( line_widths=line_widths, link_widths=link_widths, flow=flow, - #title=title, + title=title, geomap=False, projection=ccrs.PlateCarree(), color_geomap=True, @@ -2534,7 +2647,7 @@ def plot_grid( line_widths=line_widths, link_widths=link_widths, flow=flow, - #title=title, + title=title, geomap=False, boundaries=[-2.5, 16, 46.8, 58], ) From f4c0e5240fdc19f8a43093f169e9835cfbfaf104 Mon Sep 17 00:00:00 2001 From: ClaraBuettner Date: Wed, 22 Mar 2023 14:35:08 +0100 Subject: [PATCH 030/136] fix plot legends --- etrago/tools/plot.py | 71 ++++++++++++++++++++++++++++---------------- 1 file changed, 46 insertions(+), 25 deletions(-) diff --git a/etrago/tools/plot.py b/etrago/tools/plot.py index c8114524d..551395644 100644 --- a/etrago/tools/plot.py +++ b/etrago/tools/plot.py @@ -2467,7 +2467,7 @@ def plot_grid( plot_background_grid(network, ax) elif line_colors == "expansion_abs": title = "Network expansion" - label = "network expansion in MW" + label = "network expansion in GVA" all_network, line_colors, link_colors = calc_network_expansion( network, method="abs", ext_min=ext_min ) @@ -2477,6 +2477,8 @@ def plot_grid( link_widths = 0.5 + (link_colors / ext_width) else: link_widths = 2.0 + link_colors = link_colors.mul(1e-3) + line_colors = line_colors.mul(1e-3) elif line_colors == "expansion_rel": title = "Network expansion" label = "network expansion in %" @@ -2499,9 +2501,14 @@ def plot_grid( title = "Dynamic line rating" label = "MWh above nominal capacity" plot_background_grid(network, ax) - line_loading = network.lines_t.p0.mul(1/network.lines.s_nom_opt) - dlr_usage = line_loading[line_loading.abs() > 1].fillna(0).mul( - network.snapshot_weightings.generators, axis = 0).abs().sum() + line_loading = network.lines_t.p0.mul(1 / (network.lines.s_nom_opt*network.lines.s_max_pu)) + dlr_usage = ( + line_loading[line_loading.abs() > 1] + .fillna(0) + .mul(network.snapshot_weightings.generators, axis=0) + .abs() + .sum() + ) line_colors = dlr_usage if ext_width != False: line_widths = 0.5 + (line_colors / ext_width) @@ -2613,13 +2620,7 @@ def plot_grid( else: logger.warning("bus_color {} undefined".format(bus_colors)) - if not isinstance(link_colors, (pd.Series, float)): - link_colors=link_colors.mul(1e-3) - line_colors=line_colors.mul(1e-3) if cartopy_present: - #breakpoint() - - ll = network.plot( line_colors=line_colors, link_colors=link_colors, @@ -2652,24 +2653,44 @@ def plot_grid( boundaries=[-2.5, 16, 46.8, 58], ) - # legends for bus sizes and colors - if not isinstance(bus_sizes, (pd.Series, float)): + if type(bus_sizes) != float: handles = [] labels = [] - for i in legend_entries: - try: - max_value = bus_sizes[bus_sizes.index.get_level_values('carrier').str.contains(i)].max() - except: - max_value = bus_sizes.max() - handles.append(make_legend_circles_for( - [max_value], scale=1, facecolor=network.carriers.color[i], - )[0]) - - if scaling_store_expansion: - labels.append(f"{round(max_value / bus_scaling / scaling_store_expansion[i]/1000, 2)} GWh " + i) - else: - labels.append(f"{round(max_value / bus_scaling /1000, 2)} GWh " + i) + + if scaling_store_expansion: + for i in legend_entries: + try: + max_value = bus_sizes[ + bus_sizes.index.get_level_values("carrier").str.contains(i) + ].max() + except: + max_value = bus_sizes.max() + handles.append( + make_legend_circles_for( + [max_value], + scale=1, + facecolor=network.carriers.color[i], + )[0] + ) + + + labels.append( + f"{round(max_value / bus_scaling / scaling_store_expansion[i]/1000, 0).astype(int)} GWh " + + i + ) + else: + max_value = bus_sizes.max() + labels.append( + f"{round(max_value / bus_scaling /1000, 0)} GWh " + ) + handles.append( + make_legend_circles_for( + [max_value], + scale=1, + facecolor="grey", + )[0] + ) l2 = ax.legend( handles, From c7288fb7c3c96a1c7080f3d8b85fbeb5db823142 Mon Sep 17 00:00:00 2001 From: ClaraBuettner Date: Thu, 23 Mar 2023 09:46:41 +0100 Subject: [PATCH 031/136] Move egio functions to etrago --- etrago/tools/constraints.py | 2 +- etrago/tools/db.py | 200 ++++++++++++++++++++++++++++++++++++ etrago/tools/network.py | 2 +- etrago/tools/utilities.py | 2 +- 4 files changed, 203 insertions(+), 3 deletions(-) create mode 100644 etrago/tools/db.py diff --git a/etrago/tools/constraints.py b/etrago/tools/constraints.py index ab3650bca..b53ff3b10 100755 --- a/etrago/tools/constraints.py +++ b/etrago/tools/constraints.py @@ -23,7 +23,7 @@ """ import logging -from egoio.tools import db +from etrago.tools import db from pyomo.environ import Constraint from pypsa.descriptors import expand_series from pypsa.linopt import define_constraints, define_variables, get_var, linexpr diff --git a/etrago/tools/db.py b/etrago/tools/db.py new file mode 100644 index 000000000..d5de25ee8 --- /dev/null +++ b/etrago/tools/db.py @@ -0,0 +1,200 @@ +# -*- coding: utf-8 -*- +# Copyright 2016-2018 Flensburg University of Applied Sciences, +# Europa-Universität Flensburg, +# Centre for Sustainable Energy Systems, +# DLR-Institute for Networked Energy Systems + +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation; either version 3 of the +# License, or (at your option) any later version. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. + +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . + +import os +import configparser as cp +import keyring +import getpass +from sqlalchemy import create_engine +import oedialect + +def readcfg(filepath, section): + """ + Reads the configuration file. If section is not available, calls + create_oedb_config_file to add the new section to an existing config.ini. + + Parameters + ---------- + filepath : str + Absolute path of config file including the filename itself + section : str + Section in config file which contains connection details + Returns + ------- + cfg : configparser.ConfigParser + Used for configuration file parser language. + """ + + cfg = cp.ConfigParser() + cfg.read(filepath) + + if not cfg.has_section(section): + print('The section "{sec}" is not in the config file {file}.' + .format(sec=section, + file=filepath)) + cfg = create_oedb_config_file(filepath, section) + + return cfg + +def get_connection_details(section): + """ + Asks the user for the database connection details and returns them as a + ConfigParser-object. + + Parameters + ---------- + None + + Returns + ------- + cfg : configparser.ConfigParser + Used for configuration file parser language. + """ + print('Please enter your connection details:') + dialect = input('Enter input value for `dialect` (default: psycopg2): ') or 'psycopg2' + username = input('Enter value for `username`: ') + database = input('Enter value for `database`: ') + host = input('Enter value for `host`: ') + port = input('Enter value for `port` (default: 5432): ') or '5432' + + cfg = cp.ConfigParser() + cfg.add_section(section) + cfg.set(section, 'dialect', dialect) + cfg.set(section, 'username', username) + cfg.set(section, 'host', host) + cfg.set(section, 'port', port) + cfg.set(section, 'database', database) + pw = getpass.getpass(prompt="Enter your password/token to " \ + "store it in " + "keyring: ".format(database=section)) + keyring.set_password(section, cfg.get(section, "username"), pw) + + return cfg + +def create_oedb_config_file(filepath, section='oep'): + """ + + Parameters + ---------- + filepath : str + Absolute path of config file including the filename itself + section : str + Section in config file which contains connection details + + Returns + ------- + cfg : configparser.ConfigParser + Used for configuration file parser language. + """ + + cfg = get_connection_details(section) + + print('Do you want to store the connection details in the config file {file} ?' + .format(file=filepath)) + choice = '' + while choice not in ['y', 'n']: + choice = input('(y/n): ') + + if choice == 'y': + # create egoio dir if not existent + base_path = os.path.split(filepath)[0] + if not os.path.isdir(base_path): + os.mkdir(base_path) + print('The directory {path} was created.'.format(path=base_path)) + + with open(filepath, 'a') as configfile: + cfg.write(configfile) + pass + + + print('Template {0} with section "{1}" created.\nYou can manually edit' + ' the config file.' + .format(filepath, + section)) + else: + pass + + return cfg + +def connection(filepath=None, section='oep', readonly=False): + """ + Instantiate a database connection (for the use with SQLAlchemy). + + The keyword argument `filepath` specifies the location of the config file + that contains database connection information. If not given, the default + of `~/.etrago_database/config.ini` applies. + + Parameters + ---------- + filepath : str + Absolute path of config file including the filename itself + section : str + Section in config file containing database connection parameters. + Default: 'oep'. + readonly : bool + Set this option to True for creating a read-only and passwordless + engine for accessing the open energy platform. + Default: False. + + Returns + ------- + conn : sqlalchemy.engine + SQLalchemy engine object containing the connection details + """ + + if readonly: + conn = create_engine( + "postgresql+oedialect://openenergy-platform.org") + else: + # define default filepath if not provided + if filepath is None: + filepath = os.path.join(os.path.expanduser("~"), '.etrago_database', 'config.ini') + + # does the file exist? + if not os.path.isfile(filepath): + print('DB config file {file} not found. ' + 'This might be the first run of the tool. ' + .format(file=filepath)) + cfg = create_oedb_config_file(filepath, section=section) + else: + cfg = readcfg(filepath, section) + + try: + pw = cfg.get(section, "password") + except: + pw = keyring.get_password(section, + cfg.get(section, "username")) + if pw is None: + pw = getpass.getpass(prompt='No password found for database "{db}". ' + 'Enter your password to ' + 'store it in keyring: ' + .format(db=cfg.get(section, 'database'))) + keyring.set_password(section, cfg.get(section, "username"), pw) + + # establish connection and return it + conn = create_engine( + "postgresql+{dialect}://{user}:{password}@{host}:{port}/{db}".format( + dialect=cfg.get(section, 'dialect', fallback='psycopg2'), + user=cfg.get(section, 'username'), + password=pw, + host=cfg.get(section, 'host'), + port=cfg.get(section, 'port'), + db=cfg.get(section, 'database'))) + + return conn \ No newline at end of file diff --git a/etrago/tools/network.py b/etrago/tools/network.py index fa215352e..07c0b4666 100644 --- a/etrago/tools/network.py +++ b/etrago/tools/network.py @@ -24,7 +24,7 @@ import logging -from egoio.tools import db +from etrago.tools import db from pypsa.components import Network from sqlalchemy.orm import sessionmaker import pandas as pd diff --git a/etrago/tools/utilities.py b/etrago/tools/utilities.py index e1c266e13..bc2be0441 100755 --- a/etrago/tools/utilities.py +++ b/etrago/tools/utilities.py @@ -29,7 +29,7 @@ import math import os -from egoio.tools import db +from etrago.tools import db from pyomo.environ import Constraint, PositiveReals, Var from shapely.geometry import LineString, Point import geopandas as gpd From fc74e3ab2f01888f2803f971a04c82192fa57caf Mon Sep 17 00:00:00 2001 From: ClaraBuettner Date: Thu, 23 Mar 2023 09:47:05 +0100 Subject: [PATCH 032/136] Remove egio from installation --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 4ea9b7ab6..dac4c1eb5 100755 --- a/setup.py +++ b/setup.py @@ -43,9 +43,9 @@ def read(*names, **kwargs): packages=find_packages(), include_package_data=True, install_requires=[ - "egoio == 0.4.7", "geoalchemy2 >= 0.3.0", "geopandas", + "keyring", "matplotlib >= 3.0.3", "oedialect", # PyPSA uses a deprecated import that errors with Pyomo 6.4.3. From 71c9bc27882de810c0e0864b9263aa4241f3f53d Mon Sep 17 00:00:00 2001 From: ClaraBuettner Date: Thu, 23 Mar 2023 09:49:13 +0100 Subject: [PATCH 033/136] Apply black and isort --- etrago/tools/constraints.py | 3 +- etrago/tools/db.py | 152 +++++++++++++++++++++--------------- etrago/tools/network.py | 2 +- etrago/tools/utilities.py | 3 +- 4 files changed, 92 insertions(+), 68 deletions(-) diff --git a/etrago/tools/constraints.py b/etrago/tools/constraints.py index b53ff3b10..b77197e17 100755 --- a/etrago/tools/constraints.py +++ b/etrago/tools/constraints.py @@ -23,7 +23,6 @@ """ import logging -from etrago.tools import db from pyomo.environ import Constraint from pypsa.descriptors import expand_series from pypsa.linopt import define_constraints, define_variables, get_var, linexpr @@ -32,6 +31,8 @@ import pandas as pd import pyomo.environ as po +from etrago.tools import db + logger = logging.getLogger(__name__) __copyright__ = ( diff --git a/etrago/tools/db.py b/etrago/tools/db.py index d5de25ee8..3f9ca2a5a 100644 --- a/etrago/tools/db.py +++ b/etrago/tools/db.py @@ -17,18 +17,20 @@ # You should have received a copy of the GNU Affero General Public License # along with this program. If not, see . -import os import configparser as cp -import keyring import getpass +import os + from sqlalchemy import create_engine +import keyring import oedialect + def readcfg(filepath, section): - """ + """ Reads the configuration file. If section is not available, calls create_oedb_config_file to add the new section to an existing config.ini. - + Parameters ---------- filepath : str @@ -43,51 +45,60 @@ def readcfg(filepath, section): cfg = cp.ConfigParser() cfg.read(filepath) - + if not cfg.has_section(section): - print('The section "{sec}" is not in the config file {file}.' - .format(sec=section, - file=filepath)) - cfg = create_oedb_config_file(filepath, section) + print( + 'The section "{sec}" is not in the config file {file}.'.format( + sec=section, file=filepath + ) + ) + cfg = create_oedb_config_file(filepath, section) return cfg + def get_connection_details(section): """ Asks the user for the database connection details and returns them as a ConfigParser-object. - + Parameters ---------- None - + Returns ------- cfg : configparser.ConfigParser Used for configuration file parser language. """ - print('Please enter your connection details:') - dialect = input('Enter input value for `dialect` (default: psycopg2): ') or 'psycopg2' - username = input('Enter value for `username`: ') - database = input('Enter value for `database`: ') - host = input('Enter value for `host`: ') - port = input('Enter value for `port` (default: 5432): ') or '5432' + print("Please enter your connection details:") + dialect = ( + input("Enter input value for `dialect` (default: psycopg2): ") + or "psycopg2" + ) + username = input("Enter value for `username`: ") + database = input("Enter value for `database`: ") + host = input("Enter value for `host`: ") + port = input("Enter value for `port` (default: 5432): ") or "5432" cfg = cp.ConfigParser() cfg.add_section(section) - cfg.set(section, 'dialect', dialect) - cfg.set(section, 'username', username) - cfg.set(section, 'host', host) - cfg.set(section, 'port', port) - cfg.set(section, 'database', database) - pw = getpass.getpass(prompt="Enter your password/token to " \ - "store it in " - "keyring: ".format(database=section)) + cfg.set(section, "dialect", dialect) + cfg.set(section, "username", username) + cfg.set(section, "host", host) + cfg.set(section, "port", port) + cfg.set(section, "database", database) + pw = getpass.getpass( + prompt="Enter your password/token to " + "store it in " + "keyring: ".format(database=section) + ) keyring.set_password(section, cfg.get(section, "username"), pw) - + return cfg -def create_oedb_config_file(filepath, section='oep'): + +def create_oedb_config_file(filepath, section="oep"): """ Parameters @@ -96,43 +107,46 @@ def create_oedb_config_file(filepath, section='oep'): Absolute path of config file including the filename itself section : str Section in config file which contains connection details - + Returns ------- cfg : configparser.ConfigParser Used for configuration file parser language. """ - + cfg = get_connection_details(section) - print('Do you want to store the connection details in the config file {file} ?' - .format(file=filepath)) - choice = '' - while choice not in ['y', 'n']: - choice = input('(y/n): ') + print( + "Do you want to store the connection details in the config file {file} ?".format( + file=filepath + ) + ) + choice = "" + while choice not in ["y", "n"]: + choice = input("(y/n): ") - if choice == 'y': + if choice == "y": # create egoio dir if not existent base_path = os.path.split(filepath)[0] if not os.path.isdir(base_path): os.mkdir(base_path) - print('The directory {path} was created.'.format(path=base_path)) - - with open(filepath, 'a') as configfile: + print("The directory {path} was created.".format(path=base_path)) + + with open(filepath, "a") as configfile: cfg.write(configfile) pass - - - print('Template {0} with section "{1}" created.\nYou can manually edit' - ' the config file.' - .format(filepath, - section)) + + print( + 'Template {0} with section "{1}" created.\nYou can manually edit' + " the config file.".format(filepath, section) + ) else: pass - + return cfg -def connection(filepath=None, section='oep', readonly=False): + +def connection(filepath=None, section="oep", readonly=False): """ Instantiate a database connection (for the use with SQLAlchemy). @@ -151,7 +165,7 @@ def connection(filepath=None, section='oep', readonly=False): Set this option to True for creating a read-only and passwordless engine for accessing the open energy platform. Default: False. - + Returns ------- conn : sqlalchemy.engine @@ -159,18 +173,22 @@ def connection(filepath=None, section='oep', readonly=False): """ if readonly: - conn = create_engine( - "postgresql+oedialect://openenergy-platform.org") + conn = create_engine("postgresql+oedialect://openenergy-platform.org") else: # define default filepath if not provided if filepath is None: - filepath = os.path.join(os.path.expanduser("~"), '.etrago_database', 'config.ini') + filepath = os.path.join( + os.path.expanduser("~"), ".etrago_database", "config.ini" + ) # does the file exist? if not os.path.isfile(filepath): - print('DB config file {file} not found. ' - 'This might be the first run of the tool. ' - .format(file=filepath)) + print( + "DB config file {file} not found. " + "This might be the first run of the tool. ".format( + file=filepath + ) + ) cfg = create_oedb_config_file(filepath, section=section) else: cfg = readcfg(filepath, section) @@ -178,23 +196,27 @@ def connection(filepath=None, section='oep', readonly=False): try: pw = cfg.get(section, "password") except: - pw = keyring.get_password(section, - cfg.get(section, "username")) + pw = keyring.get_password(section, cfg.get(section, "username")) if pw is None: - pw = getpass.getpass(prompt='No password found for database "{db}". ' - 'Enter your password to ' - 'store it in keyring: ' - .format(db=cfg.get(section, 'database'))) + pw = getpass.getpass( + prompt='No password found for database "{db}". ' + "Enter your password to " + "store it in keyring: ".format( + db=cfg.get(section, "database") + ) + ) keyring.set_password(section, cfg.get(section, "username"), pw) # establish connection and return it conn = create_engine( "postgresql+{dialect}://{user}:{password}@{host}:{port}/{db}".format( - dialect=cfg.get(section, 'dialect', fallback='psycopg2'), - user=cfg.get(section, 'username'), + dialect=cfg.get(section, "dialect", fallback="psycopg2"), + user=cfg.get(section, "username"), password=pw, - host=cfg.get(section, 'host'), - port=cfg.get(section, 'port'), - db=cfg.get(section, 'database'))) + host=cfg.get(section, "host"), + port=cfg.get(section, "port"), + db=cfg.get(section, "database"), + ) + ) - return conn \ No newline at end of file + return conn diff --git a/etrago/tools/network.py b/etrago/tools/network.py index 07c0b4666..38251cf06 100644 --- a/etrago/tools/network.py +++ b/etrago/tools/network.py @@ -24,7 +24,6 @@ import logging -from etrago.tools import db from pypsa.components import Network from sqlalchemy.orm import sessionmaker import pandas as pd @@ -34,6 +33,7 @@ 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 import db from etrago.tools.calc_results import calc_etrago_results from etrago.tools.execute import ( dispatch_disaggregation, diff --git a/etrago/tools/utilities.py b/etrago/tools/utilities.py index bc2be0441..e758d2af4 100755 --- a/etrago/tools/utilities.py +++ b/etrago/tools/utilities.py @@ -29,7 +29,6 @@ import math import os -from etrago.tools import db from pyomo.environ import Constraint, PositiveReals, Var from shapely.geometry import LineString, Point import geopandas as gpd @@ -38,6 +37,8 @@ import pypsa import sqlalchemy.exc +from etrago.tools import db + logger = logging.getLogger(__name__) From f1e1760dd0b1b24af9e5e682b1a69d62d17eb77b Mon Sep 17 00:00:00 2001 From: CarlosEpia Date: Thu, 23 Mar 2023 10:46:11 +0100 Subject: [PATCH 034/136] fix storage_expansion plot --- etrago/tools/plot.py | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/etrago/tools/plot.py b/etrago/tools/plot.py index 551395644..b6c135f92 100644 --- a/etrago/tools/plot.py +++ b/etrago/tools/plot.py @@ -124,6 +124,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", @@ -2528,6 +2529,14 @@ def plot_grid( logger.warning("line_color {} undefined".format(line_colors)) # Set bus colors + if bus_colors in ["nodal_production_balance", "storage_expansion", + "h2_battery_storage_expansion", "storage_distribution", + "gen_dist", "flexibility_usage", "h2_storage_expansion", + "PowerToH2", + ]: + use_legend = True + else: + use_legend = False if bus_colors == "nodal_production_balance": bus_scaling = bus_sizes @@ -2654,11 +2663,12 @@ def plot_grid( ) # legends for bus sizes and colors - if type(bus_sizes) != float: + if use_legend: handles = [] labels = [] - if scaling_store_expansion: + if not isinstance(legend_entries, list): + raise Exception("When using scaling_store_expansion, the argument legend_entries must be a list of carrier of interest") for i in legend_entries: try: max_value = bus_sizes[ @@ -2673,8 +2683,6 @@ def plot_grid( facecolor=network.carriers.color[i], )[0] ) - - labels.append( f"{round(max_value / bus_scaling / scaling_store_expansion[i]/1000, 0).astype(int)} GWh " + i From 3788938aa8e15b40376a9e03377dfe218f4c0b06 Mon Sep 17 00:00:00 2001 From: CarlosEpia Date: Thu, 23 Mar 2023 11:57:44 +0100 Subject: [PATCH 035/136] change title plot line_loading --- etrago/tools/plot.py | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/etrago/tools/plot.py b/etrago/tools/plot.py index b6c135f92..6f6ca7911 100644 --- a/etrago/tools/plot.py +++ b/etrago/tools/plot.py @@ -2424,12 +2424,7 @@ def plot_grid( # Set line colors if line_colors == "line_loading": - title = ( - "Mean loading from " - + str(network.snapshots[timesteps[0]]) - + " to " - + str(network.snapshots[timesteps[-1]]) - ) + title = "Mean line loading" rep_snapshots = network.snapshot_weightings["objective"][ network.snapshots[timesteps] ].sum() From 6fe87839d1ff4c6f11092c1737ae6ef539a561b8 Mon Sep 17 00:00:00 2001 From: CarlosEpia Date: Thu, 23 Mar 2023 11:58:31 +0100 Subject: [PATCH 036/136] fix units plot q_flow_max --- etrago/tools/plot.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/etrago/tools/plot.py b/etrago/tools/plot.py index 6f6ca7911..592e1fe5b 100644 --- a/etrago/tools/plot.py +++ b/etrago/tools/plot.py @@ -2487,7 +2487,7 @@ def plot_grid( link_widths = 0.5 + (link_colors / ext_width) elif line_colors == "q_flow_max": title = "Maximum reactive power flows" - label = "flow in Mvar" + label = "flow in pu" line_colors = abs( network.lines_t.q0.abs().max() / (network.lines.s_nom) ) From 730642353f8b2e26a6a17c8da71e1855e3e49f32 Mon Sep 17 00:00:00 2001 From: CarlosEpia Date: Thu, 23 Mar 2023 15:47:50 +0100 Subject: [PATCH 037/136] add missing carriers to coloring --- etrago/tools/plot.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/etrago/tools/plot.py b/etrago/tools/plot.py index 592e1fe5b..9cf8e3f1f 100644 --- a/etrago/tools/plot.py +++ b/etrago/tools/plot.py @@ -133,6 +133,8 @@ def coloring(): "central_resistive_heater": "blueviolet", "rural_heat_pump": "violet", "CH4": "yellow", + "CH4_biogas": "yellow", + "CH4_NG": "yellow", "CH4_to_H2": "yellowgreen", "industrial_gas_CHP": "olive", "rural_gas_boiler": "sandybrown", @@ -181,6 +183,7 @@ def coloring(): "battery": "blue", "pumped_hydro": "indigo", "BEV charger": "indigo", + "others": "dimgrey" } return colors From 114a92005ce368f301d85c400f4374699270289f Mon Sep 17 00:00:00 2001 From: CarlosEpia Date: Thu, 23 Mar 2023 15:48:43 +0100 Subject: [PATCH 038/136] fix gen_dist plot --- etrago/tools/plot.py | 111 ++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 110 insertions(+), 1 deletion(-) diff --git a/etrago/tools/plot.py b/etrago/tools/plot.py index 9cf8e3f1f..5dd8f3cc3 100644 --- a/etrago/tools/plot.py +++ b/etrago/tools/plot.py @@ -756,7 +756,114 @@ def calc_dispatch_per_carrier(network, timesteps): dispatch per carrier """ + def import_gen_from_links(network): + from pypsa.networkclustering import aggregategenerators + from etrago.cluster.spatial import strategies_generators + """ + create gas generators from links in order to not lose them when + dropping non-electric carriers + """ + # Discard all generators < 1kW + discard_gen = network.links[network.links["p_nom"] <= 0.001].index + network.links.drop(discard_gen, inplace=True) + for df in network.links_t: + if not network.links_t[df].empty: + network.links_t[df].drop( + columns=discard_gen.values, inplace=True, errors="ignore" + ) + + gas_to_add = network.links[ + network.links.carrier.isin( + [ + "central_gas_CHP", + "OCGT", + "H2_to_power", + "industrial_gas_CHP", + ] + ) + ].copy() + + # Drop generators from the links table + network.links.drop(gas_to_add.index, inplace=True) + + gas_to_add.rename(columns={"bus1": "bus"}, inplace=True) + + # Create generators' names like in network.generators + gas_to_add["Generator"] = ( + gas_to_add["bus"] + " " + gas_to_add.index + gas_to_add["carrier"] + ) + gas_to_add_orig = gas_to_add.copy() + gas_to_add.set_index("Generator", drop=True, inplace=True) + gas_to_add = gas_to_add[ + gas_to_add.columns[ + gas_to_add.columns.isin(network.generators.columns) + ] + ] + + network.import_components_from_dataframe(gas_to_add, "Generator") + + # Dealing with generators_t + columns_new = network.links_t.p1.columns[ + network.links_t.p1.columns.isin(gas_to_add_orig.index) + ] + + new_gen_t = network.links_t.p1[columns_new] * -1 + new_gen_t.rename(columns=gas_to_add_orig["Generator"], inplace=True) + network.generators_t.p = network.generators_t.p.join(new_gen_t) + + # Drop generators from the links_t table + for df in network.links_t: + if not network.links_t[df].empty: + network.links_t[df].drop( + columns=gas_to_add_orig.index, + inplace=True, + errors="ignore", + ) + + # Group generators per bus if needed + if not ( + network.generators.groupby(["bus", "carrier"]).p_nom.count() == 1 + ).all(): + network.generators["weight"] = network.generators.p_nom + df, df_t = aggregategenerators( + network, + busmap=pd.Series( + index=network.buses.index, data=network.buses.index + ), + custom_strategies=strategies_generators(), + ) + # Keep control arguments from generators + control = network.generators.groupby( + ["bus", "carrier"] + ).control.first() + control.index = ( + control.index.get_level_values(0) + + " " + + control.index.get_level_values(1) + ) + df.control = control + + # Drop non-aggregated generators + network.mremove("Generator", network.generators.index) + + # Insert aggregated generators and time series + network.import_components_from_dataframe(df, "Generator") + + for attr, data in df_t.items(): + if not data.empty: + network.import_series_from_dataframe( + data, "Generator", attr + ) + + return + + import_gen_from_links(network) + + ac_buses = network.buses[network.buses.carrier == "AC"].index + network.generators = network.generators[network.generators.bus.isin(ac_buses)] + network.generators_t.p = network.generators_t.p.loc[:,network.generators_t.p.columns.isin(network.generators.index)] + index = [ (network.generators.bus[idx], network.generators.carrier[idx]) for idx in network.generators.index @@ -2424,6 +2531,8 @@ def plot_grid( else: fig, ax = plt.subplots(figsize=(5, 5)) + + fig.set_tight_layout(True) # Set line colors if line_colors == "line_loading": @@ -2626,7 +2735,7 @@ def plot_grid( bus_scaling = bus_sizes else: logger.warning("bus_color {} undefined".format(bus_colors)) - + if cartopy_present: ll = network.plot( line_colors=line_colors, From 222e2e3bebaafc76671260224ca59db678cf2053 Mon Sep 17 00:00:00 2001 From: CarlosEpia Date: Fri, 24 Mar 2023 09:34:02 +0100 Subject: [PATCH 039/136] dont delete no DC links when calc network expansion --- etrago/tools/plot.py | 62 +++++++++++++++++++++++--------------------- 1 file changed, 33 insertions(+), 29 deletions(-) diff --git a/etrago/tools/plot.py b/etrago/tools/plot.py index 5dd8f3cc3..3ed84f1fa 100644 --- a/etrago/tools/plot.py +++ b/etrago/tools/plot.py @@ -1794,7 +1794,7 @@ def calc_network_expansion(network, method="abs", ext_min=0.1): Returns ------- - all_network : :class:`pypsa.Network + network_c : :class:`pypsa.Network Whole network including not extended lines extension_lines : pandas.Series AC-line expansion @@ -1803,64 +1803,66 @@ def calc_network_expansion(network, method="abs", ext_min=0.1): """ - all_network = network.copy() + network_c = network.copy() - network.lines = network.lines[ - network.lines.s_nom_extendable + network_c.lines = network_c.lines[ + network_c.lines.s_nom_extendable & ( - (network.lines.s_nom_opt - network.lines.s_nom_min) - / network.lines.s_nom + (network_c.lines.s_nom_opt - network_c.lines.s_nom_min) + / network_c.lines.s_nom >= ext_min ) ] - network.links = network.links[ - network.links.p_nom_extendable - & (network.links.carrier == "DC") + network_c.links = network_c.links[ + network_c.links.p_nom_extendable + & (network_c.links.carrier == "DC") & ( - (network.links.p_nom_opt - network.links.p_nom_min) - / network.links.p_nom + (network_c.links.p_nom_opt - network_c.links.p_nom_min) + / network_c.links.p_nom >= ext_min ) ] - for i, row in network.links.iterrows(): - linked = network.links[ - (row["bus1"] == network.links.bus0) - & (row["bus0"] == network.links.bus1) + for i, row in network_c.links.iterrows(): + linked = network_c.links[ + (row["bus1"] == network_c.links.bus0) + & (row["bus0"] == network_c.links.bus1) ] if not linked.empty: if row["p_nom_opt"] < linked.p_nom_opt.values[0]: - network.links.p_nom_opt[i] = linked.p_nom_opt.values[0] + network_c.links.p_nom_opt[i] = linked.p_nom_opt.values[0] if method == "rel": extension_lines = ( 100 - * (network.lines.s_nom_opt - network.lines.s_nom_min) - / network.lines.s_nom + * (network_c.lines.s_nom_opt - network_c.lines.s_nom_min) + / network_c.lines.s_nom ) extension_links = pd.DataFrame( - data=network.links, index=network.links.index + data=network_c.links, index=network_c.links.index ) extension_links[extension_links.carrier != "DC"] = 0 extension_links = ( 100 - * (network.links.p_nom_opt - network.links.p_nom_min) - / (network.links.p_nom) + * (network_c.links.p_nom_opt - network_c.links.p_nom_min) + / (network_c.links.p_nom) ) extension_links = extension_links.fillna(0) if method == "abs": - extension_lines = network.lines.s_nom_opt - network.lines.s_nom_min + extension_lines = network_c.lines.s_nom_opt - network_c.lines.s_nom_min extension_links = pd.DataFrame( - data=network.links, index=network.links.index + data=network_c.links, index=network_c.links.index ) extension_links[extension_links.carrier != "DC"] = 0 - extension_links = network.links.p_nom_opt - network.links.p_nom_min + extension_links = network_c.links.p_nom_opt - network_c.links.p_nom_min - return all_network, extension_lines, extension_links + extension_lines = pd.Series(data=extension_lines, index= network.lines.index).fillna(0) + extension_links = pd.Series(data=extension_links, index= network.links.index).fillna(0) + return network, extension_lines, extension_links def plot_background_grid(network, ax): @@ -2582,9 +2584,12 @@ def plot_grid( plot_background_grid(all_network, ax) if ext_width != False: line_widths = 0.5 + (line_colors / ext_width) - link_widths = 0.5 + (link_colors / ext_width) + link_widths = 0.5 + (line_colors / ext_width) else: - link_widths = 2.0 + dc_link = network.links.index[network.links.carrier == "DC"] + link_widths = pd.Series(0, index=network.links.index) + link_widths.loc[dc_link] = 2 + link_colors = link_colors.mul(1e-3) line_colors = line_colors.mul(1e-3) elif line_colors == "expansion_rel": @@ -2725,8 +2730,6 @@ def plot_grid( bus_unit = "TW" elif bus_colors == "grey": bus_scaling = bus_sizes - # bus_unit = "" - # bus_legend = "" bus_sizes = pd.Series( data=network.buses.carrier, index=network.buses.index ) @@ -2829,6 +2832,7 @@ def plot_grid( positive = mpatches.Patch(color="green", label="generation") negative = mpatches.Patch(color="red", label="consumption") handles = [positive, negative] + elif legend_entries != "all": for i in legend_entries: patch = mpatches.Patch(color=network.carriers.color[i], label=i) From 0d97fe4ce8e98d2e06da0b79e7777ddb1e2f4aa1 Mon Sep 17 00:00:00 2001 From: CarlosEpia Date: Fri, 24 Mar 2023 09:52:13 +0100 Subject: [PATCH 040/136] update function calc_storage_expansion_per_bus --- etrago/tools/plot.py | 157 ++++++++++++++++++++++--------------------- 1 file changed, 81 insertions(+), 76 deletions(-) diff --git a/etrago/tools/plot.py b/etrago/tools/plot.py index 3ed84f1fa..6af07299f 100644 --- a/etrago/tools/plot.py +++ b/etrago/tools/plot.py @@ -892,74 +892,30 @@ def import_gen_from_links(network): return dist -def calc_storage_expansion_per_bus(network): +def calc_storage_expansion_per_bus( + network, + carriers=[ + "battery", + "H2_overground", + "H2_underground", + "rural_heat_store", + "central_heat_store", + ], +): """Function that calculates storage expansion per bus and technology - Parameters ---------- network : PyPSA network container Holds topology of grid including results from powerflow analysis - Returns ------- dist : pandas.Series storage expansion per bus and technology - """ - - batteries = network.storage_units[ - network.storage_units.carrier == "battery" - ] - h2_overground = network.stores[network.stores.carrier == "H2_overground"] - h2_underground = network.stores[network.stores.carrier == "H2_underground"] - rural_heat = network.stores[network.stores.carrier == "rural_heat_store"] - central_heat = network.stores[ - network.stores.carrier == "central_heat_store" - ] - # hydrogen = network.storage_units[network.storage_units.carrier == - # 'extendable_hydrogen_storage'] - battery_distribution = ( - network.storage_units.p_nom_opt[batteries.index] - .groupby(network.storage_units.bus) - .sum() - .reindex(network.buses.index, fill_value=0.0) - ) - h2_over_distribution = ( - network.stores.e_nom_opt[h2_overground.index] - .groupby(network.stores.bus) - .sum() - .reindex(network.buses.index, fill_value=0.0) - ) - h2_under_distribution = ( - network.stores.e_nom_opt[h2_underground.index] - .groupby(network.stores.bus) - .sum() - .reindex(network.buses.index, fill_value=0.0) - ) - rural_heat_distribution = ( - network.stores.e_nom_opt[rural_heat.index] - .groupby(network.stores.bus) - .sum() - .reindex(network.buses.index, fill_value=0.0) - ) - central_heat_distribution = ( - network.stores.e_nom_opt[central_heat.index] - .groupby(network.stores.bus) - .sum() - .reindex(network.buses.index, fill_value=0.0) - ) - # hydrogen_distribution =\ - # network.storage_units.p_nom_opt[hydrogen.index].groupby( - # network.storage_units.bus).sum().reindex( - # network.buses.index, fill_value=0.) index = [(idx, "battery") for idx in network.buses.index] - for c in [ - "H2_overground", - "H2_underground", - "rural_heat_store", - "central_heat_store", - ]: - index.extend([(idx, c) for idx in network.buses.index]) + for c in carriers: + if c != "battery": + index.extend([(idx, c) for idx in network.buses.index]) # index.extend([(idx, 'hydrogen_storage') for idx in network.buses.index]) dist = pd.Series( @@ -967,25 +923,74 @@ def calc_storage_expansion_per_bus(network): dtype=float, ) - dist.iloc[ - dist.index.get_level_values("carrier") == "battery" - ] = battery_distribution.sort_index().values - dist.iloc[ - dist.index.get_level_values("carrier") == "H2_overground" - ] = h2_over_distribution.sort_index().values - dist.iloc[ - dist.index.get_level_values("carrier") == "H2_underground" - ] = h2_under_distribution.sort_index().values - dist.iloc[ - dist.index.get_level_values("carrier") == "rural_heat_store" - ] = rural_heat_distribution.sort_index().values - dist.iloc[ - dist.index.get_level_values("carrier") == "central_heat_store" - ] = central_heat_distribution.sort_index().values - # dist.iloc[dist.index.get_level_values('carrier') == 'hydrogen_storage'] = \ - # hydrogen_distribution.sort_index().values - # network.carriers.color['hydrogen_storage'] = 'orange' - # network.carriers.color['battery_storage'] = 'blue' + if "battery" in carriers: + batteries = network.storage_units[ + network.storage_units.carrier == "battery" + ] + battery_distribution = ( + network.storage_units.p_nom_opt[batteries.index] + .groupby(network.storage_units.bus) + .sum() + .reindex(network.buses.index, fill_value=0.0) + ).mul(6) + dist.iloc[ + dist.index.get_level_values("carrier") == "battery" + ] = battery_distribution.sort_index().values + if "H2_overground" in carriers: + h2_overground = network.stores[ + network.stores.carrier == "H2_overground" + ] + h2_over_distribution = ( + network.stores.e_nom_opt[h2_overground.index] + .groupby(network.stores.bus) + .sum() + .reindex(network.buses.index, fill_value=0.0) + ) + dist.iloc[ + dist.index.get_level_values("carrier") == "H2_overground" + ] = h2_over_distribution.sort_index().values + + if "H2_overground" in carriers: + h2_underground = network.stores[ + network.stores.carrier == "H2_underground" + ] + h2_under_distribution = ( + network.stores.e_nom_opt[h2_underground.index] + .groupby(network.stores.bus) + .sum() + .reindex(network.buses.index, fill_value=0.0) + ) + dist.iloc[ + dist.index.get_level_values("carrier") == "H2_underground" + ] = h2_under_distribution.sort_index().values + + if "rural_heat_store" in carriers: + rural_heat = network.stores[ + network.stores.carrier == "rural_heat_store" + ] + rural_heat_distribution = ( + network.stores.e_nom_opt[rural_heat.index] + .groupby(network.stores.bus) + .sum() + .reindex(network.buses.index, fill_value=0.0) + ) + + dist.iloc[ + dist.index.get_level_values("carrier") == "rural_heat_store" + ] = rural_heat_distribution.sort_index().values + if "central_heat_store" in carriers: + central_heat = network.stores[ + network.stores.carrier == "central_heat_store" + ] + central_heat_distribution = ( + network.stores.e_nom_opt[central_heat.index] + .groupby(network.stores.bus) + .sum() + .reindex(network.buses.index, fill_value=0.0) + ) + dist.iloc[ + dist.index.get_level_values("carrier") == "central_heat_store" + ] = central_heat_distribution.sort_index().values return dist From 449abca8edf5893ccd3cd0743b9006842a0143cd Mon Sep 17 00:00:00 2001 From: CarlosEpia Date: Fri, 24 Mar 2023 09:53:35 +0100 Subject: [PATCH 041/136] raise exception in storage expansion plot --- etrago/tools/plot.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/etrago/tools/plot.py b/etrago/tools/plot.py index 6af07299f..81ec4c2b3 100644 --- a/etrago/tools/plot.py +++ b/etrago/tools/plot.py @@ -2663,6 +2663,11 @@ def plot_grid( bus_legend = "Nodal production balance" bus_unit = "TWh" elif bus_colors == "storage_expansion": + if not isinstance(scaling_store_expansion, dict): + raise Exception("""To plot storage_expansion, the argument scaling_store_expansion must be a dictionary like: + {"H2": 50, + "heat": 0.1, + "battery": 10}""") bus_scaling = bus_sizes bus_sizes = bus_scaling * calc_storage_expansion_per_bus(network) for store_carrier in ["H2", "heat", "battery"]: From 36f2735d88f27751d5255e0b5258d6daf0ebec24 Mon Sep 17 00:00:00 2001 From: ClaraBuettner Date: Mon, 27 Mar 2023 12:02:00 +0200 Subject: [PATCH 042/136] Reformat etrago/tools/calc_results --- etrago/tools/calc_results.py | 8 ++------ noxfile.py | 1 + 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/etrago/tools/calc_results.py b/etrago/tools/calc_results.py index 2ef643c7a..5c3f3f4c8 100755 --- a/etrago/tools/calc_results.py +++ b/etrago/tools/calc_results.py @@ -25,9 +25,7 @@ if "READTHEDOCS" not in os.environ: import logging - import time - import numpy as np import pandas as pd logger = logging.getLogger(__name__) @@ -386,7 +384,7 @@ def system_costs_germany(self): .sum() .sum() ) - if not c.name in [ + if c.name not in [ "Bus", "Load", "LineType", @@ -629,6 +627,7 @@ def dc_export_per_country(self): return result + def calc_etrago_results(self): """Function that calculates main results of grid optimization and adds them to Etrago object. @@ -716,7 +715,6 @@ def calc_etrago_results(self): 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() @@ -741,7 +739,6 @@ def calc_etrago_results(self): # 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] @@ -753,7 +750,6 @@ def calc_etrago_results(self): # 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() diff --git a/noxfile.py b/noxfile.py index 98f0c9eb6..69cf698e1 100644 --- a/noxfile.py +++ b/noxfile.py @@ -4,6 +4,7 @@ cleaned = [ "etrago/cluster/disaggregation.py", + "etrago/tools/calc_results.py", "etrago/tools/network.py", "etrago/tools/utilities.py", "noxfile.py", From 0d7334ab43f37ffbc97b999e3f9e2860b3f413d3 Mon Sep 17 00:00:00 2001 From: CarlosEpia Date: Mon, 27 Mar 2023 12:07:22 +0200 Subject: [PATCH 043/136] fix plot h2_battery_storage_expansion --- etrago/tools/plot.py | 66 ++++++++++++++++++++++++++++++-------------- 1 file changed, 45 insertions(+), 21 deletions(-) diff --git a/etrago/tools/plot.py b/etrago/tools/plot.py index 81ec4c2b3..7300b97c3 100644 --- a/etrago/tools/plot.py +++ b/etrago/tools/plot.py @@ -933,9 +933,12 @@ def calc_storage_expansion_per_bus( .sum() .reindex(network.buses.index, fill_value=0.0) ).mul(6) - dist.iloc[ + + battery_distribution.index = pd.MultiIndex.from_tuples([(idx, "battery") for idx in battery_distribution.index]) + + dist.loc[ dist.index.get_level_values("carrier") == "battery" - ] = battery_distribution.sort_index().values + ] = battery_distribution if "H2_overground" in carriers: h2_overground = network.stores[ network.stores.carrier == "H2_overground" @@ -946,9 +949,12 @@ def calc_storage_expansion_per_bus( .sum() .reindex(network.buses.index, fill_value=0.0) ) - dist.iloc[ + + h2_over_distribution.index = pd.MultiIndex.from_tuples([(idx, "H2_overground") for idx in h2_over_distribution.index]) + + dist.loc[ dist.index.get_level_values("carrier") == "H2_overground" - ] = h2_over_distribution.sort_index().values + ] = h2_over_distribution if "H2_overground" in carriers: h2_underground = network.stores[ @@ -960,9 +966,12 @@ def calc_storage_expansion_per_bus( .sum() .reindex(network.buses.index, fill_value=0.0) ) - dist.iloc[ + + h2_under_distribution.index = pd.MultiIndex.from_tuples([(idx, "H2_underground") for idx in h2_under_distribution.index]) + + dist.loc[ dist.index.get_level_values("carrier") == "H2_underground" - ] = h2_under_distribution.sort_index().values + ] = h2_under_distribution if "rural_heat_store" in carriers: rural_heat = network.stores[ @@ -974,10 +983,12 @@ def calc_storage_expansion_per_bus( .sum() .reindex(network.buses.index, fill_value=0.0) ) + + rural_heat_distribution.index = pd.MultiIndex.from_tuples([(idx, "rural_heat_store") for idx in rural_heat_distribution.index]) - dist.iloc[ + dist.loc[ dist.index.get_level_values("carrier") == "rural_heat_store" - ] = rural_heat_distribution.sort_index().values + ] = rural_heat_distribution if "central_heat_store" in carriers: central_heat = network.stores[ network.stores.carrier == "central_heat_store" @@ -988,9 +999,12 @@ def calc_storage_expansion_per_bus( .sum() .reindex(network.buses.index, fill_value=0.0) ) - dist.iloc[ + + central_heat_distribution.index = pd.MultiIndex.from_tuples([(idx, "central_heat_store") for idx in central_heat_distribution.index]) + + dist.loc[ dist.index.get_level_values("carrier") == "central_heat_store" - ] = central_heat_distribution.sort_index().values + ] = central_heat_distribution return dist @@ -2646,14 +2660,7 @@ def plot_grid( logger.warning("line_color {} undefined".format(line_colors)) # Set bus colors - if bus_colors in ["nodal_production_balance", "storage_expansion", - "h2_battery_storage_expansion", "storage_distribution", - "gen_dist", "flexibility_usage", "h2_storage_expansion", - "PowerToH2", - ]: - use_legend = True - else: - use_legend = False + bus_legend = False if bus_colors == "nodal_production_balance": bus_scaling = bus_sizes @@ -2677,7 +2684,21 @@ def plot_grid( elif bus_colors == "h2_battery_storage_expansion": bus_scaling = bus_sizes bus_sizes = bus_scaling * calc_storage_expansion_per_bus(network, carriers=["battery", "H2_overground", "H2_underground"]) - bus_legend = "Storage expansion" + if (("battery" not in scaling_store_expansion.keys()) | + ("H2_overground" not in scaling_store_expansion.keys()) | + ("H2_underground" not in scaling_store_expansion.keys())): + raise Exception("""To plot h2_battery_storage_expansion, the argument scaling_store_expansion must be a dictionary like: + {"H2_overground": 1, + "H2_underground": 1, + "battery": 1,}""") + + for store_carrier in ["battery", "H2_overground", "H2_underground"]: + bus_sizes[ + bus_sizes.index.get_level_values("carrier").str.contains( + store_carrier + ) + ] *= scaling_store_expansion[store_carrier] + bus_legend = "Battery and H2 storage expansion" bus_unit = "GW" elif bus_colors == "storage_distribution": bus_scaling = bus_sizes @@ -2783,12 +2804,15 @@ def plot_grid( ) # legends for bus sizes and colors - if use_legend: + if bus_legend: handles = [] labels = [] if scaling_store_expansion: if not isinstance(legend_entries, list): - raise Exception("When using scaling_store_expansion, the argument legend_entries must be a list of carrier of interest") + if bus_legend == "Storage expansion": + legend_entries = list(scaling_store_expansion.keys()) + if bus_legend == "Battery and H2 storage expansion": + legend_entries = ["battery", "H2_overground", "H2_underground"] for i in legend_entries: try: max_value = bus_sizes[ From 31d54768f8e8307036c9710d9680d720787408eb Mon Sep 17 00:00:00 2001 From: CarlosEpia Date: Mon, 27 Mar 2023 13:32:04 +0200 Subject: [PATCH 044/136] show just needed lables --- etrago/tools/plot.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/etrago/tools/plot.py b/etrago/tools/plot.py index 7300b97c3..56d5dee1c 100644 --- a/etrago/tools/plot.py +++ b/etrago/tools/plot.py @@ -2872,7 +2872,7 @@ def plot_grid( patch = mpatches.Patch(color=network.carriers.color[i], label=i) handles.append(patch) else: - for i in network.carriers.color.index: + for i in bus_sizes.index.get_level_values("carrier").unique(): patch = mpatches.Patch(color=network.carriers.color[i], label=i) handles.append(patch) From 00eb485a30ede8dab4a38b929725839c7cbac29b Mon Sep 17 00:00:00 2001 From: CarlosEpia Date: Mon, 27 Mar 2023 13:49:33 +0200 Subject: [PATCH 045/136] comment title of plot and use Black --- etrago/tools/plot.py | 373 +++++++++++++++++++++++++------------------ 1 file changed, 218 insertions(+), 155 deletions(-) diff --git a/etrago/tools/plot.py b/etrago/tools/plot.py index 56d5dee1c..75d951a34 100644 --- a/etrago/tools/plot.py +++ b/etrago/tools/plot.py @@ -117,7 +117,6 @@ def plot_osm(x, y, zoom, alpha=0.4): def coloring(): - colors = { "load": "red", "DC": "blue", @@ -183,7 +182,7 @@ def coloring(): "battery": "blue", "pumped_hydro": "indigo", "BEV charger": "indigo", - "others": "dimgrey" + "others": "dimgrey", } return colors @@ -222,6 +221,7 @@ def plot_line_loading_diff(networkA, networkB, timestep=0, osm=False): set_epsg_network(networkA) set_epsg_network(networkB) plot_osm(osm["x"], osm["y"], osm["zoom"]) + # new colormap to make sure 0% difference has the same color in every plot def shiftedColorMap( cmap, start=0, midpoint=0.5, stop=1.0, name="shiftedcmap" @@ -756,9 +756,11 @@ def calc_dispatch_per_carrier(network, timesteps): dispatch per carrier """ + def import_gen_from_links(network): from pypsa.networkclustering import aggregategenerators from etrago.cluster.spatial import strategies_generators + """ create gas generators from links in order to not lose them when dropping non-electric carriers @@ -857,13 +859,17 @@ def import_gen_from_links(network): ) return - + import_gen_from_links(network) ac_buses = network.buses[network.buses.carrier == "AC"].index - network.generators = network.generators[network.generators.bus.isin(ac_buses)] - network.generators_t.p = network.generators_t.p.loc[:,network.generators_t.p.columns.isin(network.generators.index)] - + network.generators = network.generators[ + network.generators.bus.isin(ac_buses) + ] + network.generators_t.p = network.generators_t.p.loc[ + :, network.generators_t.p.columns.isin(network.generators.index) + ] + index = [ (network.generators.bus[idx], network.generators.carrier[idx]) for idx in network.generators.index @@ -933,9 +939,11 @@ def calc_storage_expansion_per_bus( .sum() .reindex(network.buses.index, fill_value=0.0) ).mul(6) - - battery_distribution.index = pd.MultiIndex.from_tuples([(idx, "battery") for idx in battery_distribution.index]) - + + battery_distribution.index = pd.MultiIndex.from_tuples( + [(idx, "battery") for idx in battery_distribution.index] + ) + dist.loc[ dist.index.get_level_values("carrier") == "battery" ] = battery_distribution @@ -949,9 +957,11 @@ def calc_storage_expansion_per_bus( .sum() .reindex(network.buses.index, fill_value=0.0) ) - - h2_over_distribution.index = pd.MultiIndex.from_tuples([(idx, "H2_overground") for idx in h2_over_distribution.index]) - + + h2_over_distribution.index = pd.MultiIndex.from_tuples( + [(idx, "H2_overground") for idx in h2_over_distribution.index] + ) + dist.loc[ dist.index.get_level_values("carrier") == "H2_overground" ] = h2_over_distribution @@ -966,9 +976,11 @@ def calc_storage_expansion_per_bus( .sum() .reindex(network.buses.index, fill_value=0.0) ) - - h2_under_distribution.index = pd.MultiIndex.from_tuples([(idx, "H2_underground") for idx in h2_under_distribution.index]) - + + h2_under_distribution.index = pd.MultiIndex.from_tuples( + [(idx, "H2_underground") for idx in h2_under_distribution.index] + ) + dist.loc[ dist.index.get_level_values("carrier") == "H2_underground" ] = h2_under_distribution @@ -983,8 +995,13 @@ def calc_storage_expansion_per_bus( .sum() .reindex(network.buses.index, fill_value=0.0) ) - - rural_heat_distribution.index = pd.MultiIndex.from_tuples([(idx, "rural_heat_store") for idx in rural_heat_distribution.index]) + + rural_heat_distribution.index = pd.MultiIndex.from_tuples( + [ + (idx, "rural_heat_store") + for idx in rural_heat_distribution.index + ] + ) dist.loc[ dist.index.get_level_values("carrier") == "rural_heat_store" @@ -999,9 +1016,14 @@ def calc_storage_expansion_per_bus( .sum() .reindex(network.buses.index, fill_value=0.0) ) - - central_heat_distribution.index = pd.MultiIndex.from_tuples([(idx, "central_heat_store") for idx in central_heat_distribution.index]) - + + central_heat_distribution.index = pd.MultiIndex.from_tuples( + [ + (idx, "central_heat_store") + for idx in central_heat_distribution.index + ] + ) + dist.loc[ dist.index.get_level_values("carrier") == "central_heat_store" ] = central_heat_distribution @@ -1283,9 +1305,11 @@ def nodal_production_balance(network, timesteps, scaling=0.00001): 'green' for producer and 'red' for consumer """ + def import_gen_from_links(network): from pypsa.networkclustering import aggregategenerators from etrago.cluster.spatial import strategies_generators + """ create gas generators from links in order to not lose them when dropping non-electric carriers @@ -1384,13 +1408,17 @@ def import_gen_from_links(network): ) return - + import_gen_from_links(network) - + ac_buses = network.buses[network.buses.carrier == "AC"].index - network.generators = network.generators[network.generators.bus.isin(ac_buses)] - network.generators_t.p = network.generators_t.p.loc[:,network.generators_t.p.columns.isin(network.generators.index)] - + network.generators = network.generators[ + network.generators.bus.isin(ac_buses) + ] + network.generators_t.p = network.generators_t.p.loc[ + :, network.generators_t.p.columns.isin(network.generators.index) + ] + gen = ( mul_weighting(network, network.generators_t.p) .groupby(network.generators.bus, axis=1) @@ -1414,9 +1442,11 @@ def import_gen_from_links(network): ) bus_sizes = residual_load.abs() * scaling - bus_sizes = pd.Series(data= bus_sizes, index = network.buses.index).fillna(0) - - bus_colors = pd.Series(data= bus_colors, index = network.buses.index).fillna("grey") + bus_sizes = pd.Series(data=bus_sizes, index=network.buses.index).fillna(0) + + bus_colors = pd.Series(data=bus_colors, index=network.buses.index).fillna( + "grey" + ) return bus_sizes, bus_colors @@ -1466,7 +1496,6 @@ def storage_p_soc(network, mean="1H", filename=None): network.storage_units.p_nom_opt[sbatt].sum() > 1 and network.storage_units.p_nom_opt[shydr].sum() < 1 ): - ( network.storage_units_t.p[sbatt].resample(mean).mean().sum(axis=1) / network.storage_units.p_nom_opt[sbatt].sum() @@ -1666,7 +1695,6 @@ def mul_weighting(network, timeseries): return timeseries.mul(network.snapshot_weightings.generators, axis=0) - def calc_ac_loading(network, timesteps): """Calculates loading of AC-lines @@ -1691,7 +1719,6 @@ def calc_ac_loading(network, timesteps): ) if not network.lines_t.q0.empty: - loading_lines = ( loading_lines**2 + mul_weighting(network, network.lines_t.q0) @@ -1732,7 +1759,6 @@ def calc_dc_loading(network, timesteps): & (network.links.length == row["length"]) ] ).empty: - l = network.links.index[ (network.links.bus0 == row["bus1"]) & (network.links.bus1 == row["bus0"]) @@ -1852,7 +1878,6 @@ def calc_network_expansion(network, method="abs", ext_min=0.1): network_c.links.p_nom_opt[i] = linked.p_nom_opt.values[0] if method == "rel": - extension_lines = ( 100 * (network_c.lines.s_nom_opt - network_c.lines.s_nom_min) @@ -1879,8 +1904,12 @@ def calc_network_expansion(network, method="abs", ext_min=0.1): extension_links[extension_links.carrier != "DC"] = 0 extension_links = network_c.links.p_nom_opt - network_c.links.p_nom_min - extension_lines = pd.Series(data=extension_lines, index= network.lines.index).fillna(0) - extension_links = pd.Series(data=extension_links, index= network.links.index).fillna(0) + extension_lines = pd.Series( + data=extension_lines, index=network.lines.index + ).fillna(0) + extension_links = pd.Series( + data=extension_links, index=network.links.index + ).fillna(0) return network, extension_lines, extension_links @@ -2310,7 +2339,6 @@ def flexibility_usage( ) elif flexibility == "battery": - df = pd.DataFrame(index=self.network.snapshots[snapshots]) su = self.network.storage_units[ @@ -2397,13 +2425,11 @@ def plot_carrier(network, carrier_links=["AC"], carrier_buses=["AC"]): link_width = pd.Series(index=network.links.index, data=2) if len(carrier_links) > 0: - link_width.loc[~network.links.carrier.isin(carrier_links)] = 0 bus_sizes = pd.Series(index=network.buses.index, data=0.0005) if len(carrier_buses) > 0: - bus_sizes.loc[~network.buses.carrier.isin(carrier_buses)] = 0 link_colors = network.links.carrier.map(colors) @@ -2457,72 +2483,72 @@ def plot_grid( ext_min=0.1, ext_width=False, legend_entries="all", - scaling_store_expansion = { - "H2": 50, - "heat": 0.1, - "battery": 10, - } + scaling_store_expansion={ + "H2": 50, + "heat": 0.1, + "battery": 10, + }, ): """Function that plots etrago.network and results for lines and buses - Parameters - ---------- - line_colors : str - Set static line color or attribute to plot e.g. 'expansion_abs' - Current options: - 'line_loading': mean line loading in p.u. in selected timesteps - 'v_nom': nominal voltage of linesflow = pd.Series(index=network.branches().index, dtype="float64") -flow.iloc[flow.index.get_level_values("component") == "Line"] = ( - mul_weighting(network, network.lines_t.p0) - .loc[network.snapshots[timesteps]] - .sum() - / network.lines.s_nom - / rep_snapshots -).values -flow.iloc[flow.index.get_level_values("component") == "Link"] = ( - calc_dc_loading(network, timesteps) / rep_snapshots -).values -flow = flow[(flow.index.get_level_values("component")=="Line")| - (flow.index.isin(link_widths[link_colors.index.isin( - network.links[network.links.carrier=="DC"].index) - ].index, level=1))] -flow = flow.apply(lambda x: x+5 if x > 0 else x-5) - 'expansion_abs': absolute network expansion in MVA - 'expansion_rel': network expansion in p.u. of existing capacity - 'q_flow_max': maximal reactive flows - bus_sizes : float, optional - Size of buses. The default is 0.001. - bus_colors : str, optional - Set static bus color or attribute to plot. The default is 'grey'. - Current options: - 'nodal_production_balance': net producer/consumer in selected timeteps - 'storage_expansion': storage expansion per bus and technology - 'storage_distribution': installed storage units per bus - 'gen_dist': dispatch per carrier in selected timesteps - timesteps : array, optional - Timesteps consideredd in time depended plots. The default is range(2). - osm : bool or dict, e.g. {'x': [1,20], 'y': [47, 56], 'zoom' : 6} - If not False, osm is set as background - with the following settings as dict: - 'x': array of two floats, x axis boundaries (lat) - 'y': array of two floats, y axis boundaries (long) - 'zoom' : resolution of osm. The default is False. - boundaries: array - Set fixed boundaries of heatmap axis. The default is None. - filename: str or None - Save figure in this direction. The default is None. - disaggregated : bool, optional - Choose if disaggregated network is shown. The default is False. - ext_min: float - Choose minimum relative line extension shown in plot in p.u.. - ext_width: float or bool - Choose if line_width respects line extension. Turn off with 'False' or - set linear factor to decremise extension line_width. - The default is False. - - Returns - ------- - None. + Parameters + ---------- + line_colors : str + Set static line color or attribute to plot e.g. 'expansion_abs' + Current options: + 'line_loading': mean line loading in p.u. in selected timesteps + 'v_nom': nominal voltage of linesflow = pd.Series(index=network.branches().index, dtype="float64") + flow.iloc[flow.index.get_level_values("component") == "Line"] = ( + mul_weighting(network, network.lines_t.p0) + .loc[network.snapshots[timesteps]] + .sum() + / network.lines.s_nom + / rep_snapshots + ).values + flow.iloc[flow.index.get_level_values("component") == "Link"] = ( + calc_dc_loading(network, timesteps) / rep_snapshots + ).values + flow = flow[(flow.index.get_level_values("component")=="Line")| + (flow.index.isin(link_widths[link_colors.index.isin( + network.links[network.links.carrier=="DC"].index) + ].index, level=1))] + flow = flow.apply(lambda x: x+5 if x > 0 else x-5) + 'expansion_abs': absolute network expansion in MVA + 'expansion_rel': network expansion in p.u. of existing capacity + 'q_flow_max': maximal reactive flows + bus_sizes : float, optional + Size of buses. The default is 0.001. + bus_colors : str, optional + Set static bus color or attribute to plot. The default is 'grey'. + Current options: + 'nodal_production_balance': net producer/consumer in selected timeteps + 'storage_expansion': storage expansion per bus and technology + 'storage_distribution': installed storage units per bus + 'gen_dist': dispatch per carrier in selected timesteps + timesteps : array, optional + Timesteps consideredd in time depended plots. The default is range(2). + osm : bool or dict, e.g. {'x': [1,20], 'y': [47, 56], 'zoom' : 6} + If not False, osm is set as background + with the following settings as dict: + 'x': array of two floats, x axis boundaries (lat) + 'y': array of two floats, y axis boundaries (long) + 'zoom' : resolution of osm. The default is False. + boundaries: array + Set fixed boundaries of heatmap axis. The default is None. + filename: str or None + Save figure in this direction. The default is None. + disaggregated : bool, optional + Choose if disaggregated network is shown. The default is False. + ext_min: float + Choose minimum relative line extension shown in plot in p.u.. + ext_width: float or bool + Choose if line_width respects line extension. Turn off with 'False' or + set linear factor to decremise extension line_width. + The default is False. + + Returns + ------- + None. """ # Choose network or disaggregated_network @@ -2552,7 +2578,7 @@ def plot_grid( else: fig, ax = plt.subplots(figsize=(5, 5)) - + fig.set_tight_layout(True) # Set line colors @@ -2568,7 +2594,7 @@ def plot_grid( line_widths = 10 label = "line loading in p.u." plot_background_grid(network, ax) - #Only active flow direction is displayed! + # Only active flow direction is displayed! flow = pd.Series(1, index=network.branches().index, dtype="float64") flow.iloc[flow.index.get_level_values("component") == "Line"] = ( mul_weighting(network, network.lines_t.p0) @@ -2581,13 +2607,22 @@ def plot_grid( calc_dc_loading(network, timesteps) / rep_snapshots ).values - flow = flow[(flow.index.get_level_values("component")=="Line")| - (flow.index.isin(link_widths[link_widths.index.isin( - network.links[network.links.carrier=="DC"].index) - ].index, level=1))] - flow[flow<0] = -1 - flow[flow>0] = 1 - + flow = flow[ + (flow.index.get_level_values("component") == "Line") + | ( + flow.index.isin( + link_widths[ + link_widths.index.isin( + network.links[network.links.carrier == "DC"].index + ) + ].index, + level=1, + ) + ) + ] + flow[flow < 0] = -1 + flow[flow > 0] = 1 + elif line_colors == "v_nom": title = "Voltage levels" label = "v_nom in kV" @@ -2633,7 +2668,9 @@ def plot_grid( title = "Dynamic line rating" label = "MWh above nominal capacity" plot_background_grid(network, ax) - line_loading = network.lines_t.p0.mul(1 / (network.lines.s_nom_opt*network.lines.s_max_pu)) + line_loading = network.lines_t.p0.mul( + 1 / (network.lines.s_nom_opt * network.lines.s_max_pu) + ) dlr_usage = ( line_loading[line_loading.abs() > 1] .fillna(0) @@ -2646,16 +2683,15 @@ def plot_grid( line_widths = 0.5 + (line_colors / ext_width) link_colors = pd.Series(data=0, index=network.links.index) - elif line_colors == 'blue': - title = "" - label = "" - line_colors = 'blue' - link_colors = 'blue' - plot_background_grid(network, ax) - link_widths = 0 - line_widths = 0 - - + elif line_colors == "blue": + title = "" + label = "" + line_colors = "blue" + link_colors = "blue" + plot_background_grid(network, ax) + link_widths = 0 + line_widths = 0 + else: logger.warning("line_color {} undefined".format(line_colors)) @@ -2671,27 +2707,39 @@ def plot_grid( bus_unit = "TWh" elif bus_colors == "storage_expansion": if not isinstance(scaling_store_expansion, dict): - raise Exception("""To plot storage_expansion, the argument scaling_store_expansion must be a dictionary like: + raise Exception( + """To plot storage_expansion, the argument scaling_store_expansion must be a dictionary like: {"H2": 50, "heat": 0.1, - "battery": 10}""") + "battery": 10}""" + ) bus_scaling = bus_sizes bus_sizes = bus_scaling * calc_storage_expansion_per_bus(network) for store_carrier in ["H2", "heat", "battery"]: - bus_sizes[bus_sizes.index.get_level_values('carrier').str.contains(store_carrier)] *= scaling_store_expansion[store_carrier] + bus_sizes[ + bus_sizes.index.get_level_values("carrier").str.contains( + store_carrier + ) + ] *= scaling_store_expansion[store_carrier] bus_legend = "Storage expansion" bus_unit = "GW" elif bus_colors == "h2_battery_storage_expansion": bus_scaling = bus_sizes - bus_sizes = bus_scaling * calc_storage_expansion_per_bus(network, carriers=["battery", "H2_overground", "H2_underground"]) - if (("battery" not in scaling_store_expansion.keys()) | - ("H2_overground" not in scaling_store_expansion.keys()) | - ("H2_underground" not in scaling_store_expansion.keys())): - raise Exception("""To plot h2_battery_storage_expansion, the argument scaling_store_expansion must be a dictionary like: + bus_sizes = bus_scaling * calc_storage_expansion_per_bus( + network, carriers=["battery", "H2_overground", "H2_underground"] + ) + if ( + ("battery" not in scaling_store_expansion.keys()) + | ("H2_overground" not in scaling_store_expansion.keys()) + | ("H2_underground" not in scaling_store_expansion.keys()) + ): + raise Exception( + """To plot h2_battery_storage_expansion, the argument scaling_store_expansion must be a dictionary like: {"H2_overground": 1, "H2_underground": 1, - "battery": 1,}""") - + "battery": 1,}""" + ) + for store_carrier in ["battery", "H2_overground", "H2_underground"]: bus_sizes[ bus_sizes.index.get_level_values("carrier").str.contains( @@ -2734,15 +2782,17 @@ def plot_grid( .mul(network.snapshot_weightings.generators, axis=0) .sum() ) - bus_sizes = bus_scaling * flex_links.groupby(["bus0", "carrier"]).p0_sum.sum() + bus_sizes = ( + bus_scaling * flex_links.groupby(["bus0", "carrier"]).p0_sum.sum() + ) bus_unit = "TWh" bus_legend = "flexibility_usage" elif bus_colors == "h2_storage_expansion": bus_scaling = bus_sizes bus_sizes = bus_scaling * calc_storage_expansion_per_bus(network) bus_sizes = bus_sizes.reset_index() - bus_sizes = bus_sizes[bus_sizes.carrier.str.contains('H2')] - bus_sizes.set_index(['bus','carrier'],inplace=True) + bus_sizes = bus_sizes[bus_sizes.carrier.str.contains("H2")] + bus_sizes.set_index(["bus", "carrier"], inplace=True) bus_legend = "Storage expansion" bus_unit = "GW" elif ( @@ -2781,14 +2831,14 @@ def plot_grid( line_widths=line_widths, link_widths=link_widths, flow=flow, - title=title, + # title=title, geomap=False, projection=ccrs.PlateCarree(), color_geomap=True, boundaries=[-2.5, 16, 46.8, 58], ) else: - ll =network.plot( + ll = network.plot( line_colors=line_colors, link_colors=link_colors, line_cmap=plt.cm.jet, @@ -2798,11 +2848,11 @@ def plot_grid( line_widths=line_widths, link_widths=link_widths, flow=flow, - title=title, + # title=title, geomap=False, boundaries=[-2.5, 16, 46.8, 58], ) - + # legends for bus sizes and colors if bus_legend: handles = [] @@ -2812,11 +2862,17 @@ def plot_grid( if bus_legend == "Storage expansion": legend_entries = list(scaling_store_expansion.keys()) if bus_legend == "Battery and H2 storage expansion": - legend_entries = ["battery", "H2_overground", "H2_underground"] + legend_entries = [ + "battery", + "H2_overground", + "H2_underground", + ] for i in legend_entries: try: max_value = bus_sizes[ - bus_sizes.index.get_level_values("carrier").str.contains(i) + bus_sizes.index.get_level_values( + "carrier" + ).str.contains(i) ].max() except: max_value = bus_sizes.max() @@ -2833,9 +2889,7 @@ def plot_grid( ) else: max_value = bus_sizes.max() - labels.append( - f"{round(max_value / bus_scaling /1000, 0)} GWh " - ) + labels.append(f"{round(max_value / bus_scaling /1000, 0)} GWh ") handles.append( make_legend_circles_for( [max_value], @@ -2853,14 +2907,13 @@ def plot_grid( framealpha=1.0, title=bus_legend, handler_map=make_handler_map_to_scale_circles_as_in(ax), - prop={'size': 8} + prop={"size": 8}, ) ax.add_artist(l2) - plt.setp(l2.get_title(),fontsize='9') + plt.setp(l2.get_title(), fontsize="9") if not scaling_store_expansion: - handles = [] if bus_legend == "Nodal production balance": positive = mpatches.Patch(color="green", label="generation") @@ -2869,27 +2922,33 @@ def plot_grid( elif legend_entries != "all": for i in legend_entries: - patch = mpatches.Patch(color=network.carriers.color[i], label=i) + patch = mpatches.Patch( + color=network.carriers.color[i], label=i + ) handles.append(patch) else: for i in bus_sizes.index.get_level_values("carrier").unique(): - patch = mpatches.Patch(color=network.carriers.color[i], label=i) + patch = mpatches.Patch( + color=network.carriers.color[i], label=i + ) handles.append(patch) - + l3 = plt.legend( - handles=handles, loc="upper left", ncol=2, bbox_to_anchor=(0, 0) + handles=handles, + loc="upper left", + ncol=2, + bbox_to_anchor=(0, 0), ) ax.add_artist(l3) if type(line_colors) != str: - # Set fixed boundaries if selected in parameters if not boundaries: boundaries = [ min(line_colors.min(), link_colors.min()), max(line_colors.max(), link_colors.max()), ] - + # Create ticks for legend v = np.linspace(boundaries[0], boundaries[1], 101) for l_collection in ll: @@ -2897,8 +2956,12 @@ def plot_grid( # colorbar for line heatmap cb = plt.colorbar( - ll[1], values=v, ticks=v[0:101:10], fraction=0.028, pad=0.04, - ) + ll[1], + values=v, + ticks=v[0:101:10], + fraction=0.028, + pad=0.04, + ) # Set legend label cb.set_label(label) @@ -3707,7 +3770,7 @@ def plot_heat_summary(self, t_resolution="20H", stacked=True, save_path=False): data.resample(t_resolution).mean().plot( ax=ax, label=i, legend=True ) - + heat_store_dispatch_hb.resample(t_resolution).mean().plot.line( ax=ax, legend=True, From db21a490ca6b43a6f2868af71a306e7dc7b34319 Mon Sep 17 00:00:00 2001 From: CarlosEpia Date: Mon, 27 Mar 2023 15:41:43 +0200 Subject: [PATCH 046/136] apply flake8 --- etrago/tools/plot.py | 156 ++++++++++++++++++++----------------------- 1 file changed, 72 insertions(+), 84 deletions(-) diff --git a/etrago/tools/plot.py b/etrago/tools/plot.py index 75d951a34..968feb6c2 100644 --- a/etrago/tools/plot.py +++ b/etrago/tools/plot.py @@ -21,12 +21,17 @@ """ Plot.py defines functions necessary to plot results of eTraGo. """ -from math import log10, sqrt +from math import sqrt import logging import os from matplotlib import pyplot as plt +from matplotlib.legend_handler import HandlerPatch +from matplotlib.patches import Circle, Ellipse from pyproj import Proj, transform +from pypsa.plot import draw_map_cartopy +from shapely.geometry import LineString, Point +import geopandas as gpd import matplotlib import matplotlib.patches as mpatches import numpy as np @@ -35,15 +40,10 @@ cartopy_present = True try: - import cartopy import cartopy.crs as ccrs - import cartopy.mpl.geoaxes - import requests except ImportError: cartopy_present = False -from pypsa.plot import draw_map_cartopy -from shapely.geometry import LineString, MultiPoint, Point, Polygon -import geopandas as gpd + logger = logging.getLogger(__name__) @@ -216,7 +216,7 @@ def plot_line_loading_diff(networkA, networkB, timestep=0, osm=False): 'y': array of two floats, y axis boundaries (long) 'zoom' : resolution of osm """ - if osm != False: + if osm is not False: if set_epsg_network.counter == 0: set_epsg_network(networkA) set_epsg_network(networkB) @@ -347,7 +347,7 @@ def network_expansion_diff( 'zoom' : resolution of osm """ - if osm != False: + if osm is not False: if set_epsg_network.counter == 0: set_epsg_network(networkA) set_epsg_network(networkB) @@ -648,7 +648,7 @@ def plot_voltage(network, boundaries=[], osm=False): ------- Plot """ - if osm != False: + if osm is not False: if set_epsg_network.counter == 0: set_epsg_network(network) plot_osm(osm["x"], osm["y"], osm["zoom"]) @@ -759,6 +759,7 @@ def calc_dispatch_per_carrier(network, timesteps): def import_gen_from_links(network): from pypsa.networkclustering import aggregategenerators + from etrago.cluster.spatial import strategies_generators """ @@ -1182,11 +1183,11 @@ def nodal_gen_dispatch( 'zoom' : resolution of osm """ - if osm != False: + if osm is not False: if set_epsg_network.counter == 0: set_epsg_network(network) fig, ax = plot_osm(osm["x"], osm["y"], osm["zoom"]) - elif (osm == False) and cartopy_present: + elif (osm is False) and cartopy_present: fig, ax = plt.subplots( subplot_kw={"projection": ccrs.PlateCarree()}, figsize=(5, 5) ) @@ -1308,6 +1309,7 @@ def nodal_production_balance(network, timesteps, scaling=0.00001): def import_gen_from_links(network): from pypsa.networkclustering import aggregategenerators + from etrago.cluster.spatial import strategies_generators """ @@ -1607,15 +1609,6 @@ def storage_soc_sorted(network, filename=None): & (network.storage_units.max_hours == 168) ] - cap_batt = ( - network.storage_units.max_hours[sbatt] - * network.storage_units.p_nom_opt[sbatt] - ).sum() - cap_hydr = ( - network.storage_units.max_hours[shydr] - * network.storage_units.p_nom_opt[shydr] - ).sum() - fig, ax = plt.subplots(1, 1) if ( @@ -1759,13 +1752,13 @@ def calc_dc_loading(network, timesteps): & (network.links.length == row["length"]) ] ).empty: - l = network.links.index[ + links_df = network.links.index[ (network.links.bus0 == row["bus1"]) & (network.links.bus1 == row["bus0"]) & (network.links.length == row["length"]) ] - network.links.at[i, "linked_to"] = l.values[0] + network.links.at[i, "linked_to"] = links_df.values[0] network.links.linked_to = network.links.linked_to.astype(str) # Set p_nom_max and line_loading for one directional links @@ -1978,26 +1971,26 @@ def demand_side_management(self, buses, snapshots, agg="5h", used=False): """ df = pd.DataFrame(index=self.network.snapshots[snapshots]) - l = self.network.links[ + link = self.network.links[ (self.network.links.carrier == "dsm") & (self.network.links.bus0.isin(buses)) ] s = self.network.stores[ (self.network.stores.carrier == "dsm") - & (self.network.stores.bus.isin(l.bus1.values)) + & (self.network.stores.bus.isin(link.bus1.values)) ] df["p_min"] = ( - self.network.links_t.p_min_pu[l.index] - .mul(l.p_nom, axis=1) + self.network.links_t.p_min_pu[link.index] + .mul(link.p_nom, axis=1) .sum(axis=1) .resample(agg) .mean() .iloc[snapshots] ) df["p_max"] = ( - self.network.links_t.p_max_pu[l.index] - .mul(l.p_nom, axis=1) + self.network.links_t.p_max_pu[link.index] + .mul(link.p_nom, axis=1) .sum(axis=1) .resample(agg) .mean() @@ -2019,7 +2012,7 @@ def demand_side_management(self, buses, snapshots, agg="5h", used=False): if used: df["p"] = ( - self.network.links_t.p0[l.index] + self.network.links_t.p0[link.index] .clip(lower=0) .sum(axis=1) .resample(agg) @@ -2288,7 +2281,8 @@ def flexibility_usage( snapshots : list, optional Considered snapshots, if empty all are considered. The default is []. buses : list, optional - Considered components at AC buses, if empty all are considered. The default is []. + Considered components at AC buses, if empty all are considered. + The default is []. pre_path : str, optional State of and where you want to store the figure. The default is None. @@ -2302,9 +2296,6 @@ def flexibility_usage( colors["h2_store"] = colors["H2_underground"] colors["heat"] = colors["central_heat_store"] - potential = pd.DataFrame(index=self.network.snapshots[snapshots]) - used = pd.DataFrame(index=self.network.snapshots[snapshots]) - if not buses: buses = self.network.buses.index @@ -2497,22 +2488,7 @@ def plot_grid( Set static line color or attribute to plot e.g. 'expansion_abs' Current options: 'line_loading': mean line loading in p.u. in selected timesteps - 'v_nom': nominal voltage of linesflow = pd.Series(index=network.branches().index, dtype="float64") - flow.iloc[flow.index.get_level_values("component") == "Line"] = ( - mul_weighting(network, network.lines_t.p0) - .loc[network.snapshots[timesteps]] - .sum() - / network.lines.s_nom - / rep_snapshots - ).values - flow.iloc[flow.index.get_level_values("component") == "Link"] = ( - calc_dc_loading(network, timesteps) / rep_snapshots - ).values - flow = flow[(flow.index.get_level_values("component")=="Line")| - (flow.index.isin(link_widths[link_colors.index.isin( - network.links[network.links.carrier=="DC"].index) - ].index, level=1))] - flow = flow.apply(lambda x: x+5 if x > 0 else x-5) + 'v_nom': nominal voltage of lines 'expansion_abs': absolute network expansion in MVA 'expansion_rel': network expansion in p.u. of existing capacity 'q_flow_max': maximal reactive flows @@ -2521,12 +2497,14 @@ def plot_grid( bus_colors : str, optional Set static bus color or attribute to plot. The default is 'grey'. Current options: - 'nodal_production_balance': net producer/consumer in selected timeteps + 'nodal_production_balance': net producer/consumer in + selected timeteps 'storage_expansion': storage expansion per bus and technology 'storage_distribution': installed storage units per bus 'gen_dist': dispatch per carrier in selected timesteps timesteps : array, optional - Timesteps consideredd in time depended plots. The default is range(2). + Timesteps consideredd in time depended plots. The default + is range(2). osm : bool or dict, e.g. {'x': [1,20], 'y': [47, 56], 'zoom' : 6} If not False, osm is set as background with the following settings as dict: @@ -2542,8 +2520,8 @@ def plot_grid( ext_min: float Choose minimum relative line extension shown in plot in p.u.. ext_width: float or bool - Choose if line_width respects line extension. Turn off with 'False' or - set linear factor to decremise extension line_width. + Choose if line_width respects line extension. Turn off with + 'False' or set linear factor to decremise extension line_width. The default is False. Returns @@ -2562,16 +2540,17 @@ def plot_grid( # Set default values flow = None + title = "" line_widths = 2 link_widths = 0 # Plot osm map in background - if osm != False: + if osm is not False: if network.srid == 4326: set_epsg_network(network) fig, ax = plot_osm(osm["x"], osm["y"], osm["zoom"]) - elif (osm == False) and cartopy_present: + elif (osm is False) and cartopy_present: fig, ax = plt.subplots( subplot_kw={"projection": ccrs.PlateCarree()}, figsize=(5, 5) ) @@ -2636,7 +2615,7 @@ def plot_grid( network, method="abs", ext_min=ext_min ) plot_background_grid(all_network, ax) - if ext_width != False: + if ext_width is not False: line_widths = 0.5 + (line_colors / ext_width) link_widths = 0.5 + (line_colors / ext_width) else: @@ -2653,7 +2632,7 @@ def plot_grid( network, method="rel", ext_min=ext_min ) plot_background_grid(all_network, ax) - if ext_width != False: + if ext_width is not False: line_widths = 0.5 + (line_colors / ext_width) link_widths = 0.5 + (link_colors / ext_width) elif line_colors == "q_flow_max": @@ -2679,7 +2658,7 @@ def plot_grid( .sum() ) line_colors = dlr_usage - if ext_width != False: + if ext_width is not False: line_widths = 0.5 + (line_colors / ext_width) link_colors = pd.Series(data=0, index=network.links.index) @@ -2708,7 +2687,8 @@ def plot_grid( elif bus_colors == "storage_expansion": if not isinstance(scaling_store_expansion, dict): raise Exception( - """To plot storage_expansion, the argument scaling_store_expansion must be a dictionary like: + """To plot storage_expansion, the argument\ + scaling_store_expansion must be a dictionary like: {"H2": 50, "heat": 0.1, "battery": 10}""" @@ -2734,7 +2714,8 @@ def plot_grid( | ("H2_underground" not in scaling_store_expansion.keys()) ): raise Exception( - """To plot h2_battery_storage_expansion, the argument scaling_store_expansion must be a dictionary like: + """To plot h2_battery_storage_expansion, the argument\ + scaling_store_expansion must be a dictionary like: {"H2_overground": 1, "H2_underground": 1, "battery": 1,}""" @@ -2831,7 +2812,7 @@ def plot_grid( line_widths=line_widths, link_widths=link_widths, flow=flow, - # title=title, + title=title, geomap=False, projection=ccrs.PlateCarree(), color_geomap=True, @@ -2848,7 +2829,7 @@ def plot_grid( line_widths=line_widths, link_widths=link_widths, flow=flow, - # title=title, + title=title, geomap=False, boundaries=[-2.5, 16, 46.8, 58], ) @@ -2874,7 +2855,7 @@ def plot_grid( "carrier" ).str.contains(i) ].max() - except: + except KeyError: max_value = bus_sizes.max() handles.append( make_legend_circles_for( @@ -2884,8 +2865,9 @@ def plot_grid( )[0] ) labels.append( - f"{round(max_value / bus_scaling / scaling_store_expansion[i]/1000, 0).astype(int)} GWh " - + i + f""" + {round(max_value/bus_scaling/scaling_store_expansion[i]/ + 1000, 0).astype(int)} {bus_unit} """ + i ) else: max_value = bus_sizes.max() @@ -2979,10 +2961,8 @@ def plot_grid( set_epsg_network.counter = 0 -### the following functions are copied from pypsa-eur-sec ### -### see here: https://github.com/PyPSA/pypsa-eur-sec/blob/master/scripts/plot_network.py -from matplotlib.legend_handler import HandlerPatch -from matplotlib.patches import Circle, Ellipse +# the following functions are copied from pypsa-eur-sec. see: +# https://github.com/PyPSA/pypsa-eur-sec/blob/master/scripts/plot_network.py def make_handler_map_to_scale_circles_as_in(ax, dont_resize_actively=False): @@ -3191,7 +3171,8 @@ def plot_gas_generation( self : :class:`Etrago Overall container of Etrago t_resolution : str, optional - sets the resampling rate of timeseries data to allow for smoother line plots + sets the resampling rate of timeseries data to allow for smoother + line plots save_path : bool, optional Path to save the generated plot. The default is False. @@ -3256,10 +3237,12 @@ def plot_gas_summary(self, t_resolution="20H", stacked=True, save_path=False): self : :class:`Etrago Overall container of Etrago t_resolution : str, optional - sets the resampling rate of timeseries data to allow for smoother line plots + sets the resampling rate of timeseries data to allow for smoother + line plots stacked : bool, optional - If True all TS data will be shown as stacked area plot. Total gas generation - will then also be plotted to check for matching demand and generation. + If True all TS data will be shown as stacked area plot. Total gas + generation will then also be plotted to check for matching demand and + generation. save_path : bool, optional Path to save the generated plot. The default is False. @@ -3410,7 +3393,8 @@ def plot_h2_generation(self, t_resolution="20H", save_path=False): self : :class:`Etrago Overall container of Etrago t_resolution : str, optional - sets the resampling rate of timeseries data to allow for smoother line plots + sets the resampling rate of timeseries data to allow for smoother + line plots save_path : bool, optional Path to save the generated plot. The default is False. @@ -3468,10 +3452,12 @@ def plot_h2_summary(self, t_resolution="20H", stacked=True, save_path=False): self : :class:`Etrago Overall container of Etrago t_resolution : str, optional - sets the resampling rate of timeseries data to allow for smoother line plots + sets the resampling rate of timeseries data to allow for smoother + line plots stacked : bool, optional - If True all TS data will be shown as stacked area plot. Total H2 generation - will then also be plotted to check for matching demand and generation. + If True all TS data will be shown as stacked area plot. Total H2 + generation will then also be plotted to check for matching demand and + generation. save_path : bool, optional Path to save the generated plot. The default is False. @@ -3480,7 +3466,6 @@ def plot_h2_summary(self, t_resolution="20H", stacked=True, save_path=False): None. """ - colors = coloring() rel_h2_links = ["H2_feedin", "H2_to_CH4", "H2_to_power"] rel_h2_loads = ["H2_for_industry", "H2_hgv_load"] @@ -3593,7 +3578,8 @@ def plot_heat_loads(self, t_resolution="20H", save_path=False): self : :class:`Etrago Overall container of Etrago t_resolution : str, optional - sets the resampling rate of timeseries data to allow for smoother line plots + sets the resampling rate of timeseries data to allow for smoother + line plots save_path : bool, optional Path to save the generated plot. The default is False. @@ -3642,10 +3628,12 @@ def plot_heat_summary(self, t_resolution="20H", stacked=True, save_path=False): self : :class:`Etrago Overall container of Etrago t_resolution : str, optional - sets the resampling rate of timeseries data to allow for smoother line plots + sets the resampling rate of timeseries data to allow for smoother + line plots stacked : bool, optional - If True all TS data will be shown as stacked area plot. Total heat demand - will then also be plotted to check for matching generation and demand. + If True all TS data will be shown as stacked area plot. Total heat + demand will then also be plotted to check for matching generation and + demand. save_path : bool, optional Path to save the generated plot. The default is False. @@ -3717,7 +3705,7 @@ def plot_heat_summary(self, t_resolution="20H", stacked=True, save_path=False): / 1e3 ) - if stacked == True: + if stacked: data = pd.DataFrame(-(data.sum(axis=1))) data = data.rename(columns={0: heat_gen_techs[0]}) From 7eddadfac2b046c466c756a9b89b2ef155e7e71a Mon Sep 17 00:00:00 2001 From: CarlosEpia Date: Tue, 28 Mar 2023 09:10:07 +0200 Subject: [PATCH 047/136] update docstrings plot_grid --- etrago/tools/plot.py | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/etrago/tools/plot.py b/etrago/tools/plot.py index 968feb6c2..f58b3daa9 100644 --- a/etrago/tools/plot.py +++ b/etrago/tools/plot.py @@ -2492,6 +2492,8 @@ def plot_grid( 'expansion_abs': absolute network expansion in MVA 'expansion_rel': network expansion in p.u. of existing capacity 'q_flow_max': maximal reactive flows + 'dlr': energy above nominal capacity + 'grey': plot all lines and DC linkd grey colored bus_sizes : float, optional Size of buses. The default is 0.001. bus_colors : str, optional @@ -2499,9 +2501,13 @@ def plot_grid( Current options: 'nodal_production_balance': net producer/consumer in selected timeteps - 'storage_expansion': storage expansion per bus and technology + 'storage_expansion': storage expansion per bus and technology. + 'h2_battery_storage_expansion': storage expansion per bus and + technology for underground and overground H2 and batteries. 'storage_distribution': installed storage units per bus 'gen_dist': dispatch per carrier in selected timesteps + 'PowerToH2': location and sizes of electrolizers + 'flexibility_usage': use of DSM and BEV charger timesteps : array, optional Timesteps consideredd in time depended plots. The default is range(2). @@ -2523,7 +2529,13 @@ def plot_grid( Choose if line_width respects line extension. Turn off with 'False' or set linear factor to decremise extension line_width. The default is False. - + legend_entries : list, optional + Set the legends for buses to be plotted. The default is 'all'. + scaling_store_expansion : dict, optional + Set scaling values to be used per technology for the plots + storage_expansion and h2_battery_storage_expansion. The default is + {"H2": 50, "heat": 0.1, "battery": 10} + Returns ------- None. From ef95e2ca648ac652a62b02cd8c89c975a52e8d54 Mon Sep 17 00:00:00 2001 From: CarlosEpia Date: Tue, 28 Mar 2023 09:11:02 +0200 Subject: [PATCH 048/136] change line colors basic to grey --- etrago/tools/plot.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/etrago/tools/plot.py b/etrago/tools/plot.py index f58b3daa9..1e1ad9b99 100644 --- a/etrago/tools/plot.py +++ b/etrago/tools/plot.py @@ -2674,11 +2674,11 @@ def plot_grid( line_widths = 0.5 + (line_colors / ext_width) link_colors = pd.Series(data=0, index=network.links.index) - elif line_colors == "blue": + elif line_colors == "grey": title = "" label = "" - line_colors = "blue" - link_colors = "blue" + line_colors = "grey" + link_colors = "grey" plot_background_grid(network, ax) link_widths = 0 line_widths = 0 From 28204f1d071bbcf1fa06b4f79540fe1a57d11f56 Mon Sep 17 00:00:00 2001 From: CarlosEpia Date: Tue, 28 Mar 2023 09:58:23 +0200 Subject: [PATCH 049/136] extract the function import_gen_from_links from pf_post_lopf --- etrago/tools/execute.py | 200 ++++++++++++++++++++-------------------- 1 file changed, 101 insertions(+), 99 deletions(-) diff --git a/etrago/tools/execute.py b/etrago/tools/execute.py index 0e8c688cc..3b7eb17a8 100755 --- a/etrago/tools/execute.py +++ b/etrago/tools/execute.py @@ -391,6 +391,107 @@ def dispatch_disaggregation(self): logger.info("Time for LOPF [min]: {}".format(round(z, 2))) +def import_gen_from_links(network): + """ + create gas generators from links in order to not lose them when + dropping non-electric carriers + """ + # Discard all generators < 1kW + discard_gen = network.links[network.links["p_nom"] <= 0.001].index + network.links.drop(discard_gen, inplace=True) + for df in network.links_t: + if not network.links_t[df].empty: + network.links_t[df].drop( + columns=discard_gen.values, inplace=True, errors="ignore" + ) + + gas_to_add = network.links[ + network.links.carrier.isin( + [ + "central_gas_CHP", + "OCGT", + "H2_to_power", + "industrial_gas_CHP", + ] + ) + ].copy() + + # Drop generators from the links table + network.links.drop(gas_to_add.index, inplace=True) + + gas_to_add.rename(columns={"bus1": "bus"}, inplace=True) + + # Create generators' names like in network.generators + gas_to_add["Generator"] = ( + gas_to_add["bus"] + " " + gas_to_add.index + gas_to_add["carrier"] + ) + gas_to_add_orig = gas_to_add.copy() + gas_to_add.set_index("Generator", drop=True, inplace=True) + gas_to_add = gas_to_add[ + gas_to_add.columns[ + gas_to_add.columns.isin(network.generators.columns) + ] + ] + + network.import_components_from_dataframe(gas_to_add, "Generator") + + # Dealing with generators_t + columns_new = network.links_t.p1.columns[ + network.links_t.p1.columns.isin(gas_to_add_orig.index) + ] + + new_gen_t = network.links_t.p1[columns_new] * -1 + new_gen_t.rename(columns=gas_to_add_orig["Generator"], inplace=True) + network.generators_t.p = network.generators_t.p.join(new_gen_t) + + # Drop generators from the links_t table + for df in network.links_t: + if not network.links_t[df].empty: + network.links_t[df].drop( + columns=gas_to_add_orig.index, + inplace=True, + errors="ignore", + ) + + # Group generators per bus if needed + if not ( + network.generators.groupby(["bus", "carrier"]).p_nom.count() == 1 + ).all(): + network.generators["weight"] = network.generators.p_nom + df, df_t = aggregategenerators( + network, + busmap=pd.Series( + index=network.buses.index, data=network.buses.index + ), + custom_strategies=strategies_generators(), + ) + + # Keep control arguments from generators + control = network.generators.groupby( + ["bus", "carrier"] + ).control.first() + control.index = ( + control.index.get_level_values(0) + + " " + + control.index.get_level_values(1) + ) + df.control = control + + # Drop non-aggregated generators + network.mremove("Generator", network.generators.index) + + # Insert aggregated generators and time series + network.import_components_from_dataframe(df, "Generator") + + for attr, data in df_t.items(): + if not data.empty: + network.import_series_from_dataframe( + data, "Generator", attr + ) + + return + + def run_pf_post_lopf(self): """Functions that runs pf_post_lopf accordning to arguments @@ -542,105 +643,6 @@ def drop_foreign_components(network): return foreign_bus, foreign_comp, foreign_series - def import_gen_from_links(network): - """ - create gas generators from links in order to not lose them when - dropping non-electric carriers - """ - # Discard all generators < 1kW - discard_gen = network.links[network.links["p_nom"] <= 0.001].index - network.links.drop(discard_gen, inplace=True) - for df in network.links_t: - if not network.links_t[df].empty: - network.links_t[df].drop( - columns=discard_gen.values, inplace=True, errors="ignore" - ) - - gas_to_add = network.links[ - network.links.carrier.isin( - [ - "central_gas_CHP", - "OCGT", - "H2_to_power", - "industrial_gas_CHP", - ] - ) - ].copy() - - # Drop generators from the links table - network.links.drop(gas_to_add.index, inplace=True) - - gas_to_add.rename(columns={"bus1": "bus"}, inplace=True) - - # Create generators' names like in network.generators - gas_to_add["Generator"] = ( - gas_to_add["bus"] + " " + gas_to_add.index + gas_to_add["carrier"] - ) - gas_to_add_orig = gas_to_add.copy() - gas_to_add.set_index("Generator", drop=True, inplace=True) - gas_to_add = gas_to_add[ - gas_to_add.columns[ - gas_to_add.columns.isin(network.generators.columns) - ] - ] - - network.import_components_from_dataframe(gas_to_add, "Generator") - - # Dealing with generators_t - columns_new = network.links_t.p1.columns[ - network.links_t.p1.columns.isin(gas_to_add_orig.index) - ] - - new_gen_t = network.links_t.p1[columns_new] * -1 - new_gen_t.rename(columns=gas_to_add_orig["Generator"], inplace=True) - network.generators_t.p = network.generators_t.p.join(new_gen_t) - - # Drop generators from the links_t table - for df in network.links_t: - if not network.links_t[df].empty: - network.links_t[df].drop( - columns=gas_to_add_orig.index, - inplace=True, - errors="ignore", - ) - - # Group generators per bus if needed - if not ( - network.generators.groupby(["bus", "carrier"]).p_nom.count() == 1 - ).all(): - network.generators["weight"] = network.generators.p_nom - df, df_t = aggregategenerators( - network, - busmap=pd.Series( - index=network.buses.index, data=network.buses.index - ), - custom_strategies=strategies_generators(), - ) - - # Keep control arguments from generators - control = network.generators.groupby( - ["bus", "carrier"] - ).control.first() - control.index = ( - control.index.get_level_values(0) - + " " - + control.index.get_level_values(1) - ) - df.control = control - - # Drop non-aggregated generators - network.mremove("Generator", network.generators.index) - - # Insert aggregated generators and time series - network.import_components_from_dataframe(df, "Generator") - - for attr, data in df_t.items(): - if not data.empty: - network.import_series_from_dataframe( - data, "Generator", attr - ) - - return x = time.time() network = etrago.network From 5f1d03eb2bfd85106b06488536ac9d50981b9310 Mon Sep 17 00:00:00 2001 From: CarlosEpia Date: Tue, 28 Mar 2023 10:28:07 +0200 Subject: [PATCH 050/136] delete duplicated functions --- etrago/tools/plot.py | 209 +------------------------------------------ 1 file changed, 1 insertion(+), 208 deletions(-) diff --git a/etrago/tools/plot.py b/etrago/tools/plot.py index 1e1ad9b99..25364b1c4 100644 --- a/etrago/tools/plot.py +++ b/etrago/tools/plot.py @@ -31,6 +31,7 @@ from pyproj import Proj, transform from pypsa.plot import draw_map_cartopy from shapely.geometry import LineString, Point +from etrago.tools.execute import import_gen_from_links import geopandas as gpd import matplotlib import matplotlib.patches as mpatches @@ -757,110 +758,6 @@ def calc_dispatch_per_carrier(network, timesteps): """ - def import_gen_from_links(network): - from pypsa.networkclustering import aggregategenerators - - from etrago.cluster.spatial import strategies_generators - - """ - create gas generators from links in order to not lose them when - dropping non-electric carriers - """ - # Discard all generators < 1kW - discard_gen = network.links[network.links["p_nom"] <= 0.001].index - network.links.drop(discard_gen, inplace=True) - for df in network.links_t: - if not network.links_t[df].empty: - network.links_t[df].drop( - columns=discard_gen.values, inplace=True, errors="ignore" - ) - - gas_to_add = network.links[ - network.links.carrier.isin( - [ - "central_gas_CHP", - "OCGT", - "H2_to_power", - "industrial_gas_CHP", - ] - ) - ].copy() - - # Drop generators from the links table - network.links.drop(gas_to_add.index, inplace=True) - - gas_to_add.rename(columns={"bus1": "bus"}, inplace=True) - - # Create generators' names like in network.generators - gas_to_add["Generator"] = ( - gas_to_add["bus"] + " " + gas_to_add.index + gas_to_add["carrier"] - ) - gas_to_add_orig = gas_to_add.copy() - gas_to_add.set_index("Generator", drop=True, inplace=True) - gas_to_add = gas_to_add[ - gas_to_add.columns[ - gas_to_add.columns.isin(network.generators.columns) - ] - ] - - network.import_components_from_dataframe(gas_to_add, "Generator") - - # Dealing with generators_t - columns_new = network.links_t.p1.columns[ - network.links_t.p1.columns.isin(gas_to_add_orig.index) - ] - - new_gen_t = network.links_t.p1[columns_new] * -1 - new_gen_t.rename(columns=gas_to_add_orig["Generator"], inplace=True) - network.generators_t.p = network.generators_t.p.join(new_gen_t) - - # Drop generators from the links_t table - for df in network.links_t: - if not network.links_t[df].empty: - network.links_t[df].drop( - columns=gas_to_add_orig.index, - inplace=True, - errors="ignore", - ) - - # Group generators per bus if needed - if not ( - network.generators.groupby(["bus", "carrier"]).p_nom.count() == 1 - ).all(): - network.generators["weight"] = network.generators.p_nom - df, df_t = aggregategenerators( - network, - busmap=pd.Series( - index=network.buses.index, data=network.buses.index - ), - custom_strategies=strategies_generators(), - ) - - # Keep control arguments from generators - control = network.generators.groupby( - ["bus", "carrier"] - ).control.first() - control.index = ( - control.index.get_level_values(0) - + " " - + control.index.get_level_values(1) - ) - df.control = control - - # Drop non-aggregated generators - network.mremove("Generator", network.generators.index) - - # Insert aggregated generators and time series - network.import_components_from_dataframe(df, "Generator") - - for attr, data in df_t.items(): - if not data.empty: - network.import_series_from_dataframe( - data, "Generator", attr - ) - - return - import_gen_from_links(network) ac_buses = network.buses[network.buses.carrier == "AC"].index @@ -1307,110 +1204,6 @@ def nodal_production_balance(network, timesteps, scaling=0.00001): """ - def import_gen_from_links(network): - from pypsa.networkclustering import aggregategenerators - - from etrago.cluster.spatial import strategies_generators - - """ - create gas generators from links in order to not lose them when - dropping non-electric carriers - """ - # Discard all generators < 1kW - discard_gen = network.links[network.links["p_nom"] <= 0.001].index - network.links.drop(discard_gen, inplace=True) - for df in network.links_t: - if not network.links_t[df].empty: - network.links_t[df].drop( - columns=discard_gen.values, inplace=True, errors="ignore" - ) - - gas_to_add = network.links[ - network.links.carrier.isin( - [ - "central_gas_CHP", - "OCGT", - "H2_to_power", - "industrial_gas_CHP", - ] - ) - ].copy() - - # Drop generators from the links table - network.links.drop(gas_to_add.index, inplace=True) - - gas_to_add.rename(columns={"bus1": "bus"}, inplace=True) - - # Create generators' names like in network.generators - gas_to_add["Generator"] = ( - gas_to_add["bus"] + " " + gas_to_add.index + gas_to_add["carrier"] - ) - gas_to_add_orig = gas_to_add.copy() - gas_to_add.set_index("Generator", drop=True, inplace=True) - gas_to_add = gas_to_add[ - gas_to_add.columns[ - gas_to_add.columns.isin(network.generators.columns) - ] - ] - - network.import_components_from_dataframe(gas_to_add, "Generator") - - # Dealing with generators_t - columns_new = network.links_t.p1.columns[ - network.links_t.p1.columns.isin(gas_to_add_orig.index) - ] - - new_gen_t = network.links_t.p1[columns_new] * -1 - new_gen_t.rename(columns=gas_to_add_orig["Generator"], inplace=True) - network.generators_t.p = network.generators_t.p.join(new_gen_t) - - # Drop generators from the links_t table - for df in network.links_t: - if not network.links_t[df].empty: - network.links_t[df].drop( - columns=gas_to_add_orig.index, - inplace=True, - errors="ignore", - ) - - # Group generators per bus if needed - if not ( - network.generators.groupby(["bus", "carrier"]).p_nom.count() == 1 - ).all(): - network.generators["weight"] = network.generators.p_nom - df, df_t = aggregategenerators( - network, - busmap=pd.Series( - index=network.buses.index, data=network.buses.index - ), - custom_strategies=strategies_generators(), - ) - - # Keep control arguments from generators - control = network.generators.groupby( - ["bus", "carrier"] - ).control.first() - control.index = ( - control.index.get_level_values(0) - + " " - + control.index.get_level_values(1) - ) - df.control = control - - # Drop non-aggregated generators - network.mremove("Generator", network.generators.index) - - # Insert aggregated generators and time series - network.import_components_from_dataframe(df, "Generator") - - for attr, data in df_t.items(): - if not data.empty: - network.import_series_from_dataframe( - data, "Generator", attr - ) - - return - import_gen_from_links(network) ac_buses = network.buses[network.buses.carrier == "AC"].index From 1d728d245fa46a9e8fe2232fcb3da465b89c9608 Mon Sep 17 00:00:00 2001 From: CarlosEpia Date: Tue, 28 Mar 2023 15:14:20 +0200 Subject: [PATCH 051/136] delete not necessary legends for PowerToH2 plot --- etrago/tools/plot.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/etrago/tools/plot.py b/etrago/tools/plot.py index 25364b1c4..e5ea073ae 100644 --- a/etrago/tools/plot.py +++ b/etrago/tools/plot.py @@ -2700,13 +2700,12 @@ def plot_grid( plt.setp(l2.get_title(), fontsize="9") - if not scaling_store_expansion: + if ((not scaling_store_expansion) & (bus_legend != "PowerToH2")): handles = [] if bus_legend == "Nodal production balance": positive = mpatches.Patch(color="green", label="generation") negative = mpatches.Patch(color="red", label="consumption") handles = [positive, negative] - elif legend_entries != "all": for i in legend_entries: patch = mpatches.Patch( From 71af64bab8f6eff324a30f874d63bd3df4a9551d Mon Sep 17 00:00:00 2001 From: CarlosEpia Date: Wed, 29 Mar 2023 08:44:51 +0200 Subject: [PATCH 052/136] Fix calculation DLR potential --- etrago/tools/plot.py | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/etrago/tools/plot.py b/etrago/tools/plot.py index e5ea073ae..9245f0092 100644 --- a/etrago/tools/plot.py +++ b/etrago/tools/plot.py @@ -2452,16 +2452,27 @@ def plot_grid( title = "Dynamic line rating" label = "MWh above nominal capacity" plot_background_grid(network, ax) - line_loading = network.lines_t.p0.mul( - 1 / (network.lines.s_nom_opt * network.lines.s_max_pu) - ) + # Extract branch_capacity_factors + bcf_hv = self.args["branch_capacity_factor"]["HV"] + bcf_ehv = self.args["branch_capacity_factor"]["eHV"] + # calc min capacity per line in the given period + network.lines.s_max_pu = network.lines_t.s_max_pu.min() + network.lines.s_max_pu[network.lines.s_max_pu < 0.7] = 0.5 + network.lines.s_max_pu[(network.lines.s_max_pu > 0.5) & + (network.lines.s_max_pu < 0.70)] = (bcf_hv + bcf_ehv)/2 + network.lines.s_max_pu[network.lines.s_max_pu >= 0.70] = 0.70 + line_loading = (network.lines_t.p0.mul( + 1 / (network.lines.s_nom_opt * network.lines.s_max_pu)).abs() + ) + # keep only the capacity allowed by dlr + line_loading = line_loading - 1 dlr_usage = ( - line_loading[line_loading.abs() > 1] + line_loading[line_loading > 0] .fillna(0) .mul(network.snapshot_weightings.generators, axis=0) - .abs() .sum() ) + dlr_usage = dlr_usage * network.lines.s_nom * network.lines.s_max_pu line_colors = dlr_usage if ext_width is not False: line_widths = 0.5 + (line_colors / ext_width) From 569303d9837dfd4a48746702c61597662d96bf57 Mon Sep 17 00:00:00 2001 From: CarlosEpia Date: Wed, 29 Mar 2023 09:25:36 +0200 Subject: [PATCH 053/136] fix cropped figures when exporting to file --- etrago/tools/plot.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/etrago/tools/plot.py b/etrago/tools/plot.py index 9245f0092..37a5f13d8 100644 --- a/etrago/tools/plot.py +++ b/etrago/tools/plot.py @@ -2649,7 +2649,7 @@ def plot_grid( geomap=False, boundaries=[-2.5, 16, 46.8, 58], ) - + l3 = None # legends for bus sizes and colors if bus_legend: handles = [] @@ -2769,8 +2769,11 @@ def plot_grid( plt.show() else: from matplotlib import pylab - - pylab.savefig(filename, dpi=300, bbox_inches="tight") + if l3 == None: + pylab.savefig(filename, dpi=300, bbox_inches='tight') + else: + pylab.savefig(filename, dpi=300, bbox_inches='tight', + bbox_extra_artists= [l3]) plt.close() From 5becb5e8c96007ff502fea92375918ff65fb32b1 Mon Sep 17 00:00:00 2001 From: ClaraBuettner Date: Wed, 29 Mar 2023 10:46:38 +0200 Subject: [PATCH 054/136] Fix calc results functions --- etrago/tools/calc_results.py | 2 +- etrago/tools/network.py | 14 ++++++++++---- 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/etrago/tools/calc_results.py b/etrago/tools/calc_results.py index 5c3f3f4c8..1605f9915 100755 --- a/etrago/tools/calc_results.py +++ b/etrago/tools/calc_results.py @@ -355,7 +355,7 @@ def system_costs_germany(self): """ - network_de = self.german_network + network_de = self.german_network() marginal_cost = 0 invest_cost = 0 diff --git a/etrago/tools/network.py b/etrago/tools/network.py index 2ed5aa5e9..8e4b57b7f 100644 --- a/etrago/tools/network.py +++ b/etrago/tools/network.py @@ -40,6 +40,8 @@ calc_etrago_results, dc_export, dc_export_per_country, + german_network, + system_costs_germany, ) from etrago.tools.execute import ( dispatch_disaggregation, @@ -246,22 +248,26 @@ def __init__( calc_results = calc_etrago_results - calc_ac_export = ac_export() + calc_ac_export = ac_export - calc_ac_export_per_country = ac_export_per_country() + calc_ac_export_per_country = ac_export_per_country - calc_dc_export = dc_export() + calc_dc_export = dc_export - calc_dc_export_per_country = dc_export_per_country() + 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 From 1e2ac6182d645b246ffc2fde117198303e4b42e8 Mon Sep 17 00:00:00 2001 From: CarlosEpia Date: Wed, 29 Mar 2023 15:51:06 +0200 Subject: [PATCH 055/136] add legends to the power2H2 plot --- etrago/tools/plot.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/etrago/tools/plot.py b/etrago/tools/plot.py index 37a5f13d8..0680c4eed 100644 --- a/etrago/tools/plot.py +++ b/etrago/tools/plot.py @@ -2711,12 +2711,16 @@ def plot_grid( plt.setp(l2.get_title(), fontsize="9") - if ((not scaling_store_expansion) & (bus_legend != "PowerToH2")): + if not scaling_store_expansion: handles = [] if bus_legend == "Nodal production balance": positive = mpatches.Patch(color="green", label="generation") negative = mpatches.Patch(color="red", label="consumption") handles = [positive, negative] + + elif bus_legend == "PowerToH2": + pth = mpatches.Patch(color="cyan", label="PowerToH2") + handles = [pth] elif legend_entries != "all": for i in legend_entries: patch = mpatches.Patch( From 82eae3a16e9d755e962571dd0479cc30a13027e9 Mon Sep 17 00:00:00 2001 From: CarlosEpia Date: Fri, 31 Mar 2023 11:44:32 +0200 Subject: [PATCH 056/136] make the parameter ext_width usable again --- etrago/tools/plot.py | 32 ++++++++++++++++++++++---------- 1 file changed, 22 insertions(+), 10 deletions(-) diff --git a/etrago/tools/plot.py b/etrago/tools/plot.py index 0680c4eed..3d8cee30b 100644 --- a/etrago/tools/plot.py +++ b/etrago/tools/plot.py @@ -1575,14 +1575,16 @@ def calc_dc_loading(network, timesteps): network.links.index == row["linked_to"] ].values[0], ) - - return ( + dc_load = ( mul_weighting(network, link_load) .loc[network.snapshots[timesteps]] .abs() .sum()[network.links.index] / p_nom_opt_max ).dropna() + dc_load.loc[network.links.carrier != "DC"] = 0 + + return dc_load def plotting_colors(network): @@ -2373,9 +2375,12 @@ def plot_grid( ].sum() line_colors = calc_ac_loading(network, timesteps).abs() / rep_snapshots link_colors = calc_dc_loading(network, timesteps).abs() / rep_snapshots - link_widths = network.links.carrier - link_widths = link_widths.apply(lambda x: 10 if x == "DC" else 0) - line_widths = 10 + if ext_width is not False: + link_widths = link_colors.apply(lambda x: 10 + (x/ext_width) if x != 0 else 0) + line_widths = 10 + (line_colors / ext_width) + else: + link_widths = link_colors.apply(lambda x: 10 if x != 0 else 0) + line_widths = 10 label = "line loading in p.u." plot_background_grid(network, ax) # Only active flow direction is displayed! @@ -2387,9 +2392,10 @@ def plot_grid( / network.lines.s_nom / rep_snapshots ).values - flow.iloc[flow.index.get_level_values("component") == "Link"] = ( - calc_dc_loading(network, timesteps) / rep_snapshots - ).values + + dc_loading = calc_dc_loading(network, timesteps) / rep_snapshots + dc_loading.index = pd.MultiIndex.from_tuples([("Link", name) for name in dc_loading.index], names = ["component", "name"]) + flow.loc["Link", :] = dc_loading flow = flow[ (flow.index.get_level_values("component") == "Line") @@ -2422,7 +2428,7 @@ def plot_grid( plot_background_grid(all_network, ax) if ext_width is not False: line_widths = 0.5 + (line_colors / ext_width) - link_widths = 0.5 + (line_colors / ext_width) + link_widths = link_colors.apply(lambda x: 0.5 + x / ext_width if x != 0 else 0) else: dc_link = network.links.index[network.links.carrier == "DC"] link_widths = pd.Series(0, index=network.links.index) @@ -2439,13 +2445,19 @@ def plot_grid( plot_background_grid(all_network, ax) if ext_width is not False: line_widths = 0.5 + (line_colors / ext_width) - link_widths = 0.5 + (link_colors / ext_width) + link_widths = link_colors.apply(lambda x: 0.5 + x / ext_width if x != 0 else 0) + else: + dc_link = network.links.index[network.links.carrier == "DC"] + link_widths = pd.Series(0, index=network.links.index) + link_widths.loc[dc_link] = 2 elif line_colors == "q_flow_max": title = "Maximum reactive power flows" label = "flow in pu" line_colors = abs( network.lines_t.q0.abs().max() / (network.lines.s_nom) ) + if ext_width is not False: + line_widths = 0.5 + (line_colors / ext_width) link_colors = pd.Series(data=0, index=network.links.index) plot_background_grid(network, ax) elif line_colors == "dlr": From 55bfbdddc941dfc9f04c8a1be73f9801b238e798 Mon Sep 17 00:00:00 2001 From: CarlosEpia Date: Fri, 31 Mar 2023 11:57:47 +0200 Subject: [PATCH 057/136] Solve cropped legends when visualizing in IDE --- etrago/tools/plot.py | 1 + 1 file changed, 1 insertion(+) diff --git a/etrago/tools/plot.py b/etrago/tools/plot.py index 3d8cee30b..15f2d39a9 100644 --- a/etrago/tools/plot.py +++ b/etrago/tools/plot.py @@ -2782,6 +2782,7 @@ def plot_grid( if filename is None: if not isinstance(bus_sizes, (pd.Series, float)): logger.warning("Legend of bus sizes will change when zooming") + plt.tight_layout() plt.show() else: from matplotlib import pylab From 0e13a63ab2462a63aa196eedb82607f34b469a49 Mon Sep 17 00:00:00 2001 From: CarlosEpia Date: Fri, 31 Mar 2023 13:21:01 +0200 Subject: [PATCH 058/136] use Black and Isort --- etrago/tools/plot.py | 158 +++++++++++++++++++++++-------------------- 1 file changed, 86 insertions(+), 72 deletions(-) diff --git a/etrago/tools/plot.py b/etrago/tools/plot.py index 15f2d39a9..800db821d 100644 --- a/etrago/tools/plot.py +++ b/etrago/tools/plot.py @@ -31,7 +31,6 @@ from pyproj import Proj, transform from pypsa.plot import draw_map_cartopy from shapely.geometry import LineString, Point -from etrago.tools.execute import import_gen_from_links import geopandas as gpd import matplotlib import matplotlib.patches as mpatches @@ -39,6 +38,8 @@ import pandas as pd import tilemapbase +from etrago.tools.execute import import_gen_from_links + cartopy_present = True try: import cartopy.crs as ccrs @@ -2277,63 +2278,63 @@ def plot_grid( ): """Function that plots etrago.network and results for lines and buses - Parameters - ---------- - line_colors : str - Set static line color or attribute to plot e.g. 'expansion_abs' - Current options: - 'line_loading': mean line loading in p.u. in selected timesteps - 'v_nom': nominal voltage of lines - 'expansion_abs': absolute network expansion in MVA - 'expansion_rel': network expansion in p.u. of existing capacity - 'q_flow_max': maximal reactive flows - 'dlr': energy above nominal capacity - 'grey': plot all lines and DC linkd grey colored - bus_sizes : float, optional - Size of buses. The default is 0.001. - bus_colors : str, optional - Set static bus color or attribute to plot. The default is 'grey'. - Current options: - 'nodal_production_balance': net producer/consumer in - selected timeteps - 'storage_expansion': storage expansion per bus and technology. - 'h2_battery_storage_expansion': storage expansion per bus and - technology for underground and overground H2 and batteries. - 'storage_distribution': installed storage units per bus - 'gen_dist': dispatch per carrier in selected timesteps - 'PowerToH2': location and sizes of electrolizers - 'flexibility_usage': use of DSM and BEV charger - timesteps : array, optional - Timesteps consideredd in time depended plots. The default - is range(2). - osm : bool or dict, e.g. {'x': [1,20], 'y': [47, 56], 'zoom' : 6} - If not False, osm is set as background - with the following settings as dict: - 'x': array of two floats, x axis boundaries (lat) - 'y': array of two floats, y axis boundaries (long) - 'zoom' : resolution of osm. The default is False. - boundaries: array - Set fixed boundaries of heatmap axis. The default is None. - filename: str or None - Save figure in this direction. The default is None. - disaggregated : bool, optional - Choose if disaggregated network is shown. The default is False. - ext_min: float - Choose minimum relative line extension shown in plot in p.u.. - ext_width: float or bool - Choose if line_width respects line extension. Turn off with - 'False' or set linear factor to decremise extension line_width. - The default is False. - legend_entries : list, optional - Set the legends for buses to be plotted. The default is 'all'. - scaling_store_expansion : dict, optional - Set scaling values to be used per technology for the plots - storage_expansion and h2_battery_storage_expansion. The default is - {"H2": 50, "heat": 0.1, "battery": 10} - - Returns - ------- - None. + Parameters + ---------- + line_colors : str + Set static line color or attribute to plot e.g. 'expansion_abs' + Current options: + 'line_loading': mean line loading in p.u. in selected timesteps + 'v_nom': nominal voltage of lines + 'expansion_abs': absolute network expansion in MVA + 'expansion_rel': network expansion in p.u. of existing capacity + 'q_flow_max': maximal reactive flows + 'dlr': energy above nominal capacity + 'grey': plot all lines and DC linkd grey colored + bus_sizes : float, optional + Size of buses. The default is 0.001. + bus_colors : str, optional + Set static bus color or attribute to plot. The default is 'grey'. + Current options: + 'nodal_production_balance': net producer/consumer in + selected timeteps + 'storage_expansion': storage expansion per bus and technology. + 'h2_battery_storage_expansion': storage expansion per bus and + technology for underground and overground H2 and batteries. + 'storage_distribution': installed storage units per bus + 'gen_dist': dispatch per carrier in selected timesteps + 'PowerToH2': location and sizes of electrolizers + 'flexibility_usage': use of DSM and BEV charger + timesteps : array, optional + Timesteps consideredd in time depended plots. The default + is range(2). + osm : bool or dict, e.g. {'x': [1,20], 'y': [47, 56], 'zoom' : 6} + If not False, osm is set as background + with the following settings as dict: + 'x': array of two floats, x axis boundaries (lat) + 'y': array of two floats, y axis boundaries (long) + 'zoom' : resolution of osm. The default is False. + boundaries: array + Set fixed boundaries of heatmap axis. The default is None. + filename: str or None + Save figure in this direction. The default is None. + disaggregated : bool, optional + Choose if disaggregated network is shown. The default is False. + ext_min: float + Choose minimum relative line extension shown in plot in p.u.. + ext_width: float or bool + Choose if line_width respects line extension. Turn off with + 'False' or set linear factor to decremise extension line_width. + The default is False. + legend_entries : list, optional + Set the legends for buses to be plotted. The default is 'all'. + scaling_store_expansion : dict, optional + Set scaling values to be used per technology for the plots + storage_expansion and h2_battery_storage_expansion. The default is + {"H2": 50, "heat": 0.1, "battery": 10} + + Returns + ------- + None. """ # Choose network or disaggregated_network @@ -2376,7 +2377,9 @@ def plot_grid( line_colors = calc_ac_loading(network, timesteps).abs() / rep_snapshots link_colors = calc_dc_loading(network, timesteps).abs() / rep_snapshots if ext_width is not False: - link_widths = link_colors.apply(lambda x: 10 + (x/ext_width) if x != 0 else 0) + link_widths = link_colors.apply( + lambda x: 10 + (x / ext_width) if x != 0 else 0 + ) line_widths = 10 + (line_colors / ext_width) else: link_widths = link_colors.apply(lambda x: 10 if x != 0 else 0) @@ -2394,7 +2397,10 @@ def plot_grid( ).values dc_loading = calc_dc_loading(network, timesteps) / rep_snapshots - dc_loading.index = pd.MultiIndex.from_tuples([("Link", name) for name in dc_loading.index], names = ["component", "name"]) + dc_loading.index = pd.MultiIndex.from_tuples( + [("Link", name) for name in dc_loading.index], + names=["component", "name"], + ) flow.loc["Link", :] = dc_loading flow = flow[ @@ -2428,7 +2434,9 @@ def plot_grid( plot_background_grid(all_network, ax) if ext_width is not False: line_widths = 0.5 + (line_colors / ext_width) - link_widths = link_colors.apply(lambda x: 0.5 + x / ext_width if x != 0 else 0) + link_widths = link_colors.apply( + lambda x: 0.5 + x / ext_width if x != 0 else 0 + ) else: dc_link = network.links.index[network.links.carrier == "DC"] link_widths = pd.Series(0, index=network.links.index) @@ -2445,7 +2453,9 @@ def plot_grid( plot_background_grid(all_network, ax) if ext_width is not False: line_widths = 0.5 + (line_colors / ext_width) - link_widths = link_colors.apply(lambda x: 0.5 + x / ext_width if x != 0 else 0) + link_widths = link_colors.apply( + lambda x: 0.5 + x / ext_width if x != 0 else 0 + ) else: dc_link = network.links.index[network.links.carrier == "DC"] link_widths = pd.Series(0, index=network.links.index) @@ -2470,12 +2480,13 @@ def plot_grid( # calc min capacity per line in the given period network.lines.s_max_pu = network.lines_t.s_max_pu.min() network.lines.s_max_pu[network.lines.s_max_pu < 0.7] = 0.5 - network.lines.s_max_pu[(network.lines.s_max_pu > 0.5) & - (network.lines.s_max_pu < 0.70)] = (bcf_hv + bcf_ehv)/2 + network.lines.s_max_pu[ + (network.lines.s_max_pu > 0.5) & (network.lines.s_max_pu < 0.70) + ] = (bcf_hv + bcf_ehv) / 2 network.lines.s_max_pu[network.lines.s_max_pu >= 0.70] = 0.70 - line_loading = (network.lines_t.p0.mul( - 1 / (network.lines.s_nom_opt * network.lines.s_max_pu)).abs() - ) + line_loading = network.lines_t.p0.mul( + 1 / (network.lines.s_nom_opt * network.lines.s_max_pu) + ).abs() # keep only the capacity allowed by dlr line_loading = line_loading - 1 dlr_usage = ( @@ -2695,7 +2706,8 @@ def plot_grid( labels.append( f""" {round(max_value/bus_scaling/scaling_store_expansion[i]/ - 1000, 0).astype(int)} {bus_unit} """ + i + 1000, 0).astype(int)} {bus_unit} """ + + i ) else: max_value = bus_sizes.max() @@ -2729,7 +2741,7 @@ def plot_grid( positive = mpatches.Patch(color="green", label="generation") negative = mpatches.Patch(color="red", label="consumption") handles = [positive, negative] - + elif bus_legend == "PowerToH2": pth = mpatches.Patch(color="cyan", label="PowerToH2") handles = [pth] @@ -2786,11 +2798,13 @@ def plot_grid( plt.show() else: from matplotlib import pylab + if l3 == None: - pylab.savefig(filename, dpi=300, bbox_inches='tight') + pylab.savefig(filename, dpi=300, bbox_inches="tight") else: - pylab.savefig(filename, dpi=300, bbox_inches='tight', - bbox_extra_artists= [l3]) + pylab.savefig( + filename, dpi=300, bbox_inches="tight", bbox_extra_artists=[l3] + ) plt.close() From 18e85bdb0204b6a94ad37bcc6ffab30f3565013e Mon Sep 17 00:00:00 2001 From: CarlosEpia Date: Fri, 31 Mar 2023 13:28:40 +0200 Subject: [PATCH 059/136] applying Flake8 --- etrago/tools/plot.py | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/etrago/tools/plot.py b/etrago/tools/plot.py index 800db821d..545774cd6 100644 --- a/etrago/tools/plot.py +++ b/etrago/tools/plot.py @@ -23,7 +23,6 @@ """ from math import sqrt import logging -import os from matplotlib import pyplot as plt from matplotlib.legend_handler import HandlerPatch @@ -49,9 +48,6 @@ logger = logging.getLogger(__name__) -if "READTHEDOCS" not in os.environ: - from geoalchemy2.shape import to_shape - __copyright__ = ( "Flensburg University of Applied Sciences, " "Europa-Universität Flensburg, " @@ -2799,7 +2795,7 @@ def plot_grid( else: from matplotlib import pylab - if l3 == None: + if l3 is None: pylab.savefig(filename, dpi=300, bbox_inches="tight") else: pylab.savefig( From 073700a38e9800d1dd93ffce50d38534c87b7f37 Mon Sep 17 00:00:00 2001 From: CarlosEpia Date: Fri, 31 Mar 2023 15:50:20 +0200 Subject: [PATCH 060/136] import own functions checking READTHEDOCS --- etrago/tools/plot.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/etrago/tools/plot.py b/etrago/tools/plot.py index 545774cd6..c27f0cdaa 100644 --- a/etrago/tools/plot.py +++ b/etrago/tools/plot.py @@ -23,6 +23,7 @@ """ from math import sqrt import logging +import os from matplotlib import pyplot as plt from matplotlib.legend_handler import HandlerPatch @@ -37,7 +38,8 @@ import pandas as pd import tilemapbase -from etrago.tools.execute import import_gen_from_links +if "READTHEDOCS" not in os.environ: + from etrago.tools.execute import import_gen_from_links cartopy_present = True try: From 9c3fbc1dadd85e073424ce0f4f58f5276c41114a Mon Sep 17 00:00:00 2001 From: CarlosEpia Date: Wed, 5 Apr 2023 14:07:03 +0200 Subject: [PATCH 061/136] adjust dlr plot --- etrago/tools/plot.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/etrago/tools/plot.py b/etrago/tools/plot.py index c27f0cdaa..274de4dfa 100644 --- a/etrago/tools/plot.py +++ b/etrago/tools/plot.py @@ -2470,7 +2470,7 @@ def plot_grid( plot_background_grid(network, ax) elif line_colors == "dlr": title = "Dynamic line rating" - label = "MWh above nominal capacity" + label = "TWh above nominal capacity" plot_background_grid(network, ax) # Extract branch_capacity_factors bcf_hv = self.args["branch_capacity_factor"]["HV"] @@ -2493,10 +2493,13 @@ def plot_grid( .mul(network.snapshot_weightings.generators, axis=0) .sum() ) - dlr_usage = dlr_usage * network.lines.s_nom * network.lines.s_max_pu + dlr_usage = ( + dlr_usage * network.lines.s_nom * network.lines.s_max_pu / 1000000 + ) + dlr_usage = dlr_usage.round(decimals=0) line_colors = dlr_usage if ext_width is not False: - line_widths = 0.5 + (line_colors / ext_width) + line_widths = 0.2 + (line_colors / ext_width) link_colors = pd.Series(data=0, index=network.links.index) elif line_colors == "grey": From 791c76b0b1b58c1e790976447d4c1b73bd712874 Mon Sep 17 00:00:00 2001 From: CarlosEpia Date: Mon, 17 Jul 2023 14:36:30 +0200 Subject: [PATCH 062/136] use p_opt - p_min in storage exp --- etrago/tools/plot.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/etrago/tools/plot.py b/etrago/tools/plot.py index 274de4dfa..2da438368 100644 --- a/etrago/tools/plot.py +++ b/etrago/tools/plot.py @@ -830,8 +830,10 @@ def calc_storage_expansion_per_bus( batteries = network.storage_units[ network.storage_units.carrier == "battery" ] + #breakpoint() battery_distribution = ( - network.storage_units.p_nom_opt[batteries.index] + (network.storage_units.p_nom_opt[batteries.index] - + network.storage_units.p_nom_min[batteries.index]) .groupby(network.storage_units.bus) .sum() .reindex(network.buses.index, fill_value=0.0) From 2a566a760ebe044135284f8856eb2e49e3361306 Mon Sep 17 00:00:00 2001 From: CarlosEpia Date: Mon, 17 Jul 2023 16:00:37 +0200 Subject: [PATCH 063/136] dont color not expanded lines --- etrago/tools/plot.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/etrago/tools/plot.py b/etrago/tools/plot.py index 2da438368..4ce1883a4 100644 --- a/etrago/tools/plot.py +++ b/etrago/tools/plot.py @@ -2432,18 +2432,21 @@ def plot_grid( network, method="abs", ext_min=ext_min ) plot_background_grid(all_network, ax) + if ext_width is not False: - line_widths = 0.5 + (line_colors / ext_width) + line_widths = (line_colors / ext_width) link_widths = link_colors.apply( - lambda x: 0.5 + x / ext_width if x != 0 else 0 + lambda x: x / ext_width if x != 0 else 0 ) else: dc_link = network.links.index[network.links.carrier == "DC"] link_widths = pd.Series(0, index=network.links.index) - link_widths.loc[dc_link] = 2 + link_widths.loc[dc_link] = 1.5 + line_widths = line_colors.apply(lambda x: 1.5 if x != 0 else 0) link_colors = link_colors.mul(1e-3) line_colors = line_colors.mul(1e-3) + elif line_colors == "expansion_rel": title = "Network expansion" label = "network expansion in %" From 6999e9cb8ea2d31423e4ec690ac44d1af8dc0f56 Mon Sep 17 00:00:00 2001 From: CarlosEpia Date: Mon, 17 Jul 2023 17:16:16 +0200 Subject: [PATCH 064/136] use 1 decimal in color bar --- etrago/tools/plot.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/etrago/tools/plot.py b/etrago/tools/plot.py index 4ce1883a4..fa648ee0a 100644 --- a/etrago/tools/plot.py +++ b/etrago/tools/plot.py @@ -2776,12 +2776,12 @@ def plot_grid( # Set fixed boundaries if selected in parameters if not boundaries: boundaries = [ - min(line_colors.min(), link_colors.min()), - max(line_colors.max(), link_colors.max()), + min(round(line_colors.min(),1), round(link_colors.min(),1)), + max(round(line_colors.max()), round(link_colors.max())), ] # Create ticks for legend - v = np.linspace(boundaries[0], boundaries[1], 101) + v = [round(x,1) for x in np.linspace(boundaries[0], boundaries[1], 101)] for l_collection in ll: l_collection.set_clim(boundaries[0], boundaries[1]) From 5ea7a7f2a8b390028e732f07354bd0bc92deb87d Mon Sep 17 00:00:00 2001 From: ClaraBuettner Date: Thu, 14 Sep 2023 15:40:03 +0200 Subject: [PATCH 065/136] Apply black --- etrago/cluster/electrical.py | 21 ++- etrago/cluster/gas.py | 4 +- etrago/tools/calc_results.py | 324 +++++++++++++++++++++-------------- etrago/tools/constraints.py | 1 - 4 files changed, 207 insertions(+), 143 deletions(-) diff --git a/etrago/cluster/electrical.py b/etrago/cluster/electrical.py index e46d6b0ff..a791e8642 100755 --- a/etrago/cluster/electrical.py +++ b/etrago/cluster/electrical.py @@ -69,7 +69,7 @@ def _leading(busmap, df): """ - Returns a function that computes the leading bus_id for a given mapped + Returns a function that computes the leading bus_id for a given mapped list of buses. Parameters @@ -95,7 +95,7 @@ def leader(x): def adjust_no_electric_network(etrago, busmap, cluster_met): """ - Adjusts the non-electric network based on the electrical network + Adjusts the non-electric network based on the electrical network (esp. eHV network), adds the gas buses to the busmap, and creates the new buses for the non-electric network. @@ -117,7 +117,7 @@ def adjust_no_electric_network(etrago, busmap, cluster_met): """ network = etrago.network - # network2 is supposed to contain all the not electrical or gas buses + # network2 is supposed to contain all the not electrical or gas buses # and links network2 = network.copy(with_time=False) network2.buses = network2.buses[ @@ -386,9 +386,9 @@ def cluster_on_extra_high_voltage(etrago, busmap, with_time=True): def delete_ehv_buses_no_lines(network): """ - When there are AC buses totally isolated, this function deletes them in + When there are AC buses totally isolated, this function deletes them in order to make possible the creation of busmaps based on electrical - connections and other purposes. Additionally, it throws a warning to + connections and other purposes. Additionally, it throws a warning to inform the user in case that any correction should be done. Parameters @@ -769,9 +769,9 @@ def preprocessing(etrago): if settings["method"] == "kmedoids-dijkstra": lines_col = network_elec.lines.columns - # The Dijkstra clustering works using the shortest electrical path + # The Dijkstra clustering works using the shortest electrical path # between buses. In some cases, a bus has just DC connections, which - # are considered links. Therefore it is necessary to include + # are considered links. Therefore it is necessary to include # temporarily the DC links into the lines table. dc = network.links[network.links.carrier == "DC"] str1 = "DC_" @@ -936,7 +936,6 @@ def weighting_for_scenario(network, save=None): """ def calc_availability_factor(gen): - """ Calculate the availability factor for a given generator. @@ -952,10 +951,10 @@ def calc_availability_factor(gen): Notes ----- - Availability factor is defined as the ratio of the average power - output of the generator over the maximum power output capacity of + Availability factor is defined as the ratio of the average power + output of the generator over the maximum power output capacity of the generator. If the generator is time-dependent, its average power - output is calculated using the `network.generators_t` DataFrame. + output is calculated using the `network.generators_t` DataFrame. Otherwise, its availability factor is obtained from the `fixed_capacity_fac` dictionary, which contains pre-defined factors for fixed capacity generators. If the generator's availability factor diff --git a/etrago/cluster/gas.py b/etrago/cluster/gas.py index f0dfcbf42..7a6d0f15d 100644 --- a/etrago/cluster/gas.py +++ b/etrago/cluster/gas.py @@ -344,8 +344,8 @@ def gas_postprocessing(etrago, busmap, medoid_idx=None): + str(settings["n_clusters_gas"]) + "_result.csv" ) - - if 'H2' in etrago.network.buses.carrier.unique(): + + if "H2" in etrago.network.buses.carrier.unique(): busmap = get_h2_clusters(etrago, busmap) # Add all other buses to busmap diff --git a/etrago/tools/calc_results.py b/etrago/tools/calc_results.py index fca593a97..f3e3338dd 100755 --- a/etrago/tools/calc_results.py +++ b/etrago/tools/calc_results.py @@ -22,7 +22,8 @@ calc_results.py defines methods to calculate results of eTraGo """ import os -if 'READTHEDOCS' not in os.environ: + +if "READTHEDOCS" not in os.environ: import time import logging @@ -31,16 +32,18 @@ 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 +51,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 +70,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 +88,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 +113,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 +147,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 +190,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,21 +210,35 @@ 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 + """Function that calculates main results of grid optimization and adds them to Etrago object. Returns @@ -218,99 +246,137 @@ def calc_etrago_results(self): 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 = 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." # system 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'] + 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/constraints.py b/etrago/tools/constraints.py index 453b076d4..da6911fd8 100755 --- a/etrago/tools/constraints.py +++ b/etrago/tools/constraints.py @@ -2721,7 +2721,6 @@ def disaggregation_sto_soc(m, s, h): def split_dispatch_disaggregation_constraints_nmp(self, n, sns): - print("TODO") # TODO: implementieren From e5db91bbd70462cc2a68c4b4b19df7676e243d6b Mon Sep 17 00:00:00 2001 From: ClaraBuettner Date: Thu, 14 Sep 2023 15:42:09 +0200 Subject: [PATCH 066/136] Apply isort --- etrago/cluster/snapshot.py | 3 ++- etrago/tools/calc_results.py | 4 ++-- etrago/tools/extendable.py | 10 ++++------ etrago/tools/plot.py | 2 +- 4 files changed, 9 insertions(+), 10 deletions(-) diff --git a/etrago/cluster/snapshot.py b/etrago/cluster/snapshot.py index fbbd1f68e..8afec2f1a 100644 --- a/etrago/cluster/snapshot.py +++ b/etrago/cluster/snapshot.py @@ -26,9 +26,10 @@ ( https://github.com/FZJ-IEK3-VSA/tsam ) developed by Leander Kotzur et al. """ -import pandas as pd import os +import pandas as pd + if "READTHEDOCS" not in os.environ: import pyomo.environ as po import tsam.timeseriesaggregation as tsam diff --git a/etrago/tools/calc_results.py b/etrago/tools/calc_results.py index f3e3338dd..aa6c6cb21 100755 --- a/etrago/tools/calc_results.py +++ b/etrago/tools/calc_results.py @@ -24,11 +24,11 @@ import os if "READTHEDOCS" not in os.environ: - import time import logging + import time - import pandas as pd import numpy as np + import pandas as pd logger = logging.getLogger(__name__) diff --git a/etrago/tools/extendable.py b/etrago/tools/extendable.py index 6bb566585..ba4be5173 100644 --- a/etrago/tools/extendable.py +++ b/etrago/tools/extendable.py @@ -21,16 +21,14 @@ """ Extendable.py defines function to set PyPSA components extendable. """ -from etrago.tools.utilities import convert_capital_costs, find_snapshots - -from etrago.cluster.snapshot import snapshot_clustering +from math import sqrt +import time import numpy as np import pandas as pd -import time -from math import sqrt - +from etrago.cluster.snapshot import snapshot_clustering +from etrago.tools.utilities import convert_capital_costs, find_snapshots __copyright__ = ( "Flensburg University of Applied Sciences, " diff --git a/etrago/tools/plot.py b/etrago/tools/plot.py index d3ae2068a..cc290c63e 100644 --- a/etrago/tools/plot.py +++ b/etrago/tools/plot.py @@ -45,9 +45,9 @@ if "READTHEDOCS" not in os.environ: from geoalchemy2.shape import to_shape - import geopandas as gpd from pyproj import Proj, transform from shapely.geometry import LineString, MultiPoint, Point, Polygon + import geopandas as gpd import tilemapbase __copyright__ = ( From 4a9bad71fce988aa33bbad9938209dfe18fb0305 Mon Sep 17 00:00:00 2001 From: ClaraBuettner Date: Thu, 14 Sep 2023 15:45:50 +0200 Subject: [PATCH 067/136] Add updated files to noxfile --- noxfile.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/noxfile.py b/noxfile.py index 9764d83de..95039d5d5 100644 --- a/noxfile.py +++ b/noxfile.py @@ -4,7 +4,15 @@ import nox cleaned = [ + "etrago/appl.py", + "etrago/cluster/electrical.py", "etrago/cluster/disaggregation.py", + "etrago/cluster/gas.py", + "etrago/cluster/snapshot.py", + "etrago/cluster/spatial.py", + "etrago/tools/extendable.py", + "etrago/tools/execute.py", + "etrago/tools/io.py", "etrago/tools/network.py", "etrago/tools/utilities.py", "noxfile.py", From b115f51f2d44e523b05549a24a5f0802c8c2a1fd Mon Sep 17 00:00:00 2001 From: ClaraBuettner Date: Thu, 14 Sep 2023 15:49:31 +0200 Subject: [PATCH 068/136] Sort list of files alphabetically --- noxfile.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/noxfile.py b/noxfile.py index 95039d5d5..fa2cf9c7a 100644 --- a/noxfile.py +++ b/noxfile.py @@ -4,14 +4,14 @@ import nox cleaned = [ - "etrago/appl.py", - "etrago/cluster/electrical.py", + "etrago/appl.py", "etrago/cluster/disaggregation.py", + "etrago/cluster/electrical.py", "etrago/cluster/gas.py", "etrago/cluster/snapshot.py", "etrago/cluster/spatial.py", - "etrago/tools/extendable.py", "etrago/tools/execute.py", + "etrago/tools/extendable.py", "etrago/tools/io.py", "etrago/tools/network.py", "etrago/tools/utilities.py", From b68186724c08bc1aa5a13ade5698501084e8cceb Mon Sep 17 00:00:00 2001 From: ClaraBuettner Date: Thu, 14 Sep 2023 16:07:42 +0200 Subject: [PATCH 069/136] Changes in docstring of run_etrago for flake8 --- etrago/appl.py | 325 ++++++++++++++++++++++++++----------------------- 1 file changed, 175 insertions(+), 150 deletions(-) diff --git a/etrago/appl.py b/etrago/appl.py index cdf3f3ad8..b9e94eee8 100644 --- a/etrago/appl.py +++ b/etrago/appl.py @@ -169,8 +169,8 @@ def run_etrago(args, json_path): Parameters ---------- db : str - Name of Database session setting stored in *config.ini* of *.egoio*, e.g. - ``'oedb'``. + Name of Database session setting stored in *config.ini* of *.egoio*, + e.g. ``'oedb'``. gridversion : None or str Name of the data version number of oedb: state ``'None'`` for model_draft (sand-box) or an explicit version number @@ -183,51 +183,51 @@ def run_etrago(args, json_path): Type of optimization, currently only "lopf". Default: "lopf". * "n_iter" : int In case of extendable lines, several LOPFs have to be performed. - You can either set "n_iter" and specify a fixed number of iterations - or set "threshold" and specify a threshold of the objective function as - abort criteria of the iterative optimization. + You can either set "n_iter" and specify a fixed number of + iterations or set "threshold" and specify a threshold of the + objective function as abort criteria of the iterative optimization. Default: 4. * "threshold" : int In case of extendable lines, several LOPFs have to be performed. - You can either set "n_iter" and specify a fixed number of iterations - or set "threshold" and specify a threshold of the objective function as - abort criteria of the iterative optimization. Per default, "n_iter" of 4 - is set. + You can either set "n_iter" and specify a fixed number of + iterations or set "threshold" and specify a threshold of the + objective function as abort criteria of the iterative optimization. + Per default, "n_iter" of 4 is set. * "pyomo" : bool Set to True, if pyomo is used for model building. Set to False for big optimization problems - currently only possible when solver is "gurobi". pf_post_lopf : dict - Settings for option to run a non-linear power flow (PF) directly after the - linear optimal power flow (LOPF), and thus the dispatch optimisation, has - finished. + Settings for option to run a non-linear power flow (PF) directly after + the linear optimal power flow (LOPF), and thus the dispatch + optimisation, has finished. The provided dictionary can have the following entries: * "active" : bool If True, a PF is performed after the LOPF. Default: True. * "add_foreign_lopf" : bool - If foreign lines are modeled as DC-links (see parameter `foreign_lines`), - results of the LOPF can be added by setting "add_foreign_lopf" to True. - Default: True. + If foreign lines are modeled as DC-links (see parameter + `foreign_lines`), results of the LOPF can be added by setting + "add_foreign_lopf" to True. Default: True. * "q_allocation" : bool - Allocate reactive power to all generators at the same bus either by "p_nom" - or "p". + Allocate reactive power to all generators at the same bus either + by "p_nom" or "p". Default: "p_nom". start_snapshot : int Start hour of the scenario year to be calculated. Default: 1. end_snapshot : int - End hour of the scenario year to be calculated. If snapshot clustering is used - (see parameter `snapshot_clustering`), the selected snapshots should cover - the number of periods / segments. Default: 2. + End hour of the scenario year to be calculated. If snapshot clustering + is used (see parameter `snapshot_clustering`), the selected snapshots + should cover the number of periods / segments. Default: 2. solver : str Choose your preferred solver. Current options: "glpk" (open-source), "cplex" or "gurobi". Default: "gurobi". solver_options : dict Choose settings of solver to improve simulation time and result. - Options are described in documentation of chosen solver. Per default, the - following dictionary is set: + Options are described in documentation of chosen solver. Per default, + the following dictionary is set: { "BarConvTol": 1.0e-5, @@ -238,13 +238,13 @@ def run_etrago(args, json_path): "threads": 4, } - Make sure to reset or adapt these settings when using another solver! Otherwise, - you may run into errors. + Make sure to reset or adapt these settings when using another solver! + Otherwise, you may run into errors. model_formulation : str Choose formulation of pyomo-model. Current options are: "angles", "cycles", "kirchhoff", "ptdf". - "angels" works best for small networks, while "kirchhoff" works best for larger - networks. + "angels" works best for small networks, while "kirchhoff" works best + for larger networks. Default: "kirchhoff". scn_name : str Choose your scenario. Currently, there are two different @@ -291,8 +291,8 @@ def run_etrago(args, json_path): False or '/path/tofolder'. Default: False. extendable : dict - Choose components you want to optimize and set upper bounds for grid expansion. - The provided dictionary can have the following entries: + Choose components you want to optimize and set upper bounds for grid + expansion. The provided dictionary can have the following entries: * "extendable_components" : list(str) The list defines a set of components to optimize. @@ -300,18 +300,21 @@ def run_etrago(args, json_path): The most important possibilities: * 'as_in_db' - leaves everything as it is defined in the data coming from the database + leaves everything as it is defined in the data coming from the + database * 'network' - set all lines, links and transformers in electrical grid extendable + set all lines, links and transformers in electrical grid + extendable * 'german_network' set lines and transformers in German electrical grid extendable * 'foreign_network' - set foreign lines and transformers in electrical grid extendable + set foreign lines and transformers in electrical grid + extendable * 'transformers' set all transformers extendable * 'storages' / 'stores' - allow to install extendable storages (unlimited in size) at each grid - node in order to meet the flexibility demand + allow to install extendable storages (unlimited in size) at + each grid node in order to meet the flexibility demand Default: "as_in_db". @@ -319,15 +322,16 @@ def run_etrago(args, json_path): Dictionary can have the following entries: * 'grid_max_D' - Upper bounds for electrical grid expansion can be defined for lines in - Germany relative to the existing capacity. Alternatively, - 'grid_max_abs_D' can be used. Per default, this is set to None and - 'grid_max_abs_D' is set. + Upper bounds for electrical grid expansion can be defined for + lines in Germany relative to the existing capacity. + Alternatively, 'grid_max_abs_D' can be used. Per default, this + is set to None and 'grid_max_abs_D' is set. * 'grid_max_abs_D' - Upper bounds for electrical grid expansion can be defined for lines in - Germany as absolute maximum capacities between two electrical buses - per voltage level. Per default the following dictionary is set: + Upper bounds for electrical grid expansion can be defined for + lines in Germany as absolute maximum capacities between two + electrical buses per voltage level. Per default the following + dictionary is set: { "380": {"i": 1020, "wires": 4, "circuits": 4}, @@ -336,13 +340,14 @@ def run_etrago(args, json_path): "dc": 0, } * 'grid_max_foreign' - Upper bounds for border-crossing electrical lines can be defined - relative to the existing capacity. Alternatively, 'grid_max_abs_foreign' - can be set. + Upper bounds for border-crossing electrical lines can be + defined relative to the existing capacity. Alternatively, + 'grid_max_abs_foreign' can be set. Default: 4. * 'grid_max_abs_foreign' - Upper bounds for border-crossing electrical lines can be defined equally - to 'grid_max_abs_D' as absolute capacity per voltage level. + Upper bounds for border-crossing electrical lines can be + defined equally to 'grid_max_abs_D' as absolute capacity per + voltage level. Default: None. generator_noise : bool or int @@ -385,8 +390,8 @@ def run_etrago(args, json_path): substation, taking into account the shortest distance on power lines. Default: False. network_clustering : dict - Choose if you want to apply a clustering of all network buses and specify - settings. + Choose if you want to apply a clustering of all network buses and + specify settings. The provided dictionary can have the following entries: * "active" : bool @@ -394,75 +399,81 @@ def run_etrago(args, json_path): and the gas buses are clustered down to``'n_clusters_gas'``. Default: True. * "method" : str - Method used for AC clustering. You can choose between two clustering - methods: + Method used for AC clustering. You can choose between two + clustering methods: * "kmeans": considers geographical locations of buses - * "kmedoids-dijkstra": considers electrical distances between buses + * "kmedoids-dijkstra": considers electrical distances between + buses Default: "kmedoids-dijkstra". * "n_clusters_AC" : int - Defines total number of resulting AC nodes including DE and foreign nodes - if `cluster_foreign_AC` is set to True, otherwise only DE nodes. + Defines total number of resulting AC nodes including DE and foreign + nodes if `cluster_foreign_AC` is set to True, otherwise only DE + nodes. Default: 30. * "cluster_foreign_AC" : bool If set to False, the AC buses outside Germany are not clustered - and the buses inside Germany are clustered to complete ``'n_clusters_AC'``. - If set to True, foreign AC buses are clustered as well and included - in number of clusters specified through ``'n_clusters_AC'``. + and the buses inside Germany are clustered to complete + ``'n_clusters_AC'``. If set to True, foreign AC buses are clustered + as well and included in number of clusters specified through + ``'n_clusters_AC'``. Default: False. * "method_gas" : str - Method used for gas clustering. You can choose between two clustering - methods: + Method used for gas clustering. You can choose between two + clustering methods: * "kmeans": considers geographical locations of buses - * "kmedoids-dijkstra": considers 'electrical' distances between buses + * "kmedoids-dijkstra": considers 'electrical' distances between + buses Default: "kmedoids-dijkstra". * "n_clusters_gas" : int - Defines total number of resulting CH4 nodes including DE and foreign nodes - if `cluster_foreign_gas` is set to True, otherwise only DE nodes. + Defines total number of resulting CH4 nodes including DE and + foreign nodes if `cluster_foreign_gas` is set to True, otherwise + only DE nodes. Default: 17. * "cluster_foreign_gas" : bool If set to False, the gas buses outside Germany are not clustered - and the buses inside Germany are clustered to complete ``'n_clusters_gas'``. - If set to True, foreign gas buses are clustered as well and included - in number of clusters specified through ``'n_clusters_gas'``. + and the buses inside Germany are clustered to complete + ``'n_clusters_gas'``. If set to True, foreign gas buses are + clustered as well and included in number of clusters specified + through ``'n_clusters_gas'``. Default: False. * "k_elec_busmap" : bool or str - With this option you can load cluster coordinates from a previous AC - clustering run. Options are False, in which case no previous busmap is - loaded, and path/to/busmap.csv in which case the busmap is loaded from the - specified file. Please note, that when a path is provided, the set number - of clusters will be ignored. + With this option you can load cluster coordinates from a previous + AC clustering run. Options are False, in which case no previous + busmap is loaded, and path/to/busmap.csv in which case the busmap + is loaded from the specified file. Please note, that when a path is + provided, the set number of clusters will be ignored. Default: False. * "k_gas_busmap" : bool or str - With this option you can load cluster coordinates from a previous gas - clustering run. Options are False, in which case no previous busmap is - loaded, and path/to/busmap.csv in which case the busmap is loaded from the - specified file. Please note, that when a path is provided, the set number - of clusters will be ignored. + With this option you can load cluster coordinates from a previous + gas clustering run. Options are False, in which case no previous + busmap is loaded, and path/to/busmap.csv in which case the busmap + is loaded from the specified file. Please note, that when a path is + provided, the set number of clusters will be ignored. Default: False. * "bus_weight_fromcsv" : None or str - In general, the weighting of AC buses takes place considering generation and - load at each node. With this option, you can load an own weighting for - the AC buses by providing a path to a csv file. If None, weighting is - conducted as described above. + In general, the weighting of AC buses takes place considering + generation and load at each node. With this option, you can load an + own weighting for the AC buses by providing a path to a csv file. + If None, weighting is conducted as described above. Default: None. * "bus_weight_tocsv" : None or str - Specifies whether to store the weighting of AC buses to csv or not. If - None, it is not stored. Otherwise, it is stored to the provided + Specifies whether to store the weighting of AC buses to csv or not. + If None, it is not stored. Otherwise, it is stored to the provided path/to/bus_weight.csv. Default: None. * "gas_weight_fromcsv" : None or str - In general, the weighting of CH4 nodes takes place considering generation - and load at each node, as well as non-transport capacities at each node. - With this option, you can load an own weighting for the CH4 buses by - providing a path to a csv file. If None, weighting is conducted as - described above. + In general, the weighting of CH4 nodes takes place considering + generation and load at each node, as well as non-transport + capacities at each node. With this option, you can load an own + weighting for the CH4 buses by providing a path to a csv file. If + None, weighting is conducted as described above. Default: None. * "gas_weight_tocsv" : None or str - Specifies whether to store the weighting of gas buses to csv or not. If - None, it is not stored. Otherwise, it is stored to the provided - path/to/gas_bus_weight.csv. + Specifies whether to store the weighting of gas buses to csv or + not. If None, it is not stored. Otherwise, it is stored to the + provided path/to/gas_bus_weight.csv. Default: None. * "line_length_factor" : float Defines the factor to multiply the crow-flies distance @@ -480,59 +491,62 @@ def run_etrago(args, json_path): * "random_state" : int Random state for replicability of clustering results. Default: 42. * "n_init" : int - Affects clustering algorithm, only change when necessary! Documentation - and possible settings are described in sklearn-package - (sklearn/cluster/kmeans.py). + Affects clustering algorithm, only change when necessary! + Documentation and possible settings are described in + sklearn-package (sklearn/cluster/kmeans.py). Default: 10. * "max_iter" : int - Affects clustering algorithm, only change when necessary! Documentation - and possible settings are described in sklearn-package - (sklearn/cluster/kmeans.py). + Affects clustering algorithm, only change when necessary! + Documentation and possible settings are described in + sklearn-package (sklearn/cluster/kmeans.py). Default: 100. * "tol" : float - Affects clustering algorithm, only change when necessary! Documentation - and possible settings are described in sklearn-package - (sklearn/cluster/kmeans.py). + Affects clustering algorithm, only change when necessary! + Documentation and possible settings are described in + sklearn-package (sklearn/cluster/kmeans.py). Default: 1e-6. * "CPU_cores" : int or str - Number of cores used in clustering. Specify a concrete number or "max" to - use all cores available. + Number of cores used in clustering. Specify a concrete number or + "max" to use all cores available. Default: 4. sector_coupled_clustering : dict - Choose if you want to apply a clustering of sector coupled carriers, such - as central_heat, and specify settings. + Choose if you want to apply a clustering of sector coupled carriers, + such as central_heat, and specify settings. The provided dictionary can have the following entries: * "active" : bool - State if you want to apply clustering of sector coupled carriers, such - as central_heat. + State if you want to apply clustering of sector coupled carriers, + such as central_heat. Default: True. * "carrier_data" : dict[str, dict] - Keys of the dictionary specify carriers affected by sector coupling, e.g. - "central_heat". The corresponding dictionaries specify, how the carrier - should be clustered. This dictionary must contain the following entries: + Keys of the dictionary specify carriers affected by sector + coupling, e.g. "central_heat". The corresponding dictionaries + specify, how the carrier should be clustered. This dictionary must + contain the following entries: * "base" : list(str) - The approach bases on already clustered buses (AC and CH4) and builds - clusters around the topology of those buses. With this option, you can - specify the carriers to use as base. See `strategy` for more - information. + The approach bases on already clustered buses (AC and CH4) and + builds clusters around the topology of those buses. With this + option, you can specify the carriers to use as base. See + `strategy` for more information. * "strategy" : str Strategy to use in the clustering. Possible options are: * "consecutive" - This strategy clusters around the buses of the first carrier in the - `'base'`` list. The links to other buses are preserved. All buses, - that have no connection to the first carrier will then be clustered - around the buses of the second carrier in the list. + This strategy clusters around the buses of the first + carrier in the `'base'`` list. The links to other buses are + preserved. All buses, that have no connection to the first + carrier will then be clustered around the buses of the + second carrier in the list. * "simultaneous" - This strategy looks for links connecting the buses of the carriers - in the ``'base'`` list and aggregates buses in case they have the - same set of links connected. For example, a heat bus connected to - CH4 via gas boiler and to AC via heat pump will only form a cluster - with other buses, if these have the same links to - the same clusters of CH4 and AC. + This strategy looks for links connecting the buses of the + carriers in the ``'base'`` list and aggregates buses in + case they have the same set of links connected. For + example, a heat bus connected to CH4 via gas boiler and to + AC via heat pump will only form a cluster with other buses, + if these have the same links to the same clusters of CH4 + and AC. Per default, the following dictionary is set: { @@ -547,61 +561,68 @@ def run_etrago(args, json_path): method you want to use for the spatial disaggregation. Only possible option is currently "uniform". snapshot_clustering : dict - State if you want to apply a temporal clustering and run the optimization - only on a subset of snapshot periods, and specify settings. - The provided dictionary can have the following entries: + State if you want to apply a temporal clustering and run the + optimization only on a subset of snapshot periods, and specify + settings. The provided dictionary can have the following entries: * "active" : bool - Choose, if clustering is activated or not. If True, it is activated. + Choose, if clustering is activated or not. If True, it is + activated. Default: False. * "method" : str - Method to apply. Possible options are "typical_periods" and "segmentation". + Method to apply. Possible options are "typical_periods" and + "segmentation". Default: "segmentation". * "extreme_periods" : None or str - Method used to consider extreme snapshots (time steps with extreme residual - load) in reduced timeseries. + Method used to consider extreme snapshots (time steps with extreme + residual load) in reduced timeseries. Possible options are None, "append", "new_cluster_center", and - "replace_cluster_center". The default is None, in which case extreme periods - are not considered. + "replace_cluster_center". The default is None, in which case + extreme periods are not considered. * "how" : str Definition of period in case `method` is set to "typical_periods". - Possible options are "daily", "weekly", and "monthly". Default: "daily". + Possible options are "daily", "weekly", and "monthly". + Default: "daily". * "storage_constraints" : str - Defines additional constraints for storage units in case `method` is set to - "typical_periods". Possible options are "daily_bounds", "soc_constraints" - and "soc_constraints_simplified". Default: "soc_constraints". + Defines additional constraints for storage units in case `method` + is set to "typical_periods". Possible options are "daily_bounds", + "soc_constraints" and "soc_constraints_simplified". + Default: "soc_constraints". * "n_clusters" : int - Number of clusters in case `method` is set to "typical_periods". Default: 5. + Number of clusters in case `method` is set to "typical_periods". + Default: 5. * "n_segments" : int - Number of segments in case `method` is set to "segmentation". Default: 5. + Number of segments in case `method` is set to "segmentation". + Default: 5. skip_snapshots : bool or int State None, if you want to use all time steps, or provide a number, if you only want to consider every n-th timestep to reduce temporal complexity. Default: 5. temporal_disaggregation : dict - State if you want to apply a second LOPF considering dispatch only (no capacity - optimization) to disaggregate the dispatch to the whole temporal complexity. - Be aware that a load shedding will be applied in this optimization. - The provided dictionary must have the following entries: + State if you want to apply a second LOPF considering dispatch only + (no capacity optimization) to disaggregate the dispatch to the whole + temporal complexity. Be aware that a load shedding will be applied in + this optimization. The provided dictionary must have the following + entries: * "active" : bool - Choose, if temporal disaggregation is activated or not. If True, it is - activated. + Choose, if temporal disaggregation is activated or not. If True, + it is activated. Default: False. * "no_slices" : int - With "no_slices" the optimization problem will be calculated as a given - number of sub-problems while using some information on the state of charge - of storage units and stores from the former optimization (at the moment - only possible with skip_snapshots and extra_functionalities are - disregarded). + With "no_slices" the optimization problem will be calculated as a + given number of sub-problems while using some information on the + state of charge of storage units and stores from the former + optimization (at the moment only possible with skip_snapshots and + extra_functionalities are disregarded). Default: 8. branch_capacity_factor : dict[str, float] Add a factor here if you want to globally change line capacities - (e.g. to "consider" an (n-1) criterion or for debugging purposes). The factor - specifies the p.u. branch rating, e.g. 0.5 to allow half the line capacity. - Per default, it is set to {'HV': 0.5, 'eHV' : 0.7}. + (e.g. to "consider" an (n-1) criterion or for debugging purposes). + The factor specifies the p.u. branch rating, e.g. 0.5 to allow half the + line capacity. Per default, it is set to {'HV': 0.5, 'eHV' : 0.7}. load_shedding : bool State here if you want to make use of the load shedding function which is helpful when debugging: a very expensive generator is set to each @@ -673,8 +694,12 @@ def run_etrago(args, json_path): etrago.session.close() # plots: more in tools/plot.py # make a line loading plot - # etrago.plot_grid(line_colors='line_loading', bus_sizes=0.0001, timesteps=range(2)) + # etrago.plot_grid( + # line_colors='line_loading', bus_sizes=0.0001, timesteps=range(2)) # network and storage - # etrago.plot_grid(line_colors='expansion_abs', bus_colors='storage_expansion', bus_sizes=0.0001) + # etrago.plot_grid( + # line_colors='expansion_abs', + # bus_colors='storage_expansion', + # bus_sizes=0.0001) # flexibility usage # etrago.flexibility_usage('DSM') From 2fe133d06a3764fbc4bb2cb780e888bb40f62b48 Mon Sep 17 00:00:00 2001 From: ClaraBuettner Date: Thu, 14 Sep 2023 16:09:02 +0200 Subject: [PATCH 070/136] Remove unused import --- etrago/cluster/electrical.py | 1 - 1 file changed, 1 deletion(-) diff --git a/etrago/cluster/electrical.py b/etrago/cluster/electrical.py index a791e8642..19e4e281a 100755 --- a/etrago/cluster/electrical.py +++ b/etrago/cluster/electrical.py @@ -46,7 +46,6 @@ strategies_generators, strategies_one_ports, ) - from etrago.tools.utilities import * logger = logging.getLogger(__name__) From 2287591e405377e1870bf63423ee0217a4f829bd Mon Sep 17 00:00:00 2001 From: ClaraBuettner Date: Thu, 14 Sep 2023 16:12:46 +0200 Subject: [PATCH 071/136] Fix flake8 warnings "line too long" --- etrago/cluster/electrical.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/etrago/cluster/electrical.py b/etrago/cluster/electrical.py index 19e4e281a..85cced1df 100755 --- a/etrago/cluster/electrical.py +++ b/etrago/cluster/electrical.py @@ -147,7 +147,8 @@ def adjust_no_electric_network(etrago, busmap, cluster_met): # eHV network busmap2 = {} - # Map crossborder AC buses in case that they were not part of the k-mean clustering + # Map crossborder AC buses in case that they were not part of the k-mean + # clustering if (not etrago.args["network_clustering"]["cluster_foreign_AC"]) & ( cluster_met in ["kmeans", "kmedoids-dijkstra"] ): @@ -749,7 +750,7 @@ def preprocessing(etrago): ----------------------- WARNING --------------------------- THE FOLLOWING BUSES HAVE NOT COUNTRY DATA: - {network.buses[network.buses.country.isna()].index.to_list()}. + {network.buses[network.buses.country.isna()].index.to_list()}. THEY WILL BE ASSIGNED TO GERMANY, BUT IT IS POTENTIALLY A SIGN OF A PROBLEM IN THE DATASET. ----------------------- WARNING --------------------------- From a528398a8c90d9efd1287e4ee887972b599b4775 Mon Sep 17 00:00:00 2001 From: ClaraBuettner Date: Thu, 14 Sep 2023 16:13:12 +0200 Subject: [PATCH 072/136] Fix flake8 E712 --- etrago/cluster/electrical.py | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/etrago/cluster/electrical.py b/etrago/cluster/electrical.py index 85cced1df..da4b67475 100755 --- a/etrago/cluster/electrical.py +++ b/etrago/cluster/electrical.py @@ -178,7 +178,7 @@ def adjust_no_electric_network(etrago, busmap, cluster_met): & (network2.links["carrier"] == map_carrier[carry]) ].copy() df["elec"] = df["bus0"].isin(busmap.keys()) - df = df[df["elec"] == True] + df = df[df["elec"] is True] if len(df) > 0: bus_hv = df["bus0"][0] @@ -411,10 +411,10 @@ def delete_ehv_buses_no_lines(network): buses_ac["with_gen"] = buses_ac.index.isin(network.generators.bus) delete_buses = buses_ac[ - (buses_ac["with_line"] == False) - & (buses_ac["with_load"] == False) - & (buses_ac["with_link"] == False) - & (buses_ac["with_gen"] == False) + (buses_ac["with_line"] is False) + & (buses_ac["with_load"] is False) + & (buses_ac["with_link"] is False) + & (buses_ac["with_gen"] is False) ].index if len(delete_buses): @@ -825,7 +825,7 @@ def postprocessing(etrago, busmap, busmap_foreign, medoid_idx=None): method = settings["method"] num_clusters = settings["n_clusters_AC"] - if settings["k_elec_busmap"] == False: + if not settings["k_elec_busmap"]: busmap.name = "cluster" busmap_elec = pd.DataFrame(busmap.copy(), dtype="string") busmap_elec.index.name = "bus" @@ -871,7 +871,7 @@ def postprocessing(etrago, busmap, busmap_foreign, medoid_idx=None): ) # merge busmap for foreign buses with the German buses - if settings["cluster_foreign_AC"] == False: + if not settings["cluster_foreign_AC"]: for bus in busmap_foreign.index: busmap[bus] = busmap_foreign[bus] if bus == busmap_foreign[bus]: @@ -1057,7 +1057,7 @@ def run_spatial_clustering(self): elec_network, weight, n_clusters, busmap_foreign = preprocessing(self) if self.args["network_clustering"]["method"] == "kmeans": - if self.args["network_clustering"]["k_elec_busmap"] == False: + if not self.args["network_clustering"]["k_elec_busmap"]: logger.info("Start k-means Clustering") busmap = kmean_clustering( @@ -1069,7 +1069,7 @@ def run_spatial_clustering(self): medoid_idx = pd.Series(dtype=str) elif self.args["network_clustering"]["method"] == "kmedoids-dijkstra": - if self.args["network_clustering"]["k_elec_busmap"] == False: + if not self.args["network_clustering"]["k_elec_busmap"]: logger.info("Start k-medoids Dijkstra Clustering") busmap, medoid_idx = kmedoids_dijkstra_clustering( From 81590fbbe738da197071f88ebfbaafe5704ee3e7 Mon Sep 17 00:00:00 2001 From: ClaraBuettner Date: Thu, 14 Sep 2023 16:14:42 +0200 Subject: [PATCH 073/136] Fix flake8 error --- etrago/cluster/electrical.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/etrago/cluster/electrical.py b/etrago/cluster/electrical.py index da4b67475..11c114b0b 100755 --- a/etrago/cluster/electrical.py +++ b/etrago/cluster/electrical.py @@ -1089,7 +1089,7 @@ def run_spatial_clustering(self): ) self.update_busmap(busmap) - if self.args["disaggregation"] != None: + if self.args["disaggregation"] is not None: self.disaggregated_network = self.network.copy() else: self.disaggregated_network = self.network.copy(with_time=False) From fa577fc93d53842775fc8239b9babba4edb227b6 Mon Sep 17 00:00:00 2001 From: ClaraBuettner Date: Tue, 19 Sep 2023 10:16:24 +0200 Subject: [PATCH 074/136] Apply black --- etrago/appl.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/etrago/appl.py b/etrago/appl.py index b9e94eee8..fadd0bba1 100644 --- a/etrago/appl.py +++ b/etrago/appl.py @@ -169,7 +169,7 @@ def run_etrago(args, json_path): Parameters ---------- db : str - Name of Database session setting stored in *config.ini* of *.egoio*, + Name of Database session setting stored in *config.ini* of *.egoio*, e.g. ``'oedb'``. gridversion : None or str Name of the data version number of oedb: state ``'None'`` for @@ -183,14 +183,14 @@ def run_etrago(args, json_path): Type of optimization, currently only "lopf". Default: "lopf". * "n_iter" : int In case of extendable lines, several LOPFs have to be performed. - You can either set "n_iter" and specify a fixed number of + You can either set "n_iter" and specify a fixed number of iterations or set "threshold" and specify a threshold of the objective function as abort criteria of the iterative optimization. Default: 4. * "threshold" : int In case of extendable lines, several LOPFs have to be performed. - You can either set "n_iter" and specify a fixed number of - iterations or set "threshold" and specify a threshold of the + You can either set "n_iter" and specify a fixed number of + iterations or set "threshold" and specify a threshold of the objective function as abort criteria of the iterative optimization. Per default, "n_iter" of 4 is set. * "pyomo" : bool From 28f5fbd2883aefaf169e07c8e34834c00dda891b Mon Sep 17 00:00:00 2001 From: ClaraBuettner Date: Tue, 19 Sep 2023 10:17:10 +0200 Subject: [PATCH 075/136] Remove unused import --- etrago/appl.py | 1 - 1 file changed, 1 deletion(-) diff --git a/etrago/appl.py b/etrago/appl.py index fadd0bba1..a297b01a8 100644 --- a/etrago/appl.py +++ b/etrago/appl.py @@ -29,7 +29,6 @@ import os import os.path -import numpy as np __copyright__ = ( "Flensburg University of Applied Sciences, " From 4fb21bc0726dcf5a9c1d7aa22971b254b043ed60 Mon Sep 17 00:00:00 2001 From: ClaraBuettner Date: Tue, 19 Sep 2023 10:50:07 +0200 Subject: [PATCH 076/136] Fix flake8 errors due to comparision with None --- etrago/tools/extendable.py | 41 +++++++++++++++++++++++--------------- 1 file changed, 25 insertions(+), 16 deletions(-) diff --git a/etrago/tools/extendable.py b/etrago/tools/extendable.py index ba4be5173..a1da98b45 100644 --- a/etrago/tools/extendable.py +++ b/etrago/tools/extendable.py @@ -37,7 +37,8 @@ "DLR-Institute for Networked Energy Systems" ) __license__ = "GNU Affero General Public License Version 3 (AGPL-3.0)" -__author__ = "ulfmueller, s3pp, wolfbunke, mariusves, lukasol, ClaraBuettner, KathiEsterl, CarlosEpia" +__author__ = """ulfmueller, s3pp, wolfbunke, mariusves, lukasol, ClaraBuettner, + KathiEsterl, CarlosEpia""" def extendable( @@ -58,13 +59,16 @@ def extendable( Parameters ---------- grid_max_D : int, optional - Upper bounds for electrical grid expansion relative to existing capacity. The default is None. + Upper bounds for electrical grid expansion relative to existing + capacity. The default is None. grid_max_abs_D : dict, optional Absolute upper bounds for electrical grid expansion in Germany. grid_max_foreign : int, optional - Upper bounds for expansion of electrical foreign lines relative to the existing capacity. The default is 4. + Upper bounds for expansion of electrical foreign lines relative to the + existing capacity. The default is 4. grid_max_abs_foreign : dict, optional - Absolute upper bounds for expansion of foreign electrical grid. The default is None. + Absolute upper bounds for expansion of foreign electrical grid. + The default is None. Returns ------- @@ -75,7 +79,7 @@ def extendable( network = self.network extendable_settings = self.args["extendable"] - if not "as_in_db" in extendable_settings["extendable_components"]: + if "as_in_db" not in extendable_settings["extendable_components"]: network.lines.s_nom_extendable = False network.transformers.s_nom_extendable = False network.links.p_nom_extendable = False @@ -355,7 +359,7 @@ def extendable( # constrain network expansion to maximum - if not grid_max_abs_D == None: + if grid_max_abs_D is not None: buses = network.buses[ (network.buses.country == "DE") & (network.buses.carrier == "AC") ] @@ -371,7 +375,7 @@ def extendable( "p_nom_max", ] = grid_max_abs_D["dc"] - if not grid_max_abs_foreign == None: + if grid_max_abs_foreign is not None: foreign_buses = network.buses[ (network.buses.country != "DE") & (network.buses.carrier == "AC") ] @@ -393,7 +397,7 @@ def extendable( "p_nom_max", ] = grid_max_abs_foreign["dc"] - if not grid_max_D == None: + if grid_max_D is not None: buses = network.buses[ (network.buses.country == "DE") & (network.buses.carrier == "AC") ] @@ -419,7 +423,7 @@ def extendable( grid_max_D * network.links.p_nom ) - if not grid_max_foreign == None: + if grid_max_foreign is not None: foreign_buses = network.buses[ (network.buses.country != "DE") & (network.buses.carrier == "AC") ] @@ -488,7 +492,8 @@ def line_max_abs( }, ): """ - Function to calculate limitation for capacity expansion of lines in network. + Function to calculate limitation for capacity expansion of lines in + network. Parameters ---------- @@ -533,7 +538,8 @@ def line_max_abs( wires=line_max_abs["380"]["wires"], circuits=line_max_abs["380"]["circuits"], ) * (network.lines["cables"] / network.lines["total_cables"]) - # set the s_nom_max depending on the voltage level and the share of the route + # set the s_nom_max depending on the voltage level + # and the share of the route network.lines.loc[ (network.lines.bus0.isin(buses.index)) & (network.lines.bus1.isin(buses.index)) @@ -585,7 +591,8 @@ def line_max_abs( def transformer_max_abs(network, buses): """ - Function to calculate limitation for capacity expansion of transformers in network. + Function to calculate limitation for capacity expansion of transformers in + network. Parameters ---------- @@ -601,8 +608,8 @@ def transformer_max_abs(network, buses): """ # To determine the maximum extendable capacity of a transformer, the sum of - # the maximum capacities of the lines connected to it is calculated for each - # of its 2 sides. The smallest one is selected. + # the maximum capacities of the lines connected to it is calculated for + # each of its 2 sides. The smallest one is selected. smax_bus0 = network.lines.s_nom_max.groupby(network.lines.bus0).sum() smax_bus1 = network.lines.s_nom_max.groupby(network.lines.bus1).sum() smax_bus = pd.concat([smax_bus0, smax_bus1], axis=1) @@ -804,12 +811,14 @@ def print_expansion_costs(network): if not ext_storage.empty: print( - "Investment costs for all storage units in selected snapshots [EUR]:", + """Investment costs for all storage units in selected snapshots + [EUR]:""", round(storage_costs, 2), ) if not ext_lines.empty: print( - "Investment costs for all lines and transformers in selected snapshots [EUR]:", + """Investment costs for all lines and transformers in selected + snapshots [EUR]:""", round(network_costs, 2), ) From 8f74ae3461ebc6a6b3019b47476cc44e47b1b34b Mon Sep 17 00:00:00 2001 From: ClaraBuettner Date: Tue, 19 Sep 2023 10:56:59 +0200 Subject: [PATCH 077/136] Manual break overly long lines --- etrago/tools/constraints.py | 36 +++++++++++++++++++++++------------- 1 file changed, 23 insertions(+), 13 deletions(-) diff --git a/etrago/tools/constraints.py b/etrago/tools/constraints.py index da6911fd8..ada6f18ff 100755 --- a/etrago/tools/constraints.py +++ b/etrago/tools/constraints.py @@ -311,8 +311,9 @@ def _rule(m): def _cross_border_flow(self, network, snapshots): """ - Extra_functionality that limits overall AC crossborder flows from/to Germany. - Add key 'cross_border_flow' and array with minimal and maximal import/export + Extra_functionality that limits overall AC crossborder flows from/to + Germany. Add key 'cross_border_flow' and array with minimal and maximal + import/export Example: {'cross_border_flow': [-x, y]} (with x Import, y Export) Parameters @@ -402,7 +403,8 @@ def _rule_max(m): def _cross_border_flow_nmp(self, network, snapshots): """ Extra_functionality that limits overall crossborder flows from/to Germany. - Add key 'cross_border_flow' and array with minimal and maximal import/export + Add key 'cross_border_flow' and array with minimal and maximal + import/export Example: {'cross_border_flow': [-x, y]} (with x Import, y Export) Parameters @@ -471,7 +473,8 @@ def _cross_border_flow_per_country_nmp(self, network, snapshots): Extra_functionality that limits AC crossborder flows for each given foreign country from/to Germany. Add key 'cross_border_flow_per_country' to args.extra_functionality and - define dictionary of country keys and desired limitations of im/exports in MWh + define dictionary of country keys and desired limitations of im/exports + in MWh Example: {'cross_border_flow_per_country': {'DK':[-X, Y], 'FR':[0,0]}} Parameters @@ -560,7 +563,8 @@ def _cross_border_flow_per_country(self, network, snapshots): Extra_functionality that limits AC crossborder flows for each given foreign country from/to Germany. Add key 'cross_border_flow_per_country' to args.extra_functionality and - define dictionary of country keys and desired limitations of im/exports in MWh + define dictionary of country keys and desired limitations of im/exports + in MWh Example: {'cross_border_flow_per_country': {'DK':[-X, Y], 'FR':[0,0]}} Parameters @@ -2667,7 +2671,8 @@ def split_dispatch_disaggregation_constraints(self, n, sns): """ Add constraints for state of charge of storage units and stores when separating the optimization into smaller subproblems - while conducting thedispatch_disaggregation in temporally fully resolved network + while conducting thedispatch_disaggregation in temporally fully resolved + network The state of charge at the end of each slice is set to the value calculated in the optimization with the temporally reduced network @@ -2825,7 +2830,8 @@ def functionality(self, network, snapshots): ): if self.args["snapshot_clustering"]["how"] == "hourly": logger.info( - "soc_constraints_simplified not possible while hourly clustering -> changed to soc_constraints" + """soc_constraints_simplified not possible while hourly + clustering -> changed to soc_constraints""" ) if self.args["method"]["pyomo"]: @@ -2848,8 +2854,10 @@ def functionality(self, network, snapshots): else: logger.error( - "If you want to use constraints considering the storage behaviour, snapshot clustering constraints must be in" - + " [daily_bounds, soc_constraints, soc_constraints_simplified]" + """If you want to use constraints considering the storage + behaviour, snapshot clustering constraints must be in + [daily_bounds, soc_constraints, + soc_constraints_simplified]""" ) if self.conduct_dispatch_disaggregation is not False: @@ -2866,8 +2874,9 @@ def functionality(self, network, snapshots): def add_chp_constraints_nmp(n): """ Limits the dispatch of combined heat and power links based on - T.Brown et. al : Synergies of sector coupling and transmission reinforcement - in a cost-optimised, highly renewable European energy system, 2018 + T.Brown et. al : Synergies of sector coupling and transmission + reinforcement in a cost-optimised, highly renewable European energy system, + 2018 Parameters ---------- @@ -2947,8 +2956,9 @@ def add_chp_constraints_nmp(n): def add_chp_constraints(network, snapshots): """ Limits the dispatch of combined heat and power links based on - T.Brown et. al : Synergies of sector coupling and transmission reinforcement - in a cost-optimised, highly renewable European energy system, 2018 + T.Brown et. al : Synergies of sector coupling and transmission + reinforcement in a cost-optimised, highly renewable European energy system, + 2018 Parameters ---------- From f7fecf92bf1e9c7311d4feb5a993cb9b7bf8cbfe Mon Sep 17 00:00:00 2001 From: ClaraBuettner Date: Tue, 19 Sep 2023 10:59:33 +0200 Subject: [PATCH 078/136] Replace comparison with False --- etrago/tools/constraints.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/etrago/tools/constraints.py b/etrago/tools/constraints.py index ada6f18ff..74f174fd2 100755 --- a/etrago/tools/constraints.py +++ b/etrago/tools/constraints.py @@ -1656,7 +1656,7 @@ def snapshot_clustering_seasonal_storage( # create set for inter-temp constraints and variables network.model.candidates = po.Set(initialize=candidates, ordered=True) - if simplified == False: + if not simplified: # create intra soc variable for each storage/store and each hour network.model.state_of_charge_intra = po.Var( sus.index, network.snapshots From 87b3d622a62d80688883a4148166c556918bcb5a Mon Sep 17 00:00:00 2001 From: ClaraBuettner Date: Tue, 19 Sep 2023 10:59:49 +0200 Subject: [PATCH 079/136] Remove empty comments --- etrago/tools/constraints.py | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/etrago/tools/constraints.py b/etrago/tools/constraints.py index 74f174fd2..7c17de175 100755 --- a/etrago/tools/constraints.py +++ b/etrago/tools/constraints.py @@ -1963,7 +1963,7 @@ def state_of_charge_lower(m, s, h): delta_t = h - period_start intra_hour = first_hour + delta_t else: - hrs = 24 # 0 ### + hrs = 24 date = str( network.snapshots[ network.snapshots.dayofyear - 1 @@ -1978,7 +1978,7 @@ def state_of_charge_lower(m, s, h): + m.state_of_charge_inter[ s, network.cluster_ts["Candidate_day"][h] ] - * (1 - network.storage_units.at[s, "standing_loss"]) ** hrs ### + * (1 - network.storage_units.at[s, "standing_loss"]) ** hrs >= 0 ) @@ -2005,7 +2005,7 @@ def state_of_charge_lower_store(m, s, h): delta_t = h - period_start intra_hour = first_hour + delta_t else: - hrs = 24 # 0 ### + hrs = 24 date = str( network.snapshots[ network.snapshots.dayofyear - 1 @@ -2028,7 +2028,7 @@ def state_of_charge_lower_store(m, s, h): + m.state_of_charge_inter_store[ s, network.cluster_ts["Candidate_day"][h] ] - * (1 - network.stores.at[s, "standing_loss"]) ** hrs ### + * (1 - network.stores.at[s, "standing_loss"]) ** hrs >= low ) @@ -2046,7 +2046,7 @@ def state_of_charge_lower_simplified(m, s, h): elif self.args["snapshot_clustering"]["how"] == "monthly": hrs = 720 else: - hrs = 24 # 0 + hrs = 24 return ( m.state_of_charge_intra_min[ @@ -2055,7 +2055,7 @@ def state_of_charge_lower_simplified(m, s, h): + m.state_of_charge_inter[ s, network.cluster_ts["Candidate_day"][h] ] - * (1 - network.storage_units.at[s, "standing_loss"]) ** hrs ### + * (1 - network.storage_units.at[s, "standing_loss"]) ** hrs >= 0 ) @@ -2065,7 +2065,7 @@ def state_of_charge_lower_store_simplified(m, s, h): elif self.args["snapshot_clustering"]["how"] == "monthly": hrs = 720 else: - hrs = 24 # 0 + hrs = 24 if "DSM" in s: if self.args["snapshot_clustering"]["how"] == "weekly": @@ -2109,7 +2109,7 @@ def state_of_charge_lower_store_simplified(m, s, h): + m.state_of_charge_inter_store[ s, network.cluster_ts["Candidate_day"][h] ] - * (1 - network.stores.at[s, "standing_loss"]) ** hrs ### + * (1 - network.stores.at[s, "standing_loss"]) ** hrs >= low ) @@ -2188,7 +2188,7 @@ def state_of_charge_upper(m, s, h): + m.state_of_charge_inter[ s, network.cluster_ts["Candidate_day"][h] ] - * (1 - network.storage_units.at[s, "standing_loss"]) ** hrs ### + * (1 - network.storage_units.at[s, "standing_loss"]) ** hrs <= p_nom * network.storage_units.at[s, "max_hours"] ) @@ -2241,7 +2241,7 @@ def state_of_charge_upper_store(m, s, h): + m.state_of_charge_inter_store[ s, network.cluster_ts["Candidate_day"][h] ] - * (1 - network.stores.at[s, "standing_loss"]) ** hrs ### + * (1 - network.stores.at[s, "standing_loss"]) ** hrs <= e_nom ) @@ -2265,7 +2265,7 @@ def state_of_charge_upper_simplified(m, s, h): + m.state_of_charge_inter[ s, network.cluster_ts["Candidate_day"][h] ] - * (1 - network.storage_units.at[s, "standing_loss"]) ** hrs ### + * (1 - network.storage_units.at[s, "standing_loss"]) ** hrs <= p_nom * network.storage_units.at[s, "max_hours"] ) @@ -2331,7 +2331,7 @@ def state_of_charge_upper_store_simplified(m, s, h): + m.state_of_charge_inter_store[ s, network.cluster_ts["Candidate_day"][h] ] - * (1 - network.stores.at[s, "standing_loss"]) ** hrs ### + * (1 - network.stores.at[s, "standing_loss"]) ** hrs <= e_nom ) From 73bfb637d24a64dcec7675023da1ce3b48f26dfd Mon Sep 17 00:00:00 2001 From: ClaraBuettner Date: Tue, 19 Sep 2023 11:08:19 +0200 Subject: [PATCH 080/136] Exclude saio imports from flaxe8 checks --- etrago/tools/io.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/etrago/tools/io.py b/etrago/tools/io.py index 5bb2b019e..31d84ba56 100644 --- a/etrago/tools/io.py +++ b/etrago/tools/io.py @@ -196,7 +196,7 @@ def fetch_by_relname(self, name): pd.DataFrame Component data. """ - from saio.grid import ( + from saio.grid import ( # noqa: F401 egon_etrago_bus, egon_etrago_generator, egon_etrago_line, @@ -251,7 +251,7 @@ def series_fetch_by_relname(self, network, name, pypsa_name): pd.DataFrame Component data. """ - from saio.grid import ( + from saio.grid import ( # noqa: F401 egon_etrago_bus_timeseries, egon_etrago_generator_timeseries, egon_etrago_line_timeseries, From cbcbd50e805523732ca873f019014c41bd9cf30e Mon Sep 17 00:00:00 2001 From: ClaraBuettner Date: Tue, 19 Sep 2023 11:08:44 +0200 Subject: [PATCH 081/136] Replace unavailable function --- etrago/tools/io.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/etrago/tools/io.py b/etrago/tools/io.py index 31d84ba56..569f95a25 100644 --- a/etrago/tools/io.py +++ b/etrago/tools/io.py @@ -500,7 +500,7 @@ def results_to_oedb(session, network, args, grid="hv", safe_results=False): print("Uploading results to db...") # get last result id and get new one - last_res_id = session.query(func.max(ResultMeta.result_id)).scalar() + last_res_id = session.query(max(ResultMeta.result_id)).scalar() if last_res_id == None: new_res_id = 1 else: @@ -539,7 +539,7 @@ def results_to_oedb(session, network, args, grid="hv", safe_results=False): if network.generators.carrier[gen] not in sources.name.values: new_source = Source() new_source.source_id = ( - session.query(func.max(Source.source_id)).scalar() + 1 + session.query(max(Source.source_id)).scalar() + 1 ) new_source.name = network.generators.carrier[gen] session.add(new_source) @@ -564,7 +564,7 @@ def results_to_oedb(session, network, args, grid="hv", safe_results=False): if network.storage_units.carrier[stor] not in sources.name.values: new_source = Source() new_source.source_id = ( - session.query(func.max(Source.source_id)).scalar() + 1 + session.query(max(Source.source_id)).scalar() + 1 ) new_source.name = network.storage_units.carrier[stor] session.add(new_source) From 5a85f55032e63bb4947e7886cedb7398da69fb4f Mon Sep 17 00:00:00 2001 From: ClaraBuettner Date: Tue, 19 Sep 2023 11:08:55 +0200 Subject: [PATCH 082/136] Delete unused variable --- etrago/tools/io.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/etrago/tools/io.py b/etrago/tools/io.py index 569f95a25..58878670c 100644 --- a/etrago/tools/io.py +++ b/etrago/tools/io.py @@ -875,10 +875,6 @@ def decommissioning(self, **kwargs): row["scn_name"] == "extension_" + self.args["scn_decommissioning"] ): - v_nom_dec = df_decommisionning["v_nom"][ - (df_decommisionning.project == row["project"]) - & (df_decommisionning.project_id == row["project_id"]) - ] self.network.lines.s_nom_min[ self.network.lines.index == idx From a0a408fe3b69a2830bf3d1da7506b3d534e29240 Mon Sep 17 00:00:00 2001 From: ClaraBuettner Date: Tue, 19 Sep 2023 11:10:23 +0200 Subject: [PATCH 083/136] Fix flaxe8 issues --- etrago/tools/io.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/etrago/tools/io.py b/etrago/tools/io.py index 58878670c..02fa6fb81 100644 --- a/etrago/tools/io.py +++ b/etrago/tools/io.py @@ -171,7 +171,7 @@ def configure_timeindex(self): ) ) - self.timeindex = timeindex[self.start_snapshot - 1 : self.end_snapshot] + self.timeindex = timeindex[self.start_snapshot - 1: self.end_snapshot] def id_to_source(self): ormclass = self._mapped["Source"] @@ -290,7 +290,7 @@ def series_fetch_by_relname(self, network, name, pypsa_name): vars()[f"egon_etrago_{name.lower()}_timeseries"], index_col ), getattr(vars()[f"egon_etrago_{name.lower()}_timeseries"], col)[ - self.start_snapshot : self.end_snapshot + self.start_snapshot:self.end_snapshot ], ).filter( vars()[f"egon_etrago_{name.lower()}_timeseries"].scn_name @@ -501,7 +501,7 @@ def results_to_oedb(session, network, args, grid="hv", safe_results=False): print("Uploading results to db...") # get last result id and get new one last_res_id = session.query(max(ResultMeta.result_id)).scalar() - if last_res_id == None: + if last_res_id is None: new_res_id = 1 else: new_res_id = last_res_id + 1 From d493fb9896172743f7e97bb3d2f7a55a9a0325a7 Mon Sep 17 00:00:00 2001 From: ClaraBuettner Date: Tue, 19 Sep 2023 11:19:08 +0200 Subject: [PATCH 084/136] Break overly long lines --- etrago/cluster/gas.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/etrago/cluster/gas.py b/etrago/cluster/gas.py index 7a6d0f15d..61b23dc90 100644 --- a/etrago/cluster/gas.py +++ b/etrago/cluster/gas.py @@ -101,7 +101,8 @@ def preprocessing(etrago): ) ] - # select buses dependent on whether they should be clustered in (only DE or DE+foreign) + # select buses dependent on whether they should be clustered in + # (only DE or DE+foreign) if not settings["cluster_foreign_gas"]: network_ch4.buses = network_ch4.buses.loc[ ch4_filter & (network_ch4.buses["country"].values == "DE") @@ -997,7 +998,8 @@ def run_spatial_clustering_gas(self): self.update_busmap(busmap) logger.info( - "GAS Network clustered to {} DE-buses and {} foreign buses with {} algorithm.".format( + """GAS Network clustered to {} DE-buses and {} foreign buses + with {} algorithm.""".format( len( self.network.buses.loc[ (self.network.buses.carrier == "CH4") From 1fb8cfc4b655075174ceb390ee44224ddb221aab Mon Sep 17 00:00:00 2001 From: ClaraBuettner Date: Tue, 19 Sep 2023 11:36:13 +0200 Subject: [PATCH 085/136] Fix number of whitespaces --- etrago/appl.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/etrago/appl.py b/etrago/appl.py index a297b01a8..083d87b86 100644 --- a/etrago/appl.py +++ b/etrago/appl.py @@ -143,7 +143,7 @@ "extreme_periods": None, # consideration of extreme timesteps; e.g. 'append' "how": "daily", # type of period - only relevant for 'typical_periods' "storage_constraints": "soc_constraints", # additional constraints for storages - only relevant for 'typical_periods' - "n_clusters": 5, # number of periods - only relevant for 'typical_periods' + "n_clusters": 5, # number of periods - only relevant for 'typical_periods' "n_segments": 5, # number of segments - only relevant for segmentation }, "skip_snapshots": 5, # False or number of snapshots to skip From 96eb11a7757bd05cf071c78b5de9d820fcc21099 Mon Sep 17 00:00:00 2001 From: ClaraBuettner Date: Tue, 19 Sep 2023 11:42:40 +0200 Subject: [PATCH 086/136] Add unit to docstring --- etrago/tools/calc_results.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/etrago/tools/calc_results.py b/etrago/tools/calc_results.py index 1605f9915..bf1edac83 100755 --- a/etrago/tools/calc_results.py +++ b/etrago/tools/calc_results.py @@ -505,7 +505,7 @@ def ac_export_per_country(self): Returns ------- float - Electricity export (if negative: import) from Germany + Electricity export (if negative: import) from Germany in TWh """ de_buses = self.network.buses[self.network.buses.country == "DE"] @@ -585,7 +585,7 @@ def dc_export_per_country(self): Returns ------- float - Electricity export (if negative: import) from Germany + Electricity export (if negative: import) from Germany in TWh """ de_buses = self.network.buses[self.network.buses.country == "DE"] From 0256a3f254fbad6843f18f3cb0a9e4b4a71db5e3 Mon Sep 17 00:00:00 2001 From: ClaraBuettner Date: Tue, 19 Sep 2023 13:42:30 +0200 Subject: [PATCH 087/136] Replace flexibility duration curve plot --- etrago/tools/network.py | 3 -- etrago/tools/plot.py | 117 +++++++++++++++++++++++----------------- 2 files changed, 68 insertions(+), 52 deletions(-) diff --git a/etrago/tools/network.py b/etrago/tools/network.py index 8e4b57b7f..ebefc8db7 100644 --- a/etrago/tools/network.py +++ b/etrago/tools/network.py @@ -58,7 +58,6 @@ from etrago.tools.plot import ( bev_flexibility_potential, demand_side_management, - flexibility_duration_curve, flexibility_usage, heat_stores, hydrogen_stores, @@ -290,8 +289,6 @@ def __init__( plot_heat_summary = plot_heat_summary - plot_flexibility_duration_curve = flexibility_duration_curve - plot_flexibility_usage = flexibility_usage demand_side_management = demand_side_management diff --git a/etrago/tools/plot.py b/etrago/tools/plot.py index 953c3841e..feff65558 100644 --- a/etrago/tools/plot.py +++ b/etrago/tools/plot.py @@ -124,6 +124,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", @@ -144,6 +145,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", @@ -3335,14 +3337,15 @@ def shifted_energy(self, carrier, buses): shifted = supply - demand return shifted - -def flexibility_duration_curve(self, buses, filename=None): +def flexibility_duration_curve(etrago, etrago_lowflex, filename=None): """Plot duration curves of flexibility options Parameters ---------- - buses : list - List of considered bus indices + 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. @@ -3351,51 +3354,67 @@ def flexibility_duration_curve(self, buses, filename=None): None. """ - fig, ax = plt.subplots(figsize=(15, 8)) - df = pd.DataFrame(index=range(len(self.network.snapshots))) - - df["dsm"] = ( - self.demand_side_management( - buses=buses, - snapshots=range(len(self.network.snapshots)), - used=True, - ) - .e.sort_values() - .reset_index() - .e - ) - - df["mobility"] = ( - ( - self.bev_flexibility_potential( - buses=buses, - snapshots=range(len(self.network.snapshots)), - used=True, - ).e - - self.bev_flexibility_potential( - buses=buses, - snapshots=range(len(self.network.snapshots)), - used=True, - )[["e_max", "e_min"]].mean(axis=1) - ) - .sort_values() - .reset_index()[0] - ) - df["mobility"] - df["heat"] = ( - shifted_energy(self, "heat", buses).sort_values().reset_index()[0] - ) - - df["hydrogen_stores"] = ( - shifted_energy(self, "H2", buses).sort_values().reset_index()[0] - ) + colors = coloring() - df.mul(1e-3).plot(ax=ax) - ax.set_ylabel("Usage in GWh") - plt.axhline(y=0.0, color="grey", linestyle="dotted") + 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) - if filename is None: - plt.show() - else: - matplotlib.pylab.savefig(filename, dpi=400, bbox_inches="tight") + 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() From ad5f9cc079f192b366f1af3e7c52079da69e92d5 Mon Sep 17 00:00:00 2001 From: ClaraBuettner Date: Tue, 19 Sep 2023 13:42:57 +0200 Subject: [PATCH 088/136] Apply black --- etrago/tools/plot.py | 125 ++++++++++++++++++++++++++++--------------- 1 file changed, 81 insertions(+), 44 deletions(-) diff --git a/etrago/tools/plot.py b/etrago/tools/plot.py index feff65558..6a227ab54 100644 --- a/etrago/tools/plot.py +++ b/etrago/tools/plot.py @@ -117,7 +117,6 @@ def plot_osm(x, y, zoom, alpha=0.4): def coloring(): - colors = { "load": "red", "DC": "blue", @@ -219,6 +218,7 @@ def plot_line_loading_diff(networkA, networkB, timestep=0, osm=False): set_epsg_network(networkA) set_epsg_network(networkB) plot_osm(osm["x"], osm["y"], osm["zoom"]) + # new colormap to make sure 0% difference has the same color in every plot def shiftedColorMap( cmap, start=0, midpoint=0.5, stop=1.0, name="shiftedcmap" @@ -1224,7 +1224,6 @@ def storage_p_soc(network, mean="1H", filename=None): network.storage_units.p_nom_opt[sbatt].sum() > 1 and network.storage_units.p_nom_opt[shydr].sum() < 1 ): - ( network.storage_units_t.p[sbatt].resample(mean).mean().sum(axis=1) / network.storage_units.p_nom_opt[sbatt].sum() @@ -1449,7 +1448,6 @@ def calc_ac_loading(network, timesteps): ) if not network.lines_t.q0.empty: - loading_lines = ( loading_lines**2 + mul_weighting(network, network.lines_t.q0) @@ -1490,7 +1488,6 @@ def calc_dc_loading(network, timesteps): & (network.links.length == row["length"]) ] ).empty: - l = network.links.index[ (network.links.bus0 == row["bus1"]) & (network.links.bus1 == row["bus0"]) @@ -1608,7 +1605,6 @@ def calc_network_expansion(network, method="abs", ext_min=0.1): network.links.p_nom_opt[i] = linked.p_nom_opt.values[0] if method == "rel": - extension_lines = ( 100 * (network.lines.s_nom_opt - network.lines.s_nom_min) @@ -1668,6 +1664,7 @@ def plot_background_grid(network, ax): color_geomap=True, ) + def demand_side_management(self, buses, snapshots, agg="5h", used=False): """Calculate shifting potential of demand side management @@ -2051,7 +2048,6 @@ def flexibility_usage( ) elif flexibility == "battery": - df = pd.DataFrame(index=self.network.snapshots[snapshots]) su = self.network.storage_units[ @@ -2105,7 +2101,9 @@ def flexibility_usage( fig_e.savefig(pre_path + f"stored_e_{flexibility}") -def plot_carrier(network, carrier_links=["AC"], carrier_buses=["AC"], cartopy=True): +def plot_carrier( + network, carrier_links=["AC"], carrier_buses=["AC"], cartopy=True +): """ Parameters ---------- @@ -2138,13 +2136,11 @@ def plot_carrier(network, carrier_links=["AC"], carrier_buses=["AC"], cartopy=Tr link_width = pd.Series(index=network.links.index, data=2) if len(carrier_links) > 0: - link_width.loc[~network.links.carrier.isin(carrier_links)] = 0 bus_sizes = pd.Series(index=network.buses.index, data=0.0005) if len(carrier_buses) > 0: - bus_sizes.loc[~network.buses.carrier.isin(carrier_buses)] = 0 link_colors = network.links.carrier.map(colors) @@ -3259,7 +3255,7 @@ def plot_heat_summary(self, t_resolution="20H", stacked=True, save_path=False): data.resample(t_resolution).mean().plot( ax=ax, label=i, legend=True ) - + heat_store_dispatch_hb.resample(t_resolution).mean().plot.line( ax=ax, legend=True, @@ -3279,6 +3275,7 @@ 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 @@ -3337,6 +3334,7 @@ def shifted_energy(self, carrier, buses): shifted = supply - demand return shifted + def flexibility_duration_curve(etrago, etrago_lowflex, filename=None): """Plot duration curves of flexibility options @@ -3356,57 +3354,96 @@ def flexibility_duration_curve(etrago, etrago_lowflex, filename=None): """ colors = coloring() - value = 'p' - + 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) + + 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'] - + 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 = 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) + 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) + 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) + 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') + 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") From b95fda944820f2d96e5e60105ae5abdd7f851c47 Mon Sep 17 00:00:00 2001 From: ClaraBuettner Date: Tue, 19 Sep 2023 13:47:57 +0200 Subject: [PATCH 089/136] Apply black --- etrago/tools/calc_results.py | 324 +++++++++++++++++++++-------------- 1 file changed, 195 insertions(+), 129 deletions(-) diff --git a/etrago/tools/calc_results.py b/etrago/tools/calc_results.py index fca593a97..f3e3338dd 100755 --- a/etrago/tools/calc_results.py +++ b/etrago/tools/calc_results.py @@ -22,7 +22,8 @@ calc_results.py defines methods to calculate results of eTraGo """ import os -if 'READTHEDOCS' not in os.environ: + +if "READTHEDOCS" not in os.environ: import time import logging @@ -31,16 +32,18 @@ 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 +51,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 +70,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 +88,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 +113,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 +147,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 +190,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,21 +210,35 @@ 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 + """Function that calculates main results of grid optimization and adds them to Etrago object. Returns @@ -218,99 +246,137 @@ def calc_etrago_results(self): 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 = 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." # system 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'] + 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() + ) From fc683c57818fad81d418a615d713b3e59297e903 Mon Sep 17 00:00:00 2001 From: ClaraBuettner Date: Tue, 19 Sep 2023 15:46:32 +0200 Subject: [PATCH 090/136] Black --- etrago/tools/io.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/etrago/tools/io.py b/etrago/tools/io.py index 02fa6fb81..c87bda98b 100644 --- a/etrago/tools/io.py +++ b/etrago/tools/io.py @@ -171,7 +171,7 @@ def configure_timeindex(self): ) ) - self.timeindex = timeindex[self.start_snapshot - 1: self.end_snapshot] + self.timeindex = timeindex[self.start_snapshot - 1 : self.end_snapshot] def id_to_source(self): ormclass = self._mapped["Source"] @@ -290,7 +290,7 @@ def series_fetch_by_relname(self, network, name, pypsa_name): vars()[f"egon_etrago_{name.lower()}_timeseries"], index_col ), getattr(vars()[f"egon_etrago_{name.lower()}_timeseries"], col)[ - self.start_snapshot:self.end_snapshot + self.start_snapshot : self.end_snapshot ], ).filter( vars()[f"egon_etrago_{name.lower()}_timeseries"].scn_name @@ -875,7 +875,6 @@ def decommissioning(self, **kwargs): row["scn_name"] == "extension_" + self.args["scn_decommissioning"] ): - self.network.lines.s_nom_min[ self.network.lines.index == idx ] = self.network.lines.s_nom_min From 326906bc626f036944e4180f7b6dd5afdf17f126 Mon Sep 17 00:00:00 2001 From: ClaraBuettner Date: Tue, 19 Sep 2023 15:51:12 +0200 Subject: [PATCH 091/136] Solve flake8 errors --- etrago/cluster/spatial.py | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/etrago/cluster/spatial.py b/etrago/cluster/spatial.py index 43af10b9b..cb203823c 100755 --- a/etrago/cluster/spatial.py +++ b/etrago/cluster/spatial.py @@ -41,8 +41,14 @@ import networkx as nx import numpy as np import pandas as pd + import pypsa - from etrago.tools.utilities import * + from etrago.tools.utilities import ( + buses_grid_linked, + buses_of_vlvl, + connected_grid_lines, + connected_transformer, + ) logger = logging.getLogger(__name__) @@ -92,7 +98,7 @@ def nan_links(x): def ext_storage(x): - v = any(x[x == True]) + v = any(x[x is True]) return v @@ -314,7 +320,7 @@ def gen(nodes, n, graph): g = graph.copy() for i in range(0, len(nodes), n): - yield (nodes[i : i + n], g) + yield (nodes[i: i + n], g) def shortest_path(paths, graph): @@ -341,7 +347,7 @@ def shortest_path(paths, graph): df_isna = df.isnull() for s, t in paths: - while df_isna.loc[(s, t), "path_length"] == True: + while df_isna.loc[(s, t), "path_length"]: try: s_to_other = nx.single_source_dijkstra_path_length(graph, s) for t in idx.levels[1]: @@ -506,7 +512,7 @@ def busmap_from_psql(etrago): """ scn_name = ( etrago.args["scn_name"] - if etrago.args["scn_extension"] == None + if etrago.args["scn_extension"] is None else etrago.args["scn_name"] + "_ext_" + "_".join(etrago.args["scn_extension"]) @@ -717,8 +723,9 @@ def kmedoids_dijkstra_clustering( etrago, buses, connections, weight, n_clusters ): """ - Applies a k-medoids clustering on the given network and calls the function to conduct a Dijkstra's - algorithm afterwards for the consideration of the network's topology in the spatial clustering. + Applies a k-medoids clustering on the given network and calls the function + to conduct a Dijkstra's algorithm afterwards for the consideration of the + network's topology in the spatial clustering. Parameters ---------- From bb767450cd1d0e5cac93d923d2953477f4b4165e Mon Sep 17 00:00:00 2001 From: ClaraBuettner Date: Tue, 19 Sep 2023 15:51:37 +0200 Subject: [PATCH 092/136] Apply black --- etrago/cluster/spatial.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/etrago/cluster/spatial.py b/etrago/cluster/spatial.py index cb203823c..7499ae197 100755 --- a/etrago/cluster/spatial.py +++ b/etrago/cluster/spatial.py @@ -48,7 +48,7 @@ buses_of_vlvl, connected_grid_lines, connected_transformer, - ) + ) logger = logging.getLogger(__name__) @@ -320,7 +320,7 @@ def gen(nodes, n, graph): g = graph.copy() for i in range(0, len(nodes), n): - yield (nodes[i: i + n], g) + yield (nodes[i : i + n], g) def shortest_path(paths, graph): From fef2eed727c48ab26a1bd1e291a82432a3739fc7 Mon Sep 17 00:00:00 2001 From: ClaraBuettner Date: Tue, 19 Sep 2023 15:57:47 +0200 Subject: [PATCH 093/136] Fix flake8 errors --- etrago/cluster/snapshot.py | 68 ++++++++++++++++++++++---------------- 1 file changed, 39 insertions(+), 29 deletions(-) diff --git a/etrago/cluster/snapshot.py b/etrago/cluster/snapshot.py index 8afec2f1a..a476e93ac 100644 --- a/etrago/cluster/snapshot.py +++ b/etrago/cluster/snapshot.py @@ -18,7 +18,8 @@ # along with this program. If not, see . # File description for read-the-docs -""" This module contains functions for reducing the complexity of a PyPSA network in temporal dimension by +""" This module contains functions for reducing the complexity of a PyPSA + network in temporal dimension by a) downsampling to every n-th snapshot b) clustering to typical periods (eg days, weeks) c) clustering to segments of variable length @@ -31,7 +32,6 @@ import pandas as pd if "READTHEDOCS" not in os.environ: - import pyomo.environ as po import tsam.timeseriesaggregation as tsam __copyright__ = ( @@ -40,12 +40,14 @@ "Centre for Sustainable Energy Systems" ) __license__ = "GNU Affero General Public License Version 3 (AGPL-3.0)" -__author__ = "ClaraBuettner, ulfmueller, KathiEsterl, simnh, wheitkoetter, BartelsJ, AmeliaNadal" +__author__ = """ClaraBuettner, ulfmueller, KathiEsterl, simnh, wheitkoetter, + BartelsJ, AmeliaNadal""" def snapshot_clustering(self): """ - Function to call the snapshot clustering function with the respecting method and settings. + Function to call the snapshot clustering function with the respecting + method and settings. Raises ------ @@ -58,9 +60,9 @@ def snapshot_clustering(self): """ - if self.args["snapshot_clustering"]["active"] == True: + if self.args["snapshot_clustering"]["active"]: # save second network for optional dispatch disaggregation - if self.args["temporal_disaggregation"]["active"] == True: + if self.args["temporal_disaggregation"]["active"]: self.network_tsa = self.network.copy() if self.args["snapshot_clustering"]["method"] == "segmentation": @@ -84,7 +86,8 @@ def snapshot_clustering(self): ) else: raise ValueError( - "Type of clustering should be 'typical_periods' or 'segmentation'" + """Type of clustering should be 'typical_periods' or + 'segmentation'""" ) @@ -98,7 +101,8 @@ def tsam_cluster( segm_hoursperperiod=24, ): """ - Conducts the clustering of the snapshots for temporal aggregation with the respecting method. + Conducts the clustering of the snapshots for temporal aggregation with the + respecting method. Parameters ---------- @@ -108,14 +112,16 @@ def tsam_cluster( Number of clusters for typical_periods. The default is 10. how : {'daily', 'weekly', 'monthly'}, optional Definition of period for typical_periods. The default is 'daily'. - extremePeriodMethod : {'None','append','new_cluster_center', 'replace_cluster_center'}, optional - Method to consider extreme snapshots in reduced timeseries. The default is 'None'. + extremePeriodMethod : {'None','append','new_cluster_center', + 'replace_cluster_center'}, optional Method to consider extreme + snapshots in reduced timeseries. The default is 'None'. segmentation : boolean, optional Argument to activate segmenation method. The default is False. segment_no : int, optional Number of segments for segmentation. The default is 10. segm_hoursperperiod : int, optional - Only for segmentation, ensures to cluster to segments considering all snapshots. The default is 24. + Only for segmentation, ensures to cluster to segments considering all + snapshots. The default is 24. Returns ------- @@ -200,8 +206,9 @@ def tsam_cluster( timeseries_creator = aggregation.createTypicalPeriods() timeseries = timeseries_creator.copy() - # If Segmentation is True, insert 'Dates' and 'SegmentNo' column in timeseries - if segmentation == True: + # If Segmentation is True, insert 'Dates' and 'SegmentNo' column in + # timeseries + if segmentation: weights = timeseries.index.get_level_values(2) dates_df = timeseries_df.index.get_level_values(0) dates = [] @@ -230,7 +237,7 @@ def tsam_cluster( clusterOrder = aggregation.clusterOrder clusterCenterIndices = aggregation.clusterCenterIndices - if segmentation == True: + if segmentation: if extremePeriodMethod != "None": timeseries = segmentation_extreme_periods( timeseries_df, timeseries, extremePeriodMethod @@ -288,7 +295,8 @@ def tsam_cluster( df_cluster = pd.DataFrame( { "Cluster": clusterOrder, # Cluster of the day - "RepresentativeDay": representative_day, # representative day of the cluster + "RepresentativeDay": representative_day, # representative day of + # the cluster "last_hour_RepresentativeDay": last_hour_datetime, } ) # last hour of the cluster @@ -324,8 +332,9 @@ def segmentation_extreme_periods( Dataframe wit timeseries to cluster. timeseries : pd.DataFrame Information on segments after segmentation. - extremePeriodMethod : {'None','append','new_cluster_center', 'replace_cluster_center'}, optional - Method to consider extreme snapshots in reduced timeseries. The default is 'None'. + extremePeriodMethod : {'None','append','new_cluster_center', + 'replace_cluster_center'}, optional method to consider extreme + snapshots in reduced timeseries. The default is 'None'. Raises ------ @@ -461,7 +470,8 @@ def segmentation_extreme_periods( else: raise ValueError( - "Choose 'append' or 'replace_cluster_center' for consideration of extreme periods with segmentation method" + """Choose 'append' or 'replace_cluster_center' for + consideration of extreme periods with segmentation method""" ) # add timestep if it is not already calculated @@ -582,7 +592,8 @@ def segmentation_extreme_periods( else: raise ValueError( - "Choose 'append' or 'replace_cluster_center' for consideration of extreme periods with segmentation method" + """Choose 'append' or 'replace_cluster_center' for + consideration of extreme periods with segmentation method""" ) if "row_no" in timeseries.columns: @@ -599,7 +610,8 @@ def run( extreme_periods="None", ): """ - Function to call the respecting snapshot clustering function and export the result to a csv-file. + Function to call the respecting snapshot clustering function and export the + result to a csv-file. Parameters ---------- @@ -611,8 +623,10 @@ def run( Definition of period for typical_periods. The default is 'daily'. segmented_to : int, optional Number of segments for segmentation. The default is False. - extremePeriodMethod : {'None','append','new_cluster_center', 'replace_cluster_center'}, optional - Method to consider extreme snapshots in reduced timeseries. The default is 'None'. + extremePeriodMethod : {'None','append','new_cluster_center', + 'replace_cluster_center'}, optional + Method to consider extreme snapshots in reduced timeseries. + The default is 'None'. Returns ------- @@ -650,7 +664,7 @@ def run( segm_hoursperperiod=network.snapshots.size, ) - if segmentation != False: + if not segmentation: pd.DataFrame( timeseries.reset_index(), columns=["dates", "SegmentNo", "SegmentDuration"], @@ -660,16 +674,12 @@ def run( else: if how == "daily": howie = "days" - path = "typical_days" elif how == "weekly": howie = "weeks" - path = "typical_weeks" elif how == "monthly": howie = "months" - path = "typical_months" elif how == "hourly": howie = "hours" - path = "typical_hours" df_cluster.to_csv( "cluster_typical-periods=" + str(n_clusters) + howie + ".csv" ) @@ -799,8 +809,8 @@ def skip_snapshots(self): # save second network for optional dispatch disaggregation if ( - self.args["temporal_disaggregation"]["active"] == True - and self.args["snapshot_clustering"]["active"] == False + self.args["temporal_disaggregation"]["active"] + and not self.args["snapshot_clustering"]["active"] ): self.network_tsa = self.network.copy() From c9458470417c902cc1e85758299520c6ce5c384e Mon Sep 17 00:00:00 2001 From: ClaraBuettner Date: Tue, 19 Sep 2023 15:59:44 +0200 Subject: [PATCH 094/136] Fix flake8 errors --- etrago/cluster/gas.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/etrago/cluster/gas.py b/etrago/cluster/gas.py index 61b23dc90..05f350e99 100644 --- a/etrago/cluster/gas.py +++ b/etrago/cluster/gas.py @@ -31,6 +31,7 @@ busmap_by_kmeans, ) from six import iteritems + import logging import numpy as np import pandas as pd import pypsa.io as io @@ -40,7 +41,8 @@ kmedoids_dijkstra_clustering, sum_with_inf, ) - from etrago.tools.utilities import * + +logger = logging.getLogger(__name__) __copyright__ = ( "Flensburg University of Applied Sciences, " @@ -998,8 +1000,8 @@ def run_spatial_clustering_gas(self): self.update_busmap(busmap) logger.info( - """GAS Network clustered to {} DE-buses and {} foreign buses - with {} algorithm.""".format( + """GAS Network clustered to {} DE-buses and {} foreign buses + with {} algorithm.""".format( len( self.network.buses.loc[ (self.network.buses.carrier == "CH4") From 25372f3afcb777a80b6c2b14b84fc83545151c6b Mon Sep 17 00:00:00 2001 From: ClaraBuettner Date: Tue, 19 Sep 2023 16:01:42 +0200 Subject: [PATCH 095/136] Specify exception --- etrago/cluster/electrical.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/etrago/cluster/electrical.py b/etrago/cluster/electrical.py index 11c114b0b..a70a3b289 100755 --- a/etrago/cluster/electrical.py +++ b/etrago/cluster/electrical.py @@ -967,7 +967,7 @@ def calc_availability_factor(gen): else: try: cf = fixed_capacity_fac[gen["carrier"]] - except: + except KeyError: print(gen) cf = 1 return cf From 075749ab98d84cc8b4603d020bd5d975750e6d85 Mon Sep 17 00:00:00 2001 From: ClaraBuettner Date: Tue, 19 Sep 2023 16:02:41 +0200 Subject: [PATCH 096/136] Apply black --- etrago/cluster/__init__.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/etrago/cluster/__init__.py b/etrago/cluster/__init__.py index cc95f41d1..5dd475b7e 100644 --- a/etrago/cluster/__init__.py +++ b/etrago/cluster/__init__.py @@ -5,5 +5,3 @@ __copyright__ = "tba" __license__ = "tba" __author__ = "tba" - - From 90785d55ceed970958fe7211ac504170dc15be91 Mon Sep 17 00:00:00 2001 From: ClaraBuettner Date: Tue, 19 Sep 2023 16:05:35 +0200 Subject: [PATCH 097/136] Remove unused imports --- etrago/tools/calc_results.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/etrago/tools/calc_results.py b/etrago/tools/calc_results.py index aa6c6cb21..c54cd28a2 100755 --- a/etrago/tools/calc_results.py +++ b/etrago/tools/calc_results.py @@ -25,9 +25,7 @@ if "READTHEDOCS" not in os.environ: import logging - import time - import numpy as np import pandas as pd logger = logging.getLogger(__name__) From 3c96c011fe01a21c4daa2491f2f7c9e34f829478 Mon Sep 17 00:00:00 2001 From: ClaraBuettner Date: Tue, 19 Sep 2023 16:11:36 +0200 Subject: [PATCH 098/136] Fix flake8 errors --- etrago/tools/execute.py | 44 ++++++++++++++++++++++------------------- 1 file changed, 24 insertions(+), 20 deletions(-) diff --git a/etrago/tools/execute.py b/etrago/tools/execute.py index f7a13412a..81844c226 100755 --- a/etrago/tools/execute.py +++ b/etrago/tools/execute.py @@ -146,7 +146,8 @@ def run_lopf(etrago, extra_functionality, method): if method["pyomo"]: # repeat the optimization for all slices for i in range(0, no_slices): - # keep information on the initial state of charge for the respectng slice + # keep information on the initial state of charge for the + # respectng slice initial = transits[i - 1] soc_initial = etrago.conduct_dispatch_disaggregation.loc[ [etrago.network_tsa.snapshots[initial]] @@ -314,7 +315,7 @@ def iterate_lopf( for i in range(1, (1 + n_iter)): run_lopf(etrago, extra_functionality, method) - if args["csv_export"] != False: + if not args["csv_export"]: path_it = path + "/lopf_iteration_" + str(i) etrago.export_to_csv(path_it) @@ -348,7 +349,7 @@ def iterate_lopf( i += 1 - if args["csv_export"] != False: + if not args["csv_export"]: path_it = path + "/lopf_iteration_" + str(i) etrago.export_to_csv(path_it) @@ -391,7 +392,7 @@ def lopf(self): z = (y - x) / 60 logger.info("Time for LOPF [min]: {}".format(round(z, 2))) - if self.args["csv_export"] != False: + if not self.args["csv_export"]: path = self.args["csv_export"] if self.args["temporal_disaggregation"]["active"] is True: path = path + "/temporally_reduced" @@ -431,14 +432,16 @@ def dispatch_disaggregation(self): transits = transits.insert( (len(transits)), self.network.snapshots[-1] ) - # for stores, exclude emob and dsm because of their special constraints + # for stores, exclude emob and dsm because of their special + # constraints sto = self.network.stores[ ~self.network.stores.carrier.isin( ["battery_storage", "battery storage", "dsm"] ) ] - # save state of charge of storage units and stores at those transition snapshots + # save state of charge of storage units and stores at those + # transition snapshots self.conduct_dispatch_disaggregation = pd.DataFrame( columns=self.network.storage_units.index.append(sto.index), index=transits, @@ -501,7 +504,7 @@ def dispatch_disaggregation(self): ) self.network.stores.e_cyclic = self.network_tsa.stores.e_cyclic - if self.args["csv_export"] != False: + if not self.args["csv_export"]: path = self.args["csv_export"] self.export_to_csv(path) self.export_to_csv(path + "/temporal_disaggregaton") @@ -530,8 +533,8 @@ def pf_post_lopf(etrago, calc_losses=False): Function that prepares and runs non-linar load flow using PyPSA pf. If crossborder lines are DC-links, pf is only applied on german network. Crossborder flows are still considerd due to the active behavior of links. - To return a network containing the whole grid, the optimised solution of the - foreign components can be added afterwards. + To return a network containing the whole grid, the optimised solution of + the foreign components can be added afterwards. Parameters ---------- @@ -807,7 +810,7 @@ def import_gen_from_links(network): # if foreign lines are DC, execute pf only on sub_network in Germany if (args["foreign_lines"]["carrier"] == "DC") or ( - (args["scn_extension"] != None) + (args["scn_extension"] is None) and ("BE_NO_NEP 2035" in args["scn_extension"]) ): foreign_bus, foreign_comp, foreign_series = drop_foreign_components( @@ -831,7 +834,7 @@ def import_gen_from_links(network): # Find out the name of the main subnetwork main_subnet = str(network.buses.sub_network.value_counts().argmax()) - # Delete very small p_set and q_set values to avoid problems with the solver + # Delete very small p_set and q_set values to avoid problems when solving network.generators_t["p_set"][ np.abs(network.generators_t["p_set"]) < 0.001 ] = 0 @@ -880,7 +883,7 @@ def import_gen_from_links(network): if ( (args["foreign_lines"]["carrier"] == "DC") or ( - (args["scn_extension"] != None) + (args["scn_extension"] is None) and ("BE_NO_NEP 2035" in args["scn_extension"]) ) ) and etrago.args["pf_post_lopf"]["add_foreign_lopf"]: @@ -892,7 +895,7 @@ def import_gen_from_links(network): foreign_series[comp][attr], comp, attr ) - if args["csv_export"] != False: + if not args["csv_export"]: path = args["csv_export"] + "/pf_post_lopf" etrago.export_to_csv(path) pf_solve.to_csv(os.path.join(path, "pf_solution.csv"), index=True) @@ -928,7 +931,7 @@ def distribute_q(network, allocation="p_nom"): ac_bus = network.buses[network.buses.carrier == "AC"] gen_elec = network.generators[ - (network.generators.bus.isin(ac_bus.index) == True) + (network.generators.bus.isin(ac_bus.index)) & (network.generators.carrier != "load shedding") ].carrier.unique() @@ -966,9 +969,9 @@ def distribute_q(network, allocation="p_nom"): ) else: print( - """WARNING: Distribution of reactive power based on active power is - currently outdated for sector coupled models. This process - will continue with the option allocation = 'p_nom'""" + """WARNING: Distribution of reactive power based on active + power is currently outdated for sector coupled models. This + process will continue with the option allocation = 'p_nom'""" ) allocation = "p_nom" @@ -989,7 +992,7 @@ def distribute_q(network, allocation="p_nom"): ac_bus = network.buses[network.buses.carrier == "AC"] gen_elec = network.generators[ - (network.generators.bus.isin(ac_bus.index) == True) + (network.generators.bus.isin(ac_bus.index)) & (network.generators.carrier != "load shedding") & (network.generators.p_nom > 0) ].sort_index() @@ -1061,8 +1064,9 @@ def calc_line_losses(network, converged): s0_lines = (network.lines_t.p0**2 + network.lines_t.q0**2).apply( np.sqrt ) - # in case some snapshots did not converge, discard them from the calculation - s0_lines.loc[converged[converged == False].index, :] = np.nan + # in case some snapshots did not converge, discard them from the + # calculation + s0_lines.loc[converged[converged is False].index, :] = np.nan # calculate current I = S / U [in A] i0_lines = np.multiply(s0_lines, 1000000) / np.multiply( network.lines.v_nom, 1000 From e64585abae88cdb857f53a70636e6298a5b9c834 Mon Sep 17 00:00:00 2001 From: ClaraBuettner Date: Tue, 19 Sep 2023 16:15:33 +0200 Subject: [PATCH 099/136] Fix flake8 error --- etrago/tools/io.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/etrago/tools/io.py b/etrago/tools/io.py index c87bda98b..0ca049d81 100644 --- a/etrago/tools/io.py +++ b/etrago/tools/io.py @@ -171,7 +171,7 @@ def configure_timeindex(self): ) ) - self.timeindex = timeindex[self.start_snapshot - 1 : self.end_snapshot] + self.timeindex = timeindex[self.start_snapshot - 1: self.end_snapshot] def id_to_source(self): ormclass = self._mapped["Source"] @@ -290,7 +290,7 @@ def series_fetch_by_relname(self, network, name, pypsa_name): vars()[f"egon_etrago_{name.lower()}_timeseries"], index_col ), getattr(vars()[f"egon_etrago_{name.lower()}_timeseries"], col)[ - self.start_snapshot : self.end_snapshot + self.start_snapshot: self.end_snapshot ], ).filter( vars()[f"egon_etrago_{name.lower()}_timeseries"].scn_name From f0a8141edfe8caedb6ac75c4e25b68c28ff0bbf1 Mon Sep 17 00:00:00 2001 From: ClaraBuettner Date: Tue, 19 Sep 2023 16:15:54 +0200 Subject: [PATCH 100/136] Exclude appl.py from noxfile --- noxfile.py | 1 - 1 file changed, 1 deletion(-) diff --git a/noxfile.py b/noxfile.py index fa2cf9c7a..005264069 100644 --- a/noxfile.py +++ b/noxfile.py @@ -4,7 +4,6 @@ import nox cleaned = [ - "etrago/appl.py", "etrago/cluster/disaggregation.py", "etrago/cluster/electrical.py", "etrago/cluster/gas.py", From 76a0c69ad1b69d91be419a7f9cecc7c6ef2c09ec Mon Sep 17 00:00:00 2001 From: ClaraBuettner Date: Tue, 19 Sep 2023 16:16:09 +0200 Subject: [PATCH 101/136] Temporary exclude E722 and W605 from flake8 checks --- noxfile.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/noxfile.py b/noxfile.py index 005264069..8de5d1468 100644 --- a/noxfile.py +++ b/noxfile.py @@ -56,7 +56,7 @@ def flake8(session): """Check for happy little style accidents with `flake8`.""" setdefaults(session) session.install("Flake8-pyproject", "flake8") - session.run("flake8", *cleaned) + session.run("flake8", "--ignore=E722, W605", *cleaned) @nox.session(python=["3", "3.8", "3.9", "3.10", "3.11"]) From dc09191d72fced4de9798cd3a01b26f05a8ff113 Mon Sep 17 00:00:00 2001 From: ClaraBuettner Date: Tue, 19 Sep 2023 16:19:58 +0200 Subject: [PATCH 102/136] Apply black --- etrago/tools/io.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/etrago/tools/io.py b/etrago/tools/io.py index 0ca049d81..c87bda98b 100644 --- a/etrago/tools/io.py +++ b/etrago/tools/io.py @@ -171,7 +171,7 @@ def configure_timeindex(self): ) ) - self.timeindex = timeindex[self.start_snapshot - 1: self.end_snapshot] + self.timeindex = timeindex[self.start_snapshot - 1 : self.end_snapshot] def id_to_source(self): ormclass = self._mapped["Source"] @@ -290,7 +290,7 @@ def series_fetch_by_relname(self, network, name, pypsa_name): vars()[f"egon_etrago_{name.lower()}_timeseries"], index_col ), getattr(vars()[f"egon_etrago_{name.lower()}_timeseries"], col)[ - self.start_snapshot: self.end_snapshot + self.start_snapshot : self.end_snapshot ], ).filter( vars()[f"egon_etrago_{name.lower()}_timeseries"].scn_name From a6634a6f09b7862315d0d75af48300593fe5c6fe Mon Sep 17 00:00:00 2001 From: ClaraBuettner Date: Tue, 19 Sep 2023 16:21:38 +0200 Subject: [PATCH 103/136] Apply isort --- etrago/cluster/gas.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/etrago/cluster/gas.py b/etrago/cluster/gas.py index 05f350e99..b25f64e6b 100644 --- a/etrago/cluster/gas.py +++ b/etrago/cluster/gas.py @@ -24,6 +24,8 @@ import os if "READTHEDOCS" not in os.environ: + import logging + from pypsa import Network from pypsa.networkclustering import ( aggregatebuses, @@ -31,7 +33,6 @@ busmap_by_kmeans, ) from six import iteritems - import logging import numpy as np import pandas as pd import pypsa.io as io From 55ba752e3ebf38a19ccb85f5ab464107ff20dee5 Mon Sep 17 00:00:00 2001 From: ClaraBuettner Date: Wed, 20 Sep 2023 09:16:17 +0200 Subject: [PATCH 104/136] Fix comparisions to None and True --- etrago/cluster/electrical.py | 3 ++- etrago/cluster/spatial.py | 2 +- etrago/tools/execute.py | 4 ++-- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/etrago/cluster/electrical.py b/etrago/cluster/electrical.py index a70a3b289..44fc55266 100755 --- a/etrago/cluster/electrical.py +++ b/etrago/cluster/electrical.py @@ -178,7 +178,8 @@ def adjust_no_electric_network(etrago, busmap, cluster_met): & (network2.links["carrier"] == map_carrier[carry]) ].copy() df["elec"] = df["bus0"].isin(busmap.keys()) - df = df[df["elec"] is True] + + df = df[df["elec"]] if len(df) > 0: bus_hv = df["bus0"][0] diff --git a/etrago/cluster/spatial.py b/etrago/cluster/spatial.py index 7499ae197..be78296a1 100755 --- a/etrago/cluster/spatial.py +++ b/etrago/cluster/spatial.py @@ -98,7 +98,7 @@ def nan_links(x): def ext_storage(x): - v = any(x[x is True]) + v = any(x[x]) return v diff --git a/etrago/tools/execute.py b/etrago/tools/execute.py index 81844c226..1175ef9e1 100755 --- a/etrago/tools/execute.py +++ b/etrago/tools/execute.py @@ -810,7 +810,7 @@ def import_gen_from_links(network): # if foreign lines are DC, execute pf only on sub_network in Germany if (args["foreign_lines"]["carrier"] == "DC") or ( - (args["scn_extension"] is None) + (args["scn_extension"] is not None) and ("BE_NO_NEP 2035" in args["scn_extension"]) ): foreign_bus, foreign_comp, foreign_series = drop_foreign_components( @@ -883,7 +883,7 @@ def import_gen_from_links(network): if ( (args["foreign_lines"]["carrier"] == "DC") or ( - (args["scn_extension"] is None) + (args["scn_extension"] is not None) and ("BE_NO_NEP 2035" in args["scn_extension"]) ) ) and etrago.args["pf_post_lopf"]["add_foreign_lopf"]: From 51d7ee01fcbd2125eee38edf2202d4d153b2f99f Mon Sep 17 00:00:00 2001 From: CarlosEpia Date: Mon, 25 Sep 2023 11:54:05 +0200 Subject: [PATCH 105/136] include snapshots in dlr plot and black --- etrago/tools/plot.py | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/etrago/tools/plot.py b/etrago/tools/plot.py index 3742e9538..75e4c09c1 100644 --- a/etrago/tools/plot.py +++ b/etrago/tools/plot.py @@ -25,6 +25,7 @@ import logging import os +from etrago.tools.execute import import_gen_from_links from matplotlib import pyplot as plt from matplotlib.legend_handler import HandlerPatch from matplotlib.patches import Circle, Ellipse @@ -855,10 +856,11 @@ def calc_storage_expansion_per_bus( batteries = network.storage_units[ network.storage_units.carrier == "battery" ] - #breakpoint() battery_distribution = ( - (network.storage_units.p_nom_opt[batteries.index] - - network.storage_units.p_nom_min[batteries.index]) + ( + network.storage_units.p_nom_opt[batteries.index] + - network.storage_units.p_nom_min[batteries.index] + ) .groupby(network.storage_units.bus) .sum() .reindex(network.buses.index, fill_value=0.0) @@ -1589,7 +1591,6 @@ def calc_dc_loading(network, timesteps): & (network.links.length == row["length"]) ] ).empty: - links_df = network.links.index[ (network.links.bus0 == row["bus1"]) & (network.links.bus1 == row["bus0"]) @@ -2483,7 +2484,7 @@ def plot_grid( plot_background_grid(all_network, ax) if ext_width is not False: - line_widths = (line_colors / ext_width) + line_widths = line_colors / ext_width link_widths = link_colors.apply( lambda x: x / ext_width if x != 0 else 0 ) @@ -2495,7 +2496,7 @@ def plot_grid( link_colors = link_colors.mul(1e-3) line_colors = line_colors.mul(1e-3) - + elif line_colors == "expansion_rel": title = "Network expansion" label = "network expansion in %" @@ -2539,6 +2540,7 @@ def plot_grid( line_loading = network.lines_t.p0.mul( 1 / (network.lines.s_nom_opt * network.lines.s_max_pu) ).abs() + line_loading = line_loading.iloc[timesteps, :] # keep only the capacity allowed by dlr line_loading = line_loading - 1 dlr_usage = ( @@ -2825,12 +2827,14 @@ def plot_grid( # Set fixed boundaries if selected in parameters if not boundaries: boundaries = [ - min(round(line_colors.min(),1), round(link_colors.min(),1)), + min(round(line_colors.min(), 1), round(link_colors.min(), 1)), max(round(line_colors.max()), round(link_colors.max())), ] # Create ticks for legend - v = [round(x,1) for x in np.linspace(boundaries[0], boundaries[1], 101)] + v = [ + round(x, 1) for x in np.linspace(boundaries[0], boundaries[1], 101) + ] for l_collection in ll: l_collection.set_clim(boundaries[0], boundaries[1]) From 6e37a64c50c84374c944d2a8b796ab87280d5977 Mon Sep 17 00:00:00 2001 From: ClaraBuettner Date: Mon, 25 Sep 2023 13:23:40 +0200 Subject: [PATCH 106/136] Add readthedocs configuration file --- .readthedocs.yaml | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 .readthedocs.yaml diff --git a/.readthedocs.yaml b/.readthedocs.yaml new file mode 100644 index 000000000..dff7fe599 --- /dev/null +++ b/.readthedocs.yaml @@ -0,0 +1,22 @@ +# .readthedocs.yaml +# Read the Docs configuration file +# See https://docs.readthedocs.io/en/stable/config-file/v2.html for details + +# Required +version: 2 + +# Set the version of Python and other tools you might need +build: + os: ubuntu-22.04 + tools: + python: "3.11" + +# Build documentation in the docs/ directory with Sphinx +sphinx: + configuration: docs/conf.py + +# We recommend specifying your dependencies to enable reproducible builds: +# https://docs.readthedocs.io/en/stable/guides/reproducible-builds.html +python: + install: + - requirements: requirements-docs.txt \ No newline at end of file From bcbb30c9dea17fbeb8faad9336bc11f1b448bd89 Mon Sep 17 00:00:00 2001 From: ClaraBuettner Date: Mon, 25 Sep 2023 13:31:12 +0200 Subject: [PATCH 107/136] Fix name of requirements file --- .readthedocs.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.readthedocs.yaml b/.readthedocs.yaml index dff7fe599..d1786722f 100644 --- a/.readthedocs.yaml +++ b/.readthedocs.yaml @@ -19,4 +19,4 @@ sphinx: # https://docs.readthedocs.io/en/stable/guides/reproducible-builds.html python: install: - - requirements: requirements-docs.txt \ No newline at end of file + - requirements: requirements-doc.txt From 6108ad5d5ca018516facf757930105bff0047375 Mon Sep 17 00:00:00 2001 From: ClaraBuettner Date: Mon, 25 Sep 2023 13:36:45 +0200 Subject: [PATCH 108/136] Update path to conf.py --- .readthedocs.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.readthedocs.yaml b/.readthedocs.yaml index d1786722f..b0130f204 100644 --- a/.readthedocs.yaml +++ b/.readthedocs.yaml @@ -13,7 +13,7 @@ build: # Build documentation in the docs/ directory with Sphinx sphinx: - configuration: docs/conf.py + configuration: doc/conf.py # We recommend specifying your dependencies to enable reproducible builds: # https://docs.readthedocs.io/en/stable/guides/reproducible-builds.html From 4a2e2020316478a50b0da6e1c21996e4ab8e5cc1 Mon Sep 17 00:00:00 2001 From: ClaraBuettner Date: Mon, 25 Sep 2023 13:53:06 +0200 Subject: [PATCH 109/136] Change python version --- .readthedocs.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.readthedocs.yaml b/.readthedocs.yaml index b0130f204..629eb1fa8 100644 --- a/.readthedocs.yaml +++ b/.readthedocs.yaml @@ -9,7 +9,7 @@ version: 2 build: os: ubuntu-22.04 tools: - python: "3.11" + python: "3.8" # Build documentation in the docs/ directory with Sphinx sphinx: From 7be5726252b8d4e095965b6e24e9a8f73050b182 Mon Sep 17 00:00:00 2001 From: ClaraBuettner Date: Mon, 25 Sep 2023 14:08:44 +0200 Subject: [PATCH 110/136] Update package versions, trying to fix undefined variable 'key' --- requirements-doc.txt | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/requirements-doc.txt b/requirements-doc.txt index 383cf8367..99de59dee 100644 --- a/requirements-doc.txt +++ b/requirements-doc.txt @@ -1,7 +1,7 @@ # Packages for read the docs # Using single requirments for docs, see: # https://github.com/rtfd/readthedocs.org/issues/2070 -sphinx_rtd_theme +sphinx_rtd_theme == 1.3.0 pypsa == 0.20.1 numpydoc sqlalchemy @@ -9,4 +9,5 @@ geoalchemy2 matplotlib nbsphinx saio -pyomo != 6.4.3 \ No newline at end of file +pyomo != 6.4.3 +sphinx From e5a8bcf40eb3eb54c769d69bebafed99693bf2a8 Mon Sep 17 00:00:00 2001 From: ClaraBuettner Date: Mon, 25 Sep 2023 14:56:58 +0200 Subject: [PATCH 111/136] Add shifted_energy function to etrago object --- etrago/tools/network.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/etrago/tools/network.py b/etrago/tools/network.py index 8bd954326..95dbf3f3c 100644 --- a/etrago/tools/network.py +++ b/etrago/tools/network.py @@ -72,6 +72,7 @@ plot_h2_summary, plot_heat_loads, plot_heat_summary, + shifted_energy, ) from etrago.tools.utilities import ( add_missing_components, @@ -321,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) From c081a174533fda2a10d925e938ecb1d48925cfba Mon Sep 17 00:00:00 2001 From: ClaraBuettner Date: Mon, 25 Sep 2023 15:42:28 +0200 Subject: [PATCH 112/136] Import db functions from etrago --- etrago/tools/network.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/etrago/tools/network.py b/etrago/tools/network.py index a98a45f1e..eeb8dd02b 100644 --- a/etrago/tools/network.py +++ b/etrago/tools/network.py @@ -30,14 +30,13 @@ import pandas as pd if "READTHEDOCS" not in os.environ: - from egoio.tools import db + from etrago.tools import db from etrago import __version__ from etrago.cluster.disaggregation import run_disaggregation 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 import db from etrago.tools.calc_results import calc_etrago_results from etrago.tools.execute import ( dispatch_disaggregation, From 9cb4e5f3fb00b46060bcbfe0c316a2dd31226def Mon Sep 17 00:00:00 2001 From: ClaraBuettner Date: Mon, 25 Sep 2023 15:44:12 +0200 Subject: [PATCH 113/136] Apply isort --- etrago/tools/utilities.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/etrago/tools/utilities.py b/etrago/tools/utilities.py index 9f99d4598..d44171bc1 100755 --- a/etrago/tools/utilities.py +++ b/etrago/tools/utilities.py @@ -36,10 +36,11 @@ import sqlalchemy.exc if "READTHEDOCS" not in os.environ: - from etrago.tools import db from shapely.geometry import Point import geopandas as gpd + from etrago.tools import db + logger = logging.getLogger(__name__) From bcc7c070aa6e947ae965ca2514b96cec1bac4cd7 Mon Sep 17 00:00:00 2001 From: ClaraBuettner Date: Tue, 26 Sep 2023 08:44:07 +0200 Subject: [PATCH 114/136] Revert "Update package versions, trying to fix undefined variable 'key'" This reverts commit 7be5726252b8d4e095965b6e24e9a8f73050b182. --- requirements-doc.txt | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/requirements-doc.txt b/requirements-doc.txt index 99de59dee..383cf8367 100644 --- a/requirements-doc.txt +++ b/requirements-doc.txt @@ -1,7 +1,7 @@ # Packages for read the docs # Using single requirments for docs, see: # https://github.com/rtfd/readthedocs.org/issues/2070 -sphinx_rtd_theme == 1.3.0 +sphinx_rtd_theme pypsa == 0.20.1 numpydoc sqlalchemy @@ -9,5 +9,4 @@ geoalchemy2 matplotlib nbsphinx saio -pyomo != 6.4.3 -sphinx +pyomo != 6.4.3 \ No newline at end of file From 8bed6ca2144a78fefb1bb71f9e86fa28444bce79 Mon Sep 17 00:00:00 2001 From: ClaraBuettner Date: Tue, 26 Sep 2023 08:46:05 +0200 Subject: [PATCH 115/136] Set lower version for sphinx_rtd_theme --- requirements-doc.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements-doc.txt b/requirements-doc.txt index 383cf8367..864c63277 100644 --- a/requirements-doc.txt +++ b/requirements-doc.txt @@ -1,7 +1,7 @@ # Packages for read the docs # Using single requirments for docs, see: # https://github.com/rtfd/readthedocs.org/issues/2070 -sphinx_rtd_theme +sphinx_rtd_theme > 1.2.2 pypsa == 0.20.1 numpydoc sqlalchemy From f33655dc33463a2f2445178ec29aaf2463d34fbd Mon Sep 17 00:00:00 2001 From: CarlosEpia Date: Tue, 26 Sep 2023 10:07:25 +0200 Subject: [PATCH 116/136] fix dlr plot --- etrago/tools/plot.py | 18 +++++++----------- 1 file changed, 7 insertions(+), 11 deletions(-) diff --git a/etrago/tools/plot.py b/etrago/tools/plot.py index 75e4c09c1..089069969 100644 --- a/etrago/tools/plot.py +++ b/etrago/tools/plot.py @@ -1673,7 +1673,7 @@ def calc_network_expansion(network, method="abs", ext_min=0.1): Returns ------- - network_c : :class:`pypsa.Network + network : :class:`pypsa.Network Whole network including not extended lines extension_lines : pandas.Series AC-line expansion @@ -2335,7 +2335,7 @@ def plot_grid( * 'expansion_rel': network expansion in p.u. of existing capacity * 'q_flow_max': maximal reactive flows * 'dlr': energy above nominal capacity - * 'grey': plot all lines and DC linkd grey colored + * 'grey': plot all lines and DC links grey colored bus_sizes : float, optional Size of buses. The default is 0.001. @@ -2527,16 +2527,12 @@ def plot_grid( title = "Dynamic line rating" label = "TWh above nominal capacity" plot_background_grid(network, ax) - # Extract branch_capacity_factors - bcf_hv = self.args["branch_capacity_factor"]["HV"] - bcf_ehv = self.args["branch_capacity_factor"]["eHV"] - # calc min capacity per line in the given period + + # calc min capacity per line in the given period: Since lines with + # different original voltage level could be aggregated during the + # clustering, the security factors can be values in between the values + # provided in the args for branch_capacity_factor. network.lines.s_max_pu = network.lines_t.s_max_pu.min() - network.lines.s_max_pu[network.lines.s_max_pu < 0.7] = 0.5 - network.lines.s_max_pu[ - (network.lines.s_max_pu > 0.5) & (network.lines.s_max_pu < 0.70) - ] = (bcf_hv + bcf_ehv) / 2 - network.lines.s_max_pu[network.lines.s_max_pu >= 0.70] = 0.70 line_loading = network.lines_t.p0.mul( 1 / (network.lines.s_nom_opt * network.lines.s_max_pu) ).abs() From f6086973cef1a1fd444d24f28d73def775c11d99 Mon Sep 17 00:00:00 2001 From: CarlosEpia Date: Tue, 26 Sep 2023 11:08:35 +0200 Subject: [PATCH 117/136] fix plots expansion_abs/rel --- etrago/tools/plot.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/etrago/tools/plot.py b/etrago/tools/plot.py index 089069969..ba4de1133 100644 --- a/etrago/tools/plot.py +++ b/etrago/tools/plot.py @@ -1721,7 +1721,7 @@ def calc_network_expansion(network, method="abs", ext_min=0.1): extension_links = pd.DataFrame( data=network_c.links, index=network_c.links.index ) - extension_links[extension_links.carrier != "DC"] = 0 + extension_links = ( 100 * (network_c.links.p_nom_opt - network_c.links.p_nom_min) @@ -1735,7 +1735,7 @@ def calc_network_expansion(network, method="abs", ext_min=0.1): extension_links = pd.DataFrame( data=network_c.links, index=network_c.links.index ) - extension_links[extension_links.carrier != "DC"] = 0 + extension_links = network_c.links.p_nom_opt - network_c.links.p_nom_min extension_lines = pd.Series( @@ -2513,6 +2513,7 @@ def plot_grid( dc_link = network.links.index[network.links.carrier == "DC"] link_widths = pd.Series(0, index=network.links.index) link_widths.loc[dc_link] = 2 + line_widths = line_colors.apply(lambda x: 1.5 if x != 0 else 0) elif line_colors == "q_flow_max": title = "Maximum reactive power flows" label = "flow in pu" From 783ea65badfff5cd337787c3f6ae0f72bf53a07d Mon Sep 17 00:00:00 2001 From: CarlosEpia Date: Tue, 26 Sep 2023 11:47:10 +0200 Subject: [PATCH 118/136] improve plot powertoH2 --- etrago/tools/plot.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/etrago/tools/plot.py b/etrago/tools/plot.py index ba4de1133..0ab5b8276 100644 --- a/etrago/tools/plot.py +++ b/etrago/tools/plot.py @@ -2680,6 +2680,8 @@ def plot_grid( .sum() .p_nom_opt ) + if len(bus_sizes) == 0: + print("There is no PowerToH2 to plot") bus_colors = coloring()["power_to_H2"] bus_legend = "PowerToH2" bus_unit = "TW" @@ -2727,6 +2729,7 @@ def plot_grid( boundaries=[-2.5, 16, 46.8, 58], ) l3 = None + # legends for bus sizes and colors if bus_legend: handles = [] @@ -2764,7 +2767,10 @@ def plot_grid( + i ) else: - max_value = bus_sizes.max() + if len(bus_sizes) > 0: + max_value = bus_sizes.max() + else: + max_value = 0 labels.append(f"{round(max_value / bus_scaling /1000, 0)} GWh ") handles.append( make_legend_circles_for( From bca53a79db0da452589e5f6c4146d988b8eb65cc Mon Sep 17 00:00:00 2001 From: ClaraBuettner Date: Tue, 26 Sep 2023 16:41:00 +0200 Subject: [PATCH 119/136] Add dc-lines to edges of ehv clustering --- etrago/cluster/spatial.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/etrago/cluster/spatial.py b/etrago/cluster/spatial.py index 43af10b9b..7c4b4850b 100755 --- a/etrago/cluster/spatial.py +++ b/etrago/cluster/spatial.py @@ -395,6 +395,12 @@ def busmap_by_shortest_path(etrago, scn_name, fromlvl, tolvl, cpu_cores=4): transformer = connected_transformer(etrago.network, s_buses) mask = transformer.bus1.isin(buses_of_vlvl(etrago.network, tolvl)) + dc = etrago.network.links[etrago.network.links.carrier == "DC"] + dc.index = "DC_" + dc.index + lines_plus_dc = pd.concat([lines, dc]) + lines_plus_dc = lines_plus_dc[etrago.network.lines.columns] + lines_plus_dc["carrier"] = "AC" + # temporary end points, later replaced by bus1 pendant t_buses = transformer[mask].bus0 @@ -403,7 +409,7 @@ def busmap_by_shortest_path(etrago, scn_name, fromlvl, tolvl, cpu_cores=4): # graph creation edges = [ - (row.bus0, row.bus1, row.length, ix) for ix, row in lines.iterrows() + (row.bus0, row.bus1, row.length, ix) for ix, row in lines_plus_dc.iterrows() ] M = graph_from_edges(edges) From 5e566b1de1b8a126b94ef2b6efeeadb3f8d3f8ae Mon Sep 17 00:00:00 2001 From: ClaraBuettner Date: Tue, 26 Sep 2023 16:41:28 +0200 Subject: [PATCH 120/136] Add buses connected by dc lines to connected_buses --- etrago/tools/utilities.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/etrago/tools/utilities.py b/etrago/tools/utilities.py index d44171bc1..c3f29dee9 100755 --- a/etrago/tools/utilities.py +++ b/etrago/tools/utilities.py @@ -127,6 +127,8 @@ def buses_grid_linked(network, voltage_level): mask = ( network.buses.index.isin(network.lines.bus0) | (network.buses.index.isin(network.lines.bus1)) + | (network.buses.index.isin(network.links.loc[network.links.carrier=="DC", "bus0"])) + | (network.buses.index.isin(network.links.loc[network.links.carrier=="DC", "bus1"])) ) & (network.buses.v_nom.isin(voltage_level)) df = network.buses[mask] From 4579467e8bdab11abd5d4ab352691e0db7edc21e Mon Sep 17 00:00:00 2001 From: ClaraBuettner Date: Thu, 28 Sep 2023 09:40:30 +0200 Subject: [PATCH 121/136] Apply black --- etrago/tools/execute.py | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/etrago/tools/execute.py b/etrago/tools/execute.py index 28a90df74..ea643bf2f 100755 --- a/etrago/tools/execute.py +++ b/etrago/tools/execute.py @@ -551,9 +551,7 @@ def import_gen_from_links(network): gas_to_add_orig = gas_to_add.copy() gas_to_add.set_index("Generator", drop=True, inplace=True) gas_to_add = gas_to_add[ - gas_to_add.columns[ - gas_to_add.columns.isin(network.generators.columns) - ] + gas_to_add.columns[gas_to_add.columns.isin(network.generators.columns)] ] network.import_components_from_dataframe(gas_to_add, "Generator") @@ -608,9 +606,7 @@ def import_gen_from_links(network): for attr, data in df_t.items(): if not data.empty: - network.import_series_from_dataframe( - data, "Generator", attr - ) + network.import_series_from_dataframe(data, "Generator", attr) return From 5d846e5de939f4ce29a0ec423fe99ed9b659db38 Mon Sep 17 00:00:00 2001 From: ClaraBuettner Date: Thu, 28 Sep 2023 09:59:13 +0200 Subject: [PATCH 122/136] Add missing parameters --- etrago/args.json | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/etrago/args.json b/etrago/args.json index 1ebcf89a1..8693f929f 100644 --- a/etrago/args.json +++ b/etrago/args.json @@ -57,7 +57,8 @@ "method": "kmedoids-dijkstra", "n_clusters_AC": 30, "cluster_foreign_AC": false, - "n_clusters_gas": 30, + "method_gas": "kmedoids-dijkstra", + "n_clusters_gas": 17, "cluster_foreign_gas": false, "k_elec_busmap": false, "k_gas_busmap": false, @@ -68,10 +69,11 @@ "line_length_factor": 1, "remove_stubs": false, "use_reduced_coordinates": false, - "random_state":42, + "random_state": 42, "n_init": 10, "max_iter": 100, - "tol": 1e-06 + "tol": 1e-6, + "CPU_cores": 4, }, "sector_coupled_clustering": { "active": true, @@ -88,10 +90,11 @@ "snapshot_clustering": { "active": false, "method": "segmentation", + "extreme_periods": null, "how": "daily", "storage_constraints": "soc_constraints", "n_clusters": 5, - "n_segments": 5 + "n_segments": 5, }, "skip_snapshots": 5, "temporal_disaggregation": { From 3edaf2574abfb0013be44f5b37e6e29ae7ef60b1 Mon Sep 17 00:00:00 2001 From: CarlosEpia Date: Wed, 4 Oct 2023 17:36:07 +0200 Subject: [PATCH 123/136] discard DC links under 220kV --- etrago/cluster/electrical.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/etrago/cluster/electrical.py b/etrago/cluster/electrical.py index e46d6b0ff..a834e8c3a 100755 --- a/etrago/cluster/electrical.py +++ b/etrago/cluster/electrical.py @@ -320,6 +320,8 @@ def cluster_on_extra_high_voltage(etrago, busmap, with_time=True): # Dealing with links links = network.links.copy() dc_links = links[links["carrier"] == "DC"] + # Discard links connected to buses under 220 kV + dc_links = dc_links[dc_links.bus0.isin(buses.index)] links = links[links["carrier"] != "DC"] new_links = ( From c63a459fa4b57dee6c4efb1432fad2078344b6d6 Mon Sep 17 00:00:00 2001 From: CarlosEpia Date: Thu, 5 Oct 2023 08:55:39 +0200 Subject: [PATCH 124/136] create hv busmap everytime eHV clus runs --- etrago/cluster/spatial.py | 39 +++++++++++++++++++++------------------ 1 file changed, 21 insertions(+), 18 deletions(-) diff --git a/etrago/cluster/spatial.py b/etrago/cluster/spatial.py index 7c4b4850b..0cfe098f9 100755 --- a/etrago/cluster/spatial.py +++ b/etrago/cluster/spatial.py @@ -409,7 +409,8 @@ def busmap_by_shortest_path(etrago, scn_name, fromlvl, tolvl, cpu_cores=4): # graph creation edges = [ - (row.bus0, row.bus1, row.length, ix) for ix, row in lines_plus_dc.iterrows() + (row.bus0, row.bus1, row.length, ix) + for ix, row in lines_plus_dc.iterrows() ] M = graph_from_edges(edges) @@ -538,24 +539,26 @@ def fetch(): busmap = fetch() - # TODO: Or better try/except/finally - if not busmap: - print("Busmap does not exist and will be created.\n") - - cpu_cores = etrago.args["network_clustering"]["CPU_cores"] - if cpu_cores == "max": - cpu_cores = mp.cpu_count() - else: - cpu_cores = int(cpu_cores) - - busmap_by_shortest_path( - etrago, - scn_name, - fromlvl=[110], - tolvl=[220, 380, 400, 450], - cpu_cores=cpu_cores, + if busmap: + print( + "Existing busmap will be deleted and a new one will be calculated.\n" ) - busmap = fetch() + etrago.engine.execute("""DELETE FROM grid.egon_etrago_hv_busmap""") + + cpu_cores = etrago.args["network_clustering"]["CPU_cores"] + if cpu_cores == "max": + cpu_cores = mp.cpu_count() + else: + cpu_cores = int(cpu_cores) + + busmap_by_shortest_path( + etrago, + scn_name, + fromlvl=[110], + tolvl=[220, 380, 400, 450], + cpu_cores=cpu_cores, + ) + busmap = fetch() return busmap From 421777295e58211c67f5ba85b8b3750751998944 Mon Sep 17 00:00:00 2001 From: CarlosEpia Date: Thu, 5 Oct 2023 15:53:19 +0200 Subject: [PATCH 125/136] make delete_dispensable_ac_buses optional --- etrago/appl.py | 6 ++++++ etrago/cluster/spatial.py | 2 +- etrago/tools/utilities.py | 2 ++ 3 files changed, 9 insertions(+), 1 deletion(-) diff --git a/etrago/appl.py b/etrago/appl.py index cdf3f3ad8..4237ea4b8 100644 --- a/etrago/appl.py +++ b/etrago/appl.py @@ -100,6 +100,7 @@ "grid_max_abs_foreign": None, # absolute capacity per voltage level }, }, + "delete_dispensable_ac_buses": False, # bool. Find and delete unnecesary buses "generator_noise": 789456, # apply generator noise, False or seed number "extra_functionality": {}, # Choose function name or {} # Spatial Complexity: @@ -379,6 +380,11 @@ def run_etrago(args, json_path): Limit overall energy production country-wise for each generator by carrier. Set upper/lower limit in p.u. + delete_dispensable_ac_buses: bool + Choose if unnecessary buses should be identified and deleted from the + grid. This buses have no load or generation attached. Additionally, + they are just connected to two other buses. + Default: False. network_clustering_ehv : bool Choose if you want to cluster the full HV/EHV dataset down to only the EHV buses. In that case, all HV buses are assigned to their closest EHV diff --git a/etrago/cluster/spatial.py b/etrago/cluster/spatial.py index 0cfe098f9..db3cc161c 100755 --- a/etrago/cluster/spatial.py +++ b/etrago/cluster/spatial.py @@ -541,7 +541,7 @@ def fetch(): if busmap: print( - "Existing busmap will be deleted and a new one will be calculated.\n" + "Existing busmap will be deleted and a new one will be calculated." ) etrago.engine.execute("""DELETE FROM grid.egon_etrago_hv_busmap""") diff --git a/etrago/tools/utilities.py b/etrago/tools/utilities.py index c3f29dee9..dc66d2322 100755 --- a/etrago/tools/utilities.py +++ b/etrago/tools/utilities.py @@ -1081,6 +1081,8 @@ def delete_dispensable_ac_buses(etrago): None. """ + if etrago.args["delete_dispensable_ac_buses"] is False: + return def delete_buses(delete_buses, network): drop_buses = delete_buses.index.to_list() From b264ff55d06045a5272e68ecc2e0f1830b45100c Mon Sep 17 00:00:00 2001 From: ClaraBuettner Date: Thu, 5 Oct 2023 15:56:59 +0200 Subject: [PATCH 126/136] Limit version of tables, 3.9 does not support python3.8 anymore --- setup.py | 1 + 1 file changed, 1 insertion(+) diff --git a/setup.py b/setup.py index 5491991a3..7cfa7ab3e 100755 --- a/setup.py +++ b/setup.py @@ -65,6 +65,7 @@ def read(*names, **kwargs): "setuptools >= 54.2.0", "shapely", "sqlalchemy < 2", + "tables < 3.9", "tilemapbase == 0.4.5", "tsam", ], From db95efc116591e13ca38cc24dc189e118bbc8a3b Mon Sep 17 00:00:00 2001 From: CarlosEpia Date: Thu, 5 Oct 2023 15:57:16 +0200 Subject: [PATCH 127/136] use black --- etrago/tools/utilities.py | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/etrago/tools/utilities.py b/etrago/tools/utilities.py index dc66d2322..ebc858f1c 100755 --- a/etrago/tools/utilities.py +++ b/etrago/tools/utilities.py @@ -127,8 +127,16 @@ def buses_grid_linked(network, voltage_level): mask = ( network.buses.index.isin(network.lines.bus0) | (network.buses.index.isin(network.lines.bus1)) - | (network.buses.index.isin(network.links.loc[network.links.carrier=="DC", "bus0"])) - | (network.buses.index.isin(network.links.loc[network.links.carrier=="DC", "bus1"])) + | ( + network.buses.index.isin( + network.links.loc[network.links.carrier == "DC", "bus0"] + ) + ) + | ( + network.buses.index.isin( + network.links.loc[network.links.carrier == "DC", "bus1"] + ) + ) ) & (network.buses.v_nom.isin(voltage_level)) df = network.buses[mask] From 27d1714d417da4ee42b81863775ceccee4e35015 Mon Sep 17 00:00:00 2001 From: CarlosEpia Date: Mon, 9 Oct 2023 09:40:38 +0200 Subject: [PATCH 128/136] include the argument delete_dispensable_ac_buses --- etrago/args.json | 1 + 1 file changed, 1 insertion(+) diff --git a/etrago/args.json b/etrago/args.json index 8693f929f..7056e9070 100644 --- a/etrago/args.json +++ b/etrago/args.json @@ -51,6 +51,7 @@ }, "generator_noise": 789456, "extra_functionality": {}, + "delete_dispensable_ac_buses": true, "network_clustering_ehv": false, "network_clustering": { "active": true, From 85216abe9b0dc976b2b1adc5914ddd808616f961 Mon Sep 17 00:00:00 2001 From: CarlosEpia Date: Mon, 9 Oct 2023 09:42:25 +0200 Subject: [PATCH 129/136] adjust description new args --- etrago/appl.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/etrago/appl.py b/etrago/appl.py index 5bf011c8b..a53a360e6 100644 --- a/etrago/appl.py +++ b/etrago/appl.py @@ -99,10 +99,10 @@ "grid_max_abs_foreign": None, # absolute capacity per voltage level }, }, - "delete_dispensable_ac_buses": False, # bool. Find and delete unnecesary buses "generator_noise": 789456, # apply generator noise, False or seed number "extra_functionality": {}, # Choose function name or {} # Spatial Complexity: + "delete_dispensable_ac_buses": True, # bool. Find and delete expendable buses "network_clustering_ehv": False, # clustering of HV buses to EHV buses "network_clustering": { "active": True, # choose if clustering is activated @@ -385,10 +385,11 @@ def run_etrago(args, json_path): by carrier. Set upper/lower limit in p.u. delete_dispensable_ac_buses: bool - Choose if unnecessary buses should be identified and deleted from the - grid. This buses have no load or generation attached. Additionally, - they are just connected to two other buses. - Default: False. + Choose if electrical buses that are only connecting two lines should be + removed. These buses have no other components attached to them. The + connected lines are merged. This reduces the spatial complexity without + losing any accuracy. + Default: True. network_clustering_ehv : bool Choose if you want to cluster the full HV/EHV dataset down to only the EHV buses. In that case, all HV buses are assigned to their closest EHV From 1027eca1d964f1602ec9ea017cbacdcc24562639 Mon Sep 17 00:00:00 2001 From: CarlosEpia Date: Tue, 17 Oct 2023 15:09:18 +0200 Subject: [PATCH 130/136] include busmap status for eHV clustering --- etrago/appl.py | 18 +++++++- etrago/args.json | 5 ++- etrago/cluster/electrical.py | 20 +++------ etrago/cluster/spatial.py | 84 ++++++++---------------------------- etrago/tools/utilities.py | 2 +- 5 files changed, 47 insertions(+), 82 deletions(-) diff --git a/etrago/appl.py b/etrago/appl.py index a53a360e6..699897fe4 100644 --- a/etrago/appl.py +++ b/etrago/appl.py @@ -103,7 +103,10 @@ "extra_functionality": {}, # Choose function name or {} # Spatial Complexity: "delete_dispensable_ac_buses": True, # bool. Find and delete expendable buses - "network_clustering_ehv": False, # clustering of HV buses to EHV buses + "network_clustering_ehv": { + "active": False, # choose if clustering of HV buses to EHV buses is activated + "busmap": False, # False or path to stored busmap + }, "network_clustering": { "active": True, # choose if clustering is activated "method": "kmedoids-dijkstra", # choose clustering method: kmeans or kmedoids-dijkstra @@ -390,11 +393,22 @@ def run_etrago(args, json_path): connected lines are merged. This reduces the spatial complexity without losing any accuracy. Default: True. - network_clustering_ehv : bool + network_clustering_ehv : dict + Choose if you want to apply an extra high voltage clustering to the + electrical network. + The provided dictionary can have the following entries: + + * "active" : bool Choose if you want to cluster the full HV/EHV dataset down to only the EHV buses. In that case, all HV buses are assigned to their closest EHV substation, taking into account the shortest distance on power lines. Default: False. + * "busmap" : str + Choose if an stored busmap can be used to make the process quicker, or + a new busmap must be calculated. False or path to the busmap in csv + format should be given. + Default: False + network_clustering : dict Choose if you want to apply a clustering of all network buses and specify settings. diff --git a/etrago/args.json b/etrago/args.json index 7056e9070..e64c7b533 100644 --- a/etrago/args.json +++ b/etrago/args.json @@ -52,7 +52,10 @@ "generator_noise": 789456, "extra_functionality": {}, "delete_dispensable_ac_buses": true, - "network_clustering_ehv": false, + "network_clustering_ehv": { + "active": false, + "busmap": false, + }, "network_clustering": { "active": true, "method": "kmedoids-dijkstra", diff --git a/etrago/cluster/electrical.py b/etrago/cluster/electrical.py index 76afc3a8f..82ecab195 100755 --- a/etrago/cluster/electrical.py +++ b/etrago/cluster/electrical.py @@ -290,11 +290,6 @@ def cluster_on_extra_high_voltage(etrago, busmap, with_time=True): etrago, busmap, cluster_met="ehv" ) - pd.DataFrame(busmap.items(), columns=["bus0", "bus1"]).to_csv( - "ehv_elecgrid_busmap_result.csv", - index=False, - ) - buses = aggregatebuses( network, busmap, @@ -381,7 +376,6 @@ def cluster_on_extra_high_voltage(etrago, busmap, with_time=True): io.import_series_from_dataframe(network_c, df, one_port, attr) network_c.links, network_c.links_t = group_links(network_c) - network_c.determine_network_topology() return (network_c, busmap) @@ -414,10 +408,10 @@ def delete_ehv_buses_no_lines(network): buses_ac["with_gen"] = buses_ac.index.isin(network.generators.bus) delete_buses = buses_ac[ - (buses_ac["with_line"] is False) - & (buses_ac["with_load"] is False) - & (buses_ac["with_link"] is False) - & (buses_ac["with_gen"] is False) + (buses_ac["with_line"] == False) + & (buses_ac["with_load"] == False) + & (buses_ac["with_link"] == False) + & (buses_ac["with_gen"] == False) ].index if len(delete_buses): @@ -455,8 +449,8 @@ def ehv_clustering(self): """ Cluster the network based on Extra High Voltage (EHV) grid. - If `network_clustering_ehv` argument is True, the function clusters the - network based on the EHV grid. + If 'active' in the `network_clustering_ehv` argument is True, the function + clusters the network based on the EHV grid. Parameters ---------- @@ -468,7 +462,7 @@ def ehv_clustering(self): None """ - if self.args["network_clustering_ehv"]: + if self.args["network_clustering_ehv"]["active"]: logger.info("Start ehv clustering") self.network.generators.control = "PV" diff --git a/etrago/cluster/spatial.py b/etrago/cluster/spatial.py index e2afcd7d9..679dcafa3 100755 --- a/etrago/cluster/spatial.py +++ b/etrago/cluster/spatial.py @@ -362,7 +362,7 @@ def shortest_path(paths, graph): return df -def busmap_by_shortest_path(etrago, scn_name, fromlvl, tolvl, cpu_cores=4): +def busmap_by_shortest_path(etrago, fromlvl, tolvl, cpu_cores=4): """ Creates a busmap for the EHV-Clustering between voltage levels based on dijkstra shortest path. The result is automatically written to the @@ -379,8 +379,6 @@ def busmap_by_shortest_path(etrago, scn_name, fromlvl, tolvl, cpu_cores=4): Container for all network components. session : sqlalchemy.orm.session.Session object Establishes interactions with the database. - scn_name : str - Name of the scenario. fromlvl : list List of voltage-levels to cluster. tolvl : list @@ -393,8 +391,6 @@ def busmap_by_shortest_path(etrago, scn_name, fromlvl, tolvl, cpu_cores=4): None """ - # cpu_cores = mp.cpu_count() - # data preperation s_buses = buses_grid_linked(etrago.network, fromlvl) lines = connected_grid_lines(etrago.network, s_buses) @@ -476,25 +472,11 @@ def busmap_by_shortest_path(etrago, scn_name, fromlvl, tolvl, cpu_cores=4): df = pd.concat([df, tofill], ignore_index=True, axis=0) df.drop_duplicates(inplace=True) - # prepare data for export - - df["scn_name"] = scn_name - df["version"] = etrago.args["gridversion"] - - if not df.version.any(): - df.version = "testcase" - df.rename(columns={"source": "bus0", "target": "bus1"}, inplace=True) - df.set_index(["scn_name", "bus0", "bus1"], inplace=True) - df.to_sql( - "egon_etrago_hv_busmap", - con=etrago.engine, - schema="grid", - if_exists="append", - ) + busmap = pd.Series(df.bus1.values, index=df.bus0).to_dict() - return + return busmap def busmap_from_psql(etrago): @@ -517,54 +499,26 @@ def busmap_from_psql(etrago): busmap : dict Maps old bus_ids to new bus_ids. """ - scn_name = ( - etrago.args["scn_name"] - if etrago.args["scn_extension"] is None - else etrago.args["scn_name"] - + "_ext_" - + "_".join(etrago.args["scn_extension"]) - ) - - from saio.grid import egon_etrago_hv_busmap - - filter_version = etrago.args["gridversion"] - if not filter_version: - filter_version = "testcase" - - def fetch(): - query = ( - etrago.session.query( - egon_etrago_hv_busmap.bus0, egon_etrago_hv_busmap.bus1 - ) - .filter(egon_etrago_hv_busmap.scn_name == scn_name) - .filter(egon_etrago_hv_busmap.version == filter_version) + if etrago.args["network_clustering_ehv"]["busmap"] is False: + cpu_cores = etrago.args["network_clustering"]["CPU_cores"] + if cpu_cores == "max": + cpu_cores = mp.cpu_count() + else: + cpu_cores = int(cpu_cores) + + busmap = busmap_by_shortest_path( + etrago, + fromlvl=[110], + tolvl=[220, 380, 400, 450], + cpu_cores=cpu_cores, ) - - return dict(query.all()) - - busmap = fetch() - - if busmap: - print( - "Existing busmap will be deleted and a new one will be calculated." + pd.DataFrame(busmap.items(), columns=["bus0", "bus1"]).to_csv( + "ehv_elecgrid_busmap_result.csv", + index=False, ) - etrago.engine.execute("""DELETE FROM grid.egon_etrago_hv_busmap""") - - cpu_cores = etrago.args["network_clustering"]["CPU_cores"] - if cpu_cores == "max": - cpu_cores = mp.cpu_count() else: - cpu_cores = int(cpu_cores) - - busmap_by_shortest_path( - etrago, - scn_name, - fromlvl=[110], - tolvl=[220, 380, 400, 450], - cpu_cores=cpu_cores, - ) - busmap = fetch() + busmap = pd.read_csv(etrago.args["network_clustering_ehv"]["busmap"]) return busmap diff --git a/etrago/tools/utilities.py b/etrago/tools/utilities.py index ebc858f1c..164b7c9a7 100755 --- a/etrago/tools/utilities.py +++ b/etrago/tools/utilities.py @@ -1878,7 +1878,7 @@ def get_clustering_data(self, path): """ - if (self.args["network_clustering_ehv"]) | ( + if (self.args["network_clustering_ehv"]["active"]) | ( self.args["network_clustering"]["active"] ): path_clus = os.path.join(path, "clustering") From a5c36ad0d6ad382a002fe6b93513d994c6adc582 Mon Sep 17 00:00:00 2001 From: CarlosEpia Date: Tue, 17 Oct 2023 15:54:10 +0200 Subject: [PATCH 131/136] solve flake8 problem --- etrago/cluster/electrical.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/etrago/cluster/electrical.py b/etrago/cluster/electrical.py index 82ecab195..072b70dc1 100755 --- a/etrago/cluster/electrical.py +++ b/etrago/cluster/electrical.py @@ -408,10 +408,10 @@ def delete_ehv_buses_no_lines(network): buses_ac["with_gen"] = buses_ac.index.isin(network.generators.bus) delete_buses = buses_ac[ - (buses_ac["with_line"] == False) - & (buses_ac["with_load"] == False) - & (buses_ac["with_link"] == False) - & (buses_ac["with_gen"] == False) + (~buses_ac["with_line"]) + & (~buses_ac["with_load"]) + & (~buses_ac["with_link"]) + & (~buses_ac["with_gen"]) ].index if len(delete_buses): From 7e0eb63ff856bd74fd31d08789028cbf3184a198 Mon Sep 17 00:00:00 2001 From: CarlosEpia Date: Wed, 18 Oct 2023 13:03:15 +0200 Subject: [PATCH 132/136] fix import busmap eHV --- etrago/cluster/spatial.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/etrago/cluster/spatial.py b/etrago/cluster/spatial.py index 679dcafa3..637b3bee8 100755 --- a/etrago/cluster/spatial.py +++ b/etrago/cluster/spatial.py @@ -519,6 +519,8 @@ def busmap_from_psql(etrago): ) else: busmap = pd.read_csv(etrago.args["network_clustering_ehv"]["busmap"]) + busmap = pd.Series(busmap.bus1.apply(str).values, + index=busmap.bus0.apply(str)).to_dict() return busmap From 9529a8da8bbb4015c82b21b6ee953dbedc320986 Mon Sep 17 00:00:00 2001 From: CarlosEpia Date: Wed, 18 Oct 2023 13:16:09 +0200 Subject: [PATCH 133/136] update name and description of busmap_from_psql --- etrago/cluster/electrical.py | 4 ++-- etrago/cluster/spatial.py | 16 ++++++---------- 2 files changed, 8 insertions(+), 12 deletions(-) diff --git a/etrago/cluster/electrical.py b/etrago/cluster/electrical.py index 072b70dc1..2a22ce937 100755 --- a/etrago/cluster/electrical.py +++ b/etrago/cluster/electrical.py @@ -39,7 +39,7 @@ import pypsa.io as io from etrago.cluster.spatial import ( - busmap_from_psql, + busmap_ehv_clustering, group_links, kmean_clustering, kmedoids_dijkstra_clustering, @@ -469,7 +469,7 @@ def ehv_clustering(self): delete_ehv_buses_no_lines(self.network) - busmap = busmap_from_psql(self) + busmap = busmap_ehv_clustering(self) self.network, busmap = cluster_on_extra_high_voltage( self, busmap, with_time=True diff --git a/etrago/cluster/spatial.py b/etrago/cluster/spatial.py index 637b3bee8..e402f797e 100755 --- a/etrago/cluster/spatial.py +++ b/etrago/cluster/spatial.py @@ -479,20 +479,16 @@ def busmap_by_shortest_path(etrago, fromlvl, tolvl, cpu_cores=4): return busmap -def busmap_from_psql(etrago): +def busmap_ehv_clustering(etrago): """ - Retrieves busmap from `model_draft.ego_grid_pf_hv_busmap` on the - [www.openenergy-platform.org] by a given scenario - name. If this busmap does not exist, it is created with default values. + Generates a busmap that can be used to cluster an electrical network to + only extra high voltage buses. If a path to a busmap in a csv file is + passed in the arguments, it loads the csv file and returns it. Parameters ---------- - network : pypsa.Network - Container for all network components. - session : sqlalchemy.orm.session.Session object - Establishes interactions with the database. - scn_name : str - Name of the scenario. + etrago : Etrago + An instance of the Etrago class Returns ------- From b76a7bff2ba7c94972c71d200d0b8cd9a47d5ad9 Mon Sep 17 00:00:00 2001 From: CarlosEpia Date: Wed, 18 Oct 2023 13:55:56 +0200 Subject: [PATCH 134/136] using Black --- etrago/cluster/spatial.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/etrago/cluster/spatial.py b/etrago/cluster/spatial.py index e402f797e..37dbb75e6 100755 --- a/etrago/cluster/spatial.py +++ b/etrago/cluster/spatial.py @@ -515,8 +515,9 @@ def busmap_ehv_clustering(etrago): ) else: busmap = pd.read_csv(etrago.args["network_clustering_ehv"]["busmap"]) - busmap = pd.Series(busmap.bus1.apply(str).values, - index=busmap.bus0.apply(str)).to_dict() + busmap = pd.Series( + busmap.bus1.apply(str).values, index=busmap.bus0.apply(str) + ).to_dict() return busmap From 92aeaf1ff934e9e323441aaf778426209bb9ee9e Mon Sep 17 00:00:00 2001 From: CarlosEpia Date: Thu, 21 Sep 2023 09:28:03 +0200 Subject: [PATCH 135/136] cluster DE no elec buses only to DE --- etrago/cluster/electrical.py | 120 ++++++++++++++--------------------- 1 file changed, 49 insertions(+), 71 deletions(-) diff --git a/etrago/cluster/electrical.py b/etrago/cluster/electrical.py index 2a22ce937..50d2cb55c 100755 --- a/etrago/cluster/electrical.py +++ b/etrago/cluster/electrical.py @@ -115,6 +115,27 @@ def adjust_no_electric_network(etrago, busmap, cluster_met): Maps old bus_ids to new bus_ids including all sectors. """ + + def find_de_closest(network, bus_ne): + ac_ehv = network.buses[ + (network.buses.v_nom > 110) + & (network.buses.carrier == "AC") + & (network.buses.country == "DE") + ] + + bus_ne_x = network.buses.loc[bus_ne, "x"] + bus_ne_y = network.buses.loc[bus_ne, "y"] + + ac_ehv["dist"] = ac_ehv.apply( + lambda x: ((x.x - bus_ne_x) ** 2 + (x.y - bus_ne_y) ** 2) + ** (1 / 2), + axis=1, + ) + + new_ehv_bus = ac_ehv.dist.idxmin() + + return new_ehv_bus + network = etrago.network # network2 is supposed to contain all the not electrical or gas buses # and links @@ -135,16 +156,9 @@ def adjust_no_electric_network(etrago, busmap, cluster_met): "rural_heat": "rural_heat_pump", } - # no_elec_to_cluster maps the no electrical buses to the eHV/kmean bus - no_elec_to_cluster = pd.DataFrame( - columns=["cluster", "carrier", "new_bus"] - ).set_index("new_bus") - - max_bus = max([int(item) for item in network.buses.index.to_list()]) - no_elec_conex = [] - # busmap2 maps all the no electrical buses to the new buses based on the - # eHV network + # busmap2 defines how the no electrical buses directly connected to AC + # are going to be clustered busmap2 = {} # Map crossborder AC buses in case that they were not part of the k-mean @@ -160,53 +174,37 @@ def adjust_no_electric_network(etrago, busmap, cluster_met): for bus_out in ac_buses_out.index: busmap2[bus_out] = bus_out + foreign_hv = network.buses[ + (network.buses.country != "DE") + & (network.buses.carrier == "AC") + & (network.buses.v_nom > 110) + ].index + busmap3 = pd.DataFrame(columns=["elec_bus", "carrier", "cluster"]) for bus_ne in network2.buses.index: - bus_hv = -1 carry = network2.buses.loc[bus_ne, "carrier"] - - if ( - len( - network2.links[ - (network2.links["bus1"] == bus_ne) - & (network2.links["carrier"] == map_carrier[carry]) - ] - ) - > 0 - ): + busmap3.at[bus_ne, "carrier"] = carry + try: df = network2.links[ (network2.links["bus1"] == bus_ne) & (network2.links["carrier"] == map_carrier[carry]) ].copy() df["elec"] = df["bus0"].isin(busmap.keys()) - - df = df[df["elec"]] - if len(df) > 0: - bus_hv = df["bus0"][0] - - if bus_hv == -1: - busmap2[bus_ne] = str(bus_ne) + bus_hv = df[df["elec"] == True]["bus0"][0] + bus_ehv = busmap[bus_hv] + if bus_ehv not in foreign_hv: + busmap3.at[bus_ne, "elec_bus"] = bus_ehv + else: + busmap3.at[bus_ne, "elec_bus"] = find_de_closest( + network, bus_ne + ) + except: no_elec_conex.append(bus_ne) - continue - - if ( - (no_elec_to_cluster.cluster == busmap[bus_hv]) - & (no_elec_to_cluster.carrier == carry) - ).any(): - bus_cluster = no_elec_to_cluster[ - (no_elec_to_cluster.cluster == busmap[bus_hv]) - & (no_elec_to_cluster.carrier == carry) - ].index[0] - else: - bus_cluster = str(max_bus + 1) - max_bus = max_bus + 1 - new = pd.DataFrame( - {"cluster": busmap[bus_hv], "carrier": carry}, - index=[bus_cluster], - ) + busmap3.at[bus_ne, "elec_bus"] = bus_ne - no_elec_to_cluster = pd.concat([no_elec_to_cluster, new]) + for a, df in busmap3.groupby(["elec_bus", "carrier"]): + busmap3.loc[df.index, "cluster"] = df.index[0] - busmap2[bus_ne] = bus_cluster + busmap3 = busmap3["cluster"].to_dict() if no_elec_conex: logger.info( @@ -215,17 +213,17 @@ def adjust_no_electric_network(etrago, busmap, cluster_met): ) # rural_heat_store buses are clustered based on the AC buses connected to - # their corresponding rural_heat buses + # their corresponding rural_heat buses. Results saved in busmap4 links_rural_store = etrago.network.links[ etrago.network.links.carrier == "rural_heat_store_charger" ].copy() - busmap3 = {} - links_rural_store["to_ac"] = links_rural_store["bus0"].map(busmap2) + busmap4 = {} + links_rural_store["to_ac"] = links_rural_store["bus0"].map(busmap3) for rural_heat_bus, df in links_rural_store.groupby("to_ac"): cluster_bus = df.bus1.iat[0] for rural_store_bus in df.bus1: - busmap3[rural_store_bus] = cluster_bus + busmap4[rural_store_bus] = cluster_bus # Add the gas buses to the busmap and map them to themself for gas_bus in network.buses[ @@ -236,28 +234,8 @@ def adjust_no_electric_network(etrago, busmap, cluster_met): ].index: busmap2[gas_bus] = gas_bus - busmap = {**busmap, **busmap2, **busmap3} - - # The new buses based on the eHV network for not electrical buses are - # created - if cluster_met in ["kmeans", "kmedoids-dijkstra"]: - network.madd( - "Bus", - names=no_elec_to_cluster.index, - carrier=no_elec_to_cluster.carrier, - ) + busmap = {**busmap, **busmap2, **busmap3, **busmap4} - else: - network.madd( - "Bus", - names=no_elec_to_cluster.index, - carrier=no_elec_to_cluster.carrier.values, - x=network.buses.loc[no_elec_to_cluster.cluster.values, "x"].values, - y=network.buses.loc[no_elec_to_cluster.cluster.values, "y"].values, - country=network.buses.loc[ - no_elec_to_cluster.cluster.values, "country" - ].values, - ) return network, busmap From 79ecf3e81bc1725beff7834b9f1fbdcba3f08e23 Mon Sep 17 00:00:00 2001 From: CarlosEpia Date: Thu, 19 Oct 2023 15:03:39 +0200 Subject: [PATCH 136/136] solve Flake8 problem --- etrago/cluster/electrical.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/etrago/cluster/electrical.py b/etrago/cluster/electrical.py index 50d2cb55c..538acf296 100755 --- a/etrago/cluster/electrical.py +++ b/etrago/cluster/electrical.py @@ -189,7 +189,7 @@ def find_de_closest(network, bus_ne): & (network2.links["carrier"] == map_carrier[carry]) ].copy() df["elec"] = df["bus0"].isin(busmap.keys()) - bus_hv = df[df["elec"] == True]["bus0"][0] + bus_hv = df[df["elec"]]["bus0"][0] bus_ehv = busmap[bus_hv] if bus_ehv not in foreign_hv: busmap3.at[bus_ne, "elec_bus"] = bus_ehv