PyPSA / PyPSA/linopy

Add a `mask` parameter to `add_piecewise_formulation`

Open
#799 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

enhancement model formulation
Dominant language
Python
Stars
257
Forks
87
Avg merge
1d 3h
Merged PRs (30d)
29

Description

This is not needed explicitly by anyone yet, but should be added for consistency with add_variables etc

[!NOTE]
The following content was generated by AI.

Summary

Add a mask parameter to add_piecewise_formulation, matching the add_variables(mask=…) / add_constraints(mask=…) convention: where mask=False, no piecewise formulation is built for that entry (no auxiliary variables, no link/convexity constraints). It answers a structural question — does this entity have a piecewise relationship at all? — and takes priority over active.

Motivation

Heterogeneous sets where only some entities have a piecewise curve. Today you either split into several add_piecewise_formulation calls or rely on NaN-breakpoint tricks. A mask is the declarative, convention-aligned way to say "this entity has no curve here."

Relationship to active / active_fill (#796, #797, #798)

mask is orthogonal to gating and does not overlap with the #796 work:

state how to express
entity has no curve mask=False
curve, gated on/off (unit commitment) active=status
curve, always on (ungated) active_fill=1 (or full active=1)
  • mask: structural / compile-time — does the constraint exist here?
  • active: solve-time decision — is it engaged?

They compose: mask filters which entries get a formulation, active gates the survivors. A masked entry has no constraint, so active/active_fill for it is moot — hence mask priority. mask does not solve #796: masking the non-committable units would delete their curve, but those units still dispatch on it.

Relationship to .sel()

For a clean label-subset along one dimension, .sel() on the operands already achieves "no constraint elsewhere", so no parameter is needed there. mask earns its place for:

  1. Ragged / multi-dim holes that no rectangular .sel() can express (scattered (gen, time) pairs, not whole labels along an axis).
  2. Full-index output.sel() returns reduced-index objects that must be realigned to the full coordinate afterwards; mask keeps everything on the full index with holes.
  3. One declarative argument instead of slicing every operand (and keeping breakpoint arrays consistent).
Notes
  • Not urgent and not a blocker for the #796 gating fix; filed separately so the two designs don't get tangled.
  • Originally raised by @fneum in review of #797.

🤖 Generated with Claude Code

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start by locating the add_piecewise_formulation entry point and compare its parameter handling with add_variables(mask=…) and add_constraints(mask=…). Check the existing tests around piecewise formulations, then verify that masked entries create no auxiliary variables or constraints while unmasked entries still honor active and active_fill.

Written by the indexing model from the issue text.

Assessment

Tech stack
python
Domain
backend-api-design
Issue type
Feature
Difficulty
4/5
Estimated time
3-5 days
Activity status
Quiet
Clarity
Mostly clear
Newbie friendliness
50/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.