Absence and where#
A where: does not set a variable to zero. It leaves the variable unbuilt
at the masked coordinates: no column, and no value.
dimensions:
g: { dtype: str }
parameters:
capacity: { dims: [g] }
variables:
dispatch:
dims: [g]
where: "capacity > 0"
With capacity = {wind: 10, gas: 5, old: 0}, the model has dispatch[wind] and
dispatch[gas], but no dispatch[old].
The where grammar says what a where: may
hold.
What creates absence#
| Construct | What is absent |
|---|---|
where: on a variable |
the variable, at the masked coordinates |
where: on a constraint |
the row |
shift(x, along=d, offset=n) without edge= |
the vacated edge coordinate (shift) |
a key combination a missing: absent relation has no row for |
that combination's place in the relation (relations) |
a missing row of a missing: absent parameter |
the parameter, at that coordinate |
Nothing else creates absence. A missing row is the only gap a parameter can
have, because a null or NaN value is refused when
the data is attached. The parameter's missing:
key says what a missing row is:
missing: |
A missing row |
|---|---|
refused, by default |
is refused where the model reads it (where a parameter is read) |
absent |
is absence, by the rules on this page |
| a value | reads as that value, wherever the model reads the parameter |
A value is the same number at every position, so the file says what a missing
row is: missing: 0 for a cost that is zero, missing: 1 for an efficiency
that is one, and missing: false for a flag that is off. The table below says
what a missing row does at each position:
| Position | refused |
absent |
a value |
|---|---|---|---|
| a coefficient, or a constant on either side of a comparison | is refused | removes the row | reads the value |
a factor of a summand in a sum |
is refused | removes the summand, not the row | reads the value |
| a divisor | is refused | removes the row | reads the value. 0 is refused at load |
a bounds: entry |
is refused | leaves that side of the variable open | reads the value |
the amount of an offset= or a window= |
is refused | makes the operator absent there | reads the value |
a comparison in a where |
is false | is false | compares the value |
a bare name in a where |
is false | is false | is false. A bare bool name reads the value |
A value of 0 as an amount reads shift(x, along=t, offset=0) as x itself,
and sums nothing under sum_back(x, along=t, window=0). A curve needs a row at
each breakpoint its points: admits, and
missing breakpoints says what a missing row
of a table that a curve reads means. For a bound only where the data has one,
mask the variable, or declare the parameter missing: absent or
missing: .inf.
How absence travels#
An absent term in arithmetic removes the whole row. An absent term inside a summing operator is only one summand fewer, so the row stays.
variables:
x: { dims: [g] }
y: { dims: [g], where: "capacity > 0" } # no y[old]
constraints:
each:
dims: [g]
expression: x + y >= 1 # rows at wind and gas; no row at old
total:
dims: []
expression: sum(x + y, over=g) >= 1 # x[wind] + y[wind] + x[gas] + y[gas] >= 1
split:
dims: []
expression: sum(x, over=g) + sum(y, over=g) >= 1 # x[old] is back in
total sums the summand wherever the summand exists, but split sums each
operand over its own domain, so the two are different constraints.
A parameter with a missing row works the other way:
Where the variable y is masked, the row is gone. Where a missing: 0
parameter rel_max has no row, it reads as 0, and the row stands as
x <= 0. To drop the row there instead, declare rel_max missing: absent, or
write where: rel_max on the constraint.
| Operator | An output slot reads | An absent input |
|---|---|---|
sum(x, over=d) |
every position along d |
is one summand fewer; the row stands |
sum(x, over=d, by=relation[c]) |
every member of the group | is one summand fewer; the row stands |
sum_back(x, along=d, window=w) |
the positions the window covers | is one summand fewer; the row stands |
shift(x, along=d, offset=n) |
one position, n back |
is the output, so it spreads |
at(x, by=relation[c]) |
one position, through the map | is the output, so it spreads |
Where a parameter is read#
A refused parameter needs a row at each coordinate where the model reads its
value, and at no other coordinate. A tool builds the masks first, because the
masks decide which variables and rows exist. Then the tool refuses a missing
row at a coordinate the model reads, and names the parameter and the
coordinate.
dimensions:
g: { dtype: str }
parameters:
capacity: { dims: [g] }
p_max: { dims: [g] } # refused, by default
variables:
dispatch:
dims: [g]
where: "capacity > 0"
bounds: { lower: 0, upper: p_max }
With capacity = {wind: 10, gas: 5, old: 0}, the model has no dispatch[old].
So the model never reads p_max[old], and a p_max table with rows for wind
and gas only is accepted. A p_max table with no row for gas is refused,
because the model reads the upper bound of dispatch[gas].
The model reads a parameter where its value goes into the model it builds:
| The parameter stands as | The model reads it at |
|---|---|
| a coefficient, a divisor or a constant in a constraint | each row that the model builds |
a factor of a summand in sum or sum_back |
each summand that exists, so not where the summand has an absent factor |
a bounds: entry |
each coordinate where the variable exists |
the amount of an offset= or a window= |
each coordinate where a row that the model builds evaluates the operator |
| a term of the objective | each term that exists |
| a term of a reported expression | each coordinate where the expression is reported |
a values parameter of a piecewise: block |
each breakpoint that the curve runs through |
A row that an absent term removes is not built, so the model reads no parameter in that row.
A where does not read a parameter. A bare name asks whether the table has a
row. A comparison at a missing row is false, unless the missing: of the
parameter names a value. So where: "capacity > 0" above is false at a
generator with no capacity row, and that generator has no dispatch. A
mask reads the same way.
What a missing coordinate means#
By default a masked coordinate has no value, and a row that needs it is not
built. Some quantities have a known number outside their mask, such as zero,
and the missing: key of the variable names it:
variables:
spill:
dims: [storage]
where: has_inflow
missing: 0 # outside the mask spill is 0 and the row stands
soc:
dims: [storage]
where: has_store # the default, missing: absent — no row
constraints:
balance:
dims: [storage]
expression: inflow - spill - soc == 0
At a storage with a store and no inflow, balance reads inflow - soc == 0. At
a storage with inflow and no store, there is no row.
A variable takes missing: absent or a finite number, because a variable has
no data to refuse. A number needs a where:. It must lie in the domain: of
the variable, so a binary variable takes 0 or 1, and inside its literal
bounds:. to_spec refuses the file otherwise. Inside a summing operator,
missing: 0 changes nothing, and another number adds itself once for each
coordinate that the mask leaves out. The typeset legend prints the number
where the mask leaves it out beside the variable.
Rows with no variable terms#
A missing row that reads as 0 can leave a row with nothing to decide, such as
0 == load at a bus with no generator, and such a row is not built. to_spec
refuses an expression that names no variable in the file.
Reported values#
A reported expression inherits the absence of
the solved numbers it reads, by the rules above. A quotient is absent where its
divisor is absent, and where its divisor is exactly zero, whether a solve or the
data gave that zero. A solved value is read as the engine reads it back, so
an engine may read a value within its solver's tolerance of zero as zero. A
divisor parameter with a missing row where the quotient is read is refused,
unless the parameter is missing: absent or names a value other than 0. A deleted row has
no dual.