Skip to content

API Reference - Separation Module

bn77(Q, L_min, snow_freeze_period, observational_precision, quantile=0.9)

Identifies the drought flow points in the discharge time series. Cheng, Lei, Lu Zhang, and Wilfried Brutsaert. “Automated Selection of Pure Base Flows from Regular Daily Streamflow Data: Objective Algorithm.” Journal of Hydrologic Engineering 21, no. 11 (November 1, 2016): 06016008. https://doi.org/10.1061/(ASCE)HE.1943-5584.0001427.

Parameters:

Name Type Description Default
Q ndarray

The discharge time series.

required
L_min int

Minimum number of points to be eliminated at the beginning and end of recession episode.

required
snow_freeze_period tuple

Start and end indices of the snow and/or freeze period.

required
observational_precision float

Observational precision threshold.

required
quantile float

Quanti le for identifying major events, default is 0.9.

0.9

Returns:

Type Description

numpy.ndarray: The indices of the drought flow points.

Source code in baseflow/separation.py
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
def bn77(Q, L_min, snow_freeze_period, observational_precision, quantile=0.9):
    """
    Identifies the drought flow points in the discharge time series.
    Cheng, Lei, Lu Zhang, and Wilfried Brutsaert. “Automated Selection of Pure Base Flows from Regular Daily Streamflow Data: Objective Algorithm.” Journal of Hydrologic Engineering 21, no. 11 (November 1, 2016): 06016008. https://doi.org/10.1061/(ASCE)HE.1943-5584.0001427.

    Args:
        Q (numpy.ndarray): The discharge time series.
        L_min (int): Minimum number of points to be eliminated at the beginning and end of recession episode.
        snow_freeze_period (tuple): Start and end indices of the snow and/or freeze period.
        observational_precision (float): Observational precision threshold.
        quantile (float): Quanti le for identifying major events, default is 0.9.

    Returns:
        numpy.ndarray: The indices of the drought flow points.
    """
    # Step 1: Time series
    S = _estimate_recession_slope(Q)

    # Step 2: Recession episodes
    recession_episodes = _identify_recession_episodes(S, L_min)

    # Step 3: Drought flow points
    drought_flow_points = _eliminate_points(recession_episodes, L_min, snow_freeze_period, observational_precision, Q, quantile)

    return drought_flow_points

boughton(Q, a, C, initial_method='Q0', return_exceed=False)

Boughton doulbe-parameter filter (Boughton, 2004)

???+ Abstract "Reference" Boughton W.C. (1993) - A hydrograph-based model for estimating water yield of ungauged catchments. Institute of Engineers Australia National Conference. Publ. 93/14, pp. 317-324.

Args: Q (np.array): streamflow a (float): recession coefficient C (float): calibrated in baseflow.param_estimate initial_method (str or float, optional): method to calculate the initial baseflow value. Accepted string values are: - 'Q0': Use Q[0] as the initial baseflow value. - 'min': Use np.min(Q) as the initial baseflow value. - 'LH': Calculate the initial baseflow value using the LH method. Alternatively, a float value can be provided to directly set the initial baseflow value. Default is 'Q0'. return_exceed (bool, optional): if True, returns the number of times the baseflow exceeds the streamflow.

Returns:

Name Type Description
b array

baseflow

Source code in baseflow/separation.py
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
def boughton(Q, a, C, initial_method='Q0', return_exceed=False):
    """
    Boughton doulbe-parameter filter (Boughton, 2004)

    <style>
    .custom-abstract {
        font-size: 6px; /* Adjust this value to your desired font size */
    }
    </style>

    <div class="custom-abstract">

    ???+ Abstract "Reference"

        Boughton W.C. (1993) - A hydrograph-based model for estimating water yield of ungauged catchments. Institute of Engineers Australia National Conference. Publ. 93/14, pp. 317-324.

    </div>
    Args:
        Q (np.array): streamflow
        a (float): recession coefficient
        C (float): calibrated in baseflow.param_estimate
        initial_method (str or float, optional): method to calculate the initial baseflow value.
            Accepted string values are:
            - 'Q0': Use Q[0] as the initial baseflow value.
            - 'min': Use np.min(Q) as the initial baseflow value.
            - 'LH': Calculate the initial baseflow value using the LH method.
            Alternatively, a float value can be provided to directly set the initial baseflow value.
            Default is 'Q0'.
        return_exceed (bool, optional): if True, returns the number of times the
            baseflow exceeds the streamflow.

    Returns:
        b (np.array): baseflow
    """
    if return_exceed:
        b = np.zeros(Q.shape[0] + 1)
    else:
        b = np.zeros(Q.shape[0])

    # Set initial value for b based on the specified method
    if isinstance(initial_method, str):
        if initial_method == 'Q0':
            b[0] = Q[0]
        elif initial_method == 'min':
            b[0] = np.min(Q)
        elif initial_method == 'LH':
            b[0] = lh(Q)[0]  # Calculate the initial value using the LH method
        else:
            raise ValueError(f"Invalid initial_method: {initial_method}")
    else:
        b[0] = initial_method

    for i in range(Q.shape[0] - 1):
        b[i + 1] = a / (1 + C) * b[i] + C / (1 + C) * Q[i + 1]
        if b[i + 1] > Q[i + 1]:
            b[i + 1] = Q[i + 1]
            if return_exceed:
                b[-1] += 1

    return b

chapman(Q, a=0.925, initial_method='Q0', return_exceed=False)

Chapman filter (Chapman, 1991) Chapman, Tom G. "Comment on 'Evaluation of Automated Techniques for Base Flow and Recession Analyses' by R. J. Nathan and T. A. McMahon." Water Resources Research 27, no. 7 (1991): 1783–84. https://doi.org/10.1029/91WR01007.

Parameters:

Name Type Description Default
Q array

streamflow

required
a float

recession coefficient

0.925
initial_method str or float

method to calculate the initial baseflow value. Accepted string values are: - 'Q0': Use Q[0] as the initial baseflow value. - 'min': Use np.min(Q) as the initial baseflow value. - 'LH': Calculate the initial baseflow value using the LH method. Alternatively, a float value can be provided to directly set the initial baseflow value. Default is 'Q0'.

'Q0'
return_exceed bool

if True, returns the number of times the baseflow exceeds the streamflow.

False
Source code in baseflow/separation.py
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
def chapman(Q, a = 0.925, initial_method='Q0', return_exceed=False):
    """Chapman filter (Chapman, 1991)
    Chapman, Tom G. "Comment on 'Evaluation of Automated Techniques for Base Flow and Recession Analyses' by R. J. Nathan and T. A. McMahon." Water Resources Research 27, no. 7 (1991): 1783–84. https://doi.org/10.1029/91WR01007.

    Args:
        Q (np.array): streamflow
        a (float): recession coefficient
        initial_method (str or float, optional): method to calculate the initial baseflow value.
            Accepted string values are:
            - 'Q0': Use Q[0] as the initial baseflow value.
            - 'min': Use np.min(Q) as the initial baseflow value.
            - 'LH': Calculate the initial baseflow value using the LH method.
            Alternatively, a float value can be provided to directly set the initial baseflow value.
            Default is 'Q0'.
        return_exceed (bool, optional): if True, returns the number of times the
            baseflow exceeds the streamflow.
    """
    if return_exceed:
        b = np.zeros(Q.shape[0] + 1)
    else:
        b = np.zeros(Q.shape[0])

    # Set initial value for b based on the specified method
    if isinstance(initial_method, str):
        if initial_method == 'Q0':
            b[0] = Q[0]
        elif initial_method == 'min':
            b[0] = np.min(Q)
        elif initial_method == 'LH':
            b[0] = lh(Q)[0]  # Calculate the initial value using the LH method
        else:
            raise ValueError(f"Invalid initial_method: {initial_method}")
    else:
        b[0] = initial_method

    for i in range(Q.shape[0] - 1):
        b[i + 1] = (3 * a - 1) / (3 - a) * b[i] + (1 - a) / (3 - a) * (Q[i + 1] + Q[i])
        if b[i + 1] > Q[i + 1]:
            b[i + 1] = Q[i + 1]
            if return_exceed:
                b[-1] += 1
    return b

chapman_maxwell(Q, a, initial_method='Q0', return_exceed=False)

CM filter (Chapman & Maxwell, 1996) Chapman, T. G., Maxwell, A. I. (1996) - Baseflow separation - comparison of numerical methods with tracer experiments, in Hydrol. and Water Resour. Symp., Institution of Engineers Australia, Hobart. pp. 539-545.

Parameters:

Name Type Description Default
Q array

streamflow

required
a float

recession coefficient

required
initial_method str or float

method to calculate the initial baseflow value. Accepted string values are: - 'Q0': Use Q[0] as the initial baseflow value. - 'min': Use np.min(Q) as the initial baseflow value. - 'LH': Calculate the initial baseflow value using the LH method. Alternatively, a float value can be oythoprovided to directly set the initial baseflow value. Default is 'Q0'.

'Q0'

Returns:

Name Type Description
b array

baseflow

Source code in baseflow/separation.py
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
def chapman_maxwell(Q, a, initial_method='Q0' , return_exceed=False):
    """
    CM filter (Chapman & Maxwell, 1996)
    Chapman, T. G., Maxwell, A. I. (1996) - Baseflow separation - comparison of numerical methods with tracer experiments, in Hydrol. and Water Resour. Symp., Institution of Engineers Australia, Hobart. pp. 539-545.

    Args:
        Q (np.array): streamflow
        a (float): recession coefficient
        initial_method (str or float, optional): method to calculate the initial baseflow value.
            Accepted string values are:
            - 'Q0': Use Q[0] as the initial baseflow value.
            - 'min': Use np.min(Q) as the initial baseflow value.
            - 'LH': Calculate the initial baseflow value using the LH method.
            Alternatively, a float value can be oythoprovided to directly set the initial baseflow value.
            Default is 'Q0'.

    Returns:
        b (np.array): baseflow
    """
    if return_exceed:
        b = np.zeros(Q.shape[0] + 1)
    else:
        b = np.zeros(Q.shape[0])

   # Set initial value for b based on the specified method
    if isinstance(initial_method, str):
        if initial_method == 'Q0':
            b[0] = Q[0]
        elif initial_method == 'min':
            b[0] = np.min(Q)
        elif initial_method == 'LH':
            b[0] = lh(Q)[0]  # Calculate the initial value using the LH method
        else:
            raise ValueError(f"Invalid initial_method: {initial_method}")
    else:
        b[0] = initial_method

    for i in range(Q.shape[0] - 1):
        b[i + 1] = a / (2 - a) * b[i] + (1 - a) / (2 - a) * Q[i + 1]
        if b[i + 1] > Q[i + 1]:
            b[i + 1] = Q[i + 1]
            if return_exceed:
                b[-1] += 1
    return b

eckhardt(Q, a, BFImax, initial_method='Q0', return_exceed=False)

Eckhardt filter (Eckhardt, 2005) Eckhardt, K. “How to Construct Recursive Digital Filters for Baseflow Separation.” Hydrological Processes 19, no. 2 (2005): 507–15. https://doi.org/10.1002/hyp.5675.

Parameters:

Name Type Description Default
Q array

streamflow

required
a float

recession coefficient

required
BFImax float

maximum value of baseflow index (BFI)

required
initial_method str or float

method to calculate the initial baseflow value. Accepted string values are: - 'Q0': Use Q[0] as the initial baseflow value. - 'min': Use np.min(Q) as the initial baseflow value. - 'LH': Calculate the initial baseflow value using the LH method. Alternatively, a float value can be provided to directly set the initial baseflow value. Default is 'Q0'.

'Q0'
return_exceed bool

if True, returns the number of times the

False
Source code in baseflow/separation.py
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
def eckhardt(Q, a, BFImax, initial_method='Q0', return_exceed=False):
    """Eckhardt filter (Eckhardt, 2005)
    Eckhardt, K. “How to Construct Recursive Digital Filters for Baseflow Separation.” Hydrological Processes 19, no. 2 (2005): 507–15. https://doi.org/10.1002/hyp.5675.

    Args:
        Q (np.array): streamflow
        a (float): recession coefficient
        BFImax (float): maximum value of baseflow index (BFI)
        initial_method (str or float, optional): method to calculate the initial baseflow value.
            Accepted string values are:
            - 'Q0': Use Q[0] as the initial baseflow value.
            - 'min': Use np.min(Q) as the initial baseflow value.
            - 'LH': Calculate the initial baseflow value using the LH method.
            Alternatively, a float value can be provided to directly set the initial baseflow value.
            Default is 'Q0'.
        return_exceed (bool, optional): if True, returns the number of times the
    """
    if return_exceed:
        b = np.zeros(Q.shape[0] + 1)
    else:
        b = np.zeros(Q.shape[0])

   # Set initial value for b based on the specified method
    if isinstance(initial_method, str):
        if initial_method == 'Q0':
            b[0] = Q[0]
        elif initial_method == 'min':
            b[0] = np.min(Q)
        elif initial_method == 'LH':
            b[0] = lh(Q)[0]  # Calculate the initial value using the LH method
        else:
            raise ValueError(f"Invalid initial_method: {initial_method}")
    else:
        b[0] = initial_method

    for i in range(Q.shape[0] - 1):
        b[i + 1] = ((1 - BFImax) * a * b[i] + (1 - a) * BFImax * Q[i + 1]) / (1 - a * BFImax)
        if b[i + 1] > Q[i + 1]:
            b[i + 1] = Q[i + 1]
            if return_exceed:
                b[-1] += 1
    return b

ewma(Q, e, initial_method='Q0', return_exceed=False)

exponential weighted moving average (EWMA) filter (Tularam & Ilahee, 2008) Tularam, Gurudeo Anand, and Mahbub Ilahee. “Exponential Smoothing Method of Base Flow Separation and Its Impact on Continuous Loss Estimates.” American Journal of Environmental Sciences 4, no. 2 (April 30, 2008): 136–44. https://doi.org/10.3844/ajessp.2008.136.144.

Parameters:

Name Type Description Default
Q array

streamflow

required
e float

smoothing parameter

required
initial_method str or float

method to calculate the initial baseflow value. Accepted string values are: - 'Q0': Use Q[0] as the initial baseflow value. - 'min': Use np.min(Q) as the initial baseflow value. - 'LH': Calculate the initial baseflow value using the LH method. Alternatively, a float value can be provided to directly set the initial baseflow value. Default is 'Q0'.

'Q0'
return_exceed bool

if True, returns the number of times the

False
Source code in baseflow/separation.py
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
def ewma(Q, e, initial_method='Q0', return_exceed=False):
    """exponential weighted moving average (EWMA) filter (Tularam & Ilahee, 2008)
    Tularam, Gurudeo Anand, and Mahbub Ilahee. “Exponential Smoothing Method of Base Flow Separation and Its Impact on Continuous Loss Estimates.” American Journal of Environmental Sciences 4, no. 2 (April 30, 2008): 136–44. https://doi.org/10.3844/ajessp.2008.136.144.

    Args:
        Q (np.array): streamflow
        e (float): smoothing parameter
        initial_method (str or float, optional): method to calculate the initial baseflow value.
            Accepted string values are:
            - 'Q0': Use Q[0] as the initial baseflow value.
            - 'min': Use np.min(Q) as the initial baseflow value.
            - 'LH': Calculate the initial baseflow value using the LH method.
            Alternatively, a float value can be provided to directly set the initial baseflow value.
            Default is 'Q0'.
        return_exceed (bool, optional): if True, returns the number of times the
    """
    if return_exceed:
        b = np.zeros(Q.shape[0] + 1)
    else:
        b = np.zeros(Q.shape[0])

    # Set initial value for b based on the specified method
    if isinstance(initial_method, str):
        if initial_method == 'Q0':
            b[0] = Q[0]
        elif initial_method == 'min':
            b[0] = np.min(Q)
        elif initial_method == 'LH':
            b[0] = lh(Q)[0]  # Calculate the initial value using the LH method
        else:
            raise ValueError(f"Invalid initial_method: {initial_method}")
    else:
        b[0] = initial_method

    for i in range(Q.shape[0] - 1):
        b[i + 1] = (1 - e) * b[i] + e * Q[i + 1]
        if b[i + 1] > Q[i + 1]:
            b[i + 1] = Q[i + 1]
            if return_exceed:
                b[-1] += 1
    return b

fixed(Q, area=None)

Fixed interval graphical method from HYSEP program (Sloto & Crouse, 1996) Sloto, R. A., & Crouse, M. Y. (1996). HYSEP: A Computer Program for Streamflow Hydrograph Separation and Analysis (96-4040). Reston, VA: U.S. Geological Survey. https://doi.org/10.3133/wri964040.

Parameters:

Name Type Description Default
Q array

streamflow

required
area float

basin area in km^2

None
Source code in baseflow/separation.py
244
245
246
247
248
249
250
251
252
253
def fixed(Q, area=None):
    """Fixed interval graphical method from HYSEP program (Sloto & Crouse, 1996)
    Sloto, R. A., & Crouse, M. Y. (1996). HYSEP: A Computer Program for Streamflow Hydrograph Separation and Analysis (96-4040). Reston, VA: U.S. Geological Survey. https://doi.org/10.3133/wri964040.

    Args:
        Q (np.array): streamflow
        area (float): basin area in km^2
    """
    inN = hysep_interval(area)
    return fixed_interpolation(Q, inN)

furey(Q, a, A, initial_method='Q0', return_exceed=False)

Furey digital filter (Furey & Gupta, 2001, 2003) Furey, Peter R., and Vijay K. Gupta. “A Physically Based Filter for Separating Base Flow from Streamflow Time Series.” Water Resources Research 37, no. 11 (2001): 2709–22. https://doi.org/10.1029/2001WR000243.

Parameters:

Name Type Description Default
Q array

streamflow

required
a float

recession coefficient

required
A float

calibrated in baseflow.param_estimate

required
initial_method str or float

method to calculate the initial baseflow value. Accepted string values are: - 'Q0': Use Q[0] as the initial baseflow value. - 'min': Use np.min(Q) as the initial baseflow value. - 'LH': Calculate the initial baseflow value using the LH method. Alternatively, a float value can be provided to directly set the initial baseflow value. Default is 'Q0'.

'Q0'
return_exceed bool

if True, returns the number of times the baseflow exceeds the streamflow.

False
Source code in baseflow/separation.py
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
def furey(Q, a, A, initial_method='Q0', return_exceed=False):
    """Furey digital filter (Furey & Gupta, 2001, 2003)
    Furey, Peter R., and Vijay K. Gupta. “A Physically Based Filter for Separating Base Flow from Streamflow Time Series.” Water Resources Research 37, no. 11 (2001): 2709–22. https://doi.org/10.1029/2001WR000243.

    Args:
        Q (np.array): streamflow
        a (float): recession coefficient
        A (float): calibrated in baseflow.param_estimate
        initial_method (str or float, optional): method to calculate the initial baseflow value.
            Accepted string values are:
            - 'Q0': Use Q[0] as the initial baseflow value.
            - 'min': Use np.min(Q) as the initial baseflow value.
            - 'LH': Calculate the initial baseflow value using the LH method.
            Alternatively, a float value can be provided to directly set the initial baseflow value.
            Default is 'Q0'.
        return_exceed (bool, optional): if True, returns the number of times the
            baseflow exceeds the streamflow.
    """
    if return_exceed:
        b = np.zeros(Q.shape[0] + 1)
    else:
        b = np.zeros(Q.shape[0])

    # Set initial value for b based on the specified method
    if isinstance(initial_method, str):
        if initial_method == 'Q0':
            b[0] = Q[0]
        elif initial_method == 'min':
            b[0] = np.min(Q)
        elif initial_method == 'LH':
            b[0] = lh(Q)[0]  # Calculate the initial value using the LH method
        else:
            raise ValueError(f"Invalid initial_method: {initial_method}")
    else:
        b[0] = initial_method

    for i in range(Q.shape[0] - 1):
        b[i + 1] = (a - A * (1 - a)) * b[i] + A * (1 - a) * Q[i]
        if b[i + 1] > Q[i + 1]:
            b[i + 1] = Q[i + 1]
            if return_exceed:
                b[-1] += 1
    return b

hyd_run(streamflow, k=0.9, passes=4)

Reference
  • This code was written by Ali Javed and Scott Hamshaw as a python version of a baseflow separation function from the MATLAB HydRun toolbox:

    • Hamshaw, Scott D., Donna M. Rizzo, Ali Javed, and Linh Nguyen. "Watershed data science at the event scale: Revealing insights in watershed function through analysis of concentration-discharge relationships." In AGU Fall Meeting Abstracts, vol. 2020, pp. H077-08. 2020.
  • HydRun:

    • Tang, Weigang, and Sean K. Carey. 2017. “Hyd R Un: A MATLAB Toolbox for Rainfall–Runoff Analysis.” Hydrological Processes 31 (15): 2670–82. https://doi.org/10.1002/hyp.11185.s
  • HydRun cites the baseflow separation function is adpoted from the R package EcoHydRology, also citing the filter of Nathan and McMahon (1990):

    • Nathan, R. J., and T. A. McMahon. 1990. “Evaluation of Automated Techniques for Base Flow and Recession Analyses.” Water Resources Research 26 (7): 1465–73. https://doi.org/10.1029/WR026i007p01465.

Parameters:

Name Type Description Default
streamflow ndarray

A numpy array of streamflow values in chronological order.

required
k float

A filter coefficient between 0 and 1 (typically 0.9). Defaults to 0.9.

0.9
passes int

Number of times the filter passes through the data (typically 4). Defaults to 4.

4

Returns:

Type Description

numpy.ndarray: A numpy array of baseflow values.

Example
>>> import numpy as np
>>> streamflow = np.array([10, 15, 20, 18, 12])
>>> baseflow = hyd_run(streamflow)
>>> print(baseflow)
[10.         10.90909091 13.27272727 15.18181818 14.36363636]
Source code in baseflow/separation.py
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
def hyd_run(streamflow, k=0.9, passes=4):
    """
    ???+note "Reference"

        - This code was written by Ali Javed and Scott Hamshaw as a python version of
        a baseflow separation function from the MATLAB HydRun toolbox:
            * Hamshaw, Scott D., Donna M. Rizzo, Ali Javed, and Linh Nguyen. "Watershed data science at the event scale: Revealing insights in watershed function through analysis of concentration-discharge relationships." In AGU Fall Meeting Abstracts, vol. 2020, pp. H077-08. 2020.

        - HydRun:
            * Tang, Weigang, and Sean K. Carey. 2017. “Hyd R Un: A MATLAB Toolbox for
        Rainfall–Runoff Analysis.” Hydrological Processes 31 (15): 2670–82.
        https://doi.org/10.1002/hyp.11185.s

        - HydRun cites the baseflow separation function is adpoted from the R package
        EcoHydRology, also citing the filter of Nathan and McMahon (1990):
            * Nathan, R. J., and T. A. McMahon. 1990. “Evaluation of Automated Techniques
            for Base Flow and Recession Analyses.” Water Resources Research 26 (7):
            1465–73. https://doi.org/10.1029/WR026i007p01465.

    Args:
        streamflow (numpy.ndarray): A numpy array of streamflow values in chronological order.
        k (float, optional): A filter coefficient between 0 and 1 (typically 0.9). Defaults to 0.9.
        passes (int, optional): Number of times the filter passes through the data (typically 4). Defaults to 4.

    Returns:
        numpy.ndarray: A numpy array of baseflow values.

    ??? example
        ```python
        >>> import numpy as np
        >>> streamflow = np.array([10, 15, 20, 18, 12])
        >>> baseflow = hyd_run(streamflow)
        >>> print(baseflow)
        [10.         10.90909091 13.27272727 15.18181818 14.36363636]
        ```
    """
    # Convert to numpy array and handle NaN values
    Q = np.array(streamflow)
    Q = Q[~np.isnan(Q)]

    # Initialize baseflow list
    baseflow = np.zeros_like(Q)
    baseflow[0] = Q[0]  # Set first baseflow value to first streamflow value

    for p in range(1, passes + 1):
        # Forward and backward pass
        if p % 2 == 1:
            start, end, step = 0, len(Q), 1
        else:
            start, end, step = len(Q) - 1, -1, -1

        for i in range(start + step, end, step):
            tmp = k * baseflow[i - step] + (1 - k) * (Q[i] + Q[i - step]) / 2
            baseflow[i] = min(tmp, Q[i])

    return baseflow

lh(Q, beta=0.925, return_exceed=False)

LH digital filter (Lyne & Hollick, 1979) Lyne, V. and Hollick, M. (1979) Stochastic Time-Variable Rainfall-Runoff Modeling. Institute of Engineers Australia National Conference, 89-93.

Parameters:

Name Type Description Default
Q array

streamflow

required
beta float

filter parameter, 0.925 recommended by (Nathan & McMahon, 1990)

0.925
Source code in baseflow/separation.py
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
def lh(Q, beta=0.925, return_exceed=False):
    """LH digital filter (Lyne & Hollick, 1979)
    Lyne, V. and Hollick, M. (1979) Stochastic Time-Variable Rainfall-Runoff Modeling. Institute of Engineers Australia National Conference, 89-93.

    Args:
        Q (np.array): streamflow
        beta (float): filter parameter, 0.925 recommended by (Nathan & McMahon, 1990)
    """
    if return_exceed:
        b = np.zeros(Q.shape[0] + 1)
    else:
        b = np.zeros(Q.shape[0])

    # first pass
    b[0] = Q[0]
    for i in range(Q.shape[0] - 1):
        b[i + 1] = beta * b[i] + (1 - beta) / 2 * (Q[i] + Q[i + 1])
        if b[i + 1] > Q[i + 1]:
            b[i + 1] = Q[i + 1]
            if return_exceed:
                b[-1] += 1

    # second pass
    b1 = np.copy(b)
    for i in range(Q.shape[0] - 2, -1, -1):
        b[i] = beta * b[i + 1] + (1 - beta) / 2 * (b1[i + 1] + b1[i])
        if b[i] > b1[i]:
            b[i] = b1[i]
            if return_exceed:
                b[-1] += 1
    return b

lh_multi(Q, beta=0.925, num_pass=2, return_exceed=False)

Applies a low-pass filter to the input time series Q using the Lyne-Hollick (LH) recursive digital filter.

The filter is applied in multiple passes, with the number of passes controlled by the num_pass parameter. The filter uses a smoothing parameter beta to control the degree of filtering.

Spongberg, M. E. “Spectral Analysis of Base Flow Separation with Digital Filters.” Water Resources Research 36, no. 3 (2000): 745–52. https://doi.org/10.1029/1999WR900303.

If return_exceed is True, the function will also return the number of times the filtered output b exceeds the original input Q.

Lyne, V. and Hollick, M. (1979) Stochastic Time-Variable Rainfall-Runoff Modeling. Institute of Engineers Australia National Conference, 89-93.

Spongberg, M. E. “Spectral Analysis of Base Flow Separation with Digital Filters.” Water Resources Research 36, no. 3 (2000): 745–52. https://doi.org/10.1029/1999WR900303.

Parameters:

Name Type Description Default
Q ndarray

The input time series to be filtered.

required
beta float

The smoothing parameter for the LH filter, between 0 and 1. Defaults to 0.925.

0.925
num_pass int

The number of filter passes to apply. Defaults to 2.

2
return_exceed bool

If True, the function will return the number of times the filtered output exceeds the original input. Defaults to False.

False

Returns:

Name Type Description

numpy.ndarray: The filtered output time series.

int optional

The number of times the filtered output exceeds the original input, if return_exceed is True.

Source code in baseflow/separation.py
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
def lh_multi(Q, beta=0.925, num_pass=2, return_exceed=False):
    """
    Applies a low-pass filter to the input time series `Q` using the Lyne-Hollick (LH) recursive digital filter.

    The filter is applied in multiple passes, with the number of passes controlled by the `num_pass` parameter. The filter uses a smoothing parameter `beta` to control the degree of filtering.

    Spongberg, M. E. “Spectral Analysis of Base Flow Separation with Digital Filters.” Water Resources Research 36, no. 3 (2000): 745–52. https://doi.org/10.1029/1999WR900303.

    If `return_exceed` is True, the function will also return the number of times the filtered output `b` exceeds the original input `Q`.

    Lyne, V. and Hollick, M. (1979) Stochastic Time-Variable Rainfall-Runoff Modeling. Institute of Engineers Australia National Conference, 89-93. 

    Spongberg, M. E. “Spectral Analysis of Base Flow Separation with Digital Filters.” Water Resources Research 36, no. 3 (2000): 745–52. https://doi.org/10.1029/1999WR900303.

    Args:
        Q (numpy.ndarray): The input time series to be filtered.
        beta (float, optional): The smoothing parameter for the LH filter, between 0 and 1. Defaults to 0.925.
        num_pass (int, optional): The number of filter passes to apply. Defaults to 2.
        return_exceed (bool, optional): If True, the function will return the number of times the filtered output exceeds the original input. Defaults to False.

    Returns:
        numpy.ndarray: The filtered output time series.
        int (optional): The number of times the filtered output exceeds the original input, if `return_exceed` is True.
    """
    if return_exceed:
        b = np.zeros(Q.shape[0] + 1)
    else:
        b = np.zeros(Q.shape[0])

    b[0] = Q[0]

    for n in range(num_pass):
        if n != 0:
            b = np.flip(b, axis=0)
            Q = b.copy()

        for i in range(Q.shape[0] - 1):
            b[i + 1] = beta * b[i] + (1 - beta) / 2 * (Q[i] + Q[i + 1])
            if b[i + 1] > Q[i + 1]:
                b[i + 1] = Q[i + 1]
                if return_exceed:
                    b[-1] += 1

    if num_pass % 2 == 0:
        b = np.flip(b, axis=0)

    return b

local(Q, b_LH, area=None, return_exceed=False)

Local minimum graphical method from HYSEP program (Sloto & Crouse, 1996)

Parameters:

Name Type Description Default
Q array

streamflow

required
area float

basin area in km^2

None
Source code in baseflow/separation.py
449
450
451
452
453
454
455
456
457
458
459
460
461
462
def local(Q, b_LH, area=None, return_exceed=False):
    """Local minimum graphical method from HYSEP program (Sloto & Crouse, 1996)

    Args:
        Q (np.array): streamflow
        area (float): basin area in km^2
    """
    idx_turn = local_turn(Q, hysep_interval(area))
    if idx_turn.shape[0] < 3:
        raise IndexError('Less than 3 turning points found')
    b = linear_interpolation(Q, idx_turn, return_exceed=return_exceed)
    b[:idx_turn[0]] = b_LH[:idx_turn[0]]
    b[idx_turn[-1] + 1:] = b_LH[idx_turn[-1] + 1:]
    return b

slide(Q, area)

Slide interval graphical method from HYSEP program (Sloto & Crouse, 1996) Sloto, R. A., & Crouse, M. Y. (1996). HYSEP: A Computer Program for Streamflow Hydrograph Separation and Analysis (96-4040). Reston, VA: U.S. Geological Survey. https://doi.org/10.3133/wri964040.

Parameters:

Name Type Description Default
Q array

streamflow

required
area float

basin area in km^2

required
Source code in baseflow/separation.py
490
491
492
493
494
495
496
497
498
499
def slide(Q, area):
    """Slide interval graphical method from HYSEP program (Sloto & Crouse, 1996)
    Sloto, R. A., & Crouse, M. Y. (1996). HYSEP: A Computer Program for Streamflow Hydrograph Separation and Analysis (96-4040). Reston, VA: U.S. Geological Survey. https://doi.org/10.3133/wri964040.

    Args:
        Q (np.array): streamflow
        area (float): basin area in km^2
    """
    inN = hysep_interval(area)
    return slide_interpolation(Q, inN)

strict_baseflow(Q, ice=None, quantile=0.9)

Identify the strict baseflow component of a flow time series.

This function applies a series of heuristic rules to identify the strict baseflow component of a flow time series. The rules are based on the behavior of the derivative of the flow time series, as well as the magnitude of the flow values.

The function returns a boolean mask indicating the time steps that correspond to the strict baseflow component.

Parameters:

Name Type Description Default
Q ndarray

The flow time series.

required
ice ndarray

A boolean mask indicating time steps with ice conditions, which can invalidate the groundwater-baseflow relationship.

None
quantile float

The quantile value used to identify major events. Default is 0.9 (90th percentile).

0.9

Returns:

Type Description

numpy.ndarray: A boolean mask indicating the time steps that correspond to the strict baseflow component.

Source code in baseflow/separation.py
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
def strict_baseflow(Q, ice=None, quantile=0.9):
    """
    Identify the strict baseflow component of a flow time series.

    This function applies a series of heuristic rules to identify the strict baseflow
    component of a flow time series. The rules are based on the behavior of the
    derivative of the flow time series, as well as the magnitude of the flow values.

    The function returns a boolean mask indicating the time steps that correspond to
    the strict baseflow component.

    Parameters:
        Q (numpy.ndarray): The flow time series.
        ice (numpy.ndarray, optional): A boolean mask indicating time steps with ice
            conditions, which can invalidate the groundwater-baseflow relationship.
        quantile (float, optional): The quantile value used to identify major events.
            Default is 0.9 (90th percentile).

    Returns:
        numpy.ndarray: A boolean mask indicating the time steps that correspond to
            the strict baseflow component.
    """
    dQ = (Q[2:] - Q[:-2]) / 2

    # 1. flow data associated with positive and zero values of dy / dt
    wet1 = np.concatenate([[True], dQ >= 0, [True]])

    # 2. previous 2 points before points with dy/dt≥0, as well as the next 3 points
    idx_first = np.where(wet1[1:].astype(int) - wet1[:-1].astype(int) == 1)[0] + 1
    idx_last = np.where(wet1[1:].astype(int) - wet1[:-1].astype(int) == -1)[0]
    idx_before = np.repeat([idx_first], 2) - np.tile(range(1, 3), idx_first.shape)
    idx_next = np.repeat([idx_last], 3) + np.tile(range(1, 4), idx_last.shape)
    idx_remove = np.concatenate([idx_before, idx_next])
    wet2 = np.full(Q.shape, False)
    wet2[idx_remove.clip(min=0, max=Q.shape[0] - 1)] = True

    # 3. five data points after major events (quantile)
    growing = np.concatenate([[True], (Q[1:] - Q[:-1]) >= 0, [True]])
    idx_major = np.where((Q >= np.quantile(Q, quantile)) & growing[:-1] & ~growing[1:])[0]
    idx_after = np.repeat([idx_major], 5) + np.tile(range(1, 6), idx_major.shape)
    wet3 = np.full(Q.shape, False)
    wet3[idx_after.clip(min=0, max=Q.shape[0] - 1)] = True

    # 4. flow data followed by a data point with a larger value of -dy / dt
    wet4 = np.concatenate([[True], dQ[1:] - dQ[:-1] < 0, [True, True]])

    # dry points, namely strict baseflow
    dry = ~(wet1 + wet2 + wet3 + wet4)

    # avoid ice conditions which invalidate the groundwater-baseflow relationship
    if ice is not None:
        dry[ice] = False

    return dry

ukih(Q, b_LH, return_exceed=False)

graphical method developed by UK Institute of Hydrology (UKIH, 1980) Aksoy, Hafzullah, Ilker Kurt, and Ebru Eris. “Filtered Smoothed Minima Baseflow Separation Method.” Journal of Hydrology 372, no. 1 (June 15, 2009): 94–101. https://doi.org/10.1016/j.jhydrol.2009.03.037.

Parameters:

Name Type Description Default
Q array

streamflow

required
return_exceed bool

if True, returns the number of times the baseflow exceeds the streamflow.

False
Source code in baseflow/separation.py
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
def ukih(Q, b_LH, return_exceed=False):
    """graphical method developed by UK Institute of Hydrology (UKIH, 1980)
    Aksoy, Hafzullah, Ilker Kurt, and Ebru Eris. “Filtered Smoothed Minima Baseflow Separation Method.” Journal of Hydrology 372, no. 1 (June 15, 2009): 94–101. https://doi.org/10.1016/j.jhydrol.2009.03.037.

    Args:
        Q (np.array): streamflow
        return_exceed (bool, optional): if True, returns the number of times the
            baseflow exceeds the streamflow.
    """
    N = 5
    block_end = Q.shape[0] // N * N
    idx_min = np.argmin(Q[:block_end].reshape(-1, N), axis=1)
    idx_min = idx_min + np.arange(0, block_end, N)
    idx_turn = ukih_turn(Q, idx_min)
    if idx_turn.shape[0] < 3:
        raise IndexError('Less than 3 turning points found')
    b = linear_interpolation(Q, idx_turn, return_exceed=return_exceed)
    b[:idx_turn[0]] = b_LH[:idx_turn[0]]
    b[idx_turn[-1] + 1:] = b_LH[idx_turn[-1] + 1:]
    return b

what(streamflow, BFImax, alpha)

Separates baseflow and quickflow from a streamflow time series using the WHAT method.

Parameters:

Name Type Description Default
streamflow ndarray

A numpy array of streamflow values.

required
BFImax float

The maximum baseflow index (BFI) value.

required
alpha float

A filter parameter.

required

Returns:

Name Type Description
tuple

A tuple containing two numpy arrays: baseflow and quickflow.

Example

import numpy as np streamflow = np.array([10, 15, 20, 18, 12]) baseflow = hyd_run(streamflow) print(baseflow) [10. 10.90909091 13.27272727 15.18181818 14.36363636]

Source code in baseflow/separation.py
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
def what(streamflow, BFImax, alpha):
    """
    Separates baseflow and quickflow from a streamflow time series using the WHAT method.

    Args:
        streamflow (numpy.ndarray): A numpy array of streamflow values.
        BFImax (float): The maximum baseflow index (BFI) value.
        alpha (float): A filter parameter.

    Returns:
        tuple: A tuple containing two numpy arrays: baseflow and quickflow.

    Example:
        >>> import numpy as np
        >>> streamflow = np.array([10, 15, 20, 18, 12])
        >>> baseflow = hyd_run(streamflow)
        >>> print(baseflow)
        [10.         10.90909091 13.27272727 15.18181818 14.36363636]

    """
    baseflow = np.zeros_like(streamflow)

    for t in range(1, len(streamflow)):
        baseflow[t] = ((1 - BFImax) * alpha * baseflow[t-1] + (1 - alpha) * BFImax * streamflow[t]) / (1 - alpha * BFImax)

    quickflow = streamflow - baseflow

    return baseflow

willems(Q, a, w, initial_method='Q0', return_exceed=False)

digital filter (Willems, 2009)

Parameters:

Name Type Description Default
Q array

streamflow

required
a float

recession coefficient

required
w float

case-specific average proportion of the quick flow in the streamflow, calibrated in baseflow.param_estimate

required
initial_method str or float

method to calculate the initial baseflow value. Accepted string values are: - 'Q0': Use Q[0] as the initial baseflow value. - 'min': Use np.min(Q) as the initial baseflow value. - 'LH': Calculate the initial baseflow value using the LH method. Alternatively, a float value can be provided to directly set the initial baseflow value. Default is 'Q0'.

'Q0'
return_exceed bool

if True, returns the number of times the baseflow exceeds the streamflow.

False
Source code in baseflow/separation.py
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
def willems(Q, a, w, initial_method='Q0', return_exceed=False):
    """digital filter (Willems, 2009)

    Args:
        Q (np.array): streamflow
        a (float): recession coefficient
        w (float): case-specific average proportion of the quick flow
                   in the streamflow, calibrated in baseflow.param_estimate
        initial_method (str or float, optional): method to calculate the initial baseflow value.
            Accepted string values are:
            - 'Q0': Use Q[0] as the initial baseflow value.
            - 'min': Use np.min(Q) as the initial baseflow value.
            - 'LH': Calculate the initial baseflow value using the LH method.
            Alternatively, a float value can be provided to directly set the initial baseflow value.
            Default is 'Q0'.
        return_exceed (bool, optional): if True, returns the number of times the
            baseflow exceeds the streamflow.
    """
    if return_exceed:
        b = np.zeros(Q.shape[0] + 1)
    else:
        b = np.zeros(Q.shape[0])

    # Set initial value for b based on the specified method
    if isinstance(initial_method, str):
        if initial_method == 'Q0':
            b[0] = Q[0]
        elif initial_method == 'min':
            b[0] = np.min(Q)
        elif initial_method == 'LH':
            b[0] = lh(Q)[0]  # Calculate the initial value using the LH method
        else:
            raise ValueError(f"Invalid initial_method: {initial_method}")
    else:
        b[0] = initial_method

    v = (1 - w) * (1 - a) / (2 * w)
    for i in range(Q.shape[0] - 1):
        b[i + 1] = (a - v) / (1 + v) * b[i] + v / (1 + v) * (Q[i] + Q[i + 1])
        if b[i + 1] > Q[i + 1]:
            b[i + 1] = Q[i + 1]
            if return_exceed:
                b[-1] += 1
    return b