Skip to content

Commit fe28122

Browse files
authored
Merge pull request #316 from mbruckner-work/develop_tropomifix
Changes to MOPITT pairing
2 parents 2718b4c + bb97075 commit fe28122

File tree

7 files changed

+509
-28
lines changed

7 files changed

+509
-28
lines changed

docs/appendix/yaml.rst

+14-3
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,12 @@ or other interactive Python session,
8989
as it gives you some visual indication of the progress of multi-file data loading
9090
and some parts of the processing.
9191

92+
**pairing_kwargs:** This is an optional argument. This dictionary allows for specifying keyword arguments for pairing methods.
93+
First level should be the observation type (e.g. "sat_grid_clm", "sat_swath_clm"). Then under the observation type label provide the specific pairing options for your application.
94+
95+
* **apply_ak:** This is an optional argument used for pairing of satellite data. When no pairing keyword arguments are specified it will default to True. This should be set to True when application of satellite averaging kernels or apriori data to model observations is desired.
96+
* **mod_to_overpass:** This is an optional argument used for pairing of satellite data. When set to True the model data will be pre-processed to the published local overpass time for the satellite. As of now, local overpass times are hard-wired.
97+
9298
Models
9399
------
94100
All input for each instance of the model class. First level should be the model
@@ -120,9 +126,12 @@ data (e.g., surf_only: True).
120126
Typically this is set at the horizontal resolution of your model * 1.5. Setting
121127
this to a smaller value will speed up the pairing process.
122128

123-
**apply_ak:** This is an optional argument used for pairing of satellite data. This
124-
should be set to True when application of satellite averaging kernels or apriori data
125-
to model observations is desired.
129+
**apply_ak:** Removed. Instead, specify ``pairing_kwargs`` in the analysis section.
130+
131+
**is_global:** Optional boolean argument to specify if the model dataset is global or
132+
regional. Used in some satellite pairing methods to indicate if a longitude wrap should
133+
be applied. Defaults to False when unspecified and xesmf-based satellite pairing methods
134+
will assume the model datset is regional.
126135

127136
**mapping:** This is the mapping dictionary for all variables to be plotted.
128137
For each observational dataset, add a mapping dictionary where the model
@@ -204,6 +213,8 @@ See :doc:`../getting_started/downloading_obs` for more details.
204213
**obs_type:** The observation type. Options are: "pt_sfc" or point surface. Adding
205214
options for Aircraft and Satellite observations are under development.
206215

216+
**sat_type:** The satellite observation type. Options include: "mopitt_l3", "omps_l3", "omps_nm", "modis_l2", and "tropomi_l2_no2". Additional options are under development.
217+
207218
**data_proc:** This section stores all of the data processing information.
208219

209220
* **filter_dict:** This is a dictionary used to filter the observation data

docs/develop/development_team.rst

+2-2
Original file line numberDiff line numberDiff line change
@@ -52,8 +52,8 @@ in regional coupled chemistry-meteorology models. My plans include testing and
5252
expanding the capability of MELODIES MONET for evaluating simulations with
5353
research and operational models of fire impacts on air quality and weather.
5454

55-
**Margaret Bruckner:**
56-
I am a graduate student at the University of Wisconsin-Madison. My development plans
55+
**Maggie Bruckner:**
56+
I am an NRC postdoc associated with NOAA CSL. My development plans
5757
primarily focus on adding capabilities for comparison of satellite observations to model
5858
output and expanding data processing options.
5959

examples/jupyter_notebooks/UFS-AQM_mopitt_example.ipynb

+295
Large diffs are not rendered by default.
+79
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
analysis:
2+
start_time: '2023-06-27-00:00:00'
3+
end_time: '2023-06-29-00:59:00'
4+
output_dir: /scratch1/BMC/rcm2/mbruckner/mopitt/ufs_aqm_analysis/plots
5+
debug: False
6+
pairing_kwargs:
7+
sat_grid_clm:
8+
apply_ak: True
9+
mod_to_overpass: True
10+
model:
11+
ufsaqm:
12+
files: '/scratch1/BMC/rcm2/rhs/monet_example/AEROMMA/UFS-AQM/cmaq54_OriRave1/aqm.202306*/12/aqm.t12z.dyn.*.nc'
13+
mod_type: 'rrfs'
14+
is_global: False
15+
mapping:
16+
mopitt:
17+
co: column
18+
plot_kwargs:
19+
color: 'purple'
20+
marker: '^'
21+
linestyle: 'dotted'
22+
obs:
23+
mopitt:
24+
filename: /scratch1/BMC/rcm2/mbruckner/mopitt/v009_daily/*he5
25+
obs_type: sat_grid_clm
26+
sat_type: mopitt_l3
27+
variables:
28+
column:
29+
ylabel_plot: '$10^{18} molec/cm^{2}$'
30+
vmin_plot: 0.0 # optMin for y-axis during plotting. To apply to a plot, change restrict_yaxis = True.
31+
vmax_plot: 16
32+
vdiff_plot: 15.0
33+
plots:
34+
#plot_ts:
35+
# type: 'timeseries'
36+
# fig_kwargs:
37+
# figsize: [12,6]
38+
# default_plot_kwargs: # Opt to define defaults for all plots. Model kwargs overwrite these.
39+
# linewidth: 2.0
40+
# markersize: 10.
41+
# text_kwargs: #Opt
42+
# fontsize: 18.
43+
# domain_type: ['all']
44+
# domain_name: ['CONUS']
45+
# data: ['mopitt_ufsaqm']
46+
# data_proc:
47+
# rem_obs_nan: True # True: Remove all points where model or obs variable is NaN. False: Remove only points where model variable is NaN.
48+
# ts_select_time: 'time' #Time used for avg and plotting: Options: 'time' for UTC or 'time_local'
49+
# ts_avg_window: 'h' # Options: None for no averaging or list pandas resample rule (e.g., 'h', 'D')
50+
# set_axis: False #If select True, add vmin_plot and vmax_plot for each variable in obs.
51+
plot_grp1:
52+
type: 'gridded_spatial_bias'
53+
fig_kwargs:
54+
states: True
55+
figsize: [10,5]
56+
text_kwargs:
57+
fontsize: 16.
58+
domain_type: ['all']
59+
domain_name: ['UFS-AQM']
60+
data: ['mopitt_ufsaqm']
61+
data_proc:
62+
rem_obs_nan: True
63+
set_axis: True
64+
#plot_grp2:
65+
# type: 'taylor'
66+
# fig_kwargs:
67+
# figsize: [8.8]
68+
# default_plot_kwargs:
69+
# linewidth: 2.0
70+
# markersize: 10.
71+
# text_kwargs:
72+
# fontsize: 16.
73+
# domain_type: ['all'] #List of domain types: 'all' or any domain in obs file. (e.g., airnow: epa_region, state_name, siteid, etc.)
74+
# domain_name: ['Global'] # of domain names. If domain_type = all domain_name is used in plot title.
75+
# data: ['mopitt_ufsaqm'] # make this a list of pairs in obs_model where the obs is the obs label and model is the model_label
76+
# data_proc:
77+
# rem_obs_nan: True # True: Remove all points where model or obs variable is NaN. False: Remove only points where model variable is NaN.
78+
# set_axis: True #If select True, add ty_scale for each variable in obs.
79+

melodies_monet/driver.py

+44-10
Original file line numberDiff line numberDiff line change
@@ -247,7 +247,7 @@ def open_sat_obs(self, time_interval=None, control_dict=None):
247247
None
248248
"""
249249
from .util import time_interval_subset as tsub
250-
250+
from glob import glob
251251
try:
252252
if self.sat_type == 'omps_l3':
253253
print('Reading OMPS L3')
@@ -275,6 +275,13 @@ def open_sat_obs(self, time_interval=None, control_dict=None):
275275
else: flst = self.file
276276
self.obj = mio.sat._mopitt_l3_mm.open_dataset(flst, ['column','pressure_surf','apriori_col',
277277
'apriori_surf','apriori_prof','ak_col'])
278+
279+
# Determine if monthly or daily product and set as attribute
280+
if any(mtype in glob(self.file)[0] for mtype in ('MOP03JM','MOP03NM','MOP03TM')):
281+
self.obj.attrs['monthly'] = True
282+
else:
283+
self.obj.attrs['monthly'] = False
284+
278285
elif self.sat_type == 'modis_l2':
279286
# from monetio import modis_l2
280287
print('Reading MODIS L2')
@@ -427,7 +434,7 @@ class model:
427434
def __init__(self):
428435
"""Initialize a :class:`model` object."""
429436
self.model = None
430-
self.apply_ak = False
437+
self.is_global = False
431438
self.radius_of_influence = None
432439
self.mod_kwargs = {}
433440
self.file_str = None
@@ -716,6 +723,8 @@ def __init__(self):
716723
self.obs_gridded_dataset = None
717724
self.add_logo = True
718725
"""bool, default=True : Add the MELODIES MONET logo to the plots."""
726+
self.pairing_kwargs = {}
727+
719728

720729
def __repr__(self):
721730
return (
@@ -809,6 +818,10 @@ def read_control(self, control=None):
809818
= [[time_stamps[n], time_stamps[n+1]]
810819
for n in range(len(time_stamps)-1)]
811820

821+
# specific arguments for pairing options
822+
if 'pairing_kwargs' in self.control_dict['analysis'].keys():
823+
self.pairing_kwargs = self.control_dict['analysis']['pairing_kwargs']
824+
812825
# Enable Dask progress bars? (default: false)
813826
enable_dask_progress_bars = self.control_dict["analysis"].get(
814827
"enable_dask_progress_bars", False)
@@ -911,8 +924,8 @@ def open_models(self, time_interval=None,load_files=True):
911924
# this is the model type (ie cmaq, rapchem, gsdchem etc)
912925
m.model = self.control_dict['model'][mod]['mod_type']
913926
# set the model label in the dictionary and model class instance
914-
if "apply_ak" in self.control_dict['model'][mod].keys():
915-
m.apply_ak = self.control_dict['model'][mod]['apply_ak']
927+
if "is_global" in self.control_dict['model'][mod].keys():
928+
m.is_global = self.control_dict['model'][mod]['is_global']
916929
if 'radius_of_influence' in self.control_dict['model'][mod].keys():
917930
m.radius_of_influence = self.control_dict['model'][mod]['radius_of_influence']
918931
else:
@@ -1330,6 +1343,12 @@ def pair_data(self, time_interval=None):
13301343
# TODO: add other network types / data types where (ie flight, satellite etc)
13311344
# if sat_swath_clm (satellite l2 column products)
13321345
elif obs.obs_type.lower() == 'sat_swath_clm':
1346+
# grab kwargs for pairing. Use default if not specified
1347+
pairing_kws = {'apply_ak':True,'mod_to_overpass':False}
1348+
for key in self.pairing_kwargs[obs.obs_type.lower()]:
1349+
pairing_kws[key] = self.pairing_kwargs[obs.obs_type.lower()][key]
1350+
if 'apply_ak' not in self.pairing_kwargs[obs.obs_type.lower()]:
1351+
print('WARNING: The satellite pairing option apply_ak is being set to True because it was not specified in the YAML. Pairing will fail if there is no AK available.')
13331352

13341353
if obs.sat_type == 'omps_nm':
13351354

@@ -1340,7 +1359,7 @@ def pair_data(self, time_interval=None):
13401359
if 'time' in obs.obj.dims:
13411360
obs.obj = obs.obj.sel(time=slice(self.start_time,self.end_time))
13421361
obs.obj = obs.obj.swap_dims({'time':'x'})
1343-
if mod.apply_ak == True:
1362+
if pairing_kws['apply_ak'] == True:
13441363
model_obj = mod.obj[keys+['pres_pa_mid','surfpres_pa']]
13451364

13461365
paired_data = sutil.omps_nm_pairing_apriori(model_obj,obs.obj,keys)
@@ -1370,7 +1389,7 @@ def pair_data(self, time_interval=None):
13701389
model_obj = mutil.cal_model_no2columns(model_obj)
13711390
#obs_dat = obs.obj.sel(time=slice(self.start_time.date(),self.end_time.date())).copy()
13721391

1373-
if mod.apply_ak == True:
1392+
if pairing_kws['apply_ak'] == True:
13741393
paired_data = sutil.trp_interp_swatogrd_ak(obs.obj, model_obj)
13751394
else:
13761395
paired_data = sutil.trp_interp_swatogrd(obs.obj, model_obj)
@@ -1394,6 +1413,12 @@ def pair_data(self, time_interval=None):
13941413

13951414
# if sat_grid_clm (satellite l3 column products)
13961415
elif obs.obs_type.lower() == 'sat_grid_clm':
1416+
# grab kwargs for pairing. Use default if not specified
1417+
pairing_kws = {'apply_ak':True,'mod_to_overpass':False}
1418+
for key in self.pairing_kwargs[obs.obs_type.lower()]:
1419+
pairing_kws[key] = self.pairing_kwargs[obs.obs_type.lower()][key]
1420+
if 'apply_ak' not in self.pairing_kwargs[obs.obs_type.lower()]:
1421+
print('WARNING: The satellite pairing option apply_ak is being set to True because it was not specified in the YAML. Pairing will fail if there is no AK available.')
13971422
if len(keys) > 1:
13981423
print('Caution: More than 1 variable is included in mapping keys.')
13991424
print('Pairing code is calculating a column for {}'.format(keys[0]))
@@ -1416,13 +1441,22 @@ def pair_data(self, time_interval=None):
14161441

14171442
elif obs.sat_type == 'mopitt_l3':
14181443
from .util import satellite_utilities as sutil
1419-
if mod.apply_ak:
1444+
1445+
if pairing_kws['apply_ak']:
14201446
model_obj = mod.obj[keys+['pres_pa_mid']]
14211447
# trim to only data within analysis window, as averaging kernels can't be applied outside it
1422-
obs_dat = obs.obj.sel(time=slice(self.start_time.date(),self.end_time.date()))#.copy()
1423-
model_obj = model_obj.sel(time=slice(self.start_time.date(),self.end_time.date()))#.copy()
1448+
obs_dat = obs.obj.sel(time=slice(self.start_time.date(),self.end_time.date()))
1449+
model_obj = model_obj.sel(time=slice(self.start_time.date(),self.end_time.date()))
1450+
1451+
# Sample model to observation overpass time
1452+
if pairing_kws['mod_to_overpass']:
1453+
print('sampling model to 10:30 local overpass time')
1454+
overpass_datetime = pd.date_range(self.start_time.replace(hour=10,minute=30),
1455+
self.end_time.replace(hour=10,minute=30),freq='D')
1456+
model_obj = sutil.mod_to_overpasstime(model_obj,overpass_datetime)
1457+
14241458
# interpolate model to observation, calculate column with averaging kernels applied
1425-
paired = sutil.mopitt_l3_pairing(model_obj,obs_dat,keys[0])
1459+
paired = sutil.mopitt_l3_pairing(model_obj,obs_dat,keys[0],global_model=mod.is_global)
14261460
p = pair()
14271461
p.type = obs.obs_type
14281462
p.obs = obs.label

melodies_monet/util/sat_l2_swath_utility.py

+1-3
Original file line numberDiff line numberDiff line change
@@ -291,18 +291,16 @@ def cal_amf_wrfchem(scatw, wrfpreslayer, tpreslev, troppres, wrfno2layer_molec,
291291
vertical_pres = []
292292
vertical_scatw = []
293293
vertical_wrfp = []
294-
294+
295295
for llb in range(len(lb[0])):
296296
yy = lb[0][llb]
297297
xx = lb[1][llb]
298298
vertical_pres = tpreslev[:,yy,xx] # mli, update to new dimension
299299
vertical_scatw = scatw[yy,xx,:]
300300
vertical_wrfp = wrfpreslayer[yy,xx,:]
301-
302301
f = interpolate.interp1d(np.log10(vertical_pres[:]),vertical_scatw[:], fill_value="extrapolate")# relationship between pressure to avk
303302
wrfavk[yy,xx,:] = f(np.log10(vertical_wrfp[:])) #wrf-chem averaging kernel
304303

305-
306304
for l in range(nz-1):
307305
# check if it's within tropopause
308306
preminus[:,:] = wrfpreslayer[:,:,l] - troppres[:,:]

0 commit comments

Comments
 (0)