Skip to content

Commit 7213c95

Browse files
EsmailGhaemiEsmail GhaemiEsmail Ghaemidneriniaperezhortal
authored
Implement SAL verification method (#248)
* Add initial structure * Add SAL method * Refactor imports * Add MissingOptionalDependency checks * Rename methods * Apply suggestions from code review Co-authored-by: Andres Perez Hortal <16256571+aperezhortal@users.noreply.github.com> * Add basic test * Add interface * More testing * Return a tuple instead of pandas dataframe * Some refactoring * More refactoring * Avoid re-computing the same calculations * Extract sal method into separate module * Minor refactoring * Rename tests * Fix compability issue and rename module * Remove unused argument * Add thr_factor argument * Fix backward compatibility * Rename variable * Fix black * Fix black part 2 * Replace max_precip with quantile95_precip Replace max_precip with quantile95_precip for calculating the threshold. * Convert masked arrays to numpy arrays This avoids problems with nanquantile * Fix skimage backward compatibility * Add bibliography * Compute quantile on wet pixels only * Fix property name * Set fill value in all cases * Make sure that it works with empty inputs Return nan when a score is undefined * Aesthetics * Add no-cache-dir flag * Pin numpy build version to match the one available on conda-forge * Update module name * Pin maximum numpy version * Revert commit 94ea041 * Set minmax=minref Following suggestion from @feldmann-m * Subpress all splits if thr_factor is passed Following suggestion from @feldmann-m * New set of parameters for tstorm * Add option to specifiy the detection quantile * Improve docstrings * Set minimum python version to 3.7 (#253) * Set minimum python version to 3.7 and maximum version to 3.9 * Update github actions Co-authored-by: Andres Perez Hortal <16256571+aperezhortal@users.noreply.github.com> * Add docstring references * Use mamba to setup test environments (#256) * Try using mamba to speedup installing dependencies * Use mamba.bat command for windows Co-authored-by: Esmail Ghaemi <esi@Esmails-Air.fritz.box> Co-authored-by: Esmail Ghaemi <esi@Esmails-MacBook-Air.local> Co-authored-by: Daniele Nerini <daniele.nerini@gmail.com> Co-authored-by: Andres Perez Hortal <16256571+aperezhortal@users.noreply.github.com> Co-authored-by: Daniele Nerini <daniele.nerini@meteoswiss.ch>
1 parent fc8e00a commit 7213c95

13 files changed

+556
-26
lines changed

doc/source/pysteps_reference/verification.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,4 +11,5 @@ Methods for verification of deterministic, probabilistic and ensemble forecasts.
1111
.. automodule:: pysteps.verification.lifetime
1212
.. automodule:: pysteps.verification.plots
1313
.. automodule:: pysteps.verification.probscores
14+
.. automodule:: pysteps.verification.salscores
1415
.. automodule:: pysteps.verification.spatialscores

doc/source/references.bib

Lines changed: 39 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,16 @@ @ARTICLE{EWWM2013
5252
DOI = "10.1002/met.1392"
5353
}
5454

55+
@ARTICLE{Feldmann2021,
56+
AUTHOR = "M. Feldmann and U. Germann and M. Gabella and A. Berne",
57+
TITLE = "A Characterisation of Alpine Mesocyclone Occurrence",
58+
JOURNAL = "Weather and Climate Dynamics Discussions",
59+
PAGES = "1--26",
60+
URL = "https://wcd.copernicus.org/preprints/wcd-2021-53/",
61+
DOI = "10.5194/wcd-2021-53",
62+
YEAR = 2021
63+
}
64+
5565
@ARTICLE{FSNBG2019,
5666
AUTHOR = "Foresti, L. and Sideris, I.V. and Nerini, D. and Beusch, L. and Germann, U.",
5767
TITLE = "Using a 10-Year Radar Archive for Nowcasting Precipitation Growth and Decay: A Probabilistic Machine Learning Approach",
@@ -264,6 +274,35 @@ @ARTICLE{SPN2013
264274
DOI = "10.1002/wrcr.20536"
265275
}
266276

277+
@ARTICLE{TRT2004,
278+
AUTHOR = "A. M. Hering and C. Morel and G. Galli and P. Ambrosetti and M. Boscacci",
279+
TITLE = "Nowcasting thunderstorms in the Alpine Region using a radar based adaptive thresholding scheme",
280+
JOURNAL = "Proceedings of ERAD Conference 2004",
281+
NUMBER = "January",
282+
PAGES = "206--211",
283+
YEAR = 2004
284+
}
285+
286+
@ARTICLE{WHZ2009,
287+
AUTHOR = "Heini Wernli and Christiane Hofmann and Matthias Zimmer",
288+
TITLE = "Spatial Forecast Verification Methods Intercomparison Project: Application of the SAL Technique",
289+
JOURNAL = "Weather and Forecasting",
290+
NUMBER = "6",
291+
VOLUME = "24",
292+
PAGES = "1472 - 14847",
293+
YEAR = 2009
294+
}
295+
296+
@ARTICLE{WPHF2008,
297+
AUTHOR = "Heini Wernli and Marcus Paulat and Martin Hagen and Christoph Frei",
298+
TITLE = "SAL—A Novel Quality Measure for the Verification of Quantitative Precipitation Forecasts",
299+
JOURNAL = "Monthly Weather Review",
300+
NUMBER = "11",
301+
VOLUME = "136",
302+
PAGES = "4470 - 4487",
303+
YEAR = 2008
304+
}
305+
267306
@ARTICLE{XWF2005,
268307
AUTHOR = "K. Xu and C. K Wikle and N. I. Fox",
269308
TITLE = "A Kernel-Based Spatio-Temporal Dynamical Model for Nowcasting Weather Radar Reflectivities",
@@ -284,23 +323,3 @@ @ARTICLE{ZR2009
284323
YEAR = 2009,
285324
DOI = "10.1016/j.atmosres.2009.03.004"
286325
}
287-
288-
@ARTICLE{TRT2004,
289-
AUTHOR = "A. M. Hering and C. Morel and G. Galli and P. Ambrosetti and M. Boscacci",
290-
TITLE = "Nowcasting thunderstorms in the Alpine Region using a radar based adaptive thresholding scheme",
291-
JOURNAL = "Proceedings of ERAD Conference 2004",
292-
NUMBER = "January",
293-
PAGES = "206--211",
294-
YEAR = 2004
295-
}
296-
297-
@ARTICLE{Feldmann2021,
298-
AUTHOR = "M. Feldmann and U. Germann and M. Gabella and A. Berne",
299-
TITLE = "A Characterisation of Alpine Mesocyclone Occurrence",
300-
JOURNAL = "Weather and Climate Dynamics Discussions",
301-
PAGES = "1--26",
302-
URL = "https://wcd.copernicus.org/preprints/wcd-2021-53/",
303-
DOI = "10.5194/wcd-2021-53",
304-
YEAR = 2021
305-
}
306-

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,4 +24,4 @@ exclude = '''
2424
| build
2525
| dist
2626
)/
27-
'''
27+
'''

pysteps/tests/helpers.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -195,9 +195,9 @@ def get_precipitation_fields(
195195
reference_field, ref_metadata, threshold=0.1, zerovalue=-15.0
196196
)
197197

198-
# Set missing values with the fill value
199-
np.ma.set_fill_value(reference_field, -15.0)
200-
reference_field.data[reference_field.mask] = -15.0
198+
# Set missing values with the fill value
199+
np.ma.set_fill_value(reference_field, ref_metadata["zerovalue"])
200+
reference_field.data[reference_field.mask] = ref_metadata["zerovalue"]
201201

202202
if metadata:
203203
return reference_field, ref_metadata
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
# -*- coding: utf-8 -*-
2+
3+
import numpy as np
4+
import pytest
5+
6+
from pysteps.tests.helpers import get_precipitation_fields
7+
from pysteps.verification.salscores import sal
8+
from pysteps.utils import to_rainrate, to_reflectivity
9+
10+
test_data = [
11+
(to_rainrate, 1 / 15),
12+
(to_reflectivity, None),
13+
]
14+
15+
16+
@pytest.mark.parametrize("converter, thr_factor", test_data)
17+
class TestSAL:
18+
pytest.importorskip("pandas")
19+
pytest.importorskip("skimage")
20+
21+
def test_sal_zeros(self, converter, thr_factor):
22+
"""Test the SAL verification method."""
23+
precip, metadata = get_precipitation_fields(
24+
num_prev_files=0, log_transform=False, metadata=True
25+
)
26+
precip, metadata = converter(precip.filled(np.nan), metadata)
27+
result = sal(precip * 0, precip * 0, thr_factor)
28+
assert np.isnan(result).all()
29+
result = sal(precip * 0, precip, thr_factor)
30+
assert result[:2] == (-2, -2)
31+
assert np.isnan(result[2])
32+
result = sal(precip, precip * 0, thr_factor)
33+
assert result[:2] == (2, 2)
34+
assert np.isnan(result[2])
35+
36+
def test_sal_same_image(self, converter, thr_factor):
37+
"""Test the SAL verification method."""
38+
precip, metadata = get_precipitation_fields(
39+
num_prev_files=0, log_transform=False, metadata=True
40+
)
41+
precip, metadata = converter(precip.filled(np.nan), metadata)
42+
result = sal(precip, precip, thr_factor)
43+
assert isinstance(result, tuple)
44+
assert len(result) == 3
45+
assert np.allclose(result, [0, 0, 0])
46+
47+
def test_sal_translation(self, converter, thr_factor):
48+
precip, metadata = get_precipitation_fields(
49+
num_prev_files=0, log_transform=False, metadata=True
50+
)
51+
precip, metadata = converter(precip.filled(np.nan), metadata)
52+
precip_translated = np.roll(precip, 10, axis=0)
53+
result = sal(precip, precip_translated, thr_factor)
54+
assert np.allclose(result[0], 0)
55+
assert np.allclose(result[1], 0)
56+
assert not np.allclose(result[2], 0)

0 commit comments

Comments
 (0)