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 all 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
5 changes: 5 additions & 0 deletions CHANGES.rst
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,14 @@ 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

Breaking changes
^^^^^^^^^^^^^^^^
* The previously deprecated functions ``xclim.sdba.processing.construct_moving_yearly_window`` and ``xclim.sdba.processing.unpack_moving_yearly_window`` have been removed. These functions have been replaced by ``xclim.core.calendar.stack_periods`` and ``xclim.core.calendar.unstack_periods``. (:pull:`1717`).
* Indicators ``snw_season_length`` and ``snd_season_length`` have been modified, see above.

Bug fixes
^^^^^^^^^
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
28 changes: 20 additions & 8 deletions xclim/data/fr.json
Original file line number Diff line number Diff line change
Expand Up @@ -1005,40 +1005,52 @@
},
"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é 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",
"description": "Première date à laquelle l'épaisseur de neige est au-dessus ou égale à {thresh} pendant au moins {window} jours consécutifs.",
"title": "Date du début du couvert de neige (épaisseur)",
"title": "Date de début du couvert de neige (épaisseur)",
"abstract": "Première date à partir de laquelle l'épaisseur de neige est au-dessus ou égale à un seuil donné pendant un nombre de jours consécutifs."
},
"SND_SEASON_END": {
"long_name": "Date de fin du couvert de neige continu",
"description": "Première date à laquelle l'épaisseur de neige passe sous {thresh} pendant au moins {window} jours consécutifs suite à l'établissement du couvert de neige.",
"title": "Date du fin du couvert de neige (épaisseur)",
"title": "Date de 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",
"description": "Première date à laquelle la quantité de neige est au-dessus ou égale à {thresh} pendant au moins {window} jours consécutifs.",
"title": "Date du début du couvert de neige (quantité)",
"title": "Date de début du couvert de neige (quantité)",
"abstract": "Première date à partir de laquelle la quantité de neige est au-dessus ou égale à un seuil donné pendant un nombre de jours consécutifs."
},
"SNW_SEASON_END": {
"long_name": "Date de fin du couvert de neige continu",
"description": "Première date à laquelle la quantité de neige passe sous {thresh} pendant au moins {window} jours consécutifs suite à l'établissement du couvert de neige.",
"title": "Date du fin du couvert de neige (quantité)",
"title": "Date de 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
41 changes: 31 additions & 10 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 All @@ -39,27 +41,28 @@ class SnowWithIndexing(ResamplingIndicatorWithIndexing):


snd_season_length = SnowWithIndexing(
title="Snow cover duration (depth)",
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,
)

snw_season_length = SnowWithIndexing(
title="Snow cover duration (amount)",
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,
)

snd_season_start = Snow(
title="Start date of continuous snow depth cover",
identifier="snd_season_start",
standard_name="day_of_year",
long_name="Start date of continuous snow depth cover",
Expand All @@ -71,7 +74,6 @@ class SnowWithIndexing(ResamplingIndicatorWithIndexing):
)

snw_season_start = Snow(
title="Start date of continuous snow amount cover",
identifier="snw_season_start",
standard_name="day_of_year",
long_name="Start date of continuous snow amount cover",
Expand All @@ -83,7 +85,6 @@ class SnowWithIndexing(ResamplingIndicatorWithIndexing):
)

snd_season_end = Snow(
title="End date of continuous snow depth cover",
identifier="snd_season_end",
standard_name="day_of_year",
long_name="End date of continuous snow depth cover",
Expand All @@ -94,7 +95,6 @@ class SnowWithIndexing(ResamplingIndicatorWithIndexing):
)

snw_season_end = Snow(
title="End date of continuous snow amount cover",
identifier="snw_season_end",
standard_name="day_of_year",
long_name="End date of continuous snow amount cover",
Expand Down Expand Up @@ -231,3 +231,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