Skip to content

Commit 1ca2b5a

Browse files
Merge pull request #30 from IdahoLabResearch/dev_cb
Merging to address JOSS requirements
2 parents aa23e5b + 14e36d6 commit 1ca2b5a

File tree

375 files changed

+466260
-107
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

375 files changed

+466260
-107
lines changed

HydroGenerate/flow_preprocessing.py

Lines changed: 210 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,22 +6,80 @@
66
07-2024
77
@author: Soumyadeep Nag, Camilo J. Bastidas Pacheco, J. Gallego-Calderon
88
9-
This module applies the flow limit constaints and incorporates the impact of annual maintenance activities
9+
This module applies the flow limit constraints and incorporates the impact
10+
of annual and major maintenance activities on turbine flow.
11+
12+
It provides utilities to:
13+
1) Cap turbine flow at design limits.
14+
2) Enforce minimum turbine flow thresholds (absolute or % of design flow).
15+
3) Set turbine flow to zero during annual and major maintenance windows.
16+
17+
Expected `flow_obj` interface (used by functions here)
18+
-----------------------------------------------------
19+
Attributes
20+
----------
21+
flow : numpy.ndarray
22+
Raw inflow time series (m³/s), same length as `datetime_index`.
23+
design_flow : float or numpy.ndarray
24+
Turbine design flow (m³/s). If array, must align with `flow`.
25+
turbine_flow : numpy.ndarray
26+
Turbine flow (m³/s); created/updated by the functions in this module.
27+
datetime_index : pandas.DatetimeIndex
28+
Timestamps for `flow`. Weekly grouping assumes this index (or a level)
29+
is named "dateTime" when using pandas.Grouper(level="dateTime", ...).
30+
31+
Notes
32+
-----
33+
- Annual maintenance zeros 7 consecutive days once per year, selecting the week
34+
with the lowest weekly mean flow.
35+
- Major maintenance zeros 14 consecutive days every 5 years (skipping the first),
36+
selecting the two-week period with the lowest bi-weekly mean flow.
1037
"""
1138

39+
1240
import numpy as np
1341
import pandas as pd
1442
from datetime import datetime
1543

1644

1745
# Hydraulic design parameters
1846
class FlowPreProcessingParameters:
47+
48+
"""
49+
Container for flow pre-processing options used by flow limit and
50+
maintenance logic.
51+
52+
Parameters
53+
----------
54+
minimum_turbineflow : float or None
55+
Absolute minimum turbine flow (m^3/s). If None, a percentage of
56+
`design_flow` is used (see `minimum_turbineflow_percent`).
57+
minimum_turbineflow_percent : float or None
58+
Minimum turbine flow as a percentage of `design_flow` (1–100). Used
59+
only when `minimum_turbineflow` is None. If both are None, a default
60+
of 10% is applied.
61+
annual_maintenance_flag : bool
62+
If True, enforce a 7-day annual maintenance outage (flow set to 0).
63+
major_maintenance_flag : bool
64+
If True, enforce a 14-day major maintenance outage every 5 years
65+
(flow set to 0).
66+
67+
Attributes
68+
----------
69+
minimum_turbineflow : float or None
70+
minimum_turbineflow_percent : float or None
71+
annual_maintenance_flag : bool
72+
major_maintenance_flag : bool
73+
74+
Notes
75+
-----
76+
This class only stores configuration; calculations are performed by
77+
methods in `FlowPreProcessing`.
78+
"""
79+
1980
def __init__(self, minimum_turbineflow, minimum_turbineflow_percent, # Flow parameters
2081
annual_maintenance_flag, major_maintenance_flag):
21-
'''
22-
# This class initializes Flow preprocessing parameters needed for multiple calculations
23-
# Parameter descriptions are provided below:
24-
# '''
82+
2583
# Inputs
2684
self.minimum_turbineflow = minimum_turbineflow # minimum flow through the turbine (m3/s)
2785
self.minimum_turbineflow_percent = minimum_turbineflow_percent # Set minimum flow by using a percent of design flow, deault is 10 (%, 1-100)
@@ -32,15 +90,88 @@ def __init__(self, minimum_turbineflow, minimum_turbineflow_percent, # Flo
3290

3391
class FlowPreProcessing():
3492

93+
"""
94+
Apply design flow limits and scheduled maintenance to a flow object.
95+
96+
The methods in this class expect a `flow_obj` with, at minimum, the
97+
attributes described in the module docstring. Methods update
98+
`flow_obj.turbine_flow` in place and do not return values.
99+
100+
See Also
101+
--------
102+
max_turbineflow_checker : Cap turbine flow at design flow.
103+
min_turbineflow_checker : Enforce minimum turbine flow threshold.
104+
annual_maintenance_implementer : Zero 7 days/year at lowest-flow week.
105+
major_maintenance_implementer : Zero 14 days/5 years at lowest bi-weekly mean.
106+
"""
107+
35108
# Function that sets max turbine flow to the design flow
36109
def max_turbineflow_checker(self, flow_obj):
110+
"""
111+
Cap turbine flow at the design flow.
112+
113+
Parameters
114+
----------
115+
flow_obj : object
116+
Object with attributes:
117+
- flow : numpy.ndarray
118+
Raw inflow time series (m^3/s).
119+
- design_flow : float or numpy.ndarray
120+
Turbine design flow (m^3/s). If array, must align with `flow`.
121+
122+
Side Effects
123+
------------
124+
Updates `flow_obj.turbine_flow` (numpy.ndarray), where each element is
125+
`min(flow, design_flow)`.
126+
127+
Examples
128+
--------
129+
>>> # flow_obj.flow = [12, 8], design_flow = 10
130+
>>> # turbine_flow becomes [10, 8]
131+
"""
37132

38133
flow = flow_obj.flow # this is a numpy array
39134
design_flow = flow_obj.design_flow
40135
flow_obj.turbine_flow = np.where(flow > design_flow, design_flow, flow) # turbine_flow is created here as this code is always active and runs 1st. flow is < design flow
41136

42137
# Function that sets min turbine flow to the design flow
43138
def min_turbineflow_checker(self, flow_obj):
139+
"""
140+
Enforce a minimum turbine flow threshold; set values below it to zero.
141+
142+
Logic
143+
-----
144+
- If `flow_obj.minimum_turbineflow` is provided, use it (m^3/s).
145+
- Else, compute min flow as
146+
(`minimum_turbineflow_percent` or 10 by default) × `design_flow` / 100.
147+
The computed value is stored back into `flow_obj.minimum_turbineflow`.
148+
149+
Parameters
150+
----------
151+
flow_obj : object
152+
Object with attributes:
153+
- turbine_flow : numpy.ndarray
154+
Turbine flow series (m^3/s), typically after max-capping.
155+
- design_flow : float or numpy.ndarray
156+
Turbine design flow (m^3/s).
157+
- minimum_turbineflow : float or None
158+
- minimum_turbineflow_percent : float or None
159+
160+
Side Effects
161+
------------
162+
Updates `flow_obj.turbine_flow`, replacing values `< min_flow` with 0.
163+
164+
Notes
165+
-----
166+
- If `design_flow` is an array, elementwise percentages are applied.
167+
- Default minimum percent is 10 if neither absolute nor percentage is given.
168+
169+
Examples
170+
--------
171+
>>> # design_flow = 20 m^3/s, minimum_turbineflow_percent = 10
172+
>>> # min_flow = 2.0; turbine_flow < 2.0 becomes 0
173+
"""
174+
44175
flow = flow_obj.turbine_flow # this is a numpy array
45176
design_flow = flow_obj.design_flow
46177

@@ -59,6 +190,42 @@ def min_turbineflow_checker(self, flow_obj):
59190
flow_obj.turbine_flow = np.where(flow < min_flow, 0, flow) # replace flows less than min flow with 0
60191

61192
def annual_maintenance_implementer(self, flow_obj):
193+
"""
194+
Zero turbine flow for 7 consecutive days once per calendar year.
195+
196+
Procedure
197+
---------
198+
1) Compute weekly mean flow and identify the week with the lowest mean
199+
(excluding first/last partial weeks).
200+
2) For each year in the time span, zero out the 7-day window starting on
201+
the Monday of that lowest-flow week.
202+
203+
Parameters
204+
----------
205+
flow_obj : object
206+
Object with attributes:
207+
- turbine_flow : numpy.ndarray
208+
Turbine flow series (m^3/s).
209+
- datetime_index : pandas.DatetimeIndex
210+
Timestamps aligned with `turbine_flow`.
211+
212+
Side Effects
213+
------------
214+
Updates `flow_obj.turbine_flow`, setting the 7 chosen days per year to 0.
215+
216+
Assumptions
217+
-----------
218+
- `datetime_index` covers multiple years or at least one full year.
219+
- Weekly grouping uses `pd.Grouper(level="dateTime", freq='W')`. Ensure your
220+
index (or a MultiIndex level) is named "dateTime" or adjust the grouper
221+
accordingly.
222+
223+
Examples
224+
--------
225+
>>> # After applying, each year has a 7-day zero-flow outage at the
226+
>>> # statistically lowest weekly mean period.
227+
"""
228+
62229
# Function to set annual maintennace - i.e., make the flow 0 for a week a year
63230

64231
turbine_flow = flow_obj.turbine_flow # turbine flow series with max and min (if on) limits implemented
@@ -95,6 +262,44 @@ def annual_maintenance_implementer(self, flow_obj):
95262

96263
# Function that schedules the major maintennace
97264
def major_maintenance_implementer(self, flow_obj):
265+
266+
"""
267+
Zero turbine flow for 14 consecutive days every 5 years (skip first year).
268+
269+
Procedure
270+
---------
271+
1) Compute bi-weekly (2W) mean flow and identify the bi-weekly window with
272+
the lowest mean (excluding first/last partial windows).
273+
2) For maintenance years spaced every 5 years (starting after the first),
274+
zero out the 14-day window starting on the Monday of the selected week.
275+
276+
Parameters
277+
----------
278+
flow_obj : object
279+
Object with attributes:
280+
- turbine_flow : numpy.ndarray
281+
Turbine flow series (m^3/s).
282+
- datetime_index : pandas.DatetimeIndex
283+
Timestamps aligned with `turbine_flow`.
284+
285+
Side Effects
286+
------------
287+
Updates `flow_obj.turbine_flow`, setting the selected 14 days to 0 in each
288+
maintenance year (every 5 years; first year excluded).
289+
290+
Assumptions
291+
-----------
292+
- Sufficient span to include one or more 5-year intervals.
293+
- Bi-weekly grouping uses `pd.Grouper(level="dateTime", freq='2W')`. Ensure
294+
your index (or a MultiIndex level) is named "dateTime" or adjust the
295+
grouper accordingly.
296+
297+
Examples
298+
--------
299+
>>> # In a 10-year record, days 1–14 of the lowest 2-week flow window
300+
>>> # are zeroed in years 5 and 10 (year 0 excluded).
301+
"""
302+
98303
# Major maintenance will happend on the month with lowest flow a year - during the first 15 days of this month.
99304

100305
turbine_flow = flow_obj.turbine_flow # turbine flow series with max and min (if on) limits implemented

0 commit comments

Comments
 (0)