Skip to content

Apply the new-format custom constraints in pypsa_build - #148

Open
nick-gorman wants to merge 4 commits into
mainfrom
apply-temporal-scope-custom-constraints
Open

nick-gorman wants to merge 4 commits into
mainfrom
apply-temporal-scope-custom-constraints

Conversation

@nick-gorman

@nick-gorman nick-gorman commented Sep 30, 2026 •

Copy link
Copy Markdown
Member

The new-format custom constraints from the translator (#139) are scoped by
investment period and timeslice, e.g. a flow limit can only apply during QLD
peak demand from 2030 onwards. The existing _add_custom_constraints sums every term
over every snapshot, so it can't express that. This change adds
_add_custom_constraints_with_temporal_scope, which creates linopy constraint for each unique constraint_name, investment_period and timeslice combination.

It isn't wired into build_pypsa_network yet; that comes with the final
wiring PR.

src/ispypsa/
├── pypsa_build/custom_constraints.py        ← new temporal-scope path
├── translator/timeslices.py                 ← no-snapshot warning now per investment period
└── validation/schemas/
    ├── ispypsa_tables/                      ← existing schemas, moved
    └── pypsa_friendly_tables/               ← new: contracts for this step's inputs
tests/test_model/test_custom_constraints_with_temporal_scope.py
CLAUDE.md                                    ← "investment period", never bare "period"

Design choices worth a look:

  • Blank timeslice is a fallback, per constraint. It covers the snapshots
    in its investment period that none of the same constraint's named
    timeslices cover.
  • Blank investment_period matches only blank. An LHS term joins only the
    RHS rows with its exact investment_period, a blank investment period doesn't act as wildcard or default value. Because the translator resolves all dated inputs to explicit investment periods,
    blank only appears on the expansion limits which apply across the all investment periods at once.
  • Load terms are data, not variables. coefficient × p_set is added as a
    per-snapshot constant, which linopy moves to the RHS.
  • Storage p is net dispatch (p_dispatch − p_store), matching PLEXOS's
    paired battery Generation/Load coefficients.
  • Missing components aren't skipped. Unsupported (component, attribute)
    terms and constraints with no variable terms raise, rather than silently
    weakening the constraint.
  • Schema split. The schema folder is now ispypsa_tables (translator
    input) and pypsa_friendly_tables (pypsa_build input). Only the three
    tables this step reads are declared so far. Nothing loads the schemas yet,
    so the move is paths only.

The translator's new-format custom constraints are scoped by investment
period and timeslice, which the existing pypsa_build path can't express.
Adds the pypsa_build step that turns each RHS row into a linopy constraint
at the snapshots its period and timeslice select, with blank timeslices as
a per-constraint fallback and load terms as a per-snapshot RHS offset.

Also declares the contract for its input tables as schemas, splitting the
schema folder into ispypsa_tables (translator input) and
pypsa_friendly_tables (pypsa_build input). Not yet wired into
build_pypsa_network, so no production behaviour changes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@codecov

codecov Bot commented Sep 30, 2026 •

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.

Files with missing lines Coverage Δ
src/ispypsa/pypsa_build/custom_constraints.py 93.49% <100.00%> (+13.49%) ⬆️
src/ispypsa/templater/existing_planned.py 100.00% <ø> (ø)
src/ispypsa/templater/new_entrants.py 100.00% <ø> (ø)
src/ispypsa/translator/constraints.py 100.00% <ø> (ø)
src/ispypsa/translator/timeslices.py 100.00% <100.00%> (ø)
🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

nick-gorman and others added 3 commits September 30, 2026 11:43
On its own, "period" reads as a snapshot or time window, which is easy to
confuse with the timeslice scoping these constraints also carry.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Bare "period" kept reading as a snapshot or time window in the custom
constraint docs, so the convention is written down for future changes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Main's existing_planned docstring dropped the storage TODO while this branch
moved the schemas into ispypsa_tables/, so keep both. Main's constraints
translator still pointed at the old schema directory; repoint it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

This branch has not been deployed

No deployments
Sign up for free to 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.

1 participant