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

blue-marble / gridpath / 16356053796

17 Jul 2025 09:09PM UTC coverage: 88.961%. Remained the same
16356053796

push

github

anamileva
Curtailment cost inputs for subproblem's periods only

27343 of 30736 relevant lines covered (88.96%)

2.67 hits per line

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

86.88
/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

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

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

81

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

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

113
    |
114

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

135
    |
136

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

201
    |
202

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

243
    |
244

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

306
    |
307

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

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

415
    """
416

417
    # Sets
418
    ###########################################################################
419
    m.GEN_COMMIT_CAP = Set(
3✔
420
        within=m.PROJECTS,
421
        initialize=lambda mod: subset_init_by_param_value(
422
            mod, "PROJECTS", "operational_type", "gen_commit_cap"
423
        ),
424
    )
425

426
    m.GEN_COMMIT_CAP_OPR_TMPS = Set(
3✔
427
        dimen=2,
428
        within=m.PRJ_OPR_TMPS,
429
        initialize=lambda mod: subset_init_by_set_membership(
430
            mod=mod, superset="PRJ_OPR_TMPS", index=0, membership_set=mod.GEN_COMMIT_CAP
431
        ),
432
    )
433

434
    m.GEN_COMMIT_CAP_LINKED_TMPS = Set(dimen=2)
3✔
435

436
    # Required Params
437
    ###########################################################################
438
    m.gen_commit_cap_unit_size_mw = Param(m.GEN_COMMIT_CAP, within=NonNegativeReals)
3✔
439
    m.gen_commit_cap_min_stable_level_fraction = Param(
3✔
440
        m.GEN_COMMIT_CAP, within=PercentFraction
441
    )
442

443
    # Optional Params
444
    ###########################################################################
445

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

465
    m.gen_commit_cap_aux_consumption_frac_capacity = Param(
3✔
466
        m.GEN_COMMIT_CAP, within=PercentFraction, default=0
467
    )
468

469
    m.gen_commit_cap_aux_consumption_frac_power = Param(
3✔
470
        m.GEN_COMMIT_CAP, within=PercentFraction, default=0
471
    )
472

473
    # Linked Params
474
    ###########################################################################
475

476
    m.gen_commit_cap_linked_commit_capacity = Param(
3✔
477
        m.GEN_COMMIT_CAP_LINKED_TMPS, within=NonNegativeReals
478
    )
479

480
    m.gen_commit_cap_linked_power = Param(
3✔
481
        m.GEN_COMMIT_CAP_LINKED_TMPS, within=NonNegativeReals
482
    )
483

484
    m.gen_commit_cap_linked_upwards_reserves = Param(
3✔
485
        m.GEN_COMMIT_CAP_LINKED_TMPS, within=NonNegativeReals
486
    )
487

488
    m.gen_commit_cap_linked_downwards_reserves = Param(
3✔
489
        m.GEN_COMMIT_CAP_LINKED_TMPS, within=NonNegativeReals
490
    )
491

492
    m.gen_commit_cap_linked_startup = Param(
3✔
493
        m.GEN_COMMIT_CAP_LINKED_TMPS, within=NonNegativeReals
494
    )
495

496
    m.gen_commit_cap_linked_shutdown = Param(
3✔
497
        m.GEN_COMMIT_CAP_LINKED_TMPS, within=NonNegativeReals
498
    )
499

500
    # Variables
501
    ###########################################################################
502
    m.GenCommitCap_Provide_Power_MW = Var(
3✔
503
        m.GEN_COMMIT_CAP_OPR_TMPS, within=NonNegativeReals
504
    )
505
    m.Commit_Capacity_MW = Var(m.GEN_COMMIT_CAP_OPR_TMPS, within=NonNegativeReals)
3✔
506

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

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

525
    m.Ramp_Up_When_On_MW = Var(m.GEN_COMMIT_CAP_OPR_TMPS, within=NonNegativeReals)
3✔
526
    m.Ramp_Down_When_On_MW = Var(m.GEN_COMMIT_CAP_OPR_TMPS, within=NonPositiveReals)
3✔
527

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

533
    # Expressions
534
    ###########################################################################
535
    # TODO: the reserve rules are the same in all modules, so should be
536
    #  consolidated
537
    def upwards_reserve_rule(mod, g, tmp):
3✔
538
        return sum(getattr(mod, c)[g, tmp] for c in getattr(d, headroom_variables)[g])
3✔
539

540
    m.GenCommitCap_Upwards_Reserves_MW = Expression(
3✔
541
        m.GEN_COMMIT_CAP_OPR_TMPS, rule=upwards_reserve_rule
542
    )
543

544
    def downwards_reserve_rule(mod, g, tmp):
3✔
545
        return sum(getattr(mod, c)[g, tmp] for c in getattr(d, footroom_variables)[g])
3✔
546

547
    m.GenCommitCap_Downwards_Reserves_MW = Expression(
3✔
548
        m.GEN_COMMIT_CAP_OPR_TMPS, rule=downwards_reserve_rule
549
    )
550

551
    m.GenCommitCap_Auxiliary_Consumption_MW = Expression(
3✔
552
        m.GEN_COMMIT_CAP_OPR_TMPS, rule=auxiliary_consumption_rule
553
    )
554

555
    # Constraints
556
    ###########################################################################
557

558
    # Commitment and power
559
    m.Commit_Capacity_Constraint = Constraint(
3✔
560
        m.GEN_COMMIT_CAP_OPR_TMPS, rule=commit_capacity_constraint_rule
561
    )
562

563
    m.GenCommitCap_Max_Power_Constraint = Constraint(
3✔
564
        m.GEN_COMMIT_CAP_OPR_TMPS, rule=max_power_rule
565
    )
566

567
    m.GenCommitCap_Min_Power_Constraint = Constraint(
3✔
568
        m.GEN_COMMIT_CAP_OPR_TMPS, rule=min_power_rule
569
    )
570

571
    # Ramping
572
    m.Ramp_Up_Off_to_On_Constraint = Constraint(
3✔
573
        m.GEN_COMMIT_CAP_OPR_TMPS, rule=ramp_up_off_to_on_constraint_rule
574
    )
575

576
    m.Ramp_Up_When_On_Constraint = Constraint(
3✔
577
        m.GEN_COMMIT_CAP_OPR_TMPS, rule=ramp_up_on_to_on_constraint_rule
578
    )
579

580
    m.Ramp_Up_When_On_Headroom_Constraint = Constraint(
3✔
581
        m.GEN_COMMIT_CAP_OPR_TMPS, rule=ramp_up_on_to_on_headroom_constraint_rule
582
    )
583

584
    m.GenCommitCap_Ramp_Up_Constraint = Constraint(
3✔
585
        m.GEN_COMMIT_CAP_OPR_TMPS, rule=ramp_up_constraint_rule
586
    )
587

588
    m.Ramp_Down_On_to_Off_Constraint = Constraint(
3✔
589
        m.GEN_COMMIT_CAP_OPR_TMPS, rule=ramp_down_on_to_off_constraint_rule
590
    )
591

592
    m.Ramp_Down_When_On_Constraint = Constraint(
3✔
593
        m.GEN_COMMIT_CAP_OPR_TMPS, rule=ramp_down_on_to_on_constraint_rule
594
    )
595

596
    m.Ramp_Down_When_On_Headroom_Constraint = Constraint(
3✔
597
        m.GEN_COMMIT_CAP_OPR_TMPS, rule=ramp_down_on_to_on_headroom_constraint_rule
598
    )
599

600
    m.GenCommitCap_Ramp_Down_Constraint = Constraint(
3✔
601
        m.GEN_COMMIT_CAP_OPR_TMPS, rule=ramp_down_constraint_rule
602
    )
603

604
    # Min up and down time
605
    m.GenCommitCap_Startup_Constraint = Constraint(
3✔
606
        m.GEN_COMMIT_CAP_OPR_TMPS, rule=startup_constraint_rule
607
    )
608

609
    m.GenCommitCap_Shutdown_Constraint = Constraint(
3✔
610
        m.GEN_COMMIT_CAP_OPR_TMPS, rule=shutdown_constraint_rule
611
    )
612

613
    m.GenCommitCap_Min_Up_Time_Constraint = Constraint(
3✔
614
        m.GEN_COMMIT_CAP_OPR_TMPS, rule=min_up_time_constraint_rule
615
    )
616

617
    m.GenCommitCap_Min_Down_Time_Constraint = Constraint(
3✔
618
        m.GEN_COMMIT_CAP_OPR_TMPS, rule=min_down_time_constraint_rule
619
    )
620

621

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

636

637
# Constraint Formulation Rules
638
###############################################################################
639

640

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

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

654

655
def max_power_rule(mod, g, tmp):
3✔
656
    """
657
    **Constraint Name**: GenCommitCap_Max_Power_Constraint
658
    **Enforced Over**: GEN_COMMIT_CAP_OPR_TMPS
659

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

668

669
def min_power_rule(mod, g, tmp):
3✔
670
    """
671
    **Constraint Name**: GenCommitCap_Min_Power_Constraint
672
    **Enforced Over**: GEN_COMMIT_CAP_OPR_TMPS
673

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

683

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

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

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

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

731

732
def ramp_up_on_to_on_constraint_rule(mod, g, tmp):
3✔
733
    """
734
    **Constraint Name**: Ramp_Up_When_On_Constraint
735
    **Enforced Over**: GEN_COMMIT_CAP_OPR_TMPS
736

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

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

789

790
def ramp_up_on_to_on_headroom_constraint_rule(mod, g, tmp):
3✔
791
    """
792
    **Constraint Name**: Ramp_Up_When_On_Headroom_Constraint
793
    **Enforced Over**: GEN_COMMIT_CAP_OPR_TMPS
794

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

836

837
def ramp_up_constraint_rule(mod, g, tmp):
3✔
838
    """
839
    **Constraint Name**: GenCommitCap_Ramp_Up_Constraint
840
    **Enforced Over**: GEN_COMMIT_CAP_OPR_TMPS
841

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

910

911
def ramp_down_on_to_off_constraint_rule(mod, g, tmp):
3✔
912
    """
913
    **Constraint Name**: Ramp_Down_On_to_Off_Constraint
914
    **Enforced Over**: GEN_COMMIT_CAP_OPR_TMPS
915

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

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

957

958
def ramp_down_on_to_on_constraint_rule(mod, g, tmp):
3✔
959
    """
960
    **Constraint Name**: Ramp_Down_When_On_Constraint
961
    **Enforced Over**: GEN_COMMIT_CAP_OPR_TMPS
962

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

994

995
def ramp_down_on_to_on_headroom_constraint_rule(mod, g, tmp):
3✔
996
    """
997
    **Constraint Name**: Ramp_Down_When_On_Headroom_Constraint
998
    **Enforced Over**: GEN_COMMIT_CAP_OPR_TMPS
999

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

1022

1023
def ramp_down_constraint_rule(mod, g, tmp):
3✔
1024
    """
1025
    **Constraint Name**: GenCommitCap_Ramp_Down_Constraint
1026
    **Enforced Over**: GEN_COMMIT_CAP_OPR_TMPS
1027

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

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

1093

1094
def startup_constraint_rule(mod, g, tmp):
3✔
1095
    """
1096
    **Constraint Name**: GenCommitCap_Startup_Constraint
1097
    **Enforced Over**: GEN_COMMIT_CAP_OPR_TMPS
1098

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

1126

1127
def shutdown_constraint_rule(mod, g, tmp):
3✔
1128
    """
1129
    **Constraint Name**: GenCommitCap_Shutdown_Constraint
1130
    **Enforced Over**: GEN_COMMIT_CAP_OPR_TMPS
1131

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

1159

1160
def min_up_time_constraint_rule(mod, g, tmp):
3✔
1161
    """
1162
    **Constraint Name**: GenCommitCap_Min_Up_Time_Constraint
1163
    **Enforced Over**: GEN_COMMIT_CAP_OPR_TMPS
1164

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

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

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

1199
        return (
3✔
1200
            mod.Commit_Capacity_MW[g, tmp]
1201
            >= capacity_turned_on_min_up_time_or_less_hours_ago
1202
        )
1203

1204

1205
def min_down_time_constraint_rule(mod, g, tmp):
3✔
1206
    """
1207
    **Constraint Name**: GenCommitCap_Min_Down_Time_Constraint
1208
    **Enforced Over**: GEN_COMMIT_CAP_OPR_TMPS
1209

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

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

1224
    relevant_tmps, relevant_linked_timepoints = determine_relevant_timepoints(
3✔
1225
        mod, g, tmp, mod.gen_commit_cap_min_down_time_hours[g]
1226
    )
1227

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

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

1250

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

1264

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

1272

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

1279

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

1289

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

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

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

1313

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

1321

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

1329

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

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

1352

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

1361

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

1370

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

1379

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

1405

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

1413

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

1423

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

1439
    :param mod:
1440
    :param data_portal:
1441
    :param scenario_directory:
1442
    :param subproblem:
1443
    :param stage:
1444
    :return:
1445
    """
1446

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

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

1485

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

1509
    optype_dispatch_df = create_results_df(
3✔
1510
        index_columns=["project", "timepoint"],
1511
        results_columns=results_columns,
1512
        data=data,
1513
    )
1514

1515
    return results_columns, optype_dispatch_df
3✔
1516

1517

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

1530
    :param scenario_directory:
1531
    :param subproblem:
1532
    :param stage:
1533
    :param mod:
1534
    :param d:
1535
    :return:
1536
    """
1537

1538
    # Dispatch results added to project_timepoint.csv via add_to_prj_tmp_results()
1539

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

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

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

1595

1596
# Validation
1597
###############################################################################
1598

1599

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

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

© 2026 Coveralls, Inc