Extend a built model with spec layers that bind existing variables - #954
Open
FabianHofmann wants to merge 3 commits into
Open
Extend a built model with spec layers that bind existing variables#954FabianHofmann wants to merge 3 commits into
FabianHofmann wants to merge 3 commits into
Conversation
…variables A Variable in sources binds a declared variable instead of building it. model.spec holds ordered named Layers, each round-tripping netCDF under its own prefix; dims reconcile across sources, layers and bound variables. Notebook section on extending a hand-built model, release notes, API docs.
Build cost — v1 vs legacyv1 build peak & time relative to legacy, on this commit — not a comparison against master (that is CodSpeed).
Full table (time + peak, mean)📊 Interactive plots + CSV: download the semantics-report-v1-vs-legacy artifact from this run. Report-only · not a gate · refreshed on every push · obsolete once legacy is dropped. |
Narrow NamedExpression.expression to LinearExpression before reading its attributes, and type the typeset format parameter as FormatName.
Layer.variables maps declared names through the bindings, so a hand-built variable sharing a bound spec name is no longer hidden from unspecified. Drop the restore() alias and the builder's separate bound argument.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Stacked on #922. I try to introduce a logic for
add_specfor an initialized linopy model from a non-math lane. Note the approach does not need things to change in math-spec for now, even though a stricter logic like agiventag would be welcome.You could ask why that is needed and the answer is avoiding breaking changes and migration. At this stage it is not feasible to replace the whole optimization routine in PyPSA with math-spec and merge components and custom constraints there via a
mergelogic in the math spec lane. For an early partial adoption of a math-spec in parts of PyPSA, I would therefore vote to introduce this feature.Note
The following content was generated by AI.
Changes proposed in this Pull Request
A math-spec can be layered onto an already-built linopy model, for example a PyPSA model with hand-built variables, to add custom constraints, caps and named expressions that read the existing variables.
Binding through
sources. A declared variable whosesourcesentry is a linopyVariableis bound to it instead of built. The declaration must match the model variable: same dimension names, default bounds, nowhere:, same domain. Everything else insourcesstays ordinary data. The empty-model guard is replaced by collision guards: a spec that introduces a variable, constraint, SOS or named expression the model already holds is refused before anything is built ("bind it or rename it"), and so is an objective on a model that already has one.Layers.
model.specis aModelSpecover ordered, namedLayers, one peradd_spec(name=, else the file stem, else"spec").model.spec[name]reads a layer;model.spec.expressionsis the union across layers; typesetting joins the layers andstandalone=Trueneeds a single layer. The single-layer passthroughs (program,text,parameters,coords,lookups) raise on several layers and point tomodel.spec[name]. TheModelrepr says "extended by math-spec layer(s) ..." and tags items per layer.unspecifiedreports what no layer declares; the objective is tracked byobjective_owner, cleared when the objective is replaced by hand.netCDF. Each layer is written under its own
spec-{name}prefix with the spec text and the binding map as layer-named attributes; layer order,wholeand the objective owner are model-level attributes. Files written before this change read back as one layer namedspec.Model.copy()carries every layer;assert_model_equalcompares every layer's text, bindings and parameters including dtypes.Dimension reconciliation. A dimension's master index comes from
sources, else from an earlier layer, else from the bound variables spanning it, which must all agree; a disagreeing claimant raises "the same dimension name means the same axis". A bound variable that spans a strict subset of the master in master order is reindexed onto it lazily, with absent slots atlabels == -1; labels the master does not hold are refused. SOS declarations always act on the model-owned variable.Docs. Notebook section 11 "Extending a hand-built model",
spec.Layerin the API reference, release notes.Design plan and its review live in
dev-scripts/incremental-spec-plan.mdon the branch history. Deferred small items from the review round:Layerexposesmodelandattachedas fields,Layerhas no_repr_markdown_,restore()is a thin alias, andto_netcdftests_spectwice.Verification (e7461dd)
Checklist
AGENTS.md).doc.doc/release_notes.rstof the upcoming release is included.