OB.DAAC Logo
NASA Logo
Ocean Color Science Software

ocssw V2022
pml_iop_calculate.c
Go to the documentation of this file.
1 #include "l12_proto.h"
2 #include "pml_iop.h"
3 #include "pml_iop_config.h"
4 #include "pml_iop_tables.h"
5 #include "pml_iop_calculate.h"
6 
7 /* Module: pml_iop_calculate */
8 /* Authors: Tim Smyth and Gerald Moore */
9 /* Date: 13/2/2006 */
10 /* Version: 1.0 */
11 /* Description */
12 
13 /* Function: iop_model */
14 int iop_model(double rho_w[], float sun_theta, float sen_theta, float dphi, double a[],
15  double bbp[], double ady[], double ap[], int MODIS, int CASEII) {
16 
17  int i, result, lower = 0, upper = 1;
18  int iop_model_error = 0; /* successful iop calculation */
19  float eps_a, ady_0;
20  float adyu, adyl, apl, au, al, ysbpa_sc;
21  float x1, x2;
22 
23  eps_a = eps_a_init;
24 
25  /* If the iw531 exists (set to 3) then use MODIS scalings */
26  if (MODIS > 0)
27  eps_a = eps_a_init_modis;
28 
29  /* calculate the total absorption and backscatter */
30  /* only do calculation if rho_w values are sensible */
31 
32  if (rho_w[1] > 0. && rho_w[2] > 0. && rho_w[3] > 0. &&
33  rho_w[4] > 0.) {
34  result = mod_iter(rho_w, sun_theta, sen_theta, dphi, eps_a, a, bbp, MODIS, CASEII);
35  if (result != 0) {
36  iop_model_error = 1; /* unsuccessful primary IOP calculation */
37  for (i = 0; i < NB; i++) {
38  a[i] = 0.;
39  bbp[i] = 0.;
40  }
41  return iop_model_error;
42  }
43 
44  /* using the spectral slopes between 412 and 443 work out ady */
45  /* only calculate if a_412 and a_443 are greater than zero */
46  if (a[0] > 0. && a[1] > 0. && a[2] > 0.) {
47 
48  /* new formulation to account for non-linear behavior */
49  /* of spectral slope in ady */
50  if (ysbpa_l == 443.0) {
51  lower = 1; /* 443.0 */
52  upper = 2; /* 490.0 */
53  }
54 
55  al = a[lower];
56  au = a[upper];
57 
58  result = biogeochem_iter(al, au, &adyu, &adyl, &apl);
59  if (result != 0) {
60  iop_model_error = 2;
61  for (i = 0; i < NB; i++) {
62  ady[i] = 0.;
63  ap[i] = 0.;
64  }
65  return iop_model_error;
66  }
67 
68  ady[lower] = adyl;
69  ady[upper] = adyu;
70  ap[lower] = apl;
71 
72  if (ysbpa_l == 443.0)
73  ady_0 = ady[lower];
74  else
75  ady_0 = ady[upper];
76 
77  /* calculate the spectral slope of CDOM from info at two shorter wavelengths */
78  ysbpa_sc = log(ady[lower] / ady[upper]) / (lambda[lower] - lambda[upper]);
79 
80  for (i = 0; i < NB; i++) {
81  /* Apply gelbstoff slope and intercept (at 443) to give entire spectra */
82  ady[i] = ady_0 * exp(ysbpa_sc * (lambda[i] - ysbpa_0));
83  /* Calculate spectral ap (absorption due to pigments) using the remainder */
84  ap[i] = a[i] - ady[i];
85  }
86 
87  /* check aph443 range (this sanity check is an empirical fix from the Lee model */
88  x1 = ap[1] / a[1];
89  if (x1 < 0.15 || x1 > 0.6) {
90  /* ratio is between 443 and 412 */
91  x2 = -0.8 + 1.4 * (a[1] / a[0]);
92  if (x2 < 0.15)
93  x2 = 0.15;
94  if (x2 > 0.6)
95  x2 = 0.6;
96  ap[1] = a[1] * x2;
97  ady_0 = a[1] - ap[1];
98 
99  for (i = 0; i < NB; i++) {
100  /* Use the preset slope for gelbstoff */
101  ady[i] = ady_0 * exp(ysbpa_s * (lambda[i] - ysbpa_0));
102  ap[i] = a[i] - ady[i];
103  }
104  }
105  /* End of the empirical fix */
106  } else {
107  for (i = 0; i < NB; i++) {
108  ady[i] = 0.;
109  ap[i] = 0.;
110  }
111  iop_model_error = 2; /* unsuccessful bio-geochemical IOP calculation */
112  }
113  }
114 
115  return iop_model_error;
116 }
117 
118 /* Function: mod_iter */
119 int mod_iter(double rho_w[], float sun_theta, float sen_theta, float dphi, float eps_a, double a[], double bb[], int MODIS, int CASEII) {
120  int i, iter, fail;
121  int mod_iter_init = 0;
122  double b[NB], FC[NB], F[NB];
123  double rho_wT[2], awT[2], bbwT[2], FT[2], df;
124  double ab[2];
125  double temp, eps_b;
126  float bbw[NB]; /* backscatter due to pure water */
127  float init_a[NB], init_b[NB], bn[NB]; /* Initial guess at IOP values */
128 
129  /* Modification for MODIS - reference scattering wavelength */
130  if (MODIS > 0)
132 
133  /* Initial guess for scattering and absorption */
134  /* (based on init_chl) */
135  if (mod_iter_init == 0) {
136  temp = scat_a + scat_b * lambda[bp[1]] + scat_c * pow(lambda[bp[1]] / scat_l, scat_n);
137  for (i = 0; i < NB; i++) {
138  init_a[i] = geo2iop(ch_lev, ac, i, init_chl, ch_n);
139  init_b[i] = geo2iop(ch_lev, bc, i, init_chl, ch_n);
140  bbw[i] = b_w[i] * b_tilde_w;
141  bn[i] = scat_a + scat_b * lambda[bp[1]] + scat_c * pow(lambda[i] / scat_l, scat_n);
142  bn[i] = bn[i] / temp;
143  }
144  /* This comes out with spectrally flat eps_b */
145  eps_b = bn[bp[0]];
146 
147  /* This is a flag raised once the iterations have been initialised */
148  mod_iter_init = 1;
149  }
150 
151  if (NFLAG == 1) {
152  /* Re-evaluate scattering slope */
153  /* Set here to scat_n - but originally a user defined input 'n' */
154  /* Re-evaluate if neccessary */
155  temp = pow(lambda[bp[1]] / scat_l, scat_n);
156  for (i = 0; i < NB; i++) {
157  bn[i] = pow(lambda[i] / scat_l, scat_n);
158  bn[i] = bn[i] / temp;
159  }
160  /* The result of this is eps_b of 1.0202 for SeaWiFS (490:510) */
161  eps_b = bn[bp[0]];
162  }
163 
164  /* Set the relevant geometry */
165  setgeom(sun_theta, sen_theta, dphi);
166  /* Prepare the iterations */
167  for (i = 0; i < NB; i++) {
168  /* set the reflectance to zero if it is negative */
169  if (rho_w[i] < 0.0) rho_w[i] = 0.0;
170  a[i] = init_a[i];
171  b[i] = init_b[i];
172  bb[i] = b[i] * b_tilde_p;
173 
174  if (CASEII)
175  FC[i] = f_ab(init_a[i], init_b[i], i) / (1. + bb[i] / a[i]);
176  else
177  FC[i] = f_ab(init_a[i], init_b[i], i);
178 
179  }
180 
181  iter = fail = 0;
182 
183  /* Iteration loop */
184  do {
185  /* set the F(=pi*R*(f/Q)) for each band - */
186  /* this is reset during the iterations */
187  for (i = 0; i < NB; i++)
188  F[i] = FC[i];
189 
190  /* temporary arrays*/
191  rho_wT[0] = rho_w[bp[0]];
192  rho_wT[1] = rho_w[bp[1]];
193  awT[0] = a_w[bp[0]];
194  awT[1] = a_w[bp[1]];
195  bbwT[0] = bbw[bp[0]];
196  bbwT[1] = bbw[bp[1]];
197  FT[0] = F[bp[0]];
198  FT[1] = F[bp[1]];
199 
200  /* Calculate new a(510 (or 531 MODIS)) and bb(510 (or 531 MODIS)) values */
201  if (CASEII)
202  iter_ab2(rho_wT, awT, bbwT, FT, eps_b, eps_a, ab);
203  else
204  iter_ab(rho_wT, awT, bbwT, FT, eps_b, eps_a, ab);
205 
206  /* Evaluate a490*/
207  /* This is done via the empirically calculated slopes */
208  a[bp[0]] = ab[0] * eps_a; /* 490 nm */
209  a[bp[1]] = ab[0]; /* 510 nm (SeaWiFS) 531 (MODIS) */
210  /* df = 0.; */
211  for (i = 0; i < NB; i++) {
212  bb[i] = ab[1] * bn[i]; /* spectral bb assuming slope */
213  /* don't re-evaluate 490 and 510 (531) */
214  if ((i != bp[0]) || (i != bp[1])) {
215  if (rho_w[i] > 0.0) {
216 
217  if (CASEII)
218  a[i] = iter_a2(rho_w[i], a_w[i], bbw[i], F[i], bb[i]);
219  else
220  a[i] = iter_a(rho_w[i], a_w[i], bbw[i], F[i], bb[i]);
221  } else a[i] = 0.0;
222  }
223 
224  /* Calculate new values of b for F */
225  b[i] = bb[i] / b_tilde_p;
226 
227  /* Evaluate new F values */
228  FC[i] = f_ab(a[i], b[i], i);
229  }
230 
231  df = fabs(FC[bp[0]] - F[bp[0]]) + fabs(FC[bp[1]] - F[bp[1]]);
232 
233  /* Evaluate */
234  iter++;
235  } while ((iter <= maxit) && (df >= tol));
236 
237  if (iter > maxit)
238  fail = 2;
239 
240  return fail;
241 }
242 
243 /* Function: iter_ab */
244 
245 /* Returns the new a and b values */
246 int iter_ab(double rho_w[], double aw[], double bbw[], double F[], double epsb, double epsa, double ab[]) {
247  int result = 0;
248  double x, y, z;
249  double scale;
250 
251  x = F[0] * rho_w[1];
252  y = epsb*x;
253  z = epsa * F[1] * rho_w[0];
254  scale = y - z;
255  if (scale == 0.0) {
256  ab[0] = -1.0;
257  ab[1] = -1.0;
258  result = 1;
259  return result;
260  }
261  /* a510 (a531) */
262  ab[0] = (F[0] * F[1]*(bbw[1] * epsb - bbw[0]) + aw[0] * F[1] * rho_w[0] - aw[1] * y) / scale;
263  /* bb510 (bb531) */
264  ab[1] = (rho_w[0] * rho_w[1]*(aw[0] - aw[1] * epsa) - bbw[0] * x + bbw[1] * z) / scale;
265  return result;
266 }
267 
268 /* Function: iter_ab2 */
269 
270 /* Returns the new a and b values for CASEII formulation */
271 int iter_ab2(double rho_w[], double aw[], double bbw[], double F[], double epsb, double epsa, double ab[]) {
272  int result = 0;
273  double y, z;
274  double scale_bb, scale_a;
275 
276  z = epsa * (rho_w[0] * rho_w[1] - F[1] * rho_w[0]);
277  y = epsb * (rho_w[1] * F[0] - rho_w[1] * rho_w[0]);
278  scale_bb = z + y;
279 
280  z = epsa * rho_w[0]*(rho_w[1] - F[1]);
281  y = epsb * rho_w[1]*(rho_w[0] - F[0]);
282  scale_a = z + y;
283 
284  if (scale_a == 0.0 || scale_bb == 0.0) {
285  ab[0] = -1.0;
286  ab[1] = -1.0;
287  result = 1;
288  return result;
289  }
290  /* a510 (a531) */
291  ab[0] = (epsb * (rho_w[0] - F[0])*(F[1] * bbw[1] - rho_w[1]*(aw[1] + bbw[1]))-(rho_w[1] - F[1])*(F[0] * bbw[0] - rho_w[0]*(aw[0] + bbw[0]))) / scale_a;
292 
293  /* bb510 (bb531) */
294  ab[1] = (rho_w[0]*(epsa * (F[1] * bbw[1] - rho_w[1]*(aw[1] + bbw[1])) + rho_w[1]*(aw[0] + bbw[0])) - rho_w[1] * F[0] * bbw[0]) / scale_bb;
295 
296  return result;
297 }
298 
299 /* Function: iter_a */
300 
301 /* Solve the absorption coefficient give the backscatter */
302 double iter_a(double rho_w, double aw, double bbw, double F, double bb) {
303  double a;
304  a = F * (bb + bbw) / rho_w - aw;
305  return a;
306 }
307 
308 /* Function: iter_a2 */
309 /* Solve the absorption coefficient given the backscatter */
310 
311 /* Case II waters */
312 double iter_a2(double rho_w, double aw, double bbw, double F, double bb) {
313  double a;
314  a = F * (bb + bbw) / rho_w - (aw + bb + bbw);
315  return a;
316 }
317 
318 /* Function: biogeochem_iter */
319 /* Iterates until convergence on the "measured" value of total a */
320 /* The nomenclature is as follows: */
321 /* l: lower wavelength (412 or 443) */
322 
323 /* u: upper wavelength (443 or 490) */
324 int biogeochem_iter(float al, float au, float *adyu, float *adyl, float *aphl) {
325  float adyu_upper, adyu_lower, adyu_next;
326  float al_m, df;
327  float aphl_m, adyl_m, last_iter;
328  float TOL = 0.001;
329  float MAXIT = 20;
330  int iter = 0, exit_iter_flag = 0;
331 
332  /* Lower first guess for adyu = 0.01*au */
333  /* Upper first guess for adyu = au - biogeochem tolerance */
334  adyu_lower = 0.01 * au;
335  adyu_upper = au - TOL;
336 
337  /* first pass of the functions to get initial guesses */
338  biogeochem_mod(au, adyu_lower, &al_m, &adyl_m, &aphl_m);
339  biogeochem_mod(au, adyu_upper, &al_m, &adyl_m, &aphl_m);
340  adyu_next = adyu_upper;
341  do {
342 
343  /* bisect the interval */
344  last_iter = adyu_next;
345  adyu_next = 0.5 * (adyu_lower + adyu_upper);
346  if (fabs(last_iter - adyu_next) < 0.001) {
347  exit_iter_flag = 1;
348  break;
349  }
350  biogeochem_mod(au, adyu_next, &al_m, &adyl_m, &aphl_m);
351  /* test to see if overestimate or underestimate */
352  if ((al_m - al) > 0.)
353  adyu_upper = adyu_next;
354  if ((al_m - al) < 0.)
355  adyu_lower = adyu_next;
356 
357  /* absolute difference to see if convergence met */
358  df = fabs(al_m - al);
359  iter++;
360 
361  } while ((iter <= MAXIT) && (df >= TOL));
362 
363  if (exit_iter_flag) {
364  /* Attribute all of the absorption due to gelbstoff */
365  if ((al_m - al) < 0.) {
366  adyu_next = au;
367  adyl_m = al;
368  aphl_m = TOL;
369  }
370  /* Attribute all absorption to phytoplankton */
371  if ((al_m - al) > 0.) {
372  adyu_next = TOL;
373  adyl_m = TOL;
374  aphl_m = al;
375  }
376  }
377  /* Return error if maximum number of iterations exceeded */
378  if (iter > maxit)
379  return 1;
380 
381  *adyu = adyu_next;
382  *adyl = adyl_m;
383  *aphl = aphl_m;
384 
385  return 0;
386 }
387 
388 /* Function: biogeochem_mod */
389 /* This function was created because of the failure of the */
390 /* analytical expression, which contained in effect a first order */
391 /* polynomial for the spectral slope */
392 /* Function returns the value of a(412 or 443) */
393 /* which can be compared with the value of a(412 or 443) */
394 
395 /* obtained from the primary IOP part of the PML IOP model */
396 int biogeochem_mod(float au, float adyu, float *al_m, float *adyl_m, float *aphl_m) {
397  float ady_part, aph_part;
398 
399  /* Numbers from the NOMAD regressions */
400  /* 412:443 */
401  float A = 0.059;
402  float B = 1.099;
403  float C = 0.229;
404  float D = 0.004;
405  float E = 1.033;
406  float F = -0.059;
407 
408  /* 443:490 (to overcome problems with 412 channel) */
409  if (ysbpa_l == 443) {
410  A = 0.081;
411  B = 1.186;
412  C = 0.370;
413  D = 0.003;
414  E = 1.001;
415  F = 0.183;
416  }
417 
418  /* Relationships are quadratic in log10 space */
419  ady_part = A * pow(log10(adyu), 2) + B * log10(adyu) + C;
420  aph_part = D * pow(log10(au - adyu), 2) + E * log10(au - adyu) + F;
421 
422  *al_m = pow(10, ady_part) + pow(10, aph_part);
423  *adyl_m = pow(10, ady_part);
424  *aphl_m = pow(10, aph_part);
425 
426  return 0;
427 }
int iter_ab2(double rho_w[], double aw[], double bbw[], double F[], double epsb, double epsa, double ab[])
double iter_a2(double rho_w, double aw, double bbw, double F, double bb)
float * ch_lev
int maxit
#define MAXIT
Definition: eanom.c:49
float * b_w
const double C
float ysbpa_s
float ysbpa_0
float b_tilde_w
float * a_w
data_t lambda[NROOTS+1]
Definition: decode_rs.h:75
const double F
float geo2iop(float *levels, float *iopv[MAX_BANDS], int band, float value, int size)
int setgeom(float sun_theta, float sen_theta, float dphi)
int32_t ch_n
int iter_ab(double rho_w[], double aw[], double bbw[], double F[], double epsb, double epsa, double ab[])
float init_chl
float scat_a
float tol
float * ac[MAX_BANDS]
int iop_model(double rho_w[], float sun_theta, float sen_theta, float dphi, double a[], double bbp[], double ady[], double ap[], int MODIS, int CASEII)
float eps_a_init_modis
double f_ab(double a, double b, int band)
float scat_l_modis
data_t b[NROOTS+1]
Definition: decode_rs.h:77
#define NFLAG
Definition: l1stat.h:54
int biogeochem_iter(float al, float au, float *adyu, float *adyl, float *aphl)
float scat_l
double iter_a(double rho_w, double aw, double bbw, double F, double bb)
#define fabs(a)
Definition: misc.h:93
float eps_a_init
int biogeochem_mod(float au, float adyu, float *al_m, float *adyl_m, float *aphl_m)
int bp[2]
float scat_n
float ysbpa_l
float scat_b
int i
Definition: decode_rs.h:71
float b_tilde_p
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
int mod_iter(double rho_w[], float sun_theta, float sen_theta, float dphi, float eps_a, double a[], double bb[], int MODIS, int CASEII)
float scat_c
@ MODIS
Definition: AfrtConstants.h:65
#define NB
Definition: pml_iop.h:8