Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion config/config.de.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -444,7 +444,10 @@ solving:
gas pipeline new: 0.3
H2 pipeline: 0.05
H2 pipeline retrofitted: 0.05
fractional_last_unit_size: true
fractional_last_unit_size: true
solver:
name: highs
options: highs-default
constraints:
# The default CO2 budget uses the KSG targets, and the non CO2 emissions from the REMIND model in the KN2045_Mix scenario
co2_budget_national:
Expand Down
95 changes: 77 additions & 18 deletions scripts/pypsa-de/additional_functionality.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,33 @@
logger = logging.getLogger(__name__)


def h2_import_limits_enabled(config):
return config.get("pypsa-de", {}).get("h2_import_limits", {}).get("enable", True)


def safe_add_constraint(model, expr, rhs, sense, name):
"""Wrap solver call to skip constant-constant constraints."""
try:
if sense == "<=":
model.add_constraints(expr <= rhs, name=name)
elif sense == ">=":
model.add_constraints(expr >= rhs, name=name)
else:
raise ValueError(f"Unsupported sense '{sense}'")
return True
except ValueError as exc:
if "Both sides of the constraint are constant" in str(exc):
logger.debug(
"Skipping constraint %s because both sides are constant (%s %s %s)",
name,
expr,
sense,
rhs,
)
return False
raise


def add_capacity_limits(n, investment_year, limits_capacity, sense="maximum"):
for c in n.iterate_components(limits_capacity):
logger.info(f"Adding {sense} constraints for {c.list_name}")
Expand Down Expand Up @@ -208,6 +235,12 @@ def add_pos_neg_aux_variables(n, idx, var_name, infix):


def h2_import_limits(n, investment_year, limits_volume_max):
if not h2_import_limits_enabled(n.config):
logger.info(
"Skipping H2 import limit constraints because pypsa-de.h2_import_limits.enable is False."
)
return

for ct in limits_volume_max["h2_import"]:
limit = limits_volume_max["h2_import"][ct][investment_year] * 1e6

Expand All @@ -228,6 +261,18 @@ def h2_import_limits(n, investment_year, limits_volume_max):
& (n.links.bus1.str[:2] != ct)
]

if incoming.empty and outgoing.empty:
logger.warning(
f"No hydrogen import/export links found for {ct}; skipping limit enforcement."
)
continue

if incoming.empty and outgoing.empty:
logger.warning(
f"No hydrogen import/export links found for {ct}; skipping limit enforcement."
)
continue

incoming_p = (
n.model["Link-p"].loc[:, incoming] * n.snapshot_weightings.generators
).sum()
Expand All @@ -239,43 +284,57 @@ def h2_import_limits(n, investment_year, limits_volume_max):

cname = f"H2_import_limit-{ct}"

n.model.add_constraints(lhs <= limit, name=f"GlobalConstraint-{cname}")
added = safe_add_constraint(
n.model,
lhs,
limit,
"<=",
name=f"GlobalConstraint-{cname}",
)

if cname in n.global_constraints.index:
logger.warning(
f"Global constraint {cname} already exists. Dropping and adding it again."
)
n.global_constraints.drop(cname, inplace=True)

n.add(
"GlobalConstraint",
cname,
constant=limit,
sense="<=",
type="",
carrier_attribute="",
)
if added:
n.add(
"GlobalConstraint",
cname,
constant=limit,
sense="<=",
type="",
carrier_attribute="",
)

logger.info("Adding H2 export ban")

cname = f"H2_export_ban-{ct}"

n.model.add_constraints(lhs >= 0, name=f"GlobalConstraint-{cname}")
added_export = safe_add_constraint(
n.model,
lhs,
0,
">=",
name=f"GlobalConstraint-{cname}",
)

if cname in n.global_constraints.index:
logger.warning(
f"Global constraint {cname} already exists. Dropping and adding it again."
)
n.global_constraints.drop(cname, inplace=True)

n.add(
"GlobalConstraint",
cname,
constant=0,
sense=">=",
type="",
carrier_attribute="",
)
if added_export:
n.add(
"GlobalConstraint",
cname,
constant=0,
sense=">=",
type="",
carrier_attribute="",
)


def h2_production_limits(n, investment_year, limits_volume_min, limits_volume_max):
Expand Down