Skip to content

Commit

Permalink
fix snow season
Browse files Browse the repository at this point in the history
  • Loading branch information
aulemahal committed Apr 16, 2024
1 parent c75423c commit f2687a7
Show file tree
Hide file tree
Showing 6 changed files with 194 additions and 86 deletions.
4 changes: 4 additions & 0 deletions CHANGES.rst
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,10 @@ Announcements
^^^^^^^^^^^^^
* `xclim` has migrated its development branch name from `master` to `main`. (:issue:`1667`, :pull:`1669`).

New indicators
^^^^^^^^^^^^^^
* New ``snw_season_length`` and ``snd_season_length`` computing the duration between the start and the end of the snow season, both defined as the first day of a continuous period with snow above/under a threshold. Previous versions of these indicators were renamed ``snw_days_above`` and ``snd_days_above`` to better reflect what they computed : the number of days with snow above a given threshold (with no notion of continuity). (:issue:`1703`, :pull:`1708`).

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`).
Expand Down
16 changes: 11 additions & 5 deletions tests/test_indices.py
Original file line number Diff line number Diff line change
Expand Up @@ -2820,21 +2820,27 @@ def test_nan_slices(self, snd_series, snw_series):


class TestSnowCover:
@pytest.mark.parametrize("length", [0, 10])
@pytest.mark.parametrize("length", [0, 15])
def test_snow_season_length(self, snd_series, snw_series, length):
a = np.zeros(366)
a[10 : 10 + length] = 0.3
a[20 : 20 + length] = 0.3
snd = snd_series(a)
# kg m-2 = 1000 kg m-3 * 1 m
snw = snw_series(1000 * a)

out = xci.snd_season_length(snd)
assert len(out) == 2
assert out[0] == length
if length == 0:
assert out.isnull().all()
else:
assert out[0] == length

out = xci.snw_season_length(snw)
assert len(out) == 2
assert out[0] == length
if length == 0:
assert out.isnull().all()
else:
assert out[0] == length

def test_continous_snow_season_start(self, snd_series, snw_series):
a = np.arange(366) / 100.0
Expand All @@ -2851,7 +2857,7 @@ def test_continous_snow_season_start(self, snd_series, snw_series):

out = xci.snw_season_start(snw)
assert len(out) == 2
np.testing.assert_array_equal(out, [snw.time.dt.dayofyear[0].data + 2, np.nan])
np.testing.assert_array_equal(out, [snw.time.dt.dayofyear[0].data + 1, np.nan])
for attr in ["units", "is_dayofyear", "calendar"]:
assert attr in out.attrs.keys()
assert out.attrs["units"] == ""
Expand Down
18 changes: 14 additions & 4 deletions tests/test_snow.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ class TestSnowDepthCoverDuration:
def test_simple(self, snd_series):
snd = snd_series(np.ones(110), start="2001-01-01")

out = land.snd_season_length(snd, freq="ME")
out = land.snd_days_above(snd, freq="ME")
assert out.units == "days"
np.testing.assert_array_equal(out, [31, 28, 31, np.nan])

Expand All @@ -30,16 +30,17 @@ class TestSnowWaterCoverDuration:
)
def test_simple(self, snw_series, factor, exp):
snw = snw_series(np.ones(110) * factor, start="2001-01-01")
out = land.snw_season_length(snw, freq="ME")
out = land.snw_days_above(snw, freq="ME")
assert out.units == "days"
np.testing.assert_array_equal(out, exp)


class TestContinuousSnowDepthCoverStartEnd:
class TestContinuousSnowDepthSeason:
def test_simple(self, snd_series):
a = np.zeros(365)
# snow depth
a[100:200] = 0.03
a[150:160] = 0
snd = snd_series(a, start="2001-07-01")
snd = snd.expand_dims(lat=[0, 1, 2])

Expand All @@ -51,12 +52,17 @@ def test_simple(self, snd_series):
assert out.units == ""
np.testing.assert_array_equal(out.isel(lat=0), snd.time.dt.dayofyear[200])

out = land.snd_season_length(snd)
assert out.units == "days"
np.testing.assert_array_equal(out.isel(lat=0), 100)


class TestContinuousSnowWaterCoverStartEnd:
class TestContinuousSnowWaterSeason:
def test_simple(self, snw_series):
a = np.zeros(365)
# snow amount
a[100:200] = 0.03 * 1000
a[150:160] = 0
snw = snw_series(a, start="2001-07-01")
snw = snw.expand_dims(lat=[0, 1, 2])

Expand All @@ -68,6 +74,10 @@ def test_simple(self, snw_series):
assert out.units == ""
np.testing.assert_array_equal(out.isel(lat=0), snw.time.dt.dayofyear[200])

out = land.snw_season_length(snw)
assert out.units == "days"
np.testing.assert_array_equal(out.isel(lat=0), 100)


class TestSndMaxDoy:
def test_simple(self, snd_series):
Expand Down
20 changes: 16 additions & 4 deletions xclim/data/fr.json
Original file line number Diff line number Diff line change
Expand Up @@ -1005,9 +1005,9 @@
},
"SND_SEASON_LENGTH": {
"long_name": "Durée de couvert de neige",
"description": "Nombre {freq:m} de jours où l'épaisseur de neige est au-dessus ou égale à {thresh}.",
"description": "La saison débute lorsque l'épaisseur de neige est au-dessus de {thresh} durant {window} jours et se termine lorsqu'elle redescend sous {thresh} durant {window} jours.",
"title": "Durée du couvert de neige (épaisseur)",
"abstract": "Nombre de jours pendant lesquels l'épaisseur de neige est au-dessus ou égale à un seuil donné."
"abstract": "Durée de la saison de neige, qui débute lorsque la quantité de neige est au-dessus d'un seuil donné durant un nombre de jours donnés et qui se termine lorsqu'elle redescend sous le seuil pour la même durée."
},
"SND_SEASON_START": {
"long_name": "Date du début du couvert de neige continu",
Expand All @@ -1021,11 +1021,17 @@
"title": "Date du fin du couvert de neige (épaisseur)",
"abstract": "Première date à partir de laquelle l'épaisseur de neige est sous à un seuil donné pendant un nombre de jours consécutifs suite au début du couvert de neige."
},
"SND_DAYS_ABOVE": {
"long_name": "Nombre de jours avec de la neige au sol.",
"description": "Nombre {freq:m} de jours avec au moins {thresh} de neige au sol.",
"title": "Nombre de jours avec de la neige au sol (épaisseur)",
"abstract": "Nombre de jours avec une épaisseur de neige au sol au-dessus d'un seuil donné."
},
"SNW_SEASON_LENGTH": {
"long_name": "Durée de couvert de neige",
"description": "Nombre {freq:m} de jours où la quantité de neige est au-dessus ou égale à {thresh}.",
"description": "La saison débute lorsque la quantité de neige est au-dessus de {thresh} durant {window} jours et se termine lorsqu'elle redescend sous {thresh} durant {window} jours.",
"title": "Durée du couvert de neige (quantité)",
"abstract": "Nombre de jours pendant lesquels la quantité de neige est au-dessus ou égale à un seuil donné."
"abstract": "Durée de la saison de neige, qui débute lorsque l'épaisseur de neige est au-dessus d'un seuil donné durant un nombre de jours donnés et qui se termine lorsqu'elle redescend sous le seuil pour la même durée."
},
"SNW_SEASON_START": {
"long_name": "Date du début du couvert de neige continu",
Expand All @@ -1039,6 +1045,12 @@
"title": "Date du fin du couvert de neige (quantité)",
"abstract": "Première date à partir de laquelle la quantité de neige est sous à un seuil donné pendant un nombre de jours consécutifs suite au début du couvert de neige."
},
"SNW_DAYS_ABOVE": {
"long_name": "Nombre de jours avec de la neige au sol.",
"description": "Nombre {freq:m} de jours avec au moins {thresh} de neige au sol.",
"title": "Nombre de jours avec de la neige au sol (quantité)",
"abstract": "Nombre de jours avec une quantité de neige au sol au-dessus d'un seuil donné."
},
"SND_MAX": {
"long_name": "Épaisseur de neige maximale",
"description": "Épaisseur maximale {freq:f} de la neige.",
Expand Down
35 changes: 31 additions & 4 deletions xclim/indicators/land/_snow.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

__all__ = [
"blowing_snow",
"snd_days_above",
"snd_max_doy",
"snd_season_end",
"snd_season_length",
Expand All @@ -14,6 +15,7 @@
"snd_to_snw",
"snow_depth",
"snow_melt_we_max",
"snw_days_above",
"snw_max",
"snw_max_doy",
"snw_season_end",
Expand Down Expand Up @@ -43,8 +45,10 @@ class SnowWithIndexing(ResamplingIndicatorWithIndexing):
identifier="snd_season_length",
units="days",
long_name="Snow cover duration",
description="The {freq} number of days with snow depth greater than or equal to {thresh}.",
abstract="Number of days when the snow depth is greater than or equal to a given threshold.",
description=(
"The duration of the snow season, starting with at least {window} days with snow depth above {thresh} "
"and ending with at least {window} days with snow depth under {thresh}."
),
compute=xci.snd_season_length,
)

Expand All @@ -53,8 +57,10 @@ class SnowWithIndexing(ResamplingIndicatorWithIndexing):
identifier="snw_season_length",
units="days",
long_name="Snow cover duration",
description="The {freq} number of days with snow amount greater than or equal to {thresh}.",
abstract="Number of days when the snow amount is greater than or equal to a given threshold.",
description=(
"The duration of the snow season, starting with at least {window} days with snow amount above {thresh} "
"and ending with at least {window} days with snow amount under {thresh}."
),
compute=xci.snw_season_length,
)

Expand Down Expand Up @@ -231,3 +237,24 @@ class SnowWithIndexing(ResamplingIndicatorWithIndexing):
var_name="snd",
compute=xci.snw_to_snd,
)


snd_days_above = SnowWithIndexing(
title="Days with snow (depth)",
identifier="snd_days_above",
units="days",
long_name="Number of days with snow",
description="The {freq} number of days with snow depth greater than or equal to {thresh}.",
abstract="Number of days when the snow depth is greater than or equal to a given threshold.",
compute=xci.snd_days_above,
)

snw_days_above = SnowWithIndexing(
title="Days with snow (amount)",
identifier="snw_days_above",
units="days",
long_name="Number of days with snow",
description="The {freq} number of days with snow amount greater than or equal to {thresh}.",
abstract="Number of days when the snow amount is greater than or equal to a given threshold.",
compute=xci.snw_days_above,
)
Loading

0 comments on commit f2687a7

Please sign in to comment.