Skip to content

Commit 2382f9d

Browse files
committed
Set out-of-domain values to nan in S-PROG and STEPS nowcasts
1 parent 1021f08 commit 2382f9d

File tree

2 files changed

+34
-16
lines changed

2 files changed

+34
-16
lines changed

pysteps/nowcasts/sprog.py

Lines changed: 17 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -56,8 +56,7 @@ def forecast(
5656
R : array-like
5757
Array of shape (ar_order+1,m,n) containing the input precipitation fields
5858
ordered by timestamp from oldest to newest. The time steps between
59-
the inputs are assumed to be regular, and the inputs are required to have
60-
finite values.
59+
the inputs are assumed to be regular.
6160
V : array-like
6261
Array of shape (2,m,n) containing the x- and y-components of the
6362
advection field.
@@ -141,9 +140,6 @@ def forecast(
141140
if filter_kwargs is None:
142141
filter_kwargs = dict()
143142

144-
if np.any(~np.isfinite(R)):
145-
raise ValueError("R contains non-finite values")
146-
147143
if np.any(~np.isfinite(V)):
148144
raise ValueError("V contains non-finite values")
149145

@@ -191,8 +187,14 @@ def forecast(
191187
extrapolator_method = extrapolation.get_method(extrap_method)
192188

193189
R = R[-(ar_order + 1) :, :, :].copy()
194-
R_min = R.min()
190+
R_min = np.nanmin(R)
191+
192+
# determine the domain mask from non-finite values
193+
domain_mask = np.logical_or.reduce(
194+
[~np.isfinite(R[i, :]) for i in range(R.shape[0])]
195+
)
195196

197+
# determine the precipitation threshold mask
196198
if conditional:
197199
MASK_thr = np.logical_and.reduce(
198200
[R[i, :, :] >= R_thr for i in range(R.shape[0])]
@@ -207,15 +209,14 @@ def forecast(
207209

208210
extrap_kwargs = extrap_kwargs.copy()
209211
extrap_kwargs["xy_coords"] = xy_coords
212+
extrap_kwargs["allow_nonfinite_values"] = True
210213

211214
# advect the previous precipitation fields to the same position with the
212215
# most recent one (i.e. transform them into the Lagrangian coordinates)
213216
res = list()
214217

215218
def f(R, i):
216-
return extrapolator_method(R[i, :, :], V, ar_order - i, "min", **extrap_kwargs)[
217-
-1
218-
]
219+
return extrapolator_method(R[i, :], V, ar_order - i, "min", **extrap_kwargs)[-1]
219220

220221
for i in range(ar_order):
221222
if not DASK_IMPORTED:
@@ -227,6 +228,11 @@ def f(R, i):
227228
num_workers_ = len(res) if num_workers > len(res) else num_workers
228229
R = np.stack(list(dask.compute(*res, num_workers=num_workers_)) + [R[-1, :, :]])
229230

231+
# replace non-finite values with the minimum value
232+
R = R.copy()
233+
for i in range(R.shape[0]):
234+
R[i, ~np.isfinite(R[i, :])] = np.nanmin(R[i, :])
235+
230236
# compute the cascade decompositions of the input precipitation fields
231237
R_d = []
232238
for i in range(ar_order + 1):
@@ -334,6 +340,8 @@ def f(R, i):
334340
mu_fct = np.mean(R_c_[MASK])
335341
R_c_[MASK] = R_c_[MASK] - mu_fct + mu_0
336342

343+
R_c_[domain_mask] = np.nan
344+
337345
# advect the recomposed precipitation field to obtain the forecast for
338346
# time step t
339347
extrap_kwargs.update({"D_prev": D, "return_displacement": True})

pysteps/nowcasts/steps.py

Lines changed: 17 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,12 @@
22
pysteps.nowcasts.steps
33
======================
44
5-
Implementation of the STEPS stochastic nowcasting method as described in
5+
Implementation of the STEPS stochastic nowcasting method as described in
66
:cite:`Seed2003`, :cite:`BPS2006` and :cite:`SPN2013`.
77
88
.. autosummary::
99
:toctree: ../generated/
10-
10+
1111
forecast
1212
"""
1313

@@ -73,8 +73,7 @@ def forecast(
7373
R : array-like
7474
Array of shape (ar_order+1,m,n) containing the input precipitation fields
7575
ordered by timestamp from oldest to newest. The time steps between the
76-
inputs are assumed to be regular, and the inputs are required to have
77-
finite values.
76+
inputs are assumed to be regular.
7877
V : array-like
7978
Array of shape (2,m,n) containing the x- and y-components of the advection
8079
field. The velocities are assumed to represent one time step between the
@@ -273,9 +272,6 @@ def forecast(
273272
if mask_kwargs is None:
274273
mask_kwargs = dict()
275274

276-
if np.any(~np.isfinite(R)):
277-
raise ValueError("R contains non-finite values")
278-
279275
if np.any(~np.isfinite(V)):
280276
raise ValueError("V contains non-finite values")
281277

@@ -384,6 +380,12 @@ def forecast(
384380

385381
R = R[-(ar_order + 1) :, :, :].copy()
386382

383+
# determine the domain mask from non-finite values
384+
domain_mask = np.logical_or.reduce(
385+
[~np.isfinite(R[i, :]) for i in range(R.shape[0])]
386+
)
387+
388+
# determine the precipitation threshold mask
387389
if conditional:
388390
MASK_thr = np.logical_and.reduce(
389391
[R[i, :, :] >= R_thr for i in range(R.shape[0])]
@@ -395,6 +397,7 @@ def forecast(
395397
# most recent one (i.e. transform them into the Lagrangian coordinates)
396398
extrap_kwargs = extrap_kwargs.copy()
397399
extrap_kwargs["xy_coords"] = xy_coords
400+
extrap_kwargs["allow_nonfinite_values"] = True
398401
res = list()
399402

400403
def f(R, i):
@@ -412,6 +415,11 @@ def f(R, i):
412415
num_workers_ = len(res) if num_workers > len(res) else num_workers
413416
R = np.stack(list(dask.compute(*res, num_workers=num_workers_)) + [R[-1, :, :]])
414417

418+
# replace non-finite values with the minimum value
419+
R = R.copy()
420+
for i in range(R.shape[0]):
421+
R[i, ~np.isfinite(R[i, :])] = np.nanmin(R[i, :])
422+
415423
if noise_method is not None:
416424
# get methods for perturbations
417425
init_noise, generate_noise = noise.get_method(noise_method)
@@ -692,6 +700,8 @@ def worker(j):
692700
else:
693701
V_ = V
694702

703+
R_c_[domain_mask] = np.nan
704+
695705
# advect the recomposed precipitation field to obtain the forecast
696706
# for time step t
697707
extrap_kwargs.update({"D_prev": D[j], "return_displacement": True})

0 commit comments

Comments
 (0)