IODA Bundle
godae_ship2ioda.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 ship(object):
28 
29  def __init__(self, filename, date):
30 
31  self.filenamefilename = filename
32  self.datedate = date
33 
34  # Read ship data
35  self._rd_ship_rd_ship()
36 
37  return
38 
39  def _rd_ship(self):
40  '''
41  Read the surface obs data
42  Based on subroutine rd_ship 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 ship 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 ship observations to process from %s' % self.filenamefilename)
63  return
64 
65  data['ob_wm'] = fh.read_reals('>i4')
66  data['ob_glb'] = fh.read_reals('>f4')
67  data['ob_lat'] = fh.read_reals('>f4')
68  data['ob_lon'] = fh.read_reals('>f4')
69  data['ob_age'] = fh.read_reals('>f4')
70  data['ob_clm'] = fh.read_reals('>f4')
71  data['ob_qc'] = fh.read_reals('>f4')
72  data['ob_rgn'] = fh.read_reals('>f4')
73  data['ob_sst'] = fh.read_reals('>f4')
74  data['ob_typ'] = fh.read_reals('>i4')
75  data['ob_dtg'] = fh.read_record('>S12').astype('U12')
76  data['ob_rcpt'] = fh.read_record('>S12').astype('U12')
77  data['ob_scr'] = fh.read_record('>S1').astype('U1')
78 
79  if data['n_vrsn'] <= 2:
80  print('verify ob_sign for version = %d' % data['n_vrsn'])
81  data['ob_sign'] = fh.read_record('S6').astype('U6')
82  else:
83  data['ob_sign'] = fh.read_record('>S7').astype('U7')
84 
85  if data['n_vrsn'] > 1:
86  data['ob_csgm'] = fh.read_reals('>f4')
87  data['ob_gsgm'] = fh.read_reals('>f4')
88  data['ob_rsgm'] = fh.read_reals('>f4')
89  else:
90  minus999 = np.ones(data['n_obs'], dtype=np.float32) * -999.
91  data['ob_csgm'] = minus999
92  data['ob_gsgm'] = minus999
93  data['ob_rsgm'] = minus999
94 
95  fh.close()
96 
97  self.datadata = data
98 
99  return
100 
101 
102 class IODA(object):
103 
104  def __init__(self, filename, date, varDict, obsList):
105  '''
106  Initialize IODA writer class,
107  transform to IODA data structure and,
108  write out to IODA file.
109  '''
110 
111  self.filenamefilename = filename
112  self.datedate = date
113  self.varDictvarDict = varDict
114 
115  self.locKeyListlocKeyList = [
116  ("latitude", "float"),
117  ("longitude", "float"),
118  ("datetime", "string")
119  ]
120 
121  self.AttrDataAttrData = {
122  'odb_version': 1,
123  'date_time_string': self.datedate.strftime("%Y-%m-%dT%H:%M:%SZ")
124  }
125 
126  self.writerwriter = iconv.NcWriter(self.filenamefilename, self.locKeyListlocKeyList)
127 
128  self.keyDictkeyDict = DefaultOrderedDict(lambda: DefaultOrderedDict(dict))
129  for key in self.varDictvarDict.keys():
130  value = self.varDictvarDict[key]
131  self.keyDictkeyDict[key]['valKey'] = value, self.writerwriter.OvalName()
132  self.keyDictkeyDict[key]['errKey'] = value, self.writerwriter.OerrName()
133  self.keyDictkeyDict[key]['qcKey'] = value, self.writerwriter.OqcName()
134 
135  # data is the dictionary containing IODA friendly data structure
136  self.datadata = DefaultOrderedDict(lambda: DefaultOrderedDict(dict))
137 
138  recKey = 0
139 
140  for obs in obsList:
141 
142  if obs.data['n_obs'] <= 0:
143  print('No ship observations for IODA!')
144  continue
145 
146  for n in range(obs.data['n_obs']):
147 
148  lat = obs.data['ob_lat'][n]
149  lon = obs.data['ob_lon'][n]
150  dtg = datetime.strptime(obs.data['ob_dtg'][n], '%Y%m%d%H%M')
151 
152  locKey = lat, lon, dtg.strftime("%Y-%m-%dT%H:%M:%SZ")
153 
154  for key in self.varDictvarDict.keys():
155 
156  val = obs.data[key][n]
157  err = 0.5
158  qc = (100*obs.data['ob_qc'][n]).astype('i4')
159 
160  valKey = self.keyDictkeyDict[key]['valKey']
161  errKey = self.keyDictkeyDict[key]['errKey']
162  qcKey = self.keyDictkeyDict[key]['qcKey']
163 
164  self.datadata[recKey][locKey][valKey] = val
165  self.datadata[recKey][locKey][errKey] = err
166  self.datadata[recKey][locKey][qcKey] = qc
167 
168  (ObsVars, LocMdata, VarMdata) = self.writerwriter.ExtractObsData(self.datadata)
169  self.writerwriter.BuildNetcdf(ObsVars, LocMdata, VarMdata, self.AttrDataAttrData)
170 
171  return
172 
173 
174 def main():
175 
176  desc = 'Convert GODAE binary ship data to IODA netCDF4 format'
177  parser = ArgumentParser(description=desc,
178  formatter_class=ArgumentDefaultsHelpFormatter)
179  parser.add_argument(
180  '-i', '--input', help='name of the binary GODAE ship file',
181  type=str, nargs='+', required=True)
182  parser.add_argument(
183  '-o', '--output', help='name of the output netCDF GODAE ship file',
184  type=str, required=True, default=None)
185  parser.add_argument(
186  '-d', '--date', help='file date',
187  type=str, metavar='YYYYMMDDHH', required=True)
188 
189  args = parser.parse_args()
190 
191  fList = args.input
192  foutput = args.output
193  fdate = datetime.strptime(args.date, '%Y%m%d%H')
194 
195  obsList = []
196  for fname in fList:
197  obsList.append(ship(fname, fdate))
198 
199  varDict = {
200  'ob_sst': 'sea_surface_temperature',
201  }
202 
203  IODA(foutput, fdate, varDict, obsList)
204 
205 
206 if __name__ == '__main__':
207  main()
def __init__(self, filename, date, varDict, obsList)
def __init__(self, filename, date)