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 "sea_surface_temperature",
30 "sea_surface_skin_temperature"]
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')
54 time_base = ncd.variables[
'time'][:]
55 basetime = dateutil.parser.parse(ncd.variables[
'time'].units[-20:])
59 for v
in (
'platform',
'sensor',
'processing_level'):
60 attr_data[v] = ncd.getncattr(v)
66 data_in[
'quality_level'] = ncd.variables[
'quality_level'][:].ravel()
67 mask = data_in[
'quality_level'] >= 0
68 data_in[
'quality_level'] = data_in[
'quality_level'][mask]
73 lons = ncd.variables[
'lon'][:].ravel()
74 lats = ncd.variables[
'lat'][:].ravel()
75 if attr_data[
'processing_level'][:2] ==
'L3':
76 len_grid = len(lons)*len(lats)
77 lons, lats = np.meshgrid(lons, lats, copy=
False)
78 lons = np.tile(lons.ravel(), len(time_base)).ravel()[mask]
79 lats = np.tile(lats.ravel(), len(time_base)).ravel()[mask]
82 lons = np.tile(lons, len(time_base)).ravel()[mask]
83 lats = np.tile(lats, len(time_base)).ravel()[mask]
86 time = np.tile(np.atleast_2d(time_base).T, (1, len_grid)).ravel()[mask]
93 'sses_standard_deviation',
94 'sea_surface_temperature')
97 data_in[v] = ncd.variables[v][:].ravel()[mask]
102 int((global_config[
'date']-datetime(1970, 1, 1)).total_seconds()))
103 mask = np.random.uniform(size=len(lons)) > global_config[
'thin']
108 if np.ma.is_masked(data_in[v]):
109 mask = np.logical_and(mask, np.logical_not(data_in[v].mask))
116 data_in[v] = data_in[v][mask]
120 for i
in range(len(lons)):
121 obs_date = basetime + \
122 timedelta(seconds=float(time[i]+data_in[
'sst_dtime'][i]))
123 dates.append(obs_date.strftime(
"%Y-%m-%dT%H:%M:%SZ"))
130 val_sst_skin = data_in[
'sea_surface_temperature'] - 273.15
131 val_sst = val_sst_skin - data_in[
'sses_bias']
132 err = data_in[
'sses_standard_deviation']
133 qc = 5 - data_in[
'quality_level']
137 obs_dim = (len(lons))
139 if global_config[
'output_sst']:
140 obs_data[(output_var_names[0], global_config[
'oval_name'])] = np.zeros(obs_dim),
141 obs_data[(output_var_names[0], global_config[
'oerr_name'])] = np.zeros(obs_dim),
142 obs_data[(output_var_names[0], global_config[
'opqc_name'])] = np.zeros(obs_dim),
144 if global_config[
'output_skin_sst']:
145 obs_data[(output_var_names[1], global_config[
'oval_name'])] = np.zeros(obs_dim),
146 obs_data[(output_var_names[1], global_config[
'oerr_name'])] = np.zeros(obs_dim),
147 obs_data[(output_var_names[1], global_config[
'opqc_name'])] = np.zeros(obs_dim),
156 if global_config[
'output_sst']:
157 obs_data[(output_var_names[0], global_config[
'oval_name'])] = val_sst
158 obs_data[(output_var_names[0], global_config[
'oerr_name'])] = err
159 obs_data[(output_var_names[0], global_config[
'opqc_name'])] = qc
160 if global_config[
'output_skin_sst']:
161 obs_data[(output_var_names[1], global_config[
'oval_name'])] = val_sst_skin
162 obs_data[(output_var_names[1], global_config[
'oerr_name'])] = err
163 obs_data[(output_var_names[1], global_config[
'opqc_name'])] = qc
165 return (obs_data, loc_data, attr_data)
171 parser = argparse.ArgumentParser(
173 'Reads the sea surface temperature from any GHRRST Data '
174 ' Specification (GDS2.0) formatted L2 or L3 file(s) and converts'
175 ' into IODA formatted output files. Multiple files are'
176 ' concatenated and optional thinning can be performed.')
179 required = parser.add_argument_group(title=
'required arguments')
180 required.add_argument(
182 help=
"path of GHRSST GDS2.0 SST observation input file(s)",
183 type=str, nargs=
'+', required=
True)
184 required.add_argument(
186 help=
"path of IODA output file",
187 type=str, required=
True)
188 required.add_argument(
190 metavar=
"YYYYMMDDHH",
191 help=
"base date for the center of the window",
192 type=str, required=
True)
194 optional = parser.add_argument_group(title=
'optional arguments')
195 optional.add_argument(
197 help=
"percentage of random thinning, from 0.0 to 1.0. Zero indicates"
198 " no thinning is performed. (default: %(default)s)",
199 type=float, default=0.0)
200 optional.add_argument(
202 help=
'multiple threads can be used to load input files in parallel.'
203 ' (default: %(default)s)',
205 optional.add_argument(
207 help=
'if set, only the bias corrected bulk sst is output.'
208 ' Otherwise, bulk sst, and skin sst are both output.',
210 optional.add_argument(
212 help=
'if set, only the skin or subskin sst is output.'
213 ' Otherwise, bulk sst, and skin sst are both output.',
216 args = parser.parse_args()
217 args.date = datetime.strptime(args.date,
'%Y%m%d%H')
218 if not args.sst
and not args.skin_sst:
223 writer = iconv.NcWriter(args.output, [], [])
230 global_config[
'date'] = args.date
231 global_config[
'thin'] = args.thin
232 global_config[
'oval_name'] = writer.OvalName()
233 global_config[
'oerr_name'] = writer.OerrName()
234 global_config[
'opqc_name'] = writer.OqcName()
235 global_config[
'output_sst'] = args.sst
236 global_config[
'output_skin_sst'] = args.skin_sst
237 pool_inputs = [(i, global_config)
for i
in args.input]
240 pool = Pool(args.threads)
241 obs = pool.map(read_input, pool_inputs)
244 obs_data, loc_data, attr_data = obs[0]
245 loc_data[
'datetime'] = writer.FillNcVector(
246 loc_data[
'datetime'],
"datetime")
247 for i
in range(1, len(obs)):
249 axis = len(obs[i][0][k].shape)-1
250 obs_data[k] = np.concatenate(
251 (obs_data[k], obs[i][0][k]), axis=axis)
255 d = writer.FillNcVector(d,
'datetime')
256 loc_data[k] = np.concatenate((loc_data[k], d), axis=0)
260 attr_data[
'date_time_string'] = args.date.strftime(
"%Y-%m-%dT%H:%M:%SZ")
261 attr_data[
'thinning'] = args.thin
262 attr_data[
'converter'] = os.path.basename(__file__)
267 selected_names.append(output_var_names[0])
269 selected_names.append(output_var_names[1])
270 var_data = {writer._var_list_name: writer.FillNcVector(
271 selected_names,
"string")}
275 writer._nvars = len(selected_names)
276 writer._nlocs = obs_data[(selected_names[0],
'ObsValue')].shape[0]
279 writer.BuildNetcdf(obs_data, loc_data, var_data, attr_data)
282 if __name__ ==
'__main__':
def read_input(input_args)