Skip to content

Commit 50839ff

Browse files
pulkkinsspulkkin
andauthored
Importers for new OPERA products (#441)
* Fix incorrect function name * Skip reading attributes from what group if not found at main level * Add tests for OPERA NIMBUS and CIRRUS products * Add tests for NIMBUS rain accumulation composites * Make quantity attribute in what group optional * Revise function docstrings --------- Co-authored-by: Seppo Pulkkinen <seppo.pulkkinen@fmi.fi>
1 parent 8b5333c commit 50839ff

File tree

2 files changed

+148
-13
lines changed

2 files changed

+148
-13
lines changed

pysteps/io/importers.py

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1304,10 +1304,13 @@ def import_odim_hdf5(filename, qty="RATE", **kwargs):
13041304
# check if the "what" group is in the "dataset" group
13051305
if "what" in list(dsg[1].keys()):
13061306
if "quantity" in dsg[1]["what"].attrs.keys():
1307-
qty_, gain, offset, nodata, undetect = _read_opera_hdf5_what_group(
1308-
dsg[1]["what"]
1309-
)
1310-
what_grp_found = True
1307+
try:
1308+
qty_, gain, offset, nodata, undetect = (
1309+
_read_opera_hdf5_what_group(dsg[1]["what"])
1310+
)
1311+
what_grp_found = True
1312+
except KeyError:
1313+
pass
13111314

13121315
for dg in dsg[1].items():
13131316
if dg[0][0:4] == "data":
@@ -1480,7 +1483,7 @@ def import_opera_hdf5(filename, qty="RATE", **kwargs):
14801483

14811484

14821485
def _read_opera_hdf5_what_group(whatgrp):
1483-
qty = whatgrp.attrs["quantity"]
1486+
qty = whatgrp.attrs["quantity"] if "quantity" in whatgrp.attrs.keys() else b"QIND"
14841487
gain = whatgrp.attrs["gain"] if "gain" in whatgrp.attrs.keys() else 1.0
14851488
offset = whatgrp.attrs["offset"] if "offset" in whatgrp.attrs.keys() else 0.0
14861489
nodata = whatgrp.attrs["nodata"] if "nodata" in whatgrp.attrs.keys() else np.nan

pysteps/tests/test_io_opera_hdf5.py

Lines changed: 140 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -9,19 +9,57 @@
99

1010
pytest.importorskip("h5py")
1111

12+
# tests for three OPERA products:
13+
# Odyssey rain rate composite (production discontinued on October 30th 2024)
14+
# CIRRUS max. reflectivity composites
15+
# NIMBUS rain rate composites
1216

1317
root_path = pysteps.rcparams.data_sources["opera"]["root_path"]
18+
1419
filename = os.path.join(root_path, "20180824", "T_PAAH21_C_EUOC_20180824180000.hdf")
15-
precip, _, metadata = pysteps.io.import_opera_hdf5(filename)
20+
precip_odyssey, _, metadata_odyssey = pysteps.io.import_opera_hdf5(filename, qty="RATE")
21+
22+
filename = os.path.join(
23+
root_path, "20241126", "CIRRUS", "T_PABV21_C_EUOC_20241126010000.hdf"
24+
)
25+
precip_cirrus, _, metadata_cirrus = pysteps.io.import_opera_hdf5(filename, qty="DBZH")
26+
27+
filename = os.path.join(
28+
root_path, "20241126", "NIMBUS", "T_PAAH22_C_EUOC_20241126010000.hdf"
29+
)
30+
precip_nimbus_rain_rate, _, metadata_nimbus_rain_rate = pysteps.io.import_opera_hdf5(
31+
filename, qty="RATE"
32+
)
33+
34+
filename = os.path.join(
35+
root_path, "20241126", "NIMBUS", "T_PASH22_C_EUOC_20241126010000.hdf"
36+
)
37+
precip_nimbus_rain_accum, _, metadata_nimbus_rain_accum = pysteps.io.import_opera_hdf5(
38+
filename, qty="ACRR"
39+
)
1640

1741

18-
def test_io_import_opera_hdf5_shape():
42+
def test_io_import_opera_hdf5_odyssey_shape():
1943
"""Test the importer OPERA HDF5."""
20-
assert precip.shape == (2200, 1900)
44+
assert precip_odyssey.shape == (2200, 1900)
2145

2246

23-
# test_metadata: list of (variable,expected, tolerance) tuples
47+
def test_io_import_opera_hdf5_cirrus_shape():
48+
"""Test the importer OPERA HDF5."""
49+
assert precip_cirrus.shape == (4400, 3800)
50+
2451

52+
def test_io_import_opera_hdf5_nimbus_rain_rate_shape():
53+
"""Test the importer OPERA HDF5."""
54+
assert precip_nimbus_rain_rate.shape == (2200, 1900)
55+
56+
57+
def test_io_import_opera_hdf5_nimbus_rain_accum_shape():
58+
"""Test the importer OPERA HDF5."""
59+
assert precip_nimbus_rain_accum.shape == (2200, 1900)
60+
61+
62+
# test_metadata: list of (variable,expected, tolerance) tuples
2563
expected_proj = (
2664
"+proj=laea +lat_0=55.0 +lon_0=10.0 "
2765
"+x_0=1950000.0 "
@@ -30,7 +68,7 @@ def test_io_import_opera_hdf5_shape():
3068
)
3169

3270
# list of (variable,expected,tolerance) tuples
33-
test_attrs = [
71+
test_odyssey_attrs = [
3472
("projection", expected_proj, None),
3573
("ll_lon", -10.434576838640398, 1e-10),
3674
("ll_lat", 31.746215319325056, 1e-10),
@@ -53,7 +91,101 @@ def test_io_import_opera_hdf5_shape():
5391
]
5492

5593

56-
@pytest.mark.parametrize("variable, expected, tolerance", test_attrs)
57-
def test_io_import_mch_gif_dataset_attrs(variable, expected, tolerance):
94+
@pytest.mark.parametrize("variable, expected, tolerance", test_odyssey_attrs)
95+
def test_io_import_opera_hdf5_odyssey_dataset_attrs(variable, expected, tolerance):
5896
"""Test the importer OPERA HDF5."""
59-
smart_assert(metadata[variable], expected, tolerance)
97+
smart_assert(metadata_odyssey[variable], expected, tolerance)
98+
99+
100+
# list of (variable,expected,tolerance) tuples
101+
test_cirrus_attrs = [
102+
("projection", expected_proj, None),
103+
("ll_lon", -10.4345768386404, 1e-10),
104+
("ll_lat", 31.7462153182675, 1e-10),
105+
("ur_lon", 57.8119647501499, 1e-10),
106+
("ur_lat", 67.6210371071631, 1e-10),
107+
("x1", -0.00027143326587975025, 1e-6),
108+
("y1", -4400000.00116988, 1e-10),
109+
("x2", 3800000.0000817003, 1e-10),
110+
("y2", -8.761277422308922e-05, 1e-6),
111+
("xpixelsize", 1000.0, 1e-10),
112+
("ypixelsize", 1000.0, 1e-10),
113+
("cartesian_unit", "m", None),
114+
("accutime", 15.0, 1e-10),
115+
("yorigin", "upper", None),
116+
("unit", "dBZ", None),
117+
("institution", "Odyssey datacentre", None),
118+
("transform", "dB", None),
119+
("zerovalue", -32.0, 1e-10),
120+
("threshold", -31.5, 1e-10),
121+
]
122+
123+
124+
@pytest.mark.parametrize("variable, expected, tolerance", test_cirrus_attrs)
125+
def test_io_import_opera_hdf5_cirrus_dataset_attrs(variable, expected, tolerance):
126+
"""Test OPERA HDF5 importer: max. reflectivity composites from CIRRUS."""
127+
smart_assert(metadata_cirrus[variable], expected, tolerance)
128+
129+
130+
# list of (variable,expected,tolerance) tuples
131+
test_nimbus_rain_rate_attrs = [
132+
("projection", expected_proj, None),
133+
("ll_lon", -10.434599999137568, 1e-10),
134+
("ll_lat", 31.74619995126678, 1e-10),
135+
("ur_lon", 57.8119032106317, 1e-10),
136+
("ur_lat", 67.62104536996274, 1e-10),
137+
("x1", -2.5302714337594807, 1e-6),
138+
("y1", -4400001.031169886, 1e-10),
139+
("x2", 3799997.4700817037, 1e-10),
140+
("y2", -1.0300876162946224, 1e-6),
141+
("xpixelsize", 2000.0, 1e-10),
142+
("ypixelsize", 2000.0, 1e-10),
143+
("cartesian_unit", "m", None),
144+
("accutime", 15.0, 1e-10),
145+
("yorigin", "upper", None),
146+
("unit", "mm/h", None),
147+
("institution", "Odyssey datacentre", None),
148+
("transform", None, None),
149+
("zerovalue", 0.0, 1e-10),
150+
("threshold", 0.01, 1e-10),
151+
]
152+
153+
154+
@pytest.mark.parametrize("variable, expected, tolerance", test_nimbus_rain_rate_attrs)
155+
def test_io_import_opera_hdf5_nimbus_rain_rate_dataset_attrs(
156+
variable, expected, tolerance
157+
):
158+
"""Test OPERA HDF5 importer: rain rate composites from NIMBUS."""
159+
smart_assert(metadata_nimbus_rain_rate[variable], expected, tolerance)
160+
161+
162+
# list of (variable,expected,tolerance) tuples
163+
test_nimbus_rain_accum_attrs = [
164+
("projection", expected_proj, None),
165+
("ll_lon", -10.434599999137568, 1e-10),
166+
("ll_lat", 31.74619995126678, 1e-10),
167+
("ur_lon", 57.8119032106317, 1e-10),
168+
("ur_lat", 67.62104536996274, 1e-10),
169+
("x1", -2.5302714337594807, 1e-6),
170+
("y1", -4400001.031169886, 1e-10),
171+
("x2", 3799997.4700817037, 1e-10),
172+
("y2", -1.0300876162946224, 1e-6),
173+
("xpixelsize", 2000.0, 1e-10),
174+
("ypixelsize", 2000.0, 1e-10),
175+
("cartesian_unit", "m", None),
176+
("accutime", 15.0, 1e-10),
177+
("yorigin", "upper", None),
178+
("unit", "mm", None),
179+
("institution", "Odyssey datacentre", None),
180+
("transform", None, None),
181+
("zerovalue", 0.0, 1e-10),
182+
("threshold", 0.01, 1e-10),
183+
]
184+
185+
186+
@pytest.mark.parametrize("variable, expected, tolerance", test_nimbus_rain_accum_attrs)
187+
def test_io_import_opera_hdf5_nimbus_rain_accum_dataset_attrs(
188+
variable, expected, tolerance
189+
):
190+
"""Test OPERA HDF5 importer: rain accumulation composites from NIMBUS."""
191+
smart_assert(metadata_nimbus_rain_accum[variable], expected, tolerance)

0 commit comments

Comments
 (0)