Skip to content

Commit 20e75d7

Browse files
authored
Snowfall and rain approximation from Dai 2008 (#2208)
<!--Please ensure the PR fulfills the following requirements! --> <!-- If this is your first PR, make sure to add your details to the AUTHORS.rst! --> ### Pull Request Checklist: - [x] This PR addresses an already opened issue (for bug fixes / features) - This PR fixes #1752 - [x] Tests for the changes have been added (for bug fixes / features) - [x] (If applicable) Documentation has been added / updated (for bug fixes / features) - [x] CHANGELOG.rst has been updated (with summary of main changes) - [x] Link to issue (:issue:`number`) and pull request (:pull:`number`) has been added ### What kind of change does this PR introduce? * New methods "dai_annual" and "dai_seasonal" for snowfall and rain approximation. * New argument `landmask` to control which formulation (land or ocean) we use. ### Does this PR introduce a breaking change? No. Only new arguments added, no default changed. ### Other information: @Zeitsperre : The `landmask` argument is polymorphic (my favorite, your nemesis (?)), it can either be a DataArray (which is assumed to be of boolean dtype, but no need to check) or a straight boolean. This is not a `Quantified` because boolean have no units. Right now, I put the annotation that resolved into the "optional variable" `InputType`, which means "bool" is not part of it. Should we create a new `InputType` for boolean mask ? It would be similar to `Quantified` in that it can be a scalar or a DataArray, but without the units check. @tlogan2000 : I implemented the rain approximation as well. However, in opposition to the 3 other methods already implemented, the Dai rain approximation is not equal to `pr - prsn` because it considers some small fractions of the total precip to be of another phase (denoted "sleet" in the article). Thus, to imitate DonnéesClimatiques, ones need to compute `prra` as `pr - prsn` instead of using `rain_approximation`. Is this ok ? Tests need to be added, I'll try to base my tests off of DonnéesClimatiques' data. At least it would compare xclim with another implementation, if not an "original" one.
2 parents ca6eb62 + 67a2ff3 commit 20e75d7

File tree

13 files changed

+329
-38
lines changed

13 files changed

+329
-38
lines changed

CHANGELOG.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ New indicators and features
2727
* The ``xclim.indices.helpers.day_length`` function now accepts an ``infill_polar_days`` argument to control whether polar days or polar nights are filled with NaNs (``infill_polar_days=False``; default behaviour) or with a day length of 0 or 24 hours, depending on the date (``infill_polar_days=True``). (:issue:`2201`, :pull:`2207`).
2828
* The indices for ``uas_vas_to_sfcwind``, ``sfcwind_to_uas_vas``, ``rle_1d``, and ``cffwis_indices`` now provide their outputs via the ``NamedTuple`` format. This provides a method for accessing indice outputs using variable names in addition to positional indexing. (:pull:`2224`).
2929
* The new ``xclim.indicators.convert`` module is now available to provide a distinct API for conversion-focused indicators. This module is intended to regroup all conversion-based indicators that were previously scattered across different indicato realms. This module is also accessible via ``xclim.convert``. (:issue:`1289`, :pull:`2224`).
30+
* New methods ``dai_annual`` and ``dai_seasonal`` for ``xclim.convert.snowfall_approximation`` and ``xclim.convert.rain_approximation``, taken from :cite:t:`dai_snowfall_2008`. Indicator also take new argument ``landmask`` to switch between the land and ocean formulations. (:pull:`2208`, :issue:`1752`).
3031

3132
Breaking changes
3233
^^^^^^^^^^^^^^^^

docs/_static/indsearch.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
/* Array of indicator objects */
22
let indicators = [];
3-
let defModules = ["atmos", "generic", "land", "seaIce"];
3+
let defModules = ["atmos", "convert", "generic", "land", "seaIce"];
44
/* MiniSearch object defining search mechanism */
55
let miniSearch = new MiniSearch({
66
fields: ['title', 'abstract', 'variables', 'keywords', 'id'], // fields to index for full-text search

docs/api_indicators.rst

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
Indicators are the main tool xclim provides to compute climate indices. In contrast
22
to the function defined in `xclim.indices`, Indicators add a layer of health checks
3-
and metadata handling. Indicator objects are split into realms : atmos, land and seaIce.
3+
and metadata handling. Indicator objects are split into submodules according to their
4+
"realm" : atmos, land and seaIce, with two additional submodules : generic (for
5+
indicator that don't apply to a specific variable) and convert (for non-resampling
6+
indicators that transform between variables).
47

58
Virtual modules are also inserted here. A normal installation of xclim comes with three virtual modules:
69

@@ -26,6 +29,12 @@ Climate Indicators API
2629
:undoc-members:
2730
:imported-members:
2831

32+
.. automodule:: xclim.indicators.convert
33+
:members:
34+
:undoc-members:
35+
:imported-members:
36+
37+
2938
Virtual Indicator Submodules
3039
----------------------------
3140

docs/conf.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@
4646
# Get all indicators and some information about them
4747
indicators = {}
4848
# FIXME: Include cf module when its indicators documentation is improved.
49-
for module in ("atmos", "generic", "land", "seaIce", "icclim", "anuclim"):
49+
for module in ("atmos", "convert", "generic", "land", "seaIce", "icclim", "anuclim"):
5050
for key, ind in getattr(_indicators, module).__dict__.items():
5151
if hasattr(ind, "_registry_id") and ind._registry_id in registry: # noqa
5252
indicators[ind._registry_id] = { # noqa

docs/indices.rst

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,7 @@ types.
2828

2929
Indices functions do not perform missing value checks, and usually do not set CF-Convention attributes
3030
(long_name, standard_name, description, cell_methods, etc.). These functionalities are provided by
31-
:py:class:`xclim.core.indicator.Indicator` instances found in the :py:mod:`xclim.indicators.atmos`,
32-
:py:mod:`xclim.indicators.land` and :mod:`xclim.indicators.seaIce` modules.
31+
:py:class:`xclim.core.indicator.Indicator` instances found in the submodules of :py:mod:`xclim.indicators`.
3332

3433
.. automodule:: xclim.indices
3534
:members:

docs/notebooks/cli.ipynb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@
6565
"cell_type": "markdown",
6666
"metadata": {},
6767
"source": [
68-
"For more information about a specific indicator, you can either use the `info` sub-command or directly access the `--help` message of the indicator. The former gives more information about the metadata, while the latter only prints the usage. Note that the module name (`atmos`, `land` or `seaIce`) is mandatory."
68+
"For more information about a specific indicator, you can either use the `info` sub-command or directly access the `--help` message of the indicator. The former gives more information about the metadata, while the latter only prints the usage. Note that the module name (`atmos`, `convert`, `land` or `seaIce`) is mandatory."
6969
]
7070
},
7171
{

docs/notebooks/usage.ipynb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
"\n",
99
"## Climate indicator computations\n",
1010
"\n",
11-
"`xclim` is a library of climate indicators that operate on [xarray](https://docs.xarray.dev/en/stable/) `DataArray` objects. Indicators perform health checks on input data, converts units as needed, assign nans when input data is missing, and format outputs according to the Climate and Forecast (CF) convention. As the list of indicators has grown quite large, indicators are accessed through their *realm* (`xclim.atmos`, `xclim.land` and `xclim.seaIce`) to help browsing indicators by the domain they apply to. \n",
11+
"`xclim` is a library of climate indicators that operate on [xarray](https://docs.xarray.dev/en/stable/) `DataArray` objects. Indicators perform health checks on input data, converts units as needed, assign nans when input data is missing, and format outputs according to the Climate and Forecast (CF) convention. As the list of indicators has grown quite large, indicators are accessed through their *realm* (`xclim.atmos`, `xclim.land` and `xclim.seaIce`, plus the special `xclim.convert`) to help browsing indicators by the domain they apply to. \n",
1212
"\n",
1313
"**Indicators should not be confused with *indices***, which define the algorithmic layer of each indicator. Those indices perform no checks beyond units compliance, and should be considered as low-level functions. See the respective documentation on [indicators](../indicators.rst) and [indices](../indices.rst) for more information. \n",
1414
"\n",

docs/references.bib

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4149,6 +4149,25 @@ @article{schroter2015
41494149
}
41504150

41514151

4152+
@article{dai_snowfall_2008,
4153+
title = {Temperature and pressure dependence of the rain-snow phase transition over land
4154+
and ocean},
4155+
volume = {35},
4156+
copyright = {Copyright 2008 by the American Geophysical Union.},
4157+
issn = {1944-8007},
4158+
url = {https://onlinelibrary.wiley.com/doi/abs/10.1029/2008GL033295},
4159+
doi = {10.1029/2008GL033295},
4160+
language = {en},
4161+
number = {12},
4162+
urldate = {2025-07-09},
4163+
journal = {Geophysical Research Letters},
4164+
author = {Dai, Aiguo},
4165+
year = {2008},
4166+
note = {\_eprint: https://agupubs.onlinelibrary.wiley.com/doi/pdf/10.1029/2008GL033295},
4167+
keywords = {precipitation, phase transition, snow frequency}
4168+
}
4169+
4170+
41524171
@article{buck_new_1981,
41534172
title = {New {Equations} for {Computing} {Vapor} {Pressure} and {Enhancement} {Factor}},
41544173
volume = {20},

src/xclim/core/formatting.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -601,6 +601,7 @@ def unprefix_attrs(source: dict, keys: Sequence, prefix: str) -> dict:
601601
InputKind.VARIABLE: "str or DataArray",
602602
InputKind.OPTIONAL_VARIABLE: "str or DataArray, optional",
603603
InputKind.QUANTIFIED: "quantity (string or DataArray, with units)",
604+
InputKind.MASK: "DataArray or scalar",
604605
InputKind.FREQ_STR: "offset alias (string)",
605606
InputKind.NUMBER: "number",
606607
InputKind.NUMBER_SEQUENCE: "number or sequence of numbers",

src/xclim/core/utils.py

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -575,7 +575,7 @@ class InputKind(IntEnum):
575575
VARIABLE = 0
576576
"""A data variable (DataArray or variable name).
577577
578-
Annotation : ``xr.DataArray``.
578+
Annotation : ``xr.DataArray``. May not include anything else, may not be optional.
579579
"""
580580
OPTIONAL_VARIABLE = 1
581581
"""An optional data variable (DataArray or variable name).
@@ -633,6 +633,12 @@ class InputKind(IntEnum):
633633
634634
Annotation : ``dict`` or ``dict | None``, may be optional.
635635
"""
636+
MASK = 11
637+
"""A mask or flag or scalar. Any value without units that might be passed as a non-temporal DataArray.
638+
Can be a DataArray, a single bool or a single float.
639+
640+
Annotation : ``xr.DataArray | bool`` or ``xr.DataArray | float``, may be optional.
641+
"""
636642
KWARGS = 50
637643
"""A mapping from argument name to value.
638644
@@ -673,11 +679,15 @@ def infer_kind_from_parameter(param) -> InputKind:
673679
else:
674680
annot = {"no_annotation"}
675681

676-
if "DataArray" in annot and "None" not in annot and param.default is not None:
682+
if annot == {"DataArray"} and param.default is not None:
677683
return InputKind.VARIABLE
678684

679685
annot = annot - {"None"}
680686

687+
if annot == {"DataArray", "bool"} or annot == {"DataArray", "float"} or annot == {"DataArray", "int"}:
688+
return InputKind.MASK
689+
690+
# Not a mask and not a required variable
681691
if "DataArray" in annot:
682692
return InputKind.OPTIONAL_VARIABLE
683693

0 commit comments

Comments
 (0)