Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Snow season and days above #1708

Merged
merged 8 commits into from
Apr 18, 2024
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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`).
Zeitsperre marked this conversation as resolved.
Show resolved Hide resolved

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."
aulemahal marked this conversation as resolved.
Show resolved Hide resolved
},
"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",
aulemahal marked this conversation as resolved.
Show resolved Hide resolved
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)",
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"Days with snow greater than 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
Loading