From 5281e2471b441f16cfb16e8e35e041ee26a508be Mon Sep 17 00:00:00 2001 From: Pascal Bourgault Date: Wed, 17 Apr 2024 15:00:38 -0400 Subject: [PATCH 1/4] Standalone DMC --- tests/test_cffwis.py | 44 +++++++++++++ xclim/indicators/atmos/_precip.py | 10 ++- xclim/indices/__init__.py | 1 + xclim/indices/fire/_cffwis.py | 105 ++++++++++++++++++++++++++++-- 4 files changed, 155 insertions(+), 5 deletions(-) diff --git a/tests/test_cffwis.py b/tests/test_cffwis.py index f9f3a3cb4..d17ae4b8e 100644 --- a/tests/test_cffwis.py +++ b/tests/test_cffwis.py @@ -458,3 +458,47 @@ def test_gfwed_and_indicators(self, open_dataset): [ds2.DC, ds2.DMC, ds2.FFMC, ds2.ISI, ds2.BUI, ds2.FWI], outs ): np.testing.assert_allclose(out, exp, rtol=0.03) + + def test_gfwed_drought_code(self, open_dataset): + # Also tests passing parameters as quantity strings + ds = open_dataset("FWI/GFWED_sample_2017.nc") + + out = atmos.drought_code( + tas=ds.tas, + pr=ds.prbc, + snd=ds.snow_depth, + lat=ds.lat, + season_method="GFWED", + overwintering=False, + dry_start="GFWED", + temp_condition_days=3, + snow_condition_days=3, + temp_start_thresh="6 degC", + temp_end_thresh="6 degC", + ) + + np.testing.assert_allclose( + out.isel(loc=[0, 1]), ds.DC.isel(loc=[0, 1]), rtol=0.03 + ) + + def test_gfwed_duff_moisture_code(self, open_dataset): + # Also tests passing parameters as quantity strings + ds = open_dataset("FWI/GFWED_sample_2017.nc") + + out = atmos.duff_moisture_code( + tas=ds.tas, + pr=ds.prbc, + hurs=ds.rh, + snd=ds.snow_depth, + lat=ds.lat, + season_method="GFWED", + dry_start="GFWED", + temp_condition_days=3, + snow_condition_days=3, + temp_start_thresh="6 degC", + temp_end_thresh="6 degC", + ) + + np.testing.assert_allclose( + out.isel(loc=[0, 1]), ds.DMC.isel(loc=[0, 1]), rtol=0.03 + ) diff --git a/xclim/indicators/atmos/_precip.py b/xclim/indicators/atmos/_precip.py index f6b906b76..a47658e4e 100644 --- a/xclim/indicators/atmos/_precip.py +++ b/xclim/indicators/atmos/_precip.py @@ -29,6 +29,7 @@ "dry_spell_max_length", "dry_spell_total_length", "dryness_index", + "duff_moisture_code", "first_snowfall", "fraction_over_precip_doy_thresh", "fraction_over_precip_thresh", @@ -394,7 +395,6 @@ class HrPrecip(Hourly): title="Daily drought code", identifier="dc", units="", - standard_name="drought_code", long_name="Drought Code", description="Numerical code estimating the average moisture content of organic layers.", abstract="The Drought Index is part of the Canadian Forest-Weather Index system. " @@ -403,6 +403,14 @@ class HrPrecip(Hourly): missing="skip", ) +duff_moisture_code = FireWeather( + identifier="dmc", + units="", + long_name="Duff Moisture Code", + description="Numeric rating of the average moisture content of loosely compacted organic layers of moderate depth.", + compute=indices.duff_moisture_code, + missing="skip", +) cffwis_indices = FireWeather( identifier="cffwis", diff --git a/xclim/indices/__init__.py b/xclim/indices/__init__.py index 9689d5208..b7acd4544 100644 --- a/xclim/indices/__init__.py +++ b/xclim/indices/__init__.py @@ -13,6 +13,7 @@ from .fire import ( cffwis_indices, drought_code, + duff_moisture_code, fire_season, griffiths_drought_factor, keetch_byram_drought_index, diff --git a/xclim/indices/fire/_cffwis.py b/xclim/indices/fire/_cffwis.py index 240314502..010c9f312 100644 --- a/xclim/indices/fire/_cffwis.py +++ b/xclim/indices/fire/_cffwis.py @@ -150,6 +150,7 @@ "cffwis_indices", "daily_severity_rating", "drought_code", + "duff_moisture_code", "fire_season", "fire_weather_index", "fire_weather_ufunc", @@ -1247,12 +1248,13 @@ def overwintering_drought_code( return wDC -# TODO: The `noqa` can be removed when this function is no longer public -def _convert_parameters(params: dict[str, int | float]) -> dict[str, int | float]: +def _convert_parameters( + params: dict[str, int | float], funcname: str = "fire weather indices" +) -> dict[str, int | float]: for param, value in params.copy().items(): if param not in default_params: raise ValueError( - f"{param} is not a valid parameter for fire weather indices. See list in xc.indices.fire.default_params." + f"{param} is not a valid parameter for {funcname}. See the docstring of the function and the list in xc.indices.fire.default_params." ) if isinstance(default_params[param], tuple): params[param] = convert_units_to(value, default_params[param][1]) @@ -1469,12 +1471,107 @@ def drought_code( overwintering=overwintering, dry_start=dry_start, initial_start_up=initial_start_up, - **_convert_parameters(params), + **_convert_parameters(params, "drought_code"), ) out["DC"].attrs["units"] = "" return out["DC"] +@declare_units( + tas="[temperature]", + pr="[precipitation]", + hurs="[]", + lat="[]", + snd="[length]", + dmc0="[]", + season_mask="[]", +) +def duff_moisture_code( + tas: xr.DataArray, + pr: xr.DataArray, + hurs: xr.DataArray, + lat: xr.DataArray, + snd: xr.DataArray | None = None, + dmc0: xr.DataArray | None = None, + season_mask: xr.DataArray | None = None, + season_method: str | None = None, + dry_start: str | None = None, + initial_start_up: bool = True, + **params, +) -> xr.DataArray: + r"""Duff moisture code (FWI component). + + The duff moisture code is part of the Canadian Forest Fire Weather Index System. + It is a numeric rating of the average moisture content of loosely compacted organic layers of moderate depth. + + Parameters + ---------- + tas : xr.DataArray + Noon temperature. + pr : xr.DataArray + Rain fall in open over previous 24 hours, at noon. + hurs : xr.DataArray + Noon relative humidity. + lat : xr.DataArray + Latitude coordinate + snd : xr.DataArray + Noon snow depth. + dmc0 : xr.DataArray + Initial values of the duff moisture code. + season_mask : xr.DataArray, optional + Boolean mask, True where/when the fire season is active. + season_method : {None, "WF93", "LA08", "GFWED"} + How to compute the start-up and shutdown of the fire season. + If "None", no start-ups or shutdowns are computed, similar to the R fire function. + Ignored if `season_mask` is given. + dry_start : {None, "CFS", 'GFWED'} + Whether to activate the DC and DMC "dry start" mechanism and which method to use. + See :py:func:`fire_weather_ufunc`. + initial_start_up : bool + If True (default), grid points where the fire season is active on the first timestep go through a start_up phase + for that time step. Otherwise, previous codes must be given as a continuing fire season is assumed for those + points. + params + Any other keyword parameters as defined in `xclim.indices.fire.fire_weather_ufunc` and in :py:data:`default_params`. + + Returns + ------- + xr.DataArray, [dimensionless] + Duff moisture code + + Notes + ----- + See :cite:cts:`code-natural_resources_canada_data_nodate`, the :py:mod:`xclim.indices.fire` module documentation, + and the docstring of :py:func:`fire_weather_ufunc` for more information. This algorithm follows the official R code + released by the CFS, which contains revisions from the original 1982 Fortran code. + + References + ---------- + :cite:cts:`fire-wang_updated_2015` + """ + tas = convert_units_to(tas, "C") + pr = convert_units_to(pr, "mm/day") + if snd is not None: + snd = convert_units_to(snd, "m") + + out = fire_weather_ufunc( + tas=tas, + pr=pr, + hurs=hurs, + lat=lat, + dmc0=dmc0, + snd=snd, + indexes=["DMC"], + season_mask=season_mask, + season_method=season_method, + dry_start=dry_start, + initial_start_up=initial_start_up, + **_convert_parameters(params, "duff_moisture_code"), + ) + out["DMC"].attrs["units"] = "" + return out["DMC"] + + @declare_units( tas="[temperature]", snd="[length]", From 7ce0476648298dc7250756e72888f75260d9bcb3 Mon Sep 17 00:00:00 2001 From: Pascal Bourgault Date: Wed, 17 Apr 2024 15:02:52 -0400 Subject: [PATCH 2/4] Upd changes --- CHANGES.rst | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index f6ad34277..05989cbbe 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -10,6 +10,10 @@ Announcements ^^^^^^^^^^^^^ * `xclim` has migrated its development branch name from `master` to `main`. (:issue:`1667`, :pull:`1669`). +New indicators +^^^^^^^^^^^^^^ +* Added ``atmos.duff_moisture_code``, part of the Canadian Forest Fire Weather Index System. It was already an output of the `atmos.cffwis_indices`, but now has its own standalone indicator. (:issue:`1698`, :pull:`1712`). + Bug fixes ^^^^^^^^^ * Fixed an bug in sdba's ``map_groups`` that prevented passing DataArrays with cftime coordinates if the ``sdba_encode_cf`` option was True. (:issue:`1673`, :pull:`1674`). From 6ab60ca8993da40971a7aab947a8d705dddf8311 Mon Sep 17 00:00:00 2001 From: Zeitsperre <10819524+Zeitsperre@users.noreply.github.com> Date: Thu, 18 Apr 2024 14:04:00 -0400 Subject: [PATCH 3/4] add French translation --- xclim/data/fr.json | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/xclim/data/fr.json b/xclim/data/fr.json index da193349f..dc5e89282 100644 --- a/xclim/data/fr.json +++ b/xclim/data/fr.json @@ -799,6 +799,12 @@ "long_name": "Indice forêt météo", "description": "Évaluation numérique de l'intensité du feu." }, + "DMC": { + "long_name": "Indice d'humidité de l'humus (DMC)", + "description": "Évaluation numérique de la teneur en eau moyenne des couches organiques peu compactes d'une profondeur modérée.", + "title": "Indice d'humidité de l'humus", + "abstract": "Évaluation numérique de la teneur en eau moyenne des couches organiques peu compactes d'une profondeur modérée." + }, "WIND_POWER_POTENTIAL": { "long_name": "Potentiel de production éolienne", "description": "Potentiel de production éolienne estimé par une courbe de puissance semi-idéalisée d'une turbine, paramétrée par une vitesse minimale de démarrage {cut_in}, la vitesse nominale {rated}, et la vitesse d'arrêt {cut_out}.", From a070de2eb85402b6cbb13c181f9914fab9c61bf4 Mon Sep 17 00:00:00 2001 From: Pascal Bourgault Date: Thu, 18 Apr 2024 14:10:21 -0400 Subject: [PATCH 4/4] Update xclim/data/fr.json --- xclim/data/fr.json | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/xclim/data/fr.json b/xclim/data/fr.json index dc5e89282..be0cc4a14 100644 --- a/xclim/data/fr.json +++ b/xclim/data/fr.json @@ -800,10 +800,10 @@ "description": "Évaluation numérique de l'intensité du feu." }, "DMC": { - "long_name": "Indice d'humidité de l'humus (DMC)", - "description": "Évaluation numérique de la teneur en eau moyenne des couches organiques peu compactes d'une profondeur modérée.", - "title": "Indice d'humidité de l'humus", - "abstract": "Évaluation numérique de la teneur en eau moyenne des couches organiques peu compactes d'une profondeur modérée." + "long_name": "Indice de l'humus", + "description": "Évaluation numérique de la teneur moyenne en eau des couches organiques peu tassées de moyenne épaisseur.", + "title": "Indice de l'humus (IH)", + "abstract": "Évaluation numérique de la teneur moyenne en eau des couches organiques peu tassées de moyenne épaisseur." }, "WIND_POWER_POTENTIAL": { "long_name": "Potentiel de production éolienne",