Skip to content

Add auto_mask parameter to Model class - #555

Merged
FabianHofmann merged 10 commits into
PyPSA:masterfrom
fluxopt:feature/auto-masking1
Feb 6, 2026
Merged

Add auto_mask parameter to Model class#555
FabianHofmann merged 10 commits into
PyPSA:masterfrom
fluxopt:feature/auto-masking1

Conversation

@FBumann

@FBumannFBumann commented Jan 24, 2026

Copy link
Copy Markdown
Collaborator

Disclaimer

Im not sure how much value this adds, as i didnt use masking that much before. Im happy to discuss the ups and downsides of such a feature. I would have raised an issue, but i thought adding a benchmark and proof of concept would be more valuable.

Summary

Adds auto_mask parameter to Model that automatically masks variables and constraints where input data contains NaN values.

# Before: manual masking requiredm=Model()
mask=gen_capacity.notnull()
x=m.add_variables(lower=0, upper=gen_capacity, mask=mask, name="x")
# After: automaticm=Model(auto_mask=True)
x=m.add_variables(lower=0, upper=gen_capacity, name="x") # NaN auto-masked

Benefits

  • Convenience: No manual mask creation/tracking
  • Safety: Can't forget to mask NaN values
  • Performance: Uses optimized numpy internals (np.where instead of xarray where = 38x faster)

Auto-mask conditions

Variables masked out where:

  • lower is NaN, OR
  • upper is NaN

Constraints masked out where:

  • All variable references are invalid (null expression), OR
  • rhs is NaN

Performance (30% NaN, includes external mask creation)

Variables

PotentialActiveNo MaskManualAuto
10K7K14ms2ms2ms
100K70K2ms2ms2ms
500K350K3ms3ms3ms
2M1.4M6ms6ms5ms

Constraints

CountTermsNo MaskManualAuto
5K50017ms16ms14ms
10K100044ms45ms30ms
20K1000109ms61ms46ms
50K500171ms101ms61ms

Auto-mask is equal or faster than manual masking due to optimized numpy operations.

Checklist

  • Code documented
  • Tests added (62 pass)
  • Release notes added
  • MIT license consent

@FBumannFBumann changed the title Feature/auto masking1Add auto_mask parameter to Model classJan 24, 2026
FBumannand others added 4 commits January 24, 2026 12:46
Performance improvements:
- Use np.where() instead of xarray where() for mask application (~38x faster)
- Use max() == -1 instead of all() == -1 for null expression check (~30% faster)
These optimizations make auto_mask have minimal overhead compared to manual masking.
The switch from xarray's where() to numpy's where() broke dimension-aware
broadcasting. A 1D mask with shape (10,) was being broadcast to (1, 10)
instead of (10, 1), applying to the wrong dimension.
Fix: Explicitly broadcast mask to match data.labels shape before using np.where.

@FabianHofmannFabianHofmann left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I very much like this, it is non-intrusive and sensible to try out in the future

Comment threadlinopy/model.py
# Auto-mask based on null expressions or NaN RHS (use numpy for speed)
if self.auto_mask:
# Check if expression is null: all vars == -1
# Use max() instead of all() - if max == -1, all are -1 (since valid vars >= 0)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

clever!

@FBumann
FBumann marked this pull request as ready for review February 6, 2026 14:56
@FBumann

Copy link
Copy Markdown
CollaboratorAuthor

Great! I think it greatly simplifies things for the user

@FabianHofmann
FabianHofmann merged commit 9ce2005 into PyPSA:masterFeb 6, 2026
2 of 4 checks passed
@FabianHofmannFabianHofmann mentioned this pull request Feb 10, 2026
4 tasks
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@FBumann@FabianHofmann