-
Notifications
You must be signed in to change notification settings - Fork 188
Support for mixed precip/noprecip input and AR order 1 #544
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from 26 commits
cbd0d06
4d01c01
3948d5d
5170768
f3355c1
4650d8b
bd7f399
b49902e
9135618
0f45439
3c723ce
7d3a2df
2cb883f
26fa133
e605150
c47d49d
3188d38
1e05808
2cc64a4
12d10a5
55261f1
4e0b231
2bcc065
f8602b5
62f03cc
ff2bc18
15caf3a
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,3 @@ | ||
| # skip check for assert usage in the tests | ||
| assert_used: | ||
| skips: ['*test_*.py'] |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -58,7 +58,7 @@ | |
| from pysteps.nowcasts import utils as nowcast_utils | ||
| from pysteps.postprocessing import probmatching | ||
| from pysteps.timeseries import autoregression, correlation | ||
| from pysteps.utils.check_norain import check_norain | ||
| from pysteps.utils.check_norain import check_norain, check_previous_radar_obs | ||
|
|
||
| try: | ||
| import dask | ||
|
|
@@ -1196,12 +1196,22 @@ def transform_to_lagrangian(precip, i): | |
| self.__precip_models = np.stack(temp_precip_models) | ||
|
|
||
| # Check for zero input fields in the radar, nowcast and NWP data. | ||
| # decide based on latest input file only | ||
| self.__params.zero_precip_radar = check_norain( | ||
| self.__precip, | ||
| self.__precip[-1], | ||
| self.__params.precip_threshold, | ||
| self.__config.norain_threshold, | ||
| self.__params.noise_kwargs["win_fun"], | ||
| ) | ||
| # Set all inputs to 0 (also previous times) | ||
| # not strictly necessary but to be consistent with checking only | ||
| # latest observation rain field to set zero_precip_radar | ||
| if self.__params.zero_precip_radar: | ||
| self.__precip = np.where( | ||
| np.isfinite(self.__precip), | ||
| np.ones(self.__precip.shape) * np.nanmin(self.__precip), | ||
| self.__precip, | ||
| ) | ||
|
|
||
| # The norain fraction threshold used for nwp is the default value of 0.0, | ||
| # since nwp does not suffer from clutter. | ||
|
|
@@ -1481,10 +1491,15 @@ def __estimate_ar_parameters_radar(self): | |
|
|
||
| # Finally, transpose GAMMA to ensure that the shape is the same as np.empty((n_cascade_levels, ar_order)) | ||
| GAMMA = GAMMA.transpose() | ||
| assert GAMMA.shape == ( | ||
| if len(GAMMA.shape) == 1: | ||
| GAMMA = GAMMA.reshape((GAMMA.size, 1)) | ||
| if not GAMMA.shape == ( | ||
| self.__config.n_cascade_levels, | ||
| self.__config.ar_order, | ||
| ) | ||
| ): | ||
| raise ValueError( | ||
| f"GAMMA shape {GAMMA.shape} does not match {self.__config.n_cascade_levels} cascade levels and ar_order of {self.__config.ar_order}" | ||
| ) | ||
|
|
||
| # Print the GAMMA value | ||
| nowcast_utils.print_corrcoefs(GAMMA) | ||
|
|
@@ -1629,13 +1644,22 @@ def __prepare_forecast_loop(self): | |
| ) | ||
|
|
||
| # initizalize the current and previous extrapolation forecast scale for the nowcasting component | ||
| # phi1 / (1 - phi2), see BPS2004 | ||
| # ar_order=1: rho = phi1 | ||
| # ar_order=2: rho = phi1 / (1 - phi2) | ||
| # -> see Hamilton1994, Pulkkinen2019, BPS2004 (Yule-Walker equations) | ||
| self.__state.rho_extrap_cascade_prev = np.repeat( | ||
| 1.0, self.__params.PHI.shape[0] | ||
| ) | ||
| self.__state.rho_extrap_cascade = self.__params.PHI[:, 0] / ( | ||
| 1.0 - self.__params.PHI[:, 1] | ||
| ) | ||
| if self.__config.ar_order == 1: | ||
| self.__state.rho_extrap_cascade = self.__params.PHI[:, 0] | ||
| elif self.__config.ar_order == 2: | ||
| self.__state.rho_extrap_cascade = self.__params.PHI[:, 0] / ( | ||
| 1.0 - self.__params.PHI[:, 1] | ||
| ) | ||
| else: | ||
| raise ValueError( | ||
| "Autoregression of higher order than 2 is not defined. Please set ar_order = 2." | ||
| ) | ||
|
|
||
| def __initialize_noise_cascades(self): | ||
| """ | ||
|
|
@@ -2037,6 +2061,7 @@ def __determine_skill_for_current_timestep(self, t): | |
| PHI=self.__params.PHI, | ||
| correlations=self.__state.rho_extrap_cascade, | ||
| correlations_prev=self.__state.rho_extrap_cascade_prev, | ||
| ar_order=self.__config.ar_order, | ||
| ) | ||
|
|
||
| def __determine_NWP_skill_for_next_timestep(self, t, j, worker_state): | ||
|
|
@@ -2228,9 +2253,12 @@ def __regress_extrapolation_and_noise_cascades(self, j, worker_state, t): | |
| ) | ||
| ) | ||
| # Renormalize the cascade | ||
| worker_state.precip_cascades[j][i][1] /= np.std( | ||
| worker_state.precip_cascades[j][i][1] | ||
| ) | ||
| for nt, obs_cascade_timestep in enumerate( | ||
| worker_state.precip_cascades[j][i] | ||
| ): | ||
| worker_state.precip_cascades[j][i][nt] /= np.std( | ||
| obs_cascade_timestep | ||
| ) | ||
| else: | ||
| # use the deterministic AR(p) model computed above if | ||
| # perturbations are disabled | ||
|
|
@@ -3625,6 +3653,20 @@ def forecast( | |
| turns out to be a warranted functionality. | ||
| """ | ||
|
|
||
| # Check the input precip and ar_order to be consistent | ||
| # zero-precip/constant field in previous time steps has to be removed | ||
| # (constant field causes autoregression to fail) | ||
| precip, ar_order = check_previous_radar_obs( | ||
| precip, | ||
| ar_order, | ||
| { | ||
| "precip_thr": precip_thr, | ||
| "norain_thr": norain_thr, | ||
| # Set default window function to "tukey" to match the default in check_inputs() below | ||
| "win_fun": "tukey" if noise_kwargs is None else noise_kwargs["win_fun"], | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I was not totally sure whether this check should be for the entire domain or for the window function that is used in later noise generation. |
||
| }, | ||
| ) | ||
|
|
||
| blending_config = StepsBlendingConfig( | ||
| n_ens_members=n_ens_members, | ||
| n_cascade_levels=n_cascade_levels, | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -28,7 +28,7 @@ | |
| ) | ||
| from pysteps.postprocessing import probmatching | ||
| from pysteps.timeseries import autoregression, correlation | ||
| from pysteps.utils.check_norain import check_norain | ||
| from pysteps.utils.check_norain import check_norain, check_previous_radar_obs | ||
|
|
||
| try: | ||
| import dask | ||
|
|
@@ -358,11 +358,19 @@ def compute_forecast(self): | |
| self.__precip = self.__precip[-(self.__config.ar_order + 1) :, :, :].copy() | ||
| self.__initialize_nowcast_components() | ||
| if check_norain( | ||
| self.__precip, | ||
| self.__precip[-1], | ||
| self.__config.precip_threshold, | ||
| self.__config.norain_threshold, | ||
| self.__params.noise_kwargs["win_fun"], | ||
| ): | ||
| # Set all to inputs to 0 (also previous times) as we just check latest input in check_norain | ||
| self.__precip = self.__precip.copy() | ||
| self.__precip = np.where( | ||
| np.isfinite(self.__precip), | ||
| np.ones(self.__precip.shape) * np.nanmin(self.__precip), | ||
| self.__precip, | ||
| ) | ||
|
|
||
| return zero_precipitation_forecast( | ||
| self.__config.n_ens_members, | ||
| self.__time_steps, | ||
|
|
@@ -1495,6 +1503,20 @@ def forecast( | |
| :cite:`Seed2003`, :cite:`BPS2006`, :cite:`SPN2013`, :cite:`PCH2019b` | ||
| """ | ||
|
|
||
| # Check the input precip and ar_order to be consistent | ||
| # zero-precip/constant field in previous time steps has to be removed | ||
| # (constant field causes autoregression to fail) | ||
| precip, ar_order = check_previous_radar_obs( | ||
| precip, | ||
| ar_order, | ||
| { | ||
| "precip_thr": precip_thr, | ||
| "norain_thr": norain_thr, | ||
| # Set default window function to "tukey" to match the default in check_inputs() below | ||
| "win_fun": "tukey" if noise_kwargs is None else noise_kwargs["win_fun"], | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Same as in blending.steps.py. |
||
| }, | ||
| ) | ||
|
|
||
| nowcaster_config = StepsNowcasterConfig( | ||
| n_ens_members=n_ens_members, | ||
| n_cascade_levels=n_cascade_levels, | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.