math_spec.piecewise
Expand piecewise: blocks into plain variables and constraints.
A piecewise: block becomes ordinary affine declarations before anything
reads the model. The λ convex-combination method needs only the breakpoint
parameters themselves, no derived data. For a block
piecewise:
curve:
over: bp
links:
- [power, power_bp]
- [fuel * eff, fuel_bp, "<="]
with F = the union of the links' dims, it emits:
variables:
curve_lam(F, bp) in [0, 1]
curve_seg(F, bp) binary (method: adjacency)
constraints:
curve_convexity(F): sum(curve_lam, over=bp) == 1
curve_pick(F): sum(curve_seg, over=bp) == 1 (method: adjacency)
curve_adjacency(F, bp): curve_lam <= curve_seg + shift(curve_seg, over=bp, offset=1, edge=0)
curve_link0(F): (power) == sum(curve_lam * power_bp, over=bp)
curve_link1(F): (fuel * eff) <= sum(curve_lam * fuel_bp, over=bp)
Only the restriction on λ varies (:data:~math_spec.model.PIECEWISE_METHODS);
lp emits no weights at all. A link expression is judged before expansion,
so p * p is refused against the link the user wrote rather than
curve_link0.
curvature_required(pw)
#
The curvature pw's method is only exact for, or None if any shape works.
convex relaxes the weights onto the hull, which cuts the corners of a
mixed curve and nothing else, so it answers 'either'. lp states
one side of the curve as its segment lines and the bounded link's sign says
which side, so the opposite bend is silently wrong rather than merely loose.
The breakpoints decide whether a model meets the condition, and they arrive with the data rather than with the schema — so this names what to check, and the caller holding the numbers does the checking.
| PARAMETER | DESCRIPTION |
|---|---|
pw
|
The block whose method is in question.
TYPE:
|
| RETURNS | DESCRIPTION |
|---|---|
Curvature | None
|
One of :data: |
Curvature | None
|
the shape, or |
Source code in src/math_spec/piecewise.py
expand_piecewise(schema)
#
Return schema as a :class:_ExpandedSpec — every piecewise: block expanded away.
The adjacency row shifts with edge=0: a bare shift would drop the
first breakpoint's row and leave its weight unconstrained, a wrong MILP
with no error (#289). points: masks the weights and the segment
binaries and no constraint — every emitted row reduces over the breakpoint
axis or carries a masked weight. The result is memoised on schema, and a
:class:_ExpandedSpec comes straight back; a model with no piecewise: is
retyped with model_construct, its validation already done on the way in.
| RAISES | DESCRIPTION |
|---|---|
PiecewiseExpansionError
|
A block naming something that does not exist, or emitting a name the file already declares. |
Source code in src/math_spec/piecewise.py
113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 | |
mask_of(block, pw)
#
The parameter a block masks its weights with, or None for a whole curve.
points: may name the mask itself, or one of the block's own values
parameters — "the curve runs as far as this does" — in which case the
mask is derived from that parameter when data binds, under the name this
returns.