Conversation
…period classes Fold pypsa_stochastic and pypsa_multi_period into examples/pypsa.yaml; retire the sibling pages, symbols and example files. Add the rung 14 and rung 15 sections to the gallery page, and guard the plain-run collapse to the standard model with a frozen shape fixture.
Add the Process component (a generalized multi-port converter, superset of Link) and the full-depth Transformer (a passive branch like Line, with tap ratio and a fixed phase shift in the KVL cycle) to examples/pypsa.yaml, with symbols, the regenerated gallery, rung 17 and rung 18 reference networks and oracles, and the collapse guard extended to the new standard names.
rung_19 records the secant-mode triangle solved through the pinned pypsa, objective 10840.93, the same 150 rows as the tangent rung. The two loss-cut blocks now stand for both their tangent and secant PyPSA names, matched by a gallery helper that reads every backticked name before the dash.
A phase-shifting transformer's angle becomes a per-snapshot decision where phase_shift_min < phase_shift_max, bounded by them and entering the KVL cycle sum in place of the fixed constant; a plain run keeps the shift fixed and collapses byte-for-byte. rung_20 records the phase-shifter triangle solved through the pinned pypsa, objective 16455.0.
A transformer carries its own loss under transmission_losses, as a line does: the loss counted against its rating, its cap, its tangent or secant cuts and half of it at either bus. A plain run collapses byte-for-byte. rung_22 and rung_23 record the transformer triangle in both modes, objectives 10643.48 and 10822.00.
…s off Generator_com_status_must_stay_down mirrors the must-stay-up block over a data-prep mask, status fixed to zero; PyPSA names the row Generator-com-status-min_down_time_must_stay_up. rung_24 records a cheap unit held off for two snapshots, objective 9007.5.
Link gains the status, start-up and shut-down variables, the committable, big-M and modular p bounds, transitions, up and down times, both must-stay rules, the committed ramp rows, n_mod and its costs, mirroring Generator. rung_25 records five committable links, objective 14013.0.
…tment Process gains the same unit commitment blocks as Link, over its internal power. rung_26 restates rung 25's links as processes drawing a quarter more than they deliver, objective 15956.125.
…nd a start-up ramp alone A committable extendable modular unit takes the ordinary ramp rows against one module, not the big-M rows. A start-up or shut-down ramp alone builds the row, and a missing limit reads as the full build. rung_27 (45469.5) and rung_28 (83283.0) record both for Generator, Link and Process.
…amp restarts at a period start
…r any one listed outage
…es weighted builds
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015h57WkBDnpxrknuJ5zZy9F
The shell front keeps this branch's canonical verb and takes main's docstring and its hoisted model for the typeset verbs. The reading page now carries main's 22 claims and this branch's one, so the test counts 23. main removed to_program, so the canonical test reads a model's program as spec.program. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GpnFcQDuvt2bExGngb6MYk
…e assume of the data
…ghted by its years
…tic marginal cost
…ng may each be pinned to a schedule
…rowth limit, as pypsa clips it at zero
…l scenarios is refused, as pypsa does
…6k1248 # Conflicts: # CHANGELOG.md
Resolves the math_spec -> mathspec rename main made after #690's base. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Sr4SCw5A7GUF5ARhdCaKR1
The canonical tests skip patch files under variants/ and place given: where Spec.to_dict writes it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Sr4SCw5A7GUF5ARhdCaKR1
…hecks that merge gives it back Experiment for #722. It measures which declarations must share a file, writes 14 topic fragments, and compares the canonical form of their merge with the one file. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Sr4SCw5A7GUF5ARhdCaKR1
Documentation build overview
24 files changed ·
|
…other file declares, under given: given: takes parameters: and expressions: beside variables: and constraints:. A given parameter reads as a parameter; a given expression reads as a column over its frame. merge folds both into their introducer, checks a given expression's frame against the body, and refuses a name read as one kind and introduced as another. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Sr4SCw5A7GUF5ARhdCaKR1
…s' into claude/great-galileo-6k1248
…e sums them An expressions: entry marked additive: true is one share of a sum. merge sums the shares of every fragment in fragment-name order and keeps the flag, so a later merge adds to the sum. It refuses a share beside a whole definition, and a fragment that adds a share and reads the name. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Sr4SCw5A7GUF5ARhdCaKR1
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Sr4SCw5A7GUF5ARhdCaKR1
…claude/great-galileo-6k1248
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Sr4SCw5A7GUF5ARhdCaKR1
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Sr4SCw5A7GUF5ARhdCaKR1
…agment gives it Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Sr4SCw5A7GUF5ARhdCaKR1
…claude/great-galileo-6k1248
…erge back to its rows Each hub that sums a term per component (the bus balance, Kirchhoff's voltage law, the operating cost, the carrier additions and the global constraint totals) becomes an additive expression, and each component adds its share. The canonical form now sorts the names under given:. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Sr4SCw5A7GUF5ARhdCaKR1
…e composed model decides the rest merge refuses a definition whose body carries a dimension the reader does not state. A body over fewer dimensions is folded, and the composed model's load refuses a row that would repeat and accepts one another term carries the dimension through. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Sr4SCw5A7GUF5ARhdCaKR1
…s' into feat/additive-expressions # Conflicts: # docs/howto/compose.md
… contributor declares an ordinary term additive moves from a named expression to its given: entry. One reader says the name is a sum over a frame; every file that adds to it declares a plain named expression under the name. merge sums the terms of a marked name and keeps the marked entry, so a later merge adds more. A file may carry both, and then reads the sum so far; its term's frame and form are checked at load. merge refuses a cased term, a term over a dimension the sum does not state, and a contributor that reads the name without the entry. Two terms of an unmarked name collide, and the message names the fix. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Sr4SCw5A7GUF5ARhdCaKR1
Resolves the math_spec -> mathspec rename again. The fragments under examples/pypsa/ still mark their terms and are regenerated next. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Sr4SCw5A7GUF5ARhdCaKR1
…erm is a plain expression Seven sums are marked in core, where their readers may be left out; Bus_injection in network and Cycle_angle_sum in power_flow, where leaving the reader out is never wanted and now collides. The canonical form normalises a named expression written on one line, which it passed through as written. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Sr4SCw5A7GUF5ARhdCaKR1
This was referenced Sep 25, 2026
Contributor
Author
|
Superseeded by #736 |
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.
Note
The following content was generated by AI.
examples/pypsa.yamlsplits into 24 topic fragments that merge back to its rows.given:reads parameters and expressions (#728), and a reader marks a sum that fragments add terms to (#729). A new component is a new family of files.This PR presents the design and holds the experiment. Do not merge it. The language changes are reviewed in #728 and #729; the split lands in a migration PR once the stack is on
main(plan in #722).The stack
mainmerge,override,given:for variables and constraintsgiven:also reads parameters and named expressionsadditive: true; contributors declare ordinary terms;mergesums themmainmainThe design
given:— what a file reads and does not declareA fragment is a whole model: it loads, lowers and prints on its own. What it reads from a sibling it states under
given:, one kind at a time.parametersdims,dtypewheremasks and boundsvariablesdims,domainexpressionsdims,additiveconstraintsdimsdual()A given expression's
dimsare an upper bound. A definition whose body carries a dimension the reader does not state is refused. A body over fewer dimensions is folded, and the composed model decides: it refuses a row that would repeat, and accepts one where another term carries the dimension.A sum other files add to
The file that gives a sum its meaning marks it once, with its frame and description. Every contributor declares an ordinary named expression under the name.
Three kinds of file, one rule:
given:entry; one reader marks itadditive, others state the frameexpressions:entry, and the name nowhere elseA contributor that reads the name without the marked entry would mean two models, its term alone and the sum composed, and
mergerefuses it.merge— fragments as peersmergedoesgiven:entrydimsdo not namegiven:entry marksadditivemergeadds morecases:, or over a dimension the sum does not stategiven:; zero would leave its row without a variablegiven:for a host modelThe result goes through
to_spec, so the composed model is held to every rule again. In the composed program a marked entry that has a definition folds into it (ExpressionDeclaration.additive), somerged.program.givenis empty for a whole model; the file keeps the entry.override— patches over a baseUnchanged from #690. A patch edits, adds or removes (
null) declarations field by field, and laysgiven:over one kind at a time.mergeadds a term;overridereplaces one.Typesetting
=: in any one model the sum is the terms that model has.The split
corenetworkBus_injection;Bus_nodal_balance: Bus_injection == 0power_flowCycle_angle_sum; Kirchhoff's voltage lawcostcarrierglobal_constraintssecuritygenerator,link,process*_commitment*_ramping*_maintenancestorage_unit,store,line,transformer,load4980 lines in total against 4033: the frames each fragment declares to load alone, and its
given:entries. Nothing is copied. Nine names are sums, each marked once, and each carries its description there.Where a sum is marked. In the reader where a model without it is never wanted, so leaving the reader out collides:
Bus_injectioninnetwork,Cycle_angle_suminpower_flow, since lines without Kirchhoff's law are not a PyPSA model. Incorewhere the reader may go:scenario_opex,Carrier_additionsand the five global-constraint totals.Adding a feature is a fragment.
*_rampingfile alone still merges into a whole model.securityneeds lines or transformers. Line and transformer, with security andpower_flow, leave together, as a transport model.network, orpower_flowwith the branches kept, is refused as a collision that names the fix.How it is tested
tests/test_pypsa_split.py, 49 cases:merge(examples/pypsa/*.yaml)has the canonical form ofpypsa.yamlwith its hubs written as marked sums, objective description included, andmerged.program.givenis empty.pypsa.yaml. Each new hub is substituted back, every term of a row is moved to one side, and the terms are compared as a multiset. A flipped sign, a dropped term and a changed operator each fail it.tools/pypsa_split.pywrites frompypsa.yaml.network, orpower_flowalone, is refused (2).Open points and possible additions
given:. A link-only model with global constraints therefore leavesglobal_constraintsout, or a row gets a mask.canonicalover several files. feat(cli):canonical --checkfails a model file that is not in the canonical form, and--writerewrites it #718's--checktakes one file; checking a composition in CI without pytest would needcanonicalto take fragments.generator/commitment.yamlwould needmergeto name a fragment by its relative path.+=operator in the typesetter;The changes in each PR
#728 —
given:reads parameters and expressionsGivenParameterBlock(dims,dtype,description) andGivenExpressionBlock(dims,description);GivenTargets.parametersand.expressions.Variablenode; the dim rules, bounds, the flat namespace and the frame checks cover both.mergefolds both, bounds a definition by the reader'sdims, and refuses a kind mismatch.overridelays them over throughGIVEN_KINDS.typeset_declarationandadvicecover both. Docs indeclarations.md,compose.md,reading.md,errors.md,limits.md.test_given.pyfrom 40 cases to 67; six guards, each with a test that fails without it.#729 — a sum its reader marks
GivenExpressionBlock.additive,GivenDeclaration.additiveandExpressionDeclaration.additive.mergesums the terms of a marked name, keeps the entry, agrees readers on the frame, and refuses a cased term, a term over more than the sum and a self-reading contributor. The collision message names the fix. A composed objective keeps its first description.declarations.md,compose.md,reading.md.test_additive.py, 24 cases; ten guards, each with a test that fails without it.This PR
examples/pypsa/: 24 fragments, generated.tools/pypsa_split.py:splitwrites them, andcheckruns checks 1 to 3.tests/test_pypsa_split.py: the checks above.canonical_dictsorts the names undergiven:, and normalises a named expression written on one line, which it passed through as written. Both belong with feat(language): two files that mean the same model write one text #530; the first only shows once feat(language): a file says what it reads, and fragments and patches compose into one model #571 is also onmain.main, feat(language): merge and override load every model they compose and return one #690, feat(cli):canonical --checkfails a model file that is not in the canonical form, and--writerewrites it #718, feat(language): a fragment reads a parameter or a named expression another file declares, under given: #728 and feat(language): fragments add terms to a named expression that its reader marks as a sum, and merge sums them #729 into docs(pypsa): a single spec covers every pypsa model class and component #620, with the rename fixes they need (the stack still importsmath_spec).Hashes, how to reproduce, gates
pypsa-complete60c279e559ceaef3a5a8ebdf10e098b7d0a79909mainmainca2e04b8cdd20c565edb84ca142a0c1dad21e9fdfeat/compose-through-to-spec4ff3483b6e8f9087140b6a34c1f46273021e2657claude/clever-ramanujan-vsgh971affd55225d05506cdc6eb76a8128e301b01d2dcfeat/given-parameters-expressionsd41d3ee31a56922376aad60b71b2835a699fb5d9feat/additive-expressionsa98ad6ce81c06107a25de24843e383a6ecebbf45claude/great-galileo-6k1248c7b1c878d5c59c4a14518f31522d82d907b8aee9mainhas moved on sinceca2e04b, so GitHub shows this PR as conflicting. It is not meant to merge, and was not brought up to date.Pixi cannot be installed here. In a Python 3.12
uvvirtualenv:Not run:
pixi run ci,compile-tex(the tectonic bundle download is blocked),mkdocs build --strict(the proxy refuses the docs.python.org inventory),reference.py --check,zizmor,taplo, andpyreflyon this branch.No changelog line: this PR is not meant to merge, and #728 and #729 sit on a base that predates the
## Upcoming versionrule.🤖 Generated with Claude Code
https://claude.ai/code/session_01Sr4SCw5A7GUF5ARhdCaKR1