diff --git a/.zenodo.json b/.zenodo.json index 6d8446f7f..9242df632 100644 --- a/.zenodo.json +++ b/.zenodo.json @@ -125,6 +125,10 @@ "name": "Braun, Marco", "affiliation": "Ouranos Consortium, Montréal, Québec, Canada", "orcid": "0000-0001-5061-3217" + }, + { + "name": "Castro, Dante", + "affiliation": "Helmholtz-Zentrum Hereon, Geesthacht, Germany" } ], "keywords": [ diff --git a/AUTHORS.rst b/AUTHORS.rst index 46208a9c8..2f2e29442 100644 --- a/AUTHORS.rst +++ b/AUTHORS.rst @@ -40,3 +40,4 @@ Contributors * Ag Stephens `@agstephens `_ * Maliko Tanguy `@malngu `_ * Christopher Whelan `@qwhelan `_ +* Dante Castro `@profesorpaiche `_ diff --git a/CHANGES.rst b/CHANGES.rst index dd83cfc13..f86f97581 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -4,7 +4,7 @@ Changelog v0.48 (unreleased) ------------------ -Contributors to this version: Juliette Lavoie (:user:`juliettelavoie`), Pascal Bourgault (:user:`aulemahal`), Trevor James Smith (:user:`Zeitsperre`), David Huard (:user:`huard`), Éric Dupuis (:user:`coxipi`). +Contributors to this version: Juliette Lavoie (:user:`juliettelavoie`), Pascal Bourgault (:user:`aulemahal`), Trevor James Smith (:user:`Zeitsperre`), David Huard (:user:`huard`), Éric Dupuis (:user:`coxipi`), Dante Castro (:user:`profesorpaiche`). Announcements ^^^^^^^^^^^^^ @@ -21,6 +21,7 @@ New features and enhancements * Support ``indexer`` keyword in YAML indicator description. (:issue:`1522`, :pull:`1561`). * New ``xclim.core.calendar.stack_periods`` and ``unstack_periods`` for performing ``rolling(time=...).construct(..., stride=...)`` but with non-uniform temporal periods like years or months. They replace ``xclim.sdba.processing.construct_moving_yearly_window`` and ``unpack_moving_yearly_window`` which are deprecated and will be removed in a future release. * New ``as_dataset`` options for ``xclim.set_options``. When True, indicators will output Datasets instead of DataArrays. (:issue:`1257`, :pull:`1625`). +* Added new option for UTCI calculation to cap low wind velocities to a minimum of 0.5 m/s following Bröde (2012) guidelines. (:issue:`1634`, :pull:`1635`). Breaking changes ^^^^^^^^^^^^^^^^ diff --git a/docs/references.bib b/docs/references.bib index 2da431ffe..579a3ef56 100644 --- a/docs/references.bib +++ b/docs/references.bib @@ -2098,3 +2098,19 @@ @article{thom_1958 abstract = {Abstract The general properties of the gamma distribution, which has several applications in meteorology, are discussed. A short review of the general properties of good statistical estimators is given. This is applied to the gamma distribution to show that the maximum likelihood estimators are jointly sufficient. A new, simple approximation of the likelihood solutions is given, and the efficiency of the fitting procedure is computed.}, chapter = {Monthly Weather Review}, } + +@article{brode_utci_2012, + author = {Bröde, Peter and Fiala, Dusan and Błażejczyk, Krzysztof and Holmér, Ingvar and Jendritzky, Gerd and Kampmann, Bernhard and Tinz, Birger and Havenith, George}, + title = {Deriving the operational procedure for the Universal Thermal Climate Index (UTCI)}, + journal = {International Journal of Biometeorology}, + year = {2012}, + month = {May}, + day = {01}, + volume = {56}, + number = {3}, + pages = {481-494}, + abstract = {The Universal Thermal Climate Index (UTCI) aimed for a one-dimensional quantity adequately reflecting the human physiological reaction to the multi-dimensionally defined actual outdoor thermal environment. The human reaction was simulated by the UTCI-Fiala multi-node model of human thermoregulation, which was integrated with an adaptive clothing model. Following the concept of an equivalent temperature, UTCI for a given combination of wind speed, radiation, humidity and air temperature was defined as the air temperature of the reference environment, which according to the model produces an equivalent dynamic physiological response. Operationalising this concept involved (1) the definition of a reference environment with 50{\%} relative humidity (but vapour pressure capped at 20 hPa), with calm air and radiant temperature equalling air temperature and (2) the development of a one-dimensional representation of the multivariate model output at different exposure times. The latter was achieved by principal component analyses showing that the linear combination of 7 parameters of thermophysiological strain (core, mean and facial skin temperatures, sweat production, skin wettedness, skin blood flow, shivering) after 30 and 120 min exposure time accounted for two-thirds of the total variation in the multi-dimensional dynamic physiological response. The operational procedure was completed by a scale categorising UTCI equivalent temperature values in terms of thermal stress, and by providing simplified routines for fast but sufficiently accurate calculation, which included look-up tables of pre-calculated UTCI values for a grid of all relevant combinations of climate parameters and polynomial regression equations predicting UTCI over the same grid. The analyses of the sensitivity of UTCI to humidity, radiation and wind speed showed plausible reactions in the heat as well as in the cold, and indicate that UTCI may in this regard be universally useable in the major areas of research and application in human biometeorology.}, + issn = {1432-1254}, + doi = {10.1007/s00484-011-0454-1}, + url = {https://doi.org/10.1007/s00484-011-0454-1} +} diff --git a/tests/test_indices.py b/tests/test_indices.py index 80cb29d35..e815b1766 100644 --- a/tests/test_indices.py +++ b/tests/test_indices.py @@ -3481,26 +3481,31 @@ def test_simple(self, pr_series): np.testing.assert_allclose(out, [4 / 31, 0, 0, 2 / 31, 0, 0, 0, 0, 0, 0, 0, 0]) +@pytest.mark.parametrize( + "wind_cap_min,wind,expected", + [(False, 2, 17.70), (False, 1, np.nan), (True, 1, 17.76)], +) def test_universal_thermal_climate_index( tas_series, hurs_series, sfcWind_series, + wind_cap_min, + wind, + expected, ): tas = tas_series(np.array([16]) + K2C) hurs = hurs_series(np.array([36])) - sfcWind = sfcWind_series(np.array([2])) + sfcWind = sfcWind_series(np.array([wind])) mrt = tas_series(np.array([22]) + K2C) - # Expected values - utci_exp = [17.7] - utci = xci.universal_thermal_climate_index( tas=tas, hurs=hurs, sfcWind=sfcWind, mrt=mrt, + wind_cap_min=wind_cap_min, ) - np.testing.assert_allclose(utci, utci_exp, rtol=1e-03) + np.testing.assert_allclose(utci, expected, rtol=1e-03) @pytest.mark.parametrize("stat,expected", [("sunlit", 295.0), ("instant", 294.9)]) diff --git a/xclim/indices/_conversion.py b/xclim/indices/_conversion.py index 8483fb880..0c6798cb6 100644 --- a/xclim/indices/_conversion.py +++ b/xclim/indices/_conversion.py @@ -1781,6 +1781,7 @@ def universal_thermal_climate_index( rlus: xr.DataArray | None = None, stat: str = "sunlit", mask_invalid: bool = True, + wind_cap_min: bool = False, ) -> xr.DataArray: r"""Universal thermal climate index (UTCI). @@ -1818,6 +1819,10 @@ def universal_thermal_climate_index( If True (default), UTCI values are NaN where any of the inputs are outside their validity ranges : -50°C < tas < 50°C, -30°C < tas - mrt < 30°C and 0.5 m/s < sfcWind < 17.0 m/s. + wind_cap_min: bool + If True, wind velocities are capped to a minimum of 0.5 m/s following + :cite:t:`brode_utci_2012` usage guidalines. This ensures UTCI calculation + for low winds. Default value False. Returns ------- @@ -1837,11 +1842,13 @@ def universal_thermal_climate_index( References ---------- - :cite:cts:`brode_utci_2009,blazejczyk_introduction_2013` + :cite:cts:`brode_utci_2009,brode_utci_2012,blazejczyk_introduction_2013` """ e_sat = saturation_vapor_pressure(tas=tas, method="its90") tas = convert_units_to(tas, "degC") sfcWind = convert_units_to(sfcWind, "m/s") + if wind_cap_min: + sfcWind = sfcWind.clip(0.5, None) if mrt is None: mrt = mean_radiant_temperature( rsds=rsds, rsus=rsus, rlds=rlds, rlus=rlus, stat=stat @@ -1868,7 +1875,7 @@ def universal_thermal_climate_index( & (tas < 50.0) & (-30 < delta) & (delta < 30) - & (0.5 < sfcWind) + & (0.5 <= sfcWind) & (sfcWind < 17.0) ) return utci