Skip to content

Commit ace561f

Browse files
Add optional early retirement for predetermined project (either existing or planned). The retirement decisions are linear by default, and can be binary via a CLI param.
1 parent b5b1a28 commit ace561f

File tree

1 file changed

+86
-10
lines changed

1 file changed

+86
-10
lines changed

switch_model/generators/core/build.py

+86-10
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,13 @@
1313
dependencies = 'switch_model.timescales', 'switch_model.balancing.load_zones',\
1414
'switch_model.financials', 'switch_model.energy_sources.properties.properties'
1515

16+
def define_arguments(argparser):
17+
argparser.add_argument(
18+
'--enforce-binary-retirement', dest='enforce_binary_retirement',
19+
default=False, action='store_true',
20+
help="Whether to enforce all-or-nothing retirement, introducing one "
21+
"integer variable per retirement decision.")
22+
1623
def define_components(mod):
1724
"""
1825
@@ -100,6 +107,27 @@ def define_components(mod):
100107
capacity online in a given period. This is the sum of installed capacity
101108
minus all retirements.
102109
110+
gen_can_retire_early[g, build_year] is a binary parameter that describes
111+
whether existing capacity (or other pre-determined capacity) can be
112+
retired early. It is optional and defaults to False.
113+
114+
RetireGen[g, build_year, period] is a decision variable with units of MW
115+
that describes whether to retire existing capacity at the start a period
116+
where it otherwise would have been operational. Once a RetireGen decision
117+
is made, it will continue for all future periods via the
118+
Retirement_Permanence constraint. This will eliminate fixed O&M costs, and
119+
have no impact on capital repayment costs. Note, this can be activated for
120+
any predetermined capacity, not just existing capacity.
121+
122+
RetireGenAllOrNothing[g, build_year, period] is a binary decision variable
123+
that constrains RetireGen to all-or-nothing decisions. It will be skipped
124+
unless --enforce-binary-retirement is specified as a command line argument.
125+
This is linked to RetireGen via the Enforce_Binary_Retirement constraint.
126+
127+
GEN_EARLY_RETIREMENTS is a set of (g, build_year, period) for which
128+
RetireGen decisions can be made, and is defined based on the values
129+
of gen_can_retire_early.
130+
103131
Max_Build_Potential[g] is a constraint defined for each project
104132
that enforces maximum capacity limits for resource-limited projects.
105133
@@ -179,10 +207,6 @@ def define_components(mod):
179207
Proj_Fixed_Costs_Annual[g, period] for all projects that could be
180208
online in the target period. This aggregation is performed for the
181209
benefit of the objective function.
182-
183-
TODO:
184-
- Allow early capacity retirements with savings on fixed O&M
185-
186210
"""
187211
mod.GENERATION_PROJECTS = Set()
188212
mod.gen_dbid = Param(mod.GENERATION_PROJECTS, default=lambda m, g: g)
@@ -402,11 +426,57 @@ def BuildGen_assign_default_value(m, g, bld_yr):
402426
initialize=lambda m:
403427
[(g, p) for g in m.GENERATION_PROJECTS for p in m.PERIODS_FOR_GEN[g]])
404428

429+
mod.gen_can_retire_early = Param(
430+
mod.GEN_BLD_YRS,
431+
within=Binary,
432+
default=False)
433+
mod.GEN_EARLY_RETIREMENTS = Set(
434+
dimen=3,
435+
initialize = lambda m: set([
436+
(g, bld_yr, p)
437+
for (g, bld_yr) in m.GEN_BLD_YRS
438+
if m.gen_can_retire_early[g, bld_yr]
439+
for p in m.PERIODS_FOR_GEN_BLD_YR[g, bld_yr]
440+
]),
441+
doc="A sparse index set for RetireGen decisions")
442+
mod.RetireGen = Var(
443+
mod.GEN_EARLY_RETIREMENTS,
444+
within=NonNegativeReals,
445+
bounds=lambda m, g, bld_yr, p: (0, m.gen_predetermined_cap[g, bld_yr]))
446+
mod.Retirement_Permanence = Constraint(
447+
mod.GEN_EARLY_RETIREMENTS,
448+
rule=lambda m, g, bld_yr, p: (
449+
m.RetireGen[g, bld_yr, p] >= m.RetireGen[g, bld_yr, m.PERIODS.prev(p)]
450+
if p != m.PERIODS.first() and (
451+
(g, bld_yr, m.PERIODS.prev(p)) in m.GEN_EARLY_RETIREMENTS)
452+
else Constraint.Skip))
453+
if mod.options.enforce_binary_retirement:
454+
mod.RetireGenAllOrNothing = Var(
455+
mod.GEN_EARLY_RETIREMENTS,
456+
within=Binary)
457+
mod.Enforce_Binary_Retirement = Constraint(
458+
mod.GEN_EARLY_RETIREMENTS,
459+
rule=lambda m, g, bld_yr, p: (
460+
m.RetireGen[g, bld_yr, p] == m.RetireGenAllOrNothing[g, bld_yr, p] *
461+
m.gen_predetermined_cap[g, bld_yr]))
462+
463+
405464
mod.GenCapacity = Expression(
406465
mod.GENERATION_PROJECTS, mod.PERIODS,
407-
rule=lambda m, g, period: sum(
408-
m.BuildGen[g, bld_yr]
409-
for bld_yr in m.BLD_YRS_FOR_GEN_PERIOD[g, period]))
466+
rule=lambda m, g, p: (
467+
sum(m.BuildGen[g, bld_yr]
468+
for bld_yr in m.BLD_YRS_FOR_GEN_PERIOD[g, p]
469+
) -
470+
sum(m.RetireGen[g, bld_yr, p]
471+
for bld_yr in m.BLD_YRS_FOR_GEN_PERIOD[g,p]
472+
if m.gen_can_retire_early[g, bld_yr]
473+
)))
474+
mod.RETIRE_YRS_FOR_GEN_PERIOD = Set(
475+
mod.GENERATION_PROJECTS, mod.PERIODS,
476+
initialize=lambda m, g, p: [
477+
bld_yr for bld_yr in m.BLD_YRS_FOR_GEN_PERIOD[g,p]
478+
if m.gen_can_retire_early[g, bld_yr]
479+
])
410480

411481
mod.Max_Build_Potential = Constraint(
412482
mod.CAPACITY_LIMITED_GENS, mod.PERIODS,
@@ -472,8 +542,13 @@ def BuildGen_assign_default_value(m, g, bld_yr):
472542
mod.GenFixedOMCosts = Expression(
473543
mod.GENERATION_PROJECTS, mod.PERIODS,
474544
rule=lambda m, g, p: sum(
475-
m.BuildGen[g, bld_yr] * m.gen_fixed_om[g, bld_yr]
476-
for bld_yr in m.BLD_YRS_FOR_GEN_PERIOD[g, p]))
545+
m.gen_fixed_om[g, bld_yr] * (
546+
m.BuildGen[g, bld_yr] - (
547+
m.RetireGen[g, bld_yr, p]
548+
if (g, bld_yr, p) in m.GEN_EARLY_RETIREMENTS
549+
else 0
550+
)
551+
) for bld_yr in m.BLD_YRS_FOR_GEN_PERIOD[g, p]))
477552
# Summarize costs for the objective function. Units should be total
478553
# annual future costs in $base_year real dollars. The objective
479554
# function will convert these to base_year Net Present Value in
@@ -552,8 +627,9 @@ def load_inputs(mod, switch_data, inputs_dir):
552627
optional=True,
553628
filename=os.path.join(inputs_dir, 'gen_build_predetermined.csv'),
554629
auto_select=True,
630+
optional_params=['gen_can_retire_early'],
555631
index=mod.PREDETERMINED_GEN_BLD_YRS,
556-
param=(mod.gen_predetermined_cap))
632+
param=(mod.gen_predetermined_cap, mod.gen_can_retire_early))
557633
switch_data.load_aug(
558634
filename=os.path.join(inputs_dir, 'gen_build_costs.csv'),
559635
auto_select=True,

0 commit comments

Comments
 (0)