Skip to content

Commit

Permalink
Use seed for all rng in blending to make a test run completely determ…
Browse files Browse the repository at this point in the history
…inistic
  • Loading branch information
mats-knmi committed Jan 2, 2025
1 parent e332585 commit a05c87f
Show file tree
Hide file tree
Showing 4 changed files with 49 additions and 28 deletions.
43 changes: 30 additions & 13 deletions pysteps/blending/steps.py
Original file line number Diff line number Diff line change
Expand Up @@ -734,16 +734,19 @@ def forecast(
)

# 6. Initialize all the random generators and prepare for the forecast loop
randgen_prec, vps, generate_vel_noise = _init_random_generators(
velocity,
noise_method,
vel_pert_method,
vp_par,
vp_perp,
seed,
n_ens_members,
kmperpixel,
timestep,
randgen_prec, vps, generate_vel_noise, randgen_probmatching = (
_init_random_generators(
velocity,
noise_method,
probmatching_method,
vel_pert_method,
vp_par,
vp_perp,
seed,
n_ens_members,
kmperpixel,
timestep,
)
)
D, D_Yn, D_pb, R_f, R_m, mask_rim, struct, fft_objs = _prepare_forecast_loop(
precip_cascade,
Expand Down Expand Up @@ -1621,6 +1624,7 @@ def worker(j):
first_array=arr1,
second_array=arr2,
probability_first_array=weights_pm_normalized[0],
randgen=randgen_probmatching[j],
)
else:
R_pm_resampled = R_pm_blended.copy()
Expand Down Expand Up @@ -2290,6 +2294,7 @@ def _find_nwp_combination(
def _init_random_generators(
velocity,
noise_method,
probmatching_method,
vel_pert_method,
vp_par,
vp_perp,
Expand All @@ -2301,16 +2306,28 @@ def _init_random_generators(
"""Initialize all the random generators."""
if noise_method is not None:
randgen_prec = []
randgen_motion = []
for j in range(n_ens_members):
rs = np.random.RandomState(seed)
randgen_prec.append(rs)
seed = rs.randint(0, high=1e9)
else:
randgen_prec = None

Check warning on line 2314 in pysteps/blending/steps.py

View check run for this annotation

Codecov / codecov/patch

pysteps/blending/steps.py#L2314

Added line #L2314 was not covered by tests

if probmatching_method is not None:
randgen_probmatching = []
for j in range(n_ens_members):
rs = np.random.RandomState(seed)
randgen_motion.append(rs)
randgen_probmatching.append(rs)
seed = rs.randint(0, high=1e9)
else:
randgen_probmatching = None

if vel_pert_method is not None:
randgen_motion = []
for j in range(n_ens_members):
rs = np.random.RandomState(seed)
randgen_motion.append(rs)
seed = rs.randint(0, high=1e9)

Check warning on line 2330 in pysteps/blending/steps.py

View check run for this annotation

Codecov / codecov/patch

pysteps/blending/steps.py#L2326-L2330

Added lines #L2326 - L2330 were not covered by tests
init_vel_noise, generate_vel_noise = noise.get_method(vel_pert_method)

# initialize the perturbation generators for the motion field
Expand All @@ -2326,7 +2343,7 @@ def _init_random_generators(
else:
vps, generate_vel_noise = None, None

return randgen_prec, vps, generate_vel_noise
return randgen_prec, vps, generate_vel_noise, randgen_probmatching


def _prepare_forecast_loop(
Expand Down
5 changes: 3 additions & 2 deletions pysteps/noise/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -101,8 +101,9 @@ def compute_noise_stddev_adjs(
randstates = []

for k in range(num_iter):
randstates.append(np.random.RandomState(seed=seed))
seed = np.random.randint(0, high=1e9)
rs = np.random.RandomState(seed=seed)
randstates.append(rs)
seed = rs.randint(0, high=1e9)

def worker(k):
# generate Gaussian white noise field, filter it using the chosen
Expand Down
4 changes: 2 additions & 2 deletions pysteps/postprocessing/probmatching.py
Original file line number Diff line number Diff line change
Expand Up @@ -274,7 +274,7 @@ def _get_error(scale):
return shift, scale, R.reshape(shape)


def resample_distributions(first_array, second_array, probability_first_array):
def resample_distributions(first_array, second_array, probability_first_array, randgen):
"""
Merges two distributions (e.g., from the extrapolation nowcast and NWP in the blending module)
to effectively combine two distributions for probability matching without losing extremes.
Expand Down Expand Up @@ -324,7 +324,7 @@ def resample_distributions(first_array, second_array, probability_first_array):
n = asort.shape[0]

# Resample the distributions
idxsamples = np.random.binomial(1, probability_first_array, n).astype(bool)
idxsamples = randgen.binomial(1, probability_first_array, n).astype(bool)
csort = np.where(idxsamples, asort, bsort)
csort = np.sort(csort)[::-1]

Expand Down
25 changes: 14 additions & 11 deletions pysteps/tests/test_postprocessing_probmatching.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
import numpy as np
import pytest
from pysteps.postprocessing.probmatching import resample_distributions
from pysteps.postprocessing.probmatching import nonparam_match_empirical_cdf

from pysteps.postprocessing.probmatching import (
nonparam_match_empirical_cdf,
resample_distributions,
)


class TestResampleDistributions:
Expand All @@ -16,7 +19,7 @@ def test_valid_inputs(self):
second_array = np.array([2, 4, 6, 8, 10])
probability_first_array = 0.6
result = resample_distributions(
first_array, second_array, probability_first_array
first_array, second_array, probability_first_array, np.random
)
expected_result = np.array([9, 8, 6, 3, 1]) # Expected result based on the seed
assert result.shape == first_array.shape
Expand All @@ -27,7 +30,7 @@ def test_probability_zero(self):
second_array = np.array([2, 4, 6, 8, 10])
probability_first_array = 0.0
result = resample_distributions(
first_array, second_array, probability_first_array
first_array, second_array, probability_first_array, np.random
)
assert np.array_equal(result, np.sort(second_array)[::-1])

Expand All @@ -36,7 +39,7 @@ def test_probability_one(self):
second_array = np.array([2, 4, 6, 8, 10])
probability_first_array = 1.0
result = resample_distributions(
first_array, second_array, probability_first_array
first_array, second_array, probability_first_array, np.random
)
assert np.array_equal(result, np.sort(first_array)[::-1])

Expand All @@ -45,7 +48,7 @@ def test_nan_in_arr1_prob_1(self):
array_without_nan = np.array([2.0, 4, 6, 8, 10])
probability_first_array = 1.0
result = resample_distributions(
array_with_nan, array_without_nan, probability_first_array
array_with_nan, array_without_nan, probability_first_array, np.random
)
expected_result = np.array([np.nan, 9, 7, 3, 1], dtype=float)
assert np.allclose(result, expected_result, equal_nan=True)
Expand All @@ -55,7 +58,7 @@ def test_nan_in_arr1_prob_0(self):
array_without_nan = np.array([2, 4, 6, 8, 10])
probability_first_array = 0.0
result = resample_distributions(
array_with_nan, array_without_nan, probability_first_array
array_with_nan, array_without_nan, probability_first_array, np.random
)
expected_result = np.array([np.nan, 10, 8, 4, 2], dtype=float)
assert np.allclose(result, expected_result, equal_nan=True)
Expand All @@ -65,7 +68,7 @@ def test_nan_in_arr2_prob_1(self):
array_with_nan = np.array([2.0, 4, 6, np.nan, 10])
probability_first_array = 1.0
result = resample_distributions(
array_without_nan, array_with_nan, probability_first_array
array_without_nan, array_with_nan, probability_first_array, np.random
)
expected_result = np.array([np.nan, 9, 5, 3, 1], dtype=float)
assert np.allclose(result, expected_result, equal_nan=True)
Expand All @@ -75,7 +78,7 @@ def test_nan_in_arr2_prob_0(self):
array_with_nan = np.array([2, 4, 6, np.nan, 10])
probability_first_array = 0.0
result = resample_distributions(
array_without_nan, array_with_nan, probability_first_array
array_without_nan, array_with_nan, probability_first_array, np.random
)
expected_result = np.array([np.nan, 10, 6, 4, 2], dtype=float)
assert np.allclose(result, expected_result, equal_nan=True)
Expand All @@ -85,7 +88,7 @@ def test_nan_in_both_prob_1(self):
array2_with_nan = np.array([2.0, 4, np.nan, np.nan, 10])
probability_first_array = 1.0
result = resample_distributions(
array1_with_nan, array2_with_nan, probability_first_array
array1_with_nan, array2_with_nan, probability_first_array, np.random
)
expected_result = np.array([np.nan, np.nan, np.nan, 9, 1], dtype=float)
assert np.allclose(result, expected_result, equal_nan=True)
Expand All @@ -95,7 +98,7 @@ def test_nan_in_both_prob_0(self):
array2_with_nan = np.array([2.0, 4, np.nan, np.nan, 10])
probability_first_array = 0.0
result = resample_distributions(
array1_with_nan, array2_with_nan, probability_first_array
array1_with_nan, array2_with_nan, probability_first_array, np.random
)
expected_result = np.array([np.nan, np.nan, np.nan, 10, 2], dtype=float)
assert np.allclose(result, expected_result, equal_nan=True)
Expand Down

0 comments on commit a05c87f

Please sign in to comment.