• 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

97.98
/gridpath/project/availability/availability_types/binary.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
*Projects* assigned this availability type have binary decision variables
17
for their availability in each timepoint. This type can be useful in
18
optimizing planned outage schedules. A *project* of this type is constrained
19
to be unavailable for at least a pre-specified number of hours in each
20
*period*. In addition, each unavailability event can be constrained to be
21
within a minimum and maximum number of hours, and constraints can also be
22
implemented on the minimum and maximum duration between unavailability events.
23

24
"""
25

26
import csv
1✔
27
import os.path
1✔
28
from pyomo.environ import (
1✔
29
    Param,
30
    Set,
31
    Var,
32
    Constraint,
33
    Boolean,
34
    Binary,
35
    value,
36
    NonNegativeReals,
37
)
38

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

57

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

71
    +-------------------------------------------------------------------------+
72
    | Sets                                                                    |
73
    +=========================================================================+
74
    | | :code:`AVL_BIN`                                                       |
75
    |                                                                         |
76
    | The set of projects of the :code:`binary` availability type.            |
77
    +-------------------------------------------------------------------------+
78
    | | :code:`AVL_BIN_OPR_PRDS`                                              |
79
    |                                                                         |
80
    | Two-dimensional set with projects of the :code:`binary` availability    |
81
    | type and their operational periods.                                     |
82
    +-------------------------------------------------------------------------+
83
    | | :code:`AVL_BIN_OPR_TMPS`                                              |
84
    |                                                                         |
85
    | Two-dimensional set with projects of the :code:`binary` availability    |
86
    | type and their operational timepoints.                                  |
87
    +-------------------------------------------------------------------------+
88

89
    |
90

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

121
    |
122

123
    +-------------------------------------------------------------------------+
124
    | Variables                                                               |
125
    +=========================================================================+
126
    | | :code:`AvlBin_Unavailable`                                            |
127
    | | *Defined over*: :code:`AVL_BIN_OPR_TMPS`                              |
128
    | | *Within*: :code:`Binary`                                              |
129
    |                                                                         |
130
    | Binary decision variable that specifies whether the project is          |
131
    | unavailable or not in each operational timepoint (1=unavailable).       |
132
    +-------------------------------------------------------------------------+
133
    | | :code:`AvlBin_Start_Unavailability`                                   |
134
    | | *Defined over*: :code:`AVL_BIN_OPR_TMPS`                              |
135
    | | *Within*: :code:`Binary`                                              |
136
    |                                                                         |
137
    | Binary decision variable that designates the start of an unavailability |
138
    | event (when the project goes from available to unavailable.             |
139
    +-------------------------------------------------------------------------+
140
    | | :code:`AvlBin_Stop_Unavailability`                                    |
141
    | | *Defined over*: :code:`AVL_BIN_OPR_TMPS`                              |
142
    | | *Within*: :code:`Binary`                                              |
143
    |                                                                         |
144
    | Binary decision variable that designates the end of an unavailability   |
145
    | event (when the project goes from unavailable to available.             |
146
    +-------------------------------------------------------------------------+
147

148
    |
149

150
    +-------------------------------------------------------------------------+
151
    | Constraints                                                             |
152
    +=========================================================================+
153
    | | :code:`AvlBin_Tot_Sched_Unavl_per_Prd_Constraint`                     |
154
    | | *Defined over*: :code:`AVL_BIN_OPR_PRDS`                              |
155
    |                                                                         |
156
    | The project must be unavailable for :code:`avl_bin_unavl_hrs_per_prd`   |
157
    | hours in each period.                                                   |
158
    +-------------------------------------------------------------------------+
159
    | | :code:`AvlBin_Unavl_Start_and_Stop_Constraint`                        |
160
    | | *Defined over*: :code:`AVL_BIN_OPR_TMPS`                              |
161
    |                                                                         |
162
    | Link the three binary variables in each timepoint such that             |
163
    | :code:`AvlBin_Start_Unavailability` is 1 if the project goes from       |
164
    | available to unavailable, and :code:`AvlBin_Stop_Unavailability` is 1   |
165
    | if the project goes from unavailable to available.                      |
166
    +-------------------------------------------------------------------------+
167
    | | :code:`AvlBin_Min_Event_Duration_Constraint`                          |
168
    | | *Defined over*: :code:`AVL_BIN_OPR_TMPS`                              |
169
    |                                                                         |
170
    | The duration of each unavailability event should be larger than or      |
171
    | equal to :code:`avl_bin_min_unavl_hrs_per_event` hours.                 |
172
    +-------------------------------------------------------------------------+
173
    | | :code:`AvlBin_Min_Time_Between_Events_Constraint`                     |
174
    | | *Defined over*: :code:`AVL_BIN_OPR_TMPS`                              |
175
    |                                                                         |
176
    | The time between unavailability events should be larger than or equal   |
177
    | to :code:`avl_bin_min_avl_hrs_between_events` hours.                    |
178
    +-------------------------------------------------------------------------+
179

180
    """
181

182
    # Sets
183
    ###########################################################################
184

185
    m.AVL_BIN = Set(within=m.PROJECTS)
1✔
186

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

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

201
    # Required Input Params
202
    ###########################################################################
203

204
    m.avl_bin_unavl_hrs_per_prd = Param(m.AVL_BIN, within=NonNegativeReals)
1✔
205
    m.avl_bin_unavl_hrs_per_prd_req_exact = Param(m.AVL_BIN, within=Boolean)
1✔
206

207
    m.avl_bin_min_unavl_hrs_per_event = Param(m.AVL_BIN, within=NonNegativeReals)
1✔
208

209
    m.avl_bin_min_avl_hrs_between_events = Param(m.AVL_BIN, within=NonNegativeReals)
1✔
210

211
    # Variables
212
    ###########################################################################
213

214
    m.AvlBin_Unavailable = Var(m.AVL_BIN_OPR_TMPS, within=Binary)
1✔
215

216
    m.AvlBin_Start_Unavailability = Var(m.AVL_BIN_OPR_TMPS, within=Binary)
1✔
217

218
    m.AvlBin_Stop_Unavailability = Var(m.AVL_BIN_OPR_TMPS, within=Binary)
1✔
219

220
    # Constraints
221
    ###########################################################################
222

223
    m.AvlBin_Tot_Sched_Unavl_per_Prd_Constraint = Constraint(
1✔
224
        m.AVL_BIN_OPR_PRDS, rule=total_scheduled_availability_per_period_rule
225
    )
226

227
    m.AvlBin_Unavl_Start_and_Stop_Constraint = Constraint(
1✔
228
        m.AVL_BIN_OPR_TMPS, rule=unavailability_start_and_stop_rule
229
    )
230

231
    m.AvlBin_Min_Event_Duration_Constraint = Constraint(
1✔
232
        m.AVL_BIN_OPR_TMPS, rule=event_min_duration_rule
233
    )
234

235
    m.AvlBin_Min_Time_Between_Events_Constraint = Constraint(
1✔
236
        m.AVL_BIN_OPR_TMPS, rule=min_time_between_events_rule
237
    )
238

239

240
# Constraint Formulation Rules
241
###############################################################################
242

243

244
def total_scheduled_availability_per_period_rule(mod, g, p):
1✔
245
    """
246
    **Constraint Name**: AvlBin_Tot_Sched_Unavl_per_Prd_Constraint
247
    **Enforced Over**: AVL_BIN_OPR_PRDS
248

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

262

263
def unavailability_start_and_stop_rule(mod, g, tmp):
1✔
264
    """
265
    **Constraint Name**: AvlBin_Unavl_Start_and_Stop_Constraint
266
    **Enforced Over**: AVL_BIN_OPR_TMPS
267

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

293

294
def event_min_duration_rule(mod, g, tmp):
1✔
295
    """
296
    **Constraint Name**: AvlBin_Min_Event_Duration_Constraint
297
    **Enforced Over**: AVL_BIN_OPR_TMPS
298

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

313

314
def min_time_between_events_rule(mod, g, tmp):
1✔
315
    """
316
    **Constraint Name**: AvlBin_Min_Time_Between_Events_Constraint
317
    **Enforced Over**: AVL_BIN_OPR_TMPS
318

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

333

334
# Availability Type Methods
335
###############################################################################
336

337

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

342

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

347

348
# Input-Output
349
###############################################################################
350

351

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

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

386
    avl_bin_unavl_hrs_per_prd_dict = {}
1✔
387
    avl_bin_unavl_hrs_per_prd_exact_dict = {}
1✔
388
    avl_bin_min_unavl_hrs_per_event_dict = {}
1✔
389
    avl_bin_min_avl_hrs_between_events_dict = {}
1✔
390

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

407
        for row in reader:
1✔
408
            if row[0] in project_subset:
1✔
409
                avl_bin_unavl_hrs_per_prd_dict[row[0]] = float(row[1])
1✔
410
                avl_bin_unavl_hrs_per_prd_exact_dict[row[0]] = int(float(row[2]))
1✔
411
                avl_bin_min_unavl_hrs_per_event_dict[row[0]] = float(row[3])
1✔
412
                avl_bin_min_avl_hrs_between_events_dict[row[0]] = float(row[4])
1✔
413

414
    data_portal.data()["avl_bin_unavl_hrs_per_prd"] = avl_bin_unavl_hrs_per_prd_dict
1✔
415
    data_portal.data()[
1✔
416
        "avl_bin_unavl_hrs_per_prd_req_exact"
417
    ] = avl_bin_unavl_hrs_per_prd_exact_dict
418
    data_portal.data()[
1✔
419
        "avl_bin_min_unavl_hrs_per_event"
420
    ] = avl_bin_min_unavl_hrs_per_event_dict
421
    data_portal.data()[
1✔
422
        "avl_bin_min_avl_hrs_between_events"
423
    ] = avl_bin_min_avl_hrs_between_events_dict
424

425

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

446
    results_columns = [
1✔
447
        "unavailability_decision",
448
        "start_unavailability",
449
        "stop_unavailability",
450
    ]
451
    data = [
1✔
452
        [
453
            prj,
454
            tmp,
455
            value(m.AvlBin_Unavailable[prj, tmp]),
456
            value(m.AvlBin_Start_Unavailability[prj, tmp]),
457
            value(m.AvlBin_Stop_Unavailability[prj, tmp]),
458
        ]
459
        for (prj, tmp) in m.AVL_BIN_OPR_TMPS
460
    ]
461
    results_df = create_results_df(
1✔
462
        index_columns=["project", "timepoint"],
463
        results_columns=results_columns,
464
        data=data,
465
    )
466

467
    return results_columns, results_df
1✔
468

469

470
# Database
471
###############################################################################
472

473

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

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

522
    return availability_params
1✔
523

524

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

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

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

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

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

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

588

589
# Validation
590
###############################################################################
591

592

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

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

622
    df = cursor_to_df(params)
1✔
623

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

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

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