Skip to content

Commit

Permalink
Fix subdaily rate2amount, add hydro transformation from lwe thickness…
Browse files Browse the repository at this point in the history
… to amount (#1963)

### What kind of change does this PR introduce?

* The `_rate_to_amount_converter` relied on the `FREQ_UNITS` directly,
now it goes through `infer_sampling_units` instead. In exchange for
slightly more code, this fixes the issue for sub-daily freqs and gives a
better error for frequencies xclim can't understand.
* Add the "lwe thickness -> amount" hydro transformation. The inverse
was already there. This is to go from a thickness of liquid water (ex:
mm of rain) to an amount (ex: kg m-2), by multiplying by the liquid
water density (1000 kg m-3).

### Does this PR introduce a breaking change?
No.
  • Loading branch information
Zeitsperre authored Oct 17, 2024
2 parents df70d14 + 2184e74 commit 029d85c
Show file tree
Hide file tree
Showing 3 changed files with 24 additions and 4 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.rst
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@ New features and enhancements

Bug fixes
^^^^^^^^^
* Fixed ``rate2amount`` and ``amount2rate`` for sub-daily frequencies. (:issue:`1962`, :pull:`1963`).
* Added the liquid water equivalent thickness ("[length]") to amount ("[mass]/[area]") transformation to the ``hydro`` context (the inverse operation was already there). (:pull:`1963`).
* Fixed a small inefficiency in ``_otc_adjust``, and the `standardize` method of `OTC/dOTC` is now applied on individual variable (:pull:`1890`, :pull:`1896`).
* Remove deprecated cells in the tutorial notebook `sdba.ipynb` (:pull:`1895`).

Expand Down
14 changes: 14 additions & 0 deletions tests/test_units.py
Original file line number Diff line number Diff line change
Expand Up @@ -234,6 +234,20 @@ def test_rate2amount(pr_series):
np.testing.assert_array_equal(am_ys, 86400 * np.array([365, 366, 365]))


@pytest.mark.parametrize(
"srcfreq, exp", [("h", 3600), ("min", 60), ("s", 1), ("ns", 1e-9)]
)
def test_rate2amount_subdaily(srcfreq, exp):
pr = xr.DataArray(
np.ones(1000),
dims=("time",),
coords={"time": xr.date_range("2019-01-01", periods=1000, freq=srcfreq)},
attrs={"units": "kg m-2 s-1"},
)
am = rate2amount(pr)
np.testing.assert_array_equal(am, exp)


def test_amount2rate(pr_series):
pr = pr_series(np.ones(365 + 366 + 365), start="2019-01-01")
am = rate2amount(pr)
Expand Down
12 changes: 8 additions & 4 deletions xclim/core/units.py
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,11 @@
"[length]",
lambda ureg, x: x / (1000 * ureg.kg / ureg.m**3),
)
hydro.add_transformation(
"[length]",
"[mass] / [length]**2",
lambda ureg, x: x * (1000 * ureg.kg / ureg.m**3),
)
hydro.add_transformation(
"[mass] / [length]**2 / [time]",
"[length] / [time]",
Expand Down Expand Up @@ -461,12 +466,11 @@ def cf_conversion(
FREQ_UNITS = {
"D": "d",
"W": "week",
"h": "h",
}
"""
Resampling frequency units for :py:func:`xclim.core.units.infer_sampling_units`.
Mapping from offset base to CF-compliant unit. Only constant-length frequencies are included.
Mapping from offset base to CF-compliant unit. Only constant-length frequencies that are not also pint units are included.
"""


Expand Down Expand Up @@ -718,7 +722,7 @@ def _rate_and_amount_converter(
"can be used as the sampling rate, pass `sampling_rate_from_coord=True`."
) from err
if freq is not None:
multi, base, start_anchor, _ = parse_offset(freq)
_, base, start_anchor, _ = parse_offset(freq)
if base in ["M", "Q", "A", "Y"]:
start = time.indexes[dim][0]
if not start_anchor:
Expand All @@ -743,7 +747,7 @@ def _rate_and_amount_converter(
attrs=time.attrs,
)
else:
m, u = multi, FREQ_UNITS[base]
m, u = infer_sampling_units(da, freq)

out: xr.DataArray
# Freq is month, season or year, which are not constant units, or simply freq is not inferrable.
Expand Down

0 comments on commit 029d85c

Please sign in to comment.