From c88593b1613492fcaed4ee919437c6f600fb57bd Mon Sep 17 00:00:00 2001 From: faimahsho Date: Mon, 8 Jul 2024 13:16:22 -0400 Subject: [PATCH 01/18] Create _streamflow_flow_indices.py streamflow_flow_indice is a submodule in the xclim indices module to calculate streamflow signatures, representing individual watershed characteristics of large river/lake basins. --- .DS_Store | Bin 0 -> 6148 bytes AUTHORS.rst | 1 + xclim/.DS_Store | Bin 0 -> 6148 bytes xclim/indices/_streamflow_flow_indices.py | 124 ++++++++++++++++++++++ 4 files changed, 125 insertions(+) create mode 100644 .DS_Store create mode 100644 xclim/.DS_Store create mode 100644 xclim/indices/_streamflow_flow_indices.py diff --git a/.DS_Store b/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..c53134cdff09c97c935744e2f1551550ed270342 GIT binary patch literal 6148 zcmeHKI|>3Z5S{S@f{mqRuHX%V=n1@lpdf-D5ERR;JeNoF&8JxwJ1vwqFnP&jUP4~6 zvm+upziwwD6A>A~4dr1&*KFUsW4(+h5RNmBx7FozIG=W%+;;)v4&^QzImo`nw;dW4 zpaN8Y3Qz$m@M#6I!j8tDKA7iG0V?qG3fT9dzzu6+6X>4~4Bi3&2MD`i?!5%CSO8cP zn?OWh8dP9VHCqe~I^resYGM-@bkS@+G;h}IP}Fb7`Nh*kYamA|Kn2Hpg#uBZSN_$vi;uvjhTcv9BZ&f~1s7Wf)&IZwD5=1#%j$6wm+w literal 0 HcmV?d00001 diff --git a/AUTHORS.rst b/AUTHORS.rst index 0504718e7..5f7d5e3e5 100644 --- a/AUTHORS.rst +++ b/AUTHORS.rst @@ -44,3 +44,4 @@ Contributors * Dante Castro `@profesorpaiche `_ * Sascha Hofmann `@saschahofmann `_ * Javier Diez-Sierra `@JavierDiezSierra `_ +* Faisal Mahmood `@faimahsho `_ diff --git a/xclim/.DS_Store b/xclim/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..2d471e31dd09b2a0077160287402d26e7ddc9139 GIT binary patch literal 6148 zcmeHKOKQVF43%0#1Knhqs*@Tp$paP$&+hq-(Eot{$yVA4-kgO(2`_1k#() z=#BAPSeA%rd+uLE)*`Zk8_LDP-0a+ZW)GQBARKq>JqI#U0V+TReig9qLxCIC#4*r69SA-G02`#; zu=ZI3SS$goiDMu#Fbyg&sG1{&1|9j5bv1De47z9zADU0roKV!Cj{A$3i`GC!DnJFM z3OvSkZTausK8w*piQ^!TD(&B*4fKhuPyK$+-knzW>`A~!P_y=+c7rQ ejyGNub;Z^=uZd%z(~);NkUs;a3yligT7e(c4i$|6 literal 0 HcmV?d00001 diff --git a/xclim/indices/_streamflow_flow_indices.py b/xclim/indices/_streamflow_flow_indices.py new file mode 100644 index 000000000..69291b284 --- /dev/null +++ b/xclim/indices/_streamflow_flow_indices.py @@ -0,0 +1,124 @@ +from __future__ import annotations +import pandas as pd +import numpy as np +import xarray + +from xclim.core.calendar import get_calendar +from xclim.core.missing import at_least_n_valid +from xclim.core.units import declare_units, rate2amount, convert_units_to, units + +from . import generic + + + +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 + Daily streamflow data. + p is the percentile for calculating the flow index, specified as a float between 0 and 1, default is 0.95. + + Returns + ------- + xarray.DataArray + out = Normalized Qp, which is the p th percentile of daily streamflow normalized by the median flow. + + """ + qp = q.quantile(p, dim='time') + q_median = q.median(dim='time') + out = (qp / q_median) + out.attrs['units'] = " " + return out.rename ("flow_index") + + +@declare_units(q="[discharge]") +def high_flow_frequency(q: xr.DataArray, threshold_factor: int = 9.0, 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. + 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'. + + 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 + + # 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" + + # Rename the result for clarity + return out.rename(f"high flow frequency({statistic})") + + +@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 + + Parameters + ---------- + q : xarray.DataArray + Daily streamflow data. + threshold_factor : float, optional + 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'. + + 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') + + # Dynamically apply the chosen statistic using getattr + out = getattr(low_flow_days, statistic)(dim='time') + + # Assign units to the result based on the statistic + out.attrs['units'] = "days/year" if statistic == "mean" else "days" + + # Rename the result for clarity + return out.rename(f"low flow frequency({statistic})") + From 8362e33d8ffe8794978d4e8cb60bb0a680f60ca7 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 8 Jul 2024 20:34:09 +0000 Subject: [PATCH 02/18] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- xclim/indices/_streamflow_flow_indices.py | 58 ++++++++++++----------- 1 file changed, 30 insertions(+), 28 deletions(-) diff --git a/xclim/indices/_streamflow_flow_indices.py b/xclim/indices/_streamflow_flow_indices.py index 69291b284..da2d74264 100644 --- a/xclim/indices/_streamflow_flow_indices.py +++ b/xclim/indices/_streamflow_flow_indices.py @@ -1,18 +1,13 @@ from __future__ import annotations -import pandas as pd -import numpy as np -import xarray -from xclim.core.calendar import get_calendar -from xclim.core.missing import at_least_n_valid -from xclim.core.units import declare_units, rate2amount, convert_units_to, units - -from . import generic +from xclim.core.units import declare_units declare_units(q="[discharge]") -def flow_index(q: xr.DataArray, p: float= 0.95) -> xr.DataArray: + + +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. @@ -31,16 +26,21 @@ def flow_index(q: xr.DataArray, p: float= 0.95) -> xr.DataArray: xarray.DataArray out = Normalized Qp, which is the p th percentile of daily streamflow normalized by the median flow. - """ - qp = q.quantile(p, dim='time') - q_median = q.median(dim='time') - out = (qp / q_median) - out.attrs['units'] = " " - return out.rename ("flow_index") + """ + qp = q.quantile(p, dim="time") + q_median = q.median(dim="time") + out = qp / q_median + out.attrs["units"] = " " + return out.rename("flow_index") @declare_units(q="[discharge]") -def high_flow_frequency(q: xr.DataArray, threshold_factor: int = 9.0, freq: str = "A-SEP", statistic: str = "mean") -> xr.DataArray: +def high_flow_frequency( + q: xr.DataArray, + threshold_factor: int = 9.0, + 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. @@ -64,26 +64,29 @@ def high_flow_frequency(q: xr.DataArray, threshold_factor: int = 9.0, freq: str xarray.DataArray Calculated statistic of high flow days per water year, by default it is set as mean """ - median_flow = q.median(dim='time') + median_flow = q.median(dim="time") threshold = threshold_factor * median_flow # Resample data to the given frequency and count days above threshold - high_flow_days = (q > threshold).resample(time=freq).sum(dim='time') + 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') + 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" + out.attrs["units"] = "days/year" if statistic == "mean" else "days" # Rename the result for clarity return out.rename(f"high flow frequency({statistic})") @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: - +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. @@ -107,18 +110,17 @@ def low_flow_frequency(q: xr.DataArray, threshold_factor: float = 0.2, freq: str xarray.DataArray Calculated statistic of low flow days per water year, by default it is set as mean """ - mean_flow = q.mean(dim='time') + 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') + low_flow_days = (q < threshold).resample(time=freq).sum(dim="time") # Dynamically apply the chosen statistic using getattr - out = getattr(low_flow_days, statistic)(dim='time') + out = getattr(low_flow_days, statistic)(dim="time") # Assign units to the result based on the statistic - out.attrs['units'] = "days/year" if statistic == "mean" else "days" + out.attrs["units"] = "days/year" if statistic == "mean" else "days" # Rename the result for clarity return out.rename(f"low flow frequency({statistic})") - From 39351e22a2c7a0d8237d90978dddcb4a9b6af144 Mon Sep 17 00:00:00 2001 From: Faisal Mahmood Date: Thu, 11 Jul 2024 10:48:04 -0400 Subject: [PATCH 03/18] Update _streamflow_flow_indices.py --- xclim/indices/_streamflow_flow_indices.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/xclim/indices/_streamflow_flow_indices.py b/xclim/indices/_streamflow_flow_indices.py index da2d74264..f14bf244c 100644 --- a/xclim/indices/_streamflow_flow_indices.py +++ b/xclim/indices/_streamflow_flow_indices.py @@ -4,9 +4,7 @@ from xclim.core.units import declare_units -declare_units(q="[discharge]") - - +@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. From 4795cbe6797d7cfc13b7ceb4b1bab97170ac54a0 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Thu, 11 Jul 2024 14:49:08 +0000 Subject: [PATCH 04/18] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- xclim/indices/_streamflow_flow_indices.py | 1 - 1 file changed, 1 deletion(-) diff --git a/xclim/indices/_streamflow_flow_indices.py b/xclim/indices/_streamflow_flow_indices.py index f14bf244c..40aedf581 100644 --- a/xclim/indices/_streamflow_flow_indices.py +++ b/xclim/indices/_streamflow_flow_indices.py @@ -1,6 +1,5 @@ from __future__ import annotations - from xclim.core.units import declare_units From 21381a94a12c84f187e3c810a0f0d44bd5f5c9ab Mon Sep 17 00:00:00 2001 From: Faisal Mahmood Date: Thu, 11 Jul 2024 11:09:37 -0400 Subject: [PATCH 05/18] Update xclim/indices/_streamflow_flow_indices.py Co-authored-by: David Huard --- xclim/indices/_streamflow_flow_indices.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/xclim/indices/_streamflow_flow_indices.py b/xclim/indices/_streamflow_flow_indices.py index 40aedf581..0b040016d 100644 --- a/xclim/indices/_streamflow_flow_indices.py +++ b/xclim/indices/_streamflow_flow_indices.py @@ -16,7 +16,8 @@ def flow_index(q: xr.DataArray, p: float = 0.95) -> xr.DataArray: ---------- q : xarray.DataArray Daily streamflow data. - p is the percentile for calculating the flow index, specified as a float between 0 and 1, default is 0.95. + p : float + Percentile for calculating the flow index, between 0 and 1. Default of 0.95 is for high flows. Returns ------- From d8b887629305b7021853be583199dc98620cf10d Mon Sep 17 00:00:00 2001 From: Faisal Mahmood Date: Thu, 11 Jul 2024 11:10:24 -0400 Subject: [PATCH 06/18] Update xclim/indices/_streamflow_flow_indices.py Co-authored-by: David Huard --- xclim/indices/_streamflow_flow_indices.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/xclim/indices/_streamflow_flow_indices.py b/xclim/indices/_streamflow_flow_indices.py index 0b040016d..5f382db10 100644 --- a/xclim/indices/_streamflow_flow_indices.py +++ b/xclim/indices/_streamflow_flow_indices.py @@ -35,7 +35,7 @@ def flow_index(q: xr.DataArray, p: float = 0.95) -> xr.DataArray: @declare_units(q="[discharge]") def high_flow_frequency( q: xr.DataArray, - threshold_factor: int = 9.0, + threshold_factor: int = 9 freq: str = "A-SEP", statistic: str = "mean", ) -> xr.DataArray: From 48ded61ae0167ccf7ed98470045983be9ea6956e Mon Sep 17 00:00:00 2001 From: faimahsho Date: Tue, 16 Jul 2024 11:45:33 -0400 Subject: [PATCH 07/18] Update references.bib --- docs/references.bib | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/docs/references.bib b/docs/references.bib index b0d868e69..880fb2016 100644 --- a/docs/references.bib +++ b/docs/references.bib @@ -2152,3 +2152,35 @@ @article{droogers2002 url = {https://www.scopus.com/inward/record.uri?eid=2-s2.0-0036464359&doi=10.1023%2fA%3a1015508322413&partnerID=40&md5=7322aaa4c6874878f5b1dab3c73c1718}, type = {Article} } + +@article{article, + author = {Addor, Nans and Nearing, Grey and Prieto, Cristina and Newman, A. and Le Vine, Nataliya and Clark, Martyn}, + year = {2018}, + month = {11}, + pages = {}, + title = {A Ranking of Hydrological Signatures Based on Their Predictability in Space}, + journal = {Water Resources Research}, + doi = {10.1029/2018WR022606} +} + +@article{article, + author = {Clausen, B and Biggs, Barry}, + year = {2000}, + month = {11}, + pages = {184-197}, + title = {Flow variables for ecological studies in temperate streams: Groupings based on covariance}, + volume = {237}, + journal = {Journal of Hydrology}, + doi = {10.1016/S0022-1694(00)00306-1} +} + +@article{article, + author = {Olden, Julian and Poff, N.}, + year = {2003}, + month = {03}, + pages = {101 - 121}, + title = {Redundancy and the Choice of Hydrologic Indices for Characterizing Stream Flow Regimes}, + volume = {19}, + journal = {River Research and Applications}, + doi = {10.1002/rra.700} +} From bb69dd4ae782e1e98669d7e8cc7b13fd3495d429 Mon Sep 17 00:00:00 2001 From: faimahsho Date: Tue, 16 Jul 2024 12:45:04 -0400 Subject: [PATCH 08/18] Update _streamflow_flow_indices.py --- docs/references.bib | 6 +- tests/test_hydrology.py | 32 +++++- xclim/indices/_hydrology.py | 115 +++++++++++++++++++++- xclim/indices/_streamflow_flow_indices.py | 102 +++++++++---------- 4 files changed, 197 insertions(+), 58 deletions(-) diff --git a/docs/references.bib b/docs/references.bib index 880fb2016..e25dcc1af 100644 --- a/docs/references.bib +++ b/docs/references.bib @@ -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}, @@ -2163,7 +2163,7 @@ @article{article doi = {10.1029/2018WR022606} } -@article{article, +@article{Clausen2000, author = {Clausen, B and Biggs, Barry}, year = {2000}, month = {11}, @@ -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}, diff --git a/tests/test_hydrology.py b/tests/test_hydrology.py index e31c64ffc..a77e8a802 100644 --- a/tests/test_hydrology.py +++ b/tests/test_hydrology.py @@ -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) + diff --git a/xclim/indices/_hydrology.py b/xclim/indices/_hydrology.py index 3785157af..38721097d 100644 --- a/xclim/indices/_hydrology.py +++ b/xclim/indices/_hydrology.py @@ -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 @@ -19,6 +20,9 @@ "snow_melt_we_max", "snw_max", "snw_max_doy", + "flow_index", + "high_flow_frequency", + "low_flow_frequency" ] @@ -279,3 +283,112 @@ 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 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 diff --git a/xclim/indices/_streamflow_flow_indices.py b/xclim/indices/_streamflow_flow_indices.py index 5f382db10..036204c3d 100644 --- a/xclim/indices/_streamflow_flow_indices.py +++ b/xclim/indices/_streamflow_flow_indices.py @@ -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 @@ -22,60 +25,58 @@ 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]") @@ -83,42 +84,37 @@ 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})") From a1dcd766e62da0e6bb318d9f9f13cd99e3b2fec7 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Tue, 16 Jul 2024 22:24:26 +0000 Subject: [PATCH 09/18] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- tests/test_hydrology.py | 7 +++---- xclim/indices/_hydrology.py | 18 ++++++++++-------- xclim/indices/_streamflow_flow_indices.py | 17 ++++++++++------- 3 files changed, 23 insertions(+), 19 deletions(-) diff --git a/tests/test_hydrology.py b/tests/test_hydrology.py index a77e8a802..cfb1a0500 100644 --- a/tests/test_hydrology.py +++ b/tests/test_hydrology.py @@ -64,7 +64,7 @@ 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) @@ -87,7 +87,7 @@ def test_simple(self, q_series): a[50:60] = 10 a[200:210] = 20 q = q_series(a) - out = xci.high_flow_frequency(q, 9, freq='YS') + out = xci.high_flow_frequency(q, 9, freq="YS") np.testing.assert_array_equal(out, 20) @@ -97,6 +97,5 @@ def test_simple(self, q_series): a[50:60] = 1 a[200:210] = 1 q = q_series(a) - out = xci.low_flow_frequency(q, 0.2, freq='YS') + out = xci.low_flow_frequency(q, 0.2, freq="YS") np.testing.assert_array_equal(out, 20) - diff --git a/xclim/indices/_hydrology.py b/xclim/indices/_hydrology.py index 38721097d..0260ec63d 100644 --- a/xclim/indices/_hydrology.py +++ b/xclim/indices/_hydrology.py @@ -7,12 +7,15 @@ 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 xclim.indices.generic import compare from . import generic __all__ = [ "base_flow_index", + "flow_index", + "high_flow_frequency", + "low_flow_frequency", "melt_and_precip_max", "rb_flashiness_index", "snd_max", @@ -20,9 +23,6 @@ "snow_melt_we_max", "snw_max", "snw_max_doy", - "flow_index", - "high_flow_frequency", - "low_flow_frequency" ] @@ -344,11 +344,12 @@ def high_flow_frequency( 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") + 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 @@ -384,11 +385,12 @@ def low_flow_frequency( 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") + 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 diff --git a/xclim/indices/_streamflow_flow_indices.py b/xclim/indices/_streamflow_flow_indices.py index 036204c3d..186c59846 100644 --- a/xclim/indices/_streamflow_flow_indices.py +++ b/xclim/indices/_streamflow_flow_indices.py @@ -1,8 +1,9 @@ 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 +from xclim.indices.generic import compare __all__ = [ "flow_index", @@ -10,6 +11,7 @@ "low_flow_frequency", ] + @declare_units(q="[discharge]") def flow_index(q: xr.DataArray, p: float = 0.95) -> xr.DataArray: """ @@ -69,11 +71,12 @@ def high_flow_frequency( 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") + 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 @@ -109,12 +112,12 @@ def low_flow_frequency( 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") + 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 - From 4e086d693adf213cde0d63637f729ace2172e513 Mon Sep 17 00:00:00 2001 From: David Huard Date: Wed, 17 Jul 2024 09:38:04 -0400 Subject: [PATCH 10/18] return time series for high flow and low flow frequency, instead of mean over whole period. This part of the analysis can be done on the user-side. Added support for **indexer, allowing more freedom when selecting the period --- .DS_Store | Bin 6148 -> 0 bytes .gitignore | 3 + xclim/.DS_Store | Bin 6148 -> 0 bytes xclim/indices/_hydrology.py | 115 ++++++++++---------- xclim/indices/_streamflow_flow_indices.py | 123 ---------------------- 5 files changed, 56 insertions(+), 185 deletions(-) delete mode 100644 .DS_Store delete mode 100644 xclim/.DS_Store delete mode 100644 xclim/indices/_streamflow_flow_indices.py diff --git a/.DS_Store b/.DS_Store deleted file mode 100644 index c53134cdff09c97c935744e2f1551550ed270342..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6148 zcmeHKI|>3Z5S{S@f{mqRuHX%V=n1@lpdf-D5ERR;JeNoF&8JxwJ1vwqFnP&jUP4~6 zvm+upziwwD6A>A~4dr1&*KFUsW4(+h5RNmBx7FozIG=W%+;;)v4&^QzImo`nw;dW4 zpaN8Y3Qz$m@M#6I!j8tDKA7iG0V?qG3fT9dzzu6+6X>4~4Bi3&2MD`i?!5%CSO8cP zn?OWh8dP9VHCqe~I^resYGM-@bkS@+G;h}IP}Fb7`Nh*kYamA|Kn2Hpg#uBZSN_$vi;uvjhTcv9BZ&f~1s7Wf)&IZwD5=1#%j$6wm+w diff --git a/.gitignore b/.gitignore index cef52ef05..a9e772152 100644 --- a/.gitignore +++ b/.gitignore @@ -122,3 +122,6 @@ docs/variables.json # dask dask-worker-space + +# Apple +.DS_Store diff --git a/xclim/.DS_Store b/xclim/.DS_Store deleted file mode 100644 index 2d471e31dd09b2a0077160287402d26e7ddc9139..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6148 zcmeHKOKQVF43%0#1Knhqs*@Tp$paP$&+hq-(Eot{$yVA4-kgO(2`_1k#() z=#BAPSeA%rd+uLE)*`Zk8_LDP-0a+ZW)GQBARKq>JqI#U0V+TReig9qLxCIC#4*r69SA-G02`#; zu=ZI3SS$goiDMu#Fbyg&sG1{&1|9j5bv1De47z9zADU0roKV!Cj{A$3i`GC!DnJFM z3OvSkZTausK8w*piQ^!TD(&B*4fKhuPyK$+-knzW>`A~!P_y=+c7rQ ejyGNub;Z^=uZd%z(~);NkUs;a3yligT7e(c4i$|6 diff --git a/xclim/indices/_hydrology.py b/xclim/indices/_hydrology.py index 0260ec63d..bf05ef704 100644 --- a/xclim/indices/_hydrology.py +++ b/xclim/indices/_hydrology.py @@ -6,8 +6,8 @@ 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 +from xclim.core.units import declare_units, rate2amount, to_agg_units +from xclim.indices.generic import select_time, threshold_count from . import generic @@ -27,7 +27,7 @@ @declare_units(q="[discharge]") -def base_flow_index(q: xarray.DataArray, freq: str = "YS") -> xarray.DataArray: +def base_flow_index(q: xr.DataArray, freq: str = "YS") -> xr.DataArray: r"""Base flow index. Return the base flow index, defined as the minimum 7-day average flow divided by the mean flow. @@ -71,7 +71,7 @@ def base_flow_index(q: xarray.DataArray, freq: str = "YS") -> xarray.DataArray: @declare_units(q="[discharge]") -def rb_flashiness_index(q: xarray.DataArray, freq: str = "YS") -> xarray.DataArray: +def rb_flashiness_index(q: xr.DataArray, freq: str = "YS") -> xr.DataArray: r"""Richards-Baker flashiness index. Measures oscillations in flow relative to total flow, quantifying the frequency and rapidity of short term changes @@ -109,7 +109,7 @@ def rb_flashiness_index(q: xarray.DataArray, freq: str = "YS") -> xarray.DataArr @declare_units(snd="[length]") -def snd_max(snd: xarray.DataArray, freq: str = "YS-JUL") -> xarray.DataArray: +def snd_max(snd: xr.DataArray, freq: str = "YS-JUL") -> xr.DataArray: """Maximum snow depth. The maximum daily snow depth. @@ -130,7 +130,7 @@ def snd_max(snd: xarray.DataArray, freq: str = "YS-JUL") -> xarray.DataArray: @declare_units(snd="[length]") -def snd_max_doy(snd: xarray.DataArray, freq: str = "YS-JUL") -> xarray.DataArray: +def snd_max_doy(snd: xr.DataArray, freq: str = "YS-JUL") -> xr.DataArray: """Maximum snow depth day of year. Day of year when surface snow reaches its peak value. If snow depth is 0 over entire period, return NaN. @@ -161,7 +161,7 @@ def snd_max_doy(snd: xarray.DataArray, freq: str = "YS-JUL") -> xarray.DataArray @declare_units(snw="[mass]/[area]") -def snw_max(snw: xarray.DataArray, freq: str = "YS-JUL") -> xarray.DataArray: +def snw_max(snw: xr.DataArray, freq: str = "YS-JUL") -> xr.DataArray: """Maximum snow amount. The maximum daily snow amount. @@ -182,7 +182,7 @@ def snw_max(snw: xarray.DataArray, freq: str = "YS-JUL") -> xarray.DataArray: @declare_units(snw="[mass]/[area]") -def snw_max_doy(snw: xarray.DataArray, freq: str = "YS-JUL") -> xarray.DataArray: +def snw_max_doy(snw: xr.DataArray, freq: str = "YS-JUL") -> xr.DataArray: """Maximum snow amount day of year. Day of year when surface snow amount reaches its peak value. If snow amount is 0 over entire period, return NaN. @@ -214,8 +214,8 @@ def snw_max_doy(snw: xarray.DataArray, freq: str = "YS-JUL") -> xarray.DataArray @declare_units(snw="[mass]/[area]") def snow_melt_we_max( - snw: xarray.DataArray, window: int = 3, freq: str = "YS-JUL" -) -> xarray.DataArray: + snw: xr.DataArray, window: int = 3, freq: str = "YS-JUL" +) -> xr.DataArray: """Maximum snow melt. The maximum snow melt over a given number of days expressed in snow water equivalent. @@ -248,8 +248,8 @@ def snow_melt_we_max( @declare_units(snw="[mass]/[area]", pr="[precipitation]") def melt_and_precip_max( - snw: xarray.DataArray, pr: xarray.DataArray, window: int = 3, freq: str = "YS-JUL" -) -> xarray.DataArray: + snw: xr.DataArray, pr: xr.DataArray, window: int = 3, freq: str = "YS-JUL" +) -> xr.DataArray: """Maximum snow melt and precipitation. The maximum snow melt plus precipitation over a given number of days expressed in snow water equivalent. @@ -288,109 +288,100 @@ def melt_and_precip_max( @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. + Flow index + + Calculate the pth percentile of daily streamflow normalized by the median flow. Parameters ---------- - q : xarray.DataArray + q : xr.DataArray Daily streamflow data. p : float Percentile for 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 + xr.DataArray + Normalized Qp, which is the p th percentile of daily streamflow normalized by the median flow. + References + ---------- + :cite:cts:`addor2018,Clausen2000` """ qp = q.quantile(p, dim="time") q_median = q.median(dim="time") out = qp / q_median - out.attrs["units"] = " " + out.attrs["units"] = "1" return out @declare_units(q="[discharge]") def high_flow_frequency( - q: xr.DataArray, - threshold_factor: int = 9, - freq: str = "A-SEP", + q: xr.DataArray, threshold_factor: int = 9, freq: str = "YS-OCT", **indexer ) -> 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. + High flow frequency. + + Calculate the 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 + q : xr.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: "<". + Resampling frequency, default is 'YS-OCT' for water year starting in October and ending in September. + indexer + Indexing parameters to perform a temporal subset of the data. + It accepts the same arguments as :py:func:`xclim.indices.generic.select_time`. Returns ------- - xarray.DataArray - Calculated mean of high flow days per water year + xr.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 + :cite:cts:`addor2018,Clausen2000` """ 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 + threshold = threshold_factor * median_flow + sel = select_time(q, **indexer) + out = threshold_count(sel, ">", threshold, freq=freq) + return to_agg_units(out, q, "count") @declare_units(q="[discharge]") def low_flow_frequency( - q: xr.DataArray, - threshold_factor: float = 0.2, - freq: str = "A-SEP", + q: xr.DataArray, threshold_factor: float = 0.2, freq: str = "YS-OCT", **indexer ) -> 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. + Calculate 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. Parameters ---------- - q : xarray.DataArray + q : xr.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: "<". + Resampling frequency, default is 'YS-OCT' for water year starting in October and ending in September. + indexer + Indexing parameters to perform a temporal subset of the data. + It accepts the same arguments as :py:func:`xclim.indices.generic.select_time`. Returns ------- - xarray.DataArray - Calculated mean of low flow days per water year + xr.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 + :cite:cts:`Olden2003` """ 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 + threshold = threshold_factor * mean_flow + sel = select_time(q, **indexer) + out = threshold_count(q, "<", threshold, freq=freq) + return to_agg_units(out, q, "count") diff --git a/xclim/indices/_streamflow_flow_indices.py b/xclim/indices/_streamflow_flow_indices.py deleted file mode 100644 index 186c59846..000000000 --- a/xclim/indices/_streamflow_flow_indices.py +++ /dev/null @@ -1,123 +0,0 @@ -from __future__ import annotations - -import xarray as xr - -from xclim.core.units import declare_units -from xclim.indices.generic import compare - -__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. - - Parameters - ---------- - q : xarray.DataArray - Daily streamflow data. - p : float - Percentile for 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/year" - 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 From 222b33b68cec5b3252354ba0e464aa9bd0c4bf4c Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Thu, 18 Jul 2024 20:58:54 +0000 Subject: [PATCH 11/18] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- xclim/indices/_hydrology.py | 1 + 1 file changed, 1 insertion(+) diff --git a/xclim/indices/_hydrology.py b/xclim/indices/_hydrology.py index bf05ef704..37064463d 100644 --- a/xclim/indices/_hydrology.py +++ b/xclim/indices/_hydrology.py @@ -378,6 +378,7 @@ def low_flow_frequency( Calculated mean of low flow days per water year. References + ---------- :cite:cts:`Olden2003` """ mean_flow = q.mean(dim="time") From e3dfa79eb7f97dd0b94492209128c51e70682a67 Mon Sep 17 00:00:00 2001 From: Faisal Mahmood Date: Fri, 26 Jul 2024 09:28:41 -0400 Subject: [PATCH 12/18] Update xclim/indices/_hydrology.py Co-authored-by: Trevor James Smith <10819524+Zeitsperre@users.noreply.github.com> --- xclim/indices/_hydrology.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/xclim/indices/_hydrology.py b/xclim/indices/_hydrology.py index 37064463d..00c1c009a 100644 --- a/xclim/indices/_hydrology.py +++ b/xclim/indices/_hydrology.py @@ -384,5 +384,5 @@ def low_flow_frequency( mean_flow = q.mean(dim="time") threshold = threshold_factor * mean_flow sel = select_time(q, **indexer) - out = threshold_count(q, "<", threshold, freq=freq) + out = threshold_count(sel, "<", threshold, freq=freq) return to_agg_units(out, q, "count") From 62d4ebed4249ee419c329196e654140849261691 Mon Sep 17 00:00:00 2001 From: Trevor James Smith <10819524+Zeitsperre@users.noreply.github.com> Date: Fri, 26 Jul 2024 12:20:32 -0400 Subject: [PATCH 13/18] Update xclim/indices/_hydrology.py --- xclim/indices/_hydrology.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/xclim/indices/_hydrology.py b/xclim/indices/_hydrology.py index 00c1c009a..ec342fd64 100644 --- a/xclim/indices/_hydrology.py +++ b/xclim/indices/_hydrology.py @@ -358,7 +358,9 @@ def low_flow_frequency( q: xr.DataArray, threshold_factor: float = 0.2, freq: str = "YS-OCT", **indexer ) -> xr.DataArray: """ - Calculate 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. + Calculate 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. Parameters ---------- From 163faa75375f29da03b8b94ee76870a799ba6342 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Fri, 26 Jul 2024 16:21:34 +0000 Subject: [PATCH 14/18] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- xclim/indices/_hydrology.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/xclim/indices/_hydrology.py b/xclim/indices/_hydrology.py index ec342fd64..0f77cced6 100644 --- a/xclim/indices/_hydrology.py +++ b/xclim/indices/_hydrology.py @@ -359,7 +359,7 @@ def low_flow_frequency( ) -> xr.DataArray: """ Calculate 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. Parameters From adc9144927f4012daddd855d00a60e8f42c01edc Mon Sep 17 00:00:00 2001 From: David Huard Date: Wed, 17 Jul 2024 10:56:32 -0400 Subject: [PATCH 15/18] resolved merge conflicts. added indicator tests --- tests/test_hydrology.py | 27 ++++++++++++---- tests/test_land.py | 31 ++++++++++++++++++ xclim/data/fr.json | 18 +++++++++++ xclim/indicators/land/_streamflow.py | 48 ++++++++++++++++++++++++++-- xclim/indices/_hydrology.py | 21 ++++++++---- 5 files changed, 130 insertions(+), 15 deletions(-) diff --git a/tests/test_hydrology.py b/tests/test_hydrology.py index 990a09b11..a8869c5de 100644 --- a/tests/test_hydrology.py +++ b/tests/test_hydrology.py @@ -74,28 +74,43 @@ def test_simple(self, snw_series, pr_series): class TestFlowindex: def test_simple(self, q_series): - a = np.ones(365) * 10 - a[10:30] = 50 + a = np.ones(365 * 2) * 10 + a[10:50] = 50 q = q_series(a) out = xci.flow_index(q, 0.95) np.testing.assert_array_equal(out, 5) + def test_indexer(self, q_series): + a = np.ones(365 * 2) * 10 + a[10:30] = 50 + a[375:395] = 50 + q = q_series(a) + out = xci.flow_index( + q, + 0.95, + month=[ + 1, + ], + ) + np.testing.assert_array_equal(out, 1) + class TestHighflowfrequency: def test_simple(self, q_series): - a = np.zeros(365) + a = np.zeros(365 * 2) 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) + np.testing.assert_array_equal(out, [20, 0]) class TestLowflowfrequency: def test_simple(self, q_series): - a = np.ones(365) * 10 + a = np.ones(365 * 2) * 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) + + np.testing.assert_array_equal(out, [20, 0]) diff --git a/tests/test_land.py b/tests/test_land.py index a1782f1be..aedc1563b 100644 --- a/tests/test_land.py +++ b/tests/test_land.py @@ -66,3 +66,34 @@ def test_snw_storm_days(snw_series): snw = snw_series(a) out = land.snw_storm_days(snw, thresh="0.5 kg m-2") np.testing.assert_array_equal(out, [9, np.nan]) + + +def test_flow_index(q_series): + a = np.ones(365 * 2) * 10 + a[10:50] = 50 + q = q_series(a) + + out = land.flow_index(q, p=0.95) + np.testing.assert_array_equal(out, 5) + + +def test_high_flow_frequency(q_series): + a = np.zeros(366 * 2) * 10 + a[50:60] = 10 + a[200:210] = 20 + q = q_series(a) + out = land.high_flow_frequency( + q, + threshold_factor=9, + freq="YS", + ) + np.testing.assert_array_equal(out, [20, 0, np.nan]) + + +def test_low_flow_frequency(q_series): + a = np.ones(366 * 2) * 10 + a[50:60] = 1 + a[200:210] = 1 + q = q_series(a) + out = land.low_flow_frequency(q, threshold_factor=0.2, freq="YS") + np.testing.assert_array_equal(out, [20, 0, np.nan]) diff --git a/xclim/data/fr.json b/xclim/data/fr.json index d11f72253..16d117494 100644 --- a/xclim/data/fr.json +++ b/xclim/data/fr.json @@ -1385,5 +1385,23 @@ "description": "Calcul du rayonnement de grandes longueurs d'onde ascendant à partir du rayonnement de grandes longueurs d'onde net et descendant.", "title": "Rayonnement de grandes longueurs d'onde ascendant", "abstract": "Calcul du rayonnement de grandes longueurs d'onde ascendant à partir du rayonnement de grandes longueurs d'onde net et descendant." + }, + "FLOW_INDEX": { + "long_name": "Indice de débit", + "description": "Le {p}e percentile du débit normalisé par le débit médian ({indexer}).", + "title": "Indice de débit", + "abstract": "Indice de débit, calculé comme le rapport entre un percentile donné du débit et la médiane." + }, + "HIGH_FLOW_FREQUENCY": { + "long_name": "Fréquence des débits élevés", + "description": "Nombre de jours où le débit est {thresholf_factor} fois supérieur à la médiane.", + "title": "Fréquence des débits élevés", + "abstract": "Fréquence des débits élevés, calculée comme le nombre de jours où le débit est supérieur à un multiple de la médiane." + }, + "LOW_FLOW_FREQUENCY": { + "long_name": "Fréquence des débits faibles", + "description": "Nombre de jours où le débit est inférieur à une fraction ({threshold_factor}) de la moyenne.", + "title": "Fréquence des débits faibles", + "abstract": "Fréquence des débits faibles, calculée comme le nombre de jours où le débit est inférieur à une fraction de la moyenne." } } diff --git a/xclim/indicators/land/_streamflow.py b/xclim/indicators/land/_streamflow.py index 63a9ba39f..1b3abe159 100644 --- a/xclim/indicators/land/_streamflow.py +++ b/xclim/indicators/land/_streamflow.py @@ -3,14 +3,24 @@ from __future__ import annotations from xclim.core.cfchecks import check_valid -from xclim.core.indicator import ResamplingIndicator +from xclim.core.indicator import ReducingIndicator, ResamplingIndicator from xclim.core.units import declare_units -from xclim.indices import base_flow_index, generic, rb_flashiness_index +from xclim.indices import ( + base_flow_index, + flow_index, + generic, + high_flow_frequency, + low_flow_frequency, + rb_flashiness_index, +) __all__ = [ "base_flow_index", "doy_qmax", "doy_qmin", + "flow_index", + "high_flow_frequency", + "low_flow_frequency", "rb_flashiness_index", ] @@ -74,3 +84,37 @@ def cfcheck(q): compute=declare_units(da="[discharge]")(generic.select_resample_op), parameters={"op": generic.doymin, "out_units": None}, ) + +flow_index = ReducingIndicator( + realm="land", + context="hydro", + title="Flow index", + identifier="flow_index", + var_name="q{indexer}_flow_index", + long_name="Flow index over {indexer}", + description="{p}th percentile normalized by the median flow, computed over {indexer}.", + units="1", + compute=flow_index, +) + + +high_flow_frequency = Streamflow( + title="High flow frequency", + identifier="high_flow_frequency", + var_name="q{indexer}_high_flow_frequency", + long_name="High flow frequency over {indexer}", + description="{freq} frequency of flows greater than {threshold_factor} times the median flow over {indexer}.", + units="days", + compute=high_flow_frequency, +) + + +low_flow_frequency = Streamflow( + title="Low flow frequency", + identifier="low_flow_frequency", + var_name="q{indexer}_low_flow_frequency", + long_name="Low flow frequency over {indexer}", + description="{freq} frequency of flows smaller than a fraction ({threshold_factor}) of the mean flow over {indexer}.", + units="days", + compute=low_flow_frequency, +) diff --git a/xclim/indices/_hydrology.py b/xclim/indices/_hydrology.py index 0f77cced6..707f4a679 100644 --- a/xclim/indices/_hydrology.py +++ b/xclim/indices/_hydrology.py @@ -286,7 +286,7 @@ def melt_and_precip_max( @declare_units(q="[discharge]") -def flow_index(q: xr.DataArray, p: float = 0.95) -> xr.DataArray: +def flow_index(q: xr.DataArray, p: float = 0.95, **indexer) -> xr.DataArray: """ Flow index @@ -298,6 +298,9 @@ def flow_index(q: xr.DataArray, p: float = 0.95) -> xr.DataArray: Daily streamflow data. p : float Percentile for calculating the flow index, between 0 and 1. Default of 0.95 is for high flows. + indexer + Indexing parameters to perform a temporal subset of the data. + It accepts the same arguments as :py:func:`xclim.indices.generic.select_time`. Returns ------- @@ -308,8 +311,9 @@ def flow_index(q: xr.DataArray, p: float = 0.95) -> xr.DataArray: ---------- :cite:cts:`addor2018,Clausen2000` """ - qp = q.quantile(p, dim="time") - q_median = q.median(dim="time") + qt = select_time(q, **indexer) + qp = qt.quantile(p, dim="time") + q_median = qt.median(dim="time") out = qp / q_median out.attrs["units"] = "1" return out @@ -322,8 +326,9 @@ def high_flow_frequency( """ High flow frequency. - Calculate the 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. + Calculate the number of days in a given period with flows greater than a specified threshold, given as a + multiple of the median flow. By default, the period is the water year starting on 1st October and ending on + 30th September, as commonly defined in North America. Parameters ---------- @@ -358,9 +363,11 @@ def low_flow_frequency( q: xr.DataArray, threshold_factor: float = 0.2, freq: str = "YS-OCT", **indexer ) -> xr.DataArray: """ - Calculate the number of days in a given period with flows lower than a specified threshold. + Low flow frequency. - By default, the period is the water year starting on 1st October and ending on 30th September, as commonly defined in North America. + Calculate the number of days in a given period with flows lower than a specified threshold, given by a fraction + of the mean flow. By default, the period is the water year starting on 1st October and ending on 30th September, + as commonly defined in North America. Parameters ---------- From f8b4fce5c3d8acec1f11c24243a0545f47d9e508 Mon Sep 17 00:00:00 2001 From: Pascal Bourgault Date: Wed, 2 Oct 2024 09:33:58 -0400 Subject: [PATCH 16/18] Update xclim/data/fr.json --- xclim/data/fr.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/xclim/data/fr.json b/xclim/data/fr.json index 16d117494..2ab37645e 100644 --- a/xclim/data/fr.json +++ b/xclim/data/fr.json @@ -1394,7 +1394,7 @@ }, "HIGH_FLOW_FREQUENCY": { "long_name": "Fréquence des débits élevés", - "description": "Nombre de jours où le débit est {thresholf_factor} fois supérieur à la médiane.", + "description": "Nombre de jours où le débit est {threshold_factor} fois supérieur à la médiane.", "title": "Fréquence des débits élevés", "abstract": "Fréquence des débits élevés, calculée comme le nombre de jours où le débit est supérieur à un multiple de la médiane." }, From 3b02e3ef42874e0ca5b8f44e2974e089a45c8452 Mon Sep 17 00:00:00 2001 From: David Huard Date: Mon, 7 Oct 2024 19:09:00 -0400 Subject: [PATCH 17/18] suggestions from review --- xclim/indices/_hydrology.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/xclim/indices/_hydrology.py b/xclim/indices/_hydrology.py index 707f4a679..6a1128cf8 100644 --- a/xclim/indices/_hydrology.py +++ b/xclim/indices/_hydrology.py @@ -309,7 +309,7 @@ def flow_index(q: xr.DataArray, p: float = 0.95, **indexer) -> xr.DataArray: References ---------- - :cite:cts:`addor2018,Clausen2000` + :cite:cts:`Clausen2000` """ qt = select_time(q, **indexer) qp = qt.quantile(p, dim="time") @@ -345,15 +345,15 @@ def high_flow_frequency( Returns ------- xr.DataArray - Calculated mean of high flow days per water year + Number of high flow days. References ---------- :cite:cts:`addor2018,Clausen2000` """ - median_flow = q.median(dim="time") - threshold = threshold_factor * median_flow sel = select_time(q, **indexer) + median_flow = sel.median(dim="time") + threshold = threshold_factor * median_flow out = threshold_count(sel, ">", threshold, freq=freq) return to_agg_units(out, q, "count") @@ -384,14 +384,14 @@ def low_flow_frequency( Returns ------- xr.DataArray - Calculated mean of low flow days per water year. + Number of low flow days. References ---------- :cite:cts:`Olden2003` """ - mean_flow = q.mean(dim="time") - threshold = threshold_factor * mean_flow sel = select_time(q, **indexer) + mean_flow = sel.mean(dim="time") + threshold = threshold_factor * mean_flow out = threshold_count(sel, "<", threshold, freq=freq) return to_agg_units(out, q, "count") From 8fabf4fd56c670d80e34d71c2e07e86c6ee95de9 Mon Sep 17 00:00:00 2001 From: David Huard Date: Tue, 8 Oct 2024 14:31:22 -0400 Subject: [PATCH 18/18] removed indexing to all three functions. I think the potential for confusion outweighs the benefits. --- tests/test_hydrology.py | 14 ------------ xclim/data/fr.json | 2 +- xclim/indicators/land/_streamflow.py | 23 +++++++++++--------- xclim/indices/_hydrology.py | 32 +++++++++------------------- 4 files changed, 24 insertions(+), 47 deletions(-) diff --git a/tests/test_hydrology.py b/tests/test_hydrology.py index a8869c5de..0572d12e1 100644 --- a/tests/test_hydrology.py +++ b/tests/test_hydrology.py @@ -80,20 +80,6 @@ def test_simple(self, q_series): out = xci.flow_index(q, 0.95) np.testing.assert_array_equal(out, 5) - def test_indexer(self, q_series): - a = np.ones(365 * 2) * 10 - a[10:30] = 50 - a[375:395] = 50 - q = q_series(a) - out = xci.flow_index( - q, - 0.95, - month=[ - 1, - ], - ) - np.testing.assert_array_equal(out, 1) - class TestHighflowfrequency: def test_simple(self, q_series): diff --git a/xclim/data/fr.json b/xclim/data/fr.json index 568358feb..64bae13e1 100644 --- a/xclim/data/fr.json +++ b/xclim/data/fr.json @@ -1394,7 +1394,7 @@ }, "FLOW_INDEX": { "long_name": "Indice de débit", - "description": "Le {p}e percentile du débit normalisé par le débit médian ({indexer}).", + "description": "Le {p}e percentile du débit normalisé par le débit médian.", "title": "Indice de débit", "abstract": "Indice de débit, calculé comme le rapport entre un percentile donné du débit et la médiane." }, diff --git a/xclim/indicators/land/_streamflow.py b/xclim/indicators/land/_streamflow.py index 1b3abe159..fc73699af 100644 --- a/xclim/indicators/land/_streamflow.py +++ b/xclim/indicators/land/_streamflow.py @@ -3,7 +3,10 @@ from __future__ import annotations from xclim.core.cfchecks import check_valid -from xclim.core.indicator import ReducingIndicator, ResamplingIndicator +from xclim.core.indicator import ( + ReducingIndicator, + ResamplingIndicator, +) from xclim.core.units import declare_units from xclim.indices import ( base_flow_index, @@ -90,9 +93,9 @@ def cfcheck(q): context="hydro", title="Flow index", identifier="flow_index", - var_name="q{indexer}_flow_index", - long_name="Flow index over {indexer}", - description="{p}th percentile normalized by the median flow, computed over {indexer}.", + var_name="q_flow_index", + long_name="Flow index", + description="{p}th percentile normalized by the median flow.", units="1", compute=flow_index, ) @@ -101,9 +104,9 @@ def cfcheck(q): high_flow_frequency = Streamflow( title="High flow frequency", identifier="high_flow_frequency", - var_name="q{indexer}_high_flow_frequency", - long_name="High flow frequency over {indexer}", - description="{freq} frequency of flows greater than {threshold_factor} times the median flow over {indexer}.", + var_name="q_high_flow_frequency", + long_name="High flow frequency", + description="{freq} frequency of flows greater than {threshold_factor} times the median flow.", units="days", compute=high_flow_frequency, ) @@ -112,9 +115,9 @@ def cfcheck(q): low_flow_frequency = Streamflow( title="Low flow frequency", identifier="low_flow_frequency", - var_name="q{indexer}_low_flow_frequency", - long_name="Low flow frequency over {indexer}", - description="{freq} frequency of flows smaller than a fraction ({threshold_factor}) of the mean flow over {indexer}.", + var_name="q_low_flow_frequency", + long_name="Low flow frequency", + description="{freq} frequency of flows smaller than a fraction ({threshold_factor}) of the mean flow.", units="days", compute=low_flow_frequency, ) diff --git a/xclim/indices/_hydrology.py b/xclim/indices/_hydrology.py index 6a1128cf8..23d320cbc 100644 --- a/xclim/indices/_hydrology.py +++ b/xclim/indices/_hydrology.py @@ -7,7 +7,7 @@ from xclim.core.calendar import get_calendar from xclim.core.missing import at_least_n_valid from xclim.core.units import declare_units, rate2amount, to_agg_units -from xclim.indices.generic import select_time, threshold_count +from xclim.indices.generic import threshold_count from . import generic @@ -286,7 +286,7 @@ def melt_and_precip_max( @declare_units(q="[discharge]") -def flow_index(q: xr.DataArray, p: float = 0.95, **indexer) -> xr.DataArray: +def flow_index(q: xr.DataArray, p: float = 0.95) -> xr.DataArray: """ Flow index @@ -298,9 +298,6 @@ def flow_index(q: xr.DataArray, p: float = 0.95, **indexer) -> xr.DataArray: Daily streamflow data. p : float Percentile for calculating the flow index, between 0 and 1. Default of 0.95 is for high flows. - indexer - Indexing parameters to perform a temporal subset of the data. - It accepts the same arguments as :py:func:`xclim.indices.generic.select_time`. Returns ------- @@ -311,9 +308,8 @@ def flow_index(q: xr.DataArray, p: float = 0.95, **indexer) -> xr.DataArray: ---------- :cite:cts:`Clausen2000` """ - qt = select_time(q, **indexer) - qp = qt.quantile(p, dim="time") - q_median = qt.median(dim="time") + qp = q.quantile(p, dim="time") + q_median = q.median(dim="time") out = qp / q_median out.attrs["units"] = "1" return out @@ -321,7 +317,7 @@ def flow_index(q: xr.DataArray, p: float = 0.95, **indexer) -> xr.DataArray: @declare_units(q="[discharge]") def high_flow_frequency( - q: xr.DataArray, threshold_factor: int = 9, freq: str = "YS-OCT", **indexer + q: xr.DataArray, threshold_factor: int = 9, freq: str = "YS-OCT" ) -> xr.DataArray: """ High flow frequency. @@ -338,9 +334,6 @@ def high_flow_frequency( Factor by which the median flow is multiplied to set the high flow threshold, default is 9. freq : str, optional Resampling frequency, default is 'YS-OCT' for water year starting in October and ending in September. - indexer - Indexing parameters to perform a temporal subset of the data. - It accepts the same arguments as :py:func:`xclim.indices.generic.select_time`. Returns ------- @@ -351,16 +344,15 @@ def high_flow_frequency( ---------- :cite:cts:`addor2018,Clausen2000` """ - sel = select_time(q, **indexer) - median_flow = sel.median(dim="time") + median_flow = q.median(dim="time") threshold = threshold_factor * median_flow - out = threshold_count(sel, ">", threshold, freq=freq) + out = threshold_count(q, ">", threshold, freq=freq) return to_agg_units(out, q, "count") @declare_units(q="[discharge]") def low_flow_frequency( - q: xr.DataArray, threshold_factor: float = 0.2, freq: str = "YS-OCT", **indexer + q: xr.DataArray, threshold_factor: float = 0.2, freq: str = "YS-OCT" ) -> xr.DataArray: """ Low flow frequency. @@ -377,9 +369,6 @@ def low_flow_frequency( 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 'YS-OCT' for water year starting in October and ending in September. - indexer - Indexing parameters to perform a temporal subset of the data. - It accepts the same arguments as :py:func:`xclim.indices.generic.select_time`. Returns ------- @@ -390,8 +379,7 @@ def low_flow_frequency( ---------- :cite:cts:`Olden2003` """ - sel = select_time(q, **indexer) - mean_flow = sel.mean(dim="time") + mean_flow = q.mean(dim="time") threshold = threshold_factor * mean_flow - out = threshold_count(sel, "<", threshold, freq=freq) + out = threshold_count(q, "<", threshold, freq=freq) return to_agg_units(out, q, "count")