IODA Bundle
ascat_ssm2ioda.py
Go to the documentation of this file.
1 #!/usr/bin/env python3
2 
3 #
4 # (C) Copyright 2021 NOAA/NWS/NCEP/EMC
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 import sys
11 import argparse
12 import netCDF4 as nc
13 import numpy as np
14 from datetime import datetime, timedelta
15 import os
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 collections import defaultdict, OrderedDict
25 from orddicts import DefaultOrderedDict
26 
27 locationKeyList = [
28  ("latitude", "float"),
29  ("longitude", "float"),
30  ("datetime", "string")
31 ]
32 
33 obsvars = {
34  'soil_moisture': 'soilMoistureNormalized',
35 }
36 
37 AttrData = {
38  'converter': os.path.basename(__file__),
39 }
40 
41 
42 class ascat(object):
43  def __init__(self, filename, mask, writer):
44  self.filenamefilename = filename
45  self.maskmask = mask
46  self.writerwriter = writer
47  self.varDictvarDict = defaultdict(lambda: defaultdict(dict))
48  self.outdataoutdata = defaultdict(lambda: DefaultOrderedDict(OrderedDict))
49  self.loc_mdataloc_mdata = defaultdict(lambda: DefaultOrderedDict(OrderedDict))
50  self.var_mdatavar_mdata = defaultdict(lambda: DefaultOrderedDict(OrderedDict))
51  self.unitsunits = {}
52  self._read_read()
53 
54  # Open input file and read relevant info
55  def _read(self):
56  # set up variable names for IODA
57  for iodavar in ['soilMoistureNormalized']:
58  self.varDictvarDict[iodavar]['valKey'] = iodavar, self.writerwriter.OvalName()
59  self.varDictvarDict[iodavar]['errKey'] = iodavar, self.writerwriter.OerrName()
60  self.varDictvarDict[iodavar]['qcKey'] = iodavar, self.writerwriter.OqcName()
61  self.unitsunits[iodavar] = '%'
62  # open input file name
63  ncd = nc.Dataset(self.filenamefilename, 'r')
64  # set and get global attributes
65  AttrData["satellite"] = ncd.getncattr('source')
66  AttrData['platform'] = ncd.getncattr('platform_long_name')
67 
68  lats = ncd.variables['lat'][:].ravel()
69  lons = ncd.variables['lon'][:].ravel()
70  vals = ncd.variables['soil_moisture'][:].ravel()
71  errs = ncd.variables['soil_moisture_error'][:].ravel()
72  wflg = ncd.variables['wetland_flag'][:].ravel()
73  tflg = ncd.variables['topography_flag'][:].ravel()
74  times = np.empty_like(vals, dtype=object)
75 
76  num_cells = ncd.dimensions['numCells'].size
77  secs = ncd.variables['record_start_time'][:].ravel()
78  secs = np.repeat(secs, num_cells)
79  qflg = 0*vals.astype('int32')
80  wflg = wflg.astype('int32')
81  tflg = tflg.astype('int32')
82 
83  if self.maskmask == "maskout":
84  mask = np.logical_not(vals.mask)
85  vals = vals[mask]
86  lons = lons[mask]
87  lats = lats[mask]
88  errs = errs[mask]
89  qflg = qflg[mask]
90  wflg = wflg[mask]
91  tflg = tflg[mask]
92  secs = secs[mask]
93  times = times[mask]
94 
95  for i in range(len(lons)):
96  base_date = datetime(2000, 1, 1) + timedelta(seconds=int(secs[i]))
97  base_datetime = base_date.strftime("%Y-%m-%dT%H:%M:%SZ")
98  AttrData['date_time_string'] = base_datetime
99  times[i] = base_datetime
100 
101  self.loc_mdataloc_mdata['datetime'] = self.writerwriter.FillNcVector(times, "datetime")
102  self.loc_mdataloc_mdata['latitude'] = lats
103  self.loc_mdataloc_mdata['longitude'] = lons
104  self.loc_mdataloc_mdata['wetlandFraction'] = wflg
105  self.loc_mdataloc_mdata['topographyComplexity'] = tflg
106  for iodavar in ['soilMoistureNormalized']:
107  self.outdataoutdata[self.varDictvarDict[iodavar]['valKey']] = vals
108  self.outdataoutdata[self.varDictvarDict[iodavar]['errKey']] = errs
109  self.outdataoutdata[self.varDictvarDict[iodavar]['qcKey']] = qflg
110  self.writerwriter._nvars = len(obsvars)
111  self.writerwriter._nlocs = len(self.loc_mdataloc_mdata['datetime'])
112 
113 
114 def main():
115 
116  # get command line arguments
117  parser = argparse.ArgumentParser(
118  description=(
119  'Reads ASCAT L2NRT SM netCDF file(s) provided by EUMETSAT'
120  ' and converts into IODA formatted output file.')
121  )
122  required = parser.add_argument_group(title='required arguments')
123  required.add_argument(
124  '-i', '--input',
125  help="name of ASCAT L2NRT SM observation netCDF input file(s)",
126  type=str, required=True)
127  required.add_argument(
128  '-o', '--output',
129  help="path of IODA output file",
130  type=str, required=True)
131  optional = parser.add_argument_group(title='optional arguments')
132  optional.add_argument(
133  '-m', '--mask',
134  help="maskout missing values: maskout/default, default=none",
135  type=str, required=True)
136 
137  args = parser.parse_args()
138 
139  # setup the IODA writer
140  writer = iconv.NcWriter(args.output, locationKeyList)
141 
142  # Read in the ssm data
143  ssm = ascat(args.input, args.mask, writer)
144 
145  # write everything out
146  writer.BuildNetcdf(ssm.outdata, ssm.loc_mdata, ssm.var_mdata, AttrData, ssm.units)
147 
148 
149 if __name__ == '__main__':
150  main()
def __init__(self, filename, mask, writer)