math_spec.where_parser
pyparsing-based parser for where strings — grammar and AST only.
Parses strings like "p_max > 0 AND NOT is_must_run" into an AST. What a
mask means is each backend's business: the eager lane evaluates the AST
against an xr.Dataset (builder.evaluate_where), the relational lane
lowers it to SQL predicates (lowering._lower_where).
Kept dependency-free on purpose — validation.py and lowering.py are
linopy-free by hard rule 3, and they import this module.
NotNode, AndNode and OrNode reference the WhereNode union in
their annotations before it is defined, which works only because from
__future__ import annotations makes annotations strings — removing that
future-import requires reordering the definitions.
PredicateOperator = Literal['<=', '>=', '==', '!=', '<', '>']
module-attribute
#
WhereNode = BooleanLiteralNode | UnresolvedNameNode | UnresolvedComparisonNode | UnresolvedPositionNode | DimensionPositionNode | ParameterDefinedNode | VariableDefinedNode | ParameterComparisonNode | DimensionComparisonNode | LookupComparisonNode | LookupPairComparisonNode | LookupDefinedNode | NotNode | AndNode | OrNode
module-attribute
#
DimensionComparisonNode(name, op, value)
dataclass
#
DimensionPositionNode(name, op, position, by=None)
dataclass
#
Compare a dimension's coordinates against one named by position.
where: "snapshot == index(snapshot, 0)" — the boundary of a recurrence
named by where it sits rather than by the label that happens to be there,
so the clause survives the index being relabelled. Negative counts from
the end, -1 being the last.
With by it is the boundary of each group the lookup makes —
index(snapshot, 0, by=period_of) is every period's first snapshot, and
a row reads its own group's, the broadcast at(by=) already defines.
Resolved rather than lowered to a literal: which label sits at a position is a property of the data, so the position travels and each lane reads it off the coordinate order it already holds.
LookupComparisonNode(name, over, op, value)
dataclass
#
Compare a lookup's values against a literal — period_of == 2030.
over is the dimension the lookup maps out of, copied off the
declaration during resolution so the frame check and both lanes read it
here rather than looking the lookup up again.
LookupDefinedNode(name, over)
dataclass
#
True where the named lookup has a value — the partial-lookup case.
A lookup may be partial: a null says the label belongs to no group (a generator on no bus, a line with one open end). This is how a declaration asks for the labels that do map, spelled as a bare name exactly as a parameter's definedness is.
LookupPairComparisonNode(name, other, over, op)
dataclass
#
Compare two lookups over one dimension — from != to.
The one comparison whose both sides are structure: two maps out of the same dimension, tested row by row on that dimension's own table. Over different dims there is no row to compare them on, which resolution refuses.
ParameterComparisonNode(name, op, value)
dataclass
#
ParameterDefinedNode(name)
dataclass
#
True wherever the named parameter is non-null and finite.
name
instance-attribute
#
UnresolvedComparisonNode(name, op, value, quoted=False)
dataclass
#
UnresolvedNameNode(name)
dataclass
#
A bare name — unresolved. resolution.py types it.
name
instance-attribute
#
UnresolvedPositionNode(name, op, dimension, position, by=None)
dataclass
#
lhs <op> index(dim, i) before either name is checked.
Kept apart from :class:UnresolvedComparisonNode because its right-hand
side names a dimension and a position, which no literal can carry.
resolution.py types it into :class:DimensionPositionNode.
VariableDefinedNode(name)
dataclass
#
True at the coordinates where the named variable exists.
The variable counterpart of :class:ParameterDefinedNode, and spelled the
same way — a bare name. A parameter's bare name asks whether it has a value
here; a variable's asks whether it exists here.
name
instance-attribute
#
parse_where(text)
#
Parse a where string into an AST.
With parse_all and a single top-level alternative, element 0 of the
parse result is the root node.