Skip to content

Applying the check_norain function to all timesteps #468

Open
@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

No status

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions