Unit Commitment¶
Unit commitment constraints are implemented for the Generator and Link
components. They are used to model the start-up and shut-down constraints, as
well as ramping constraints. The implementation is based on Taylor (2015)1,
and is supplemented with work by Hua et al. (2017)2 for a tightened linear
relaxation.
Start-Up and Shut-down¶
For components marked as committable=True, new binary status variables
\(u_{*,t} \in \{0,1\}\) are introduced, which indicate whether the component is
running (1) or not (0) in period \(t\). This turns the model into a mixed-integer
linear programme (MILP). The restrictions on the dispatch now enforce that the
dispatch \(g_{n,s,t}\) or \(f_{l,t}\) is only non-zero if the component is running,
i.e. \(u_{*,t} = 1\):
| Constraint | Name |
|---|---|
| \(g_{n,s,t} \geq u_{n,s,t} \cdot \underline{g}_{n,s,t} \cdot \hat{g}_{n,s}\) | Generator-com-p-lower |
| \(g_{n,s,t} \leq u_{n,s,t} \cdot \bar{g}_{n,s,t} \cdot \hat{g}_{n,s}\) | Generator-com-p-upper |
| Constraint | Name |
|---|---|
| \(f_{l,t} \geq u_{l,t} \cdot \underline{f}_{l,t} \cdot \hat{f}_{l}\) | Link-com-p-lower |
| \(f_{l,t} \leq u_{l,t} \cdot \bar{f}_{l,t} \cdot \hat{f}_{l}\) | Link-com-p-upper |
Warning
Note that components cannot be both extendable (p_nom_extendable=True) and committable (committable=True) because of the non-linear coupling of status and capacity.
If the minimum up time \(T_{\textrm{min_up}}\) is set, status switches are constrained to ensure that the component is running for at least \(T_{\textrm{min_up}}\) snapshots after it has been started up:
Constraint Generator-com-up-time:
Constraint Link-com-up-time:
The component may have been up for some periods before the optimisation period (n.optimize(snapshots=snapshots)). If the up-time before snapshots starts is less than the minimum up-time, the component is forced remain up for the difference at the start of snapshots. If the start of snapshots is the start of n.snapshots, the up-time before the simulation is read from the input attribute up_time_before. If snapshots falls in the middle of n.snapshots, then the statuses before snapshots are assumed to be set by previous runs. If the start of snapshots is very close to the start of n.snapshots, it will also take account of up_time_before as well as the statuses in between.
At the end of snapshots the minimum up-time in the constraint is only enforced for the remaining snapshots, if the number of remaining snapshots is less than \(T_{\textrm{min_up}}\).
If the minimum down time \(T_{\textrm{min_down}}\) is set, status switches are constrained to ensure that the component is not running for at least \(T_{\textrm{min_down}}\) snapshots after it has been shut down:
Constraint Generator-com-down-time:
Constraint Link-com-down-time:
The component may have been down for some periods before the optimisation period (n.optimize(snapshots=snapshots)). If the down-time before snapshots starts is less than the minimum down-time, the component is forced to remain down for the difference at the start of snapshots. If the start of snapshots is the start of n.snapshots, the down-time before the simulation is read from the input attribute down_time_before. If snapshots falls in the middle of n.snapshots, then the statuses before snapshots are assumed to be set by previous runs. If the start of snapshots is very close to the start of n.snapshots, it will also take account of down_time_before as well as the statuses in between.
Furthermore, two state transition variables for start-up (\(su_{*,t} \in \{0,1\}\)) and shut-down (\(sd_{*,t} \in\) {0,1}) are introduced to associate them with start-up and shut-down cost terms in the objective function. The constraints are set so that the start-up variable is only non-zero if the component has just started up, i.e. \(u_{n,s,t} - u_{n,s,t-1} = 1\), and the shut-down variable is only non-zero if the component has just shut down, i.e. \(u_{n,s,t-1} - u_{n,s,t} = 1\):
| Constraint | Name |
|---|---|
| \(su_{n,s,t} \geq u_{n,s,t} - u_{n,s,t-1}\) | Generator-com-transition-start-up |
| \(sd_{n,s,t} \geq u_{n,s,t-1} - u_{n,s,t}\) | Generator-com-transition-shut-down |
| Constraint | Name |
|---|---|
| \(su_{l,t} \geq u_{l,t} - u_{l,t-1}\) | Link-com-transition-start-up |
| \(sd_{l,t} \geq u_{l,t-1} - u_{l,t}\) | Link-com-transition-shut-down |
These constraints are defined in the function define_operational_constraints_for_committables().
Mapping of symbols to component attributes
| Symbol | Attribute | Type |
|---|---|---|
| \(g_{n,s,t}\) | n.generators_t.p |
Decision variable |
| \(u_{n,s,t}\) | n.generators_t.status |
Decision variable |
| \(su_{n,s,t}\) | n.generators_t.start_up |
Decision variable |
| \(sd_{n,s,t}\) | n.generators_t.shut_down |
Decision variable |
| \(\hat{g}_{n,s}\) | n.generators.p_nom |
Parameter |
| \(\underline{g}_{n,s,t}\) | n.generators_t.p_min_pu |
Parameter |
| \(\bar{g}_{n,s,t}\) | n.generators_t.p_max_pu |
Parameter |
| \(T_{\textrm{min_up}}\) | n.generators.min_up_time |
Parameter |
| \(T_{\textrm{min_down}}\) | n.generators.min_down_time |
Parameter |
| \(T_{\textrm{up_time_before}}\) | n.generators.up_time_before |
Parameter |
| \(T_{\textrm{down_time_before}}\) | n.generators.down_time_before |
Parameter |
| Symbol | Attribute | Type |
|---|---|---|
| \(f_{l,t}\) | n.links_t.p |
Decision variable |
| \(u_{l,t}\) | n.links_t.status |
Decision variable |
| \(su_{l,t}\) | n.links_t.start_up |
Decision variable |
| \(sd_{l,t}\) | n.links_t.shut_down |
Decision variable |
| \(\hat{f}_{l}\) | n.links.p_nom |
Parameter |
| \(\underline{f}_{l,t}\) | n.links_t.p_min_pu |
Parameter |
| \(\bar{f}_{l,t}\) | n.links_t.p_max_pu |
Parameter |
| \(T_{\textrm{min_up}}\) | n.links.min_up_time |
Parameter |
| \(T_{\textrm{min_down}}\) | n.links.min_down_time |
Parameter |
| \(T_{\textrm{up_time_before}}\) | n.links.up_time_before |
Parameter |
| \(T_{\textrm{down_time_before}}\) | n.links.down_time_before |
Parameter |
Ramping¶
Ramp rate limits can be defined for increasing output \(ru_{n,s,t}\) and decreasing output \(rd_{n,s,t}\). By default these are null and ignored. They must be provided per unit of the nominal capacity.
Note
When provided, ramping limits are also considered if they are not
committable (committable=False), i.e. the component is not subject to start-up and shut-down constraints.
For non-extendable but non-committable components, the dispatch obeys for \(t \in \{1,\dots |T|-1\}\):
| Constraint | Dual Variable | Name |
|---|---|---|
| \((g_{n,s,t} - g_{n,s,t-1}) \geq -rd_{n,s,t} \cdot \hat{g}_{n,s}\) | n.generators_t.mu_ramp_limit_down |
Generator-fix-p-ramp_limit_down |
| \((g_{n,s,t} - g_{n,s,t-1}) \leq ru_{n,s,t} \cdot \hat{g}_{n,s}\) | n.generators_t.mu_ramp_limit_up |
Generator-fix-p-ramp_limit_up |
| Constraint | Dual Variable | Name |
|---|---|---|
| \((f_{l,t} - f_{l,t-1}) \geq -rd_{l,t} \cdot \hat{f}_{l}\) | n.links_t.mu_ramp_limit_down |
Link-fix-p-ramp_limit_down |
| \((f_{l,t} - f_{l,t-1}) \leq ru_{l,t} \cdot \hat{f}_{l}\) | n.links_t.mu_ramp_limit_up |
Link-fix-p-ramp_limit_up |
For extendable and non-committable components, the dispatch obeys for \(t \in \{1,\dots |T|-1\}\):
| Constraint | Dual Variable | Name |
|---|---|---|
| \((g_{n,s,t} - g_{n,s,t-1}) \geq -rd_{n,s,t} \cdot G_{n,s}\) | n.generators_t.mu_ramp_limit_down |
Generator-ext-p-ramp_limit_down |
| \((g_{n,s,t} - g_{n,s,t-1}) \leq ru_{n,s,t} \cdot G_{n,s}\) | n.generators_t.mu_ramp_limit_up |
Generator-ext-p-ramp_limit_up |
| Constraint | Dual Variable | Name |
|---|---|---|
| \((f_{l,t} - f_{l,t-1}) \geq -rd_{l,t} \cdot F_{l}\) | n.links_t.mu_ramp_limit_down |
Link-ext-p-ramp_limit_down |
| \((f_{l,t} - f_{l,t-1}) \leq ru_{l,t} \cdot F_{l}\) | n.links_t.mu_ramp_limit_up |
Link-ext-p-ramp_limit_up |
For committable and non-extendable components, additional ramp limits at start-up \(rusu_{n,s}\) and shut-down \(rdsd_{n,s}\) can be specified for \(t \in \{1,\dots |T|-1\}\):
| Constraint | Name |
|---|---|
| \((g_{n,s,t} - g_{n,s,t-1}) \geq \left[ -rd_{n,s,t} \cdot u_{n,s,t} -rdsd_{n,s}(u_{n,s,t-1} - u_{n,s,t})\right] \hat{g}_{n,s}\) | Generator-com-p-ramp_limit_down |
| \((g_{n,s,t} - g_{n,s,t-1}) \leq \left[ru_{n,s,t} \cdot u_{n,s,t-1} + rusu_{n,s} (u_{n,s,t} - u_{n,s,t-1})\right] \hat{g}_{n,s}\) | Generator-com-p-ramp_limit_up |
| Constraint | Name |
|---|---|
| \((f_{l,t} - f_{l,t-1}) \geq \left[ -rd_{l,t} \cdot u_{l,t} -rdsd_{l,t}(u_{l,t-1} - u_{l,t})\right] \hat{f}_{l}\) | Link-com-p-ramp_limit_down |
| \((f_{l,t} - f_{l,t-1}) \leq \left[ru_{l,t} \cdot u_{l,t-1} + rusu_{l} (u_{l,t} - u_{l,t-1})\right] \hat{f}_{l}\) | Link-com-p-ramp_limit_up |
These constraints are defined in the function define_ramp_limit_constraints().
Mapping of symbols to component attributes
| Symbol | Attribute | Type |
|---|---|---|
| \(g_{n,s,t}\) | n.generators_t.p |
Decision variable |
| \(G_{n,s}\) | n.generators.p_nom_opt |
Decision variable |
| \(u_{n,s,t}\) | n.generators_t.status |
Decision variable |
| \(\hat{g}_{n,s}\) | n.generators.p_nom |
Parameter |
| \(\underline{g}_{n,s,t}\) | n.generators_t.p_min_pu |
Parameter |
| \(\bar{g}_{n,s,t}\) | n.generators_t.p_max_pu |
Parameter |
| \(ru_{n,s,t}\) | n.generators_t.ramp_limit_up |
Parameter |
| \(rd_{n,s,t}\) | n.generators_t.ramp_limit_down |
Parameter |
| \(rusu_{n,s}\) | n.generators.ramp_limit_start_up |
Parameter |
| \(rdsd_{n,s}\) | n.generators.ramp_limit_shut_down |
Parameter |
| Symbol | Attribute | Type |
|---|---|---|
| \(f_{l,t}\) | n.links_t.p |
Decision variable |
| \(F_{l}\) | n.links.p_nom_opt |
Decision variable |
| \(u_{l,t}\) | n.links_t.status |
Decision variable |
| \(\hat{f}_{l}\) | n.links.p_nom |
Parameter |
| \(\underline{f}_{l,t}\) | n.links_t.p_min_pu |
Parameter |
| \(\bar{f}_{l,t}\) | n.links_t.p_max_pu |
Parameter |
| \(ru_{l,t}\) | n.links_t.ramp_limit_up |
Parameter |
| \(rd_{l,t}\) | n.links_t.ramp_limit_down |
Parameter |
| \(rusu_{l}\) | n.links.ramp_limit_start_up |
Parameter |
| \(rdsd_{l}\) | n.links.ramp_limit_shut_down |
Parameter |
Linearization¶
The implementation is based on Hua et al. (2017)2 and relaxes the binary unit commitment variables to continuous variables:
This allows for partial commitment states (generators can be partially on/off), making the problem more computationally tractable while loosing some accuracy. To enable this, use:
n.optimize(linearized_unit_commitment=True)
To tighten the relaxation, additional constraints are introduced that improve capturing the relationship between commitment status, ramping, and dispatch. This requires start up and shut down costs need to be equal. Otherwise the unit commitment variables are purely relaxed. The added constraints limit the dispatch during partial start-up and shut-down, as well as ramping during partial commitment:
Constraint Generator-com-p-before:
Constraint Generator-com-p-current:
Constraint Generator-com-partly-start-up:
Constraint Generator-com-partly-shut-down:
Constraint Link-com-p-before:
Constraint Link-com-p-current:
Constraint Link-com-partly-start-up:
Constraint Link-com-partly-shut-down:
These constraints are defined in the function define_operational_constraints_for_committables().
Examples¶
-
Unit Commitment
Models generator unit commitment with start-up and shut-down costs, ramping limits, minimum part loads, up and down times using binary variables.
-
Negative Prices in Linearized Unit Commitment
Demonstrates how negative electricity prices emerge in linearized unit commitment when generators face high cycling costs and prefer to stay online at negative prices.
-
J.A. Taylor (2015), Convex Optimization of Power Systems, Cambridge University Press, Chapter 4.3. ↩
-
B. Hua, R. Baldick and J. Wang (2018), Representing Operational Flexibility in Generation Expansion Planning Through Convex Relaxation of Unit Commitment, IEEE Transactions on Power Systems, 33, 2, 2272-2281, doi:10.1109/TPWRS.2017.2735026, equations (21-24). ↩↩