Due to the lapse in federal government funding, NASA is not updating this website. We sincerely regret this inconvenience.
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