Add aggregated constraints for wind and possibility to take existing into account in solve_network
# Conflicts: # scripts/solve_network.py
This commit is contained in:
parent
5ecd56d53c
commit
ed83f1fb4a
@ -98,7 +98,6 @@ electricity:
|
|||||||
co2limit_enable: false
|
co2limit_enable: false
|
||||||
co2limit: 7.75e+7
|
co2limit: 7.75e+7
|
||||||
co2base: 1.487e+9
|
co2base: 1.487e+9
|
||||||
agg_p_nom_limits: data/agg_p_nom_minmax.csv
|
|
||||||
|
|
||||||
operational_reserve:
|
operational_reserve:
|
||||||
activate: false
|
activate: false
|
||||||
@ -758,6 +757,14 @@ solving:
|
|||||||
linearized_unit_commitment: true
|
linearized_unit_commitment: true
|
||||||
horizon: 365
|
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:
|
constraints:
|
||||||
CCL: false
|
CCL: false
|
||||||
EQ: false
|
EQ: false
|
||||||
|
@ -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_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
|
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.
|
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 <https://genxproject.github.io/GenX/dev/core/#Reserves>`_
|
operational_reserve,,,Settings for reserve requirements following `GenX <https://genxproject.github.io/GenX/dev/core/#Reserves>`_
|
||||||
,,,
|
,,,
|
||||||
-- activate,bool,true or false,Whether to take operational reserve requirements into account during optimisation
|
-- activate,bool,true or false,Whether to take operational reserve requirements into account during optimisation
|
||||||
|
|
@ -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."
|
-- 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.
|
-- 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.
|
-- 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 ,,,
|
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``.
|
-- 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.
|
-- 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.
|
||||||
|
|
@ -181,6 +181,16 @@ Upcoming Release
|
|||||||
|
|
||||||
* Fix custom busmap read in `cluster_network`.
|
* 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)
|
PyPSA-Eur 0.10.0 (19th February 2024)
|
||||||
=====================================
|
=====================================
|
||||||
|
|
||||||
|
@ -422,23 +422,54 @@ def add_CCL_constraints(n, config):
|
|||||||
agg_p_nom_limits: data/agg_p_nom_minmax.csv
|
agg_p_nom_limits: data/agg_p_nom_minmax.csv
|
||||||
"""
|
"""
|
||||||
agg_p_nom_minmax = pd.read_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")
|
logger.info("Adding generation capacity constraints per carrier and country")
|
||||||
p_nom = n.model["Generator-p_nom"]
|
p_nom = n.model["Generator-p_nom"]
|
||||||
|
|
||||||
gens = n.generators.query("p_nom_extendable").rename_axis(index="Generator-ext")
|
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")
|
lhs = p_nom.groupby(grouper).sum().rename(bus="country")
|
||||||
|
|
||||||
|
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")
|
minimum = xr.DataArray(agg_p_nom_minmax["min"].dropna()).rename(dim_0="group")
|
||||||
|
|
||||||
index = minimum.indexes["group"].intersection(lhs.indexes["group"])
|
index = minimum.indexes["group"].intersection(lhs.indexes["group"])
|
||||||
if not index.empty:
|
if not index.empty:
|
||||||
n.model.add_constraints(
|
n.model.add_constraints(
|
||||||
lhs.sel(group=index) >= minimum.loc[index], name="agg_p_nom_min"
|
lhs.sel(group=index) >= minimum.loc[index], name="agg_p_nom_min"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
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")
|
maximum = xr.DataArray(agg_p_nom_minmax["max"].dropna()).rename(dim_0="group")
|
||||||
|
|
||||||
index = maximum.indexes["group"].intersection(lhs.indexes["group"])
|
index = maximum.indexes["group"].intersection(lhs.indexes["group"])
|
||||||
if not index.empty:
|
if not index.empty:
|
||||||
n.model.add_constraints(
|
n.model.add_constraints(
|
||||||
|
Loading…
Reference in New Issue
Block a user