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 //enhanced general algo with flexible wavelengths by M. Montes in Oct 2024
13 //---------------------------------------------------------------------
14 
15 #include "l12_proto.h"
16 #include <gsl/gsl_fit.h>
17 
18 #define PARW1 400
19 #define PARW2 700
20 #define PARWN (PARW2-PARW1)+1
22 static float fqymin = 0.0;
23 static float fqymax = 0.3;
24 static float flhmin = 0.0;
25 
26 /*---------------------------------------------------------------------*/
27 /* get_fsat_hyperspectral - similar to get_fsat but for hyperspectral sensors */
28 
29 /* Implementation: M. Zhang, Jan. 2024 */
30 
31 void get_fit_coef(float *x,float *y,int n,float *coef)
32 {
33  int i;
34  float mean_x=0, mean_y=0;
35  float xy_sum=0,x2_sum=0;
36 
37  for(i=0;i<n;i++){
38  mean_x+=x[i];
39  mean_y+=y[i];
40  }
41  mean_x/=n;
42  mean_y/=n;
43 
44  for(i=0;i<n;i++){
45  xy_sum+=(x[i]-mean_x)*(y[i]-mean_y);
46  x2_sum+=(x[i]-mean_x)*(x[i]-mean_x);
47  }
48 
49  //slope
50  coef[0]=xy_sum/x2_sum;
51  //offset
52  coef[1]=mean_y-coef[0]*mean_x;
53 }
54 
55 /*---------------------------------------------------------------------*/
56 /* get_unc_fsat - normalized fluorescence line height uncertaintiy for */
57 /* each pixel unc_fsat output is in radiance units */
58 /* (mW/cm^2/um/sr) */
59 /* Unceratainties computed using first order analytical propagation*/
60 /*---------------------------------------------------------------------*/
61 
62 void get_unc_fsat(l2str *l2rec, float uflh[]) {
63  static int32_t ib665, ib680, ib709;
64  static int firstCall = 1;
65 
66  int32_t ip, ipb;
67  float nLw1, unLw1;
68  float nLw2, unLw2;
69  float nLw3, unLw3;
70  float Lf1;
71  float Lf2;
72  float Lf3;
73  float dbdnlw1, dbdnlw2, dbdnlw3;
74  float ubias;
75 
76  l1str *l1rec = l2rec->l1rec;
77  filehandle *l1file = l1rec->l1file;
78  uncertainty_t *uncertainty=l1rec->uncertainty;
79 
80  if (firstCall) {
81  firstCall = 0;
82  ib665 = windex(665., l1file->fwave, l1file->nbands);
83  ib680 = windex(680., l1file->fwave, l1file->nbands);
84  ib709 = windex(709., l1file->fwave, l1file->nbands);
85 
86  if (fabs(l1file->fwave[ib665] - 665) > 2.5){
87  printf("No fluorescence algorithm available for this sensor.\n");
88  exit(EXIT_FAILURE);
89  }
90 
91  if (fabs(l1file->fwave[ib680] - 680 ) > 2.5){
92  printf("No fluorescence algorithm available for this sensor.\n");
93  exit(EXIT_FAILURE);
94 
95  }
96  // special handling for MODIS
97  if (fabs(l1file->fwave[ib709] - 709) > 5){
98  ib709 = windex(748., l1file->fwave, l1file->nbands);
99  if (fabs(l1file->fwave[ib709] - 748) > 5){
100  printf("No fluorescence algorithm available for this sensor.\n");
101  exit(EXIT_FAILURE);
102  }
103  }
104  }
105 
106  for (ip = 0; ip < l1rec->npix; ip++) {
107 
108  uflh[ip] = BAD_FLT;
109 
110  ipb = l1file->nbands * ip;
111 
112  if (uncertainty) {
116  nLw1 = l2rec->nLw[ipb + ib665];
117  nLw2 = l2rec->nLw[ipb + ib680];
118  nLw3 = l2rec->nLw[ipb + ib709];
119 
120  unLw1 = l2rec->Rrs_unc[ipb + ib665] * l2rec->l1rec->l1file->Fobar[ib665];
121  unLw2 = l2rec->Rrs_unc[ipb + ib680] * l2rec->l1rec->l1file->Fobar[ib680];
122  unLw3 = l2rec->Rrs_unc[ipb + ib709] * l2rec->l1rec->l1file->Fobar[ib709];
123 
124  Lf1 = l1file->fwave[ib665];
125  Lf2 = l1file->fwave[ib680];
126  Lf3 = l1file->fwave[ib709];
127 
132  if (l1rec->mask[ip] || nLw1 < -0.01 || nLw2 < -0.01 || nLw3 < -0.01) {
133  l1rec->flags[ip] |= PRODFAIL;
134  continue;
135 
136  } else {
137 
138  //fsat (Behrenfeld et al.[2009] equation A2
139  //flh = nLw2 - nLw3 - (nLw1 - nLw3) * ((Lf3- Lf2) / (Lf3 - Lf1));
140 
141  dbdnlw1 = -(Lf3- Lf2) / (Lf3 - Lf1);
142  dbdnlw2 = 1.0;
143  dbdnlw3 = -1.0 + ((Lf3- Lf2) / (Lf3 - Lf1));
144 
145  //Note: assume that the bias correction applied to flh is exact.
146  //Can adjust in future if this changes.
147  ubias = 0.0;
148 
149  uflh[ip] = sqrt(pow(dbdnlw1*unLw1,2) + pow(dbdnlw2*unLw2,2)
150  + pow(dbdnlw3*unLw3,2) + pow(ubias,2) );
151 
152  }
153  }
154  }
155 }
156 
157 /*---------------------------------------------------------------------*/
158 /* get_fsat - normalized fluorescence line height for each pixel */
159 /* fsat output is in radiance units (mW/cm^2/um/sr) */
160 /*---------------------------------------------------------------------*/
161 
168 void get_fsat(l2str *l2rec, float flh[]) {
169  static int firstRun = 1;
170 
171  int32_t ib, ip, ipb;
172  l1str *l1rec = l2rec->l1rec;
173  filehandle *l1file = l1rec->l1file;
174  double c0, c1, cov00, cov01, cov11, chi_sq;
175  float *nLw;
176  static int nfit;
177  static double *xfit = NULL;
178  static double *yfit = NULL;
179  static int *baseBands;
180  static int heightBand;
181  float baseline;
182 
183  if(firstRun) {
184  firstRun = 0;
185 
186  if (input->flh_num_base_wavelengths < 2) {
187  printf("-E- Need at least 2 flh_base_wavelengths to compute FLH. Only %d provided\n", input->flh_num_base_wavelengths);
188  exit(EXIT_FAILURE);
189  }
190  if (input->flh_height_wavelength == -1.0) {
191  printf("-E- Missing flh_height_wavelength needed to compute FLH.\n");
192  exit(EXIT_FAILURE);
193  }
194 
195  // default base wavelengths for Aqua 667, 748 and 678 for height
196  // default base wavelengths for OCI 650,660,710,720 and 678 for height
197  nfit = input->flh_num_base_wavelengths;
198  xfit = (double *)malloc(nfit * sizeof(double));
199  yfit = (double *)malloc(nfit * sizeof(double));
200  baseBands = (int *)malloc(nfit * sizeof(int));
201 
202  heightBand = windex(input->flh_height_wavelength, l1file->fwave, l1file->nbands);
203 
204  for (ib = 0; ib < nfit; ib++) {
205  baseBands[ib] = windex(input->flh_base_wavelengths[ib], l1file->fwave, l1file->nbands);
206  xfit[ib] = l1file->fwave[baseBands[ib]];
207  }
208  }
209 
210  for (ip = 0; ip < l1rec->npix; ip++) {
211  flh[ip] = BAD_FLT;
212 
213  if (l1rec->mask[ip]) {
214  l1rec->flags[ip] |= PRODFAIL;
215  continue;
216  }
217 
218  ipb = l1file->nbands * ip;
219  nLw = &l2rec->nLw[ipb];
220 
221  int bad_fit = 0;
222  for (ib = 0; ib < nfit; ib++) {
223  yfit[ib] = nLw[baseBands[ib]];
224  if (yfit[ib] < -0.01) {
225  l1rec->flags[ip] |= PRODFAIL;
226  bad_fit = 1;
227  break;
228  }
229  }
230  if(bad_fit)
231  continue;
232 
233  gsl_fit_linear(xfit, 1, yfit, 1, nfit, &c0, &c1, &cov00, &cov01, &cov11, &chi_sq);
234 
235  baseline = c0 + c1 * input->flh_height_wavelength;
236 
237  flh[ip] = nLw[heightBand] - baseline - input->flh_offset;
238 
239  if (flh[ip] < flhmin) {
240  flh[ip] = BAD_FLT;
241  l1rec->flags[ip] |= PRODFAIL;
242  continue;
243  }
244  }
245 }
246 
247 /*---------------------------------------------------------------------*/
248 /* fqy - fluorescence quantum yield */
249 /*---------------------------------------------------------------------*/
250 
258 void get_fqy(l2str *l2rec, float fqy[]) {
259  static float badval = BAD_FLT;
260  static int firstCall = 1;
261  static float *ipar;
262  static float *fsat;
263 
264  int32_t ip;
265 
266  l1str *l1rec = l2rec->l1rec;
267 
268  if (firstCall) {
269 
270  firstCall = 0;
271 
272  if ((ipar = (float *) calloc(l1rec->npix, sizeof (float))) == NULL) {
273  printf("-E- %s line %d: Unable to allocate space for ipar.\n",
274  __FILE__, __LINE__);
275  exit(EXIT_FAILURE);
276  }
277  if ((fsat = (float *) calloc(l1rec->npix, sizeof (float))) == NULL) {
278  printf("-E- %s line %d: Unable to allocate space for fsat.\n",
279  __FILE__, __LINE__);
280  exit(EXIT_FAILURE);
281  }
282  }
283 
284  // Compute ipar and fsat at all pixels
285 
286  get_ipar(l2rec, ipar);
287  get_fsat(l2rec, fsat);
288 
289  // Compute fluorescence quantum yield for each pixel
290 
291  for (ip = 0; ip < l1rec->npix; ip++) {
292 
293  fqy[ip] = badval;
294 
295  if (!l1rec->mask[ip] && l2rec->chl[ip] > 0.0 && fsat[ip] >= 0.0) {
296 
297  // simple form from Behrenfeld, A12
298  fqy[ip] = 0.01 * fsat[ip] / (0.0302 * l2rec->chl[ip]) * ipar[ip]
299  * 1e6 / 1590;
300 
301  if (!isfinite(fqy[ip])) {
302  fqy[ip] = badval;
303  l1rec->flags[ip] |= PRODFAIL;
304  } else if (fqy[ip] < fqymin || fqy[ip] > fqymax) {
305  l1rec->flags[ip] |= PRODWARN;
306  }
307 
308  } else {
309  fqy[ip] = badval;
310  l1rec->flags[ip] |= PRODFAIL;
311  }
312  }
313 }
314 
315 // *************** old stuff *******************
316 
317 /* we want flhq in same units as arp (Ein/m^2/s) */
318 /* flh is in mW/cm^2/um/sr / 100 = W/m^2/nm/sr */
319 /* hc = 1986.5e-19 J*nm/photon, Lambda 676.7 nm */
320 /* radiance->iradiance = 4*pi */
321 /* line height -> area = 43.38 nm */
322 /* 6.023e23 per mole Quanta */
323 
324 /* Notes from modcol (anly8dbl.f90)
325  ! 4*$PI - convert radiance to scalar irradiance
326  ! energy of 1 photon = hc/lambda
327  ! h = 6.6261e-34 [Js]; c = 299,792,458 [m/s] (*10^9 for nm/s)
328  ! energy of one photon = (1986.5*10^-19)/Lambda [W s]
329  ! where lambda is [nm]
330  !
331  ! FLHQ units are quanta/m^2/s
332  ! to convert FLH into total fluorescence under the fluorescence
333  ! curve :
334  ! The fluorescence curve is described as a gaussian curve with
335  ! center at 683 nm and half maximum emission of 25 nm (from
336  ! Collins et al. 1985
337  ! If L683 (1 nm width) = 1 then the area under the curve= 26.61
338  ! Band14 center= 677 bandwidth= 10 Fluorescence signal read= 8.61
339  ! Band13 center= 665 bandwidth= 10 Fluorescence signal read= 2.86
340  ! Band15 center= 747 bandwidth= 10 Fluorescence signal read= 1.55e-6
341  ! All band readings correspond to a 10 nm bandwidth signal. For this
342  ! reason the signals must be divided by 10 to get the reading per nm
343  !
344  ! Because band 13 is affected by the fluorescence signal then there
345  ! is a contribution of this signal equal to 0.2478 to the baseline
346  ! correction.
347  !
348  ! The conversion factor of FLH into area under the curve can be
349  ! computed: area under the curve / (Band14 read - baseline contrib)
350  ! factor = 26.61 /(0.861 - 0.2478) = 43.38
351  ! rescale: 43.38 (nm) -> 0.04338 (um)
352  !
353  ! FLHQ = FLH * Lambda/(hc) * (radiance->irradiance)*(FLH->area)
354  ! FLHQ is in quanta m-2 s-1 and ARP is in Einstein m-2 s-1
355  ! 1 Einstein = 1 mol quanta = 6.023e23 quanta
356  */
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:258
#define NULL
Definition: decode_rs.h:63
read l1rec
#define PRODWARN
Definition: l2_flags.h:13
#define PRODFAIL
Definition: l2_flags.h:41
instr * input
void get_fsat(l2str *l2rec, float flh[])
Definition: fluorescence.c:168
void get_fit_coef(float *x, float *y, int n, float *coef)
Definition: fluorescence.c:31
#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
int i
Definition: decode_rs.h:71
void get_unc_fsat(l2str *l2rec, float uflh[])
Definition: fluorescence.c:62