Skip to content

feat(language): a piecewise where reaches a link that walks a relation, so only some converters need a curve - #635

Merged
FBumann merged 5 commits into
claude/adoring-galileo-mu42irfrom
feat/piecewise-walked-where
Sep 23, 2026
Merged

FBumann merged 5 commits into
claude/adoring-galileo-mu42irfrom
feat/piecewise-walked-where

Conversation

@FBumann

@FBumann FBumann commented Sep 22, 2026

Copy link
Copy Markdown
Contributor

Prompt: "It does mix curve and non curve converters!" … "Yes" (to: stack the piecewise change on #630, using at() in a where: from #634)

Note

The following content was generated by AI.

What this changes

Stacked on #630, with #634 merged in. A block where: now reaches a walked link. The row reads the mask through the link's relation, so a converter with no curve gets no weights, no binaries and no rows, and its flows need no breakpoints.

piecewise:
  operating_point:
    along: bp
    dims: [converter, snapshot]
    where: has_curve
    links:
      rate: { expression: rate, values: bp_rate, by: converter_of, over: converter, into: flow }

The expansion writes this row:

operating_point_rate:
  dims: [flow, snapshot]
  where: at(has_curve, by=converter_of, over=converter, into=flow)
  expression: (rate) == sum(at(operating_point_lam, by=converter_of, over=converter, into=flow) * bp_rate, over=bp)
The rules
Mask The walked row takes
none nothing, as before
carries every dim the walk consumes or joins on at(<where>, by, over, into). A ragged mask goes through as at(count(<where>, over=bp) > 0, …), as the rows over dims: alone take it
carries none of them (e.g. snapshot only) the mask as written, since the row keeps those dims
carries some and not the rest refused: where 'curved' carries ['generator'] and not ['snapshot'], and the link reads the curve through 'generator_of' at all of […]

The completeness check splits. <block>_complete covers the links on dims:. Each walked link states <block>_<link>_complete, which asks for its values only at the rows it reads the curve at: under the mask read through the relation, or where the relation has a row when there is no mask. Asked with the other links, bp_rate was demanded at every flow, including flows of converters with no curve. The per-link name is reserved like the other derived names.

examples/piecewise_coupling.yaml is fluxopt's mix. A CHP on a curve sits beside a fixed-ratio boiler, and one has_curve is read both ways: the curve reads it, and fixed_ratio reads NOT at(has_curve, …).

Guard table: each guard deleted in turn, whole suite run, tree restored
Guard Caught by
a walked row reads the where through its relation 20 tests, incl. test_a_block_mask_reaches_a_walked_link_through_its_relation
a mask carrying none of the walk's dims reads as written test_a_mask_over_dims_the_walk_keeps_reaches_the_walked_row_as_written
a partial mask is refused test_a_mask_carrying_part_of_what_a_walk_reads_through_is_refused
joined dims count as read through the same test (its relation is keyed by flow and snapshot)
a walked link has its own completeness 6 tests, incl. …asked_only_at_the_rows_it_reads_the_curve_at[a-mask-read-through]
walked completeness is asked only where the relation reaches …[no-mask-asks-only-where-the-relation-reaches]
the per-link condition name is reserved test_a_walked_links_own_condition_is_a_name_the_block_reserves

Coverage moved: test_a_block_mask_that_cannot_reach_a_walked_link_is_refused asserted the refusal this PR removes. Its case is now test_a_block_mask_reaches_a_walked_link_through_its_relation.

Gates, and what was not run
Gate Result
pytest -q -n auto 1642 passed, 6 skipped
ruff, prettier, typos, reuse lint clean
pyrefly check 12 errors, the same count as the base in this venv
mkdocs build only the docs.python.org inventory fetch fails (proxy 403)
golden output, generated pages, schema regenerated and read; the schema does not change
compile-tex, pixi run ci not run: no pixi and no LaTeX here
Deliberately not done

Why

fluxopt mixes converters on a curve with converters at a fixed ratio. Before this, a walked block built a curve and its binaries for every converter in dims:, and demanded breakpoint rows for every flow.

🤖 Generated with Claude Code

https://claude.ai/code/session_01HxoXSQCyrcDs6vuRk3x6tz


Generated by Claude Code

FBumann and others added 5 commits September 22, 2026 18:19
…sion, and composition is a limit on the file (#624)
Co-authored-by: energy-models-release-please[bot] <319464627+energy-models-release-please[bot]@users.noreply.github.com>
…at()

`at(<predicate>, by=<relation>, over=<a>, into=<b>)` reads a mask over
coarse coordinates at fine ones, as `at` reads an array: true where the
relation has a row and the predicate holds at the coordinate it maps to,
false where the relation has no row. It is the third operator that reads
a predicate, beside `shift` and `count`, and is held to the rules of the
expression `at`, so a bad read is refused in the same words in both.

The program carries it as `PulledBackPredicate`: the operand mask, the
`Direction`, and the dims after the read. `names_read` includes the
relation. Exclusivity refuses it in a case `when:` as undecidable, as it
refuses `shift` and `count`.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HxoXSQCyrcDs6vuRk3x6tz
…n, so only some converters need a curve

A walked link's row is over the dims the walk produces, and the block's
`where:` is over `dims:`. The row now reads the mask through the link's
relation, `at(<where>, by=…, over=…, into=…)`, where the mask carries every
dim the walk consumes or joins on, and as written where it carries none of
them. A mask carrying some and not the rest is refused, naming the missing
dims. The refusal of a block `where:` beside a walk is gone.

A walked link's values are asked for at the rows it reads the curve at,
under `<block>_<link>_complete`, so a flow of a converter with no curve
needs no breakpoint row. `<block>_complete` keeps the links on `dims:`.

`examples/piecewise_coupling.yaml` is now fluxopt's system: a CHP on a
curve beside a fixed-ratio boiler, one `has_curve` read both ways.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HxoXSQCyrcDs6vuRk3x6tz
@FBumann
FBumann added this pull request to stack #636 September 22, 2026 20:47
@FBumann
FBumann merged commit 0e0820b into main Sep 23, 2026
6 checks passed

FBumann commented Sep 23, 2026

Copy link
Copy Markdown
Contributor Author

Prompt: "Should we then combine 630 and 635?" … "Do it"

Note

The following content was generated by AI.

Closed in favour of #630, which now carries this PR's commit. #630's branch was fast-forwarded to this branch's head abd6821, then took #569 with main merged in. #630's body now covers both changes, including this PR's guard table.


Generated by Claude Code


Generated by Claude Code

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.

2 participants