Skip to content

Applying the check_norain function to all timesteps #468

@bwalraven

Description

@bwalraven

In PR #460 the possibility to add a check_norain to a steps nowcast was added, which is very nice!

Currently, however, the check_norain function checks if the entire precip array is above the no_rain threshold or not. This means that when it is given a 3d precip array (time, lat, lon), it can pass the no_rain check, and still fail later on if one of the timesteps contains a zero precip field but the total of all time steps is above the threshold.

Example: the following dummy precip array passes the no_rain check:

import numpy as np
import pysteps
from pysteps import utils

# Create a dummy 3d precip array of which one slice has zero precip
np.random.seed(24)

slice1 = np.random.rand(100, 100)
slice2 = np.random.rand(100, 100)
slice3 = np.random.rand(100, 100)
slice4 = np.zeros((100, 100))

precip_arr = np.stack([slice1, slice2, slice3, slice4], axis=0)

precip_thr = 0.1
norain_thr = 0.03
win_fun = 'tukey'

norain = utils.check_norain.check_norain(precip_arr[-3:, :, :], precip_thr, norain_thr, win_fun)

print(norain)

However, if we use this array of shape (4, 100, 100) to compute a motion field and then want to perform the nowcast using the following code:

# First compute the motion field
motion_method = 'LK'
oflow_method = pysteps.motion.get_method(motion_method)
velocity = oflow_method(precip_arr)

nwc_method = pysteps.nowcasts.get_method("steps")
precip_forecast = nwc_method(precip_arr[-3:, :, :],
                             velocity=velocity, 
                             timesteps=12,
                             norain_thr=0.03,
                             precip_thr=0.1, 
                             kmperpixel=1.0,
                             timestep=5)

the following error is returned pointing to the temporal autocorrelation function

---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
Cell In[3], line 7
      4 velocity = oflow_method(precip_arr)
      6 nwc_method = pysteps.nowcasts.get_method("steps")
----> 7 precip_forecast = nwc_method(precip_arr[-3:, :, :],
      8                              velocity=velocity, 
      9                              timesteps=12,
     10                              norain_thr=0.03,
     11                              precip_thr=0.1, 
     12                              kmperpixel=1.0,
     13                              timestep=5)

File ~/.conda/envs/pysteps/lib/python3.10/site-packages/pysteps/nowcasts/steps.py:1533, in forecast(precip, velocity, timesteps, n_ens_members, n_cascade_levels, precip_thr, norain_thr, kmperpixel, timestep, extrap_method, decomp_method, bandpass_filter_method, noise_method, noise_stddev_adj, ar_order, vel_pert_method, conditional, probmatching_method, mask_method, seed, num_workers, fft_method, domain, extrap_kwargs, filter_kwargs, noise_kwargs, vel_pert_kwargs, mask_kwargs, measure_time, callback, return_output)
   1529 # Create an instance of the new class with all the provided arguments
   1530 nowcaster = StepsNowcaster(
   1531     precip, velocity, timesteps, steps_config=nowcaster_config
   1532 )
-> 1533 forecast_steps_nowcast = nowcaster.compute_forecast()
   1534 nowcaster.reset_states_and_params()
   1535 # Call the appropriate methods within the class

File ~/.conda/envs/pysteps/lib/python3.10/site-packages/pysteps/nowcasts/steps.py:377, in StepsNowcaster.compute_forecast(self)
    366     return zero_precipitation_forecast(
    367         self.__config.n_ens_members,
    368         self.__time_steps,
   (...)
    373         self.__start_time_init,
    374     )
    376 self.__perform_extrapolation()
--> 377 self.__apply_noise_and_ar_model()
    378 self.__initialize_velocity_perturbations()
    379 self.__initialize_precipitation_mask()

File ~/.conda/envs/pysteps/lib/python3.10/site-packages/pysteps/nowcasts/steps.py:832, in StepsNowcaster.__apply_noise_and_ar_model(self)
    827 self.__params.autocorrelation_coefficients = np.empty(
    828     (self.__config.n_cascade_levels, self.__config.ar_order)
    829 )
    830 for i in range(self.__config.n_cascade_levels):
    831     self.__params.autocorrelation_coefficients[i, :] = (
--> 832         correlation.temporal_autocorrelation(
    833             self.__state.precip_cascades[i], mask=self.__state.mask_threshold
    834         )
    835     )
    837 nowcast_utils.print_corrcoefs(self.__params.autocorrelation_coefficients)
    839 # Adjust the lag-2 correlation coefficient if AR(2) model is used

File ~/.conda/envs/pysteps/lib/python3.10/site-packages/pysteps/timeseries/correlation.py:107, in temporal_autocorrelation(x, d, domain, x_shape, mask, use_full_fft, window, window_radius)
    102     raise ValueError(
    103         "dimension mismatch between x and mask: x.shape[1:]=%s, mask.shape=%s"
    104         % (str(x.shape[1:]), str(mask.shape))
    105     )
    106 if np.any(~np.isfinite(x)):
--> 107     raise ValueError("x contains non-finite values")
    109 if d == 1:
    110     x = np.diff(x, axis=0)

ValueError: x contains non-finite values

To fix this we could modify the check_norain function slightly like:

    if win_fun is not None:
        tapering = utils.tapering.compute_window_function(
            precip_arr.shape[-2], precip_arr.shape[-1], win_fun
        )
    else:
        tapering = np.ones((precip_arr.shape[-2], precip_arr.shape[-1]))

    tapering_mask = tapering == 0.0
    masked_precip = precip_arr.copy()
    masked_precip[..., tapering_mask] = np.nanmin(precip_arr)

    if precip_thr is None:
        precip_thr = np.nanmin(masked_precip)
    
    if precip_arr.ndim == 3:
        n_fields = precip_arr.shape[0]
        for i in range(n_fields):
            precip_field = masked_precip[i]
            rain_pixels = precip_field[precip_field > precip_thr]
            norain = rain_pixels.size / precip_field.size <= norain_thr
            print(
                  f"Rain fraction in precip_field {i} is: {str(rain_pixels.size / precip_field.size)}, while minimum fraction is {str(norain_thr)}"
              )
            if norain == True:
            # all fields should be False, if any is True return True
                    return True
    elif precip_arr.ndim == 2:
        rain_pixels = masked_precip[masked_precip > precip_thr]
        norain = rain_pixels.size / masked_precip.size <= norain_thr
        print(
            f"Rain fraction is: {str(rain_pixels.size / masked_precip.size)}, while minimum fraction is {str(norain_thr)}"
        )
        return norain

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

Status

Todo

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions