From ed83f1fb4ab9ba4ed90fc14d6cff8f305c3b3cb8 Mon Sep 17 00:00:00 2001 From: Thomas Gilon Date: Thu, 11 Apr 2024 17:43:47 +0200 Subject: [PATCH 1/3] Add aggregated constraints for wind and possibility to take existing into account in solve_network # Conflicts: # scripts/solve_network.py --- config/config.default.yaml | 9 +++++++- doc/configtables/electricity.csv | 1 - doc/configtables/solving.csv | 4 ++++ doc/release_notes.rst | 10 ++++++++ scripts/solve_network.py | 39 ++++++++++++++++++++++++++++---- 5 files changed, 57 insertions(+), 6 deletions(-) diff --git a/config/config.default.yaml b/config/config.default.yaml index d438c51f..f500c8bb 100644 --- a/config/config.default.yaml +++ b/config/config.default.yaml @@ -98,7 +98,6 @@ electricity: co2limit_enable: false co2limit: 7.75e+7 co2base: 1.487e+9 - agg_p_nom_limits: data/agg_p_nom_minmax.csv operational_reserve: activate: false @@ -758,6 +757,14 @@ solving: linearized_unit_commitment: true horizon: 365 + agg_p_nom_limits: + agg_offwind: false + include_existing: false + years: + 2030: data/agg_p_nom_minmax.csv + 2040: data/agg_p_nom_minmax.csv + 2050: data/agg_p_nom_minmax.csv + constraints: CCL: false EQ: false diff --git a/doc/configtables/electricity.csv b/doc/configtables/electricity.csv index 22a22d57..72142ae8 100644 --- a/doc/configtables/electricity.csv +++ b/doc/configtables/electricity.csv @@ -5,7 +5,6 @@ gaslimit,MWhth,float or false,Global gas usage limit co2limit_enable,bool,true or false,Add an overall absolute carbon-dioxide emissions limit configured in ``electricity: co2limit``. co2limit,:math:`t_{CO_2-eq}/a`,float,Cap on total annual system carbon dioxide emissions co2base,:math:`t_{CO_2-eq}/a`,float,Reference value of total annual system carbon dioxide emissions if relative emission reduction target is specified in ``{opts}`` wildcard. -agg_p_nom_limits,file,path,Reference to ``.csv`` file specifying per carrier generator nominal capacity constraints for individual countries if ``'CCL'`` is in ``{opts}`` wildcard. Defaults to ``data/agg_p_nom_minmax.csv``. operational_reserve,,,Settings for reserve requirements following `GenX `_ ,,, -- activate,bool,true or false,Whether to take operational reserve requirements into account during optimisation diff --git a/doc/configtables/solving.csv b/doc/configtables/solving.csv index 7189399b..f9619504 100644 --- a/doc/configtables/solving.csv +++ b/doc/configtables/solving.csv @@ -14,6 +14,10 @@ options,,, -- transmission_losses,int,[0-9],"Add piecewise linear approximation of transmission losses based on n tangents. Defaults to 0, which means losses are ignored." -- linearized_unit_commitment,bool,"{'true','false'}",Whether to optimise using the linearized unit commitment formulation. -- horizon,--,int,Number of snapshots to consider in each iteration. Defaults to 100. +agg_p_nom_limits,,,Configure per carrier generator nominal capacity constraints for individual countries if ``'CCL'`` is in ``{opts}`` wildcard. +-- agg_offwind,bool,"{'true','false'}",Aggregate together all the types of offwind when writing the constraint. Default is false. +-- include_existing,bool,"{'true','false'}",Take existing capacities into account when writing the constraint. Default is false. +-- years,-,Dictionary with planning horizons as key and path as value,Reference to ``.csv`` file for each planning horizon. Defaults to ``data/agg_p_nom_minmax.csv``. constraints ,,, -- CCL,bool,"{'true','false'}",Add minimum and maximum levels of generator nominal capacity per carrier for individual countries. These can be specified in the file linked at ``electricity: agg_p_nom_limits`` in the configuration. File defaults to ``data/agg_p_nom_minmax.csv``. -- EQ,bool/string,"{'false',`n(c| )``; i.e. ``0.5``-``0.7c``}",Require each country or node to on average produce a minimal share of its total consumption itself. Example: ``EQ0.5c`` demands each country to produce on average at least 50% of its consumption; ``EQ0.5`` demands each node to produce on average at least 50% of its consumption. diff --git a/doc/release_notes.rst b/doc/release_notes.rst index d42b149f..f334b16f 100644 --- a/doc/release_notes.rst +++ b/doc/release_notes.rst @@ -181,6 +181,16 @@ Upcoming Release * Fix custom busmap read in `cluster_network`. +* Improved the behaviour of `agg_p_nom_limits`: + + - Moved the associated configuration to `solving`. This allows *Snakemake* to correctly decide which rules to run when the configuration changes. + + - Added the ability to enable aggregation of all *offwind* types (*offwind-ac* and *offwind-dc*) when writing the constraint. + + - Added the possibility to take existing capacities into account when writing the constraint. + + - Added the possibility to have a different file for each planning horizon. + PyPSA-Eur 0.10.0 (19th February 2024) ===================================== diff --git a/scripts/solve_network.py b/scripts/solve_network.py index 0f6725c7..2d099068 100644 --- a/scripts/solve_network.py +++ b/scripts/solve_network.py @@ -422,23 +422,54 @@ def add_CCL_constraints(n, config): agg_p_nom_limits: data/agg_p_nom_minmax.csv """ agg_p_nom_minmax = pd.read_csv( - config["electricity"]["agg_p_nom_limits"], index_col=[0, 1] + config["solving"]["agg_p_nom_limits"]["years"][int(snakemake.wildcards.planning_horizons)], index_col=[0, 1] ) logger.info("Adding generation capacity constraints per carrier and country") p_nom = n.model["Generator-p_nom"] gens = n.generators.query("p_nom_extendable").rename_axis(index="Generator-ext") - grouper = pd.concat([gens.bus.map(n.buses.country), gens.carrier]) + if config["solving"]["agg_p_nom_limits"]["agg_offwind"]: + rename_offwind = {"offwind-ac": "offwind-all", "offwind-dc": "offwind-all", "offwind": "offwind-all"} + gens = gens.replace(rename_offwind) + grouper = pd.concat([gens.bus.map(n.buses.country), gens.carrier], axis=1) lhs = p_nom.groupby(grouper).sum().rename(bus="country") - minimum = xr.DataArray(agg_p_nom_minmax["min"].dropna()).rename(dim_0="group") + if config["solving"]["agg_p_nom_limits"]["include_existing"]: + gens_cst = n.generators.query("~p_nom_extendable").rename_axis(index="Generator-cst") + gens_cst = gens_cst[ + (gens_cst["build_year"] + gens_cst["lifetime"]) >= int(snakemake.wildcards.planning_horizons)] + if config["solving"]["agg_p_nom_limits"]["agg_offwind"]: + gens_cst = gens_cst.replace(rename_offwind) + rhs_cst = ( + pd.concat([gens_cst.bus.map(n.buses.country), gens_cst[["carrier", "p_nom"]]], axis=1) + .groupby(["bus", "carrier"]).sum() + ) + rhs_cst.index = rhs_cst.index.rename({"bus": "country"}) + rhs_min = agg_p_nom_minmax["min"].dropna() + idx_min = rhs_min.index.join(rhs_cst.index, how="left") + rhs_min = rhs_min.reindex(idx_min).fillna(0) + rhs = (rhs_min - rhs_cst.reindex(idx_min).fillna(0).p_nom).dropna() + rhs[rhs < 0] = 0 + minimum = xr.DataArray(rhs).rename(dim_0="group") + else: + minimum = xr.DataArray(agg_p_nom_minmax["min"].dropna()).rename(dim_0="group") + index = minimum.indexes["group"].intersection(lhs.indexes["group"]) if not index.empty: n.model.add_constraints( lhs.sel(group=index) >= minimum.loc[index], name="agg_p_nom_min" ) - maximum = xr.DataArray(agg_p_nom_minmax["max"].dropna()).rename(dim_0="group") + if config["solving"]["agg_p_nom_limits"]["include_existing"]: + rhs_max = agg_p_nom_minmax["max"].dropna() + idx_max = rhs_max.index.join(rhs_cst.index, how="left") + rhs_max = rhs_max.reindex(idx_max).fillna(0) + rhs = (rhs_max - rhs_cst.reindex(idx_max).fillna(0).p_nom).dropna() + rhs[rhs < 0] = 0 + maximum = xr.DataArray(rhs).rename(dim_0="group") + else: + maximum = xr.DataArray(agg_p_nom_minmax["max"].dropna()).rename(dim_0="group") + index = maximum.indexes["group"].intersection(lhs.indexes["group"]) if not index.empty: n.model.add_constraints( From e90b37e6d7012ce5bbc72d257a549053830560f7 Mon Sep 17 00:00:00 2001 From: Thomas Gilon Date: Fri, 19 Apr 2024 17:41:50 +0200 Subject: [PATCH 2/3] Improve agg_p_nom_minmax file management --- config/config.default.yaml | 5 +-- data/agg_p_nom_minmax.csv | 63 ++++++++++++++++++------------------ doc/configtables/solving.csv | 2 +- scripts/solve_network.py | 5 +-- 4 files changed, 37 insertions(+), 38 deletions(-) diff --git a/config/config.default.yaml b/config/config.default.yaml index f500c8bb..7f40db20 100644 --- a/config/config.default.yaml +++ b/config/config.default.yaml @@ -760,10 +760,7 @@ solving: agg_p_nom_limits: agg_offwind: false include_existing: false - years: - 2030: data/agg_p_nom_minmax.csv - 2040: data/agg_p_nom_minmax.csv - 2050: data/agg_p_nom_minmax.csv + file: data/agg_p_nom_minmax.csv constraints: CCL: false diff --git a/data/agg_p_nom_minmax.csv b/data/agg_p_nom_minmax.csv index 111215bc..7a6a7f73 100644 --- a/data/agg_p_nom_minmax.csv +++ b/data/agg_p_nom_minmax.csv @@ -1,31 +1,32 @@ -country,carrier,min,max -DE,onwind,0.1, -DE,offwind-ac,0.1, -DE,offwind-dc,0.1, -DE,solar,0.2, -LU,onwind,, -LU,solar,, -NL,onwind,, -NL,offwind-ac,, -NL,offwind-dc,, -NL,solar,, -GB,onwind,, -GB,offwind-ac,, -GB,offwind-dc,, -GB,solar,, -IE,onwind,, -IE,offwind-ac,, -IE,offwind-dc,, -IE,solar,, -FR,onwind,, -FR,offwind-ac,, -FR,offwind-dc,, -FR,solar,, -DK,onwind,, -DK,offwind-ac,, -DK,offwind-dc,, -DK,solar,, -BE,onwind,, -BE,offwind-ac,, -BE,offwind-dc,, -BE,solar,, +,,2030,2030,2040,2040,2050,2050 +country,carrier,min,max,min,max,min,max +DE,onwind,0.1,,0.1,,0.1, +DE,offwind-ac,0.1,,0.1,,0.1, +DE,offwind-dc,0.1,,0.1,,0.1, +DE,solar,0.2,,0.2,,0.2, +LU,onwind,,,,,, +LU,solar,,,,,, +NL,onwind,,,,,, +NL,offwind-ac,,,,,, +NL,offwind-dc,,,,,, +NL,solar,,,,,, +GB,onwind,,,,,, +GB,offwind-ac,,,,,, +GB,offwind-dc,,,,,, +GB,solar,,,,,, +IE,onwind,,,,,, +IE,offwind-ac,,,,,, +IE,offwind-dc,,,,,, +IE,solar,,,,,, +FR,onwind,,,,,, +FR,offwind-ac,,,,,, +FR,offwind-dc,,,,,, +FR,solar,,,,,, +DK,onwind,,,,,, +DK,offwind-ac,,,,,, +DK,offwind-dc,,,,,, +DK,solar,,,,,, +BE,onwind,,,,,, +BE,offwind-ac,,,,,, +BE,offwind-dc,,,,,, +BE,solar,,,,,, \ No newline at end of file diff --git a/doc/configtables/solving.csv b/doc/configtables/solving.csv index f9619504..2fc7999d 100644 --- a/doc/configtables/solving.csv +++ b/doc/configtables/solving.csv @@ -17,7 +17,7 @@ options,,, agg_p_nom_limits,,,Configure per carrier generator nominal capacity constraints for individual countries if ``'CCL'`` is in ``{opts}`` wildcard. -- agg_offwind,bool,"{'true','false'}",Aggregate together all the types of offwind when writing the constraint. Default is false. -- include_existing,bool,"{'true','false'}",Take existing capacities into account when writing the constraint. Default is false. --- years,-,Dictionary with planning horizons as key and path as value,Reference to ``.csv`` file for each planning horizon. Defaults to ``data/agg_p_nom_minmax.csv``. +-- file,file,path,Reference to ``.csv`` file specifying per carrier generator nominal capacity constraints for individual countries and planning horizons. Defaults to ``data/agg_p_nom_minmax.csv``. constraints ,,, -- CCL,bool,"{'true','false'}",Add minimum and maximum levels of generator nominal capacity per carrier for individual countries. These can be specified in the file linked at ``electricity: agg_p_nom_limits`` in the configuration. File defaults to ``data/agg_p_nom_minmax.csv``. -- EQ,bool/string,"{'false',`n(c| )``; i.e. ``0.5``-``0.7c``}",Require each country or node to on average produce a minimal share of its total consumption itself. Example: ``EQ0.5c`` demands each country to produce on average at least 50% of its consumption; ``EQ0.5`` demands each node to produce on average at least 50% of its consumption. diff --git a/scripts/solve_network.py b/scripts/solve_network.py index 2d099068..e0914b9f 100644 --- a/scripts/solve_network.py +++ b/scripts/solve_network.py @@ -422,8 +422,9 @@ def add_CCL_constraints(n, config): agg_p_nom_limits: data/agg_p_nom_minmax.csv """ agg_p_nom_minmax = pd.read_csv( - config["solving"]["agg_p_nom_limits"]["years"][int(snakemake.wildcards.planning_horizons)], index_col=[0, 1] - ) + config["solving"]["agg_p_nom_limits"]["file"], + index_col=[0, 1], header=[0, 1] + )[snakemake.wildcards.planning_horizons] logger.info("Adding generation capacity constraints per carrier and country") p_nom = n.model["Generator-p_nom"] From 54f9ad450e673d54b7f39a056c7b170dfee7b6f3 Mon Sep 17 00:00:00 2001 From: Thomas Gilon Date: Mon, 22 Apr 2024 11:55:17 +0200 Subject: [PATCH 3/3] Fix index issue --- data/agg_p_nom_minmax.csv | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/data/agg_p_nom_minmax.csv b/data/agg_p_nom_minmax.csv index 7a6a7f73..974b13a2 100644 --- a/data/agg_p_nom_minmax.csv +++ b/data/agg_p_nom_minmax.csv @@ -1,5 +1,6 @@ ,,2030,2030,2040,2040,2050,2050 -country,carrier,min,max,min,max,min,max +,,min,max,min,max,min,max +country,carrier,,,,,, DE,onwind,0.1,,0.1,,0.1, DE,offwind-ac,0.1,,0.1,,0.1, DE,offwind-dc,0.1,,0.1,,0.1,