Skip to content

Commit e328fae

Browse files
Port the extra contribution that ChargeStorage can make to spinning reserves from the spinning_reserves_advanced module to the spinning_reserves module.
Add a slack variable to the CommitGenSpinningReservesUp_Limit constraint in both spinning reserves modules to provide a hook for subsequent quickstart reserves. Minor documentation updates to both spinning reserve modules.
1 parent 56eaad9 commit e328fae

File tree

2 files changed

+50
-17
lines changed

2 files changed

+50
-17
lines changed

switch_model/balancing/operating_reserves/spinning_reserves.py

Lines changed: 40 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -365,14 +365,25 @@ def define_components(m):
365365
corresponding variable for downward spinning reserves.
366366
367367
CommitGenSpinningReservesUp_Limit[(g,t) in SPINNING_RESERVE_GEN_TPS] and
368-
CommitGenSpinningReservesDown_Limit constraint the CommitGenSpinningReserves
369-
variables based on DispatchSlackUp and DispatchSlackDown.
368+
CommitGenSpinningReservesDown_Limit constrain the
369+
CommitGenSpinningReserves variables based on DispatchSlackUp and
370+
DispatchSlackDown (and ChargeStorage, as applicable).
370371
371372
CommittedSpinningReserveUp[(b,t) in BALANCING_AREA_TIMEPOINTS] and
372373
CommittedSpinningReserveDown are expressions summarizing the
373374
CommitGenSpinningReserves variables for generators within each balancing
374375
area.
375376
377+
CommitGenSpinningReservesUp and CommitGenSpinningReservesDown are
378+
variables instead of aliases to DispatchSlackUp & DispatchSlackDown
379+
because they may need to take on lower values to reduce the
380+
project-level contigencies, especially when discrete unit commitment is
381+
enabled, and committed capacity may exceed the amount of capacity that
382+
is strictly needed. Having these as variables also flags them for
383+
automatic export in model dumps and tab files, and opens up the
384+
possibility of further customizations like adding variable costs for
385+
spinning reserve provision.
386+
376387
Depending on the configuration parameters unit_contingency,
377388
project_contingency and spinning_requirement_rule, other components may be
378389
added by other functions which are documented above.
@@ -389,15 +400,6 @@ def define_components(m):
389400
dimen=2,
390401
initialize=m.GEN_TPS,
391402
filter=lambda m, g, t: m.gen_can_provide_spinning_reserves[g])
392-
# CommitGenSpinningReservesUp and CommitGenSpinningReservesDown are
393-
# variables instead of aliases to DispatchSlackUp & DispatchSlackDown
394-
# because they may need to take on lower values to reduce the
395-
# project-level contigencies, especially when discrete unit commitment is
396-
# enabled, and committed capacity may exceed the amount of capacity that
397-
# is strictly needed. Having these as variables also flags them for
398-
# automatic export in model dumps and tab files, and opens up the
399-
# possibility of further customizations like adding variable costs for
400-
# spinning reserve provision.
401403
m.CommitGenSpinningReservesUp = Var(
402404
m.SPINNING_RESERVE_GEN_TPS,
403405
within=NonNegativeReals
@@ -406,15 +408,38 @@ def define_components(m):
406408
m.SPINNING_RESERVE_GEN_TPS,
407409
within=NonNegativeReals
408410
)
411+
m.CommitGenSpinningReservesSlackUp = Var(
412+
m.SPINNING_RESERVE_GEN_TPS,
413+
within=NonNegativeReals,
414+
doc="Denotes the upward slack in spinning reserves that could be used "
415+
"for quickstart reserves, or possibly other reserve products."
416+
)
409417
m.CommitGenSpinningReservesUp_Limit = Constraint(
410418
m.SPINNING_RESERVE_GEN_TPS,
411-
rule=lambda m, g, t: \
412-
m.CommitGenSpinningReservesUp[g,t] <= m.DispatchSlackUp[g, t]
419+
rule=lambda m, g, t: (
420+
m.CommitGenSpinningReservesUp[g,t] +
421+
m.CommitGenSpinningReservesSlackUp[g,t]
422+
== m.DispatchSlackUp[g, t] +
423+
# storage can give more up response by stopping charging
424+
(m.ChargeStorage[g, t]
425+
if g in getattr(m, 'STORAGE_GENS', [])
426+
else 0.0
427+
)
428+
)
413429
)
414430
m.CommitGenSpinningReservesDown_Limit = Constraint(
415431
m.SPINNING_RESERVE_GEN_TPS,
416432
rule=lambda m, g, t: \
417-
m.CommitGenSpinningReservesDown[g,t] <= m.DispatchSlackDown[g, t]
433+
m.CommitGenSpinningReservesDown[g,t] <= m.DispatchSlackDown[g, t] +
434+
# storage could give more down response by raising ChargeStorage
435+
# to the maximum rate
436+
(
437+
(m.DispatchUpperLimit[g, t] * m.gen_store_to_release_ratio[g]
438+
- m.ChargeStorage[g, t]
439+
)
440+
if g in getattr(m, 'STORAGE_GENS', [])
441+
else 0.0
442+
)
418443
)
419444

420445
# Sum of spinning reserve capacity per balancing area and timepoint..
@@ -438,7 +463,7 @@ def define_components(m):
438463
)
439464
)
440465
m.Spinning_Reserve_Down_Provisions.append('CommittedSpinningReserveDown')
441-
466+
442467
if m.options.unit_contingency:
443468
gen_unit_contingency(m)
444469
if m.options.project_contingency:

switch_model/balancing/operating_reserves/spinning_reserves_advanced.py

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33
"""
44
This is an advanced version of the basic spinning_reserves reserves module, and
55
can be used in place of it (not in addition to).
6+
7+
Specifically, this module can differentiate spinning reserve products into regulating reserves, contigency reserves, and potentially other reserve types.
68
"""
79
import os
810
from collections import defaultdict
@@ -430,16 +432,22 @@ def rule(m):
430432
# during each timepoint
431433
m.CommitGenSpinningReservesUp = Var(m.SPINNING_RESERVE_TYPE_GEN_TPS, within=NonNegativeReals)
432434
m.CommitGenSpinningReservesDown = Var(m.SPINNING_RESERVE_TYPE_GEN_TPS, within=NonNegativeReals)
435+
m.CommitGenSpinningReservesSlackUp = Var(m.SPINNING_RESERVE_CAPABLE_GEN_TPS, within=NonNegativeReals,
436+
doc="Denotes the upward slack in spinning reserves that could be used "
437+
"for quickstart reserves, or possibly other reserve products."
438+
)
433439

434440
# constrain reserve provision appropriately
435441
m.CommitGenSpinningReservesUp_Limit = Constraint(
436442
m.SPINNING_RESERVE_CAPABLE_GEN_TPS,
437-
rule=lambda m, g, tp:
443+
rule=lambda m, g, tp: (
438444
sum(m.CommitGenSpinningReservesUp[rt, g, tp] for rt in m.SPINNING_RESERVE_TYPES_FOR_GEN[g])
439-
<=
445+
+ m.CommitGenSpinningReservesSlackUp[g,tp]
446+
==
440447
m.DispatchSlackUp[g, tp]
441448
# storage can give more up response by stopping charging
442449
+ (m.ChargeStorage[g, tp] if g in getattr(m, 'STORAGE_GENS', []) else 0.0)
450+
)
443451
)
444452
m.CommitGenSpinningReservesDown_Limit = Constraint(
445453
m.SPINNING_RESERVE_CAPABLE_GEN_TPS,

0 commit comments

Comments
 (0)