OB.DAAC Logo
NASA Logo
Ocean Color Science Software

ocssw V2022
fluorescence.c
Go to the documentation of this file.
1 //---------------------------------------------------------------------
2 // fluorescence.c - functions to support chlorophyll fluorescence.
3 //
4 // Algorithm Reference:
5 // Behrenfeld, M.J., T.K. Westberry, E.S. Boss, R.T. O'Malley, D.A.
6 // Siegel, J.D. Wiggert, B.A. Franz, C.R. McClain, G.C. Feldman, S.C.
7 // Doney, J.K. Moore, G. Dall'Olmo, A. J. Milligan, I. Lima, and N.
8 // Mahowald (2009). Satellite-detected fluorescence reveals global
9 // physiology of ocean phytoplankton, Biogeosci., 6, 779-794.
10 //
11 // Implementation: B. Franz, Oct 2009.
12 //---------------------------------------------------------------------
13 
14 #include "l12_proto.h"
15 
16 #define PARW1 400
17 #define PARW2 700
18 #define PARWN (PARW2-PARW1)+1
20 static float fqymin = 0.0;
21 static float fqymax = 0.3;
22 static float flhmin = 0.0;
23 
24 /*---------------------------------------------------------------------*/
25 /* get_fsat - normalized fluorescence line height for each pixel */
26 /* fsat output is in radiance units (mW/cm^2/um/sr) */
27 /*---------------------------------------------------------------------*/
28 
35 void get_fsat(l2str *l2rec, float flh[]) {
36  static int32_t ib665, ib680, ib709;
37  static int firstCall = 1;
38 
39  int32_t ip, ipb;
40  float base;
41  float nLw1;
42  float nLw2;
43  float nLw3;
44  float Lf1;
45  float Lf2;
46  float Lf3;
47  float bias = input->flh_offset;
48 
49  l1str *l1rec = l2rec->l1rec;
50  filehandle *l1file = l1rec->l1file;
51 
52  if (firstCall) {
53  firstCall = 0;
54  ib665 = windex(665., l1file->fwave, l1file->nbands);
55  ib680 = windex(680., l1file->fwave, l1file->nbands);
56  ib709 = windex(709., l1file->fwave, l1file->nbands);
57 
58  if (fabs(l1file->fwave[ib665] - 665) > 2.5){
59  printf("No fluorescence algorithm available for this sensor.\n");
60  exit(EXIT_FAILURE);
61  }
62 
63  if (fabs(l1file->fwave[ib680] - 680 ) > 2.5){
64  printf("No fluorescence algorithm available for this sensor.\n");
65  exit(EXIT_FAILURE);
66 
67  }
68  // special handling for MODIS
69  if (fabs(l1file->fwave[ib709] - 709) > 5){
70  ib709 = windex(748., l1file->fwave, l1file->nbands);
71  if (fabs(l1file->fwave[ib709] - 748) > 5){
72  printf("No fluorescence algorithm available for this sensor.\n");
73  exit(EXIT_FAILURE);
74  }
75  }
76  }
77 
78  for (ip = 0; ip < l1rec->npix; ip++) {
79 
80  flh[ip] = BAD_FLT;
81 
82  ipb = l1file->nbands * ip;
86  nLw1 = l2rec->nLw[ipb + ib665];
87  nLw2 = l2rec->nLw[ipb + ib680];
88  nLw3 = l2rec->nLw[ipb + ib709];
89 
90  Lf1 = l1file->fwave[ib665];
91  Lf2 = l1file->fwave[ib680];
92  Lf3 = l1file->fwave[ib709];
93 
98  if (l1rec->mask[ip] || nLw1 < -0.01 || nLw2 < -0.01 || nLw3 < -0.01) {
99  l1rec->flags[ip] |= PRODFAIL;
100  continue;
101 
102  } else {
103 
107  base = nLw3 + (nLw1 - nLw3) * ((Lf3- Lf2) / (Lf3 - Lf1));
108  flh[ip] = nLw2 - base;
109 
113  flh[ip] -= bias;
114 
115  }
116  }
117 }
118 
119 /*---------------------------------------------------------------------*/
120 /* get_flh - fluorescence line height for each pixel in rec */
121 /* flh output is in radiance units (mW/cm^2/um/sr) */
122 /*---------------------------------------------------------------------*/
123 
131 void get_flh(l2str *l2rec, float flh[]) {
132  static int32_t ib665, ib680, ib709;
133  static int firstCall = 1;
134 
135  int32_t ip, ipb;
136  float Lw1;
137  float Lw2;
138  float Lw3;
139  float Lf1;
140  float Lf2;
141  float Lf3;
142  float base;
143  float bias = input->flh_offset;
144 
145  l1str *l1rec = l2rec->l1rec;
146  filehandle *l1file = l1rec->l1file;
147 
148  if (firstCall) {
149  firstCall = 0;
150  ib665 = windex(665., l1file->fwave, l1file->nbands);
151  ib680 = windex(680., l1file->fwave, l1file->nbands);
152  ib709 = windex(709., l1file->fwave, l1file->nbands);
153 
154  if (fabs(l1file->fwave[ib665] - 665) > 2.5){
155  printf("No fluorescence algorithm available for this sensor.\n");
156  exit(EXIT_FAILURE);
157  }
158 
159  if (fabs(l1file->fwave[ib680] - 680 ) > 2.5){
160  printf("No fluorescence algorithm available for this sensor.\n");
161  exit(EXIT_FAILURE);
162 
163  }
164  // special handling for MODIS
165  if (fabs(l1file->fwave[ib709] - 709) > 5){
166  ib709 = windex(748., l1file->fwave, l1file->nbands);
167  if (fabs(l1file->fwave[ib709] - 748) > 5){
168  printf("No fluorescence algorithm available for this sensor.\n");
169  exit(EXIT_FAILURE);
170  }
171 
172  }
173  }
174 
175  for (ip = 0; ip < l1rec->npix; ip++) {
176 
177  flh[ip] = BAD_FLT;
178 
179  ipb = l1file->nbands * ip;
183  Lw1 = l2rec->Lw[ipb + ib665];
184  Lw2 = l2rec->Lw[ipb + ib680];
185  Lw3 = l2rec->Lw[ipb + ib709];
186 
187  Lf1 = l1file->fwave[ib665];
188  Lf2 = l1file->fwave[ib680];
189  Lf3 = l1file->fwave[ib709];
190 
191  // scattering-angle-dependent spectral difference in aerosol model phase
192  // functions are introducing geometric artifacts
193  // replace aerosol difference between red bands with simple model
194  // rho_a[678] = rho_a[667]*(678/667)^-0.65
195 
196  //La = l2rec->La[ipb+ib678]/l2rec->t_sen[ipb+ib678]; // original aerosol
197  //Lap = 0.99*l2rec->La[ipb+ib667]/l2rec->Fo[ib667]*l2rec->Fo[ib678]/l2rec->t_sen[ipb+ib678];
198  //Lw2 = Lw2 + La - Lap;
203  if (l1rec->mask[ip] || Lw1 < -0.01 || Lw2 < -0.01 || Lw3 < -0.01) {
204  l1rec->flags[ip] |= PRODFAIL;
205  continue;
206 
207  } else {
208 
213  base = Lw3 + (Lw1 - Lw3) * ( (Lf3- Lf2) / (Lf3 - (Lf1)));
214  flh[ip] = Lw2 - base;
215 
219  flh[ip] -= bias;
220 
221  if (flh[ip] < flhmin) {
222  flh[ip] = 0.0;
223  }
224  }
225  }
226 }
227 
228 /*---------------------------------------------------------------------*/
229 /* fqy - fluorescence quantum yield */
230 /*---------------------------------------------------------------------*/
231 
239 void get_fqy(l2str *l2rec, float fqy[]) {
240  static float badval = BAD_FLT;
241  static int firstCall = 1;
242  static float *ipar;
243  static float *fsat;
244 
245  int32_t ip;
246 
247  l1str *l1rec = l2rec->l1rec;
248 
249  if (firstCall) {
250 
251  firstCall = 0;
252 
253  if ((ipar = (float *) calloc(l1rec->npix, sizeof (float))) == NULL) {
254  printf("-E- %s line %d: Unable to allocate space for ipar.\n",
255  __FILE__, __LINE__);
256  exit(EXIT_FAILURE);
257  }
258  if ((fsat = (float *) calloc(l1rec->npix, sizeof (float))) == NULL) {
259  printf("-E- %s line %d: Unable to allocate space for fsat.\n",
260  __FILE__, __LINE__);
261  exit(EXIT_FAILURE);
262  }
263  }
264 
265  // Compute ipar and fsat at all pixels
266 
267  get_ipar(l2rec, ipar);
268  get_fsat(l2rec, fsat);
269 
270  // Compute fluorescence quantum yield for each pixel
271 
272  for (ip = 0; ip < l1rec->npix; ip++) {
273 
274  fqy[ip] = badval;
275 
276  if (!l1rec->mask[ip] && l2rec->chl[ip] > 0.0 && fsat[ip] >= 0.0) {
277 
278  // simple form from Behrenfeld, A12
279  fqy[ip] = 0.01 * fsat[ip] / (0.0302 * l2rec->chl[ip]) * ipar[ip]
280  * 1e6 / 1590;
281 
282  if (!isfinite(fqy[ip])) {
283  fqy[ip] = badval;
284  l1rec->flags[ip] |= PRODFAIL;
285  } else if (fqy[ip] < fqymin || fqy[ip] > fqymax) {
286  l1rec->flags[ip] |= PRODWARN;
287  }
288 
289  } else {
290  fqy[ip] = badval;
291  l1rec->flags[ip] |= PRODFAIL;
292  }
293  }
294 }
295 
296 // *************** old stuff *******************
297 
298 /* we want flhq in same units as arp (Ein/m^2/s) */
299 /* flh is in mW/cm^2/um/sr / 100 = W/m^2/nm/sr */
300 /* hc = 1986.5e-19 J*nm/photon, Lambda 676.7 nm */
301 /* radiance->iradiance = 4*pi */
302 /* line height -> area = 43.38 nm */
303 /* 6.023e23 per mole Quanta */
304 
305 /* Notes from modcol (anly8dbl.f90)
306  ! 4*$PI - convert radiance to scalar irradiance
307  ! energy of 1 photon = hc/lambda
308  ! h = 6.6261e-34 [Js]; c = 299,792,458 [m/s] (*10^9 for nm/s)
309  ! energy of one photon = (1986.5*10^-19)/Lambda [W s]
310  ! where lambda is [nm]
311  !
312  ! FLHQ units are quanta/m^2/s
313  ! to convert FLH into total fluorescence under the fluorescence
314  ! curve :
315  ! The fluorescence curve is described as a gaussian curve with
316  ! center at 683 nm and half maximum emission of 25 nm (from
317  ! Collins et al. 1985
318  ! If L683 (1 nm width) = 1 then the area under the curve= 26.61
319  ! Band14 center= 677 bandwidth= 10 Fluorescence signal read= 8.61
320  ! Band13 center= 665 bandwidth= 10 Fluorescence signal read= 2.86
321  ! Band15 center= 747 bandwidth= 10 Fluorescence signal read= 1.55e-6
322  ! All band readings correspond to a 10 nm bandwidth signal. For this
323  ! reason the signals must be divided by 10 to get the reading per nm
324  !
325  ! Because band 13 is affected by the fluorescence signal then there
326  ! is a contribution of this signal equal to 0.2478 to the baseline
327  ! correction.
328  !
329  ! The conversion factor of FLH into area under the curve can be
330  ! computed: area under the curve / (Band14 read - baseline contrib)
331  ! factor = 26.61 /(0.861 - 0.2478) = 43.38
332  ! rescale: 43.38 (nm) -> 0.04338 (um)
333  !
334  ! FLHQ = FLH * Lambda/(hc) * (radiance->irradiance)*(FLH->area)
335  ! FLHQ is in quanta m-2 s-1 and ARP is in Einstein m-2 s-1
336  ! 1 Einstein = 1 mol quanta = 6.023e23 quanta
337  */
int32 l1file(int32 sdfid, int32 *nsamp, int32 *nscans, int16 *dtynum)
Definition: l1stat_chk.c:586
void get_fqy(l2str *l2rec, float fqy[])
Definition: fluorescence.c:239
#define NULL
Definition: decode_rs.h:63
read l1rec
#define PRODWARN
Definition: l2_flags.h:13
float32 base
Definition: maplists.h:106
def bias(y, y_hat)
Definition: metrics.py:155
#define PRODFAIL
Definition: l2_flags.h:41
instr * input
void get_fsat(l2str *l2rec, float flh[])
Definition: fluorescence.c:35
#define BAD_FLT
Definition: jplaeriallib.h:19
#define fabs(a)
Definition: misc.h:93
void get_ipar(l2str *l2rec, float ipar[])
Definition: ipar.c:18
int windex(float wave, float twave[], int ntwave)
Definition: windex.c:73
void get_flh(l2str *l2rec, float flh[])
Definition: fluorescence.c:131