Skip to content

Commit 990bce2

Browse files
committed
cleaned up exptag[_list] and outid; added/clarified comments in ObsFcstAna post-proc package
1 parent 615ca1a commit 990bce2

File tree

4 files changed

+53
-44
lines changed

4 files changed

+53
-44
lines changed

GEOSldas_App/util/postproc/ObsFcstAna_stats/Plot_stats_maps.py

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
"""
44
Sample script for plotting maps of long-term data assimilation diagnostics.
5+
Computes Nobs-weighted avg of each metric across all species.
56
Requires saved files with monthly sums (see Get_ObsFcstAna_stat.py).
67
Stats of *normalized* OmFs are approximated!
78
"""
@@ -29,7 +30,7 @@ def plot_OmF_maps(postproc_obj, stats, fig_path='./'):
2930

3031
Nmin = 20
3132

32-
# Then computer metrics of O-F, O-A, etc. based on above computed
33+
# Then compute metrics of O-F, O-A, etc. based on above computed
3334
N_data = stats['N_data']
3435
O_mean = stats['obs_mean']
3536
# mean(x-y) = E[x] - E[y]
@@ -66,7 +67,10 @@ def plot_OmF_maps(postproc_obj, stats, fig_path='./'):
6667
OmA_stdv[ N_data < Nmin] = np.nan
6768
N_data[ N_data < Nmin] = 0
6869

69-
# Combine metrics of individual species using weighted averaging
70+
# Compute Nobs-weighted avg of each metric across all species.
71+
# Typically used for SMAP Tb_h/h from asc and desc overpasses,
72+
# or ASCAT soil moisture from Metop-A/B/C.
73+
# DOES NOT MAKE SENSE IF, SAY, SPECIES HAVE DIFFERENT UNITS!
7074
Nobs_data = np.nansum( N_data, axis=1)
7175
OmF_mean = np.nansum(OmF_mean *N_data, axis=1)/Nobs_data
7276
OmF_stdv = np.nansum(OmF_stdv *N_data, axis=1)/Nobs_data
@@ -76,7 +80,7 @@ def plot_OmF_maps(postproc_obj, stats, fig_path='./'):
7680
OmA_stdv = np.nansum(OmA_stdv *N_data, axis=1)/Nobs_data
7781

7882
# Plotting
79-
expid = postproc_obj.outid
83+
exptag = postproc_obj.exptag
8084

8185
fig, axes = plt.subplots(2,2, figsize=(18,10))
8286
plt.rcParams.update({'font.size':14})
@@ -89,23 +93,23 @@ def plot_OmF_maps(postproc_obj, stats, fig_path='./'):
8993
# crange is [cmin, cmax]
9094
crange =[0, np.ceil((end_time-start_time).days/150)*300]
9195
colormap = plt.get_cmap('jet',20)
92-
title_txt = expid + ' Tb Nobs '+ start_time.strftime('%Y%m')+'_'+end_time.strftime('%Y%m')
96+
title_txt = exptag + ' Tb Nobs '+ start_time.strftime('%Y%m')+'_'+end_time.strftime('%Y%m')
9397
units = '[-]'
9498
if i == 0 and j ==1:
9599
tile_data = OmF_mean
96100
crange =[-3, 3]
97101
colormap = plt.get_cmap('bwr', 15)
98-
title_txt = expid + ' Tb O-F mean '+ start_time.strftime('%Y%m')+'_'+end_time.strftime('%Y%m')
102+
title_txt = exptag + ' Tb O-F mean '+ start_time.strftime('%Y%m')+'_'+end_time.strftime('%Y%m')
99103
if i == 1 and j == 0:
100104
tile_data = OmF_stdv
101105
crange =[0, 15]
102106
colormap = plt.get_cmap ('jet',15)
103-
title_txt = expid + ' Tb O-F stdv '+ start_time.strftime('%Y%m')+'_'+end_time.strftime('%Y%m')
107+
title_txt = exptag + ' Tb O-F stdv '+ start_time.strftime('%Y%m')+'_'+end_time.strftime('%Y%m')
104108
if i == 1 and j == 1:
105109
tile_data = OmF_norm_stdv
106110
crange =[0, 15]
107111
colormap = plt.get_cmap ('jet',15)
108-
title_txt = expid + ' Tb normalized O-F stdv (approx!) '+ start_time.strftime('%Y%m%d')+'_'+end_time.strftime('%Y%m%d')
112+
title_txt = exptag + ' Tb normalized O-F stdv (approx!) '+ start_time.strftime('%Y%m%d')+'_'+end_time.strftime('%Y%m%d')
109113

110114
colormap.set_bad(color='0.9') # light grey, 0-black, 1-white
111115

@@ -159,7 +163,7 @@ def plot_OmF_maps(postproc_obj, stats, fig_path='./'):
159163

160164
plt.tight_layout()
161165
# Save figure to file
162-
fig.savefig(fig_path+'Map_OmF_'+ expid +'_'+start_time.strftime('%Y%m')+'_'+\
166+
fig.savefig(fig_path+'Map_OmF_'+ exptag +'_'+start_time.strftime('%Y%m')+'_'+\
163167
(end_time+timedelta(days=-1)).strftime('%Y%m')+'.png')
164168
#plt.show()
165169
plt.close(fig)

GEOSldas_App/util/postproc/ObsFcstAna_stats/Plot_stats_timeseries.py

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
"""
44
Sample script for plotting monthly time series of spatially averaged data assimilation diagnostics.
5+
Computes Nobs-weighted avg of each metric across all species.
56
Requires saved files with monthly sums (see Get_ObsFcstAna_stat.py).
67
"""
78

@@ -17,11 +18,11 @@
1718

1819
def Plot_monthly_OmF_bars(postproc_obj, fig_path='./'):
1920
import pickle
20-
expid = postproc_obj.outid
21+
exptag = postproc_obj.exptag
2122
start_month = postproc_obj.start_time
22-
end_month = postproc_obj.end_time
23+
end_month = postproc_obj.end_time
2324

24-
stats_file = fig_path + 'spatial_stats_'+expid+'_'+start_month.strftime('%Y%m')+ \
25+
stats_file = fig_path + 'spatial_stats_'+exptag+'_'+start_month.strftime('%Y%m')+ \
2526
'_'+(end_month+timedelta(days=-1)).strftime('%Y%m')+'.pkl'
2627

2728
if os.path.isfile(stats_file):
@@ -47,7 +48,10 @@ def Plot_monthly_OmF_bars(postproc_obj, fig_path='./'):
4748

4849
OmFm,OmFs,OmAm,OmAs,Nobsm = postproc_obj.calc_spatial_stats_from_sums(current_month)
4950

50-
# Average individual species into a single value
51+
# Compute Nobs-weighted avg of each metric across all species.
52+
# Typically used for SMAP Tb_h/h from asc and desc overpasses,
53+
# or ASCAT soil moisture from Metop-A/B/C.
54+
# DOES NOT MAKE SENSE IF, SAY, SPECIES HAVE DIFFERENT UNITS!
5155
Nobsm = np.nansum( Nobsm)
5256
OmFm = np.nansum(OmFm*Nobsm)/Nobsm
5357
OmFs = np.nansum(OmFs*Nobsm)/Nobsm
@@ -81,13 +85,13 @@ def Plot_monthly_OmF_bars(postproc_obj, fig_path='./'):
8185
plt.grid(True, linestyle='--', alpha=0.5)
8286

8387
plt.xticks(ticks=date_vec[::2], labels=date_vec[::2])
84-
plt.title(expid+ 'monthly '+plot_var)
88+
plt.title(exptag+ 'monthly '+plot_var)
8589
plt.xlim(-1, len(date_vec)+1)
8690
plt.ylim(-.1, 2.)
8791

8892
plt.tight_layout()
8993
#plt.show()
90-
plt.savefig(fig_path+'Bars_'+plot_var+expid+date_vec[0]+'_'+\
94+
plt.savefig(fig_path+'Bars_'+plot_var+exptag+date_vec[0]+'_'+\
9195
date_vec[-1]+'.png')
9296
plt.close()
9397

GEOSldas_App/util/postproc/ObsFcstAna_stats/postproc_ObsFcstAna.py

Lines changed: 8 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -20,11 +20,11 @@
2020

2121
class postproc_ObsFcstAna:
2222

23-
def __init__(self, exp_list, start_time, end_time, sum_path='./', outid=None):
23+
def __init__(self, exp_list, start_time, end_time, sum_path='./'):
2424
self.exp_list = exp_list
2525
self.expdir_list = [item['expdir'] for item in exp_list]
2626
self.expid_list = [item['expid'] for item in exp_list]
27-
self.exptag_list = [item['exptag'] for item in exp_list]
27+
self.exptag = exp_list[0]['exptag']
2828
self.domain = exp_list[0]['domain']
2929
self.start_time = start_time
3030
self.end_time = end_time
@@ -45,11 +45,7 @@ def __init__(self, exp_list, start_time, end_time, sum_path='./', outid=None):
4545
else:
4646
self.obs_from = exp_idx
4747
if self.obs_from < 0: self.obs_from = 0 # by default, obs data are from exp_list[0]
48-
print(f"obs data are from {self.exptag_list[self.obs_from]}")
49-
50-
# Tag of the output sums files. If not provided, use main exptag
51-
if outid is None:
52-
self.outid = self.exptag_list[0]
48+
print(f"obs data are from {exp_list[self.obs_from]['expid']}")
5349

5450
# Verify the configuration every time when current class is initialized
5551
# to avoid saving sums with different configs in the same directory
@@ -63,7 +59,7 @@ def __init__(self, exp_list, start_time, end_time, sum_path='./', outid=None):
6359
config_list.append({var:exp[var] for var in config_verify if var in exp})
6460

6561
# File of configuration for verification
66-
f_config = self.sum_path + '/' + self.outid + '_config.yaml'
62+
f_config = self.sum_path + '/' + self.exptag + '_config.yaml'
6763

6864
# Save a new file or compare current configuration with previously saved
6965
if not os.path.exists(f_config):
@@ -194,7 +190,6 @@ def compute_monthly_sums(self, date_time):
194190
def save_monthly_sums(self):
195191
expdir_list = self.expdir_list
196192
expid_list = self.expid_list
197-
exptag_list = self.exptag_list
198193
domain = self.domain
199194
var_list = self.var_list
200195
tc = self.tilecoord
@@ -208,7 +203,7 @@ def save_monthly_sums(self):
208203
mo_path = self.sum_path + '/Y'+ date_time.strftime('%Y') + '/M' + date_time.strftime('%m') + '/'
209204
os.makedirs(mo_path, exist_ok=True)
210205

211-
fout = self.outid + '.ens_avg.ldas_ObsFcstAna_sums.' + date_time.strftime('%Y%m') +'.nc4'
206+
fout = self.exptag + '.ens_avg.ldas_ObsFcstAna_sums.' + date_time.strftime('%Y%m') +'.nc4'
212207

213208
fout = mo_path + fout
214209

@@ -270,7 +265,7 @@ def calc_temporal_stats_from_sums(self, write_to_nc=True, fout_stats='./stats.nc
270265

271266
fpath = self.sum_path + '/Y'+ date_time.strftime('%Y') + '/M' + date_time.strftime('%m') + '/'
272267

273-
fout = self.outid + '.ens_avg.ldas_ObsFcstAna_sums.' + date_time.strftime('%Y%m') +'.nc4'
268+
fout = self.exptag + '.ens_avg.ldas_ObsFcstAna_sums.' + date_time.strftime('%Y%m') +'.nc4'
274269

275270
fout = fpath + fout
276271

@@ -355,7 +350,7 @@ def calc_spatial_stats_from_sums(self, date_time):
355350
var_list = ['obs_obs', 'obs_fcst','obs_ana']
356351

357352
mo_path = self.sum_path + '/Y'+ date_time.strftime('%Y') + '/M' + date_time.strftime('%m') + '/'
358-
fnc4_sums = mo_path + self.outid + '.ens_avg.ldas_ObsFcstAna_sums.' + date_time.strftime('%Y%m') +'.nc4'
353+
fnc4_sums = mo_path + self.exptag + '.ens_avg.ldas_ObsFcstAna_sums.' + date_time.strftime('%Y%m') +'.nc4'
359354

360355
mdata_sum = {}
361356
mdata2_sum = {}
@@ -405,7 +400,7 @@ def calc_spatial_stats_from_sums(self, date_time):
405400
# var(x) = E[x2] - (E[x])^2
406401
data_var[var] = data2_mean[var] - data_mean[var]**2
407402

408-
# Computer metrics of O-F, O-A, etc. based on above stats
403+
# Compute metrics of O-F, O-A, etc. based on above stats
409404
O_mean = data_mean['obs_obs']
410405
F_mean = data_mean['obs_fcst']
411406
A_mean = data_mean['obs_ana']

GEOSldas_App/util/postproc/ObsFcstAna_stats/user_config.py

Lines changed: 23 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -26,21 +26,23 @@ def get_config():
2626

2727
exp_main = {
2828
'expdir' : '/discover/nobackup/projects/gmao/merra/iau/merra_land/SMAP_runs/SMAP_Nature_v11/',
29-
'expid' : 'DAv8_SMOSSMAP',
30-
'exptag' : 'DAMulti_SMAP',
31-
'domain' : 'SMAP_EASEv2_M36_GLOBAL',
32-
'da_t0' : 3, # (fractional) UTC hour of first land analysis
33-
'da_dt' : 10800, # ObsFcstAna file interval in seconds
34-
'species_list' : [5,6,7,8], # indices of species to be processed
35-
'obsparam_time' : "20150401_0000" # time stamp of obsparam file (YYYYMMDD_HHMM)
29+
'expid' : 'DAv8_SMOSSMAP', # GEOSldas exp ID of simulation
30+
'exptag' : 'DAMulti_SMAP', # string used in output file names for sums & stats,
31+
# can be same as expid or different -- e.g., reflect info
32+
# about subset of included species or about cross-masking
33+
'domain' : 'SMAP_EASEv2_M36_GLOBAL',
34+
'da_t0' : 3, # (fractional) UTC hour of first land analysis
35+
'da_dt' : 10800, # ObsFcstAna file interval in seconds
36+
'species_list' : [5,6,7,8], # indices of species to be processed
37+
'obsparam_time' : "20150401_0000" # time stamp of obsparam file (YYYYMMDD_HHMM)
3638
}
3739

3840
# Optional experiment(s) can be added for cross-masking or extracting obs from a different experiment.
3941
#
4042
# All optional experiments and the main experiment must have identical tilecoords.
4143
# If the default "species" number/order do not match, set "species_list" accordingly to force a match.
4244
# Output will be cross-masked between all specified experiments.
43-
45+
#
4446
# Forecasts and analyses are always from the main experiment.
4547
# Observations are from the experiment with 'use_obs' set to True (default is exp_main). The most
4648
# likely use case for reading obs from a supplemental experiment is when computing OmF etc diagnostics
@@ -50,21 +52,25 @@ def get_config():
5052
exp_sup1 = {
5153
'expdir' : '/discover/nobackup/projects/gmao/merra/iau/merra_land/SMAP_runs/SMAP_Nature_v11/',
5254
'expid' : 'DAv8_M36',
53-
'exptag' : 'DASMAP_SMAP',
5455
'domain' : 'SMAP_EASEv2_M36_GLOBAL',
55-
'use_obs' : True, # if True, use obs data from this exp
56-
'species_list' : [1,2,3,4], # indices of species to be processed
57-
'obsparam_time' : "20150401_0000" # time stamp of obsparam file (YYYYMMDD_HHMM)
56+
'use_obs' : True, # if True, use obs data from this exp
57+
'species_list' : [1,2,3,4], # indices of species to be processed,
58+
# must identify same species as selected in main exp
59+
'obsparam_time' : "20150401_0000" # time stamp of obsparam file (YYYYMMDD_HHMM)
5860
}
5961

60-
# Convert experiments input to a list; first entry must be exp_main
62+
# Convert experiments input to a list; first entry must be exp_main:
6163

62-
exp_list = [exp_main, exp_sup1]
64+
#exp_list = [exp_main] # no cross-masking
65+
exp_list = [exp_main, exp_sup1] # cross-mask exp_main with exp_sup1
6366

64-
# Top level directory to store monthly sum files; can use the experiment directory or a different path;
65-
# /Yyyyy/Mmm/ is added automatically for individual months
67+
# Top level directory for all output from this package:
68+
69+
out_path = '/discover/nobackup/[USERNAME]/SMAP_test/'
6670

67-
out_path = '/discover/nobackup/qliu/SMAP_test/'
71+
# Directory for monthly sum files:
72+
# - Can use the experiment directory or a different path.
73+
# - Automatically appends /Yyyyy/Mmm/ for individual months.
6874

6975
sum_path = out_path+'/'+exp_main['expid']+'/output/'+exp_main['domain']+'/ana/ens_avg/'
7076

0 commit comments

Comments
 (0)