diff --git a/doc/release_notes.rst b/doc/release_notes.rst index 230fc67d..b8aaeba2 100644 --- a/doc/release_notes.rst +++ b/doc/release_notes.rst @@ -24,6 +24,7 @@ Upcoming Release * The ``focus_weights`` are now also considered when pre-clustering in the :mod:`simplify_network` rule [`#241 `_]. * Continuous integration testing switches to Github Actions from Travis CI [`#252 `_]. * Bugfix in :mod:`build_renewable_profile` where offshore wind profiles could no longer be created [`#249 `_]. +* Bugfix: Lower expansion limit of extendable carriers is now set to the existing capacity, i.e. ``p_nom_min = p_nom`` (0 before). Simultaneously, the upper limit (``p_nom_max``) is now the maximum of the installed capacity (``p_nom``) and the previous estimate based on land availability (``p_nom_max``) [`#260 `_]. PyPSA-Eur 0.3.0 (7th December 2020) ================================== diff --git a/scripts/_helpers.py b/scripts/_helpers.py index 996baf73..0c6a4658 100644 --- a/scripts/_helpers.py +++ b/scripts/_helpers.py @@ -125,6 +125,14 @@ def load_network_for_plots(fn, tech_costs, config, combine_hydro_ps=True): return n +def update_p_nom_max(n): + # if extendable carriers (solar/onwind/...) have capacity >= 0, + # e.g. existing assets from the OPSD project are included to the network, + # the installed capacity might exceed the expansion limit. + # Hence, we update the assumptions. + + n.generators.p_nom_max = n.generators[['p_nom_min', 'p_nom_max']].max(1) + def aggregate_p_nom(n): return pd.concat([ n.generators.groupby("carrier").p_nom_opt.sum(), diff --git a/scripts/add_electricity.py b/scripts/add_electricity.py index 45d537c4..bf80e8aa 100755 --- a/scripts/add_electricity.py +++ b/scripts/add_electricity.py @@ -84,7 +84,7 @@ It further adds extendable ``generators`` with **zero** capacity for """ import logging -from _helpers import configure_logging +from _helpers import configure_logging, update_p_nom_max import pypsa import pandas as pd @@ -501,6 +501,7 @@ def attach_OPSD_renewables(n): caps = caps / gens_per_bus.reindex(caps.index, fill_value=1) n.generators.p_nom.update(gens.bus.map(caps).dropna()) + n.generators.p_nom_min.update(gens.bus.map(caps).dropna()) @@ -536,6 +537,7 @@ def estimate_renewable_capacities(n, tech_map=None): .groupby(n.generators.bus.map(n.buses.country)) .transform(lambda s: normed(s) * tech_capacities.at[s.name]) .where(lambda s: s>0.1, 0.)) # only capacities above 100kW + n.generators.loc[tech_i, 'p_nom_min'] = n.generators.loc[tech_i, 'p_nom'] def add_nice_carrier_names(n, config=None): @@ -575,6 +577,7 @@ if __name__ == "__main__": estimate_renewable_capacities(n) attach_OPSD_renewables(n) + update_p_nom_max(n) add_nice_carrier_names(n) diff --git a/scripts/cluster_network.py b/scripts/cluster_network.py index 8356f83b..3503bd9b 100644 --- a/scripts/cluster_network.py +++ b/scripts/cluster_network.py @@ -122,7 +122,7 @@ Exemplary unsolved network clustered to 37 nodes: """ import logging -from _helpers import configure_logging +from _helpers import configure_logging, update_p_nom_max import pypsa import os @@ -282,7 +282,7 @@ def clustering_for_n_clusters(n, n_clusters, custom_busmap=False, aggregate_carr aggregate_generators_carriers=aggregate_carriers, aggregate_one_ports=["Load", "StorageUnit"], line_length_factor=line_length_factor, - generator_strategies={'p_nom_max': p_nom_max_strategy}, + generator_strategies={'p_nom_max': p_nom_max_strategy, 'p_nom_min': np.sum}, scale_link_capital_costs=False) if not n.links.empty: @@ -379,6 +379,8 @@ if __name__ == "__main__": extended_link_costs=hvac_overhead_cost, focus_weights=focus_weights) + update_p_nom_max(n) + clustering.network.export_to_netcdf(snakemake.output.network) for attr in ('busmap', 'linemap'): #also available: linemap_positive, linemap_negative getattr(clustering, attr).to_csv(snakemake.output[attr]) diff --git a/scripts/simplify_network.py b/scripts/simplify_network.py index 6f33b351..4678f796 100644 --- a/scripts/simplify_network.py +++ b/scripts/simplify_network.py @@ -83,7 +83,7 @@ The rule :mod:`simplify_network` does up to four things: """ import logging -from _helpers import configure_logging +from _helpers import configure_logging, update_p_nom_max from cluster_network import clustering_for_n_clusters, cluster_regions from add_electricity import load_costs @@ -198,7 +198,7 @@ def _aggregate_and_move_components(n, busmap, connection_costs_to_bus, aggregate _adjust_capital_costs_using_connection_costs(n, connection_costs_to_bus) - generators, generators_pnl = aggregategenerators(n, busmap) + generators, generators_pnl = aggregategenerators(n, busmap, custom_strategies={'p_nom_min': np.sum}) replace_components(n, "Generator", generators, generators_pnl) for one_port in aggregate_one_ports: @@ -365,6 +365,8 @@ if __name__ == "__main__": busmaps.append(cluster_map) else: n.buses = n.buses.drop(['symbol', 'tags', 'under_construction', 'substation_lv', 'substation_off'], axis=1) + + update_p_nom_max(n) n.export_to_netcdf(snakemake.output.network)