14 from datetime
import datetime, timedelta
16 from pathlib
import Path
18 IODA_CONV_PATH = Path(__file__).parent/
"@SCRIPT_LIB_PATH@"
19 if not IODA_CONV_PATH.is_dir():
20 IODA_CONV_PATH = Path(__file__).parent/
'..'/
'lib-python'
21 sys.path.append(
str(IODA_CONV_PATH.resolve()))
23 import ioda_conv_ncio
as iconv
24 from collections
import defaultdict, OrderedDict
25 from orddicts
import DefaultOrderedDict
28 (
"latitude",
"float"),
29 (
"longitude",
"float"),
30 (
"datetime",
"string"),
34 'nitrogendioxide_tropospheric_column':
'nitrogen_dioxide_in_tropospheric_column',
35 'nitrogendioxide_total_column':
'nitrogen_dioxide_in_total_column',
39 'converter': os.path.basename(__file__),
47 self.
varDictvarDict = defaultdict(
lambda: defaultdict(dict))
57 for iodavar
in [
'nitrogen_dioxide_in_tropospheric_column',
'nitrogen_dioxide_in_total_column']:
58 self.
varDictvarDict[iodavar][
'valKey'] = iodavar, self.
writerwriter.OvalName()
59 self.
varDictvarDict[iodavar][
'errKey'] = iodavar, self.
writerwriter.OerrName()
60 self.
varDictvarDict[iodavar][
'qcKey'] = iodavar, self.
writerwriter.OqcName()
61 self.
unitsunits[iodavar] =
'mol m-2'
65 ncd = nc.Dataset(f,
'r')
67 AttrData[
'date_time_string'] = ncd.getncattr(
'time_reference')[0:19]+
'Z'
68 AttrData[
'sensor'] = ncd.getncattr(
'sensor')
69 AttrData[
'platform'] = ncd.getncattr(
'platform')
72 lats = ncd.groups[
'PRODUCT'].variables[
'latitude'][:].ravel()
73 lons = ncd.groups[
'PRODUCT'].variables[
'longitude'][:].ravel()
74 qa_value = ncd.groups[
'PRODUCT'].variables[
'qa_value'][:]
75 times = np.empty_like(qa_value, dtype=object)
76 qa_value = qa_value.ravel()
77 qc_flag = ncd.groups[
'PRODUCT'].groups[
'SUPPORT_DATA'].groups[
'DETAILED_RESULTS']\
78 .variables[
'processing_quality_flags'][:]
79 qc_flag = qc_flag.ravel().astype(
'int32')
80 time1 = ncd.groups[
'PRODUCT'].variables[
'time_utc'][:]
81 for t
in range(len(time1[0])):
82 times[0, t, :] = time1[0, t][0:19]+
'Z'
85 kernel_err = ncd.groups[
'PRODUCT'].\
86 variables[
'nitrogendioxide_tropospheric_column_precision_kernel'][:].ravel()
87 kernel_err_total = ncd.groups[
'PRODUCT'].groups[
'SUPPORT_DATA'].groups[
'DETAILED_RESULTS'].\
88 variables[
'nitrogendioxide_total_column_precision_kernel'][:].ravel()
89 trop_layer = ncd.groups[
'PRODUCT'].variables[
'tm5_tropopause_layer_index'][:].ravel()
90 total_airmass = ncd.groups[
'PRODUCT'].variables[
'air_mass_factor_total'][:].ravel()
91 trop_airmass = ncd.groups[
'PRODUCT'].\
92 variables[
'air_mass_factor_troposphere'][:].ravel()
94 avg_kernel = ncd.groups[
'PRODUCT'].variables[
'averaging_kernel'][:]
95 nlevs = len(avg_kernel[0, 0, 0])
96 AttrData[
'averaging_kernel_levels'] = np.int32(nlevs)
98 self.
loc_mdataloc_mdata[
'datetime'] = self.
writerwriter.FillNcVector(times,
"datetime")
99 self.
loc_mdataloc_mdata[
'latitude'] = lats
100 self.
loc_mdataloc_mdata[
'longitude'] = lons
101 self.
loc_mdataloc_mdata[
'quality_assurance_value'] = qa_value
102 self.
loc_mdataloc_mdata[
'troposphere_layer_index'] = trop_layer
103 self.
loc_mdataloc_mdata[
'air_mass_factor_total'] = total_airmass
104 self.
loc_mdataloc_mdata[
'air_mass_factor_troposphere'] = trop_airmass
105 self.
loc_mdataloc_mdata[
'tropospheric_averaging_kernel_precision'] = kernel_err
106 self.
loc_mdataloc_mdata[
'averaging_kernel_precision'] = kernel_err_total
107 for k
in range(nlevs):
108 varname =
'averaging_kernel_level_'+
str(k+1)
109 self.
loc_mdataloc_mdata[varname] = avg_kernel[..., k].ravel()
111 self.
loc_mdataloc_mdata[
'datetime'] = np.concatenate((self.
loc_mdataloc_mdata[
'datetime'],
112 self.
writerwriter.FillNcVector(times,
"datetime")))
113 self.
loc_mdataloc_mdata[
'latitude'] = np.concatenate((self.
loc_mdataloc_mdata[
'latitude'], lats))
114 self.
loc_mdataloc_mdata[
'longitude'] = np.concatenate((self.
loc_mdataloc_mdata[
'longitude'], lons))
115 self.
loc_mdataloc_mdata[
'quality_assurance_value'] = np.concatenate((
116 self.
loc_mdataloc_mdata[
'quality_assurance_value'], qa_value))
117 self.
loc_mdataloc_mdata[
'troposphere_layer_index'] = np.concatenate((
118 self.
loc_mdataloc_mdata[
'troposphere_layer_index'], trop_layer))
119 self.
loc_mdataloc_mdata[
'air_mass_factor_total'] = np.concatenate((
120 self.
loc_mdataloc_mdata[
'air_mass_factor_total'], total_airmass))
121 self.
loc_mdataloc_mdata[
'air_mass_factor_troposphere'] = np.concatenate((
122 self.
loc_mdataloc_mdata[
'air_mass_factor_troposphere'], trop_airmass))
123 self.
loc_mdataloc_mdata[
'tropospheric_averaging_kernel_precision'] = np.concatenate((
124 self.
loc_mdataloc_mdata[
'tropospheric_averaging_kernel_precision'], kernel_err))
125 self.
loc_mdataloc_mdata[
'averaging_kernel_precision'] = np.concatenate((
126 self.
loc_mdataloc_mdata[
'averaging_kernel_precision'], kernel_err_total))
127 for k
in range(nlevs):
128 varname =
'averaging_kernel_level_'+
str(k+1)
130 avg_kernel[..., k].ravel()))
131 for ncvar, iodavar
in obsvars.items():
132 if ncvar
in [
'nitrogendioxide_tropospheric_column']:
133 data = ncd.groups[
'PRODUCT'].variables[ncvar][:].ravel()
134 err = ncd.groups[
'PRODUCT'].variables[ncvar+
'_precision'][:].ravel()
136 data = ncd.groups[
'PRODUCT'].groups[
'SUPPORT_DATA'].groups[
'DETAILED_RESULTS'].variables[ncvar][:].ravel()
137 err = ncd.groups[
'PRODUCT'].groups[
'SUPPORT_DATA'].groups[
'DETAILED_RESULTS'].variables[ncvar+
'_precision'][:].ravel()
139 self.
outdataoutdata[self.
varDictvarDict[iodavar][
'valKey']] = data
141 self.
outdataoutdata[self.
varDictvarDict[iodavar][
'qcKey']] = qc_flag
143 self.
outdataoutdata[self.
varDictvarDict[iodavar][
'valKey']] = np.concatenate(
144 (self.
outdataoutdata[self.
varDictvarDict[iodavar][
'valKey']], data))
145 self.
outdataoutdata[self.
varDictvarDict[iodavar][
'errKey']] = np.concatenate(
146 (self.
outdataoutdata[self.
varDictvarDict[iodavar][
'errKey']], err))
147 self.
outdataoutdata[self.
varDictvarDict[iodavar][
'qcKey']] = np.concatenate(
148 (self.
outdataoutdata[self.
varDictvarDict[iodavar][
'qcKey']], qc_flag))
150 self.
writerwriter._nvars = len(obsvars)
157 parser = argparse.ArgumentParser(
159 'Reads TROPOMI NO2 netCDF files provided by NESDIS'
160 'and converts into IODA formatted output files. Multiple'
161 'files are able to be concatenated.')
164 required = parser.add_argument_group(title=
'required arguments')
165 required.add_argument(
167 help=
"path of TROPOMI L2 NO2 observation netCDF input file(s)",
168 type=str, nargs=
'+', required=
True)
169 required.add_argument(
171 help=
"path of IODA output file",
172 type=str, required=
True)
174 args = parser.parse_args()
177 writer = iconv.NcWriter(args.output, locationKeyList)
180 no2 =
tropomi(args.input, writer)
183 writer.BuildNetcdf(no2.outdata, no2.loc_mdata, no2.var_mdata, AttrData, no2.units)
186 if __name__ ==
'__main__':
def __init__(self, filenames, writer)