Constraints
Balance constraint
Nodal balance
@@addconstraintnodal_balance!
Node injection
@@addconstraintnode_injection!
Node state capacity
@@addconstraintnodestatecapacity!
Cyclic condition on node state variable
@@addconstraintcyclicnodestate!
Unit operation
In the following, the operational constraints on the variables associated with units will be elaborated on. The static constraints, in contrast to the dynamic constraints, are addressing constraints without sequential time-coupling. It should however be noted that static constraints can still perform temporal aggregation.
Static constraints
The fundamental static constraints for units within SpineOpt relate to the relationships between commodity flows from and to units and to limits on the unit flow capacity.
Conversion constraint / limiting flow shares inprocess / relationship in process
A unit can have different commodity flows associated with it. The most simple relationship between these flows is a linear relationship between input and/or output nodes/node groups. SpineOpt holds constraints for each combination of flows and also for the type of relationship, i.e. whether it is a maximum, minimum or fixed ratio between commodity flows. Note that node groups can be used in order to aggregate flows, i.e. to give a ratio between a combination of units flows.
Ratios between flows of a unit
@@addconstraintratiounitflow!
Bounds on the unit capacity
@@addconstraintunitflowcapacity!
Constraint on minimum operating point
@@addconstraintminimumoperatingpoint!
Dynamic constraints
Commitment constraints
For modeling certain technologies/units, it is important to not only have unit_flow variables of different commodities, but also model the online ("commitment") status of the unit/technology at every time step. Therefore, an additional variable units_on is introduced. This variable represents the number of online units of that technology (for a normal unit commitment model, this variable might be a binary, for investment planning purposes, this might also be an integer or even a continuous variable). To define the type of a commitment variable, see online_variable_type. Commitment variables will be introduced by the following constraints (with corresponding parameters):
- constraint on
units_on
- constraint on
units_available
- constraint on the unit state transition
- constraint on minimum down time
- constraint on minimum up time
- constraint on ramp rates
- constraint on reserve provision
Bound on available units
@@addconstraintunits_available!
Unit state transition
@@addconstraintunitstatetransition!
Minimum down time
@@addconstraintmindowntime!
Minimum up time
@@addconstraintminuptime!
Ramping constraints
To include ramping and reserve constraints, it is a pre requisite that minimum operating points and capacity constraints are enforced as described.
For dispatchable units, additional ramping constraints can be introduced. For setting up ramping characteristics of units see Ramping.
Ramp up limit
@@addconstraintramp_up!
Ramp down limit
@@addconstraintramp_down!
Reserve constraints
Constraint on minimum node state for reserve provision
(Comment 2023-11-20: Currently under development)
Operating segments
Operating segments of units
@@addconstraintoperatingpointbounds!
Rank operating segments as per the index of operating points
@@addconstraintoperatingpointrank!
Operating segments of units
@@addconstraintunitflowop_bounds!
Bounding operating segments to use up its own capacity for activating the next segment
@@addconstraintunitflowop_rank!
Bounding unit flows by summing over operating segments
@@addconstraintunitflowop_sum!
Bounds on commodity flows
Bound on cumulated unit flows
@@addconstrainttotalcumulatedunit_flow!
Network constraints
Static constraints
Capacity constraint on connections
@@addconstraintconnectionflowcapacity!
Fixed ratio between outgoing and incoming flows of a connection
@@addconstraintratiooutinconnectionflow!
Specific network representation
In the following, the different specific network representations are introduced. While the Static constraints find application in any of the different networks, the following equations are specific to the discussed use cases. Currently, SpineOpt incorporated equations for pressure driven gas networks, nodal lossless DC power flows and PTDF based lossless DC power flow.
Pressure driven gas transfer
For gas pipelines it can be relevant a pressure driven gas transfer can be modelled, i.e. to account for linepack flexibility. Generally speaking, the main challenges related to pressure driven gas transfers are the non-convexities associated with the Weymouth equation. In SpineOpt, a convexified MILP representation has been implemented, which as been presented in Schwele - Coordination of Power and Natural Gas Systems: Convexification Approaches for Linepack Modeling. The approximation approach is based on the Taylor series expansion around fixed pressure points.
In addition to the already known variables, such as connection_flow and node_state, the start and end points of a gas pipeline connection are associated with the variable node_pressure. The variable is triggered by the has_pressure parameter. For more details on how to set up a gas pipeline, see also the advanced concept section on pressure driven gas transfer.
Maximum node pressure
@@addconstraintmaxnodepressure!
Minimum node pressure
@@addconstraintminnodepressure!
Constraint on the pressure ratio between two nodes
@@addconstraintcompression_ratio!
Outer approximation through fixed pressure points
@@addconstraintfixnodepressure_point!
Enforcing unidirectional flow
@@addconstraintconnectionunitarygas_flow!
Gas connection flow capacity
@@addconstraintconnectionflowgas_capacity!
Linepack storage flexibility
@@addconstraintstoragelinepack!
Node-based lossless DC power flow
For the implementation of the nodebased loss DC powerflow model, a new variable node_voltage_angle is introduced. See also has_voltage_angle. For further explanation on setting up a database for nodal lossless DC power flow, see the advanced concept chapter on Lossless nodal DC power flows.
Maximum node voltage angle
@@addconstraintmaxnodevoltage_angle!
Minimum node voltage angle
@@addconstraintminnodevoltage_angle!
Voltage angle to connection flows
@@addconstraintnodevoltageangle!
PTDF based DC lossless powerflow
Connection intact flow PTDF
@@addconstraintconnectionintactflow_ptdf!
N-1 post contingency connection flow limits
@@addconstraintconnectionflowlodf!
Investments
Investments in units
Technical lifetime of a unit
@@addconstraintunit_lifetime!
Available Investment Units
@@addconstraintunitsinvestedavailable!
Investment transfer
@@addconstraintunitsinvestedtransition!
Investments in connections
Available invested-in connections
@@addconstraintconnectionsinvestedavailable!
Transfer of previous investments
@@addconstraintconnectionsinvestedtransition!
Intact network ptdf-based flows on connections
@@addconstraintconnectionflowintact_flow!
Intact connection flow capacity
@@addconstraintconnectionintactflow_capacity!
Fixed ratio between outgoing and incoming intact flows of a connection
@@addconstraintratiooutinconnectionintact_flow!
Lower bound on candidate connection flow
@@addconstraintcandidateconnectionflow_lb!
Upper bound on candidate connection flow
@@addconstraintcandidateconnectionflow_ub!
Technical lifetime of a connection
@@addconstraintconnection_lifetime!
Investments in storages
Note: can we actually invest in nodes that are not storages? (e.g. new location)
Available invested storages
@@addconstraintstoragesinvestedavailable!
Storage capacity transfer
@@addconstraintstoragesinvestedtransition!
Technical lifetime of a storage
@@addconstraintstorage_lifetime!
Capacity transfer
(Comment 2021-04-29: Currently under development)
Early retirement of capacity
(Comment 2021-04-29: Currently under development)
User constraints
User constraint
@@addconstraintuser_constraint!
Benders decomposition
This section describes the high-level formulation of the benders-decomposed problem.
Taking the simple example of minimising capacity and operating cost for a fleet of units with a linear cost coefficient $p^{operational\_cost}$:
\[\begin{aligned} min& \\ & \sum_{u,s,t} \left( p^{unit\_investment\_cost}_{(u,s,t)} \cdot v^{units\_invested}_{(u,s,t)} + \sum_{n,d} p^{operational\_cost}_{(u,n,d,s,t)} \cdot v^{unit\_flow}_{(u, n, d, s, t)} \right) \\ s.t. & \\ & v^{unit\_flow}_{(u,n,d,s,t)} \le p^{unit\_capacity}_{(u, n, d, s, t)} \cdot \left( v^{units\_available}_{(u,s,t)} + v^{units\_invested\_available}_{(u, s, t)} \right) \quad \forall u \in unit, n \in node, s, t \\ & \sum_{u,d} v^{unit\_flow}_{(u,n,d,s,t)} = p^{demand}_{(n, s, t)} \quad \forall n \in node, s,t \end{aligned}\]
So this is a single problem that can't be decoupled over $t$ because the investment variables units_invested_available couple the timesteps together. If units_invested_available were a constant in the problem, then all $t$'s could be solved individually. This is the basic idea in Benders decomposition. We decompose the problem into a master problem and sub problems with the master problem optimising the coupling investment variables which are treated as constants in the sub problems.
The master problem is built by replacing the operational costs (which will be minimised in the sub problem) by a new decision variable, $v^{sp\_objective}$:
\[\begin{aligned} min & \\ & \sum_{u,s,t} p^{unit\_investment\_cost}_{(u,s,t)} \cdot v^{units\_invested}_{(u,s,t)} + v^{sp\_objective} \\ s.t. & \\ & v^{sp\_objective} \geq 0 \end{aligned} \]
The solution to this problem yields values for the investment variables which are fixed as $p^{units\_invested\_available}$ in the sub problem and will be zero in the first iteration.
The sub problem for benders iteration $b$ then becomes :
\[\begin{aligned} min& \\ & sp\_obj_b = \sum_{u,n,d,s,t} p^{operational\_cost}_{(u,n,d,s,t)} \cdot v^{unit\_flow}_{(u, n, d, s, t)}\\ s.t.& \\ & v^{unit\_flow}_{(u,n,d,s,t)} \le p^{unit\_capacity}_{(u, n, d, s, t)} \cdot \left( v^{units\_available}_{(u,s,t)} + p^{units\_invested\_available}_{(b, u, s, t)} \right) \\ & \qquad \forall u \in unit, n \in node, s,t \qquad [\mu_{(b,u,s,t)}] \\ & \sum_{u,d} v^{unit\_flow}_{(u,n,d,s,t)} = p^{demand}_{(n, s, t)} \quad \forall n \in node, s,t \end{aligned}\]
This sub problem can be solved individually for each $t$. This is pretty trivial in this small example but if we consider a single t to be a single rolling horizon instead, decoupling the investment variables means that each rolling horizon can be solved individually rather than having to solve the entire model horizon as a single problem.
$\mu_{(b,u,s,t)}$ is the marginal value of the capacity constraint for benders iteration $b$ and can be interpreted as the decrease in the objective function for an additional MW of flow from unit $u$ (in scenario $s$ at time $t$). Thus, an upper bound on the sub problem objective function is obtained as follows:
\[sp\_obj_{b} + \sum_{u,n,d,s,t} \mu_{(b,u,s,t)} \cdot p^{unit\_capacity}_{(u,n,d,s,t)} \cdot \left(v^{units\_invested\_available}_{(u,s,t)} - p^{units\_invested\_available}_{(b,u,s,t)}\right)\]
The above is added to the master problem for the next iteration as a new constraint, called a Benders cut, thus becoming:
\[\begin{aligned} min & \\ & \sum_{u,s,t} p^{unit\_investment\_cost}_{(u,s,t)} \cdot v^{units\_invested}_{(u,s,t)} + v^{sp\_objective} \\ s.t. & \\ & v^{sp\_objective} \geq sp\_obj_{b} \\ & \quad + \sum_{u,n,d,s,t} \mu_{(b,u,s,t)} \cdot p^{unit\_capacity}_{(u,n,d,s,t)} \cdot \left(v^{units\_invested\_available}_{(u,s,t)} - p^{units\_invested\_available}_{(b,u,s,t)}\right) \quad \forall b \\ \end{aligned}\]
Note the benders cuts are added as inequalities because they represent an upper bound on the value we are going to get for the sub problem objective function by adjusting the master problem variables in that benders iteration. If we consider the example of renewable generation - because it's marginal cost is zero, on the first benders iteration, it could look like there would be a lot of value in increasing the capacity because of the marginal values from the sub problems. However, when the capacity variables are increased accordingly and curtailment occurs in the sub-problems, the marginal values will be zero when curtailment occurs and so, other resources may become optimal in subsequent iterations.
This is a simple example but it illustrates the general strategy. The algorithm pseudo code looks something like this:
initialise master problem
initialise sub problem
solve first master problem
create master problem variable time series
solve rolling spine opt model
save zipped marginal values
while master problem not converged
update master problem
solve master problem
update master problem variable timeseries for benders iteration b
rewind sub problem
update sub problem
solve rolling spine opt model
save zipped marginal values
test for convergence
end
Benders cuts
The benders cuts for the problem including all investments in candidate connections, storages and units is given below.
\[\begin{aligned} & v^{sp\_objective} \\ & \geq \\ & p^{sp\_obj}_{(b)} + \\ & \sum_{u,s,t} p^{units\_invested\_available\_mv}_{(b,u,s,t)} \cdot \left( v^{units\_invested\_available}_{(u,s,t)} - p^{units\_invested\_available}_{(u,s,t)} \right) \\ & + \sum_{c,s,t} p^{connections\_invested\_available\_mv}_{(b,c,s,t)} \cdot \left( v^{connections\_invested\_available}_{(c,s,t)} - p^{connections\_invested\_available}_{(c,s,t)} \right) \\ & + \sum_{n,s,t} p^{storages\_invested\_available\_mv}_{(b,n,s,t)} \cdot \left( v^{storages\_invested\_available}_{(n,s,t)} - p^{storages\_invested\_available}_{(n,s,t)} \right) \\ \end{aligned}\]
where
- $p^{sp\_obj}_{(b)}$ is the sub problem objective function value in benders iteration $b$,
- $p^{units\_invested\_available\_mv}$ is the reduced cost of the units_invested_available fixed sub-problem variable, representing the reduction in operating costs possible from an investment in a unit of this type,
- $p^{connections\_invested\_available\_mv}$ is the reduced cost of the connections_invested_available fixed sub-problem variable, representing the reduction in operating costs possible from an investment in a connection of this type,
- $p^{storages\_invested\_available\_mv}$ is the reduced cost of the storages_invested_available fixed sub-problem variable, representing the reduction in operating costs possible from an investment in a storage node of this type,
- $p^{units\_invested\_available}$ is the value of the fixed sub problem variable units_invested_available in benders iteration $b$,
- $p^{connections\_invested\_available}$ is the value of the fixed sub problem variable connections_invested_available in benders iteration $b$ and
- $p^{storages\_invested\_available}$ is the value of the fixed sub problem variable storages_invested_available in benders iteration $b$