add land use constraint
This commit is contained in:
parent
6b07faf7cd
commit
ca0d67110e
@ -32,6 +32,7 @@ import re
|
|||||||
import numpy as np
|
import numpy as np
|
||||||
import pandas as pd
|
import pandas as pd
|
||||||
import pypsa
|
import pypsa
|
||||||
|
from pypsa.descriptors import nominal_attrs
|
||||||
import xarray as xr
|
import xarray as xr
|
||||||
from _helpers import configure_logging, update_config_with_sector_opts
|
from _helpers import configure_logging, update_config_with_sector_opts
|
||||||
|
|
||||||
@ -40,7 +41,8 @@ from vresutils.benchmark import memory_logger
|
|||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
pypsa.pf.logger.setLevel(logging.WARNING)
|
pypsa.pf.logger.setLevel(logging.WARNING)
|
||||||
from pypsa.descriptors import get_switchable_as_dense as get_as_dense
|
from pypsa.descriptors import get_switchable_as_dense as get_as_dense
|
||||||
|
from linopy.expressions import merge
|
||||||
|
from numpy import isnan
|
||||||
|
|
||||||
def add_land_use_constraint(n, planning_horizons, config):
|
def add_land_use_constraint(n, planning_horizons, config):
|
||||||
if "m" in snakemake.wildcards.clusters:
|
if "m" in snakemake.wildcards.clusters:
|
||||||
@ -49,6 +51,64 @@ def add_land_use_constraint(n, planning_horizons, config):
|
|||||||
_add_land_use_constraint(n)
|
_add_land_use_constraint(n)
|
||||||
|
|
||||||
|
|
||||||
|
def add_land_use_constraint_perfect(n):
|
||||||
|
"""Add global constraints for tech capacity limit.
|
||||||
|
"""
|
||||||
|
logger.info("Add land-use constraint for perfect foresight")
|
||||||
|
def compress_series(s):
|
||||||
|
|
||||||
|
def process_group(group):
|
||||||
|
if group.nunique() == 1:
|
||||||
|
return pd.Series(group.iloc[0], index=[None])
|
||||||
|
else:
|
||||||
|
return group
|
||||||
|
|
||||||
|
return s.groupby(level=[0,1]).apply(process_group)
|
||||||
|
|
||||||
|
def new_index_name(t):
|
||||||
|
# Convert all elements to string and filter out None values
|
||||||
|
parts = [str(x) for x in t if x is not None]
|
||||||
|
# Join with space, but use a dash for the last item if not None
|
||||||
|
return ' '.join(parts[:2]) + (f'-{parts[-1]}' if len(parts) > 2 else '')
|
||||||
|
|
||||||
|
def check_p_min_p_max(p_nom_max):
|
||||||
|
p_nom_min = n.generators[ext_i].groupby(grouper).sum().p_nom_min
|
||||||
|
p_nom_min = p_nom_min.reindex(p_nom_max.index)
|
||||||
|
check = (p_nom_min.groupby(level=[0,1]).sum()>p_nom_max.groupby(level=[0,1]).min())
|
||||||
|
if check.sum():
|
||||||
|
logger.warning(f"summed p_min_pu values at node larger than technical potential {check[check].index}")
|
||||||
|
|
||||||
|
grouper = [n.generators.carrier, n.generators.bus, n.generators.build_year]
|
||||||
|
ext_i = n.generators.p_nom_extendable
|
||||||
|
# get technical limit per node and investment period
|
||||||
|
p_nom_max = n.generators[ext_i].groupby(grouper).min().p_nom_max
|
||||||
|
# drop carriers without tech limit
|
||||||
|
p_nom_max = p_nom_max[~p_nom_max.isin([np.inf, np.nan])]
|
||||||
|
# carrier
|
||||||
|
carriers = p_nom_max.index.get_level_values(0).unique()
|
||||||
|
gen_i = n.generators[(n.generators.carrier.isin(carriers)) &
|
||||||
|
(ext_i) &
|
||||||
|
(n.generators.build_year>n.investment_periods[0])
|
||||||
|
].index
|
||||||
|
n.generators.loc[gen_i, "p_nom_min"] = 0
|
||||||
|
# check minimum capacities
|
||||||
|
check_p_min_p_max(p_nom_max)
|
||||||
|
# drop multi entries in case p_nom_max stays constant in different periods
|
||||||
|
# p_nom_max = compress_series(p_nom_max)
|
||||||
|
# adjust name to fit syntax of nominal constraint per bus
|
||||||
|
df = p_nom_max.reset_index()
|
||||||
|
df["name"] = df.apply(lambda row: f"nom_max_{row['carrier']}"
|
||||||
|
+ (f"_{row['build_year']}" if row['build_year'] is not None else ""),
|
||||||
|
axis=1)
|
||||||
|
|
||||||
|
for name in df.name.unique():
|
||||||
|
df_carrier = df[df.name==name]
|
||||||
|
bus = df_carrier.bus
|
||||||
|
n.buses.loc[bus, name] = df_carrier.p_nom_max.values
|
||||||
|
|
||||||
|
return n
|
||||||
|
|
||||||
|
|
||||||
def _add_land_use_constraint(n):
|
def _add_land_use_constraint(n):
|
||||||
# warning: this will miss existing offwind which is not classed AC-DC and has carrier 'offwind'
|
# warning: this will miss existing offwind which is not classed AC-DC and has carrier 'offwind'
|
||||||
|
|
||||||
@ -233,6 +293,9 @@ def prepare_network(
|
|||||||
if foresight == "myopic":
|
if foresight == "myopic":
|
||||||
add_land_use_constraint(n, planning_horizons, config)
|
add_land_use_constraint(n, planning_horizons, config)
|
||||||
|
|
||||||
|
if foresight == "perfect":
|
||||||
|
n = add_land_use_constraint_perfect(n)
|
||||||
|
|
||||||
if n.stores.carrier.eq("co2 stored").any():
|
if n.stores.carrier.eq("co2 stored").any():
|
||||||
limit = co2_sequestration_potential
|
limit = co2_sequestration_potential
|
||||||
add_co2_sequestration_limit(n, config, limit=limit)
|
add_co2_sequestration_limit(n, config, limit=limit)
|
||||||
@ -627,6 +690,7 @@ def extra_functionality(n, snapshots):
|
|||||||
add_EQ_constraints(n, o)
|
add_EQ_constraints(n, o)
|
||||||
add_battery_constraints(n)
|
add_battery_constraints(n)
|
||||||
add_pipe_retrofit_constraint(n)
|
add_pipe_retrofit_constraint(n)
|
||||||
|
if n._multi_invest:
|
||||||
add_carbon_neutral_constraint(n, snapshots)
|
add_carbon_neutral_constraint(n, snapshots)
|
||||||
|
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user