OB.DAAC Logo
NASA Logo
Ocean Color Science Software

ocssw V2022
AnalyticNoise2.py
Go to the documentation of this file.
1 #!/usr/bin/env python3
2 # -*- coding: utf-8 -*-
3 """
4 Created on Fri Nov 20 09:55:41 2015
5 Data might need to be transformed before variance propagation
6 --> see this page for how to do it in python
7  http://scipy.github.io/devdocs/generated/scipy.stats.boxcox.html
8 --> and this page for a discussion about it
9  http://stats.stackexchange.com/questions/18844/when-and-why-to-take-the-log-of-a-distribution-of-numbers
10 
11 @author: ekarakoy
12 """
13 import numpy as np
14 import netCDF4 as nc
15 import argparse
16 #import matplotlib.pyplot as pl
17 #from matplotlib.backends.backend_pdf import PdfPages
18 import sys
19 
20 class AnalyticNoise(object):
21  """Class to manage uncertainty estimation using an analytical approach.
22  The class takes an optional boolean argument, multiDim, that defaults
23  to False, which signals whether the user wants to follow the contribu
24  tion each band's Lt perturbation to each band's rrs noise estimation.
25 
26  The order of implementation would be to
27  * instantiate a child object specific to a sensor
28  - e.g. SwfNoise
29  * load baseline data
30  - object.GetBaseline()
31  * process pertubed data files to generate variance
32  - object.UpdVarFromPertDat --> Lt_band by Lt_band
33  - note that obj.UpdateAllVars -->
34  * plot to see all is well
35  * save computed rrs uncertainties back into the baseline file
36 
37  I might write a full sequence method, but for now it's manual
38  """
39 
40 
41  def __init__(self,silFile,noisyDir,multiDimVar = False):
42 
43  # Paths
44  self.silFile = silFile
45  self.noisyDir = noisyDir
46  # Options
47  self.multiDimVar = multiDimVar
48  # Ref. Dicts
49  self.ltProdNameDict = dict.fromkeys(self.bands)
50  self.rrsProdNameDict = dict.fromkeys(self.bands)
51  self.rrsUncProdNameDict = dict.fromkeys(self.bands)
52  # Input Data Dicts
53  self.ltSilDict = dict.fromkeys(self.bands)
54  self.rrsSilDict = dict.fromkeys(self.bands)
55  self.attrDict = dict.fromkeys(self.bands)
56  self.dimsDict = dict.fromkeys(self.bands)
57  self.dTypeDict = dict.fromkeys(self.bands)
58  # Output Data Dicts
59  self.rrsUncDict = dict.fromkeys(self.bands)
60  for band in self.bands:
61  self.ltProdNameDict[band] = 'Lt_' + band
62  self.rrsProdNameDict[band] = 'Rrs_' + band
63  self.rrsUncProdNameDict[band] = 'Rrs_unc_' + band
64 
65  def ReadFromSilent(self):
66  '''Reads Baseline file
67 
68  Flags: l2bin default flags, namely ATMFAIL(1), LAND(2), HIGLINT(8),
69  HILT(16), HISATZEN(32), STRAYLIGHT(256), CLDICE(512), COCCOLITH(1024),
70  HISOLZEN(4096), LOWLW(16384), CHLFAIL(32768), NAVWARN(65536),
71  MAXAERITER(524288), CHLWARN(2097152), ATMWARN(4194304),
72  NAVFAIL(33554432), FILTER(67108864)
73  '''
74  flagBits = 1 + 2 + 8 + 16 + 32 + 256 + 512 + 1024 + 4096 + 16384 + \
75  32768 + 65536 + 524288 + 2097152 + 4194304 + 33554432 + 67108864
76  with nc.Dataset(self.silFile,'r') as dsSil:
77  geoGr = dsSil.groups['geophysical_data']
78  geoVar = geoGr.variables
79  l2flags = geoVar['l2_flags'][:]
80  flagMaskArr = (l2flags & flagBits > 0)
81  for band in self.bands:
82  variable = geoVar[self.rrsProdNameDict[band]]
83  self.rrsSilDict[band] = variable[:]
84  self.rrsSilDict[band].mask += flagMaskArr # Apply l2bin flags
85  self.attrDict[band] = {'long_name' : 'Uncertainty in ' +
86  variable.long_name,
87  '_FillValue': variable._FillValue,
88  'units': variable.units,
89  'scale_factor':variable.scale_factor,
90  'add_offset':variable.add_offset}
91  self.dimsDict[band] = variable.dimensions
92  self.dTypeDict[band] = variable.dtype
93  self.ltSilDict[band] = geoVar[self.ltProdNameDict[band]][:]
94 
95  def BuildUncs(self,noisySfx,verbose=False):
96 
97  for band_rrs in self.bands:
98  if self.multiDimVar:
99  self.rrsUncDict[band_rrs]={}
100  for bandLt in self.bands:
101  band_lt = 'Lt_' + bandLt
102  self.rrsUncDict[band_rrs][band_lt] = np.zeros_like(self.rrsSilDict[bandLt])
103  self.rrsUncDict[band_rrs]['Aggregate'] = np.zeros_like(self.rrsSilDict[band_rrs])
104  else:
105  self.rrsUncDict[band_rrs] = np.zeros_like(self.rrsSilDict[band_rrs])
106 
107  fBaseName = self.silFile.split('/')[-1]
108  for i,ltBand in enumerate(self.bands):
109  noisyFileName = self.noisyDir + '/' + fBaseName + noisySfx + str(i).zfill(4) + '.L2'
110  ltKey = 'Lt_' + ltBand
111  print("searching for %s" % noisyFileName, flush=True)
112  with nc.Dataset(noisyFileName) as nds:
113  if verbose:
114  print("Loading and reading %s" % noisyFileName)
115  ngv = nds.groups['geophysical_data'].variables
116  if verbose:
117  print("Processing perturbation of Lt_%s" % ltBand)
118  ltPert = ngv[self.ltProdNameDict[ltBand]][:]
119  ltSigma = self._ApplyPolyFit(self.coeffsDict[ltBand],
120  self.ltSilDict[ltBand],self.polyDeg)
121  ltSigma = self._ApplyLims(ltSigma,self.sigLimDict[ltBand])
122  dLt = ltPert - self.ltSilDict[ltBand]
123  for band in self.bands:
124  rrsPert = ngv[self.rrsProdNameDict[band]][:]
125  rrsPert.mask += self.rrsSilDict[band].mask
126  sigTemp = ltSigma * (rrsPert - self.rrsSilDict[band]) / dLt
127  varTemp = sigTemp **2
128  if verbose:
129  print("----> estimating %s" % self.rrsUncProdNameDict[band])
130  if self.multiDimVar:
131  #update current bands's Lt perturbation contribution to rrs noise
132  self.rrsUncDict[band][ltKey] += varTemp # CAN I JUST TO THAT ON THE COMPRESSED DATA?
133  # aggregate current band's contribution to rrs noise
134  self.rrsUncDict[band]['Aggregate'] += varTemp
135  else:
136  # only aggregate current band's contribution to rrs noise
137  self.rrsUncDict[band] += varTemp
138 
139  def WriteToSilent(self):
140  # first create NC variables if necessary
141  # save unc in corresponding NC variables.
142  with nc.Dataset(self.silFile,'r+') as dsSil:
143  geoGr = dsSil.groups['geophysical_data']
144  geoVar = geoGr.variables
145  for band in self.bands:
146  uncProdName = self.rrsUncProdNameDict[band]
147  if uncProdName not in geoVar:
148  dType = self.dTypeDict[band]
149  dIms = self.dimsDict[band]
150  variable = geoGr.createVariable(uncProdName,dType,dIms)
151  variable.setncatts(self.attrDict[band])
152  else:
153  variable=geoVar[uncProdName]
154  if self.multiDimVar:
155  variable[:] = self.rrsUncDict[band]['Aggregate']
156  else:
157  variable[:] = self.rrsUncDict[band]
158 
159  @staticmethod
160  def _ApplyLims(sigs,lims):
161  """This function applies thresholds to sigmas from pre-computed
162  upper and lower bounds
163 
164  """
165  finiteSigsIdx = np.isfinite(sigs)
166  sigsSub = sigs[finiteSigsIdx]
167  sigsSub[np.where(sigsSub<lims[0])] = lims[0]
168  sigsSub[np.where(sigsSub>lims[1])] = lims[1]
169  sigs[finiteSigsIdx] = sigsSub
170  return sigs
171 
172  @staticmethod
173  def _ApplyPolyFit(cf,lt,deg):
174  """This function applies a polynomial fit to the data in lt.
175  cf: coefficients of the polynomial
176  deg: degree of the polynomial
177  """
178  y = 0
179  for i in range(deg):
180  y += cf[i] * (lt** (deg - i))
181  y += cf[deg]
182  return 1.0/ y
183 
184 
186  """Subclass to AnalyticNoise, specific to SeaWiFS"""
187 
188  def __init__(self,*args,**nargs):
189  self.sensor='SeaWiFS'
190  self.polyDeg = 4
191  self.bands = ['412','443','490','510','555','670','765','865']
192  self.coeffsDict={'412':np.array([-8.28726301e-11,3.85425664e-07,-9.10776926e-04,
193  1.65881862e+00,4.54351582e-01]),
194  '443':np.array([-1.21871258e-10,5.21579320e-07,-1.14574109e-03,
195  1.96509056e+00,4.18921861e-01]),
196  '490':np.array([-2.99068165e-10,1.05225457e-06,-1.90591166e-03,
197  2.66343986e+00,6.67187489e-01]),
198  '510':np.array([-5.68939986e-10,1.67950509e-06,-2.56915149e-03,
199  3.05832773e+00,9.34468454e-01]),
200  '555':np.array([-1.31635902e-09,3.09617393e-06,-3.73473556e-03,
201  3.52394751e+00,3.54105899e-01]),
202  '670':np.array([-8.65458303e-09,1.18857306e-05,-8.37771886e-03,
203  4.64496430e+00,4.14633422e-02]),
204  '765':np.array([-4.96827099e-08,4.50239057e-05,-2.10425126e-02,
205  7.75862055e+00,5.18893137e-02]),
206  '865':np.array([-1.30487418e-07,9.35407901e-05,-3.40988182e-02,
207  9.43414239e+00,7.84956550e-01])
208  }
209  self.sigLimDict={'412':[0.0008826466289493833,0.058990630111850566],
210  '443':[0.00078708204284562292,0.050110810767361902],
211  '490':[0.00073633772526737848,0.036883976493020949],
212  '510':[0.00074975219339103519,0.031987200608546547],
213  '555':[0.00080870577569697015,0.055925717740595647],
214  '670':[0.0010890690698014294,0.04336828983700642],
215  '765':[0.00093810092188024833,0.026092951412422679],
216  '865':[0.0010906675048335769,0.02122474906498301]
217  }
218  self.colDict = {'412':'#001166','443':'#004488','490':'#116688',
219  '510':'#228844','555':'#667722','670':'#aa2211',
220  '765':'#770500','865':'#440000'}
221  super(SeaWiFSNoise,self).__init__(*args,**nargs)
222 
223 def Main(argv):
224 
225  """ Expects baseline filepath/name and where pertubed files
226  are stored.
227  """
228  __version__="2.0"
229  parser = argparse.ArgumentParser(prog="AnalyticNoise")
230  parser.add_argument('--version', action='version', version='%(prog)s ' + __version__)
231  parser.add_argument('baselinefile', type=str, help=' input bseline file',metavar="BASELINEFILE")
232  parser.add_argument('noiseDir',type=str, help='directory where pertubed files are stored',metavar="NOISEDIR")
233  args = parser.parse_args()
234  baseLineFile = args.baselinefile
235  noisyDataDir = args.noiseDir
236 
237  noisySfx = '_wiggledBand_' #argv[2]
238  swf = SeaWiFSNoise(baseLineFile,noisyDataDir,multiDimVar=True)
239  swf.ReadFromSilent()
240  swf.BuildUncs(noisySfx,verbose=True)
241  swf.WriteToSilent()
242 
243 if __name__ == "__main__":
244  Main(sys.argv[1:])
def __init__(self, *args, **nargs)
def __init__(self, silFile, noisyDir, multiDimVar=False)
def BuildUncs(self, noisySfx, verbose=False)
const char * str
Definition: l1c_msi.cpp:35
def _ApplyPolyFit(cf, lt, deg)