Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion examples/building-models-from-specs.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -925,7 +925,7 @@
"cell_type": "markdown",
"id": "49",
"metadata": {},
"source": "## Where the code lives, and an upstream note\n\nThe feature is a small package, `linopy/spec/`, imported only when you call\n`add_spec`/`from_spec` — `import linopy` never pulls in `math_spec`. Roughly:\n\n- `accessor.py` — `model.spec`, the `NamedExpression` views, `evaluate`, and\n typesetting: the whole model (`m.spec.to_latex` / `.to_markdown` /\n `.to_typst`) and any single declaration — a named expression, constraint or\n variable — via `m.spec.declaration(name)` and math-spec's\n `typeset_declaration`.\n- `attach.py` — the three attachment rules; data onto master coordinates.\n- `builder.py` — emits variables, constraints, objective; folds expressions.\n- `operators.py` — `sum`, `by=`, `shift`, `at`, `sum_back`.\n- `where.py` — `where:` predicates as boolean masks.\n- `coverage.py` / `terms.py` — the absence rule from section 6: a missing row\n is refused wherever it is used.\n- `curves.py` — the data side of `piecewise:` blocks.\n- `netcdf.py` — the factorize-based persistence from section 10.\n- `nodes.py` — walks over expression nodes. One workaround lives here:\n math-spec alpha.73's `program.children()` does not descend into a `Power`\n node, so parameters hidden under `**` would be missed; `nodes.py` walks into\n the base and exponent itself.\n\n### Summary\n\nA spec is the maths over labelled axes; the sources are the numbers. `linopy`\nattaches them into an ordinary model, hands each named expression back as three\nviews — its formula, its unsolved linopy expression and its solution — refuses a\nmissing parameter row wherever it is used (as a coefficient, bound, constant\nside or divisor alike, with `where:` and filling the data as the escape\nhatches), and round-trips the lot through netCDF by keeping the spec as text\nbeside factorized labels."
"source": "## Where the code lives\n\nThe feature is a small package, `linopy/spec/`, imported only when you call\n`add_spec`/`from_spec` — `import linopy` never pulls in `math_spec`. Roughly:\n\n- `accessor.py` — `model.spec`, the `NamedExpression` views, `evaluate`, and\n typesetting: the whole model (`m.spec.to_latex` / `.to_markdown` /\n `.to_typst`) and any single declaration — a named expression, constraint or\n variable — via `m.spec.declaration(name)` and math-spec's\n `typeset_declaration`.\n- `attach.py` — the three attachment rules; data onto master coordinates.\n- `builder.py` — emits variables, constraints, objective; folds expressions.\n- `operators.py` — `sum`, `by=`, `shift`, `at`, `sum_back`.\n- `where.py` — `where:` predicates as boolean masks.\n- `coverage.py` / `terms.py` — the absence rule from section 6: a missing row\n is refused wherever it is used.\n- `curves.py` — the data side of `piecewise:` blocks.\n- `netcdf.py` — the factorize-based persistence from section 10.\n- `nodes.py` — walks over expression nodes, and the dimensions a node\n spans before any data is bound.\n\n### Summary\n\nA spec is the maths over labelled axes; the sources are the numbers. `linopy`\nattaches them into an ordinary model, hands each named expression back as three\nviews — its formula, its unsolved linopy expression and its solution — refuses a\nmissing parameter row wherever it is used (as a coefficient, bound, constant\nside or divisor alike, with `where:` and filling the data as the escape\nhatches), and round-trips the lot through netCDF by keeping the spec as text\nbeside factorized labels."
}
],
"metadata": {
Expand Down
4 changes: 2 additions & 2 deletions linopy/spec/coverage.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
from linopy.spec import terms
from linopy.spec.context import Context
from linopy.spec.errors import SpecDataError
from linopy.spec.nodes import amounts_of, children, parameters_of
from linopy.spec.nodes import amounts_of, parameters_of
from linopy.spec.where import evaluate_where

Rows = xr.DataArray | None
Expand Down Expand Up @@ -101,7 +101,7 @@ def _collect(
narrowed = inside if rows is None else rows & inside
_collect(region.value, ctx, narrowed, constant, into)
return
for child in children(node):
for child in ms.children(node):
_collect(child, ctx, rows, constant, into)


Expand Down
13 changes: 3 additions & 10 deletions linopy/spec/nodes.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
"""Walks over expression nodes that descend into every operand, a ``Power``'s included."""
"""Walks over a program's expression nodes, and the dimensions a node spans."""

from __future__ import annotations

Expand All @@ -7,18 +7,11 @@
from math_spec import program as ms


def children(node: ms.ExpressionNode) -> tuple[ms.ExpressionNode, ...]:
"""The operands of *node*: ``math_spec.program.children`` plus a power's base and exponent."""
if isinstance(node, ms.Power):
return (node.base, node.exponent)
return ms.children(node)


def walk(*nodes: ms.ExpressionNode) -> Iterator[ms.ExpressionNode]:
"""Every node under *nodes*, each of them included, parents first."""
for node in nodes:
yield node
yield from walk(*children(node))
yield from walk(*ms.children(node))


def amounts_of(node: ms.ExpressionNode) -> Iterator[str]:
Expand Down Expand Up @@ -55,4 +48,4 @@ def _dims(node: ms.ExpressionNode, program: ms.Program) -> frozenset[str]:
return (_dims(node.operand, program) - {node.over}) | set(node.into)
if isinstance(node, ms.Cases):
return frozenset().union(*(_dims(r.value, program) for r in node.regions))
return frozenset().union(*(_dims(c, program) for c in children(node)))
return frozenset().union(*(_dims(c, program) for c in ms.children(node)))
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,7 @@ gpu = [
# keeps the git pin out of the published wheel metadata, which PyPI rejects.
# Install with `uv sync --group spec` or `uv pip install --group spec`.
spec = [
"math-spec @ git+https://github.com/energy-models/math-spec.git@67aeedb988ee95d196456b4cbe50821e3799edaa ; python_version >= '3.12'",
"math-spec @ git+https://github.com/energy-models/math-spec.git@v0.0.0-alpha.76 ; python_version >= '3.12'",
"pyyaml ; python_version >= '3.12'",
"pyarrow ; python_version >= '3.12'",
]
Expand Down
Loading