Skip to content

Commit d36a35d

Browse files
A module & test case for quickstart reserves, based on the pattern of spinning reserves, but without contingencies. I only implemented a single rule for quickstart reserves here because that satisfies my immediate use case, and I don't know what rules were used in the GE Hawaii RPS study. I structured the quickstart reserve module to allow for easy subsequent extension of rules for quickstart reserve requirements.
1 parent 59ad400 commit d36a35d

23 files changed

+294
-1
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
SYNOPSIS
2+
switch solve --verbose --log-run
3+
4+
This example extends spinning_reserves by adding quickstart reserve requirements.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
param base_financial_year := 2015;
2+
param interest_rate := .07;
3+
param discount_rate := .05;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
load_zone fuel period fuel_cost
2+
South NaturalGas 2010 4
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
fuel co2_intensity upstream_co2_intensity
2+
NaturalGas 0.05306 0
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
GENERATION_PROJECT build_year gen_overnight_cost gen_fixed_om
2+
S-Geothermal 1998 5524200 0
3+
S-NG_CC 2000 1143900 5868.3
4+
S-NG_GT 1990 605430 4891.8
5+
S-NG_GT 2002 605430 4891.8
6+
S-Central_PV-1 2001 2334300 41850
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
GENERATION_PROJECT build_year gen_predetermined_cap
2+
S-Geothermal 1998 2.0
3+
S-NG_CC 2000 7.0
4+
S-NG_GT 1990 3.0
5+
S-NG_GT 2002 4.0
6+
S-Central_PV-1 2001 3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
GENERATION_PROJECT power_start_mw power_end_mw incremental_heat_rate_mbtu_per_mwhr fuel_use_rate_mmbtu_per_h
2+
S-NG_CC 40 . . 269.4069
3+
S-NG_CC 40 100.0 6.684885 .
4+
S-NG_GT 0 . . 0.1039
5+
S-NG_GT 0 1.0 10.2861 .
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
GENERATION_PROJECT gen_dbid gen_tech gen_load_zone gen_connect_cost_per_mw gen_capacity_limit_mw gen_max_age gen_min_build_capacity gen_scheduled_outage_rate gen_forced_outage_rate gen_is_variable gen_is_baseload gen_is_cogen gen_variable_om gen_energy_source gen_full_load_heat_rate gen_unit_size gen_min_load_fraction gen_startup_fuel gen_startup_om gen_min_downtime gen_can_provide_spinning_reserves
2+
S-Geothermal 33 Geothermal South 134222 3 30 0 0.0075 0.0241 0 1 0 28.83 Geothermal . . . . . . 0
3+
S-NG_CC 34 NG_CC South 57566.6 . 20 0 0.04 0.06 0 0 0 3.4131 NaturalGas 6.705 1 0.4 9.16 10.3 12 1
4+
S-NG_GT 36 NG_GT South 57566.6 . 20 0 0.04 0.06 0 0 0 27.807 NaturalGas 10.39 . 0 0.22 0.86 . 1
5+
S-Central_PV-1 41 Central_PV South 74881.9 4 20 0 0 0.02 1 0 0 0 Solar . . 0 . 0 . 0
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
LOAD_ZONE existing_local_td local_td_annual_cost_per_mw
2+
South 10 128040
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
LOAD_ZONE TIMEPOINT zone_demand_mw
2+
South 1 3
3+
South 2 8
4+
South 3 10
5+
South 4 7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
# Core Modules
2+
switch_model
3+
switch_model.timescales
4+
switch_model.financials
5+
switch_model.balancing.load_zones
6+
switch_model.energy_sources.properties
7+
switch_model.generators.core.build
8+
switch_model.generators.core.dispatch
9+
switch_model.reporting
10+
# Custom Modules
11+
switch_model.transmission.local_td
12+
switch_model.generators.core.commit.operate
13+
switch_model.generators.core.commit.fuel_use
14+
switch_model.energy_sources.fuel_costs.simple
15+
switch_model.balancing.operating_reserves.areas
16+
switch_model.balancing.operating_reserves.spinning_reserves
17+
switch_model.balancing.operating_reserves.quickstart
18+
#switch_model.reporting.dump
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
energy_source
2+
Geothermal
3+
Solar
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
INVESTMENT_PERIOD period_start period_end
2+
2010 2008 2012
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
param contingency_safety_factor := 1;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
2.0.4
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
timepoint_id timestamp timeseries
2+
1 2010011500 2010_all
3+
2 2010011506 2010_all
4+
3 2010011512 2010_all
5+
4 2010011518 2010_all
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
TIMESERIES ts_period ts_duration_of_tp ts_num_tps ts_scale_to_period
2+
2010_all 2010 6 1 1826.25
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
GENERATION_PROJECT timepoint gen_max_capacity_factor
2+
S-Central_PV-1 1 0.0
3+
S-Central_PV-1 2 0.61
4+
S-Central_PV-1 3 1
5+
S-Central_PV-1 4 0.4
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
LOAD_ZONE PERIOD zone_expected_coincident_peak_demand
2+
South 2010 10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
--spinning-requirement-rule 3+5
2+
--unit-contingency
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
36406402.36853148
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,211 @@
1+
# Copyright (c) 2015-2019 The Switch Authors. All rights reserved.
2+
# Licensed under the Apache License, Version 2.0, which is in the LICENSE file.
3+
"""
4+
A simple model of quickstart reserves to accompany the spinning_reserves
5+
modules. Patterns from the spinning reserves modules were followed where
6+
practical to simplify implementation and review.
7+
8+
Unlike spinning reserves, this module does not currently implement
9+
contingency-based requirements because I lack an immediate use case for that.
10+
The contigency reserves methodology from spinning reserve modules could
11+
probably be adapted readily if needed.
12+
13+
For more discussion of operating reserve considerations and modeling
14+
approaches, see the spinning_reserves module.
15+
16+
"""
17+
import os
18+
from pyomo.environ import *
19+
20+
dependencies = (
21+
'switch_model.timescales',
22+
'switch_model.balancing.load_zones',
23+
'switch_model.balancing.operating_reserves.areas',
24+
'switch_model.financials',
25+
'switch_model.energy_sources.properties',
26+
'switch_model.generators.core.build',
27+
'switch_model.generators.core.dispatch',
28+
'switch_model.generators.core.commit.operate',
29+
)
30+
optional_prerequisites = (
31+
'switch_model.balancing.operating_reserves.spinning_reserves',
32+
'switch_model.balancing.operating_reserves.spinning_reserves_advanced',
33+
)
34+
35+
# Uncomment this section when more than one rule is implemented.
36+
# def define_arguments(argparser):
37+
# group = argparser.add_argument_group(__name__)
38+
# group.add_argument('--quickstart-requirement-rule', default="3+5",
39+
# dest='quickstart_requirement_rule',
40+
# choices = ["3+5"],
41+
# help=("Choose rules for quickstart reserves requirements as a function "
42+
# "of variable renewable power and load. '3+5' requires 3%% of "
43+
# "load and 5%% of variable renewable output, based on the "
44+
# "heuristic described in the 2010 Western Wind and Solar "
45+
# "Integration Study.")
46+
# )
47+
48+
49+
def define_dynamic_lists(mod):
50+
"""
51+
Quickstart_Reserve_Requirements is a list of model components that
52+
contribute to quickstart reserve requirements in each balancing area and
53+
timepoint.
54+
55+
Quickstart_Reserve_Provisions is a list of model components that help
56+
satisfy spinning reserve requirements in each balancing area and
57+
timepoint.
58+
59+
Each component in both lists needs to use units of MW and be indexed by:
60+
(b, t) in BALANCING_AREA_TIMEPOINTS.
61+
"""
62+
mod.Quickstart_Reserve_Requirements = []
63+
mod.Quickstart_Reserve_Provisions = []
64+
65+
66+
def nrel_3_5_quickstart_reserve_requirements(mod):
67+
"""
68+
NREL35QuickstartRequirement[(b,t) in BALANCING_AREA_TIMEPOINTS] is
69+
an expression for quickstart reserve requirements of 3% of load plus 5% of
70+
renewable output, based on a heuristic described in NREL's 2010 Western
71+
Wind and Solar Integration study. It is added to the
72+
Quickstart_Reserve_Requirements list. If the local_td module is available
73+
with DER accounting, load will be set to WithdrawFromCentralGrid.
74+
Otherwise load will be set to lz_demand_mw.
75+
"""
76+
def NREL35QuickstartRequirement_rule(m, b, t):
77+
try:
78+
load = m.WithdrawFromCentralGrid
79+
except AttributeError:
80+
load = m.lz_demand_mw
81+
return (0.03 * sum(load[z, t] for z in m.LOAD_ZONES
82+
if b == m.zone_balancing_area[z])
83+
+ 0.05 * sum(m.DispatchGen[g, t] for g in m.VARIABLE_GENS
84+
if (g, t) in m.VARIABLE_GEN_TPS and
85+
b == m.zone_balancing_area[m.gen_load_zone[g]]))
86+
mod.NREL35QuickstartRequirement = Expression(
87+
mod.BALANCING_AREA_TIMEPOINTS,
88+
rule=NREL35QuickstartRequirement_rule
89+
)
90+
mod.Quickstart_Reserve_Requirements.append('NREL35QuickstartRequirement')
91+
92+
93+
def define_components(mod):
94+
"""
95+
gen_can_provide_quickstart_reserves[g] is a binary flag indicating whether
96+
a generation project can provide quickstart reserves. Default to False for
97+
baseload & variable generators, otherwise defaults to True.
98+
99+
QUICKSTART_RESERVE_GEN_TPS is a subset of GEN_TPS for generators that
100+
have gen_can_provide_quickstart_reserves set to True.
101+
102+
CommitQuickstartReserves[(g,t) in QUICKSTART_RESERVE_GEN_TPS] is a
103+
decision variable of how much quickstart reserve capacity to commit
104+
(in MW).
105+
106+
CommitQuickstartReserves_Limit[(g,t) in SPINNING_RESERVE_GEN_TPS]
107+
constrain the CommitGenSpinningReserves variables based on CommitSlackUp
108+
(and CommitGenSpinningReservesSlackUp as applicable).
109+
110+
For example, if discrete unit commitment is enabled, and a 5MW hydro
111+
generator is fully committed but only providing 2MW of power and 1 MW of
112+
spinning reserves, the remaining 2MW of capacity (summarized in
113+
CommitGenSpinningReservesSlackUp) could be committed to quickstart
114+
reserves.
115+
116+
CommittedQuickstartReserves[(b,t) in BALANCING_AREA_TIMEPOINTS] is an
117+
expression summarizing the CommitQuickstartReserves variables for
118+
generators within each balancing area.
119+
120+
See also: NREL35QuickstartRequirement defined in the function above.
121+
"""
122+
mod.gen_can_provide_quickstart_reserves = Param(
123+
mod.GENERATION_PROJECTS,
124+
within=Boolean,
125+
default=lambda m, g: not (m.gen_is_baseload[g] or m.gen_is_variable[g]),
126+
doc="Denotes whether a generation project can provide quickstart "
127+
"reserves. Default to false for baseload & variable generators, "
128+
"otherwise true. Can be explicitly specified in an input file."
129+
)
130+
mod.QUICKSTART_RESERVE_GEN_TPS = Set(
131+
dimen=2,
132+
within=mod.GEN_TPS,
133+
initialize=lambda m: set(
134+
(g,t)
135+
for g in m.GENERATION_PROJECTS
136+
if m.gen_can_provide_quickstart_reserves[g]
137+
for t in m.TPS_FOR_GEN[g]
138+
)
139+
)
140+
mod.CommitQuickstartReserves = Var(
141+
mod.QUICKSTART_RESERVE_GEN_TPS,
142+
within=NonNegativeReals
143+
)
144+
def CommitQuickstartReserves_Limit_rule(m, g, t):
145+
limit = m.CommitSlackUp[g,t]
146+
try:
147+
limit += m.CommitGenSpinningReservesSlackUp[g,t]
148+
except (AttributeError, KeyError):
149+
pass
150+
return (m.CommitQuickstartReserves[g,t] <= limit)
151+
mod.CommitQuickstartReserves_Limit = Constraint(
152+
mod.QUICKSTART_RESERVE_GEN_TPS,
153+
doc="Constrain committed quickstart reserves to uncommited capacity "
154+
"plus any dispatch slack that is not committed to spinning "
155+
"reserves (if applicable).",
156+
rule=CommitQuickstartReserves_Limit_rule
157+
)
158+
159+
mod.CommittedQuickstartReserves = Expression(
160+
mod.BALANCING_AREA_TIMEPOINTS,
161+
doc="Sum of committed quickstart reserve capacity per balancing "
162+
"area and timepoint.",
163+
rule=lambda m, b, t: (
164+
sum(m.CommitQuickstartReserves[g, t]
165+
for z in m.ZONES_IN_BALANCING_AREA[b]
166+
for g in m.GENS_IN_ZONE[z]
167+
if (g,t) in m.QUICKSTART_RESERVE_GEN_TPS
168+
)
169+
)
170+
)
171+
mod.Quickstart_Reserve_Provisions.append('CommittedQuickstartReserves')
172+
173+
# These rules are in a separate function in anticipation of additional
174+
# rulesets eventually being defined and selectable via command line
175+
# arguments.
176+
nrel_3_5_quickstart_reserve_requirements(mod)
177+
178+
179+
def define_dynamic_components(mod):
180+
"""
181+
Satisfy_Quickstart_Requirement[(b,t) in BALANCING_AREA_TIMEPOINTS]
182+
is a constraint that ensures quickstart reserve requirements are
183+
being satisfied based on the sum of the dynamic lists
184+
Quickstart_Reserve_Requirements & Quickstart_Reserve_Provisions.
185+
"""
186+
mod.Satisfy_Quickstart_Requirement = Constraint(
187+
mod.BALANCING_AREA_TIMEPOINTS,
188+
rule=lambda m, b, t: (
189+
sum(getattr(m, provision)[b,t]
190+
for provision in m.Quickstart_Reserve_Provisions
191+
) >=
192+
sum(getattr(m, requirement)[b,t]
193+
for requirement in m.Quickstart_Reserve_Requirements
194+
)
195+
)
196+
)
197+
198+
199+
def load_inputs(mod, switch_data, inputs_dir):
200+
"""
201+
All files & columns are optional. See notes above for default values.
202+
203+
generation_projects_info.tab
204+
GENERATION_PROJECTS, ... gen_can_provide_quickstart_reserves
205+
"""
206+
switch_data.load_aug(
207+
filename=os.path.join(inputs_dir, 'generation_projects_info.tab'),
208+
auto_select=True,
209+
optional_params=['gen_can_provide_quickstart_reserves'],
210+
param=(mod.gen_can_provide_quickstart_reserves)
211+
)

switch_model/balancing/operating_reserves/spinning_reserves.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
reserves, and contingency reserves without distinguishing their timescales or
1616
required response duration. Operating reserves at timescales with slower
1717
responses for load following or longer-term recovery from contingencies are not
18-
included here.
18+
included here, but can be accounted for with the quickstart module.
1919
2020
Most regions and countries use distinct terminology for reserves products and
2121
distinct procedures for determining reserve requirements. This module provides

0 commit comments

Comments
 (0)