@@ -19,10 +19,10 @@ def forecast(R, V, n_timesteps, n_ens_members=24, n_cascade_levels=6, R_thr=None
1919 kmperpixel = None , timestep = None , extrap_method = "semilagrangian" ,
2020 decomp_method = "fft" , bandpass_filter_method = "gaussian" ,
2121 noise_method = "nonparametric" , noise_stddev_adj = False , ar_order = 2 ,
22- vel_pert_method = None , conditional = False , use_precip_mask = True ,
23- use_probmatching = True , mask_method = "incremental" , callback = None ,
24- return_output = True , seed = None , num_workers = None , fft_method = "numpy" ,
25- extrap_kwargs = {}, filter_kwargs = {}, noise_kwargs = {}, vel_pert_kwargs = {}):
22+ vel_pert_method = "bps" , conditional = False , probmatching_method = "cdf" ,
23+ mask_method = "incremental" , callback = None , return_output = True ,
24+ seed = None , num_workers = None , fft_method = "numpy" , extrap_kwargs = {} ,
25+ filter_kwargs = {}, noise_kwargs = {}, vel_pert_kwargs = {}):
2626 """Generate a nowcast ensemble by using the Short-Term Ensemble Prediction
2727 System (STEPS) method.
2828
@@ -47,7 +47,7 @@ def forecast(R, V, n_timesteps, n_ens_members=24, n_cascade_levels=6, R_thr=None
4747 ----------------
4848 R_thr : float
4949 Specifies the threshold value for minimum observable precipitation
50- intensity. Must be set if use_probmatching is True or conditional is True.
50+ intensity. Required if mask_method is not None or conditional is True.
5151 kmperpixel : float
5252 Spatial resolution of the input data (kilometers/pixel). Required if
5353 vel_pert_method is not None or mask_method is 'incremental'.
@@ -78,21 +78,20 @@ def forecast(R, V, n_timesteps, n_ens_members=24, n_cascade_levels=6, R_thr=None
7878 field is not perturbed.
7979 conditional : bool
8080 If set to True, compute the statistics of the precipitation field
81- conditionally by excluding the areas where the values are below the
82- threshold R_thr.
83- use_precip_mask : bool
84- If True, set pixels outside precipitation areas to the minimum value of
85- the observed field.
86- mask_method : {'obs', 'sprog', 'incremental'}
87- The precipitation/no precipitation method to use with mask: 'obs' = apply R_thr
88- to the most recently observed precipitation intensity field, 'sprog' = use the
89- smoothed forecast field from S-PROG, where the AR(p) model has been applied,
90- 'incremental' = iteratively buffer the mask with a certain rate (currently
91- it is 1 km/min)
92- use_probmatching : bool
93- If True, apply probability matching to the forecast field in order to
94- preserve the distribution of the most recently observed precipitation
95- field.
81+ conditionally by excluding pixels where the values are below the threshold
82+ R_thr.
83+ mask_method : {'obs','sprog','incremental',None}
84+ The method to use for masking no precipitation areas in the forecast field.
85+ The masked pixels are set to the minimum value of the observations.
86+ 'obs' = apply R_thr to the most recently observed precipitation intensity
87+ field, 'sprog' = use the smoothed forecast field from S-PROG, where the
88+ AR(p) model has been applied, 'incremental' = iteratively buffer the mask
89+ with a certain rate (currently it is 1 km/min), None=no masking.
90+ probmatching_method : {'cdf','mean',None}
91+ Method for matching the statistics of the forecast field with those of
92+ the most recently observed one. Requires that mask_method is not None.
93+ 'cdf'=map the forecast CDF to the observed one, 'mean'=adjust only the
94+ mean value of the forecast field, None=no matching applied.
9695 callback : function
9796 Optional function that is called after computation of each time step of
9897 the nowcast. The function takes one argument: a three-dimensional array
@@ -110,19 +109,19 @@ def forecast(R, V, n_timesteps, n_ens_members=24, n_cascade_levels=6, R_thr=None
110109 all available CPUs. Applicable if dask is enabled.
111110 fft_method : str or tuple
112111 A string or a (function,kwargs) tuple defining the FFT method to use
113- (see utils.fft.get_method). Defaults to " numpy" .
112+ (see utils.fft.get_method). Defaults to ' numpy' .
114113 extrap_kwargs : dict
115- Optional dictionary that is supplied as keyword arguments to the
116- extrapolation method.
114+ Optional dictionary containing keyword arguments for the extrapolation
115+ method. See the documentation of pysteps.extrapolation .
117116 filter_kwargs : dict
118- Optional dictionary that is supplied as keyword arguments to the
119- filter method .
117+ Optional dictionary containing keyword arguments for the filter method.
118+ See the documentation of pysteps.cascade.bandpass_filters.py .
120119 noise_kwargs : dict
121- Optional dictionary that is supplied as keyword arguments to the
122- initializer of the noise generator.
120+ Optional dictionary containing keyword arguments for the initializer of
121+ the noise generator. See the documentation of pysteps.noise.fftgenerators .
123122 vel_pert_kwargs : dict
124- Optional dictionary that is supplied as keyword arguments to the
125- initializer of the velocity perturbator.
123+ Optional dictionary containing keyword arguments for the initializer of
124+ the velocity perturbator. See the documentation of pysteps.noise.motion .
126125
127126 Returns
128127 -------
@@ -139,9 +138,9 @@ def forecast(R, V, n_timesteps, n_ens_members=24, n_cascade_levels=6, R_thr=None
139138
140139 Notes
141140 -----
142- If noise_method and vel_pert_method are set to None and n_ens_members is set
143- to 1, the produced nowcast is deterministic (i.e. the S-PROG nowcast, see
144- :cite:`Seed2003`) .
141+ If noise_method and vel_pert_method are None, n_ens_members is 1,
142+ mask_method is 'sprog' and probmatching_method is 'mean', the deterministic
143+ S-PROG nowcast is generated, see :cite:`Seed2003`.
145144
146145 References
147146 ----------
@@ -156,14 +155,17 @@ def forecast(R, V, n_timesteps, n_ens_members=24, n_cascade_levels=6, R_thr=None
156155 if np .any (~ np .isfinite (V )):
157156 raise ValueError ("V contains non-finite values" )
158157
159- if mask_method not in ["obs" , "sprog" , "incremental" ]:
160- raise ValueError ("unknown mask method %s: must be 'obs', 'sprog' or 'incremental'" % mask_method )
158+ if mask_method not in ["obs" , "sprog" , "incremental" , None ]:
159+ raise ValueError ("unknown mask method %s: must be 'obs', 'sprog' or 'incremental' or None " % mask_method )
161160
162161 if conditional and R_thr is None :
163162 raise ValueError ("conditional=True but R_thr is not set" )
164163
165- if use_probmatching and R_thr is None :
166- raise ValueError ("use_probmatching=True but R_thr is not set" )
164+ if probmatching_method is not None and R_thr is None :
165+ raise ValueError ("probmatching_method!=None but R_thr is not set" )
166+
167+ if probmatching_method is not None and mask_method is None :
168+ raise ValueError ("probmatching_method!=None but mask_method=None" )
167169
168170 if kmperpixel is None :
169171 if vel_pert_method is not None :
@@ -199,9 +201,8 @@ def forecast(R, V, n_timesteps, n_ens_members=24, n_cascade_levels=6, R_thr=None
199201 print ("noise adjustment: %s" % ("yes" if noise_stddev_adj else "no" ))
200202 print ("velocity perturbator: %s" % vel_pert_method )
201203 print ("conditional statistics: %s" % ("yes" if conditional else "no" ))
202- print ("precipitation mask: %s" % ("yes" if use_precip_mask else "no" ))
203- print ("mask method: %s" % mask_method )
204- print ("probability matching: %s" % ("yes" if use_probmatching else "no" ))
204+ print ("precip. mask method: %s" % mask_method )
205+ print ("probability matching: %s" % probmatching_method )
205206 print ("FFT method: %s" % fft_method )
206207 print ("" )
207208
@@ -219,14 +220,14 @@ def forecast(R, V, n_timesteps, n_ens_members=24, n_cascade_levels=6, R_thr=None
219220 print ("velocity perturbations, perpendicular: %g,%g,%g" % \
220221 (vp_perp [0 ], vp_perp [1 ], vp_perp [2 ]))
221222
222- if conditional or use_probmatching :
223- print ("conditional precip. intensity threshold: %g" % R_thr )
223+ if conditional or mask_method is not None :
224+ print ("precip. intensity threshold: %g" % R_thr )
224225
225226 M ,N = R .shape [1 :]
226227 extrap_method = extrapolation .get_method (extrap_method )
227228 R = R [- (ar_order + 1 ):, :, :].copy ()
228229
229- if conditional or use_probmatching :
230+ if conditional :
230231 MASK_thr = np .logical_and .reduce ([R [i , :, :] >= R_thr for i in range (R .shape [0 ])])
231232 else :
232233 MASK_thr = None
@@ -342,29 +343,32 @@ def forecast(R, V, n_timesteps, n_ens_members=24, n_cascade_levels=6, R_thr=None
342343 D = [None for j in range (n_ens_members )]
343344 R_f = [[] for j in range (n_ens_members )]
344345
345- if use_precip_mask :
346+ if mask_method is not None :
347+ MASK_prec = R [- 1 , :, :] >= R_thr
348+
346349 if mask_method == "obs" :
347- MASK_prec = R [ - 1 , :, :] >= R_thr
350+ pass
348351 # add a slight buffer to the mask
349352 # n=5
350353 # kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (n,n))
351354 # MASK_prec = MASK_prec.astype('uint8')
352355 # MASK_prec = cv2.dilate(MASK_prec,kernel).astype(bool)
353356 elif mask_method == "sprog" :
354357 # compute the wet area ratio and the precipitation mask
355- MASK_prec = R [- 1 , :, :] >= R_thr
356358 war = 1.0 * np .sum (MASK_prec ) / (R .shape [1 ]* R .shape [2 ])
357359 R_m = R_c [0 , :, :, :].copy ()
358360 elif mask_method == "incremental" :
359361 # initialize precip mask for each member
360- MASK_prec_ = R [- 1 , :, :] >= R_thr
361- MASK_prec = [MASK_prec_ .copy () for j in range (n_ens_members )]
362+ MASK_prec = [MASK_prec .copy () for j in range (n_ens_members )]
362363 # initialize the structuring element
363364 struct = scipy .ndimage .generate_binary_structure (2 , 1 )
364365 # iterate it to expand it nxn
365366 n = timestep / kmperpixel
366367 struct = scipy .ndimage .iterate_structure (struct , int ((n - 1 )/ 2. ))
367368
369+ if probmatching_method == "mean" :
370+ mu_0 = np .mean (R [- 1 , :, :][MASK_prec ])
371+
368372 R = R [- 1 , :, :]
369373
370374 print ("Starting nowcast computation." )
@@ -375,7 +379,7 @@ def forecast(R, V, n_timesteps, n_ens_members=24, n_cascade_levels=6, R_thr=None
375379 sys .stdout .flush ()
376380 starttime = time .time ()
377381
378- if use_precip_mask and mask_method == "sprog" :
382+ if mask_method == "sprog" :
379383 for i in range (n_cascade_levels ):
380384 # use a separate AR(p) model for the non-perturbed forecast,
381385 # from which the mask is obtained
@@ -434,24 +438,29 @@ def worker(j):
434438 # obtained from the AR(p) model(s)
435439 R_c_ = _recompose_cascade (R_c [j , :, :, :], mu , sigma )
436440
437- if use_precip_mask :
441+ if mask_method is not None :
438442 # apply the precipitation mask to prevent generation of new
439443 # precipitation into areas where it was not originally
440444 # observed
441445 if mask_method == "obs" :
442- R_c_ [ ~ MASK_prec ] = R_c_ . min ()
446+ MASK_prec_ = ~ MASK_prec
443447 elif mask_method == "incremental" :
444- R_c_ [ ~ MASK_prec [j ]] = R_c_ . min ()
448+ MASK_prec_ = ~ MASK_prec [j ]
445449 elif mask_method == "sprog" :
446- R_c_ [ MASK_prec ] = R_c_ . min ()
450+ MASK_prec_ = MASK_prec
447451
448- if use_probmatching :
449- ## adjust the conditional CDF of the forecast (precipitation
450- ## intensity above the threshold R_thr) to match the most
451- ## recently observed precipitation field
452+ R_c_ [MASK_prec_ ] = R_c_ .min ()
453+
454+ if probmatching_method == "cdf" :
455+ # adjust the conditional CDF of the forecast (precipitation
456+ # intensity above the threshold R_thr) to match the most
457+ # recently observed precipitation field
452458 R_c_ = probmatching .nonparam_match_empirical_cdf (R_c_ , R )
459+ elif probmatching_method == "mean" :
460+ mu_fct = np .mean (R_c_ [~ MASK_prec_ ])
461+ R_c_ [~ MASK_prec_ ] = R_c_ [~ MASK_prec_ ] - mu_fct + mu_0
453462
454- if use_precip_mask and mask_method == "incremental" :
463+ if mask_method == "incremental" :
455464 MASK_prec_ = R_c_ >= R_thr
456465 MASK_prec_ = scipy .ndimage .morphology .binary_dilation (MASK_prec_ , struct )
457466 MASK_prec [j ] = MASK_prec_
@@ -493,10 +502,7 @@ def worker(j):
493502 R_f [j ].append (R_f_ [j ])
494503
495504 if return_output :
496- if n_ens_members == 1 :
497- return np .stack (R_f [0 ])
498- else :
499- return np .stack ([np .stack (R_f [j ]) for j in range (n_ens_members )])
505+ return np .stack ([np .stack (R_f [j ]) for j in range (n_ens_members )])
500506 else :
501507 return None
502508
0 commit comments