|
13 | 13 | dependencies = 'switch_model.timescales', 'switch_model.balancing.load_zones',\
|
14 | 14 | 'switch_model.financials', 'switch_model.energy_sources.properties.properties'
|
15 | 15 |
|
| 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 | + |
16 | 23 | def define_components(mod):
|
17 | 24 | """
|
18 | 25 |
|
@@ -100,6 +107,27 @@ def define_components(mod):
|
100 | 107 | capacity online in a given period. This is the sum of installed capacity
|
101 | 108 | minus all retirements.
|
102 | 109 |
|
| 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 | +
|
103 | 131 | Max_Build_Potential[g] is a constraint defined for each project
|
104 | 132 | that enforces maximum capacity limits for resource-limited projects.
|
105 | 133 |
|
@@ -179,10 +207,6 @@ def define_components(mod):
|
179 | 207 | Proj_Fixed_Costs_Annual[g, period] for all projects that could be
|
180 | 208 | online in the target period. This aggregation is performed for the
|
181 | 209 | benefit of the objective function.
|
182 |
| -
|
183 |
| - TODO: |
184 |
| - - Allow early capacity retirements with savings on fixed O&M |
185 |
| -
|
186 | 210 | """
|
187 | 211 | mod.GENERATION_PROJECTS = Set()
|
188 | 212 | 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):
|
402 | 426 | initialize=lambda m:
|
403 | 427 | [(g, p) for g in m.GENERATION_PROJECTS for p in m.PERIODS_FOR_GEN[g]])
|
404 | 428 |
|
| 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 | + |
405 | 464 | mod.GenCapacity = Expression(
|
406 | 465 | 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 | + ]) |
410 | 480 |
|
411 | 481 | mod.Max_Build_Potential = Constraint(
|
412 | 482 | mod.CAPACITY_LIMITED_GENS, mod.PERIODS,
|
@@ -472,8 +542,13 @@ def BuildGen_assign_default_value(m, g, bld_yr):
|
472 | 542 | mod.GenFixedOMCosts = Expression(
|
473 | 543 | mod.GENERATION_PROJECTS, mod.PERIODS,
|
474 | 544 | 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])) |
477 | 552 | # Summarize costs for the objective function. Units should be total
|
478 | 553 | # annual future costs in $base_year real dollars. The objective
|
479 | 554 | # function will convert these to base_year Net Present Value in
|
@@ -552,8 +627,9 @@ def load_inputs(mod, switch_data, inputs_dir):
|
552 | 627 | optional=True,
|
553 | 628 | filename=os.path.join(inputs_dir, 'gen_build_predetermined.csv'),
|
554 | 629 | auto_select=True,
|
| 630 | + optional_params=['gen_can_retire_early'], |
555 | 631 | index=mod.PREDETERMINED_GEN_BLD_YRS,
|
556 |
| - param=(mod.gen_predetermined_cap)) |
| 632 | + param=(mod.gen_predetermined_cap, mod.gen_can_retire_early)) |
557 | 633 | switch_data.load_aug(
|
558 | 634 | filename=os.path.join(inputs_dir, 'gen_build_costs.csv'),
|
559 | 635 | auto_select=True,
|
|
0 commit comments