IODA Bundle
godae_trak2ioda.py
Go to the documentation of this file.
1 #!/usr/bin/env python3
2 
3 #
4 # (C) Copyright 2019 UCAR
5 #
6 # This software is licensed under the terms of the Apache Licence Version 2.0
7 # which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
8 #
9 
10 from __future__ import print_function
11 import sys
12 import numpy as np
13 from datetime import datetime
14 from scipy.io import FortranFile
15 from argparse import ArgumentParser, ArgumentDefaultsHelpFormatter
16 from pathlib import Path
17 
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()))
22 
23 import ioda_conv_ncio as iconv
24 from orddicts import DefaultOrderedDict
25 
26 
27 class trak(object):
28 
29  def __init__(self, filename, date):
30 
31  self.filenamefilename = filename
32  self.datedate = date
33 
34  # Read trak data
35  self._rd_trak_rd_trak()
36 
37  return
38 
39  def _rd_trak(self):
40  '''
41  Read the trak data
42  Based on subroutine rd_trak in ocn_obs.f
43  '''
44 
45  try:
46  fh = FortranFile(self.filenamefilename, mode='r', header_dtype='>u4')
47  except IOError:
48  raise IOError('%s file not found!' % self.filenamefilename)
49  except Exception:
50  raise Exception('Unknown error opening %s' % self.filenamefilename)
51 
52  # data is the dictionary with data structure as in ocn_obs.f
53  data = {}
54 
55  data['n_obs'], data['n_lvl'], data['n_vrsn'] = fh.read_ints('>i4')
56 
57  print(' number trak obs: %d' % data['n_obs'])
58  print(' max number levels: %d' % data['n_lvl'])
59  print('file version number: %d' % data['n_vrsn'])
60 
61  if data['n_obs'] <= 0:
62  print('No trak observations to process from %s' % self.filenamefilename)
63  return
64 
65  data['ob_wm'] = fh.read_reals('>i4')
66  data['ob_gsal'] = fh.read_reals('>f4')
67  data['ob_gsst'] = fh.read_reals('>f4')
68  data['ob_lat'] = fh.read_reals('>f4')
69  data['ob_lon'] = fh.read_reals('>f4')
70  data['ob_age'] = fh.read_reals('>f4')
71  data['ob_csal'] = fh.read_reals('>f4')
72  data['ob_csst'] = fh.read_reals('>f4')
73  data['ob_qc_sal'] = fh.read_reals('>f4')
74  data['ob_qc_sst'] = fh.read_reals('>f4')
75  data['ob_qc_vel'] = fh.read_reals('>f4')
76  data['ob_rsal'] = fh.read_reals('>f4')
77  data['ob_rsst'] = fh.read_reals('>f4')
78  data['ob_sal'] = fh.read_reals('>f4')
79  data['ob_sst'] = fh.read_reals('>f4')
80  data['ob_typ'] = fh.read_reals('>i4')
81  data['ob_uuu'] = fh.read_reals('>f4')
82  data['ob_vvv'] = fh.read_reals('>f4')
83  data['ob_dtg'] = fh.read_record('>S12').astype('U12')
84  data['ob_rcpt'] = fh.read_record('>S12').astype('U12')
85  data['ob_scr'] = fh.read_record('>S1').astype('U1')
86  data['ob_sgn'] = fh.read_record('>S6').astype('U6')
87  data['ob_csgm'] = fh.read_reals('>f4')
88  data['ob_gsgm'] = fh.read_reals('>f4')
89  data['ob_rsgm'] = fh.read_reals('>f4')
90 
91  fh.close()
92 
93  self.datadata = data
94 
95  return
96 
97 
98 class IODA(object):
99 
100  def __init__(self, filename, date, varDict, obsList):
101  '''
102  Initialize IODA writer class,
103  transform to IODA data structure and,
104  write out to IODA file.
105  '''
106 
107  self.filenamefilename = filename
108  self.datedate = date
109  self.varDictvarDict = varDict
110 
111  self.locKeyListlocKeyList = [
112  ("latitude", "float"),
113  ("longitude", "float"),
114  ("datetime", "string")
115  ]
116 
117  self.AttrDataAttrData = {
118  'odb_version': 1,
119  'date_time_string': self.datedate.strftime("%Y-%m-%dT%H:%M:%SZ")
120  }
121 
122  self.writerwriter = iconv.NcWriter(self.filenamefilename, self.locKeyListlocKeyList)
123 
124  self.keyDictkeyDict = DefaultOrderedDict(lambda: DefaultOrderedDict(dict))
125  for key in self.varDictvarDict.keys():
126  value = self.varDictvarDict[key]
127  self.keyDictkeyDict[key]['valKey'] = value, self.writerwriter.OvalName()
128  self.keyDictkeyDict[key]['errKey'] = value, self.writerwriter.OerrName()
129  self.keyDictkeyDict[key]['qcKey'] = value, self.writerwriter.OqcName()
130 
131  # data is the dictionary containing IODA friendly data structure
132  self.datadata = DefaultOrderedDict(lambda: DefaultOrderedDict(dict))
133 
134  recKey = 0
135 
136  for obs in obsList:
137 
138  if obs.data['n_obs'] <= 0:
139  print('No trak observations for IODA!')
140  continue
141 
142  for n in range(obs.data['n_obs']):
143 
144  lat = obs.data['ob_lat'][n]
145  lon = obs.data['ob_lon'][n]
146  dtg = datetime.strptime(obs.data['ob_dtg'][n], '%Y%m%d%H%M')
147 
148  locKey = lat, lon, dtg.strftime("%Y-%m-%dT%H:%M:%SZ")
149 
150  for key in self.varDictvarDict.keys():
151 
152  if key in ['ob_uuu', 'ob_vvv']:
153  varName = 'vel'
154  else:
155  varName = key.split('_')[-1]
156 
157  val = obs.data[key][n]
158  err = 1.0
159  qc = (100*obs.data['ob_qc_'+varName][n]).astype('i4')
160 
161  valKey = self.keyDictkeyDict[key]['valKey']
162  errKey = self.keyDictkeyDict[key]['errKey']
163  qcKey = self.keyDictkeyDict[key]['qcKey']
164 
165  self.datadata[recKey][locKey][valKey] = val
166  self.datadata[recKey][locKey][errKey] = err
167  self.datadata[recKey][locKey][qcKey] = qc
168 
169  (ObsVars, LocMdata, VarMdata) = self.writerwriter.ExtractObsData(self.datadata)
170  self.writerwriter.BuildNetcdf(ObsVars, LocMdata, VarMdata, self.AttrDataAttrData)
171 
172  return
173 
174 
175 def main():
176 
177  desc = 'Convert GODAE binary track data to IODA netCDF4 format'
178  parser = ArgumentParser(
179  description=desc,
180  formatter_class=ArgumentDefaultsHelpFormatter)
181  parser.add_argument(
182  '-i', '--input', help='name of the binary GODAE track file',
183  type=str, nargs='+', required=True)
184  parser.add_argument(
185  '-o', '--output', help='name of the output netCDF GODAE ship file',
186  type=str, required=True, default=None)
187  parser.add_argument(
188  '-d', '--date', help='file date',
189  type=str, metavar='YYYYMMDDHH', required=True)
190 
191  args = parser.parse_args()
192 
193  fList = args.input
194  foutput = args.output
195  fdate = datetime.strptime(args.date, '%Y%m%d%H')
196 
197  obsList = []
198  for fname in fList:
199  obsList.append(trak(fname, fdate))
200 
201  varDict = {
202  'ob_sst': 'sea_surface_temperature',
203  'ob_sal': 'sea_surface_salinity',
204  'ob_uuu': 'sea_surface_zonal_wind',
205  'ob_vvv': 'sea_surface_meriodional_wind'
206  }
207 
208  IODA(foutput, fdate, varDict, obsList)
209 
210 
211 if __name__ == '__main__':
212  main()
def __init__(self, filename, date, varDict, obsList)
def __init__(self, filename, date)