IODA Bundle
smap_ssm2ioda.py
Go to the documentation of this file.
1 #!/usr/bin/env python3
2 #
3 # (C) Copyright 2021 EMC/NCEP/NWS/NOAA
4 #
5 # This software is licensed under the terms of the Apache Licence Version 2.0
6 # which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
7 #
8 import time, os, sys
9 import argparse
10 import netCDF4 as nc
11 import numpy as np
12 import re
13 from datetime import datetime, timedelta
14 from pathlib import Path
15 
16 IODA_CONV_PATH = Path(__file__).parent/"@SCRIPT_LIB_PATH@"
17 if not IODA_CONV_PATH.is_dir():
18  IODA_CONV_PATH = Path(__file__).parent/'..'/'lib-python'
19 sys.path.append(str(IODA_CONV_PATH.resolve()))
20 
21 import ioda_conv_ncio as iconv
22 from collections import defaultdict, OrderedDict
23 from orddicts import DefaultOrderedDict
24 
25 locationKeyList = [
26  ("latitude", "float"),
27  ("longitude", "float"),
28  ("datetime", "string")
29 ]
30 
31 obsvars = {
32  'soil_moisture': 'soilMoistureVolumetric',
33 }
34 
35 AttrData = {
36  'converter': os.path.basename(__file__),
37 }
38 
39 
40 class smap(object):
41  def __init__(self, filename, mask, writer):
42  self.filenamefilename = filename
43  self.maskmask = mask
44  self.writerwriter = writer
45  self.varDictvarDict = defaultdict(lambda: defaultdict(dict))
46  self.outdataoutdata = defaultdict(lambda: DefaultOrderedDict(OrderedDict))
47  self.loc_mdataloc_mdata = defaultdict(lambda: DefaultOrderedDict(OrderedDict))
48  self.var_mdatavar_mdata = defaultdict(lambda: DefaultOrderedDict(OrderedDict))
49  self.unitsunits = {}
50  self._read_read()
51 
52  # Open input file and read relevant info
53  def _read(self):
54  # set up variable names for IODA
55  for iodavar in ['soilMoistureVolumetric']:
56  self.varDictvarDict[iodavar]['valKey'] = iodavar, self.writerwriter.OvalName()
57  self.varDictvarDict[iodavar]['errKey'] = iodavar, self.writerwriter.OerrName()
58  self.varDictvarDict[iodavar]['qcKey'] = iodavar, self.writerwriter.OqcName()
59  self.unitsunits[iodavar] = 'm3m-3'
60  # open input file name
61  ncd = nc.Dataset(self.filenamefilename, 'r')
62  # set and get global attributes
63  self.satellitesatellite = "SMAP"
64  self.sensorsensor = "radar and radiometer"
65  AttrData["satellite"] = self.satellitesatellite
66  AttrData["sensor"] = self.sensorsensor
67 
68  data = ncd.groups['Soil_Moisture_Retrieval_Data'].variables['soil_moisture'][:]
69  vals = data[:].ravel()
70  _FillValue = ncd.groups['Soil_Moisture_Retrieval_Data'].variables['soil_moisture'].getncattr('_FillValue')
71  valid_max = ncd.groups['Soil_Moisture_Retrieval_Data'].variables['soil_moisture'].getncattr('valid_max')
72  valid_min = ncd.groups['Soil_Moisture_Retrieval_Data'].variables['soil_moisture'].getncattr('valid_min')
73 
74  lats = ncd.groups['Soil_Moisture_Retrieval_Data'].variables['latitude'][:].ravel()
75  lons = ncd.groups['Soil_Moisture_Retrieval_Data'].variables['longitude'][:].ravel()
76  errs = ncd.groups['Soil_Moisture_Retrieval_Data'].variables['soil_moisture_error'][:].ravel()
77  qflg = ncd.groups['Soil_Moisture_Retrieval_Data'].variables['retrieval_qual_flag'][:].ravel()
78  times = np.empty_like(vals, dtype=object)
79 
80  if self.maskmask == "maskout":
81  with np.errstate(invalid='ignore'):
82  mask = (vals > valid_min) & (vals < valid_max)
83  vals = vals[mask]
84  lats = lats[mask]
85  lons = lons[mask]
86  errs = errs[mask]
87  qflg = qflg[mask]
88  times = times[mask]
89 
90  # get datetime from filename
91  str_split = self.filenamefilename.split("_")
92  str_datetime = str_split[7]
93  my_datetime = datetime.strptime(str_datetime, "%Y%m%dT%H%M%S")
94  base_datetime = my_datetime.strftime('%Y-%m-%dT%H:%M:%SZ')
95 
96  qflg = qflg.astype('int32')
97  AttrData['date_time_string'] = base_datetime
98 
99  for i in range(len(lons)):
100 
101  if vals[i] > 0.0:
102  # assumed 4% SM rathern than -999.0
103  errs[i] = 0.04*vals[i]
104  if qflg[i] > 5:
105  qflg[i] = 0
106  else:
107  qflg[i] = 1
108  else:
109  qflg[i] = 1
110 
111  times[i] = base_datetime
112  self.loc_mdataloc_mdata['datetime'] = self.writerwriter.FillNcVector(times, "datetime")
113  self.loc_mdataloc_mdata['latitude'] = lats
114  self.loc_mdataloc_mdata['longitude'] = lons
115  for iodavar in ['soilMoistureVolumetric']:
116  self.outdataoutdata[self.varDictvarDict[iodavar]['valKey']] = vals
117  self.outdataoutdata[self.varDictvarDict[iodavar]['errKey']] = errs
118  self.outdataoutdata[self.varDictvarDict[iodavar]['qcKey']] = qflg
119  self.writerwriter._nvars = len(obsvars)
120  self.writerwriter._nlocs = len(self.loc_mdataloc_mdata['datetime'])
121 
122 
123 def main():
124 
125  parser = argparse.ArgumentParser(
126  description=('Read SMAP surface soil moisture file(s) and Converter'
127  ' of native h5 format for observations of surface'
128  ' soil moisture to IODA netCDF format.')
129  )
130  parser.add_argument('-i', '--input',
131  help="name of smap surface soil moisture input file(s)",
132  type=str, required=True)
133  parser.add_argument('-o', '--output',
134  help="name of ioda output file",
135  type=str, required=True)
136  optional = parser.add_argument_group(title='optional arguments')
137  optional.add_argument(
138  '-m', '--mask',
139  help="maskout missing values: maskout/default, default=none",
140  type=str, required=True)
141 
142  args = parser.parse_args()
143 
144  writer = iconv.NcWriter(args.output, locationKeyList)
145 
146  # Read in the profiles
147  ssm = smap(args.input, args.mask, writer)
148 
149  writer.BuildNetcdf(ssm.outdata, ssm.loc_mdata, ssm.var_mdata, AttrData, ssm.units)
150 
151 
152 if __name__ == '__main__':
153  main()
def __init__(self, filename, mask, writer)