• 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

69.7
/gridpath/project/availability/availability_types/continuous.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 *availability type* is formulated like the *binary* type except that
17
all binary decision variables are relaxed to be continuous with bounds
18
between 0 and 1. This can be useful to address computational difficulties
19
when modeling endogenous *project* availabilities.
20

21
"""
22

23
import csv
1✔
24
import os.path
1✔
25
from pyomo.environ import (
1✔
26
    Param,
27
    Set,
28
    Var,
29
    Constraint,
30
    Boolean,
31
    PercentFraction,
32
    value,
33
    NonNegativeReals,
34
)
35

36
from gridpath.auxiliary.auxiliary import cursor_to_df, subset_init_by_set_membership
1✔
37
from gridpath.auxiliary.validations import (
1✔
38
    write_validation_to_database,
39
    get_expected_dtypes,
40
    validate_dtypes,
41
    validate_missing_inputs,
42
    validate_column_monotonicity,
43
)
44
from gridpath.common_functions import create_results_df
1✔
45
from gridpath.project import PROJECT_TIMEPOINT_DF
1✔
46
from gridpath.project.operations.operational_types.common_functions import (
1✔
47
    determine_relevant_timepoints,
48
)
49
from gridpath.project.common_functions import (
1✔
50
    determine_project_subset,
51
    check_if_boundary_type_and_first_timepoint,
52
)
53

54

55
def add_model_components(
1✔
56
    m,
57
    d,
58
    scenario_directory,
59
    weather_iteration,
60
    hydro_iteration,
61
    availability_iteration,
62
    subproblem,
63
    stage,
64
):
65
    """
66
    The following Pyomo model components are defined in this module:
67

68
    +-------------------------------------------------------------------------+
69
    | Sets                                                                    |
70
    +=========================================================================+
71
    | | :code:`AVL_CONT`                                                      |
72
    |                                                                         |
73
    | The set of projects of the :code:`continuous` availability type.        |
74
    +-------------------------------------------------------------------------+
75
    | | :code:`AVL_CONT_OPR_PRDS`                                             |
76
    |                                                                         |
77
    | Two-dimensional set with projects of the :code:`continuous` availability|
78
    | type and their operational periods.                                     |
79
    +-------------------------------------------------------------------------+
80
    | | :code:`AVL_CONT_OPR_TMPS`                                             |
81
    |                                                                         |
82
    | Two-dimensional set with projects of the :code:`continuous` availability|
83
    | type and their operational timepoints.                                  |
84
    +-------------------------------------------------------------------------+
85

86
    |
87

88
    +-------------------------------------------------------------------------+
89
    | Required Input Params                                                   |
90
    +=========================================================================+
91
    | | :code:`avl_cont_unavl_hrs_per_prd`                                    |
92
    | | *Defined over*: :code:`AVL_CONT`                                      |
93
    | | *Within*: :code:`NonNegativeReals`                                    |
94
    |                                                                         |
95
    | The number of hours the project must be unavailable per period.         |
96
    +-------------------------------------------------------------------------+
97
    | | :code:`avl_cont_unavl_hrs_per_prd_req_exact`                          |
98
    | | *Defined over*: :code:`AVL_BIN`                                       |
99
    | | *Within*: :code:`Boolean`                                             |
100
    |                                                                         |
101
    | Require exactly the number of hours the project must be unavailable per |
102
    | period. If set to 0, the constraint is soft.                            |
103
    +-------------------------------------------------------------------------+
104
    | | :code:`avl_cont_min_unavl_hrs_per_event`                              |
105
    | | *Defined over*: :code:`AVL_CONT`                                      |
106
    | | *Within*: :code:`NonNegativeReals`                                    |
107
    |                                                                         |
108
    | The minimum number of hours an unavailability event should last for.    |
109
    +-------------------------------------------------------------------------+
110
    | | :code:`avl_cont_min_avl_hrs_between_events`                           |
111
    | | *Defined over*: :code:`AVL_CONT`                                      |
112
    | | *Within*: :code:`NonNegativeReals`                                    |
113
    |                                                                         |
114
    | The minimum number of hours a project should be available between       |
115
    | unavailability events.                                                  |
116
    +-------------------------------------------------------------------------+
117

118
    |
119

120
    +-------------------------------------------------------------------------+
121
    | Variables                                                               |
122
    +=========================================================================+
123
    | | :code:`AvlCont_Unavailable`                                           |
124
    | | *Defined over*: :code:`AVL_CONT_OPR_TMPS`                             |
125
    | | *Within*: :code:`PercentFraction`                                     |
126
    |                                                                         |
127
    | Continuous decision variable that specifies whteher the project is      |
128
    | unavailable or not in each operational timepoint (1=unavailable).       |
129
    +-------------------------------------------------------------------------+
130
    | | :code:`AvlCont_Start_Unavailability`                                  |
131
    | | *Defined over*: :code:`AVL_CONT_OPR_TMPS`                             |
132
    | | *Within*: :code:`PercentFraction`                                     |
133
    |                                                                         |
134
    | Continuous decision variable that designates the start of an            |
135
    | unavailability event (when the project goes from available to           |
136
    | unavailable.                                                            |
137
    +-------------------------------------------------------------------------+
138
    | | :code:`AvlCont_Stop_Unavailability`                                   |
139
    | | *Defined over*: :code:`AVL_CONT_OPR_TMPS`                             |
140
    | | *Within*: :code:`PercentFraction`                                     |
141
    |                                                                         |
142
    | Continuous decision variable that designates the end of an              |
143
    | unavailability event (when the project goes from unavailable to         |
144
    | available.                                                              |
145
    +-------------------------------------------------------------------------+
146

147
    |
148

149
    +-------------------------------------------------------------------------+
150
    | Constraints                                                             |
151
    +=========================================================================+
152
    | | :code:`AvlCont_Tot_Sched_Unavl_per_Prd_Constraint`                    |
153
    | | *Defined over*: :code:`AVL_CONT_OPR_PRDS`                             |
154
    |                                                                         |
155
    | The project must be unavailable for :code:`avl_cont_unavl_hrs_per_prd`  |
156
    | hours in each period.                                                   |
157
    +-------------------------------------------------------------------------+
158
    | | :code:`AvlCont_Unavl_Start_and_Stop_Constraint`                       |
159
    | | *Defined over*: :code:`AVL_CONT_OPR_TMPS`                             |
160
    |                                                                         |
161
    | Link the three continuous variables in each timepoint such that         |
162
    | :code:`AvlCont_Start_Unavailability` is 1 if the project goes from      |
163
    | available to unavailable, and :code:`AvlCont_Stop_Unavailability` is 1  |
164
    | if the project goes from unavailable to available.                      |
165
    +-------------------------------------------------------------------------+
166
    | | :code:`AvlCont_Min_Event_Duration_Constraint`                         |
167
    | | *Defined over*: :code:`AVL_CONT_OPR_TMPS`                             |
168
    |                                                                         |
169
    | The duration of each unavailability event should be larger than or      |
170
    | equal to :code:`avl_cont_min_unavl_hrs_per_event` hours.                |
171
    +-------------------------------------------------------------------------+
172
    | | :code:`AvlCont_Min_Time_Between_Events_Constraint`                    |
173
    | | *Defined over*: :code:`AVL_CONT_OPR_TMPS`                             |
174
    |                                                                         |
175
    | The time between unavailability events should be larger than or equal   |
176
    | to :code:`avl_cont_min_avl_hrs_between_events` hours.                   |
177
    +-------------------------------------------------------------------------+
178

179
    """
180

181
    # Sets
182
    ###########################################################################
183

184
    m.AVL_CONT = Set(within=m.PROJECTS)
1✔
185

186
    m.AVL_CONT_OPR_PRDS = Set(
1✔
187
        dimen=2,
188
        initialize=lambda mod: subset_init_by_set_membership(
189
            mod=mod, superset="PRJ_OPR_PRDS", index=0, membership_set=mod.AVL_CONT
190
        ),
191
    )
192

193
    m.AVL_CONT_OPR_TMPS = Set(
1✔
194
        dimen=2,
195
        initialize=lambda mod: subset_init_by_set_membership(
196
            mod=mod, superset="PRJ_OPR_TMPS", index=0, membership_set=mod.AVL_CONT
197
        ),
198
    )
199

200
    # Required Input Params
201
    ###########################################################################
202

203
    m.avl_cont_unavl_hrs_per_prd = Param(m.AVL_CONT, within=NonNegativeReals)
1✔
204
    m.avl_cont_unavl_hrs_per_prd_req_exact = Param(m.AVL_CONT, within=Boolean)
1✔
205

206
    m.avl_cont_min_unavl_hrs_per_event = Param(m.AVL_CONT, within=NonNegativeReals)
1✔
207

208
    m.avl_cont_min_avl_hrs_between_events = Param(m.AVL_CONT, within=NonNegativeReals)
1✔
209

210
    # Variables
211
    ###########################################################################
212

213
    m.AvlCont_Unavailable = Var(m.AVL_CONT_OPR_TMPS, within=PercentFraction)
1✔
214

215
    m.AvlCont_Start_Unavailability = Var(m.AVL_CONT_OPR_TMPS, within=PercentFraction)
1✔
216

217
    m.AvlCont_Stop_Unavailability = Var(m.AVL_CONT_OPR_TMPS, within=PercentFraction)
1✔
218

219
    # Constraints
220
    ###########################################################################
221

222
    m.AvlCont_Tot_Sched_Unavl_per_Prd_Constraint = Constraint(
1✔
223
        m.AVL_CONT_OPR_PRDS, rule=total_scheduled_availability_per_period_rule
224
    )
225

226
    m.AvlCont_Unavl_Start_and_Stop_Constraint = Constraint(
1✔
227
        m.AVL_CONT_OPR_TMPS, rule=unavailability_start_and_stop_rule
228
    )
229

230
    m.AvlCont_Min_Event_Duration_Constraint = Constraint(
1✔
231
        m.AVL_CONT_OPR_TMPS, rule=event_min_duration_rule
232
    )
233

234
    m.AvlCont_Min_Time_Between_Events_Constraint = Constraint(
1✔
235
        m.AVL_CONT_OPR_TMPS, rule=min_time_between_events_rule
236
    )
237

238

239
# Constraint Formulation Rules
240
###############################################################################
241

242

243
def total_scheduled_availability_per_period_rule(mod, g, p):
1✔
244
    """
245
    **Constraint Name**: AvlCont_Tot_Sched_Unavl_per_Prd_Constraint
246
    **Enforced Over**: AVL_CONT_OPR_PRDS
247

248
    The project must be down for avl_cont_unavl_hrs_per_prd in each period if
249
    avl_cont_unavl_hrs_per_prd_req_exact is 1 or at least
250
    avl_cont_unavl_hrs_per_prd otherwise.
251
    """
252
    lhs = sum(
1✔
253
        mod.AvlCont_Unavailable[g, tmp] * mod.hrs_in_tmp[tmp] * mod.tmp_weight[tmp]
254
        for tmp in mod.TMPS_IN_PRD[p]
255
    )
256
    if mod.avl_cont_unavl_hrs_per_prd_req_exact:
1✔
257
        return lhs == mod.avl_cont_unavl_hrs_per_prd[g]
1✔
258
    else:
259
        return lhs >= mod.avl_cont_unavl_hrs_per_prd[g]
×
260

261

262
def unavailability_start_and_stop_rule(mod, g, tmp):
1✔
263
    """
264
    **Constraint Name**: AvlCont_Unavl_Start_and_Stop_Constraint
265
    **Enforced Over**: AVL_CONT_OPR_TMPS
266

267
    Constrain the start and stop availability variables based on the
268
    availability status in the current and previous timepoint. If the
269
    project is down in the current timepoint and was not down in the
270
    previous timepoint, then the RHS is 1 and AvlCont_Start_Unavailability
271
    must be set to 1. If the project is not down in the current
272
    timepoint and was down in the previous timepoint, then the RHS is -1
273
    and AvlCont_Stop_Unavailability must be set to 1.
274
    """
275
    if check_if_boundary_type_and_first_timepoint(
1✔
276
        mod=mod,
277
        tmp=tmp,
278
        balancing_type=mod.balancing_type_project[g],
279
        boundary_type="linear",
280
    ):
281
        return Constraint.Skip
1✔
282
    else:
283
        return (
1✔
284
            mod.AvlCont_Start_Unavailability[g, tmp]
285
            - mod.AvlCont_Stop_Unavailability[g, tmp]
286
            == mod.AvlCont_Unavailable[g, tmp]
287
            - mod.AvlCont_Unavailable[
288
                g, mod.prev_tmp[tmp, mod.balancing_type_project[g]]
289
            ]
290
        )
291

292

293
def event_min_duration_rule(mod, g, tmp):
1✔
294
    """
295
    **Constraint Name**: AvlCont_Min_Event_Duration_Constraint
296
    **Enforced Over**: AVL_CONT_OPR_TMPS
297

298
    If a project became unavailable within avl_cont_min_unavl_hrs_per_event
299
    from the current timepoint, it must still be unavailable in the current
300
    timepoint.
301
    """
302
    relevant_tmps, _ = determine_relevant_timepoints(
1✔
303
        mod, g, tmp, mod.avl_cont_min_unavl_hrs_per_event[g]
304
    )
305
    if relevant_tmps == [tmp]:
1✔
306
        return Constraint.Skip
1✔
307
    return (
1✔
308
        sum(mod.AvlCont_Start_Unavailability[g, tp] for tp in relevant_tmps)
309
        <= mod.AvlCont_Unavailable[g, tmp]
310
    )
311

312

313
def min_time_between_events_rule(mod, g, tmp):
1✔
314
    """
315
    **Constraint Name**: AvlCont_Min_Time_Between_Events_Constraint
316
    **Enforced Over**: AVL_CONT_OPR_TMPS
317

318
    If a project became available within avl_cont_min_avl_hrs_between_events
319
    from the current timepoint, it must still be available in the current
320
    timepoint.
321
    """
322
    relevant_tmps, _ = determine_relevant_timepoints(
1✔
323
        mod, g, tmp, mod.avl_cont_min_avl_hrs_between_events[g]
324
    )
325
    if relevant_tmps == [tmp]:
1✔
326
        return Constraint.Skip
1✔
327
    return (
1✔
328
        sum(mod.AvlCont_Stop_Unavailability[g, tp] for tp in relevant_tmps)
329
        <= 1 - mod.AvlCont_Unavailable[g, tmp]
330
    )
331

332

333
# Availability Type Methods
334
###############################################################################
335

336

337
def availability_derate_cap_rule(mod, g, tmp):
1✔
338
    """ """
339
    return 1 - mod.AvlCont_Unavailable[g, tmp]
1✔
340

341

342
def availability_derate_hyb_stor_cap_rule(mod, g, tmp):
1✔
343
    """ """
UNCOV
344
    return 1
×
345

346

347
# Input-Output
348
###############################################################################
349

350

351
def load_model_data(
1✔
352
    m,
353
    d,
354
    data_portal,
355
    scenario_directory,
356
    weather_iteration,
357
    hydro_iteration,
358
    availability_iteration,
359
    subproblem,
360
    stage,
361
):
362
    """
363
    :param m:
364
    :param data_portal:
365
    :param scenario_directory:
366
    :param subproblem:
367
    :param stage:
368
    :return:
369
    """
370
    # Figure out which projects have this availability type
371
    project_subset = determine_project_subset(
1✔
372
        scenario_directory=scenario_directory,
373
        weather_iteration=weather_iteration,
374
        hydro_iteration=hydro_iteration,
375
        availability_iteration=availability_iteration,
376
        subproblem=subproblem,
377
        stage=stage,
378
        column="availability_type",
379
        type="continuous",
380
        prj_or_tx="project",
381
    )
382

383
    data_portal.data()["AVL_CONT"] = {None: project_subset}
1✔
384

385
    avl_cont_unavl_hrs_per_prd_dict = {}
1✔
386
    avl_cont_unavl_hrs_per_prd_exact_dict = {}
1✔
387
    avl_cont_min_unavl_hrs_per_event_dict = {}
1✔
388
    avl_cont_min_avl_hrs_between_events_dict = {}
1✔
389

390
    with open(
1✔
391
        os.path.join(
392
            scenario_directory,
393
            weather_iteration,
394
            hydro_iteration,
395
            availability_iteration,
396
            subproblem,
397
            stage,
398
            "inputs",
399
            "project_availability_endogenous.tab",
400
        ),
401
        "r",
402
    ) as f:
403
        reader = csv.reader(f, delimiter="\t", lineterminator="\n")
1✔
404
        next(reader)
1✔
405

406
        for row in reader:
1✔
407
            if row[0] in project_subset:
1✔
408
                avl_cont_unavl_hrs_per_prd_dict[row[0]] = float(row[1])
1✔
409
                avl_cont_unavl_hrs_per_prd_exact_dict[row[0]] = int(float(row[2]))
1✔
410
                avl_cont_min_unavl_hrs_per_event_dict[row[0]] = float(row[3])
1✔
411
                avl_cont_min_avl_hrs_between_events_dict[row[0]] = float(row[4])
1✔
412

413
    data_portal.data()["avl_cont_unavl_hrs_per_prd"] = avl_cont_unavl_hrs_per_prd_dict
1✔
414
    data_portal.data()[
1✔
415
        "avl_cont_unavl_hrs_per_prd_req_exact"
416
    ] = avl_cont_unavl_hrs_per_prd_exact_dict
417
    data_portal.data()[
1✔
418
        "avl_cont_min_unavl_hrs_per_event"
419
    ] = avl_cont_min_unavl_hrs_per_event_dict
420
    data_portal.data()[
1✔
421
        "avl_cont_min_avl_hrs_between_events"
422
    ] = avl_cont_min_avl_hrs_between_events_dict
423

424

425
def add_to_prj_tmp_results(
1✔
426
    scenario_directory,
427
    weather_iteration,
428
    hydro_iteration,
429
    availability_iteration,
430
    subproblem,
431
    stage,
432
    m,
433
    d,
434
):
435
    """
436
    Export operations results.
437
    :param scenario_directory:
438
    :param subproblem:
439
    :param stage:
440
    :param m: The Pyomo abstract model
441
    :param d: Dynamic components
442
    :return: Nothing
443
    """
444

445
    results_columns = [
×
446
        "unavailability_decision",
447
        "start_unavailability",
448
        "stop_unavailability",
449
    ]
450
    data = [
×
451
        [
452
            prj,
453
            tmp,
454
            value(m.AvlCont_Unavailable[prj, tmp]),
455
            value(m.AvlCont_Start_Unavailability[prj, tmp]),
456
            value(m.AvlCont_Stop_Unavailability[prj, tmp]),
457
        ]
458
        for (prj, tmp) in m.AVL_CONT_OPR_TMPS
459
    ]
460
    results_df = create_results_df(
×
461
        index_columns=["project", "timepoint"],
462
        results_columns=results_columns,
463
        data=data,
464
    )
465

466
    return results_columns, results_df
×
467

468

469
# Database
470
###############################################################################
471

472

473
def get_inputs_from_database(
1✔
474
    scenario_id,
475
    subscenarios,
476
    weather_iteration,
477
    hydro_iteration,
478
    availability_iteration,
479
    subproblem,
480
    stage,
481
    conn,
482
):
483
    """
484
    :param subscenarios:
485
    :param subproblem:
486
    :param stage:
487
    :param conn:
488
    :return:
489
    """
490

491
    # Get project availability if project_availability_scenario_id is not NUL
492
    c = conn.cursor()
×
493
    availability_params = c.execute(
×
494
        """
495
            SELECT project, unavailable_hours_per_period, 
496
            unavailable_hours_per_period_require_exact,
497
            unavailable_hours_per_event_min,
498
            available_hours_between_events_min
499
            FROM (
500
            SELECT project
501
            FROM inputs_project_portfolios
502
            WHERE project_portfolio_scenario_id = {}
503
            ) as portfolio_tbl
504
            INNER JOIN (
505
                SELECT project, endogenous_availability_scenario_id
506
                FROM inputs_project_availability
507
                WHERE project_availability_scenario_id = {}
508
                AND availability_type = 'continuous'
509
                AND endogenous_availability_scenario_id IS NOT NULL
510
                ) AS avail_char
511
             USING (project)
512
            LEFT OUTER JOIN
513
            inputs_project_availability_endogenous
514
            USING (endogenous_availability_scenario_id, project);
515
            """.format(
516
            subscenarios.PROJECT_PORTFOLIO_SCENARIO_ID,
517
            subscenarios.PROJECT_AVAILABILITY_SCENARIO_ID,
518
        )
519
    )
520

521
    return availability_params
×
522

523

524
def write_model_inputs(
1✔
525
    scenario_directory,
526
    scenario_id,
527
    subscenarios,
528
    weather_iteration,
529
    hydro_iteration,
530
    availability_iteration,
531
    subproblem,
532
    stage,
533
    conn,
534
):
535
    """
536

537
    :param scenario_directory:
538
    :param subscenarios:
539
    :param subproblem:
540
    :param stage:
541
    :param conn:
542
    :return:
543
    """
544

545
    endogenous_availability_params = get_inputs_from_database(
×
546
        scenario_id=scenario_id,
547
        subscenarios=subscenarios,
548
        weather_iteration=weather_iteration,
549
        hydro_iteration=hydro_iteration,
550
        availability_iteration=availability_iteration,
551
        subproblem=subproblem,
552
        stage=stage,
553
        conn=conn,
554
    )
555

556
    # Check if project_availability_endogenous.tab exists; only write header
557
    # if the file wasn't already created
558
    availability_file = os.path.join(
×
559
        scenario_directory,
560
        subproblem,
561
        stage,
562
        "inputs",
563
        "project_availability_endogenous.tab",
564
    )
565

566
    if not os.path.exists(availability_file):
×
567
        with open(availability_file, "w", newline="") as f:
×
568
            writer = csv.writer(f, delimiter="\t", lineterminator="\n")
×
569
            # Write header
570
            writer.writerow(
×
571
                [
572
                    "project",
573
                    "unavailable_hours_per_period",
574
                    "unavailable_hours_per_period_require_exact",
575
                    "unavailable_hours_per_event_min",
576
                    "available_hours_between_events_min",
577
                ]
578
            )
579

580
    with open(availability_file, "a", newline="") as f:
×
581
        writer = csv.writer(f, delimiter="\t", lineterminator="\n")
×
582
        # Write rows
583
        for row in endogenous_availability_params:
×
584
            replace_nulls = ["." if i is None else i for i in row]
×
585
            writer.writerow(replace_nulls)
×
586

587

588
# Validation
589
###############################################################################
590

591

592
def validate_inputs(
1✔
593
    scenario_id,
594
    subscenarios,
595
    weather_iteration,
596
    hydro_iteration,
597
    availability_iteration,
598
    subproblem,
599
    stage,
600
    conn,
601
):
602
    """
603
    :param subscenarios:
604
    :param subproblem:
605
    :param stage:
606
    :param conn:
607
    :return:
608
    """
609

610
    params = get_inputs_from_database(
×
611
        scenario_id,
612
        subscenarios,
613
        weather_iteration,
614
        hydro_iteration,
615
        availability_iteration,
616
        subproblem,
617
        stage,
618
        conn,
619
    )
620

621
    df = cursor_to_df(params)
×
622

623
    # Check data types availability
624
    expected_dtypes = get_expected_dtypes(
×
625
        conn, ["inputs_project_availability", "inputs_project_availability_endogenous"]
626
    )
627
    dtype_errors, error_columns = validate_dtypes(df, expected_dtypes)
×
628
    write_validation_to_database(
×
629
        conn=conn,
630
        scenario_id=scenario_id,
631
        weather_iteration=weather_iteration,
632
        hydro_iteration=hydro_iteration,
633
        availability_iteration=availability_iteration,
634
        subproblem_id=subproblem,
635
        stage_id=stage,
636
        gridpath_module=__name__,
637
        db_table="inputs_project_availability_endogenous",
638
        severity="High",
639
        errors=dtype_errors,
640
    )
641

642
    # Check for missing inputs
643
    msg = ""
×
644
    value_cols = [
×
645
        "unavailable_hours_per_period",
646
        "unavailable_hours_per_event_min",
647
        "available_hours_between_events_min",
648
    ]
649
    write_validation_to_database(
×
650
        conn=conn,
651
        scenario_id=scenario_id,
652
        weather_iteration=weather_iteration,
653
        hydro_iteration=hydro_iteration,
654
        availability_iteration=availability_iteration,
655
        subproblem_id=subproblem,
656
        stage_id=stage,
657
        gridpath_module=__name__,
658
        db_table="inputs_project_availability_endogenous",
659
        severity="Low",
660
        errors=validate_missing_inputs(df, value_cols, "project", msg),
661
    )
662

663
    cols = ["unavailable_hours_per_event_min", "unavailable_hours_per_period"]
×
664
    write_validation_to_database(
×
665
        conn=conn,
666
        scenario_id=scenario_id,
667
        weather_iteration=weather_iteration,
668
        hydro_iteration=hydro_iteration,
669
        availability_iteration=availability_iteration,
670
        subproblem_id=subproblem,
671
        stage_id=stage,
672
        gridpath_module=__name__,
673
        db_table="inputs_project_availability_endogenous",
674
        severity="High",
675
        errors=validate_column_monotonicity(df=df, cols=cols, idx_col=["project"]),
676
    )
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