OB.DAAC Logo
NASA Logo
Ocean Color Science Software

ocssw V2022
mkern_viirs.py
Go to the documentation of this file.
1 '''
2 * NAME: mkern_viirs.py
3 *
4 * DESCRIPTION: Executable program to automate generation of VIIRS Deep Blue
5 * Aerosol products from a directory of L1A files specified in the command line.
6 
7 * USAGE: python mkern_viirs.py -i [input directory] -o [output directory]
8 
9 * Created on July 8, 2019
10 
11 * Author: Samuel Anderson
12 '''
13 
14 import os
15 import sys
16 import logging
17 import optparse
18 import numpy as np
19 from numpy import linalg as la
20 from scipy.stats import lognorm
21 from scipy.integrate import quad
22 import matplotlib as mpl
23 import matplotlib.pyplot as plt
24 import matplotlib.image as mpimg
25 from mpl_toolkits.mplot3d import Axes3D
26 from PIL import Image as im
27 import tables
28 import xarray as xr
29 from netCDF4 import num2date
30 import bhmie as bh
31 import mie_kern
32 LOG = logging.getLogger('mkern_viirs')
33 from itertools import permutations
34 from random import sample
35 
36 def normalize(a, order=2, axis=-1 ):
37  l2 = np.atleast_1d(np.linalg.norm(a, order, axis))
38  l2[l2==0] = 1
39  return a / np.expand_dims(l2, axis)
40 
41 def mie(sz,wl,m):
42  s1,s2,qx,qs,qb,gs = bh.bhmie(2*np.pi*sz/wl,m,1)
43  return qs
44 
45 
46 
49 
50 def main():
51 
52  args = sys.argv
53 
54  description = '''Generate mie kernal transforms.'''
55  usage = "usage: python mkern_viirs.py -i [input directory] -o [output directory]"
56  version = "v1"
57 
58  parser = optparse.OptionParser(description=description,usage=usage,version=version)
59 
60  # Mandatory arguments
61  mandatoryGroup = optparse.OptionGroup(parser, "Mandatory Arguments",
62  "At a minimum these arguments must be specified")
63 
64  parser.add_option_group(mandatoryGroup)
65 
66  mandatoryGroup.add_option('-i','--in',
67  action="store",
68  dest="idir" ,
69  type="string",
70  help="The full path of the input directory of L1B files to be processed")
71 
72  mandatoryGroup.add_option('-o','--out',
73  action="store",
74  dest="odir" ,
75  type="string",
76  help="The output directory path")
77 
78  # Optional arguments
79  optionalGroup = optparse.OptionGroup(parser, "Extra Options",
80  "These options may be used to customize behavior of this program.")
81 
82  parser.add_option('-v', '--verbose',
83  dest='verbosity',
84  action="count",
85  default=0,
86  help='each occurrence increases verbosity 1 level from ERROR: -v=WARNING -vv=INFO -vvv=DEBUG')
87 
88  parser.add_option_group(optionalGroup)
89 
90  # Parse the arguments from the command line
91  (opt, args) = parser.parse_args()
92  # Set up the logging levels
93  levels = [logging.ERROR, logging.WARN, logging.INFO, logging.DEBUG]
94  logging.basicConfig(level = levels[opt.verbosity])
95  # Check that all of the mandatory options are given. If one or more
96  # are missing, print error message and exit...
97  mandatories = []
98  mand_errors = []
99  isMissingMand = False
100  for m,m_err in zip(mandatories,mand_errors):
101  if not opt.__dict__[m]:
102  isMissingMand = True
103  print(m_err)
104  if isMissingMand :
105  parser.error("Incomplete mandatory arguments, aborting...")
106  # Check that the output directory actually exists
107  opt.odir = os.path.expanduser(opt.odir)
108  LOG.info('Processing VIIRS files in %s' % (opt.idir) )
109 
110  if not opt.odir:
111  dir = alg + datetime.now().isoformat()
112  o_path = os.getcwd() + dir
113  os.mkdir(o_path)
114  elif os.path.isdir(opt.odir):
115  o_path = opt.odir
116  elif os.path.isdir(os.path.dirname(opt.odir)):
117  o_path = opt.odir
118  os.mkdir(o_path)
119  else:
120  print("Output path invalid")
121  return
122 
123  color_lvl = 8
124  rgb = list(permutations(range(0,256,color_lvl),3))
125  colors = np.array(sample(rgb,120))
126  colors = colors/256.0
127 
128  start = 200
129  stop = 4000
130  awl = [ 412, 488, 550, 670, 865, 1240, 1610, 2250 ]
131  wl = np.copy(awl)
132  nwl = len(wl)
133  lsz = np.linspace(np.log(start),np.log(stop),nwl)
134  sz = np.exp(lsz)
135  nsz = len(sz)
136  #sz[nsz-1] = 10000
137  x = np.arange(start,stop,1, )
138  nx = len(x)
139  # regularization matrices
140  I = np.identity(nsz, dtype=np.float64)
141  # first difference matrix
142  H0 = -np.identity(nsz, dtype=np.float64)
143  H0[0,0] = 0
144  for i in range(1,nsz):
145  for j in range(nsz):
146  H0[i,j] = 1 if (j==i-1) else H0[i,j]
147  # sum of first differences squared
148  H1 = np.dot(H0.T,H0)
149  # sum of second differences squared
150  H2 = H1.copy()
151  H2[0,:] = 0
152  H2 = np.dot(H2.T,H2)
153  # sum of third differences squared
154  H3 = np.zeros([nsz,nwl])
155  for i in range(3,nsz):
156  for j in range(nwl):
157  if (j==i):
158  H3[i,j] = -1
159  elif (j==i-1):
160  H3[i,j] = 3
161  elif (j==i-2):
162  H3[i,j] = -3
163  elif (j==i-3):
164  H3[i,j] = 1
165  H3 = np.dot(H3.T,H3)
166 
167  # The standard deviations of the particle size distributions for each size bin
168  lsig = np.zeros(nsz)
169  sig = np.zeros(nsz)
170  for i in range(1,nsz-1):
171  lsig[i] = (lsz[i+1]-lsz[i-1])/5.0
172  sig[i] = (sz[i+1]-sz[i-1])/4.5
173  lsig[0] = np.log((sz[1]-sz[0])/2)
174  sig[0] = (sz[1]-sz[0])/2
175  lsig[nsz-1] = (lsz[nsz-1]-lsz[nsz-2])/2
176  sig[nsz-1] = (sz[nsz-1]-sz[nsz-2])/2
177 
178  # Index of refraction
179  m = np.zeros((nwl), dtype=np.complex64)
180  m[0] = 1.33+6.0E-04j
181  m[1] = 1.33+6.0E-04j
182  m[2] = 1.33+2.0E-03j
183  m[3] = 1.33+1.0E-05j
184  m[4] = 1.33+1.0E-06j
185  m[5] = 1.33+1.0E-03j
186  m[6] = 1.33+8.0E-02j
187  m[7] = 1.33+8.0E-02j
188 
189  #mk = mie_kern()
190  kbh = np.zeros((nwl, nsz), dtype=np.float64)
191  errs = np.zeros((nwl, nsz), dtype=np.float64)
192  # compute kernel based on Mie scattering
193  lpdf = lambda y, s, scale: lognorm.pdf(y, s=s, loc=0, scale=scale)
194  for iwl in range(nwl):
195  for isz in range(1,nsz):
196  lmie = lambda y: lpdf(y,lsig[isz],sz[isz])*mie(y,wl[iwl],m[iwl])
197  kbh[iwl,isz],errs[iwl,isz] = quad(lmie, sz[isz]-3*sig[isz], sz[isz]\
198  + 3*sig[isz], epsabs=0.001,limit=100)
199  # kbh[iwl,isz] = mie(y,sz[isz],m)
200 
201 # Add Rayleigh scattering component to the first column
202  kbh[:,0] = 1*(wl[0]/wl[:])**4
203 # Normalize the kernal
204  kbh = normalize(kbh, axis=0)
205  mpl.rcParams["font.size"] = 18
206  plt.clf()
207  fig = plt.figure()
208  ax = fig.add_subplot(111, projection='3d')
209  wlg, szg = np.meshgrid(wl, sz)
210  ax.plot_wireframe(wlg, szg, kbh.T)
211  plt.show()
212  # Inverse kernel
213  rp0 = 1e-1
214  rp1 = 1e-5
215  rp2 = 1e-5
216  rp3 = 1e-2
217  rikern = la.inv(np.dot(kbh.T,kbh) + rp0*I + rp1*H1 + rp2*H2 + rp3*H3)
218 
219  # Process and append files and generate db files
220  content_viirs = os.listdir(opt.idir)
221  content_viirs.sort()
222  for x in content_viirs:
223  if x[0] == ".":
224  continue
225  inpath = opt.idir + "/" + x
226  if not os.path.isfile(inpath):
227  continue
228  # open viirs file and read reflectances into matrix refl
229  fin = tables.open_file(inpath, 'a')
230  refl = np.append(fin.get_node("/", "reflectance/TOA")[:6,:,:], \
231  fin.get_node("/", "reflectance/TOA")[7:,:,:],axis=0)
232 
233  psd = np.tensordot(np.dot(rikern,kbh.T),refl,(((1),(0))))
234  irefl = np.tensordot(kbh,psd,(((1),(0))))
235  err = la.norm(refl-irefl, ord=2, axis=0)/(la.norm(refl, ord=2, axis=0)+.001)
236  ratio = la.norm(psd, ord=2, axis=0)/(la.norm(refl, ord=2, axis=0)+.001)
237 
238  print( inpath )
239  fname = opt.odir + "/SZ_" + x + ".png"
240 
241  plt.clf()
242  fig, axs = plt.subplots(nrows=2, ncols=2, figsize=(50, 30))
243  fig.suptitle('Reflectance as a function of wavelength and particle size: ' + \
244  "{: .0e}".format(rp0) + " " + "{: .0e}".format(rp1) + " " + \
245  "{: .0e}".format(rp2) + " " + "{: .0e}".format(rp3))
246  axs[0,0].set(xlabel='Wavelength (Nm)', ylabel='Reflectance',
247  title='Reflectance as a function of Wavelength')
248  axs[0,0].grid(b=True, which='both', axis='both')
249  axs[1,0].set(xlabel='Particle Size (Nm)', ylabel='Inversion',
250  title='Representation as a function of Particle Size')
251  axs[1,0].grid(b=True, which='both', axis='both')
252  axs[1,1].set(xlabel='Root sum of squares error ', ylabel='Frequency in Granule',
253  title='Histogram of errors in reconstructed reflectance')
254  axs[1,1].grid(b=True, which='both', axis='both')
255  axs[0,1].set(title='True color image')
256 
257  maxo_all = max(np.max(refl[3]), np.max(refl[2]), np.max(refl[1])) + 0.001
258  ro = refl[3]/maxo_all
259  go = refl[2]/maxo_all
260  bo = refl[1]/maxo_all
261 
262  gamma = 0.5
263  ga = np.ones_like(ro)*gamma
264  rg = np.power(ro, ga)
265  gg = np.power(go, ga)
266  bg = np.power(bo, ga)
267 
268  scale = 255
269  im_red = im.fromarray(np.uint8(np.clip(rg,0,1.0)*scale))
270  im_green = im.fromarray(np.uint8(np.clip(gg,0,1.0)*scale))
271  im_blue = im.fromarray(np.uint8(np.clip(bg,0,1.0)*scale))
272 
273  im_rgb = im.merge("RGB", (im_red, im_green, im_blue))
274  axs[0,1].imshow(im_rgb)
275  column = 1800
276  for i in range(0,3200,10):
277  axs[0,0].plot(wl,refl[:,i,column])
278  axs[0,0].plot(wl,irefl[:,i,column], color=colors[int(7*i)%120])
279  axs[1,0].semilogx(sz,psd[:,i,column], color=colors[int(7*i)%120])
280  axs[0,1].scatter(column,i,marker=".",color=colors[int(7*i)%120])
281  # axs[0,1].scatter(i, err[500,i], marker='o', color=colors[int(13*i)%120])
282  ehist,be = np.histogram(err)
283  axs[1,1].semilogy(be[:-1],ehist)
284  # rhist,be = np.histogram(ratio, bins=20)
285  # axs[1,1].semilogy(be[:-1],rhist)
286  # plt.show(block=True)
287  plt.savefig(fname)
288  plt.close(fig)
289  fin.close()
290 
291  LOG.info('Exiting...')
292 
293 if __name__=='__main__':
294  sys.exit(main())
295 
list(APPEND LIBS ${PGSTK_LIBRARIES}) add_executable(atteph_info_modis atteph_info_modis.c) target_link_libraries(atteph_info_modis $
Definition: CMakeLists.txt:7
def mie(sz, wl, m)
Definition: mkern_viirs.py:41
def normalize(a, order=2, axis=-1)
Definition: mkern_viirs.py:36
def main()
Main Function #.
Definition: mkern_viirs.py:50