12 from datetime
import datetime, timedelta
15 from pathlib
import Path
18 import dateutil.parser
22 IODA_CONV_PATH = Path(__file__).parent/
"@SCRIPT_LIB_PATH@"
23 if not IODA_CONV_PATH.is_dir():
24 IODA_CONV_PATH = Path(__file__).parent/
'..'/
'lib-python'
25 sys.path.append(
str(IODA_CONV_PATH.resolve()))
27 import ioda_conv_ncio
as iconv
31 VAR_NAME =
"brightness_temperature"
34 (
"latitude",
"float"),
35 (
"longitude",
"float"),
36 (
"datetime",
"string")
40 LOC_MDATA_MAP = {
'latitude':
'IRLW_Latitude',
41 'longitude':
'IRLW_Longitude',
42 'solar_zenith_angle':
'IRLW_SolarZenith',
43 'solar_azimuth_angle':
'IRLW_SolarAzimuth',
44 'sensor_zenith_angle':
'IRLW_SatelliteZenith',
45 'sensor_view_angle':
'IRLW_SatelliteZenith',
46 'sensor_azimuth_angle':
'IRLW_SatelliteAzimuth',
47 'cloud_fraction':
'Cloud_Fraction'}
55 center_datetime: Window center (type: datetime or string representation)
56 out_file: Name of IODA file to write (type: str or Path)
58 if isinstance(center_datetime, str):
59 center_datetime = datetime.strptime(center_datetime,
'%Y%m%d%H')
64 self.
datadata = collections.defaultdict(dict)
66 self.
LocMdataLocMdata = collections.defaultdict(dict)
67 self.
VarMdataVarMdata = collections.defaultdict(dict)
68 self.
AttrDataAttrData = collections.defaultdict(dict)
114 self.
writerwriter._nlocs = 0
115 self.
writerwriter._nrecs = 1
117 for f
in (ff.strip()
for ff
in filenames):
118 with nc.Dataset(f,
'r')
as ncd:
121 sat_zen = ncd.variables[
'IRLW_SatelliteZenith'][:]
122 except KeyError
as e:
123 print(f
"[GIIRS2IODA] Processing {totalFiles}/{len(filenames)} Missing metadata variables. Skipping. -- {Path(f).name} ")
126 nlocs = np.count_nonzero(mask)
128 print(f
"[GIIRS2IODA] Processing {totalFiles}/{len(filenames)} NO VALID OBS. Skipping. -- {Path(f).name} ")
131 print(f
"[GIIRS2IODA] Processing {totalFiles}/{len(filenames)} #locs:{nlocs} -- {Path(f).name}")
136 nchans = ncd.dimensions[
"LWchannel"].size
137 self.
writerwriter._nvars = nchans
141 LW_wnum = np.asarray(ncd.variables[
"LW_wnum"])
142 K1 = LW_wnum*100*self.
_K1_K1
143 K1 = K1.astype(np.float32)[:, np.newaxis]
144 K3 = (1e5*self.
_K2_K2*(LW_wnum*100)**3.).astype(np.float32)
145 K3 = K3.astype(np.float32)[:, np.newaxis]
148 ndetectors = ncd.dimensions[
"LWdetector"].size
149 detector_array = np.arange(1, ndetectors+1, dtype=np.int32)
155 obs_vals = np.full((nchans, self.
nlocs_maxnlocs_max), nc.default_fillvals[
'f4'], dtype=np.float32, order=
'F')
157 self.
LocMdataLocMdata[
'scan_position'] = np.full(self.
nlocs_maxnlocs_max, nc.default_fillvals[
'f4'], dtype=np.float32)
160 self.
VarMdataVarMdata[
'channel_wavenumber'] = self.
writerwriter.FillNcVector(LW_wnum,
'float')
161 self.
VarMdataVarMdata[
'channel_number'] = self.
writerwriter.FillNcVector(np.arange(1, nchans+1, dtype=np.int32),
'integer')
162 self.
units_valuesunits_values[
'channel_wavenumber'] = ncd.variables[
'LW_wnum'].getncattr(
'units')
164 for new_key, old_key
in self.
LOC_MDATA_MAPLOC_MDATA_MAP.items():
168 this_nchans = ncd.dimensions[
"LWchannel"].size
169 this_ndetectors = ncd.dimensions[
"LWdetector"].size
170 if this_nchans != nchans:
171 raise RuntimeError(f
"Number of channels:{this_nchans} does not match with initial value channels:{nchans}")
172 if this_ndetectors != ndetectors:
173 raise RuntimeError(f
"Number of detectors:{this_ndetectors} does not match with initial value channels:{ndetectors}")
178 obsDate = ncd.getncattr(
"Observing Beginning Date").split(
"-")
179 obsTime = ncd.getncattr(
"Observing Beginning Time").split(
":")
180 obsSeconds = timedelta(seconds=
int(round(float(obsTime[2]))))
181 obsDateTime = datetime(year=
int(obsDate[0]), month=
int(obsDate[1]),
182 day=
int(obsDate[2]), hour=
int(obsTime[0]),
183 minute=
int(obsTime[1])) + obsSeconds
184 obsDtimeString = obsDateTime.strftime(
"%Y-%m-%dT%H:%M:%SZ")
186 meta_datetime.extend([obsDtimeString]*nlocs)
189 for new_key, old_key
in self.
LOC_MDATA_MAPLOC_MDATA_MAP.items():
190 if old_key
in ncd.variables:
191 if new_key
not in self.
LocMdataLocMdata:
194 self.
LocMdataLocMdata[new_key][nlocs_tot:nlocs_tot+nlocs] = np.asarray(ncd.variables[old_key])[mask]
195 self.
LocMdataLocMdata[
'scan_position'][nlocs_tot:nlocs_tot+nlocs] = detector_array[mask]
199 ncVar = ncd.variables[
'ES_RealLW']
200 lwRad = np.array(ncVar[:, mask])
202 lwRadiance_apo = np.copy(lwRad)
203 lwRadiance_apo[1:-1, :] = 0.23*lwRad[:-2, :] + 0.54*lwRad[1:-1, :] + 0.23*lwRad[2:, :]
204 valid = (lwRadiance_apo > 0) & (lwRadiance_apo < 300)
206 shape = (nchans, nlocs)
207 np.place(obs_vals[:, nlocs_tot:nlocs_tot+nlocs], valid,
208 np.broadcast_to(K1, shape)[valid] / np.log1p(np.broadcast_to(K3, shape)[valid]/lwRadiance_apo[valid]))
213 self.
writerwriter._nlocs = nlocs_tot
214 self.
LocMdataLocMdata[
'datetime'] = self.
writerwriter.FillNcVector(meta_datetime,
'datetime')
215 self.
units_valuesunits_values[
'datetime'] =
'ISO 8601 format'
216 self.
LocMdataLocMdata = {k: v[:nlocs_tot]
for k, v
in self.
LocMdataLocMdata.items()}
217 for ivar
in range(nchans):
218 varname = f
"brightness_temperature_{ivar+1}"
220 self.
datadata[(varname,
'ObsValue')] = self.
writerwriter.FillNcVector(obs_vals[ivar, :nlocs_tot],
'float')
221 self.
datadata[(varname,
'PreQC')] = self.
writerwriter.FillNcVector(np.full(nlocs_tot, 0, dtype=np.int32),
'integer')
222 self.
datadata[(varname,
'ObsError')] = self.
writerwriter.FillNcVector(np.full(nlocs_tot, 2., dtype=np.float32),
'float')
223 print(f
"[GIIRS2IODA] Processed {nlocs_tot} total locs from {succFiles}/{totalFiles} valid files.")
231 _H_PLANCK = 6.62606957 * 1e-34
232 _K_BOLTZMANN = 1.3806488 * 1e-23
233 _C_SPEED = 2.99792458 * 1e8
234 _K1 = _H_PLANCK * _C_SPEED / _K_BOLTZMANN
235 _K2 = 2 * _H_PLANCK * _C_SPEED**2
240 K3 = cls.
_K2_K2 * wavnum**3
241 bt = cls.
_K1_K1 * wavnum / np.log1p(K3/radiance)
246 Common initialization for LocMdata metadata for new variable names
247 new_key - IODA variable name
248 old_key - GIIRS SSEC NetCDF variable name
249 ncd - GIIRS NetCDF scan file
251 if old_key
in ncd.variables:
252 self.
LocMdataLocMdata[new_key] = np.full(self.
nlocs_maxnlocs_max, nc.default_fillvals[
'f4'], dtype=np.float32)
253 if 'units' in ncd.variables[old_key].ncattrs():
254 self.
units_valuesunits_values[new_key] = ncd.variables[old_key].getncattr(
'units')
258 parser = argparse.ArgumentParser(
260 'Read SSEC GIIRS long wave radiance file(s) and convert'
261 ' to a concatenated IODA formatted output file converting radiance'
262 ' to brightness-temperature units.')
264 required = parser.add_argument_group(title=
'required arguments')
265 required.add_argument(
267 help=
"file name of giirs input file(s)",
268 type=str, nargs=
'+', required=
True)
269 required.add_argument(
271 help=
"path to ioda output file",
272 type=str, required=
True)
273 required.add_argument(
275 help=
"base date for the center of the window",
276 metavar=
"YYYYMMDDHH", type=str, required=
True)
277 args = parser.parse_args()
280 conv.readInFiles(args.input)
284 if __name__ ==
'__main__':
def readInFiles(self, filenames)
def __init__(self, center_datetime, out_file)
def _initialize_metadata_key(self, new_key, old_key, ncd)
def _rad2bt(cls, wavnum, radiance)