OB.DAAC Logo
NASA Logo
Ocean Color Science Software

ocssw V2022
get_Kd.c
Go to the documentation of this file.
1 /* =================================================================== */
2 /* Algorithms for computing diffuse attenuation coefficient for MSl12. */
3 /* */
4 /* B. Franz, NASA Ocean Biology Processing Group, SAIC, March 2005. */
5 /* =================================================================== */
6 
7 #include <stdlib.h>
8 #include <math.h>
9 #include "l12_proto.h"
10 
11 #define KD_MAX 6.4
12 #define KD_MIN 0.016
13 
14 static float kdbad = BAD_FLT;
15 
16 /* ------------------------------------------------------------------- */
17 /* Kd490_KD2 - diffuse attenuation at 490nm (2-band polynomial). */
18 /* */
19 /* Inputs: */
20 /* l2rec - level-2 structure containing one complete scan after */
21 /* atmospheric correction. */
22 /* Outputs: */
23 /* k490 - diffuse attenuation coefficient, 1 value per pixel. */
24 /* */
25 /* Algorithm: P.J.Werdell, June 2009 */
26 
27 /* ------------------------------------------------------------------- */
28 void Kd490_KD2(l2str *l2rec, float *Kd) {
29  static int32_t *w = NULL;
30  static float *a = NULL;
31  static int ib1 = -1;
32  static int ib2 = -1;
33 
34  int32_t ip, ipb;
35  float R;
36 
37  l1str *l1rec = l2rec->l1rec;
38 
39  if (w == NULL) {
40  w = input->kd2w;
41  a = input->kd2c;
42  if (w[0] < 0 || w[1] < 0) {
43  printf("Kd490_KD2: algorithm coefficients not provided for this sensor.\n");
44  exit(1);
45  }
46  ib1 = bindex_get(w[0]);
47  ib2 = bindex_get(w[1]);
48  if (ib1 < 0 || ib2 < 0) {
49  printf("Kd490_KD2: incompatible sensor wavelengths for this algorithm\n");
50  exit(1);
51  }
52  }
53 
54  for (ip = 0; ip < l1rec->npix; ip++) {
55 
56  ipb = l1rec->l1file->nbands*ip;
57 
58  if (l1rec->mask[ip] ||
59  l2rec->Rrs[ipb + ib1] <= 0.0 || l2rec->Rrs[ipb + ib2] <= 0.0) {
60  Kd[ip] = kdbad;
61  l1rec->flags[ip] |= PRODFAIL;
62  } else {
63  R = log10(l2rec->Rrs[ipb + ib1] / l2rec->Rrs[ipb + ib2]);
64  if (isnan(R)) {
65  Kd[ip] = kdbad;
66  l1rec->flags[ip] |= PRODFAIL;
67  } else {
68  Kd[ip] = a[0] + pow(10.0, a[1] + R * (a[2] + R * (a[3] + R * (a[4] + R * a[5]))));
69  if (Kd[ip] > KD_MAX) {
70  Kd[ip] = KD_MAX;
71  l1rec->flags[ip] |= PRODWARN;
72  }
73  }
74  }
75  }
76 }
77 
78 
79 /* ------------------------------------------------------------------- */
80 /* Kd490_mueller() - diffuse attenuation at 490nm (J. Mueller). */
81 /* */
82 /* Inputs: */
83 /* l2rec - level-2 structure containing one complete scan after */
84 /* atmospheric correction. */
85 /* Outputs: */
86 /* k490 - diffuse attenuation coefficient, 1 value per pixel. */
87 /* */
88 /* Algorithm provided by: J. Mueller */
89 /* OCTS coefficents: S. Bailey, 16 July 2001 */
90 
91 /* ------------------------------------------------------------------- */
92 void Kd490_mueller(l2str *l2rec, float k490[]) {
93  int32_t ip, ipb;
94 
95  static float a = 0.15645;
96  static float b = -1.5401;
97  static float Kw = 0.016;
98 
99  static float maxval = 6.4;
100 
101  l1str *l1rec = l2rec->l1rec;
102 
103  if (l1rec->l1file->sensorID == OCTS) {
104  /* Coefs for 490/565 band combination */
105  a = 0.2166;
106  b = -1.6355;
107  }
108 
109  for (ip = 0; ip < l1rec->npix; ip++) {
110 
111  ipb = l1rec->l1file->nbands*ip;
112 
113  if (l1rec->mask[ip]) { /* pixel was masked */
114  k490[ip] = kdbad;
115  l1rec->flags[ip] |= PRODFAIL;
116  } else if (l2rec->nLw[ipb + 2] <= 0.0) { /* unknown, high attenuation */
117  k490[ip] = maxval;
118  } else if (l2rec->nLw[ipb + 4] <= 0.0) { /* unknown, low attenuation */
119  k490[ip] = Kw;
120  } else {
121  k490[ip] = a * pow(l2rec->nLw[ipb + 2] / l2rec->nLw[ipb + 4], b) + Kw;
122  if (k490[ip] > maxval) {
123  k490[ip] = maxval;
124  }
125  }
126  }
127 }
128 
129 
130 /* ------------------------------------------------------------------- */
131 /* Kd490_obpg - diffuse attenuation at 490nm (Mueller & Werdell). */
132 /* */
133 /* Inputs: */
134 /* l2rec - level-2 structure containing one complete scan after */
135 /* atmospheric correction. */
136 /* Outputs: */
137 /* k490 - diffuse attenuation coefficient, 1 value per pixel. */
138 /* */
139 /* Algorithm provided by: J. Mueller */
140 /* New coefficents and updated form: P.J.Werdell, February 2005. */
141 
142 /* ------------------------------------------------------------------- */
143 void Kd490_obpg(l2str *l2rec, float k490[]) {
144  int32_t ip, ipb;
145 
146  static float a;
147  static float b;
148  static int32_t ib490;
149  static int32_t ib555;
150 
151  static int firstCall = 1;
152 
153  l1str *l1rec = l2rec->l1rec;
154 
155  if (firstCall) {
156 
157  /* select 490/555 or 490/565 fit, depending on proximity of */
158  /* sensor bands to fitted bands */
159 
160  if ((ib555 = bindex_get(551)) > 0) {
161  a = 0.1853;
162  b = -1.349;
163  } else if ((ib555 = bindex_get(565)) > 0) {
164  a = 0.1787;
165  b = -1.122;
166  } else {
167  printf("Kd_obpg: incompatible sensor wavelengths (no 555 or 565).\n");
168  exit(1);
169  }
170 
171  if ((ib490 = bindex_get(490)) < 0) {
172  printf("Kd_obpg: incompatible sensor wavelengths (no 490).\n");
173  exit(1);
174  }
175 
176  firstCall = 0;
177  }
178 
179  for (ip = 0; ip < l1rec->npix; ip++) {
180 
181  ipb = l1rec->l1file->nbands*ip;
182 
183  if (l1rec->mask[ip] ||
184  l2rec->nLw[ipb + ib490] <= 0.0 || l2rec->nLw[ipb + ib555] <= 0.0) {
185  k490[ip] = kdbad;
186  l1rec->flags[ip] |= CHLFAIL;
187  l1rec->flags[ip] |= PRODFAIL;
188  } else {
189  k490[ip] = a * pow(l2rec->nLw[ipb + ib490] / l2rec->nLw[ipb + ib555], b);
190  if (k490[ip] > KD_MAX) {
191  k490[ip] = KD_MAX;
192  l1rec->flags[ip] |= PRODWARN;
193  }
194  /* not until reprocessing
195  if (k490[ip] < KD_MIN) {
196  k490[ip] = KD_MIN;
197  l2rec->flags[ip] |= PRODWARN;
198  }
199  */
200  }
201 
202  }
203 }
204 
205 
206 /* ------------------------------------------------------------------- */
207 /* Kd490_morel() - diffuse attenuation at 490 using Morel (2007) */
208 /* */
209 /* Inputs: */
210 /* l2rec - level-2 structure containing one complete scan after */
211 /* atmospheric correction. */
212 /* band - waveband number (0 - nbands-1) at which Kd computed. */
213 /* */
214 /* Outputs: */
215 /* Kd - diffuse attenuation at 490nm, 1 value per pixel. */
216 /* */
217 /* Description: */
218 /* This produces the estimate of diffuse attenation at 490 */
219 /* using the satellite derived chlorophyll. */
220 /* */
221 /* Reference: */
222 /* */
223 /* Morel, A., Y. Huot, B. Gentili, P.J. Werdell, S.B. Hooker (2007). */
224 /* Consistency of products derived from various ocean color sensors: */
225 /* An examination before merging these products and extending their */
226 /* applications, Remote Sensing of Environment, to be submitted. */
227 /* */
228 /* New equation 8 (combined LOV and NOMAD derivation) */
229 /* */
230 /* Original Implementation: B. Franz, February 2007 */
231 
232 /*---------------------------------------------------------------------*/
233 void Kd490_morel(l2str *l2rec, float *Kd) {
234  static float Kw = 0.01660;
235  static float X = 0.077746;
236  static float e = 0.672846;
237 
238  float chl;
239  int32_t ip;
240 
241  l1str *l1rec = l2rec->l1rec;
242 
243  for (ip = 0; ip < l1rec->npix; ip++) {
244 
245  chl = l2rec->chl[ip];
246 
247  if (l1rec->mask[ip] || chl <= 0.0) {
248  Kd[ip] = kdbad;
249  l1rec->flags[ip] |= PRODFAIL;
250  } else {
251  Kd[ip] = Kw + X * pow(chl, e);
252  if (Kd[ip] > KD_MAX) {
253  Kd[ip] = KD_MAX;
254  l1rec->flags[ip] |= PRODWARN;
255  } else
256  if (Kd[ip] < KD_MIN) {
257  Kd[ip] = KD_MIN;
258  l1rec->flags[ip] |= PRODWARN;
259  }
260  }
261  }
262 
263  return;
264 }
265 
266 
267 /* ------------------------------------------------------------------- */
268 /* Kd_PAR_morel() - spectrally integrated attenuation using Morel */
269 /* */
270 /* Inputs: */
271 /* l2rec - level-2 structure containing one complete scan after */
272 /* atmospheric correction. */
273 /* depth - layer depth 1=1/k490, 2=2/k490 */
274 /* */
275 /* Outputs: */
276 /* Kd - Kd(PAR), 1 value per pixel. */
277 /* */
278 /* Description: */
279 /* */
280 /* Reference: */
281 /* */
282 /* Morel, A., Y. Huot, B. Gentili, P.J. Werdell, S.B. Hooker (2007). */
283 /* Consistency of products derived from various ocean color sensors: */
284 /* An examination before merging these products and extending their */
285 /* applications, Remote Sensing of Environment, to be submitted. */
286 /* */
287 /* Original Implementation: B. Franz, November 2006 */
288 
289 /*---------------------------------------------------------------------*/
290 void Kd_PAR_morel(l2str *l2rec, int depth, float *Kd) {
291  static float *Kd490 = NULL;
292 
293  int32_t ip;
294 
295  l1str *l1rec = l2rec->l1rec;
296 
297  if (Kd490 == NULL) {
298  if ((Kd490 = malloc(l1rec->npix * sizeof (float))) == NULL) {
299  printf("-E- %s: Error allocating space for %d records.\n", __FILE__, l1rec->npix);
300  exit(1);
301  }
302  }
303 
304  Kd490_morel(l2rec, Kd490);
305 
306  for (ip = 0; ip < l1rec->npix; ip++) {
307 
308  if (l1rec->mask[ip] || Kd490[ip] <= 0.0) {
309  Kd[ip] = kdbad;
310  l1rec->flags[ip] |= PRODFAIL;
311  } else {
312  switch (depth) {
313  case 1:
314  Kd[ip] = 0.0864 + 0.884 * Kd490[ip] - 0.00137 / Kd490[ip];
315  break;
316  case 2:
317  Kd[ip] = 0.0665 + 0.874 * Kd490[ip] - 0.00121 / Kd490[ip];
318  break;
319  default:
320  printf("-E- %s: Invalid depth for Kd(PAR) (1 or 2).\n", __FILE__);
321  exit(1);
322  break;
323  }
324  }
325  }
326 
327  return;
328 }
329 
330 /* ------------------------------------------------------------------- */
331 /* Zhl_morel() - heated layer depth using Morel (2007) */
332 /* */
333 /* Inputs: */
334 /* l2rec - level-2 structure containing one complete scan after */
335 /* atmospheric correction. */
336 /* */
337 /* Outputs: */
338 /* Zhl - heated layer depth (m) */
339 /* */
340 /* Description: */
341 /* */
342 /* Reference: */
343 /* */
344 /* Morel, A., Y. Huot, B. Gentili, P.J. Werdell, S.B. Hooker (2007). */
345 /* Consistency of products derived from various ocean color sensors: */
346 /* An examination before merging these products and extending their */
347 /* applications, Remote Sensing of Environment, to be submitted. */
348 /* */
349 /* Original Implementation: B. Franz, November 2006 */
350 
351 /*---------------------------------------------------------------------*/
352 void Zhl_morel(l2str *l2rec, float *Zhl) {
353  static float *KdPAR = NULL;
354 
355  int32_t ip;
356  l1str *l1rec = l2rec->l1rec;
357 
358  /* need Kd(PAR) at 2nd optical depth */
359  if (KdPAR == NULL) {
360  if ((KdPAR = malloc(l1rec->npix * sizeof (float))) == NULL) {
361  printf("-E- %s: Error allocating space for %d records.\n", __FILE__, l1rec->npix);
362  exit(1);
363  }
364  }
365  Kd_PAR_morel(l2rec, 2, KdPAR);
366 
367  for (ip = 0; ip < l1rec->npix; ip++) {
368 
369  if (l1rec->mask[ip] || KdPAR[ip] <= 1e-5) {
370  Zhl[ip] = -999;
371  l1rec->flags[ip] |= PRODFAIL;
372  } else
373  Zhl[ip] = 2.0 / KdPAR[ip];
374  }
375 
376  return;
377 }
378 
379 
380 /* ------------------------------------------------------------------- */
381 /* Kd532() - spectral diffuse attenuation using Mueller/Austin&Petzold */
382 /* */
383 /* Inputs: */
384 /* l2rec - level-2 structure containing one complete scan after */
385 /* atmospheric correction. */
386 /* flag - return Kd in meters (1) or 1/meters */
387 /* Kd - diffuse attenuation at 490 nm. */
388 /* Outputs: */
389 /* Kd - diffuse attenuation at 532 nm. */
390 /* */
391 /* Description: */
392 /* This produces the estimate of diffuse attenation at 532 nm from */
393 /* the estimate of diffuse attenuation at 490 nm using the spectral */
394 /* K algorithm of Austin and Petzold. */
395 /* */
396 /* Reference: */
397 /* Austin, R. W and T. J. Petzold, "Spectral Dependence of the */
398 /* Diffuse Attenuation Coefficient of Light in Ocean Waters", */
399 /* SPIE Vol 489 Ocean Optics (1984) pp 168-178 */
400 /* */
401 /* Original Implementation: P. Martinolich, NRL/Stennis, May 2005 */
402 
403 /*---------------------------------------------------------------------*/
404 
405 void Kd532(l2str *l2rec, int flag, float k532[]) {
406  const float M532 = 0.68052;
407  const float KW490 = 0.0224;
408  const float KW532 = 0.05356;
409 
410  static float maxval = 6.4;
411 
412  int ip;
413  float temp;
414 
415  l1str *l1rec = l2rec->l1rec;
416 
417  for (ip = 0; ip < l1rec->npix; ip++) {
418 
419  if (k532[ip] < 0.0)
420  k532[ip] = kdbad;
421  else if (k532[ip] >= maxval)
422  k532[ip] = maxval;
423  else if (l1rec->mask[ip])
424  k532[ip] = kdbad;
425  else {
426  temp = M532 * (k532[ip] - KW490) + KW532;
427  if (flag > 0)
428  k532[ip] = 1.0 / temp;
429  else
430  k532[ip] = temp;
431  if (k532[ip] < 0.0)
432  k532[ip] = kdbad;
433  if (k532[ip] > maxval)
434  k532[ip] = maxval;
435  }
436 
437  }
438 }
439 
467 void Kd_lee(l2str *l2rec, int band, float *Kd)
468 {
469 
470  const float m1 = 4.259;
471  const float m2 = 0.520;
472  const float m3 = -10.800;
473  const float m4 = 0.265;
474 
475  float m0;
476  int ip, ipb;
477 
478  l1str *l1rec = l2rec->l1rec;
479 
480  if (input->iop_opt == IOPNONE) {
481  printf("IOP-based Kd_lee product requires iop model selection (iop_opt). ");
482  printf("Using default model.\n");
483  input->iop_opt = IOPDEFAULT;
484  get_iops(l2rec, input->iop_opt);
485  }
486 
487  for (ip = 0; ip < l1rec->npix; ip++) {
488 
489  ipb = ip * l1rec->l1file->nbands + band;
490 
491  if (l1rec->mask[ip] ||
492  l2rec->a[ipb] <= 0.0 || l2rec->bb[ipb] <= 0.0 ) {
493  Kd[ip] = kdbad;
494  l1rec->flags[ip] |= PRODFAIL;
495  } else {
496  m0 = 1.0 + 0.005 * l1rec->solz[ip];
497  Kd[ip] = m0 * l2rec->a[ipb]
498  + m1 * (1.0 - m2 * exp( m3 * l2rec->a[ipb])) * l2rec->bb[ipb]
499  * (1.0 - m4 * (l1rec->sw_bb[band] / l2rec->bb[ipb]));
500  if (Kd[ip] > KD_MAX) {
501  Kd[ip] = KD_MAX;
502  l1rec->flags[ip] |= PRODWARN;
503  } else
504  if (Kd[ip] < KD_MIN) {
505  Kd[ip] = KD_MIN;
506  l1rec->flags[ip] |= PRODWARN;
507  }
508  }
509  }
510 
511  return;
512 }
513 
514 /* ------------------------------------------------------------------- */
515 /* nKd_lee() - spectral normalized diffuse attenuation using */
516 /* Lin, et. (2016) */
517 /* */
518 /* Inputs: */
519 /* l2rec - level-2 structure containing one complete scan after */
520 /* atmospheric correction. */
521 /* band - waveband number (0 - nbands-1) at which nKd computed. */
522 /* */
523 /* Outputs: */
524 /* nKd - normalized diffuse attenuation at specified band, */
525 /* 1 value per pixel. */
526 /* */
527 /* Description: */
528 /* This produces the estimate of the normalized diffuse attenation at */
529 /* the given band using the satellite derived total absorption and */
530 /* backscattering and solar zenith angle. */
531 /* */
532 /* Reference: */
533 /* Lin, J., Lee, Z.P., Ondrusek, M. and K. Du (2016) "Remote sensing */
534 /* the normalized diffuse attenuation coefficient of downwelling */
535 /* irradiance", J. Geophys. Res. Oceans, 121: 6717-6730 */
536 /* */
537 /* Original Implementation: L. McKinna, June 2017 */
538 
539 /*---------------------------------------------------------------------*/
540 void nKd_lin(l2str *l2rec, int band, float *nKd) {
541 
542  const float m1 = 4.18;
543  const float m2 = 0.52;
544  const float m3 = -10.80;
545 
546  const float n1 = 7.41;
547  const float n2 = 0.13;
548  const float n3 = 4.36;
549  const float n4 = -0.0051;
550  const float n5 = 0.0094;
551  const float n6 = 1.78;
552  const float p = 1.14;
553  const float radCon = M_PI / 180.0; //degrees -> radians conversion
554 
555  float m0, sasw, D0, f, lambda, Kd;
556 
557  int ip, ipb;
558 
559  l1str *l1rec = l2rec->l1rec;
560 
561  if (input->iop_opt == IOPNONE) {
562  printf("IOP-based Kd_lee product requires iop model selection (iop_opt). ");
563  printf("Using default model.\n");
564  input->iop_opt = IOPDEFAULT;
565  get_iops(l2rec, input->iop_opt);
566  }
567 
568  for (ip = 0; ip < l1rec->npix; ip++) {
569 
570  ipb = ip * l1rec->l1file->nbands + band;
571  lambda = l2rec->l1rec->l1file->fwave[band];
572  sasw = asin(sin(radCon * l1rec->solz[ip]) / 1.34);
573  f = n1 * (n2 - exp(-n3 * (lambda / 490.))) - (n4 * (lambda / 490.) + n5) * exp(n6 * (l1rec->solz[ip] / 30.));
574  D0 = (f / cos(sasw)) + p * (1.0 - f);
575 
576  if (l1rec->mask[ip] ||
577  l2rec->a[ipb] <= 0.0 || l2rec->bb[ipb] <= 0.0) {
578  nKd[ip] = kdbad;
579  l1rec->flags[ip] |= PRODFAIL;
580  } else {
581  m0 = 1.0 + 0.005 * l1rec->solz[ip];
582  Kd = m0 * l2rec->a[ipb]
583  + m1 * (1.0 - m2 * exp(m3 * l2rec->a[ipb])) * l2rec->bb[ipb];
584 
585  nKd[ip] = Kd / D0;
586 
587  if (nKd[ip] > KD_MAX) {
588  nKd[ip] = KD_MAX;
589  l1rec->flags[ip] |= PRODWARN;
590  } else
591  if (nKd[ip] < KD_MIN) {
592  nKd[ip] = KD_MIN;
593  l1rec->flags[ip] |= PRODWARN;
594  }
595  }
596  }
597 
598  return;
599 }
600 
601 /* ------------------------------------------------------------------- */
602 /* Kd_PAR_lee() - spectrally integrated attenuation using ZP Lee */
603 /* */
604 /* Inputs: */
605 /* l2rec - level-2 structure containing one complete scan after */
606 /* atmospheric correction. */
607 /* */
608 /* Outputs: */
609 /* Kd - Kd(PAR), 1 value per pixel. */
610 /* */
611 /* Description: */
612 /* */
613 /* Reference: */
614 /* */
615 /* Original Implementation: B. Franz, September 2007 */
616 
617 /*---------------------------------------------------------------------*/
618 void Kd_PAR_lee(l2str *l2rec, float *Kd) {
619  void Zphotic_lee(l2str *l2rec, l2prodstr *p, float Zp[]);
620 
621  float *Zp = Kd;
622  l2prodstr p;
623  int32_t ip;
624 
625  // get first optical depth from Lee
626  p.cat_ix = CAT_Zphotic_lee;
627  p.prod_ix = -3;
628  Zphotic_lee(l2rec, &p, Zp);
629 
630  // invert to get Kd(PAR) at 1st optical depth
631  for (ip = 0; ip < l2rec->l1rec->npix; ip++) {
632  if (Zp[ip] > 0.0)
633  Kd[ip] = 1.0 / Zp[ip];
634  else {
635  Kd[ip] = kdbad;
636  l2rec->l1rec->flags[ip] |= PRODFAIL;
637  }
638  }
639 }
640 
641 /* ------------------------------------------------------------------- */
642 /* Kd_jamet() - spectral diffuse attenuation using Jamet et al, (2012) */
643 /* */
644 /* Inputs: */
645 /* l2rec - level-2 structure containing one complete scan after */
646 /* atmospheric correction. */
647 /* band - waveband number (0 - nbands-1) at which Kd computed. */
648 /* */
649 /* Outputs: */
650 /* Kd - diffuse attenuation at specified band, 1 value per pixel. */
651 /* */
652 /* Description: */
653 /* This derives the Kd using the full set of Rrs available and only */
654 /* for the SeaWiFS instrument (at this time). A neural network is */
655 /* used to perform this. */
656 /* */
657 /* Reference: */
658 /* Jamet, C., H. Loisel, and D. Dessailly (2012), Retrieval of the */
659 /* spectral diffuse attenuation coefficient Kd(lambda) in open and */
660 /* coastal ocean waters using a neural network inversion, J Geophys. */
661 /* Res., 117, C10023, doi: 10.1029/2012JC008076. */
662 /* */
663 /* Original Implementation: C. Jamet */
664 
665 /*---------------------------------------------------------------------*/
666 void Kd_jamet(l2str *l2rec, int band, float *Kd) {
667  static int firstCall = 1;
668  int ip, ipb;
669  static float alg_lambda[16], alg_inp[16];
670  int32 i, j, bad_prd;
671  static float *b1, *w1, *w2, *mean, *sd, b2;
672  static float *xnorm, *alayer;
673  static int32 nrrs, n_input, n_norm, n_nodes;
674  static int32 *bnd_ptr;
675  float y;
676 
677  filehandle *l1file = l2rec->l1rec->l1file;
678 
679  /*
680  * Initialize the coefficients for the computation
681  */
682  if (firstCall) {
683  FILE *fp;
684  char *filedir;
685  char filename[FILENAME_MAX];
686  char line [801];
687  int com_ln;
688  int poub;
689 
690  firstCall = 0;
691  if ((bnd_ptr = (int32 *) calloc(l1file->nbands, sizeof (int32))) == NULL) {
692  printf("-E- : Error allocating memory to bnd_ptr in get_Kd\n");
693  exit(FATAL_ERROR);
694  }
695  if ((filedir = getenv("OCDATAROOT")) == NULL) {
696  printf("-E- %s, %d: OCDATAROOT env variable undefined.\n",
697  __FILE__, __LINE__);
698  exit(1);
699  }
700  strcpy(filename, filedir);
701  switch (l1file->sensorID) {
702  case SEAWIFS:
703  strcat(filename, "/seawifs/iop/kd_jamet/seawifs_kd_jamet.dat");
704  break;
705  case MERIS:
706  strcat(filename, "/meris/iop/kd_jamet/meris_kd_jamet.dat");
707  break;
708  default:
709  printf(
710  "-E- %s, %d: Kd_jamet can only be generated for the SEAWIFS instrument\n",
711  __FILE__, __LINE__);
712  exit(1);
713  break;
714  }
715  printf("Loading Kd coefficient table %s\n", filename);
716 
717  if ((fp = fopen(filename, "r")) == NULL) {
718  fprintf(stderr, "-E- %s, %d: unable to open %s for reading\n",
719  __FILE__, __LINE__, filename);
720  exit(1);
721  }
722  /*
723  * read the # bands for the Rrs and their values
724  */
725  com_ln = 1;
726  while (com_ln == 1) {
727  if (fgets(line, 800, fp) == NULL) {
728  fprintf(stderr, "-E- %s, %d: Error reading %s\n",
729  __FILE__, __LINE__, filename);
730  exit(1);
731  }
732  if (line[0] != ';') com_ln = 0;
733  }
734  if (sscanf(line, "%d", &nrrs) != 1) {
735  fprintf(stderr, "-E- %s, %d: Error reading %s\n",
736  __FILE__, __LINE__, filename);
737  exit(1);
738  }
739  for (i = 0; i < nrrs; i++) {
740  fgets(line, 800, fp);
741  sscanf(line, "%f", &alg_lambda[i]);
742  }
743  n_input = nrrs + 1;
744  n_norm = n_input + 1;
745  /*
746  * get the # nodes in the hidden layer
747  */
748  com_ln = 1;
749  while (com_ln == 1) {
750  fgets(line, 800, fp);
751  if (line[0] != ';') com_ln = 0;
752  }
753  sscanf(line, "%d", &n_nodes);
754 
755  b1 = malloc(n_nodes * sizeof ( float));
756  w1 = malloc(n_nodes * n_norm * sizeof ( float));
757  w2 = malloc(n_nodes * sizeof ( float));
758  mean = malloc(n_norm * sizeof ( float));
759  sd = malloc(n_norm * sizeof ( float));
760  xnorm = malloc(n_input * sizeof ( float));
761  alayer = malloc(n_nodes * sizeof ( float));
762 
763  if ((b1 == NULL) || (w1 == NULL) || (w2 == NULL) ||
764  (mean == NULL) || (sd == NULL) ||
765  (xnorm == NULL) || (alayer == NULL)) {
766  fprintf(stderr, "-E- %s, %d: allocation of Kd weights failed\n",
767  __FILE__, __LINE__);
768  exit(1);
769  }
770  /*
771  * get the weights for the hidden and output layers
772  */
773  com_ln = 1;
774  while (com_ln == 1) {
775  fgets(line, 800, fp);
776  if (line[0] != ';') com_ln = 0;
777  }
778  /* ( this line is a throw-away line) */
779  for (i = 0; i < n_nodes; i++) {
780  fgets(line, 800, fp);
781  sscanf(line, "%d %d %f", &poub, &poub, &b1[i]);
782  }
783  fgets(line, 800, fp);
784  sscanf(line, "%d %d %f", &poub, &poub, &b2);
785  for (j = 0; j < n_input; j++)
786  for (i = 0; i < n_nodes; i++) {
787  fgets(line, 800, fp);
788  sscanf(line, "%d %d %f", &poub, &poub, (w1 + i + n_nodes * j));
789  }
790  for (j = 0; j < n_nodes; j++) {
791  fgets(line, 800, fp);
792  sscanf(line, "%d %d %f", &poub, &poub, &w2[j]);
793  }
794  /*
795  * lastly, read the mean and standard deviation data
796  */
797  com_ln = 1;
798  while (com_ln == 1) {
799  fgets(line, 800, fp);
800  if (line[0] != ';') com_ln = 0;
801  }
802  sscanf(line, "%f", &mean[0]);
803  for (i = 1; i < n_norm; i++) {
804  fgets(line, 800, fp);
805  sscanf(line, "%f", &mean[i]);
806  }
807  com_ln = 1;
808  while (com_ln == 1) {
809  fgets(line, 800, fp);
810  if (line[0] != ';') com_ln = 0;
811  }
812  sscanf(line, "%f", &sd[0]);
813  for (i = 1; i < n_norm; i++) {
814  fgets(line, 800, fp);
815  sscanf(line, "%f", &sd[i]);
816  }
817  fclose(fp);
818  /*
819  * set up correct band locations
820  */
821  printf("Kd_jamet band assignment:\n");
822  printf("Alg wave inst wave inst band\n");
823  for (i = 0; i < nrrs; i++) {
824  bnd_ptr[i] = windex(alg_lambda[i], l1file->fwave, l1file->nbands);
825  printf("%7.2f %7.2f %3d\n", alg_lambda[i],
826  l1file->fwave[bnd_ptr[i]], bnd_ptr[i]);
827  }
828  printf("\n");
829  }
830  /*
831  * derive the Kd
832  */
833  for (ip = 0; ip < l2rec->l1rec->npix; ip++) {
834  ipb = ip * l1file->nbands;
835  /*
836  * Normalize the Rrs, lambda
837  */
838  bad_prd = 0;
839  if (l2rec->l1rec->mask[ip]) bad_prd = 1;
840  /*
841  for( i = 0; i < nrrs; i++ )
842  {
843  alg_inp[i] = l2rec->Rrs[ ipb + bnd_ptr[i] ];
844  if( i <= 1 ) {
845  if( alg_inp[i] <= -0.01 ) bad_prd = 1;
846  }
847  else {
848  if( alg_inp[i] <= 0.0 ) bad_prd = 1;
849  }
850  }
851  */
852  for (i = 0; i < nrrs; i++) {
853  alg_inp[i] = l2rec->Rrs[ ipb + bnd_ptr[i] ];
854  if (alg_inp[i] < BAD_FLT + 1) bad_prd = 1;
855  }
856  alg_inp[nrrs] = l1file->fwave[band];
857 
858  if (bad_prd == 1) {
859  Kd[ip] = kdbad;
860  l2rec->l1rec->flags[ip] |= PRODFAIL;
861  } else {
862  for (i = 0; i < n_input; i++)
863  xnorm[i] = ((2. / 3.) *(alg_inp[i] - mean[i])) / sd[i];
864  /*
865  * Apply the nn algorithm
866  */
867  for (i = 0; i < n_nodes; i++) {
868  alayer[i] = 0.0;
869  for (j = 0; j < n_input; j++) {
870  alayer[i] += (xnorm[j] * *(w1 + i + n_nodes * j));
871  }
872  alayer[i] = 1.715905 * (float) tanh((2. / 3.) *
873  (double) (alayer[i] + b1[i]));
874  }
875 
876  for (j = 0, y = 0.; j < n_nodes; j++)
877  y += (alayer[j] * w2[j]);
878  /*
879  * De-normalize the log(Kd) and make Kd
880  */
881  y = 1.5 * (y + b2) * sd[n_input] + mean[n_input];
882  *(Kd + ip) = (float) pow(10., (double) y);
883  }
884  }
885  return;
886 }
887 
888 /* ------------------------------------------------------------------- */
889 /* Kd_rhos() - spectral diffuse attenuation using Stumpf, et. (2013) */
890 /* */
891 /* Inputs: */
892 /* l2rec - level-2 structure containing one complete scan after */
893 /* atmospheric correction. */
894 /* band - waveband number (0 - nbands-1) at which Kd computed. */
895 /* */
896 /* Outputs: */
897 /* Kd - diffuse attenuation at specified band, 1 value per pixel. */
898 /* */
899 /* Description: */
900 /* This produces the estimate of diffuse attenation at the given band */
901 /* using the satellite derived total absorption and backscattering. */
902 /* */
903 /* Reference: */
904 /* Original Implementation: R. Healy, NASA-GSFC, August 2015 */
905 
906 /*---------------------------------------------------------------------*/
907 void Kd_rhos(l2str *l2rec, float *Kd) {
908 
909  const float factor = 0.7;
910 
911  static int ib443, ib490, ib620, ib665, ib645, ib469, ib859, ib865, firstCall = 1;
912  int ip, ipb;
913 
914  if (firstCall == 1) {
915 
916  ib645 = bindex_get(645);
917  ib469 = bindex_get(469);
918  ib859 = bindex_get(859);
919  firstCall = 0;
920 
921  ib443 = -1;
922  if (ib645 < 0 || ib469 < 0 || ib859 < 0) {
923  // try the Meris wavelengths
924 
925  ib443 = bindex_get(443);
926  ib490 = bindex_get(490);
927  ib620 = bindex_get(620);
928  ib665 = bindex_get(665);
929  ib865 = bindex_get(865);
930  firstCall = 0;
931 
932 
933  if (ib443 < 0 || ib490 < 0 || ib620 < 0 || ib665 < 0 || ib865 < 0) {
934  printf("Kd_rhos: incompatible sensor wavelengths for this algorithm\n");
935  exit(1);
936  } else {
937  printf("Kd_rhos: Using the Meris band averaging for this product\n");
938  }
939 
940  }
941 
942  }
943 
944  if (ib443 < 0) {
945  for (ip = 0; ip < l2rec->l1rec->npix; ip++) {
946 
947  ipb = l2rec->l1rec->l1file->nbands*ip;
948 
949  // if (l2rec->mask[ip] ||
950  if (l2rec->Rrs[ipb + ib645] <= 0.0 || l2rec->Rrs[ipb + ib469] <= 0.0 || l2rec->Rrs[ipb + ib859] <= 0.0) {
951  Kd[ip] = kdbad;
952  l2rec->l1rec->flags[ip] |= PRODFAIL;
953  } else {
954  Kd[ip] = factor * (l2rec->l1rec->rhos[ipb + ib645] - l2rec->l1rec->rhos[ipb + ib859])
955  / (l2rec->l1rec->rhos[ipb + ib469] - l2rec->l1rec->rhos[ipb + ib859]);
956  }
957  }
958  } else {
959  for (ip = 0; ip < l2rec->l1rec->npix; ip++) {
960 
961  ipb = l2rec->l1rec->l1file->nbands*ip;
962 
963  // if (l2rec->mask[ip] ||
964  if (l2rec->Rrs[ipb + ib665] <= 0.0 || l2rec->Rrs[ipb + ib620] <= 0.0 || l2rec->Rrs[ipb + ib443] <= 0.0 || l2rec->Rrs[ipb + ib490] <= 0.0 || l2rec->Rrs[ipb + ib865] <= 0.0) {
965  Kd[ip] = kdbad;
966  l2rec->l1rec->flags[ip] |= PRODFAIL;
967  } else {
968  Kd[ip] = factor * ((l2rec->l1rec->rhos[ipb + ib620] + l2rec->l1rec->rhos[ipb + ib665]) / 2 - l2rec->l1rec->rhos[ipb + ib865])
969  / ((l2rec->l1rec->rhos[ipb + ib443] + l2rec->l1rec->rhos[ipb + ib490]) / 2 - l2rec->l1rec->rhos[ipb + ib865]);
970  }
971 
972  }
973  }
974  return;
975 }
976 
977 /* ------------------------------------------------------------------- */
978 /* get_Kd() - l2_hdf_generic interface for Kd */
979 
980 /* ------------------------------------------------------------------- */
981 void get_Kd(l2str *l2rec, l2prodstr *p, float prod[]) {
982  switch (p->cat_ix) {
983  case CAT_Kd_mueller:
984  Kd490_mueller(l2rec, prod);
985  break;
986  case CAT_Kd_obpg:
987  Kd490_obpg(l2rec, prod);
988  break;
989  case CAT_Kd_KD2:
990  Kd490_KD2(l2rec, prod);
991  break;
992  case CAT_Kd_lee:
993  Kd_lee(l2rec, p->prod_ix, prod);
994  break;
995  case CAT_Kd_532:
996  Kd490_mueller(l2rec, prod);
997  Kd532(l2rec, p->prod_ix, prod);
998  break;
999  case CAT_Kd_morel:
1000  Kd490_morel(l2rec, prod);
1001  break;
1002  case CAT_Kd_jamet:
1003  Kd_jamet(l2rec, p->prod_ix, prod);
1004  break;
1005  case CAT_Kd_rhos:
1006  Kd_rhos(l2rec, prod);
1007  break;
1008  case CAT_KPAR_morel:
1009  Kd_PAR_morel(l2rec, p->prod_ix, prod);
1010  break;
1011  case CAT_KPAR_lee:
1012  Kd_PAR_lee(l2rec, prod);
1013  break;
1014  case CAT_Zhl_morel:
1015  Zhl_morel(l2rec, prod);
1016  break;
1017  case CAT_nKd_lin:
1018  nKd_lin(l2rec, p->prod_ix, prod);
1019  break;
1020  default:
1021  printf("Error: %s : Unknown product specifier: %d\n", __FILE__, p->cat_ix);
1022  exit(1);
1023  break;
1024  }
1025 
1026  return;
1027 }
1028 
1029 
1030 /* =================================================================== */
1031 /* defunct versions */
1032 /* =================================================================== */
1033 
1034 
1035 /* ------------------------------------------------------------------- */
1036 /* Kd_morel() - spectral diffuse attenuation using Morel (2007) */
1037 /* */
1038 /* Inputs: */
1039 /* l2rec - level-2 structure containing one complete scan after */
1040 /* atmospheric correction. */
1041 /* band - waveband number (0 - nbands-1) at which Kd computed. */
1042 /* */
1043 /* Outputs: */
1044 /* Kd - diffuse attenuation at specified band, 1 value per pixel. */
1045 /* */
1046 /* Description: */
1047 /* This produces the estimate of diffuse attenation at the given band */
1048 /* using the satellite derived chlorophyll. */
1049 /* */
1050 /* Reference: */
1051 /* */
1052 /* Morel, A., Y. Huot, B. Gentili, P.J. Werdell, S.B. Hooker (2007). */
1053 /* Consistency of products derived from various ocean color sensors: */
1054 /* An examination before merging these products and extending their */
1055 /* applications, Remote Sensing of Environment, to be submitted. */
1056 /* */
1057 /* Equation 8 */
1058 /* */
1059 /* Original Implementation: B. Franz, August 2006 */
1060 /*---------------------------------------------------------------------*/
1061 /*
1062 void Kd_morel(l2str *l2rec, int band, float *Kd)
1063 {
1064  typedef struct kd_morel_table {
1065  float wave;
1066  float Kw;
1067  float X;
1068  float e;
1069  } kdtabstr;
1070 
1071  static kdtabstr *kdtab = NULL;
1072  static int ntab = 0;
1073 
1074  float chl;
1075  float Kd1, Kd2;
1076  float wt;
1077  float wave;
1078  int32_t ip, itab, i1, i2;
1079 
1080  if (kdtab == NULL) {
1081  FILE *fp = NULL;
1082  char *tmp_str;
1083  char file [FILENAME_MAX] = "";
1084  if ((tmp_str = getenv("OCDATAROOT")) == NULL) {
1085  printf("OCDATAROOT environment variable is not defined.\n");
1086  exit(1);
1087  }
1088  strcpy(file,tmp_str); strcat(file,"/common/kd_morel.dat");
1089  if ( (fp = fopen(file,"r")) == NULL ) {
1090  printf("-E- %s: Error opening file %s.\n",__FILE__,file);
1091  exit(1);
1092  }
1093  printf("\nLoading Morel spectral Kd table\n %s\n",file);
1094  fscanf(fp,"%d",&ntab);
1095  if (ntab <= 0) {
1096  printf("-E- %s: Error reading %s.\n",__FILE__,file);
1097  exit(1);
1098  }
1099  if ((kdtab = malloc(ntab*sizeof(kdtabstr))) == NULL) {
1100  printf("-E- %s: Error allocating space for %d records.\n",__FILE__,ntab);
1101  exit(1);
1102  }
1103  for (itab=0; itab<ntab; itab++) {
1104  fscanf(fp,"%f %f %f %f",
1105  &kdtab[itab].wave,
1106  &kdtab[itab].Kw,
1107  &kdtab[itab].X,
1108  &kdtab[itab].e);
1109  printf("%f %f %f %f\n",
1110  kdtab[itab].wave,
1111  kdtab[itab].Kw,
1112  kdtab[itab].X,
1113  kdtab[itab].e);
1114  }
1115  fclose(fp);
1116  printf("\n");
1117  }
1118 
1119  if (band >= 0)
1120  wave = l2rec->fwave[band];
1121  else
1122  wave = 490.;
1123 
1124  for (itab=0; itab<ntab; itab++)
1125  if (wave <= kdtab[itab].wave)
1126  break;
1127 
1128  if (wave == kdtab[itab].wave) {
1129  i1 = itab;
1130  i2 = itab;
1131  } else if (itab == ntab) {
1132  i1 = itab-2;
1133  i2 = itab-1;
1134  wt = (wave-kdtab[i1].wave)/(kdtab[i2].wave - kdtab[i1].wave);
1135  } else {
1136  i1 = itab;
1137  i2 = itab+1;
1138  wt = (wave-kdtab[i1].wave)/(kdtab[i2].wave - kdtab[i1].wave);
1139  }
1140 
1141  for (ip=0; ip<l2rec->npix; ip++) {
1142 
1143  chl = l2rec->chl[ip];
1144 
1145  if (l2rec->mask[ip] || chl <= 0.0) {
1146  Kd[ip] = kdbad;
1147  l2rec->flags[ip] |= PRODFAIL;
1148  } else {
1149  if (i1 == i2) {
1150  Kd[ip] = kdtab[i1].Kw + kdtab[i1].X * pow(chl,kdtab[i1].e);
1151  } else {
1152  Kd1 = kdtab[i1].Kw + kdtab[i1].X * pow(chl,kdtab[i1].e);
1153  Kd2 = kdtab[i2].Kw + kdtab[i2].X * pow(chl,kdtab[i2].e);
1154  Kd[ip] = Kd1 + (Kd2-Kd1)*wt;
1155  }
1156  if (Kd[ip] > KD_MAX) {
1157  Kd[ip] = KD_MAX;
1158  l2rec->flags[ip] |= PRODWARN;
1159  } else
1160  if (Kd[ip] < KD_MIN) {
1161  Kd[ip] = KD_MIN;
1162  l2rec->flags[ip] |= PRODWARN;
1163  }
1164  }
1165  }
1166 
1167  return;
1168 }
1169  */
int32 l1file(int32 sdfid, int32 *nsamp, int32 *nscans, int16 *dtynum)
Definition: l1stat_chk.c:586
int j
Definition: decode_rs.h:73
void get_Kd(l2str *l2rec, l2prodstr *p, float prod[])
Definition: get_Kd.c:981
#define CAT_KPAR_lee
Definition: l2prod.h:164
float mean(float *xs, int sample_size)
Definition: numerical.c:81
#define CHLFAIL
Definition: l2_flags.h:26
#define NULL
Definition: decode_rs.h:63
read l1rec
PARAM_TYPE_NONE Default value No parameter is buried in the product name name_prefix is case insensitive string compared to the product name PARAM_TYPE_VIS_WAVE The visible wavelength bands from the sensor are buried in the product name The product name is compared by appending and name_suffix ie aph_412_giop where prod_ix will be set to PARAM_TYPE_IR_WAVE same search method as PARAM_TYPE_VIS_WAVE except only wavelength above are looped through but prod_ix is still based ie aph_2_giop for the second band
void Kd532(l2str *l2rec, int flag, float k532[])
Definition: get_Kd.c:405
#define PRODWARN
Definition: l2_flags.h:13
#define MERIS
Definition: sensorDefs.h:22
void Kd_PAR_lee(l2str *l2rec, float *Kd)
Definition: get_Kd.c:618
#define IOPDEFAULT
Definition: l12_parms.h:76
#define PRODFAIL
Definition: l2_flags.h:41
data_t lambda[NROOTS+1]
Definition: decode_rs.h:75
void Kd_PAR_morel(l2str *l2rec, int depth, float *Kd)
Definition: get_Kd.c:290
#define IOPNONE
Definition: l12_parms.h:67
#define KD_MIN
Definition: get_Kd.c:12
instr * input
character(len=1000) if
Definition: names.f90:13
void Kd490_morel(l2str *l2rec, float *Kd)
Definition: get_Kd.c:233
#define KD_MAX
Definition: get_Kd.c:11
int bindex_get(int32_t wave)
Definition: windex.c:45
void Kd490_mueller(l2str *l2rec, float k490[])
Definition: get_Kd.c:92
double precision function f(R1)
Definition: tmd.lp.f:1454
void Kd490_obpg(l2str *l2rec, float k490[])
Definition: get_Kd.c:143
void Kd_jamet(l2str *l2rec, int band, float *Kd)
Definition: get_Kd.c:666
#define CAT_KPAR_morel
Definition: l2prod.h:144
void nKd_lin(l2str *l2rec, int band, float *nKd)
Definition: get_Kd.c:540
void get_iops(l2str *l2rec, int32_t iop_opt)
Definition: convl12.c:113
#define FATAL_ERROR
Definition: swl0_parms.h:5
#define M_PI
Definition: pml_iop.h:15
#define CAT_Kd_morel
Definition: l2prod.h:142
char filename[FILENAME_MAX]
Definition: atrem_corl1.h:122
integer, parameter double
#define CAT_Kd_mueller
Definition: l2prod.h:38
instead the metadata field ProcessingEnvinronment is filled in from the output of a call to the POSIX compliant function uname from within the L1B code A small bug in L1B_Tables an incorrect comparison of RVS coefficients for TEBs to RVS coefficients for RSBs was being made This was replaced with a comparison between TEB coefficients This error never resulted in an incorrect RVS correction but did lead to recalculating the coefficients for each detector in a thermal band even if the coefficients were the same for all detectors To reduce to overall size of the reflective LUT HDF fill values were eliminated from all LUTs previously dimensioned where and where NUM_TIMES is the number of time dependent table pieces In Preprocess a small error where the trailing dropped scan counter was incremented when the leading dropped scan counter should have been was fixed This counter is internal only and is not yet used for any chiefly to casting of were added to make it LINUX compatible Output of code run on LINUX machines displays differences of at most scaled sector incalculable values of the Emissive calibration factor b1
Definition: HISTORY.txt:576
data_t b[NROOTS+1]
Definition: decode_rs.h:77
#define CAT_Kd_KD2
Definition: l2prod.h:150
#define CAT_Kd_jamet
Definition: l2prod.h:283
#define CAT_Kd_532
Definition: l2prod.h:163
#define CAT_Zphotic_lee
Definition: l2prod.h:160
#define BAD_FLT
Definition: jplaeriallib.h:19
#define OCTS
Definition: sensorDefs.h:14
int windex(float wave, float twave[], int ntwave)
Definition: windex.c:73
void Zphotic_lee(l2str *l2rec, l2prodstr *p, float Zp[])
Definition: photic_depth.c:37
#define CAT_Kd_obpg
Definition: l2prod.h:109
void Kd_rhos(l2str *l2rec, float *Kd)
Definition: get_Kd.c:907
#define R
Definition: make_L3_v1.1.c:96
#define CAT_Zhl_morel
Definition: l2prod.h:145
#define SEAWIFS
Definition: sensorDefs.h:12
#define CAT_nKd_lin
Definition: l2prod.h:372
int i
Definition: decode_rs.h:71
#define CAT_Kd_lee
Definition: l2prod.h:108
void Kd_lee(l2str *l2rec, int band, float *Kd)
spectral diffuse attenuation using Lee, et. (2005, 2016)
Definition: get_Kd.c:467
#define CAT_Kd_rhos
Definition: l2prod.h:322
How many dimensions is the output array Default is Not sure if anything above will work correctly strcpy(l2prod->title, "no title yet")
PGE01 indicating that PGE02 PGE01 V6 for and PGE01 V2 for MOD03 were used to produce the granule By convention adopted in all MODIS Terra PGE02 code versions are The fourth digit of the PGE02 version denotes the LUT version used to produce the granule The source of the metadata environment variable ProcessingCenter was changed from a QA LUT value to the Process Configuration A sign used in error in the second order term was changed to a
Definition: HISTORY.txt:424
void Zhl_morel(l2str *l2rec, float *Zhl)
Definition: get_Kd.c:352
float p[MODELMAX]
Definition: atrem_corl1.h:131
void Kd490_KD2(l2str *l2rec, float *Kd)
Definition: get_Kd.c:28