Dimensions#
A dimension is a set of labels that entries are indexed over, such as the
hours in snapshot or the units in generator.
This page lists the keys a dimension accepts, the closed label lists of
one_of, the rule for the order of a dimension, and the dimensions that
combine others under over.
dimensions#
Each key under dimensions: declares one dimension, and every dimension that
the file uses anywhere is declared here.
dimensions:
snapshot: { dtype: int, ordered: true }
generator: { dtype: str }
parameters:
load: { dims: [snapshot] }
variables:
dispatch: { dims: [snapshot, generator] }
constraints:
power_balance:
dims: [snapshot]
expression: sum(dispatch, over=generator) == load
snapshot has integer labels in a fixed order, and generator has string
labels in no order. sum(dispatch, over=generator) removes generator, so
power_balance has one row per snapshot.
The order of the keys under dimensions:, and the order of a dims: list,
change nothing. mathspec sorts both by name at load, and an entry is indexed
over its dimensions in that order. So dispatch above prints as
\(\mathit{dispatch}_{g,t}\), and the legend lists generator first.
| Key | What it sets | Default |
|---|---|---|
dtype |
the type of a label: float, int, str or datetime |
str |
one_of |
the labels a str dimension allows |
null |
ordered |
whether the order of the labels is part of the spec (ordered) |
false |
over |
the dimensions whose labels each label combines | [] |
description |
free text, which the typeset legend lists | null |
The file does not list the labels themselves. They arrive with the data, in the order that the data table lists them. With data attached, a piece of software that reads a spec (a tool) matches every entry indexed over the dimension to the labels.
mathspec loads a dimension that no entry uses, and for that dimension
python -m mathspec check prints a message that does not refuse the file
(advice).
A map from one dimension onto another is a relation, not a dimension.
one_of#
one_of lists every label that a str dimension allows. With one_of
declared, mathspec checks at load the spelling of each label that a condition
compares against.
dimensions:
carrier: { dtype: str, one_of: [ccgt, ocgt, wind] }
generator: { dtype: str }
relations:
carrier_of: { key: generator, values: carrier }
variables:
p: { dims: [generator], where: "carrier_of == 'wind'" }
p exists only at the generators whose carrier is wind. The relation
carrier_of maps each generator to one carrier, and one_of makes wind one of
three allowed labels.
A one_of list meets four rules:
- Each label is a string, so mathspec refuses a number such as
2030and accepts the string'2030'. - The list has at least one label, and each label once.
- The
dtypeisstrby default, socarrier: { one_of: [ccgt, ocgt, wind] }declares the same dimension. mathspec refuses aone_ofwith any otherdtype. - In every other rule, the dimension is an ordinary
strdimension.
The order of the list changes nothing. mathspec sorts it by name at load, so
one_of: [wind, ccgt, ocgt] declares the same dimension.
At load, mathspec refuses a condition that
compares the dimension, or a relation column over it, against a label that is
not in the list. With carrier_of == 'gnrtr' in place of 'wind' above, mathspec prints:
Variable 'p': 'gnrtr' is not a label of 'carrier', which allows only ['ccgt', 'ocgt', 'wind']. Write one of them, or add 'gnrtr' to the one_of of 'carrier'.
mathspec lists the allowed labels and suggests two rewrites.
With data attached, a tool refuses a label outside the list, in the table of the dimension and in every relation column over it.
Only a dimension takes one_of, and a one_of key on a parameter is refused
as an unknown key.
ordered#
ordered: true makes the order of the labels part of the spec. Five
constructs use the order of a dimension, and each one requires ordered: true:
| Construct | What it uses |
|---|---|
shift(x, along=d, offset=n), in an expression or a condition |
the label n positions back |
sum_back(x, along=d, window=n) |
the last n labels |
position(d) |
where a label sits along d |
piecewise: with over: d |
which breakpoints are neighbours |
a special ordered set (sos:) with type: 2 and along: d |
which labels are consecutive |
The ramp constraint below steps back one snapshot, and snapshot is not
declared ordered:
dimensions:
snapshot: { dtype: int }
generator: { dtype: str }
parameters:
ramp_limit: { dims: [generator] }
variables:
p: { dims: [snapshot, generator] }
constraints:
ramp:
dims: [snapshot, generator]
expression: p - shift(p, along=snapshot, offset=1) <= ramp_limit
Constraint 'ramp': shift(along=snapshot) reads the order of 'snapshot', which is not declared ordered, so the order would come from the row order of the data. Declare 'snapshot: {ordered: true}' under dimensions:.
The file loads once snapshot is declared { dtype: int, ordered: true }.
With snapshots 1, 2 and 3 attached, shift has a row at snapshots 2
and 3 and none at 1.
An int dimension is not ordered unless it declares ordered: true.
An sos entry of type: 1 uses no order, so it needs no ordered: true.
edge='wrap' makes one shift cyclic, so that the first
label steps back to the last. edge='wrap' does not change the dimension, and
the dimension still needs ordered: true.
over#
over declares a multi-dimension: each label is one combination of labels
of the dimensions it lists, its members. Only the combinations that the data
lists exist, so an entry over a multi-dimension has a row only at those
combinations.
dimensions:
unit: {}
bus: {}
snapshot: { dtype: int, ordered: true }
connection: { over: [unit, bus] }
parameters:
capacity: { dims: [unit] }
load: { dims: [bus, snapshot] }
variables:
flow: { dims: [connection, snapshot], bounds: { lower: 0, upper: capacity } }
constraints:
balance:
dims: [bus, snapshot]
expression: sum(flow, over=unit) == load
total:
dims: [snapshot, unit]
expression: sum(flow, over=bus) <= capacity
flow has one row per listed pair of unit and bus, at each snapshot. The data
of connection is a table with one column per member, unit and bus. Each
row is one combination, each value is a label of its member, and no
combination repeats. The typeset spec writes connection as a subset of the
product of its members:
A multi-dimension means the same as an entry over its members that has rows
only at the listed combinations. So flow above means the same as
flow: { dims: [bus, snapshot, unit] } with the rows confined to a bare
relation connection: { key: [unit, bus], missing: absent }.
The multi-dimension states the confinement once, in the dimension, and every
entry over it inherits the confinement.
How a multi-dimension meets its members#
A multi-dimension stands for its members wherever two frames meet:
| You write | mathspec reads |
|---|---|
flow <= capacity, capacity over unit |
capacity at the unit of each listed pair |
sum(flow, over=unit) |
the sum over the listed units of each bus; a bus with no listed unit gets 0 |
sum(flow, over=connection) |
the sum over every listed pair |
sum(x, over=connection), x over members |
the sum of x over the listed pairs only |
a constraint over [bus, snapshot, unit] |
flow only at the listed pairs; the constraint has no row at any other pair |
where: connection, over the members |
true at the listed pairs |
where: output, output over connection |
output at each listed pair; false at any other pair, so NOT output is true there |
shift and sum_back along a member keep the multi-dimension. A lookup or a
sum through a relation on a member reads the operand over the
members first.
What mathspec refuses#
A multi-dimension has no order and holds no member twice. So mathspec refuses, at load:
- a member listed twice, or fewer than two members;
dtype,one_ofororderedon the multi-dimension, which belong on a member;- a multi-dimension as a member of another one;
- a
dims:list with a multi-dimension beside one of its members; - two multi-dimensions that share a member in one frame, in
dims:or in arithmetic; shift,sum_back,position,piecewise:andsos:along a multi-dimension;- a comparison of a multi-dimension against a label;
- a relation column over a multi-dimension. To give each combination a value, declare a parameter over the multi-dimension.
For two multi-dimensions that share unit, connection: { over: [unit, bus] }
and active: { over: [unit, period] } in sum(on + flow, over=unit), mathspec
prints:
Constraint 'c': 'active' and 'connection' both combine 'unit', so no frame holds both. Index 'connection' by its members ['bus', 'unit'] and confine it with where: connection, or declare one dimension over ['bus', 'period', 'unit'].
See also
- choose between a dimension, a relation and a parameter for a column of data.
- limits for what a multi-dimension cannot state.