Skip to content

Commit

Permalink
Update _streamflow_flow_indices.py
Browse files Browse the repository at this point in the history
  • Loading branch information
faimahsho committed Jul 16, 2024
1 parent 48ded61 commit 0507d4c
Show file tree
Hide file tree
Showing 4 changed files with 220 additions and 58 deletions.
6 changes: 3 additions & 3 deletions docs/references.bib
Original file line number Diff line number Diff line change
Expand Up @@ -2153,7 +2153,7 @@ @article{droogers2002
type = {Article}
}

@article{article,
@article{addor2018,
author = {Addor, Nans and Nearing, Grey and Prieto, Cristina and Newman, A. and Le Vine, Nataliya and Clark, Martyn},
year = {2018},
month = {11},
Expand All @@ -2163,7 +2163,7 @@ @article{article
doi = {10.1029/2018WR022606}
}

@article{article,
@article{Clausen2000,
author = {Clausen, B and Biggs, Barry},
year = {2000},
month = {11},
Expand All @@ -2174,7 +2174,7 @@ @article{article
doi = {10.1016/S0022-1694(00)00306-1}
}

@article{article,
@article{Olden2003,
author = {Olden, Julian and Poff, N.},
year = {2003},
month = {03},
Expand Down
32 changes: 31 additions & 1 deletion tests/test_hydrology.py
Original file line number Diff line number Diff line change
Expand Up @@ -64,9 +64,39 @@ def test_simple(self, snw_series, pr_series):

# 1 kg/ m2 /d of rain on day 11
b = np.zeros(365)
b[11] = 1.0 / 60**2 / 24
b[11] = 1.0 / 60 ** 2 / 24
pr = pr_series(b, start="1999-07-01")

out = xci.melt_and_precip_max(snw, pr)
np.testing.assert_array_equal(out, 2)
assert out.units == "kg m-2"


class TestFlowindex:
def test_simple(self, q_series):
a = np.ones(365) * 10
a[10:30] = 50
q = q_series(a)
out = xci.flow_index(q, 0.95)
np.testing.assert_array_equal(out, 5)


class TestHighflowfrequency:
def test_simple(self, q_series):
a = np.zeros(365)
a[50:60] = 10
a[200:210] = 20
q = q_series(a)
out = xci.high_flow_frequency(q, 9, freq='YS')
np.testing.assert_array_equal(out, 20)


class TestLowflowfrequency:
def test_simple(self, q_series):
a = np.ones(365) * 10
a[50:60] = 1
a[200:210] = 1
q = q_series(a)
out = xci.low_flow_frequency(q, 0.2, freq='YS')
np.testing.assert_array_equal(out, 20)

138 changes: 137 additions & 1 deletion xclim/indices/_hydrology.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,12 @@
from __future__ import annotations

import numpy as np
import xarray
import xarray as xr

from xclim.core.calendar import get_calendar
from xclim.core.missing import at_least_n_valid
from xclim.core.units import declare_units, rate2amount
from xclim.indices.generic import compare, threshold_count

from . import generic

Expand All @@ -19,6 +20,9 @@
"snow_melt_we_max",
"snw_max",
"snw_max_doy",
"flow_index",
"high_flow_frequency",
"low_flow_frequency"
]


Expand Down Expand Up @@ -279,3 +283,135 @@ def melt_and_precip_max(
out = agg.resample(time=freq).max(dim="time")
out.attrs["units"] = snw.units
return out


@declare_units(q="[discharge]")
def flow_index(q: xr.DataArray, p: float = 0.95) -> xr.DataArray:
"""
Calculate the Qp (pth percentile of daily streamflow) normalized by the mean flow.
Parameters
----------
q : xarray.DataArray
Daily streamflow data.
p : float
Percentile for
args = (<xarray.DataArray 'q' ()> Size: 8B
array(5.)
Coordinates:
quantile float64 8B 0.95
Attributes:
units: , 0.08202247191011236)
kwds = {}
@wraps(func)
def inner(*args, **kwds):
with self._recreate_cm():
> return func(*args, **kwds)
E AssertionError:
E Arrays are not almost equal to 7 decimals
E ACTUAL: <xarray.DataArray 'q' ()> Size: 8B
E array(5.)
E Coordinates:...
E DESIRED: 0.08202247191011236
/Library/Frameworks/Python.framework/Versions/3.12/lib/python3.12/contextlib.py:81: AssertionError
Process finished with exit code 1
calculating the flow index, between 0 and 1. Default of 0.95 is for high flows.
Returns
-------
xarray.DataArray
Normalized Qp, which is the p th percentile of daily streamflow normalized by the median flow.
Reference:
1. Addor, Nans & Nearing, Grey & Prieto, Cristina & Newman, A. & Le Vine, Nataliya & Clark, Martyn. (2018). A Ranking of Hydrological Signatures Based on Their Predictability in Space. Water Resources Research. 10.1029/2018WR022606.
2. Clausen, B., & Biggs, B. J. F. (2000). Flow variables for ecological studies in temperate streams: Groupings based on covariance. Journal of Hydrology, 237(3–4), 184–197. https://doi.org/10.1016/S0022-1694(00)00306-1
"""
qp = q.quantile(p, dim="time")
q_median = q.median(dim="time")
out = qp / q_median
out.attrs["units"] = " "
return out


@declare_units(q="[discharge]")
def high_flow_frequency(
q: xr.DataArray,
threshold_factor: int = 9,
freq: str = "A-SEP",
) -> xr.DataArray:
"""
Calculate the mean number of days in a given period with flows greater than a specified threshold. By default, the period is the water year starting on 1st October and ending on 30th September, as commonly defined in North America.
Parameters
----------
q : xarray.DataArray
Daily streamflow data.
threshold_factor : int
Factor by which the median flow is multiplied to set the high flow threshold, default is 9.
freq : str, optional
Resampling frequency, default is 'A-SEP' for water year ending in September.
op : {">", "<", "gt", "lt"}, optional
Comparison operation. Default: "<".
Returns
-------
xarray.DataArray
Calculated mean of high flow days per water year
References
----------
1. Addor, Nans & Nearing, Grey & Prieto, Cristina & Newman, A. & Le Vine, Nataliya & Clark, Martyn. (2018). A Ranking of Hydrological Signatures Based on Their Predictability in Space. Water Resources Research. 10.1029/2018WR022606.
2. Clausen, B., & Biggs, B. J. F. (2000). Flow variables for ecological studies in temperate streams: Groupings based on covariance. Journal of Hydrology, 237(3–4), 184–197. https://doi.org/10.1016/S0022-1694(00)00306-1
"""

median_flow = q.median(dim="time")
with xr.set_options(keep_attrs=True):
threshold = threshold_factor * median_flow
high_flow_days = compare(q, op=">", right=threshold).resample(time=freq).sum(dim="time")
out = high_flow_days.mean(dim="time")
out.attrs["units"] = "days"
return out


@declare_units(q="[discharge]")
def low_flow_frequency(
q: xr.DataArray,
threshold_factor: float = 0.2,
freq: str = "A-SEP",
) -> xr.DataArray:
"""
Calculate the mean number of days in a given period with flows lower than a specified threshold. By default, the period is the water year starting on 1st October and ending on 30th September, as commonly defined in North America.
Parameters
----------
q : xarray.DataArray
Daily streamflow data.
threshold_factor : float
Factor by which the mean flow is multiplied to set the low flow threshold, default is 0.2.
freq : str, optional
Resampling frequency, default is 'A-SEP' for water year ending in September.
op : {">", "<", "gt", "lt"}, optional
Comparison operation. Default: "<".
Returns
-------
xarray.DataArray
Calculated mean of low flow days per water year
References
----------
Olden, J. D., & Poff, N. L. (2003). Redundancy and the choice of hydrologic indices for characterizing streamflow regimes. River Research and
Applications, 19(2), 101–121. https://doi.org/10.1002/rra.700
"""

mean_flow = q.mean(dim="time")
with xr.set_options(keep_attrs=True):
threshold = threshold_factor * mean_flow
low_flow_days = compare(q, op="<", right=threshold).resample(time=freq).sum(dim="time")
out = low_flow_days.mean(dim="time")
out.attrs["units"] = "days"
return out
102 changes: 49 additions & 53 deletions xclim/indices/_streamflow_flow_indices.py
Original file line number Diff line number Diff line change
@@ -1,17 +1,20 @@
from __future__ import annotations

import xarray as xr
import numpy as np
from xclim.core.units import declare_units
from xclim.indices.generic import compare, threshold_count

__all__ = [
"flow_index",
"high_flow_frequency",
"low_flow_frequency",
]

@declare_units(q="[discharge]")
def flow_index(q: xr.DataArray, p: float = 0.95) -> xr.DataArray:
"""
Calculate the Qp (pth percentile of daily streamflow) normalized by the mean flow.
Reference:
1. Addor, Nans & Nearing, Grey & Prieto, Cristina & Newman, A. & Le Vine, Nataliya & Clark, Martyn. (2018). A Ranking of Hydrological Signatures Based on Their Predictability in Space. Water Resources Research. 10.1029/2018WR022606.
2. Clausen, B., & Biggs, B. J. F. (2000). Flow variables for ecological studies in temperate streams: Groupings based on covariance. Journal of Hydrology, 237(3–4), 184–197. https://doi.org/10.1016/S0022-1694(00)00306-1
Parameters
----------
q : xarray.DataArray
Expand All @@ -22,103 +25,96 @@ def flow_index(q: xr.DataArray, p: float = 0.95) -> xr.DataArray:
Returns
-------
xarray.DataArray
out = Normalized Qp, which is the p th percentile of daily streamflow normalized by the median flow.
Normalized Qp, which is the p th percentile of daily streamflow normalized by the median flow.
Reference:
1. Addor, Nans & Nearing, Grey & Prieto, Cristina & Newman, A. & Le Vine, Nataliya & Clark, Martyn. (2018). A Ranking of Hydrological Signatures Based on Their Predictability in Space. Water Resources Research. 10.1029/2018WR022606.
2. Clausen, B., & Biggs, B. J. F. (2000). Flow variables for ecological studies in temperate streams: Groupings based on covariance. Journal of Hydrology, 237(3–4), 184–197. https://doi.org/10.1016/S0022-1694(00)00306-1
"""
qp = q.quantile(p, dim="time")
q_median = q.median(dim="time")
out = qp / q_median
out.attrs["units"] = " "
return out.rename("flow_index")
return out


@declare_units(q="[discharge]")
def high_flow_frequency(
q: xr.DataArray,
threshold_factor: int = 9
threshold_factor: int = 9,
freq: str = "A-SEP",
statistic: str = "mean",
) -> xr.DataArray:
"""
Calculate the mean number of days in a given period with flows greater than a specified threshold. By default, the period is the water year starting on 1st October and ending on 30th September, as commonly defined in North America.
Reference:
1. Addor, Nans & Nearing, Grey & Prieto, Cristina & Newman, A. & Le Vine, Nataliya & Clark, Martyn. (2018). A Ranking of Hydrological Signatures Based on Their Predictability in Space. Water Resources Research. 10.1029/2018WR022606.
2. Clausen, B., & Biggs, B. J. F. (2000). Flow variables for ecological studies in temperate streams: Groupings based on covariance. Journal of Hydrology, 237(3–4), 184–197. https://doi.org/10.1016/S0022-1694(00)00306-1
Parameters
----------
q : xarray.DataArray
Daily streamflow data.
threshold_factor : float, optional
Factor by which the median flow is multiplied to set the high flow threshold, default is 9.0.
threshold_factor : int
Factor by which the median flow is multiplied to set the high flow threshold, default is 9.
freq : str, optional
Resampling frequency, default is 'A-SEP' for water year ending in September.
statistic : str, optional
Type of statistic to return ('mean', 'sum', 'max', median etc.), default is 'mean'.
op : {">", "<", "gt", "lt"}, optional
Comparison operation. Default: "<".
Returns
-------
xarray.DataArray
Calculated statistic of high flow days per water year, by default it is set as mean
"""
median_flow = q.median(dim="time")
threshold = threshold_factor * median_flow
Calculated mean of high flow days per water year
# Resample data to the given frequency and count days above threshold
high_flow_days = (q > threshold).resample(time=freq).sum(dim="time")

# Dynamically apply the chosen statistic using getattr
out = getattr(high_flow_days, statistic)(dim="time")

# Assign units to the result based on the statistic
out.attrs["units"] = "days/year" if statistic == "mean" else "days"
References
----------
1. Addor, Nans & Nearing, Grey & Prieto, Cristina & Newman, A. & Le Vine, Nataliya & Clark, Martyn. (2018). A Ranking of Hydrological Signatures Based on Their Predictability in Space. Water Resources Research. 10.1029/2018WR022606.
2. Clausen, B., & Biggs, B. J. F. (2000). Flow variables for ecological studies in temperate streams: Groupings based on covariance. Journal of Hydrology, 237(3–4), 184–197. https://doi.org/10.1016/S0022-1694(00)00306-1
"""

# Rename the result for clarity
return out.rename(f"high flow frequency({statistic})")
median_flow = q.median(dim="time")
with xr.set_options(keep_attrs=True):
threshold = threshold_factor * median_flow
high_flow_days = compare(q, op=">", right=threshold).resample(time=freq).sum(dim="time")
out = high_flow_days.mean(dim="time")
out.attrs["units"] = "days/year"
return out


@declare_units(q="[discharge]")
def low_flow_frequency(
q: xr.DataArray,
threshold_factor: float = 0.2,
freq: str = "A-SEP",
statistic: str = "mean",
) -> xr.DataArray:
"""
Calculate the specified statistic of the number of days in a given period with flows lower than a specified threshold.
By default, the period is the water year starting on 1st October and ending on 30th September, as commonly defined in North America.
Reference:
Olden, J. D., & Poff, N. L. (2003). Redundancy and the choice of hydrologic indices for characterizing streamflow regimes. River Research and Applications, 19(2), 101–121. https://doi.org/10.1002/rra.700
Calculate the mean number of days in a given period with flows lower than a specified threshold. By default, the period is the water year starting on 1st October and ending on 30th September, as commonly defined in North America.
Parameters
----------
q : xarray.DataArray
Daily streamflow data.
threshold_factor : float, optional
threshold_factor : float
Factor by which the mean flow is multiplied to set the low flow threshold, default is 0.2.
freq : str, optional
Resampling frequency, default is 'A-SEP' for water year ending in September.
statistic : str, optional
Type of statistic to return ('mean', 'sum', 'max', median etc.), default is 'mean'.
op : {">", "<", "gt", "lt"}, optional
Comparison operation. Default: "<".
Returns
-------
xarray.DataArray
Calculated statistic of low flow days per water year, by default it is set as mean
"""
mean_flow = q.mean(dim="time")
threshold = threshold_factor * mean_flow

# Resample data to the given frequency and count days below threshold
low_flow_days = (q < threshold).resample(time=freq).sum(dim="time")
Calculated mean of low flow days per water year
# Dynamically apply the chosen statistic using getattr
out = getattr(low_flow_days, statistic)(dim="time")
References
----------
Olden, J. D., & Poff, N. L. (2003). Redundancy and the choice of hydrologic indices for characterizing streamflow regimes. River Research and
Applications, 19(2), 101–121. https://doi.org/10.1002/rra.700
"""

# Assign units to the result based on the statistic
out.attrs["units"] = "days/year" if statistic == "mean" else "days"
mean_flow = q.mean(dim="time")
with xr.set_options(keep_attrs=True):
threshold = threshold_factor * mean_flow
low_flow_days = compare(q, op="<", right=threshold).resample(time=freq).sum(dim="time")
out = low_flow_days.mean(dim="time")
out.attrs["units"] = "days"
return out

# Rename the result for clarity
return out.rename(f"low flow frequency({statistic})")

0 comments on commit 0507d4c

Please sign in to comment.