16
16
method to decompose and store the NWP model fields whenever a new NWP model
17
17
field is present, is present in pysteps.blending.utils.decompose_NWP.
18
18
#. Estimate AR parameters for the extrapolation nowcast and noise cascade.
19
- #. Before starting the forecast loop, determine which NWP models will be
20
- combined with which nowcast ensemble members. The number of output ensemble
21
- members equals the maximum number of (ensemble) members in the input, which
22
- can be either the defined number of (nowcast) ensemble members or the number
23
- of NWP models/members.
24
19
#. Initialize all the random generators.
25
20
#. Calculate the initial skill of the NWP model forecasts at t=0.
26
21
#. Start the forecasting loop:
22
+ #. Determine which NWP models will be combined with which nowcast ensemble
23
+ member. The number of output ensemble members equals the maximum number
24
+ of (ensemble) members in the input, which can be either the defined
25
+ number of (nowcast) ensemble members or the number of NWP models/members.
27
26
#. Determine the skill and weights of the forecasting components
28
27
(extrapolation, NWP and noise) for that lead time.
29
28
#. Regress the extrapolation and noise cascades separately to the subsequent
@@ -149,7 +148,9 @@ def forecast(
149
148
Datetime object containing the date and time for which the forecast
150
149
is issued.
151
150
n_ens_members: int
152
- The number of ensemble members to generate.
151
+ The number of ensemble members to generate. This number should always be
152
+ equal to or larger than the number of NWP ensemble members / number of
153
+ NWP models.
153
154
n_cascade_levels: int, optional
154
155
The number of cascade levels to use. Default set to 8 due to default
155
156
climatological skill values on 8 levels.
@@ -561,34 +562,19 @@ def forecast(
561
562
precip_cascade , ar_order , n_cascade_levels , MASK_thr
562
563
)
563
564
564
- # 5. Before calling the worker for the forecast loop, determine which (NWP)
565
- # models will be combined with which nowcast ensemble members. With the
566
- # way it is implemented at this moment: n_ens_members of the output equals
567
- # the maximum number of (ensemble) members in the input (either the nowcasts or NWP).
568
- (
569
- precip_cascade ,
570
- precip_models_cascade ,
571
- precip_models_pm ,
572
- velocity_models ,
573
- mu_models ,
574
- sigma_models ,
575
- n_ens_members ,
576
- n_model_indices ,
577
- ) = _find_nwp_combination (
578
- precip_cascade ,
579
- precip_models_cascade ,
580
- precip_models_pm ,
581
- velocity_models ,
582
- mu_models ,
583
- sigma_models ,
584
- n_ens_members ,
585
- ar_order ,
586
- n_cascade_levels ,
587
- blend_nwp_members ,
588
- )
565
+ # 5. Repeat precip_cascade for n ensemble members
566
+ # First, discard all except the p-1 last cascades because they are not needed
567
+ # for the AR(p) model
568
+ precip_cascade = [precip_cascade [i ][- ar_order :] for i in range (n_cascade_levels )]
569
+
570
+ precip_cascade = [
571
+ [precip_cascade [j ].copy () for j in range (n_cascade_levels )]
572
+ for i in range (n_ens_members )
573
+ ]
574
+ precip_cascade = np .stack (precip_cascade )
589
575
590
576
# Also initialize the cascade of temporally correlated noise, which has the
591
- # same shape as R_c , but starts with value zero.
577
+ # same shape as precip_cascade , but starts with value zero.
592
578
noise_cascade = np .zeros (precip_cascade .shape )
593
579
594
580
# 6. Initialize all the random generators and prepare for the forecast loop
@@ -617,17 +603,7 @@ def forecast(
617
603
618
604
precip = precip [- 1 , :, :]
619
605
620
- # 7. Calculate the initial skill of the (NWP) model forecasts at t=0
621
- rho_nwp_models = _compute_initial_nwp_skill (
622
- precip_cascade ,
623
- precip_models_cascade ,
624
- domain_mask ,
625
- issuetime ,
626
- outdir_path_skill ,
627
- clim_kwargs ,
628
- )
629
-
630
- # Also initizalize the current and previous extrapolation forecast scale
606
+ # 7. initizalize the current and previous extrapolation forecast scale
631
607
# for the nowcasting component
632
608
rho_extr_prev = np .repeat (1.0 , PHI .shape [0 ])
633
609
rho_extr = PHI [:, 0 ] / (1.0 - PHI [:, 1 ]) # phi1 / (1 - phi2), see BPS2004
@@ -681,9 +657,43 @@ def forecast(
681
657
if measure_time :
682
658
starttime = time .time ()
683
659
684
- # 8.1.1 Determine the skill of the components for lead time (t0 + t)
685
- # First for the extrapolation component. Only calculate it when t > 0.
660
+ # 8.1.1 Before calling the worker for the forecast loop, determine which (NWP)
661
+ # models will be combined with which nowcast ensemble members. With the
662
+ # way it is implemented at this moment: n_ens_members of the output equals
663
+ # the maximum number of (ensemble) members in the input (either the nowcasts or NWP).
664
+ (
665
+ precip_models_cascade_temp ,
666
+ precip_models_pm_temp ,
667
+ velocity_models_temp ,
668
+ mu_models_temp ,
669
+ sigma_models_temp ,
670
+ n_model_indices ,
671
+ ) = _find_nwp_combination (
672
+ precip_models_cascade [:, t , :, :, :],
673
+ precip_models_pm [:, t , :, :],
674
+ velocity_models [:, t , :, :, :],
675
+ mu_models [:, t , :],
676
+ sigma_models [:, t , :],
677
+ n_ens_members ,
678
+ ar_order ,
679
+ n_cascade_levels ,
680
+ blend_nwp_members ,
681
+ )
682
+
683
+ if t == 0 :
684
+ # 8.1.2 Calculate the initial skill of the (NWP) model forecasts at t=0
685
+ rho_nwp_models = _compute_initial_nwp_skill (
686
+ precip_cascade ,
687
+ precip_models_cascade_temp ,
688
+ domain_mask ,
689
+ issuetime ,
690
+ outdir_path_skill ,
691
+ clim_kwargs ,
692
+ )
693
+
686
694
if t > 0 :
695
+ # 8.1.3 Determine the skill of the components for lead time (t0 + t)
696
+ # First for the extrapolation component. Only calculate it when t > 0.
687
697
(
688
698
rho_extr ,
689
699
rho_extr_prev ,
@@ -740,18 +750,22 @@ def worker(j):
740
750
# Only the weights of the components without the extrapolation
741
751
# cascade will be determined here. The full set of weights are
742
752
# determined after the extrapolation step in this method.
743
- if blend_nwp_members and precip_models_cascade .shape [0 ] > 1 :
753
+ if blend_nwp_members and precip_models_cascade_temp .shape [0 ] > 1 :
744
754
weights_model_only = np .zeros (
745
- (precip_models_cascade .shape [0 ] + 1 , n_cascade_levels )
755
+ (precip_models_cascade_temp .shape [0 ] + 1 , n_cascade_levels )
746
756
)
747
757
for i in range (n_cascade_levels ):
748
758
# Determine the normalized covariance matrix (containing)
749
759
# the cross-correlations between the models
750
760
cov = np .corrcoef (
751
761
np .stack (
752
762
[
753
- precip_models_cascade [n_model , t , i , :, :].flatten ()
754
- for n_model in range (precip_models_cascade .shape [0 ])
763
+ precip_models_cascade_temp [
764
+ n_model , i , :, :
765
+ ].flatten ()
766
+ for n_model in range (
767
+ precip_models_cascade_temp .shape [0 ]
768
+ )
755
769
]
756
770
)
757
771
)
@@ -883,12 +897,12 @@ def worker(j):
883
897
V_stack = np .concatenate (
884
898
(
885
899
velocity_pert [None , :, :, :],
886
- velocity_models [:, t , :, :, :] ,
900
+ velocity_models_temp ,
887
901
),
888
902
axis = 0 ,
889
903
)
890
904
else :
891
- V_model_ = velocity_models [ j , t , :, :, : ]
905
+ V_model_ = velocity_models_temp [ j ]
892
906
V_stack = np .concatenate (
893
907
(velocity_pert [None , :, :, :], V_model_ [None , :, :, :]),
894
908
axis = 0 ,
@@ -983,11 +997,11 @@ def worker(j):
983
997
# Stack the perturbed extrapolation and the NWP velocities
984
998
if blend_nwp_members :
985
999
V_stack = np .concatenate (
986
- (velocity_pert [None , :, :, :], velocity_models [:, t , :, :, :] ),
1000
+ (velocity_pert [None , :, :, :], velocity_models_temp ),
987
1001
axis = 0 ,
988
1002
)
989
1003
else :
990
- V_model_ = velocity_models [ j , t , :, :, : ]
1004
+ V_model_ = velocity_models_temp [ j ]
991
1005
V_stack = np .concatenate (
992
1006
(velocity_pert [None , :, :, :], V_model_ [None , :, :, :]), axis = 0
993
1007
)
@@ -1050,32 +1064,32 @@ def worker(j):
1050
1064
cascades_stacked = np .concatenate (
1051
1065
(
1052
1066
R_f_ep_out [None , t_index ],
1053
- precip_models_cascade [:, t ] ,
1067
+ precip_models_cascade_temp ,
1054
1068
Yn_ep_out [None , t_index ],
1055
1069
),
1056
1070
axis = 0 ,
1057
1071
) # [(extr_field, n_model_fields, noise), n_cascade_levels, ...]
1058
1072
means_stacked = np .concatenate (
1059
- (mu_extrapolation [None , :], mu_models [:, t ] ), axis = 0
1073
+ (mu_extrapolation [None , :], mu_models_temp ), axis = 0
1060
1074
)
1061
1075
sigmas_stacked = np .concatenate (
1062
- (sigma_extrapolation [None , :], sigma_models [:, t ] ),
1076
+ (sigma_extrapolation [None , :], sigma_models_temp ),
1063
1077
axis = 0 ,
1064
1078
)
1065
1079
else :
1066
1080
cascades_stacked = np .concatenate (
1067
1081
(
1068
1082
R_f_ep_out [None , t_index ],
1069
- precip_models_cascade [None , j , t ],
1083
+ precip_models_cascade_temp [None , j ],
1070
1084
Yn_ep_out [None , t_index ],
1071
1085
),
1072
1086
axis = 0 ,
1073
1087
) # [(extr_field, n_model_fields, noise), n_cascade_levels, ...]
1074
1088
means_stacked = np .concatenate (
1075
- (mu_extrapolation [None , :], mu_models [None , j , t ]), axis = 0
1089
+ (mu_extrapolation [None , :], mu_models_temp [None , j ]), axis = 0
1076
1090
)
1077
1091
sigmas_stacked = np .concatenate (
1078
- (sigma_extrapolation [None , :], sigma_models [None , j , t ]),
1092
+ (sigma_extrapolation [None , :], sigma_models_temp [None , j ]),
1079
1093
axis = 0 ,
1080
1094
)
1081
1095
@@ -1173,15 +1187,15 @@ def worker(j):
1173
1187
R_pm_stacked = np .concatenate (
1174
1188
(
1175
1189
R_pm_ep [None , t_index ],
1176
- precip_models_pm [:, t ] ,
1190
+ precip_models_pm_temp ,
1177
1191
),
1178
1192
axis = 0 ,
1179
1193
)
1180
1194
else :
1181
1195
R_pm_stacked = np .concatenate (
1182
1196
(
1183
1197
R_pm_ep [None , t_index ],
1184
- precip_models_pm [None , j , t ],
1198
+ precip_models_pm_temp [None , j ],
1185
1199
),
1186
1200
axis = 0 ,
1187
1201
)
@@ -1198,11 +1212,11 @@ def worker(j):
1198
1212
weights_pm_normalized_mod_only .reshape (
1199
1213
weights_pm_normalized_mod_only .shape [0 ], 1 , 1
1200
1214
)
1201
- * precip_models_pm [:, t ] ,
1215
+ * precip_models_pm_temp ,
1202
1216
axis = 0 ,
1203
1217
)
1204
1218
else :
1205
- R_pm_blended_mod_only = precip_models_pm [ j , t ]
1219
+ R_pm_blended_mod_only = precip_models_pm_temp [ j ]
1206
1220
1207
1221
# The extrapolation components are NaN outside the advected
1208
1222
# radar domain. This results in NaN values in the blended
@@ -1816,7 +1830,6 @@ def _estimate_ar_parameters_radar(R_c, ar_order, n_cascade_levels, MASK_thr):
1816
1830
1817
1831
1818
1832
def _find_nwp_combination (
1819
- R_c ,
1820
1833
precip_models ,
1821
1834
R_models_pm ,
1822
1835
velocity_models ,
@@ -1831,26 +1844,22 @@ def _find_nwp_combination(
1831
1844
With the way it is implemented at this moment: n_ens_members of the output equals
1832
1845
the maximum number of (ensemble) members in the input (either the nowcasts or NWP).
1833
1846
"""
1834
- ###
1835
- # First, discard all except the p-1 last cascades because they are not needed
1836
- # for the AR(p) model
1837
- R_c = [R_c [i ][- ar_order :] for i in range (n_cascade_levels )]
1847
+ # Make sure the number of model members is not larger than than or equal to
1848
+ # n_ens_members
1849
+ n_model_members = precip_models .shape [0 ]
1850
+ if n_model_members > n_ens_members :
1851
+ raise ValueError (
1852
+ "The number of NWP model members is larger than the given number of ensemble members. n_model_members <= n_ens_members."
1853
+ )
1838
1854
1839
1855
# Check if NWP models/members should be used individually, or if all of
1840
1856
# them are blended together per nowcast ensemble member.
1841
1857
if blend_nwp_members :
1842
- # stack the extrapolation cascades into a list containing all ensemble members
1843
- R_c = [
1844
- [R_c [j ].copy () for j in range (n_cascade_levels )]
1845
- for i in range (n_ens_members )
1846
- ]
1847
- R_c = np .stack (R_c )
1848
1858
n_model_indices = None
1849
1859
1850
1860
else :
1851
1861
# Start with determining the maximum and mimimum number of members/models
1852
1862
# in both input products
1853
- n_model_members = precip_models .shape [0 ]
1854
1863
n_ens_members_max = max (n_ens_members , n_model_members )
1855
1864
n_ens_members_min = min (n_ens_members , n_model_members )
1856
1865
# Also make a list of the model index numbers. These indices are needed
@@ -1871,20 +1880,12 @@ def _find_nwp_combination(
1871
1880
# member 5, etc.), until 10 is reached.
1872
1881
if n_ens_members_min != n_ens_members_max :
1873
1882
if n_model_members == 1 :
1874
- precip_models = np .repeat (
1875
- precip_models [:, :, :, :, :], n_ens_members_max , axis = 0
1876
- )
1877
- mu_models = np .repeat (mu_models [:, :, :], n_ens_members_max , axis = 0 )
1878
- sigma_models = np .repeat (
1879
- sigma_models [:, :, :], n_ens_members_max , axis = 0
1880
- )
1881
- velocity_models = np .repeat (
1882
- velocity_models [:, :, :, :], n_ens_members_max , axis = 0
1883
- )
1883
+ precip_models = np .repeat (precip_models , n_ens_members_max , axis = 0 )
1884
+ mu_models = np .repeat (mu_models , n_ens_members_max , axis = 0 )
1885
+ sigma_models = np .repeat (sigma_models , n_ens_members_max , axis = 0 )
1886
+ velocity_models = np .repeat (velocity_models , n_ens_members_max , axis = 0 )
1884
1887
# For the prob. matching
1885
- R_models_pm = np .repeat (
1886
- R_models_pm [:, :, :, :], n_ens_members_max , axis = 0
1887
- )
1888
+ R_models_pm = np .repeat (R_models_pm , n_ens_members_max , axis = 0 )
1888
1889
# Finally, for the model indices
1889
1890
n_model_indices = np .repeat (n_model_indices , n_ens_members_max , axis = 0 )
1890
1891
@@ -1903,22 +1904,12 @@ def _find_nwp_combination(
1903
1904
# Finally, for the model indices
1904
1905
n_model_indices = np .repeat (n_model_indices , repeats , axis = 0 )
1905
1906
1906
- R_c = [
1907
- [R_c [j ].copy () for j in range (n_cascade_levels )]
1908
- for i in range (n_ens_members_max )
1909
- ]
1910
- R_c = np .stack (R_c )
1911
-
1912
- n_ens_members = n_ens_members_max
1913
-
1914
1907
return (
1915
- R_c ,
1916
1908
precip_models ,
1917
1909
R_models_pm ,
1918
1910
velocity_models ,
1919
1911
mu_models ,
1920
1912
sigma_models ,
1921
- n_ens_members ,
1922
1913
n_model_indices ,
1923
1914
)
1924
1915
@@ -2015,7 +2006,7 @@ def _compute_initial_nwp_skill(
2015
2006
rho_nwp_models = [
2016
2007
blending .skill_scores .spatial_correlation (
2017
2008
obs = R_c [0 , :, - 1 , :, :],
2018
- mod = precip_models [n_model , 0 , :, :, :],
2009
+ mod = precip_models [n_model , :, :, :],
2019
2010
domain_mask = domain_mask ,
2020
2011
)
2021
2012
for n_model in range (precip_models .shape [0 ])
@@ -2024,7 +2015,7 @@ def _compute_initial_nwp_skill(
2024
2015
2025
2016
# Ensure that the model skill decreases with increasing scale level.
2026
2017
for n_model in range (precip_models .shape [0 ]):
2027
- for i in range (1 , precip_models .shape [2 ]):
2018
+ for i in range (1 , precip_models .shape [1 ]):
2028
2019
if rho_nwp_models [n_model , i ] > rho_nwp_models [n_model , i - 1 ]:
2029
2020
# Set it equal to the previous scale level
2030
2021
rho_nwp_models [n_model , i ] = rho_nwp_models [n_model , i - 1 ]
0 commit comments