Skip to content

docs(calliope): all of calliope's math is a set of fragments that merge, with its modes laid over them as patches - #770

Open
FBumann wants to merge 13 commits into
mainfrom
docs/calliope
Open

FBumann wants to merge 13 commits into
mainfrom
docs/calliope

Conversation

@FBumann

@FBumann FBumann commented Sep 28, 2026 •

Copy link
Copy Markdown
Contributor

Prompt: Let's try to port all fragments that caliope implemented into mathspec based on main. Do it as another showcase of the composition capabilities, beside the pypsa one. Only stop after you ported everything or tried every fragment and recorded why you weren't able to port it and what mathspec is missing to make it possible

Note

The following content was generated by AI.

Every math file of Calliope v0.7.0 is ported: the base as 14 fragments that merge composes, 15 extensions, and 7 variants that override lays over them. No block is left out for want of language. Of the nine gaps the port found, two are now closed on main, and #777 tracks the other seven.

What mathspec is missing, and where it is tracked

# Gap Workaround in the port Tracked
1 ** and / over a sum of parameters none: closed by #757. The port writes (1 - storage_loss) ** h and the annuity factor as Calliope does #277 (closed)
2 a parameter default that is not zero data prep fills defaults; area_use is masked by area_use_min > 0 #773
3 no where: on a piecewise: block the SOS2 link reads a copy of flow_cap masked to the curve #77, PR #630
4 no selection of one label, flow_out[carriers=electricity] a case on the dimension plus a sum, eleven expressions #259
5 a lookup read at a dimension the operand carries: map_dim(nodes, link_from) bare relations over the pair, tested in a case #771, #772; PR #664 makes the read load
6 one parameter at several shapes: annual_flow_max, net_import_share one shape each; the group row reads a scalar of its own #775
7 an empty: true sum no file adds to is an unbuilt column, not zero penalty has the body 0 #735, PR #763 (see below)
8 sum(over=[a, b]) none: closed by #778 and #779. The nested sums use one list each #774 (closed)
9 no warning-level assumption Calliope's three errors: warn checks are out #776

Related, not gaps: one_of on a lookup is an assumption here, and #518 / #519 would declare it. Operate mode turns a capacity variable into a parameter, which #303 / PR #748 (spec.fix) make a verb. Calliope's .active: false is a patch removal, which #12 discusses.

Out by design: operate's rolling horizon and the SPORES iterations (loops of solves), and the lat/lon check (data prep).

Interacts with PR #763. #763 removes empty: true and given: … term:, and refuses a term on a name that some fragment defines. Every sum here uses those. If #763 merges first, each owner reads its sum under given:, and each term gets adds_to:. penalty then needs a term of its own in settings.yaml, no_penalty: { expression: "0", adds_to: penalty }. On #763's branch that gives penalty = no_penalty + unmet_penalty with the feasibility file and penalty = no_penalty without it.

What is in it

Source: calliope-project/calliope at c2c0549 (Release v0.7.0): src/calliope/math/{base,milp,operate,spores,storage_inter_cluster}.yaml, the 13 files of docs/user_defined_math/examples/, and example_models/urban_scale/additional_math.yaml.

  • examples/calliope/*.yaml: the base, one fragment per topic (settings, balance, flows, cost, supply, demand, conversion, storage, supply_storage, area, transmission, export, feasibility, reporting). merge gives one spec, with nothing under given: and no advice.
  • examples/calliope/extensions/: what MILP and each example add. Calliope restates system_balance, cost_investment, cost_operation_fixed or the objective whole to add one term. Here the extension adds a term to a sum instead, and no base file changes (fuel distribution, monthly peak charge, purchase cost, both piecewise costs).
  • examples/calliope/variants/: patches for what Calliope changes in the base: the MILP edits, operate mode (and its MILP half), SPORES, inter-cluster storage, and the two rewrites of balance_conversion.
  • Docs: docs/examples/calliope/ has a gallery page per file, an index with the generated sums and fragments tables, and port.md. port.md records every Calliope block with its status and the file that states it. The nav places it under Development → Proofs of concept.
  • tools/gallery.py writes the new pages. A variant page prints each declaration the patch writes, in the spec it lands on. tests/test_calliope_example.py (70 tests) holds the compositions the pages claim.
Licence and attribution

Calliope is Apache-2.0 and ships no NOTICE file.

  • Every file under examples/calliope/ carries SPDX-FileCopyrightText: Calliope contributors beside this project's line, is Apache-2.0, and says it was changed from Calliope v0.7.0, as Apache-2.0 §4(b) asks of a modified file. LICENSES/Apache-2.0.txt is already in the tree.
  • The 36 gallery pages that print those files are CC-BY-4.0 AND Apache-2.0, with both copyright lines.
  • index.md and port.md are this project's prose and stay CC-BY-4.0. The index says the math is Calliope's, under Apache-2.0, and links the licence.
  • examples/symbols/calliope.yaml is this project's and stays MIT.
  • reuse lint is green.
History, gates, what was not done

Merged main at 9ace314a (through #751), in two merge commits. The branch earlier merged #767, #768 and #769 while they were open; all three are now on main. The only conflict each time was CHANGELOG.md, and it keeps both sides.

Gaps 1 and 8 closed in 44d71cff.

  • storage_retention and cost_annuity_factor are gone. storage.yaml and the inter-cluster variant read (1 - storage_loss) ** …. The depreciation rate's last case is cost_interest_rate * (1 + cost_interest_rate) ** lifetime / ((1 + cost_interest_rate) ** lifetime - 1), and it prints as a fraction. Both are checked against Calliope's base.yaml and storage_inter_cluster.yaml at c2c0549.
  • 20 nested sums take a list: 18 become one sum(…, over=[…]), and two triple sums keep an outer sum over a product, sum(sum(…, over=[…]) * w). The commit message says 21; 20 is the count in the diff.
  • port.md marks gaps 1 and 8 closed and keeps the numbering, since Calliope v0.7.0's math ports to mathspec, and nine gaps cost the port a workaround #777 refers to it. cost_investment_annualised is now done, and the parameters row for storage_loss, cost_interest_rate and lifetime is gone.

Guard check (from the first round): with the SOS2 link pointed back at flow_cap, test_the_sos2_curve_links_a_copy_of_the_flow_capacity_masked_to_the_curve fails (assert False on the link row). Restored, it passes.

Gates. Everything below ran on 44d71cff. After the second merge, pytest, the page checks, ruff check, pyrefly and prettier --check CHANGELOG.md ran again on 93e23e5b, with the same results. Pixi is not reachable here (the proxy refuses pixi.sh), so these ran from a uv venv with the pinned tools.

  • pytest -q -n auto: 2829 passed, 82 skipped. tests/test_calliope_example.py: 70 passed.
  • ruff check, ruff format --check, pyrefly check, typos, reuse lint, prettier --check on the changed pages: clean.
  • tools.gallery regenerated 11 pages. spec_math, notation, home_math, expansion_math, schema and the typesetting golden output show no diff.
  • render-tex: 84 specs, 84 documents.
  • docs-build, compile-tex: not run. CI runs both.

Not done:

Departures from defaults here: variants live under variants/, a folder name that render-tex and the canonical test already skip. .prettierignore negates port.md, so prettier formats the one hand-written page in the folder.

🤖 Generated with Claude Code

https://claude.ai/code/session_01AvK6QNNFZHJcRzRf3sBbWe

…um gets advice rather than a KeyError

The unboundedness pass looked every column the objective reads up among the
declared variables, and skipped only a given variable. A given expression and
an empty sum are columns the program reads too, so `advice` raised KeyError on
any spec whose objective names one — every composed cost that adds a term to a
scalar sum is such a spec.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AvK6QNNFZHJcRzRf3sBbWe
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AvK6QNNFZHJcRzRf3sBbWe
…e folds into an introducer that sets one

merge read each fragment's given block back through to_dict, which writes
every default. A reading with no domain claimed continuous, and one with no
dtype claimed float, so the frame alone was refused against an integer or
binary column and an integer parameter. The composition docs promise the
reader may say less; it now hands the fold only the fields the file wrote.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AvK6QNNFZHJcRzRf3sBbWe
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AvK6QNNFZHJcRzRf3sBbWe
…e two share a file name

render_tex named each document after the spec's stem, so the library's and
PyPSA's generator.yaml and load.yaml each wrote one file and compile-tex
compiled one of each pair. A document is now named after the spec's path.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AvK6QNNFZHJcRzRf3sBbWe
…ge, with its modes laid over them as patches

Calliope v0.7.0's base math is 14 topic fragments under examples/calliope/,
merged into one spec with nothing left under given. Its MILP math, and each
of its 13 user-math examples and the urban-scale model's extra math, is an
extension fragment; where Calliope restates system_balance, cost_investment,
cost_operation_fixed or the objective to add a term, the extension adds the
term to a sum and no base file changes. The MILP edits, operate mode, SPORES,
inter-cluster storage and the two rewrites of balance_conversion are variants
that override lays over the composition.

Each fragment and variant has a gallery page, the index lists the sums and
the fragments, and the port record lists every Calliope block with its status
and the nine gaps where mathspec needs more than one block for one.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AvK6QNNFZHJcRzRf3sBbWe
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AvK6QNNFZHJcRzRf3sBbWe
…he-2.0 licence

Each file under examples/calliope/ names Calliope contributors beside this
project, is under Apache-2.0 as Calliope's math is, and says it was changed
from Calliope v0.7.0. The pages that print those files are CC-BY-4.0 AND
Apache-2.0, and the index says whose math it is.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AvK6QNNFZHJcRzRf3sBbWe
Brings in #757, #765, #778 and #779, which the next commits use to drop
two of the port's workarounds. CHANGELOG.md keeps both sides.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01N66KMDkSv1rZbhr8jqqJzJ
…multi-dimension sums as Calliope does

#757 admits a sum of parameters under ** and as a divisor, so
storage_retention and cost_annuity_factor are gone: the storage files read
(1 - storage_loss) ** …, and the depreciation rate states Calliope's annuity
factor. #778 and #779 admit sum(over=[a, b]), so the 21 nested sums are
one call each. port.md marks gaps 1 and 8 closed and keeps the numbering.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01N66KMDkSv1rZbhr8jqqJzJ
Brings in #751. CHANGELOG.md keeps both sides.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01N66KMDkSv1rZbhr8jqqJzJ

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

area: composition Assembling a model from files, and fixing a decision docs Documentation pages, guides, reference and README

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants