10 from __future__
import print_function
14 from datetime
import datetime, timedelta
15 import dateutil.parser
17 from multiprocessing
import Pool
19 from pathlib
import Path
21 IODA_CONV_PATH = Path(__file__).parent/
"@SCRIPT_LIB_PATH@"
22 if not IODA_CONV_PATH.is_dir():
23 IODA_CONV_PATH = Path(__file__).parent/
'..'/
'lib-python'
24 sys.path.append(
str(IODA_CONV_PATH.resolve()))
26 import ioda_conv_ncio
as iconv
29 "ocean_mass_content_of_particulate_organic_matter_expressed_as_carbon",
30 "mass_concentration_of_chlorophyll_in_sea_water"]
35 Reads/converts a single input file, performing optional thinning also.
39 input_args: A tuple of input filename and global_config
40 input_file: The name of file to read
41 global_config: structure for global arguments related to read
45 A tuple of (obs_data, loc_data, attr_data) needed by the IODA writer.
47 input_file = input_args[0]
48 global_config = input_args[1]
50 print(
"Reading ", input_file)
51 ncd = nc.Dataset(input_file,
'r')
55 for v
in (
'platform',
'instrument',
'processing_level'):
56 attr_data[v] = ncd.getncattr(v)
61 gpd = ncd.groups[
'geophysical_data']
63 data_in[
'l2_flags'] = gpd.variables[
'l2_flags'][:].ravel()
64 mask = data_in[
'l2_flags'] < 1073741824
65 data_in[
'l2_flags'] = data_in[
'l2_flags'][mask]
68 ngd = ncd.groups[
'navigation_data']
69 number_of_lines = len(ngd.variables[
'longitude'][:, 1])
70 pixels_per_line = len(ngd.variables[
'longitude'][1, :])
71 lons = ngd.variables[
'longitude'][:].ravel()[mask]
72 lats = ngd.variables[
'latitude'][:].ravel()[mask]
75 sla = ncd.groups[
'scan_line_attributes']
76 basetime = datetime(sla.variables[
'year'][0], 1, 1) + \
77 timedelta(days=
int(sla.variables[
'day'][0]-1),
78 milliseconds=
int(sla.variables[
'msec'][0]))
79 time = (np.repeat(sla.variables[
'msec'][:].ravel(),
80 pixels_per_line).ravel() - sla.variables[
'msec'][0])/1000.0
81 data_in[
'time'] = time[mask]
84 input_vars = (
'poc',
'chlor_a')
87 data_in[v] = gpd.variables[v][:].ravel()[mask]
92 int((global_config[
'date']-datetime(1970, 1, 1)).total_seconds()))
93 mask = np.random.uniform(size=len(lons)) > global_config[
'thin']
98 data_in[
'time'] = data_in[
'time'][mask]
99 data_in[
'l2_flags'] = data_in[
'l2_flags'][mask]
101 data_in[v] = data_in[v][mask]
105 for i
in range(len(lons)):
106 obs_date = basetime + timedelta(seconds=float(data_in[
'time'][i]))
107 dates.append(obs_date.strftime(
"%Y-%m-%dT%H:%M:%SZ"))
111 obs_dim = (len(lons))
113 if global_config[
'output_poc']:
114 obs_data[(output_var_names[0], global_config[
'oval_name'])] = \
116 obs_data[(output_var_names[0], global_config[
'oerr_name'])] = \
118 obs_data[(output_var_names[0], global_config[
'opqc_name'])] = \
121 if global_config[
'output_chl']:
122 obs_data[(output_var_names[1], global_config[
'oval_name'])] = \
124 obs_data[(output_var_names[1], global_config[
'oerr_name'])] = \
126 obs_data[(output_var_names[1], global_config[
'opqc_name'])] = \
136 if global_config[
'output_poc']:
137 obs_data[(output_var_names[0], global_config[
'oval_name'])] = \
139 obs_data[(output_var_names[0], global_config[
'oerr_name'])] = \
141 obs_data[(output_var_names[0], global_config[
'opqc_name'])] = \
143 if global_config[
'output_chl']:
144 obs_data[(output_var_names[1], global_config[
'oval_name'])] = \
146 obs_data[(output_var_names[1], global_config[
'oerr_name'])] = \
147 data_in[
'chlor_a']*0.0
148 obs_data[(output_var_names[1], global_config[
'opqc_name'])] = \
151 return (obs_data, loc_data, attr_data)
157 parser = argparse.ArgumentParser(
159 'Reads the particulate organic carbon and chlorophyll data from'
160 ' VIIRS/MODIS Specification formatted L2 file(s) and converts'
161 ' into IODA formatted output files. Multiple files are'
162 ' concatenated and optional thinning can be performed.')
165 required = parser.add_argument_group(title=
'required arguments')
166 required.add_argument(
168 help=
"path of VIIRS/MODIS L2 Ocean Color observation input file(s)",
169 type=str, nargs=
'+', required=
True)
170 required.add_argument(
172 help=
"path of IODA output file",
173 type=str, required=
True)
174 required.add_argument(
176 metavar=
"YYYYMMDDHH",
177 help=
"base date for the center of the window",
178 type=str, required=
True)
180 optional = parser.add_argument_group(title=
'optional arguments')
181 optional.add_argument(
183 help=
"percentage of random thinning, from 0.0 to 1.0. Zero indicates"
184 " no thinning is performed. (default: %(default)s)",
185 type=float, default=0.0)
186 optional.add_argument(
188 help=
'multiple threads can be used to load input files in parallel.'
189 ' (default: %(default)s)',
191 optional.add_argument(
193 help=
'if set, only poc is output.'
194 ' Otherwise, poc and chlor_a are both output.',
196 optional.add_argument(
198 help=
'if set, only chlor_a (OCI algorithm) is output.'
199 ' Otherwise, poc and chlor_a are both output.',
202 args = parser.parse_args()
203 args.date = datetime.strptime(args.date,
'%Y%m%d%H')
204 if not args.chl
and not args.poc:
209 writer = iconv.NcWriter(args.output, [], [])
216 global_config[
'date'] = args.date
217 global_config[
'thin'] = args.thin
218 global_config[
'oval_name'] = writer.OvalName()
219 global_config[
'oerr_name'] = writer.OerrName()
220 global_config[
'opqc_name'] = writer.OqcName()
221 global_config[
'output_poc'] = args.poc
222 global_config[
'output_chl'] = args.chl
223 pool_inputs = [(i, global_config)
for i
in args.input]
226 pool = Pool(args.threads)
227 obs = pool.map(read_input, pool_inputs)
230 obs_data, loc_data, attr_data = obs[0]
231 loc_data[
'datetime'] = writer.FillNcVector(
232 loc_data[
'datetime'],
"datetime")
233 for i
in range(1, len(obs)):
235 axis = len(obs[i][0][k].shape)-1
236 obs_data[k] = np.concatenate(
237 (obs_data[k], obs[i][0][k]), axis=axis)
241 d = writer.FillNcVector(d,
'datetime')
242 loc_data[k] = np.concatenate((loc_data[k], d), axis=0)
246 attr_data[
'date_time_string'] = args.date.strftime(
"%Y-%m-%dT%H:%M:%SZ")
247 attr_data[
'thinning'] = args.thin
248 attr_data[
'converter'] = os.path.basename(__file__)
253 selected_names.append(output_var_names[0])
255 selected_names.append(output_var_names[1])
256 var_data = {writer._var_list_name:
257 writer.FillNcVector(selected_names,
"string")}
261 writer._nvars = len(selected_names)
262 writer._nlocs = obs_data[(selected_names[0],
'ObsValue')].shape[0]
265 writer.BuildNetcdf(obs_data, loc_data, var_data, attr_data)
268 if __name__ ==
'__main__':
def read_input(input_args)