• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

sylvan-energy / gridpath / 28978068276

08 Jul 2026 09:47PM UTC coverage: 88.454% (-0.2%) from 88.654%
28978068276

Pull #1389

github

web-flow
Merge 07e0488a7 into 0aadf7c2f
Pull Request #1389: Model compilation efficiency enhancements

267 of 283 new or added lines in 32 files covered. (94.35%)

84 existing lines in 29 files now uncovered.

28690 of 32435 relevant lines covered (88.45%)

0.88 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

85.59
/gridpath/project/operations/operational_types/gen_commit_cap.py
1
# Copyright 2016-2023 Blue Marble Analytics LLC.
2
#
3
# Licensed under the Apache License, Version 2.0 (the "License");
4
# you may not use this file except in compliance with the License.
5
# You may obtain a copy of the License at
6
#
7
#     http://www.apache.org/licenses/LICENSE-2.0
8
#
9
# Unless required by applicable law or agreed to in writing, software
10
# distributed under the License is distributed on an "AS IS" BASIS,
11
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
# See the License for the specific language governing permissions and
13
# limitations under the License.
14

15
"""
16
This module describes the operations of generation projects with 'capacity
17
commitment' operational decisions, i.e. continuous variables to commit some
18
level of capacity below the total capacity of the project. This operational
19
type is particularly well suited for application to 'fleets' of generators
20
with the same characteristics. For example, we could have a GridPath project
21
with a total capacity of 2000 MW, which actually consists of four 500-MW
22
units. The optimization decides how much total capacity to commit (i.e. turn
23
on), e.g. if 2000 MW are committed, then four generators (x 500 MW) are on
24
and if 500 MW are committed, then one generator is on, etc.
25

26
The capacity commitment decision variables are continuous. This approach
27
makes it possible to reduce problem size by grouping similar generators
28
together and linearizing the commitment decisions.
29

30
The optimization makes the capacity-commitment and dispatch decisions in
31
every timepoint. Project power output can vary between a minimum loading level
32
(specified as a fraction of committed capacity) and the committed capacity
33
in each timepoint when the project is available. Heat rate degradation below
34
full load is considered. These projects can be allowed to provide upward
35
and/or downward reserves.
36

37
No standard approach exists for applying ramp rate and minimum up and down
38
time constraints to this operational type. GridPath does include
39
experimental functionality for doing so. Starts and stops -- and the
40
associated cost and emissions -- can also be tracked and constrained for
41
this operational type.
42

43
Costs for this operational type include fuel costs, variable O&M costs, and
44
startup and shutdown costs.
45

46
"""
47

48
import csv
1✔
49
import os.path
1✔
50
import pandas as pd
1✔
51
from pyomo.environ import (
1✔
52
    Var,
53
    Set,
54
    Constraint,
55
    Param,
56
    NonNegativeReals,
57
    NonPositiveReals,
58
    PercentFraction,
59
    Reals,
60
    value,
61
    Expression,
62
)
63

64
from gridpath.auxiliary.auxiliary import (
1✔
65
    subset_init_by_param_value,
66
    subset_init_by_set_membership,
67
)
68
from gridpath.auxiliary.dynamic_components import headroom_variables, footroom_variables
1✔
69
from gridpath.project.operations.reserves.reserve_aggregation import (
1✔
70
    headroom_provision_rule,
71
    footroom_provision_rule,
72
)
73
from gridpath.project.operations.operational_types.common_functions import (
1✔
74
    determine_relevant_timepoints,
75
    load_optype_model_data,
76
    check_for_tmps_to_link,
77
    validate_opchars,
78
)
79
from gridpath.common_functions import create_results_df
1✔
80
from gridpath.project.common_functions import (
1✔
81
    check_if_boundary_type_and_first_timepoint,
82
)
83

84

85
def add_model_components(
1✔
86
    m,
87
    d,
88
    scenario_directory,
89
    weather_iteration,
90
    hydro_iteration,
91
    availability_iteration,
92
    subproblem,
93
    stage,
94
):
95
    """
96
    The following Pyomo model components are defined in this module:
97

98
    +-------------------------------------------------------------------------+
99
    | Sets                                                                    |
100
    +=========================================================================+
101
    | | :code:`GEN_COMMIT_CAP`                                                |
102
    |                                                                         |
103
    | The set of generators of the `gen_commit_cap` operational type          |
104
    +-------------------------------------------------------------------------+
105
    | | :code:`GEN_COMMIT_CAP_OPR_TMPS`                                       |
106
    |                                                                         |
107
    | Two-dimensional set with generators of the :code:`gen_commit_cap`       |
108
    | operational type and their operational timepoints.                      |
109
    +-------------------------------------------------------------------------+
110
    | | :code:`GEN_COMMIT_CAP_LINKED_TMPS`                                    |
111
    |                                                                         |
112
    | Two-dimensional set with generators of the :code:`gen_commit_cap`       |
113
    | operational type and their linked timepoints.                           |
114
    +-------------------------------------------------------------------------+
115

116
    |
117

118
    +-------------------------------------------------------------------------+
119
    | Required Input Params                                                   |
120
    +=========================================================================+
121
    | | :code:`gen_commit_cap_unit_size_mw`                                   |
122
    | | *Defined over*: :code:`GEN_COMMIT_CAP`                                |
123
    | | *Within*: :code:`NonNegativeReals`                                    |
124
    |                                                                         |
125
    | The MW size of a unit in this project (projects of the                  |
126
    | :code:`gen_commit_cap` type can represent a fleet of similar units).    |
127
    +-------------------------------------------------------------------------+
128
    | | :code:`gen_commit_cap_min_stable_level_fraction`                      |
129
    | | *Defined over*: :code:`GEN_COMMIT_CAP`                                |
130
    | | *Within*: :code:`NonNegativeReals`                                    |
131
    |                                                                         |
132
    | The minimum stable level of this project as a fraction of its capacity. |
133
    | This can also be interpreted as the minimum stable level of a unit      |
134
    | within this project (as the project itself can represent multiple       |
135
    | units with similar characteristics.                                     |
136
    +-------------------------------------------------------------------------+
137

138
    |
139

140
    +-------------------------------------------------------------------------+
141
    | Optional Input Params                                                   |
142
    +=========================================================================+
143
    | | :code:`gen_commit_cap_startup_plus_ramp_up_rate`                      |
144
    | | *Defined over*: :code:`GEN_COMMIT_CAP`                                |
145
    | | *Within*: :code:`PercentFraction`                                     |
146
    | | *Default*: :code:`1`                                                  |
147
    |                                                                         |
148
    | The project's ramp rate when starting up as percent of project capacity |
149
    | per minute (defaults to 1 if not specified).                            |
150
    +-------------------------------------------------------------------------+
151
    | | :code:`gen_commit_cap_shutdown_plus_ramp_down_rate`                   |
152
    | | *Defined over*: :code:`GEN_COMMIT_CAP`                                |
153
    | | *Within*: :code:`PercentFraction`                                     |
154
    | | *Default*: :code:`1`                                                  |
155
    |                                                                         |
156
    | The project's ramp rate when shutting down as percent of project        |
157
    | capacity per minute (defaults to 1 if not specified).                   |
158
    +-------------------------------------------------------------------------+
159
    | | :code:`gen_commit_cap_ramp_up_when_on_rate`                           |
160
    | | *Defined over*: :code:`GEN_COMMIT_CAP`                                |
161
    | | *Within*: :code:`PercentFraction`                                     |
162
    | | *Default*: :code:`1`                                                  |
163
    |                                                                         |
164
    | The project's upward ramp rate limit during operations, defined as a    |
165
    | fraction of its capacity per minute.                                    |
166
    +-------------------------------------------------------------------------+
167
    | | :code:`gen_commit_cap_ramp_down_when_on_rate`                         |
168
    | | *Defined over*: :code:`GEN_COMMIT_CAP`                                |
169
    | | *Within*: :code:`PercentFraction`                                     |
170
    | | *Default*: :code:`1`                                                  |
171
    |                                                                         |
172
    | The project's downward ramp rate limit during operations, defined as a  |
173
    | fraction of its capacity per minute.                                    |
174
    +-------------------------------------------------------------------------+
175
    | | :code:`gen_commit_cap_min_up_time_hours`                              |
176
    | | *Defined over*: :code:`GEN_COMMIT_CAP`                                |
177
    | | *Within*: :code:`PercentFraction`                                     |
178
    | | *Default*: :code:`1`                                                  |
179
    |                                                                         |
180
    | The project's minimum up time in hours.                                 |
181
    +-------------------------------------------------------------------------+
182
    | | :code:`gen_commit_cap_min_down_time_hours`                            |
183
    | | *Defined over*: :code:`GEN_COMMIT_CAP`                                |
184
    | | *Within*: :code:`PercentFraction`                                     |
185
    | | *Default*: :code:`1`                                                  |
186
    |                                                                         |
187
    | The project's minimum down time in hours.                               |
188
    +-------------------------------------------------------------------------+
189
    | | :code:`gen_commit_cap_aux_consumption_frac_capacity`                  |
190
    | | *Defined over*: :code:`GEN_COMMIT_CAP`                                |
191
    | | *Within*: :code:`PercentFraction`                                     |
192
    | | *Default*: :code:`0`                                                  |
193
    |                                                                         |
194
    | Auxiliary consumption as a fraction of committed capacity.              |
195
    +-------------------------------------------------------------------------+
196
    | | :code:`gen_commit_cap_aux_consumption_frac_power`                     |
197
    | | *Defined over*: :code:`GEN_COMMIT_CAP`                                |
198
    | | *Within*: :code:`PercentFraction`                                     |
199
    | | *Default*: :code:`0`                                                  |
200
    |                                                                         |
201
    | Auxiliary consumption as a fraction of gross power output.              |
202
    +-------------------------------------------------------------------------+
203

204
    |
205

206
    +-------------------------------------------------------------------------+
207
    | Linked Input Params                                                     |
208
    +=========================================================================+
209
    | | :code:`gen_commit_cap_linked_commit_capacity`                         |
210
    | | *Defined over*: :code:`GEN_COMMIT_CAP_LINKED_TMPS`                    |
211
    | | *Within*: :code:`NonNegativeReals`                                    |
212
    |                                                                         |
213
    | The project's committed capacity in the linked timepoints.              |
214
    +-------------------------------------------------------------------------+
215
    | | :code:`gen_commit_cap_linked_power`                                   |
216
    | | *Defined over*: :code:`GEN_COMMIT_CAP_LINKED_TMPS`                    |
217
    | | *Within*: :code:`NonNegativeReals`                                    |
218
    |                                                                         |
219
    | The project's power provision in the linked timepoints.                 |
220
    +-------------------------------------------------------------------------+
221
    | | :code:`gen_commit_cap_linked_upwards_reserves`                        |
222
    | | *Defined over*: :code:`GEN_COMMIT_CAP_LINKED_TMPS`                    |
223
    | | *Within*: :code:`NonNegativeReals`                                    |
224
    |                                                                         |
225
    | The project's upward reserve provision in the linked timepoints.        |
226
    +-------------------------------------------------------------------------+
227
    | | :code:`gen_commit_cap_linked_downwards_reserves`                      |
228
    | | *Defined over*: :code:`GEN_COMMIT_CAP_LINKED_TMPS`                    |
229
    | | *Within*: :code:`NonNegativeReals`                                    |
230
    |                                                                         |
231
    | The project's downward reserve provision in the linked timepoints.      |
232
    +-------------------------------------------------------------------------+
233
    | | :code:`gen_commit_cap_linked_startup`                                 |
234
    | | *Defined over*: :code:`GEN_COMMIT_CAP_LINKED_TMPS`                    |
235
    | | *Within*: :code:`NonNegativeReals`                                    |
236
    |                                                                         |
237
    | The project's startup in the linked timepoints.                         |
238
    +-------------------------------------------------------------------------+
239
    | | :code:`gen_commit_cap_linked_shutdown`                                |
240
    | | *Defined over*: :code:`GEN_COMMIT_CAP_LINKED_TMPS`                    |
241
    | | *Within*: :code:`NonNegativeReals`                                    |
242
    |                                                                         |
243
    | The project's shutdown in the linked timepoints.                        |
244
    +-------------------------------------------------------------------------+
245

246
    |
247

248
    +-------------------------------------------------------------------------+
249
    | Variables                                                               |
250
    +=========================================================================+
251
    | | :code:`GenCommitCap_Provide_Power_MW`                                 |
252
    | | *Within*: :code:`NonNegativeReals`                                    |
253
    | | *Defined over*: :code:`GEN_COMMIT_CAP_OPR_TMPS`                       |
254
    |                                                                         |
255
    | Power provision in MW from this project in each timepoint in which the  |
256
    | project is operational (capacity exists and the project is available).  |
257
    | If modeling auxiliary consumption, this is the gross power output.      |
258
    +-------------------------------------------------------------------------+
259
    | | :code:`Commit_Capacity_MW`                                            |
260
    | | *Within*: :code:`NonNegativeReals`                                    |
261
    | | *Defined over*: :code:`GEN_COMMIT_CAP_OPR_TMPS`                       |
262
    |                                                                         |
263
    | A continuous variable that represents the commitment state of the       |
264
    | (i.e. of the units represented by this project).                        |
265
    +-------------------------------------------------------------------------+
266
    | | :code:`GenCommitCap_Fuel_Burn_MMBTU`                                  |
267
    | | *Within*: :code:`NonNegativeReals`                                    |
268
    | | *Defined over*: :code:`GEN_COMMIT_CAP_OPR_TMPS`                       |
269
    |                                                                         |
270
    | Fuel burn by this project in each operational timepoint.                |
271
    +-------------------------------------------------------------------------+
272
    | | :code:`Ramp_Up_Startup_MW`                                            |
273
    | | *Within*: :code:`Reals`                                               |
274
    | | *Defined over*: :code:`GEN_COMMIT_CAP_OPR_TMPS`                       |
275
    |                                                                         |
276
    | The upward ramp of the project when capacity is started up.             |
277
    +-------------------------------------------------------------------------+
278
    | | :code:`Ramp_Down_Startup_MW`                                          |
279
    | | *Within*: :code:`Reals`                                               |
280
    | | *Defined over*: :code:`GEN_COMMIT_CAP_OPR_TMPS`                       |
281
    |                                                                         |
282
    | The downward ramp of the project when capacity is shutting down.        |
283
    +-------------------------------------------------------------------------+
284
    | | :code:`Ramp_Up_When_On_MW`                                            |
285
    | | *Within*: :code:`Reals`                                               |
286
    | | *Defined over*: :code:`GEN_COMMIT_CAP_OPR_TMPS`                       |
287
    |                                                                         |
288
    | The upward ramp of the project when capacity on.                        |
289
    +-------------------------------------------------------------------------+
290
    | | :code:`Ramp_Down_When_On_MW`                                          |
291
    | | *Within*: :code:`Reals`                                               |
292
    | | *Defined over*: :code:`GEN_COMMIT_CAP_OPR_TMPS`                       |
293
    |                                                                         |
294
    | The downward ramp of the project when capacity is on.                   |
295
    +-------------------------------------------------------------------------+
296
    | | :code:`GenCommitCap_Startup_MW`                                       |
297
    | | *Within*: :code:`NonNegativeReals`                                    |
298
    | | *Defined over*: :code:`GEN_COMMIT_CAP_OPR_TMPS`                       |
299
    |                                                                         |
300
    | The amount of capacity started up (in MW).                              |
301
    +-------------------------------------------------------------------------+
302
    | | :code:`GenCommitCap_Shutdown_MW`                                      |
303
    | | *Within*: :code:`NonNegativeReals`                                    |
304
    | | *Defined over*: :code:`GEN_COMMIT_CAP_OPR_TMPS`                       |
305
    |                                                                         |
306
    | The amount of capacity shut down (in MW).                               |
307
    +-------------------------------------------------------------------------+
308

309
    |
310

311
    +-------------------------------------------------------------------------+
312
    | Expressions                                                             |
313
    +=========================================================================+
314
    | | :code:`GenCommitCap_Auxiliary_Consumption_MW`                         |
315
    | | *Defined over*: :code:`GEN_COMMIT_CAP_OPR_TMPS`                       |
316
    |                                                                         |
317
    | The project's auxiliary consumption (power consumed on-site and not     |
318
    | sent to the grid) in each timepoint.                                    |
319
    +-------------------------------------------------------------------------+
320

321
    +-------------------------------------------------------------------------+
322
    | Constraints                                                             |
323
    +=========================================================================+
324
    | Commitment and Power                                                    |
325
    +-------------------------------------------------------------------------+
326
    | | :code:`Commit_Capacity_Constraint`                                    |
327
    | | *Defined over*: :code:`GEN_COMMIT_CAP_OPR_TMPS`                       |
328
    |                                                                         |
329
    | Limits committed capacity to the available capacity.                    |
330
    +-------------------------------------------------------------------------+
331
    | | :code:`GenCommitCap_Max_Power_Constraint`                             |
332
    | | *Defined over*: :code:`GEN_COMMIT_CAP_OPR_TMPS`                       |
333
    |                                                                         |
334
    | Limits the power plus upward reserves to the committed capacity.        |
335
    +-------------------------------------------------------------------------+
336
    | | :code:`GenCommitCap_Min_Power_Constraint`                             |
337
    | | *Defined over*: :code:`GEN_COMMIT_CAP_OPR_TMPS`                       |
338
    |                                                                         |
339
    | Limits the power provision minus downward reserves to the minimum       |
340
    | stable level for the project.                                           |
341
    +-------------------------------------------------------------------------+
342
    | Ramps                                                                   |
343
    +-------------------------------------------------------------------------+
344
    | | :code:`Ramp_Up_Off_to_On_Constraint`                                  |
345
    | | *Defined over*: :code:`GEN_COMMIT_CAP_OPR_TMPS`                       |
346
    |                                                                         |
347
    | Limits the allowed project upward ramp when turning capacity on based   |
348
    | on the :code:`gen_commit_cap_startup_plus_ramp_up_rate`.                |
349
    +-------------------------------------------------------------------------+
350
    | | :code:`Ramp_Up_When_On_Constraint`                                    |
351
    | | *Defined over*: :code:`GEN_COMMIT_CAP_OPR_TMPS`                       |
352
    |                                                                         |
353
    | Limits the allowed project upward ramp when capacity is on based on     |
354
    | the :code:`gen_commit_cap_ramp_up_when_on_rate`.                        |
355
    +-------------------------------------------------------------------------+
356
    | | :code:`Ramp_Up_When_On_Headroom_Constraint`                           |
357
    | | *Defined over*: :code:`GEN_COMMIT_CAP_OPR_TMPS`                       |
358
    |                                                                         |
359
    | Limits the allowed project upward ramp based on the headroom available  |
360
    | in the previous timepoint.                                              |
361
    +-------------------------------------------------------------------------+
362
    | | :code:`GenCommitCap_Ramp_Up_Constraint`                               |
363
    | | *Defined over*: :code:`GEN_COMMIT_CAP_OPR_TMPS`                       |
364
    |                                                                         |
365
    | Limits the allowed project upward ramp (regardless of commitment state).|
366
    +-------------------------------------------------------------------------+
367
    | | :code:`Ramp_Down_On_to_Off_Constraint`                                |
368
    | | *Defined over*: :code:`GEN_COMMIT_CAP_OPR_TMPS`                       |
369
    |                                                                         |
370
    | Limits the allowed project downward ramp when turning capacity on based |
371
    | on the :code:`gen_commit_cap_shutdown_plus_ramp_down_rate`.             |
372
    +-------------------------------------------------------------------------+
373
    | | :code:`Ramp_Down_When_On_Constraint`                                  |
374
    | | *Defined over*: :code:`GEN_COMMIT_CAP_OPR_TMPS`                       |
375
    |                                                                         |
376
    | Limits the allowed project downward ramp when capacity is on based on   |
377
    | the :code:`gen_commit_cap_ramp_down_when_on_rate`.                      |
378
    +-------------------------------------------------------------------------+
379
    | | :code:`Ramp_Down_When_On_Headroom_Constraint`                         |
380
    | | *Defined over*: :code:`GEN_COMMIT_CAP_OPR_TMPS`                       |
381
    |                                                                         |
382
    | Limits the allowed project downward ramp based on the headroom          |
383
    | available in the current timepoint.                                     |
384
    +-------------------------------------------------------------------------+
385
    | | :code:`GenCommitCap_Ramp_Down_Constraint`                             |
386
    | | *Defined over*: :code:`GEN_COMMIT_CAP_OPR_TMPS`                       |
387
    |                                                                         |
388
    | Limits the allowed project downward ramp (regardless of commitment      |
389
    | state).                                                                 |
390
    +-------------------------------------------------------------------------+
391
    | Minimum Up and Down Time                                                |
392
    +-------------------------------------------------------------------------+
393
    | | :code:`GenCommitCap_Startup_Constraint`                               |
394
    | | *Defined over*: :code:`GEN_COMMIT_CAP_OPR_TMPS`                       |
395
    |                                                                         |
396
    | Limits the capacity started up to the difference in commitment between  |
397
    | the current and previous timepoint.                                     |
398
    +-------------------------------------------------------------------------+
399
    | | :code:`GenCommitCap_Shutdown_Constraint`                              |
400
    | | *Defined over*: :code:`GEN_COMMIT_CAP_OPR_TMPS`                       |
401
    |                                                                         |
402
    | Limits the capacity shut down to the difference in commitment between   |
403
    | the current and previous timepoint.                                     |
404
    +-------------------------------------------------------------------------+
405
    | | :code:`GenCommitCap_Min_Up_Time_Constraint`                           |
406
    | | *Defined over*: :code:`GEN_COMMIT_CAP_OPR_TMPS`                       |
407
    |                                                                         |
408
    | Requires that when units within this project are started, they stay on  |
409
    | for at least :code:`gen_commit_cap_min_up_time_hours`.                  |
410
    +-------------------------------------------------------------------------+
411
    | | :code:`GenCommitCap_Min_Down_Time_Constraint`                         |
412
    | | *Defined over*: :code:`GEN_COMMIT_CAP_OPR_TMPS`                       |
413
    |                                                                         |
414
    | Requires that when units within this project are stopped, they stay off |
415
    | for at least :code:`gen_commit_cap_min_down_time_hours`.                |
416
    +-------------------------------------------------------------------------+
417

418
    """
419

420
    # Sets
421
    ###########################################################################
422
    m.GEN_COMMIT_CAP = Set(
1✔
423
        within=m.PROJECTS,
424
        initialize=lambda mod: subset_init_by_param_value(
425
            mod, "PROJECTS", "operational_type", "gen_commit_cap"
426
        ),
427
    )
428

429
    m.GEN_COMMIT_CAP_OPR_TMPS = Set(
1✔
430
        dimen=2,
431
        initialize=lambda mod: subset_init_by_set_membership(
432
            mod=mod, superset="PRJ_OPR_TMPS", index=0, membership_set=mod.GEN_COMMIT_CAP
433
        ),
434
    )
435

436
    m.GEN_COMMIT_CAP_LINKED_TMPS = Set(dimen=2)
1✔
437

438
    # Required Params
439
    ###########################################################################
440
    m.gen_commit_cap_unit_size_mw = Param(m.GEN_COMMIT_CAP, within=NonNegativeReals)
1✔
441
    m.gen_commit_cap_min_stable_level_fraction = Param(
1✔
442
        m.GEN_COMMIT_CAP, within=PercentFraction
443
    )
444

445
    # Optional Params
446
    ###########################################################################
447

448
    m.gen_commit_cap_startup_plus_ramp_up_rate = Param(
1✔
449
        m.GEN_COMMIT_CAP, within=PercentFraction, default=1
450
    )
451
    m.gen_commit_cap_shutdown_plus_ramp_down_rate = Param(
1✔
452
        m.GEN_COMMIT_CAP, within=PercentFraction, default=1
453
    )
454
    m.gen_commit_cap_ramp_up_when_on_rate = Param(
1✔
455
        m.GEN_COMMIT_CAP, within=PercentFraction, default=1
456
    )
457
    m.gen_commit_cap_ramp_down_when_on_rate = Param(
1✔
458
        m.GEN_COMMIT_CAP, within=PercentFraction, default=1
459
    )
460
    m.gen_commit_cap_min_up_time_hours = Param(
1✔
461
        m.GEN_COMMIT_CAP, within=NonNegativeReals, default=1
462
    )
463
    m.gen_commit_cap_min_down_time_hours = Param(
1✔
464
        m.GEN_COMMIT_CAP, within=NonNegativeReals, default=1
465
    )
466

467
    m.gen_commit_cap_aux_consumption_frac_capacity = Param(
1✔
468
        m.GEN_COMMIT_CAP, within=PercentFraction, default=0
469
    )
470

471
    m.gen_commit_cap_aux_consumption_frac_power = Param(
1✔
472
        m.GEN_COMMIT_CAP, within=PercentFraction, default=0
473
    )
474

475
    # Linked Params
476
    ###########################################################################
477

478
    m.gen_commit_cap_linked_commit_capacity = Param(
1✔
479
        m.GEN_COMMIT_CAP_LINKED_TMPS, within=NonNegativeReals
480
    )
481

482
    m.gen_commit_cap_linked_power = Param(
1✔
483
        m.GEN_COMMIT_CAP_LINKED_TMPS, within=NonNegativeReals
484
    )
485

486
    m.gen_commit_cap_linked_upwards_reserves = Param(
1✔
487
        m.GEN_COMMIT_CAP_LINKED_TMPS, within=NonNegativeReals
488
    )
489

490
    m.gen_commit_cap_linked_downwards_reserves = Param(
1✔
491
        m.GEN_COMMIT_CAP_LINKED_TMPS, within=NonNegativeReals
492
    )
493

494
    m.gen_commit_cap_linked_startup = Param(
1✔
495
        m.GEN_COMMIT_CAP_LINKED_TMPS, within=NonNegativeReals
496
    )
497

498
    m.gen_commit_cap_linked_shutdown = Param(
1✔
499
        m.GEN_COMMIT_CAP_LINKED_TMPS, within=NonNegativeReals
500
    )
501

502
    # Variables
503
    ###########################################################################
504
    m.GenCommitCap_Provide_Power_MW = Var(
1✔
505
        m.GEN_COMMIT_CAP_OPR_TMPS, within=NonNegativeReals
506
    )
507
    m.Commit_Capacity_MW = Var(m.GEN_COMMIT_CAP_OPR_TMPS, within=NonNegativeReals)
1✔
508

509
    # Variables for optional ramp constraints
510
    # We'll have separate treatment of ramps of:
511
    # generation that is online in both the current and the previous timepoint
512
    # and of
513
    # generation that is either started up or shut down since the previous
514
    # timepoint
515

516
    # Ramp_Up_Startup_MW and Ramp_Down_Shutdown_MW must be able to take
517
    # either positive  or negative values, as they are both constrained by
518
    # a product of a positive number and the difference committed capacity
519
    # between the current and previous timepoints (which needs to be able to
520
    # take on both positive values when turning units on and negative values
521
    # when turning units off)
522
    # They also need to be separate variables, as if they were combined,
523
    # the only solution would be for there to be no startups/shutdowns
524
    m.Ramp_Up_Startup_MW = Var(m.GEN_COMMIT_CAP_OPR_TMPS, within=Reals)
1✔
525
    m.Ramp_Down_Shutdown_MW = Var(m.GEN_COMMIT_CAP_OPR_TMPS, within=Reals)
1✔
526

527
    m.Ramp_Up_When_On_MW = Var(m.GEN_COMMIT_CAP_OPR_TMPS, within=NonNegativeReals)
1✔
528
    m.Ramp_Down_When_On_MW = Var(m.GEN_COMMIT_CAP_OPR_TMPS, within=NonPositiveReals)
1✔
529

530
    # Variables for constraining up and down time
531
    # Startup and shutdown variables, must be non-negative
532
    m.GenCommitCap_Startup_MW = Var(m.GEN_COMMIT_CAP_OPR_TMPS, within=NonNegativeReals)
1✔
533
    m.GenCommitCap_Shutdown_MW = Var(m.GEN_COMMIT_CAP_OPR_TMPS, within=NonNegativeReals)
1✔
534

535
    # Expressions
536
    ###########################################################################
537
    # TODO: the reserve rules are the same in all modules, so should be
538
    #  consolidated
539
    def upwards_reserve_rule(mod, g, tmp):
1✔
540
        return headroom_provision_rule(d, mod, g, tmp)
1✔
541

542
    m.GenCommitCap_Upwards_Reserves_MW = Expression(
1✔
543
        m.GEN_COMMIT_CAP_OPR_TMPS, rule=upwards_reserve_rule
544
    )
545

546
    def downwards_reserve_rule(mod, g, tmp):
1✔
547
        return footroom_provision_rule(d, mod, g, tmp)
1✔
548

549
    m.GenCommitCap_Downwards_Reserves_MW = Expression(
1✔
550
        m.GEN_COMMIT_CAP_OPR_TMPS, rule=downwards_reserve_rule
551
    )
552

553
    m.GenCommitCap_Auxiliary_Consumption_MW = Expression(
1✔
554
        m.GEN_COMMIT_CAP_OPR_TMPS, rule=auxiliary_consumption_rule
555
    )
556

557
    # Constraints
558
    ###########################################################################
559

560
    # Commitment and power
561
    m.Commit_Capacity_Constraint = Constraint(
1✔
562
        m.GEN_COMMIT_CAP_OPR_TMPS, rule=commit_capacity_constraint_rule
563
    )
564

565
    m.GenCommitCap_Max_Power_Constraint = Constraint(
1✔
566
        m.GEN_COMMIT_CAP_OPR_TMPS, rule=max_power_rule
567
    )
568

569
    m.GenCommitCap_Min_Power_Constraint = Constraint(
1✔
570
        m.GEN_COMMIT_CAP_OPR_TMPS, rule=min_power_rule
571
    )
572

573
    # Ramping
574
    m.Ramp_Up_Off_to_On_Constraint = Constraint(
1✔
575
        m.GEN_COMMIT_CAP_OPR_TMPS, rule=ramp_up_off_to_on_constraint_rule
576
    )
577

578
    m.Ramp_Up_When_On_Constraint = Constraint(
1✔
579
        m.GEN_COMMIT_CAP_OPR_TMPS, rule=ramp_up_on_to_on_constraint_rule
580
    )
581

582
    m.Ramp_Up_When_On_Headroom_Constraint = Constraint(
1✔
583
        m.GEN_COMMIT_CAP_OPR_TMPS, rule=ramp_up_on_to_on_headroom_constraint_rule
584
    )
585

586
    m.GenCommitCap_Ramp_Up_Constraint = Constraint(
1✔
587
        m.GEN_COMMIT_CAP_OPR_TMPS, rule=ramp_up_constraint_rule
588
    )
589

590
    m.Ramp_Down_On_to_Off_Constraint = Constraint(
1✔
591
        m.GEN_COMMIT_CAP_OPR_TMPS, rule=ramp_down_on_to_off_constraint_rule
592
    )
593

594
    m.Ramp_Down_When_On_Constraint = Constraint(
1✔
595
        m.GEN_COMMIT_CAP_OPR_TMPS, rule=ramp_down_on_to_on_constraint_rule
596
    )
597

598
    m.Ramp_Down_When_On_Headroom_Constraint = Constraint(
1✔
599
        m.GEN_COMMIT_CAP_OPR_TMPS, rule=ramp_down_on_to_on_headroom_constraint_rule
600
    )
601

602
    m.GenCommitCap_Ramp_Down_Constraint = Constraint(
1✔
603
        m.GEN_COMMIT_CAP_OPR_TMPS, rule=ramp_down_constraint_rule
604
    )
605

606
    # Min up and down time
607
    m.GenCommitCap_Startup_Constraint = Constraint(
1✔
608
        m.GEN_COMMIT_CAP_OPR_TMPS, rule=startup_constraint_rule
609
    )
610

611
    m.GenCommitCap_Shutdown_Constraint = Constraint(
1✔
612
        m.GEN_COMMIT_CAP_OPR_TMPS, rule=shutdown_constraint_rule
613
    )
614

615
    m.GenCommitCap_Min_Up_Time_Constraint = Constraint(
1✔
616
        m.GEN_COMMIT_CAP_OPR_TMPS, rule=min_up_time_constraint_rule
617
    )
618

619
    m.GenCommitCap_Min_Down_Time_Constraint = Constraint(
1✔
620
        m.GEN_COMMIT_CAP_OPR_TMPS, rule=min_down_time_constraint_rule
621
    )
622

623

624
# Expression Rules
625
###############################################################################
626
def auxiliary_consumption_rule(mod, g, tmp):
1✔
627
    """
628
    **Expression Name**: GenCommitCap_Auxiliary_Consumption_MW
629
    **Defined Over**: GEN_COMMIT_CAP_OPR_TMPS
630
    """
631
    return (
1✔
632
        mod.Commit_Capacity_MW[g, tmp]
633
        * mod.gen_commit_cap_aux_consumption_frac_capacity[g]
634
        + mod.GenCommitCap_Provide_Power_MW[g, tmp]
635
        * mod.gen_commit_cap_aux_consumption_frac_power[g]
636
    )
637

638

639
# Constraint Formulation Rules
640
###############################################################################
641

642

643
# Commitment and power
644
def commit_capacity_constraint_rule(mod, g, tmp):
1✔
645
    """
646
    **Constraint Name**: Commit_Capacity_Constraint
647
    **Enforced Over**: GEN_COMMIT_CAP_OPR_TMPS
648

649
    Can't commit more capacity than available in each timepoint.
650
    """
651
    return (
1✔
652
        mod.Commit_Capacity_MW[g, tmp]
653
        <= mod.Capacity_MW[g, mod.period[tmp]] * mod.Availability_Derate[g, tmp]
654
    )
655

656

657
def max_power_rule(mod, g, tmp):
1✔
658
    """
659
    **Constraint Name**: GenCommitCap_Max_Power_Constraint
660
    **Enforced Over**: GEN_COMMIT_CAP_OPR_TMPS
661

662
    Power plus upward services cannot exceed capacity.
663
    """
664
    return (
1✔
665
        mod.GenCommitCap_Provide_Power_MW[g, tmp]
666
        + mod.GenCommitCap_Upwards_Reserves_MW[g, tmp]
667
        <= mod.Commit_Capacity_MW[g, tmp]
668
    )
669

670

671
def min_power_rule(mod, g, tmp):
1✔
672
    """
673
    **Constraint Name**: GenCommitCap_Min_Power_Constraint
674
    **Enforced Over**: GEN_COMMIT_CAP_OPR_TMPS
675

676
    Power minus downward services cannot be below a minimum stable level.
677
    """
678
    return (
1✔
679
        mod.GenCommitCap_Provide_Power_MW[g, tmp]
680
        - mod.GenCommitCap_Downwards_Reserves_MW[g, tmp]
681
        >= mod.Commit_Capacity_MW[g, tmp]
682
        * mod.gen_commit_cap_min_stable_level_fraction[g]
683
    )
684

685

686
# Ramping
687
def ramp_up_off_to_on_constraint_rule(mod, g, tmp):
1✔
688
    """
689
    **Constraint Name**: Ramp_Up_Off_to_On_Constraint
690
    **Enforced Over**: GEN_COMMIT_CAP_OPR_TMPS
691

692
    When turning on, generators can ramp up to a certain fraction of
693
    started up capacity. This fraction must be greater than or equal to
694
    the minimum stable level for the generator to be able to turn on.
695

696
    We assume that a unit has to reach its setpoint at the start of the
697
    timepoint; as such, the ramping between 2 timepoints is assumed to
698
    take place during the duration of the first timepoint, and the
699
    ramp rate limit is adjusted for the duration of the first timepoint.
700
    """
701
    if check_if_boundary_type_and_first_timepoint(
1✔
702
        mod=mod,
703
        tmp=tmp,
704
        balancing_type=mod.balancing_type_project[g],
705
        boundary_type="linear",
706
    ):
707
        return Constraint.Skip
1✔
708
    else:
709
        if check_if_boundary_type_and_first_timepoint(
1✔
710
            mod=mod,
711
            tmp=tmp,
712
            balancing_type=mod.balancing_type_project[g],
713
            boundary_type="linked",
714
        ):
715
            prev_tmp_hrs_in_tmp = mod.hrs_in_linked_tmp[0]
×
716
            prev_tmp_commit_capacity = mod.gen_commit_cap_linked_commit_capacity[g, 0]
×
717
        else:
718
            prev_tmp_hrs_in_tmp = mod.hrs_in_tmp[
1✔
719
                mod.prev_tmp[tmp, mod.balancing_type_project[g]]
720
            ]
721
            prev_tmp_commit_capacity = mod.Commit_Capacity_MW[
1✔
722
                g, mod.prev_tmp[tmp, mod.balancing_type_project[g]]
723
            ]
724

725
        return (
1✔
726
            mod.Ramp_Up_Startup_MW[g, tmp]
727
            <= (mod.Commit_Capacity_MW[g, tmp] - prev_tmp_commit_capacity)
728
            * mod.gen_commit_cap_startup_plus_ramp_up_rate[g]
729
            * 60
730
            * prev_tmp_hrs_in_tmp
731
        )
732

733

734
def ramp_up_on_to_on_constraint_rule(mod, g, tmp):
1✔
735
    """
736
    **Constraint Name**: Ramp_Up_When_On_Constraint
737
    **Enforced Over**: GEN_COMMIT_CAP_OPR_TMPS
738

739
    Generators online in the last timepoint, if still online, could have
740
    ramped up at a rate at or below the online capacity times a
741
    pre-specified ramp rate fraction. The max on to on ramp up
742
    allowed is if they all stayed online. Startups are treated separately.
743
    There are limitations to this approach. For example, if online
744
    capacity was producing at full power at t-2 and t-1, some additional
745
    capacity was turned on at t-1 and ramped to some level above its
746
    Pmin but not full output, this constraint would allow for the total
747
    committed capacity in t-1 to be ramped up, even though in reality
748
    only the started up capacity can be ramped as the capacity from t-2
749
    is already producing at full power. In reality, this situation is
750
    unlikely to be an issue, as most generators can ramp from Pmin to
751
    Pmax fully in an hour, so the fact that this constraint is too lax
752
    in this situation does not matter when modeling fleets at an hourly
753
    or coarser resolution.
754

755
    We assume that a unit has to reach its setpoint at the start of the
756
    timepoint; as such, the ramping between 2 timepoints is assumed to
757
    take place during the duration of the first timepoint, and the
758
    ramp rate limit is adjusted for the duration of the first timepoint.
759
    """
760
    if check_if_boundary_type_and_first_timepoint(
1✔
761
        mod=mod,
762
        tmp=tmp,
763
        balancing_type=mod.balancing_type_project[g],
764
        boundary_type="linear",
765
    ):
766
        return Constraint.Skip
1✔
767
    else:
768
        if check_if_boundary_type_and_first_timepoint(
1✔
769
            mod=mod,
770
            tmp=tmp,
771
            balancing_type=mod.balancing_type_project[g],
772
            boundary_type="linked",
773
        ):
774
            prev_tmp_hrs_in_tmp = mod.hrs_in_linked_tmp[0]
×
775
            prev_tmp_commit_capacity = mod.gen_commit_cap_linked_commit_capacity[g, 0]
×
776
        else:
777
            prev_tmp_hrs_in_tmp = mod.hrs_in_tmp[
1✔
778
                mod.prev_tmp[tmp, mod.balancing_type_project[g]]
779
            ]
780
            prev_tmp_commit_capacity = mod.Commit_Capacity_MW[
1✔
781
                g, mod.prev_tmp[tmp, mod.balancing_type_project[g]]
782
            ]
783
        return (
1✔
784
            mod.Ramp_Up_When_On_MW[g, tmp]
785
            <= prev_tmp_commit_capacity
786
            * mod.gen_commit_cap_ramp_up_when_on_rate[g]
787
            * 60
788
            * prev_tmp_hrs_in_tmp
789
        )
790

791

792
def ramp_up_on_to_on_headroom_constraint_rule(mod, g, tmp):
1✔
793
    """
794
    **Constraint Name**: Ramp_Up_When_On_Headroom_Constraint
795
    **Enforced Over**: GEN_COMMIT_CAP_OPR_TMPS
796

797
    Generators online in the previous timepoint that are still online
798
    could not have ramped up above their total online capacity, i.e. not
799
    more than their available headroom in the previous timepoint.
800
    The maximum possible headroom in the previous timepoint is equal to
801
    the difference between committed capacity and (power provided minus
802
    downward reserves).
803
    """
804
    # TODO: check behavior more carefully (same for ramp down)
805
    if check_if_boundary_type_and_first_timepoint(
1✔
806
        mod=mod,
807
        tmp=tmp,
808
        balancing_type=mod.balancing_type_project[g],
809
        boundary_type="linear",
810
    ):
811
        return Constraint.Skip
1✔
812
    else:
813
        if check_if_boundary_type_and_first_timepoint(
1✔
814
            mod=mod,
815
            tmp=tmp,
816
            balancing_type=mod.balancing_type_project[g],
817
            boundary_type="linked",
818
        ):
819
            prev_tmp_commit_capacity = mod.gen_commit_cap_linked_commit_capacity[g, 0]
×
820
            prev_tmp_power = mod.gen_commit_cap_linked_power[g, 0]
×
821
            prev_tmp_downwards_reserves = mod.gen_commit_cap_linked_downwards_reserves[
×
822
                g, 0
823
            ]
824
        else:
825
            prev_tmp_commit_capacity = mod.Commit_Capacity_MW[
1✔
826
                g, mod.prev_tmp[tmp, mod.balancing_type_project[g]]
827
            ]
828
            prev_tmp_power = mod.GenCommitCap_Provide_Power_MW[
1✔
829
                g, mod.prev_tmp[tmp, mod.balancing_type_project[g]]
830
            ]
831
            prev_tmp_downwards_reserves = mod.GenCommitCap_Downwards_Reserves_MW[
1✔
832
                g, mod.prev_tmp[tmp, mod.balancing_type_project[g]]
833
            ]
834
        return mod.Ramp_Up_When_On_MW[g, tmp] <= prev_tmp_commit_capacity - (
1✔
835
            prev_tmp_power - prev_tmp_downwards_reserves
836
        )
837

838

839
def ramp_up_constraint_rule(mod, g, tmp):
1✔
840
    """
841
    **Constraint Name**: GenCommitCap_Ramp_Up_Constraint
842
    **Enforced Over**: GEN_COMMIT_CAP_OPR_TMPS
843

844
    The ramp up (power provided in the current timepoint minus power
845
    provided in the previous timepoint), adjusted for any reserve provision
846
    in the current and previous timepoint, cannot exceed a prespecified
847
    ramp rate (expressed as fraction of capacity)
848
    Two components:
849
    1) Ramp_Up_Startup_MW (see Ramp_Up_Off_to_On_Constraint above):
850
    If we are turning generators on since the previous timepoint, we will
851
    allow the ramp of going from 0 to minimum stable level + some
852
    additional ramping : the gen_commit_cap_startup_plus_ramp_up_rate
853
    parameter
854
    2) Ramp_Up_When_On_MW (see Ramp_Up_When_On_Constraint and
855
    Ramp_Up_When_On_Headroom_Constraint above):
856
    Units committed in both the current timepoint and the previous
857
    timepoint could have ramped up at a certain rate since the previous
858
    timepoint
859
    """
860
    if check_if_boundary_type_and_first_timepoint(
1✔
861
        mod=mod,
862
        tmp=tmp,
863
        balancing_type=mod.balancing_type_project[g],
864
        boundary_type="linear",
865
    ):
866
        return Constraint.Skip
1✔
867
    else:
868
        if check_if_boundary_type_and_first_timepoint(
1✔
869
            mod=mod,
870
            tmp=tmp,
871
            balancing_type=mod.balancing_type_project[g],
872
            boundary_type="linked",
873
        ):
874
            prev_tmp_hrs_in_tmp = mod.hrs_in_linked_tmp[0]
×
875
            prev_tmp_power = mod.gen_commit_cap_linked_power[g, 0]
×
876
            prev_tmp_downwards_reserves = mod.gen_commit_cap_linked_downwards_reserves[
×
877
                g, 0
878
            ]
879
        else:
880
            prev_tmp_hrs_in_tmp = mod.hrs_in_tmp[
1✔
881
                mod.prev_tmp[tmp, mod.balancing_type_project[g]]
882
            ]
883
            prev_tmp_power = mod.GenCommitCap_Provide_Power_MW[
1✔
884
                g, mod.prev_tmp[tmp, mod.balancing_type_project[g]]
885
            ]
886
            prev_tmp_downwards_reserves = mod.GenCommitCap_Downwards_Reserves_MW[
1✔
887
                g, mod.prev_tmp[tmp, mod.balancing_type_project[g]]
888
            ]
889
        # If ramp rate limits, adjusted for timepoint duration, allow you to
890
        # start up the full capacity and ramp up the full operable range
891
        # between timepoints, constraint won't bind, so skip
892
        if mod.gen_commit_cap_startup_plus_ramp_up_rate[
1✔
893
            g
894
        ] * 60 * prev_tmp_hrs_in_tmp >= 1 and mod.gen_commit_cap_ramp_up_when_on_rate[
895
            g
896
        ] * 60 * prev_tmp_hrs_in_tmp >= (
897
            1 - mod.gen_commit_cap_min_stable_level_fraction[g]
898
        ):
899
            return Constraint.Skip
1✔
900
        else:
901
            return (
1✔
902
                mod.GenCommitCap_Provide_Power_MW[g, tmp]
903
                + mod.GenCommitCap_Upwards_Reserves_MW[g, tmp]
904
            ) - (
905
                prev_tmp_power - prev_tmp_downwards_reserves
906
            ) <= mod.Ramp_Up_Startup_MW[
907
                g, tmp
908
            ] + mod.Ramp_Up_When_On_MW[
909
                g, tmp
910
            ]
911

912

913
def ramp_down_on_to_off_constraint_rule(mod, g, tmp):
1✔
914
    """
915
    **Constraint Name**: Ramp_Down_On_to_Off_Constraint
916
    **Enforced Over**: GEN_COMMIT_CAP_OPR_TMPS
917

918
    When turning off, generators can ramp down from a certain
919
    fraction of the capacity to be shut down to 0. This fraction must be
920
    greater than or equal to the minimum stable level for the generator
921
    to be able to turn off.
922

923
    We assume that a unit has to reach its setpoint at the start of the
924
    timepoint; as such, the ramping between 2 timepoints is assumed to
925
    take place during the duration of the first timepoint, and the
926
    ramp rate limit is adjusted for the duration of the first timepoint.
927
    """
928
    if check_if_boundary_type_and_first_timepoint(
1✔
929
        mod=mod,
930
        tmp=tmp,
931
        balancing_type=mod.balancing_type_project[g],
932
        boundary_type="linear",
933
    ):
934
        return Constraint.Skip
1✔
935
    else:
936
        if check_if_boundary_type_and_first_timepoint(
1✔
937
            mod=mod,
938
            tmp=tmp,
939
            balancing_type=mod.balancing_type_project[g],
940
            boundary_type="linked",
941
        ):
942
            prev_tmp_hrs_in_tmp = mod.hrs_in_linked_tmp[0]
×
943
            prev_tmp_commit_capacity = mod.gen_commit_cap_linked_commit_capacity[g, 0]
×
944
        else:
945
            prev_tmp_hrs_in_tmp = mod.hrs_in_tmp[
1✔
946
                mod.prev_tmp[tmp, mod.balancing_type_project[g]]
947
            ]
948
            prev_tmp_commit_capacity = mod.Commit_Capacity_MW[
1✔
949
                g, mod.prev_tmp[tmp, mod.balancing_type_project[g]]
950
            ]
951
        return (
1✔
952
            mod.Ramp_Down_Shutdown_MW[g, tmp]
953
            >= (mod.Commit_Capacity_MW[g, tmp] - prev_tmp_commit_capacity)
954
            * mod.gen_commit_cap_shutdown_plus_ramp_down_rate[g]
955
            * 60
956
            * prev_tmp_hrs_in_tmp
957
        )
958

959

960
def ramp_down_on_to_on_constraint_rule(mod, g, tmp):
1✔
961
    """
962
    **Constraint Name**: Ramp_Down_When_On_Constraint
963
    **Enforced Over**: GEN_COMMIT_CAP_OPR_TMPS
964

965
    Generators still online in the current timepoint could have ramped
966
    down at a rate at or below the online capacity times a pre-specified
967
    ramp rate fraction. Shutdowns are treated separately.
968
    """
969
    if check_if_boundary_type_and_first_timepoint(
1✔
970
        mod=mod,
971
        tmp=tmp,
972
        balancing_type=mod.balancing_type_project[g],
973
        boundary_type="linear",
974
    ):
975
        return Constraint.Skip
1✔
976
    else:
977
        if check_if_boundary_type_and_first_timepoint(
1✔
978
            mod=mod,
979
            tmp=tmp,
980
            balancing_type=mod.balancing_type_project[g],
981
            boundary_type="linked",
982
        ):
983
            prev_tmp_hrs_in_tmp = mod.hrs_in_linked_tmp[0]
×
984
        else:
985
            prev_tmp_hrs_in_tmp = mod.hrs_in_tmp[
1✔
986
                mod.prev_tmp[tmp, mod.balancing_type_project[g]]
987
            ]
988
        return (
1✔
989
            mod.Ramp_Down_When_On_MW[g, tmp]
990
            >= mod.Commit_Capacity_MW[g, tmp]
991
            * (-mod.gen_commit_cap_ramp_down_when_on_rate[g])
992
            * 60
993
            * prev_tmp_hrs_in_tmp
994
        )
995

996

997
def ramp_down_on_to_on_headroom_constraint_rule(mod, g, tmp):
1✔
998
    """
999
    **Constraint Name**: Ramp_Down_When_On_Headroom_Constraint
1000
    **Enforced Over**: GEN_COMMIT_CAP_OPR_TMPS
1001

1002
    Generators still online in the current timepoint could not have ramped
1003
    down more than their current headroom. The maximum possible headroom is
1004
    equal to the difference between committed capacity and (power provided
1005
    minus downward reserves).
1006
    Note: Ramp_Down_When_On_MW is negative when a unit is ramping down, so
1007
    we add a negative sign before it the constraint.
1008
    """
1009
    # TODO: bug -- this shouldn't be skipping the first tmp of linear
1010
    #  horizons as it's not looking to a previous timepoint
1011
    if check_if_boundary_type_and_first_timepoint(
1✔
1012
        mod=mod,
1013
        tmp=tmp,
1014
        balancing_type=mod.balancing_type_project[g],
1015
        boundary_type="linear",
1016
    ):
1017
        return Constraint.Skip
1✔
1018
    else:
1019
        return -mod.Ramp_Down_When_On_MW[g, tmp] <= mod.Commit_Capacity_MW[g, tmp] - (
1✔
1020
            mod.GenCommitCap_Provide_Power_MW[g, tmp]
1021
            - mod.GenCommitCap_Downwards_Reserves_MW[g, tmp]
1022
        )
1023

1024

1025
def ramp_down_constraint_rule(mod, g, tmp):
1✔
1026
    """
1027
    **Constraint Name**: GenCommitCap_Ramp_Down_Constraint
1028
    **Enforced Over**: GEN_COMMIT_CAP_OPR_TMPS
1029

1030
    The ramp down (power provided in the current timepoint minus power
1031
    provided in the previous timepoint), adjusted for any reserve provision
1032
    in the current and previous timepoint, cannot exceed a prespecified
1033
    ramp rate (expressed as fraction of capacity)
1034
    Two components:
1035
    1) Ramp_Down_Shutdown_MW (see Ramp_Down_On_to_Off_Constraint above):
1036
    If we are turning generators off, we will allow the ramp of
1037
    going from minimum stable level to 0 + some additional ramping from
1038
    above minimum stable level
1039
    2) Ramp_Down_When_On_MW (see Ramp_Down_When_On_Constraint and
1040
    Ramp_Down_When_On_Headroom_Constraint above):
1041
    Units still committed in the current timepoint could have ramped down
1042
    at a certain rate since the previous timepoint
1043
    """
1044
    if check_if_boundary_type_and_first_timepoint(
1✔
1045
        mod=mod,
1046
        tmp=tmp,
1047
        balancing_type=mod.balancing_type_project[g],
1048
        boundary_type="linear",
1049
    ):
1050
        return Constraint.Skip
1✔
1051
    else:
1052
        if check_if_boundary_type_and_first_timepoint(
1✔
1053
            mod=mod,
1054
            tmp=tmp,
1055
            balancing_type=mod.balancing_type_project[g],
1056
            boundary_type="linked",
1057
        ):
1058
            prev_tmp_hrs_in_tmp = mod.hrs_in_linked_tmp[0]
×
1059
            prev_tmp_power = mod.gen_commit_cap_linked_power[g, 0]
×
1060
            prev_tmp_upwards_reserves = mod.gen_commit_cap_linked_upwards_reserves[g, 0]
×
1061
        else:
1062
            prev_tmp_hrs_in_tmp = mod.hrs_in_tmp[
1✔
1063
                mod.prev_tmp[tmp, mod.balancing_type_project[g]]
1064
            ]
1065
            prev_tmp_power = mod.GenCommitCap_Provide_Power_MW[
1✔
1066
                g, mod.prev_tmp[tmp, mod.balancing_type_project[g]]
1067
            ]
1068
            prev_tmp_upwards_reserves = mod.GenCommitCap_Upwards_Reserves_MW[
1✔
1069
                g, mod.prev_tmp[tmp, mod.balancing_type_project[g]]
1070
            ]
1071

1072
        # If ramp rate limits, adjusted for timepoint duration, allow you to
1073
        # shut down the full capacity and ramp down the full operable range
1074
        # between timepoints, constraint won't bind, so skip
1075
        if mod.gen_commit_cap_shutdown_plus_ramp_down_rate[
1✔
1076
            g
1077
        ] * 60 * prev_tmp_hrs_in_tmp >= 1 and mod.gen_commit_cap_ramp_down_when_on_rate[
1078
            g
1079
        ] * 60 * prev_tmp_hrs_in_tmp >= (
1080
            1 - mod.gen_commit_cap_min_stable_level_fraction[g]
1081
        ):
1082
            return Constraint.Skip
1✔
1083
        else:
1084
            return (
1✔
1085
                mod.GenCommitCap_Provide_Power_MW[g, tmp]
1086
                - mod.GenCommitCap_Downwards_Reserves_MW[g, tmp]
1087
            ) - (
1088
                prev_tmp_power + prev_tmp_upwards_reserves
1089
            ) >= mod.Ramp_Down_Shutdown_MW[
1090
                g, tmp
1091
            ] + mod.Ramp_Down_When_On_MW[
1092
                g, tmp
1093
            ]
1094

1095

1096
def startup_constraint_rule(mod, g, tmp):
1✔
1097
    """
1098
    **Constraint Name**: GenCommitCap_Startup_Constraint
1099
    **Enforced Over**: GEN_COMMIT_CAP_OPR_TMPS
1100

1101
    When units are shut off, GenCommitCap_Startup_MW will be 0 (as it
1102
    has to be non-negative)
1103
    """
1104
    if check_if_boundary_type_and_first_timepoint(
1✔
1105
        mod=mod,
1106
        tmp=tmp,
1107
        balancing_type=mod.balancing_type_project[g],
1108
        boundary_type="linear",
1109
    ):
1110
        return Constraint.Skip
1✔
1111
    else:
1112
        if check_if_boundary_type_and_first_timepoint(
1✔
1113
            mod=mod,
1114
            tmp=tmp,
1115
            balancing_type=mod.balancing_type_project[g],
1116
            boundary_type="linked",
1117
        ):
1118
            prev_tmp_commit_capacity = mod.gen_commit_cap_linked_commit_capacity[g, 0]
×
1119
        else:
1120
            prev_tmp_commit_capacity = mod.Commit_Capacity_MW[
1✔
1121
                g, mod.prev_tmp[tmp, mod.balancing_type_project[g]]
1122
            ]
1123
        return (
1✔
1124
            mod.GenCommitCap_Startup_MW[g, tmp]
1125
            >= mod.Commit_Capacity_MW[g, tmp] - prev_tmp_commit_capacity
1126
        )
1127

1128

1129
def shutdown_constraint_rule(mod, g, tmp):
1✔
1130
    """
1131
    **Constraint Name**: GenCommitCap_Shutdown_Constraint
1132
    **Enforced Over**: GEN_COMMIT_CAP_OPR_TMPS
1133

1134
    When units are turned on, GenCommitCap_Shutdown_MW will be 0 (as it
1135
    has to be non-negative)
1136
    """
1137
    if check_if_boundary_type_and_first_timepoint(
1✔
1138
        mod=mod,
1139
        tmp=tmp,
1140
        balancing_type=mod.balancing_type_project[g],
1141
        boundary_type="linear",
1142
    ):
1143
        return Constraint.Skip
1✔
1144
    else:
1145
        if check_if_boundary_type_and_first_timepoint(
1✔
1146
            mod=mod,
1147
            tmp=tmp,
1148
            balancing_type=mod.balancing_type_project[g],
1149
            boundary_type="linked",
1150
        ):
1151
            prev_tmp_commit_capacity = mod.gen_commit_cap_linked_commit_capacity[g, 0]
×
1152
        else:
1153
            prev_tmp_commit_capacity = mod.Commit_Capacity_MW[
1✔
1154
                g, mod.prev_tmp[tmp, mod.balancing_type_project[g]]
1155
            ]
1156
        return (
1✔
1157
            mod.GenCommitCap_Shutdown_MW[g, tmp]
1158
            >= prev_tmp_commit_capacity - mod.Commit_Capacity_MW[g, tmp]
1159
        )
1160

1161

1162
def min_up_time_constraint_rule(mod, g, tmp):
1✔
1163
    """
1164
    **Constraint Name**: GenCommitCap_Min_Up_Time_Constraint
1165
    **Enforced Over**: GEN_COMMIT_CAP_OPR_TMPS
1166

1167
    When units are started, they have to stay on for a minimum number
1168
    of hours described by the gen_commit_cap_min_up_time_hours parameter.
1169
    The constraint is enforced by ensuring that the online capacity
1170
    (committed capacity) is at least as large as the amount of capacity
1171
    that was started within min down time hours.
1172

1173
    We ensure capacity turned on less than the minimum up time ago is
1174
    still on in the current timepoint *tmp* by checking how much capacity
1175
    was turned on in each 'relevant' timepoint (i.e. a timepoint that
1176
    begins more than or equal to gen_commit_cap_min_up_time_hours ago
1177
    relative to the start of timepoint *tmp*) and then summing those
1178
    capacities.
1179
    """
1180
    relevant_tmps, relevant_linked_timepoints = determine_relevant_timepoints(
1✔
1181
        mod, g, tmp, mod.gen_commit_cap_min_up_time_hours[g]
1182
    )
1183

1184
    # If only the current timepoint is determined to be relevant (and there
1185
    # are no linked timepoints), this constraint is redundant (it will
1186
    # simplify to Commit_Capacity_MW[g, prev_tmp[tmp]} >= 0)
1187
    # This also takes care of the first timepoint in a linear horizon
1188
    # setting, which has only *tmp* in the list of relevant timepoints
1189
    if relevant_tmps == [tmp] and not relevant_linked_timepoints:
1✔
1190
        return Constraint.Skip
1✔
1191
    # Otherwise, we must have at least as much capacity committed as was
1192
    # started up in the relevant timepoints
1193
    else:
1194
        capacity_turned_on_min_up_time_or_less_hours_ago = sum(
1✔
1195
            mod.GenCommitCap_Startup_MW[g, tp] for tp in relevant_tmps
1196
        ) + sum(
1197
            mod.gen_commit_cap_linked_startup[g, ltp]
1198
            for ltp in relevant_linked_timepoints
1199
        )
1200

1201
        return (
1✔
1202
            mod.Commit_Capacity_MW[g, tmp]
1203
            >= capacity_turned_on_min_up_time_or_less_hours_ago
1204
        )
1205

1206

1207
def min_down_time_constraint_rule(mod, g, tmp):
1✔
1208
    """
1209
    **Constraint Name**: GenCommitCap_Min_Down_Time_Constraint
1210
    **Enforced Over**: GEN_COMMIT_CAP_OPR_TMPS
1211

1212
    When units are stopped, they have to stay off for a minimum number
1213
    of hours described by the gen_commit_cap_min_down_time_hours parameter.
1214
    The constraint is enforced by ensuring that the offline capacity
1215
    (available capacity minus committed capacity) is at least as large
1216
    as the amount of capacity that was stopped within min down time hours.
1217

1218
    We ensure capacity turned off less than the minimum down time ago is
1219
    still off in the current timepoint *tmp* by checking how much capacity
1220
    was turned off in each 'relevant' timepoint (i.e. a timepoint that
1221
    begins more than or equal to gen_commit_cap_min_down_time_hours ago
1222
    relative to the start of timepoint *tmp*) and then summing those
1223
    capacities.
1224
    """
1225

1226
    relevant_tmps, relevant_linked_timepoints = determine_relevant_timepoints(
1✔
1227
        mod, g, tmp, mod.gen_commit_cap_min_down_time_hours[g]
1228
    )
1229

1230
    capacity_turned_off_min_down_time_or_less_hours_ago = sum(
1✔
1231
        mod.GenCommitCap_Shutdown_MW[g, tp] for tp in relevant_tmps
1232
    ) + sum(
1233
        mod.gen_commit_cap_linked_shutdown[g, ltp] for ltp in relevant_linked_timepoints
1234
    )
1235

1236
    # If only the current timepoint is determined to be relevant (and there
1237
    # are no linked timepoints), this constraint is redundant (it will
1238
    # simplify to Commit_Capacity_MW[g, prev_tmp[tmp]} >= 0)
1239
    # This also takes care of the first timepoint in a linear horizon
1240
    # setting, which has only *tmp* in the list of relevant timepoints
1241
    if relevant_tmps == [tmp] and not relevant_linked_timepoints:
1✔
1242
        return Constraint.Skip
1✔
1243
    # Otherwise, we must have at least as much capacity off as was shut
1244
    # down in the relevant timepoints
1245
    else:
1246
        return (
1✔
1247
            mod.Capacity_MW[g, mod.period[tmp]] * mod.Availability_Derate[g, tmp]
1248
            - mod.Commit_Capacity_MW[g, tmp]
1249
            >= capacity_turned_off_min_down_time_or_less_hours_ago
1250
        )
1251

1252

1253
# Operational Type Methods
1254
###############################################################################
1255
def power_provision_rule(mod, g, tmp):
1✔
1256
    """
1257
    Power provision for dispatchable-capacity-commit generators is a
1258
    variable constrained to be between the minimum stable level (defined as
1259
    a fraction of committed capacity) and the committed capacity.
1260
    """
1261
    return (
1✔
1262
        mod.GenCommitCap_Provide_Power_MW[g, tmp]
1263
        - mod.GenCommitCap_Auxiliary_Consumption_MW[g, tmp]
1264
    )
1265

1266

1267
def commitment_rule(mod, g, tmp):
1✔
1268
    """
1269
    Number of units committed is the committed capacity divided by the unit
1270
    size
1271
    """
1272
    return mod.Commit_Capacity_MW[g, tmp]
1✔
1273

1274

1275
def online_capacity_rule(mod, g, tmp):
1✔
1276
    """
1277
    Capacity online in each timepoint
1278
    """
1279
    return mod.Commit_Capacity_MW[g, tmp]
1✔
1280

1281

1282
def fuel_burn_by_ll_rule(mod, g, tmp, s):
1✔
1283
    """ """
1284
    return (
1✔
1285
        mod.fuel_burn_slope_mmbtu_per_mwh[g, mod.period[tmp], s]
1286
        * mod.GenCommitCap_Provide_Power_MW[g, tmp]
1287
        + mod.fuel_burn_intercept_mmbtu_per_mw_hr[g, mod.period[tmp], s]
1288
        * mod.Commit_Capacity_MW[g, tmp]
1289
    )
1290

1291

1292
def variable_om_cost_rule(mod, g, tmp):
1✔
1293
    """
1294
    Variable O&M cost has two components which are additive:
1295
    1. A fixed variable O&M rate (cost/MWh) that doesn't change with loading
1296
       levels: :code:`gen_commit_cap_variable_om_cost_per_mwh`.
1297
    2. A variable variable O&M rate that changes with the loading level,
1298
       similar to the heat rates. The idea is to represent higher variable cost
1299
       rates at lower loading levels. This is captured in the
1300
       :code:`GenCommitCap_Variable_OM_Cost_By_LL` decision variable. If no
1301
       variable O&M curve inputs are provided, this component will be zero.
1302

1303
    Most users will only use the first component, which is specified in the
1304
    operational characteristics table.  Only operational types with
1305
    commitment decisions can have the second component.
1306

1307
    We need to explicitly have the op type method here because of auxiliary
1308
    consumption. The default method takes Project_Power_Provision_MW multiplied by
1309
    the variable cost, and Project_Power_Provision_MW is equal to Provide_Power_MW
1310
    minus the auxiliary consumption. The variable cost should be applied to
1311
    the gross power.
1312
    """
1313
    return mod.GenCommitCap_Provide_Power_MW[g, tmp] * mod.variable_om_cost_per_mwh[g]
1✔
1314

1315

1316
def variable_om_by_period_cost_rule(mod, prj, tmp):
1✔
1317
    """ """
1318
    return (
×
1319
        mod.GenCommitCap_Provide_Power_MW[prj, tmp]
1320
        * mod.variable_om_cost_per_mwh_by_period[prj, mod.period[tmp]]
1321
    )
1322

1323

1324
def variable_om_by_timepoint_cost_rule(mod, prj, tmp):
1✔
1325
    """ """
1326
    return (
×
1327
        mod.GenCommitCap_Provide_Power_MW[prj, tmp]
1328
        * mod.variable_om_cost_per_mwh_by_timepoint[prj, tmp]
1329
    )
1330

1331

1332
def variable_om_cost_by_ll_rule(mod, g, tmp, s):
1✔
1333
    """
1334
    Variable O&M cost has two components which are additive:
1335
    1. A fixed variable O&M rate (cost/MWh) that doesn't change with loading
1336
       levels: :code:`gen_commit_cap_variable_om_cost_per_mwh`.
1337
    2. A variable variable O&M rate that changes with the loading level,
1338
       similar to the heat rates. The idea is to represent higher variable cost
1339
       rates at lower loading levels. This is captured in the
1340
       :code:`GenCommitCap_Variable_OM_Cost_By_LL` decision variable. If no
1341
       variable O&M curve inputs are provided, this component will be zero.
1342

1343
    Most users will only use the first component, which is specified in the
1344
    operational characteristics table.  Only operational types with
1345
    commitment decisions can have the second component.
1346
    """
1347
    return (
×
1348
        mod.vom_slope_cost_per_mwh[g, mod.period[tmp], s]
1349
        * mod.GenCommitCap_Provide_Power_MW[g, tmp]
1350
        + mod.vom_intercept_cost_per_mw_hr[g, mod.period[tmp], s]
1351
        * mod.Commit_Capacity_MW[g, tmp]
1352
    )
1353

1354

1355
def startup_cost_simple_rule(mod, g, tmp):
1✔
1356
    """
1357
    Startup costs are applied in each timepoint based on the amount of capacity
1358
    (in MW) that is started up in that timepoint and the startup cost
1359
    parameter.
1360
    """
1361
    return mod.GenCommitCap_Startup_MW[g, tmp] * mod.startup_cost_per_mw[g]
1✔
1362

1363

1364
def shutdown_cost_rule(mod, g, tmp):
1✔
1365
    """
1366
    Shutdown costs are applied in each timepoint based on the amount of
1367
    capacity (in Mw) that is shut down in that timepoint and the shutdown
1368
    cost parameter.
1369
    """
1370
    return mod.GenCommitCap_Shutdown_MW[g, tmp] * mod.shutdown_cost_per_mw[g]
1✔
1371

1372

1373
def startup_fuel_burn_rule(mod, g, tmp):
1✔
1374
    """
1375
    Startup fuel burn is applied in each timepoint based on the amount of
1376
    capacity (in MW) that is started up in that timepoint and the startup
1377
    fuel parameter.
1378
    """
1379
    return mod.GenCommitCap_Startup_MW[g, tmp] * mod.startup_fuel_mmbtu_per_mw[g]
1✔
1380

1381

1382
def power_delta_rule(mod, g, tmp):
1✔
1383
    """
1384
    This rule is only used in tuning costs, so fine to skip for linked
1385
    horizon's first timepoint.
1386
    """
UNCOV
1387
    if check_if_boundary_type_and_first_timepoint(
×
1388
        mod=mod,
1389
        tmp=tmp,
1390
        balancing_type=mod.balancing_type_project[g],
1391
        boundary_type="linear",
1392
    ) or check_if_boundary_type_and_first_timepoint(
1393
        mod=mod,
1394
        tmp=tmp,
1395
        balancing_type=mod.balancing_type_project[g],
1396
        boundary_type="linked",
1397
    ):
UNCOV
1398
        pass
×
1399
    else:
UNCOV
1400
        return (
×
1401
            mod.GenCommitCap_Provide_Power_MW[g, tmp]
1402
            - mod.GenCommitCap_Provide_Power_MW[
1403
                g, mod.prev_tmp[tmp, mod.balancing_type_project[g]]
1404
            ]
1405
        )
1406

1407

1408
def capacity_providing_inertia_rule(mod, g, tmp):
1✔
1409
    """
1410
    Capacity providing inertia for GEN_VAR project is equal to the online
1411
    capacity
1412
    """
1413
    return mod.Commit_Capacity_MW[g, tmp]
1✔
1414

1415

1416
def fix_commitment(mod, g, tmp):
1✔
1417
    """
1418
    Fix committed capacity based on number of committed units and unit size
1419
    """
1420
    mod.Commit_Capacity_MW[g, tmp] = mod.fixed_commitment[
1✔
1421
        g, mod.prev_stage_tmp_map[tmp]
1422
    ]
1423
    mod.Commit_Capacity_MW[g, tmp].fixed = True
1✔
1424

1425

1426
# Input-Output
1427
###############################################################################
1428
def load_model_data(
1✔
1429
    mod,
1430
    d,
1431
    data_portal,
1432
    scenario_directory,
1433
    weather_iteration,
1434
    hydro_iteration,
1435
    availability_iteration,
1436
    subproblem,
1437
    stage,
1438
):
1439
    """
1440

1441
    :param mod:
1442
    :param data_portal:
1443
    :param scenario_directory:
1444
    :param subproblem:
1445
    :param stage:
1446
    :return:
1447
    """
1448

1449
    # Load data from projects.tab and get the list of projects of this type
1450
    projects = load_optype_model_data(
1✔
1451
        mod=mod,
1452
        data_portal=data_portal,
1453
        scenario_directory=scenario_directory,
1454
        weather_iteration=weather_iteration,
1455
        hydro_iteration=hydro_iteration,
1456
        availability_iteration=availability_iteration,
1457
        subproblem=subproblem,
1458
        stage=stage,
1459
        op_type="gen_commit_cap",
1460
    )
1461

1462
    # Linked timepoint params
1463
    linked_inputs_filename = os.path.join(
1✔
1464
        scenario_directory,
1465
        weather_iteration,
1466
        hydro_iteration,
1467
        availability_iteration,
1468
        subproblem,
1469
        stage,
1470
        "inputs",
1471
        "gen_commit_cap_linked_timepoint_params.tab",
1472
    )
1473
    if os.path.exists(linked_inputs_filename):
1✔
1474
        data_portal.load(
×
1475
            filename=linked_inputs_filename,
1476
            index=mod.GEN_COMMIT_CAP_LINKED_TMPS,
1477
            param=(
1478
                mod.gen_commit_cap_linked_commit_capacity,
1479
                mod.gen_commit_cap_linked_power,
1480
                mod.gen_commit_cap_linked_upwards_reserves,
1481
                mod.gen_commit_cap_linked_downwards_reserves,
1482
                mod.gen_commit_cap_linked_startup,
1483
                mod.gen_commit_cap_linked_shutdown,
1484
            ),
1485
        )
1486

1487

1488
def add_to_prj_tmp_results(mod):
1✔
1489
    results_columns = [
1✔
1490
        "gross_power_mw",
1491
        "auxiliary_consumption_mw",
1492
        "net_power_mw",
1493
        "committed_mw",
1494
        "committed_units",
1495
    ]
1496
    data = [
1✔
1497
        [
1498
            prj,
1499
            tmp,
1500
            value(mod.GenCommitCap_Provide_Power_MW[prj, tmp]),
1501
            value(mod.GenCommitCap_Auxiliary_Consumption_MW[prj, tmp]),
1502
            value(mod.GenCommitCap_Provide_Power_MW[prj, tmp])
1503
            - value(mod.GenCommitCap_Auxiliary_Consumption_MW[prj, tmp]),
1504
            value(mod.Commit_Capacity_MW[prj, tmp]),
1505
            value(mod.Commit_Capacity_MW[prj, tmp])
1506
            / mod.gen_commit_cap_unit_size_mw[prj],
1507
        ]
1508
        for (prj, tmp) in mod.GEN_COMMIT_CAP_OPR_TMPS
1509
    ]
1510

1511
    optype_dispatch_df = create_results_df(
1✔
1512
        index_columns=["project", "timepoint"],
1513
        results_columns=results_columns,
1514
        data=data,
1515
    )
1516

1517
    return results_columns, optype_dispatch_df
1✔
1518

1519

1520
def export_results(
1✔
1521
    mod,
1522
    d,
1523
    scenario_directory,
1524
    weather_iteration,
1525
    hydro_iteration,
1526
    availability_iteration,
1527
    subproblem,
1528
    stage,
1529
):
1530
    """
1531

1532
    :param scenario_directory:
1533
    :param subproblem:
1534
    :param stage:
1535
    :param mod:
1536
    :param d:
1537
    :return:
1538
    """
1539

1540
    # Dispatch results added to project_timepoint.csv via add_to_prj_tmp_results()
1541

1542
    # If there's a linked_subproblems_map CSV file, check which of the
1543
    # current subproblem TMPS we should export results for to link to the
1544
    # next subproblem
1545
    tmps_to_link, tmp_linked_tmp_dict = check_for_tmps_to_link(
1✔
1546
        scenario_directory=scenario_directory, subproblem=subproblem, stage=stage
1547
    )
1548

1549
    # If the list of timepoints to link is not empty, write the linked
1550
    # timepoint results for this module in the next subproblem's input
1551
    # directory
1552
    if tmps_to_link:
1✔
1553
        next_subproblem = str(int(subproblem) + 1)
×
1554

1555
        # Export params by project and timepoint
1556
        with open(
×
1557
            os.path.join(
1558
                scenario_directory,
1559
                next_subproblem,
1560
                stage,
1561
                "inputs",
1562
                "gen_commit_cap_linked_timepoint_params.tab",
1563
            ),
1564
            "w",
1565
            newline="",
1566
        ) as f:
1567
            writer = csv.writer(f, delimiter="\t", lineterminator="\n")
×
1568
            writer.writerow(
×
1569
                [
1570
                    "project",
1571
                    "linked_timepoint",
1572
                    "linked_commitment",
1573
                    "linked_provide_power",
1574
                    "linked_upward_reserves",
1575
                    "linked_downward_reserves",
1576
                    "linked_startup",
1577
                    "linked_shutdown",
1578
                ]
1579
            )
1580
            for p, tmp in sorted(mod.GEN_COMMIT_CAP_OPR_TMPS):
×
1581
                if tmp in tmps_to_link:
×
1582
                    writer.writerow(
×
1583
                        [
1584
                            p,
1585
                            tmp_linked_tmp_dict[tmp],
1586
                            max(value(mod.Commit_Capacity_MW[p, tmp]), 0),
1587
                            max(value(mod.GenCommitCap_Provide_Power_MW[p, tmp]), 0),
1588
                            max(value(mod.GenCommitCap_Upwards_Reserves_MW[p, tmp]), 0),
1589
                            max(
1590
                                value(mod.GenCommitCap_Downwards_Reserves_MW[p, tmp]), 0
1591
                            ),
1592
                            max(value(mod.GenCommitCap_Startup_MW[p, tmp]), 0),
1593
                            max(value(mod.GenCommitCap_Shutdown_MW[p, tmp]), 0),
1594
                        ]
1595
                    )
1596

1597

1598
# Validation
1599
###############################################################################
1600

1601

1602
def validate_inputs(
1✔
1603
    scenario_id,
1604
    subscenarios,
1605
    weather_iteration,
1606
    hydro_iteration,
1607
    availability_iteration,
1608
    subproblem,
1609
    stage,
1610
    conn,
1611
):
1612
    """
1613
    Get inputs from database and validate the inputs
1614
    :param subscenarios: SubScenarios object with all subscenario info
1615
    :param subproblem:
1616
    :param stage:
1617
    :param conn: database connection
1618
    :return:
1619
    """
1620

1621
    # Validate operational chars table inputs
1622
    validate_opchars(
1✔
1623
        scenario_id,
1624
        subscenarios,
1625
        weather_iteration,
1626
        hydro_iteration,
1627
        availability_iteration,
1628
        subproblem,
1629
        stage,
1630
        conn,
1631
        "gen_commit_cap",
1632
    )
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE TRIAL · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc