What can be improved?
There are currently two approaches to defining constraints / expressions in the spec:
-
One expression + mask combo for a named expression/constraint
constraints
storage_balance_first_ts:
foreach: [name, snapshot]
mask: not cyclic_state_of_charge==True and snapshot == get_val_at_index(snapshot=0)
expression: $soc + ($dispatch * snapshot_weightings) = state_of_charge_initial
all_other_ts:
foreach: [name, snapshot]
mask: (not cyclic_state_of_charge and not snapshot == get_val_at_index(snapshot=0)) or cyclic_state_of_charge
expression: $soc + ($dispatch * snapshot_weightings) = roll($soc * (standing_loss ** snapshot_weightings), snapshot=1)
-
Possibility of a list of expression + mask combos, where the masks must be disjoint
constraints
storage_balance:
foreach: [name, snapshot]
equations:
- mask: not cyclic_state_of_charge==True and snapshot == get_val_at_index(snapshot=0)
expression: $soc + ($dispatch * snapshot_weightings) = state_of_charge_initial
- mask: (not cyclic_state_of_charge and not snapshot == get_val_at_index(snapshot=0)) or cyclic_state_of_charge
expression: $soc + ($dispatch * snapshot_weightings) = roll($soc * (standing_loss ** snapshot_weightings), snapshot=1)
(2) is where we got to with the original spec but @FBumann has tended more towards (1).
The reason for (2) was to keep the number of resulting named constraints/expressions small. There are lots of cases where you have a general concept (e.g., balance storage device levels in each timestep) that comes with several variants (what to do in the first timestep differently to all other timesteps). Rather than ask the user to come up with a name for each, they can just provide lists of each variant. It also leads to much more dense expression/constraint arrays which, when storing the definition in an xarray dataset, is beneficial to reduce memory footprint of the built optimisation problem.
However, the list-of-variants approach has a few downsides:
- it's confusing, hard to debug, and not super readable nor easy to document
- overriding by merging YAMLs requires overriding all lists at once as you don't have a named key to check against
One downside both approaches have is that you will always want to define variants with disjoint masks and we have no way of knowing that they're disjoint until we evaluate. It's not something we can catch when parsing the spec.
A middle ground might be to enforce naming of variants. As with current list-of-variants approach, these would be rendered as cases of constraints in the latex math and would live in the same, single expression array of expressions.
constraints
storage_balance:
foreach: [name, snapshot]
cases:
first_ts:
mask: not cyclic_state_of_charge==True and snapshot == get_val_at_index(snapshot=0)
expression: $soc + ($dispatch * snapshot_weightings) = state_of_charge_initial
all_other_ts:
mask: (not cyclic_state_of_charge and not snapshot == get_val_at_index(snapshot=0)) or cyclic_state_of_charge
expression: >-
$soc + ($dispatch * snapshot_weightings) =
roll($soc * (standing_loss ** snapshot_weightings), snapshot=1)
constraint groups
constraint_groups:
storage_balance: [ ... ]
Version
v0.0.0
What can be improved?
There are currently two approaches to defining constraints / expressions in the spec:
One expression + mask combo for a named expression/constraint
Possibility of a list of expression + mask combos, where the masks must be disjoint
(2) is where we got to with the original spec but @FBumann has tended more towards (1).
The reason for (2) was to keep the number of resulting named constraints/expressions small. There are lots of cases where you have a general concept (e.g., balance storage device levels in each timestep) that comes with several variants (what to do in the first timestep differently to all other timesteps). Rather than ask the user to come up with a name for each, they can just provide lists of each variant. It also leads to much more dense expression/constraint arrays which, when storing the definition in an xarray dataset, is beneficial to reduce memory footprint of the built optimisation problem.
However, the list-of-variants approach has a few downsides:
One downside both approaches have is that you will always want to define variants with disjoint masks and we have no way of knowing that they're disjoint until we evaluate. It's not something we can catch when parsing the spec.
A middle ground might be to enforce naming of variants. As with current list-of-variants approach, these would be rendered as
casesof constraints in the latex math and would live in the same, single expression array of expressions.constraint groups
Version
v0.0.0