NASA Logo
Ocean Color Science Software

ocssw V2022
ams_oe_inversion.c
Go to the documentation of this file.
1 #include <stdlib.h>
2 #include <stdio.h>
3 #include "get_ctht.h"
4 
5 /*
6  file ams_oe_inversion.c, A. Sayer's inversion routine, currently limited to
7  y_true of size 8 (8 rho bands) and xa of 3 (3 prods)
8 */
9 int32_t ams_oe_inversion( double *rhot, gsl_matrix *gm_sy, double *xa,
10  gsl_matrix *gm_sa, double *lut, int32_t *lut_dims, int32_t *min_cost_loc,
11  oe_info_str *oe_info )
12 
13  /*
14  ams_oe_inversion - Generic optimal estimation (OE) inversion routine
15 
16  Returns int32_t 0 if all good
17 
18  Parameters: (in calling order)
19  Type Name I/O Description
20  ---- ---- --- -----------
21  double * rhot I sample reflectance to match
22  gsl_matrix * gm_sy I Measurement/forward model
23  covariance matrix
24  double * xa I A priori state vector (product
25  we want to get)
26  gsl_matrix * gm_sa I A priori state covariance matrix
27  double * lut I table of rho for this pixel
28  view and sfc p level
29  int32_t * lut_dims I all dims of the LUT
30  int32_t * min_cost_loc I first guess at x.
31  oe_info_str * oe_info O a structure of the inversion
32  results and uncertainty
33 
34  W. Robinson, SAIC, 18 Aug 2023, converted from A. Sayer's IDL code
35 
36 There are other controls: nitr_max=nitr_max, djconv=djconv,mqstep=mqstep,
37 mq0=mq0, verbose=verbose - perhaps some/all will be added
38 
39 */
40  {
41  int32_t nx, ny, ix, iy, idim, nitr, nitr_max, conv_flag, mqstep;
42  int32_t tmp_attempt_ctr, new_k_init;
43  double j_cost_m, j_cost_a, j_cost, j_old, djconv, mq, mq0;
44  double yres[8], xres[3], xpdx[3];
45  double j_old_t1, j_old_t2; // will need alloc if variable;
46 
47  static int32_t f_init = 0;
48  static double *y_init, *k_init;
49  static gsl_vector *gv_yres, *gv_xres, *gv_inv_sy_yres, *gv_inv_sa_xres;
50  static gsl_vector *gv_xpdx, *gv_x_fg, *gv_xa, *gv_r_minus_xa, *gv_jp_t2;
51  static gsl_vector *gv_y_init_rho, *gv_jp_t1, *gv_jp_t1_f2;
52  static gsl_vector *gv_jp, *gv_dx, *gv_nx, *gv_ny;
53  static gsl_matrix *gm_sa_inv, *gm_sy_inv_k_init, *gm_ident;
54  static gsl_matrix *gm_jpp_t1, *gm_k_init, *gm_jpp, *gm_scl_ident;
55  static gsl_matrix *gm_jpp_ident, *gm_jpp_aux, *gm_jpp_ident_inv;
56  static gsl_matrix *gm_sx, *gm_sx_t2_f1, *gm_sx_t2, *gm_sx_uinv;
57  static gsl_matrix *gm_gain, *gm_av_kernel, *gm_sy_inv, *gm_g_pt1;
58  static gsl_matrix *gm_g_pt2, *gm_g_pt3, *gm_g_pt4;
59 
60  gsl_matrix *mat_xfr; // for doing some transfers
61 
62  nx = gm_sa->size1;
63  ny = lut_dims[3];
64 
65  if( f_init == 0 )
66  {
67  y_init = (double *)malloc( ny * sizeof(double) );
68  k_init = (double *)malloc( ny * nx * sizeof(double) );
69  f_init = 1;
70 
71  gm_jpp_t1 = gsl_matrix_alloc( nx, nx );
72  gv_x_fg = gsl_vector_alloc(nx);
73  gm_ident = gsl_matrix_alloc( nx, nx );
74  gv_yres = gsl_vector_alloc(ny);
75  gv_xres = gsl_vector_alloc(nx);
76  gv_xpdx = gsl_vector_alloc( nx );
77  gv_inv_sa_xres = gsl_vector_alloc(nx);
78  gv_inv_sy_yres = gsl_vector_alloc(ny);
79  gm_sy_inv_k_init = gsl_matrix_alloc( ny, nx );
80  gm_k_init = gsl_matrix_alloc( nx, ny );
81  gv_jp_t1_f2 = gsl_vector_alloc( ny );
82  gm_jpp = gsl_matrix_alloc( nx,nx );
83  gv_xa = gsl_vector_alloc( nx );
84  gv_r_minus_xa = gsl_vector_alloc( nx );
85  gv_jp_t2 = gsl_vector_alloc( nx );
86  gv_y_init_rho = gsl_vector_alloc( ny );
87  gv_jp_t1 = gsl_vector_alloc( nx );
88  gv_jp = gsl_vector_alloc( nx );
89  gm_scl_ident = gsl_matrix_alloc( nx, nx );
90  gm_jpp_ident = gsl_matrix_alloc( nx, nx );
91  gm_jpp_aux = gsl_matrix_alloc( nx, nx );
92  gv_dx = gsl_vector_alloc( nx );
93  gv_nx = gsl_vector_alloc( nx );
94  gv_ny = gsl_vector_alloc( ny );
95  gm_sx = gsl_matrix_alloc( nx, nx );
96  gm_sx_t2_f1 = gsl_matrix_alloc( ny, nx );
97  gm_sx_t2 = gsl_matrix_alloc( nx, nx );
98  gm_sx_uinv = gsl_matrix_alloc( nx, nx );
99  gm_av_kernel = gsl_matrix_alloc( nx, nx );
100  gm_g_pt1 = gsl_matrix_alloc( nx, ny );
101  gm_g_pt2 = gsl_matrix_alloc( nx, nx );
102  gm_g_pt3 = gsl_matrix_alloc( nx, nx );
103  gm_g_pt4 = gsl_matrix_alloc( nx, nx );
104  gm_gain = gsl_matrix_alloc( nx, ny );
105  // some output struct init
106  oe_info->x_prd_state = (float *) malloc( nx * sizeof(float) );
107  }
108  // we'll initialize the optional controls
109  j_cost = 1.e20; // Cost: initialise to high value
110  j_old = j_cost; // Previous attempt's cost
111  nitr = 0; // Number of iterations attempted
112  conv_flag = 0; // Flag for convergence (0=not converged, 1=converged)
113  // Default parameter values, if keywords not set
114  nitr_max = 50; // Maximum number of iterations
115  djconv = 0.01; // Threshold change in cost
116  mqstep = 5; // Marquardt step parameter
117  mq0 = 0.00001; // Marquardt parameter initial value, Don't start this
118  // too high or can oscillate.
119  int32_t snax[3];
120  new_k_init = 1;
121 
122  for( idim = 0; idim < 3; idim++ )
123  snax[idim] = lut_dims[idim];
124  /*
125  * Compute initial guess, y, and cost - this can also be set to xa
126  */
127  for( ix = 0; ix < nx; ix++ )
128  gsl_vector_set( gv_x_fg, ix, min_cost_loc[ix] );
129  // set identity matrix
130  gsl_matrix_set_identity( gm_ident );
131  /*
132  * Get initial values of y from x
133  */
134  my_lut_funct_3d_oe( gv_x_fg, lut, snax, ny, y_init, k_init );
135  /*
136  * Calculate cost
137  */
138  // set xres, yres and vector versions
139  for( idim = 0; idim < ny; idim++ )
140  yres[idim] = y_init[idim] - rhot[idim];
141 
142  for( idim = 0; idim < ny; idim++ )
143  gsl_vector_set( gv_yres, idim, yres[idim] );
144 
145  for( idim = 0; idim < nx; idim++ )
146  xres[idim] = gv_x_fg->data[idim] - xa[idim];
147 
148  for( idim = 0; idim < nx; idim++ )
149  gsl_vector_set( gv_xres, idim, xres[idim] );
150 
151  // Do line: j_old=yres#la_invert(Sy)#yres + xres#la_invert(Sa)#xres
152  gm_sy_inv = invert_a_matrix( gm_sy );
153  gm_sa_inv = invert_a_matrix( gm_sa );
154 
155  // term 1: yres#la_invert(Sy)#yres
156  gsl_blas_dgemv( CblasNoTrans, 1., gm_sy_inv, gv_yres, 0., gv_inv_sy_yres );
157  gsl_blas_ddot( gv_yres, gv_inv_sy_yres, &j_old_t1 );
158 
159  // term 2: xres#la_invert(Sa)#xres
160  gsl_blas_dgemv( CblasNoTrans, 1., gm_sa_inv, gv_xres, 0., gv_inv_sa_xres );
161  gsl_blas_ddot( gv_xres, gv_inv_sa_xres, &j_old_t2 );
162 
163  j_old = j_old_t1 + j_old_t2;
164  // Finished making j_old
165 
166  /*
167  * For first attempt set mq0 to trace of J''
168  * jpp=ym.k#la_invert(Sy)#transpose(ym.k) + la_invert(Sa)
169  * ym.k -> k_init Sy -> gm_sy Sa -> gm_sa
170  */
171  // 1st, la_invert(Sy)#transpose(ym.k) sy_inv_k_init
172  for( iy = 0; iy < ny; iy++ )
173  for( ix = 0; ix < nx; ix++ )
174  gsl_matrix_set( gm_k_init, ix, iy, *( k_init + ix + nx * iy ) );
175 
176  gsl_blas_dgemm( CblasNoTrans, CblasTrans, 1., gm_sy_inv, gm_k_init, 0., gm_sy_inv_k_init );
177  // finally, worked. on to the other parts and adding it together
178  gsl_blas_dgemm( CblasNoTrans, CblasNoTrans, 1., gm_k_init, gm_sy_inv_k_init, 0, gm_jpp_t1 );
179 
180  // the 2nd term is just an inverse of Sa so just have to get jpp
181  gsl_matrix_memcpy( gm_jpp, gm_sa_inv );
182  gsl_matrix_add( gm_jpp, gm_jpp_t1 );
183 
184  // and to get mq0,= trace(jpp/nx) no gsl for that
185  double tsum = 0.;
186  for( ix = 0; ix < nx; ix++ )
187  tsum += gsl_matrix_get( gm_jpp, ix, ix );
188  mq0 *= tsum / nx;
189 
190  /*
191  * Next, Do the iteration
192  * looks like only 1 new matrix, vector operation set along with
193  * previous matrix results
194  * So, get the whole loop to end sketched out
195  */
196  while( ( conv_flag == 0 ) && ( nitr <= nitr_max ) )
197  {
198  nitr++;
199  // Reset Marquardt parameter to default value
200  // variable 'mq' represents value used in this step
201  mq = mq0;
202 
203  // Set max number of attempts per iteration in case of
204  // e.g. numerical instability problems
205  tmp_attempt_ctr = 0;
206 
207  while( ( j_cost >= j_old ) && ( tmp_attempt_ctr <= 100 ) )
208  {
209  tmp_attempt_ctr++;
210  // This should not result in an infinite loop
211  // unless gradients are calculated incorrectly
212  // or a parameter is unstable,as it will
213  // tend towards making very small steps, which
214  // must lead to a decreased cost, unless you
215  // are already at a minimum in which case
216  // the cost function should have converged.
217 
218  // OK, sep 29 23 update to call my_lut_funct_3d_oe here
219  // Call forward model to get prediction and gradients.
220  // Necessary as otherwise things would get out of sync if an iteration
221  // attempt is unsuccessful
222  my_lut_funct_3d_oe( gv_x_fg, lut, snax, ny, y_init, k_init );
223  // don't forget to make gsl version of k_init
224  for( iy = 0; iy < ny; iy++ )
225  for( ix = 0; ix < nx; ix++ )
226  gsl_matrix_set( gm_k_init, ix, iy, *( k_init + ix + nx * iy ) );
227 
228  // Increase Marquardt parameter
229  mq *= mqstep;
230  // Get step for next iteration attempt:
231  // Need gradient of cost function, J', denoted jp here
232  // *** NEW jp=ym.k#la_invert(Sy)#(ym.y-y_true) + la_invert(Sa)#(x-xa)
233  // [3, 8] [8,8] [8] [3,3] 3]
234  // siz 3 siz 3
235  // siz 3
236  // jp= k_init # gm_sy_inv # ( y_init - rhot ) + gm_sa_inv # ( rhot - xa )
237 
238  // need to make gv_xa from xa here
239  for( ix = 0; ix < nx; ix++ )
240  gsl_vector_set( gv_xa, ix, xa[ix] );
241 
242  // create rhot - xa
243  gsl_vector_memcpy( gv_r_minus_xa, gv_x_fg );
244  gsl_vector_sub( gv_r_minus_xa, gv_xa );
245  // gv_r_minus_xa is (x-xa) above
246 
247  // make 2nd term, la_invert(Sa)#(x-xa)
248  // or: gm_sa_inv # ( rhot - xa ) (la_invert(Sa)#(x-xa))
249  gsl_blas_dgemv( CblasNoTrans, 1., gm_sa_inv, gv_r_minus_xa, 0.,
250  gv_jp_t2 );
251 
252  // 1st term, k_init # gm_sy_inv # ( y_init - rhot )
253  // do y_init - rhot part
254  for( iy = 0; iy < ny; iy++ )
255  gsl_vector_set( gv_y_init_rho, iy, y_init[iy] - rhot[iy] );
256 
257  // intermediate - la_invert(Sy)#(ym.y-y_true)
258  gsl_blas_dgemv( CblasNoTrans, 1., gm_sy_inv, gv_y_init_rho, 0., gv_jp_t1_f2);
259 
260  // ok, ym.k#la_invert(Sy)#(ym.y-y_true)
261  gsl_blas_dgemv( CblasNoTrans, 1., gm_k_init, gv_jp_t1_f2, 0., gv_jp_t1 );
262 
263  // finally, jp_t1 + jp_t2
264  gsl_vector_memcpy( gv_jp, gv_jp_t1 );
265  gsl_vector_add( gv_jp, gv_jp_t2 );
266  // OK, jp is made
267 
268  // and J'', jpp
269  // Use the jpp computed before for 1st time through
270  // printf( "%s, %d, I: Note that we already have jpp from before the while\n", __FILE__, __LINE__ );
271  if( new_k_init == 1 )
272  {
273  // WDR NOTE that this is a repeat of code above - should make sub
274  /*
275  * For first attempt set mq0 to trace of J''
276  * jpp=ym.k#la_invert(Sy)#transpose(ym.k) + la_invert(Sa)
277  * ym.k -> k_init Sy -> gm_sy Sa -> gm_sa
278  */
279  // 1st, la_invert(Sy)#transpose(ym.k)
280  for( iy = 0; iy < ny; iy++ )
281  for( ix = 0; ix < nx; ix++ )
282  gsl_matrix_set( gm_k_init, ix, iy, *( k_init + ix + nx * iy ) );
283 
284  gsl_blas_dgemm( CblasNoTrans, CblasTrans, 1., gm_sy_inv, gm_k_init,
285  0., gm_sy_inv_k_init );
286  // finally, worked. on to the other parts and adding it together
287  gsl_blas_dgemm( CblasNoTrans, CblasNoTrans, 1., gm_k_init,
288  gm_sy_inv_k_init, 0, gm_jpp_t1 );
289 
290  // the 2nd term is just an inverse of Sa so just have to get jpp
291  gsl_matrix_memcpy( gm_jpp, gm_sa_inv );
292  gsl_matrix_add( gm_jpp, gm_jpp_t1 );
293  }
294  new_k_init = 1;
295 
296  // This is the step size dx
297  // *** NEW TOO dx = -1*la_invert(jpp + mq*ident_matrix)#jp
298 
299  // mq*ident_matrix: gm_scl_ident
300  gsl_matrix_memcpy( gm_scl_ident, gm_ident );
301  gsl_matrix_scale( gm_scl_ident, mq );
302 
303  // jpp + mq*ident_matrix: gm_jpp_ident
304  gsl_matrix_memcpy( gm_jpp_ident, gm_scl_ident );
305  gsl_matrix_memcpy( gm_jpp_aux, gm_jpp );
306  gsl_matrix_add( gm_jpp_ident, gm_jpp_aux );
307 
308  // la_invert(jpp + mq*ident_matrix): gm_jpp_ident_inv
309  gm_jpp_ident_inv = invert_a_matrix( gm_jpp_ident );
310 
311  // -1*la_invert(jpp + mq*ident_matrix)#jp: gv_dx
312  gsl_blas_dgemv( CblasNoTrans, -1., gm_jpp_ident_inv, gv_jp, 0., gv_dx );
313  gsl_matrix_free( gm_jpp_ident_inv );
314  // DX MADE
315 
316  // curtail the search if the dx has a NaN
317  for( ix = 0; ix < nx; ix++ )
318  {
319  if( isnan( gv_dx->data[ix] ) )
320  {
321  gsl_vector_set_zero( gv_dx );
322  tmp_attempt_ctr=1000;
323  break;
324  }
325  }
326  // Create new x+dx array, make sure remains within bounds
327  for( ix = 0; ix < nx; ix++ )
328  {
329  xpdx[ix] = gv_x_fg->data[ix] + gv_dx->data[ix]; // go gsl?
330  xpdx[ix] = ( xpdx[ix] <= 1.e-5 ) ? 1.e-5 : xpdx[ix];
331  xpdx[ix] = ( xpdx[ix] > lut_dims[ix] - 1 ) ? lut_dims[ix] - 1 : xpdx[ix];
332  }
333  // Try next step: see if cost lower
334  for( ix = 0; ix < nx; ix++ )
335  gsl_vector_set( gv_xpdx, ix, xpdx[ix] );
336  my_lut_funct_3d_oe( gv_xpdx, lut, snax, ny, y_init, k_init );
337  // get gm_k_init
338  for( iy = 0; iy < ny; iy++ )
339  for( ix = 0; ix < nx; ix++ )
340  gsl_matrix_set( gm_k_init, ix, iy, *( k_init + ix + nx * iy ) );
341 
342  // re-compute the x,yres and vector versions
343  for( idim = 0; idim < ny; idim++ )
344  {
345  yres[idim] = y_init[idim] - rhot[idim];
346  gsl_vector_set( gv_yres, idim, yres[idim] );
347  }
348  for( idim = 0; idim < nx; idim++ )
349  {
350  xres[idim] = gv_x_fg->data[idim] - xa[idim];
351  gsl_vector_set( gv_xres, idim, xres[idim] );
352  }
353 
354  for( idim = 0; idim < ny; idim++ )
355  gsl_vector_set( gv_yres, idim, yres[idim] );
356  // re-compute the Measurement contribution to cost
357  // and A priori contribution to cost, currently repeat of dbl_int1, 2
358  // derivation higher up
359  // term 1
360  gsl_blas_dgemv( CblasNoTrans, 1., gm_sy_inv, gv_yres, 0., gv_ny );
361  gsl_blas_ddot( gv_yres, gv_ny, &j_cost_m );
362 
363  // term 2
364  gsl_blas_dgemv( CblasNoTrans, 1., gm_sa_inv, gv_xres, 0., gv_nx );
365  gsl_blas_ddot( gv_xres, gv_nx, &j_cost_a );
366 
367  // j_cost_m = Measurement contribution to cost or
368  // jm=yres#la_invert(Sy)#yres
369  // j_cost_a = A priori contribution to cost or
370  // ja=xres#la_invert(Sa)#xres
371  j_cost = j_cost_m + j_cost_a;
372 
373  if( isnan( j_cost ) )
374  {
375  printf( "%s, %d, E: Meaningless cost found\n", __FILE__, __LINE__ );
376  exit(1);
377  }
378 // temp to print out the values during iteration
379 /*
380 printf( "\nEnd step loop, nitr: %d, tmp_attempt_ctr: %d\n", nitr,
381  tmp_attempt_ctr );
382 printf( "dx values:\n" );
383 gsl_vector_fprintf( stdout, gv_dx, "%f" );
384 printf( "mq value: %f\n", mq );
385 printf( "jp values:\n" );
386 gsl_vector_fprintf( stdout, gv_jp, "%f" );
387 printf( "jpp:\n" );
388 print_mat_contents( gm_jpp, nx, nx );
389 printf( "old, new cost at this point: %f, %f\n", j_old, j_cost );
390 printf( "END contents\n" );
391 */
392  } // End loop trying to find next step
393 
394  // Update state vector
395  gsl_vector_memcpy( gv_x_fg, gv_xpdx );
396 
397  // If cost change small, or measurement cost is very low, we have converged
398  if( ( fabs( j_cost - j_old ) < ( djconv * ny ) ) ||
399  ( j_cost_m < djconv * ny ) ) conv_flag = 1;
400 // temp WDR show items used to decide convergence
401 // printf( "CONV INFO, j_cost: %f, j_old: %f, djconv: %f, ny: %d, j_cost_m: %f\n", j_cost, j_old, djconv, ny, j_cost_m );
402 
403  // If cost decreased but we've not converged, carry on
404  // using smaller Marquardt parameter for next step
405  j_old = j_cost;
406  mq0 = mq / mqstep;
407 // printf( "\nEnd iteration loop, nitr: %d, tmp_attempt_ctr: %d\n", nitr,
408 // tmp_attempt_ctr );
409  } // End iteration loop
410 
411 //;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
412 //; Calculate information about the solution ;
413 //;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
414 
415 // Calculate uncertainty on retrieved state
416 // Assume model parameter error is included in Sy
417 // Put Sy as double here as otherwise from time to time float issues can
418 // make the expression uninvertable
419 // Sx=la_invert(la_invert(Sa) + ym.k#la_invert(double(Sy))#transpose(ym.k))
420 // make gm_sx_t2_f1 = la_invert(double(Sy))#transpose(ym.k)
421 gsl_blas_dgemm( CblasNoTrans, CblasTrans, 1., gm_sy_inv, gm_k_init, 0., gm_sx_t2_f1 );
422 // make gm_sx_t2 = ym.k#la_invert(double(Sy))#transpose(ym.k)
423 gsl_blas_dgemm( CblasNoTrans, CblasNoTrans, 1., gm_k_init, gm_sx_t2_f1, 0., gm_sx_t2 );
424 
425 gsl_matrix_memcpy( gm_sx_uinv, gm_sa_inv );
426 gsl_matrix_add( gm_sx_uinv, gm_sx_t2 ); // may want better descrip of term1
427  // it is prev computed ym.k#la_invert(Sy)#transpose(ym.k))
428 // la_invert( gm_sx_uninv )
429 mat_xfr = invert_a_matrix( gm_sx_uinv );
430 gsl_matrix_memcpy( gm_sx, mat_xfr );
431 gsl_matrix_free( mat_xfr );
432 
433 
434 // Calculate gain matrix: gm_gain
435 // G = la_invert((la_invert(Sa) + $
436 // ym.k # la_invert(double(Sy)) # transpose(ym.k))) # ym.k#la_invert(Sy)
437 // this is
438 // gm_g = la_invert( ( gm_sa_inv + gm_k_init # gm_sy_inv # gm_k_init^T ) )
439 // # gm_k_init # gm_sy_inv
440 //
441 // part 1: gm_k_init # gm_sy_inv -> gm_g_pt1 // appears 2x in above
442 gsl_blas_dgemm( CblasNoTrans, CblasNoTrans, 1., gm_k_init, gm_sy_inv, 0.,
443  gm_g_pt1 );
444 
445 // part 2: gm_g_pt1 # gm_k_init^T -> gm_g_pt2
446 gsl_blas_dgemm( CblasNoTrans, CblasTrans, 1., gm_g_pt1, gm_k_init, 0., gm_g_pt2 );
447 
448 // part 3: gm_sa_inv + gm_g_pt3
449 gsl_matrix_memcpy( gm_g_pt3, gm_sa_inv );
450 gsl_matrix_add( gm_g_pt3, gm_g_pt2 );
451 
452 // part 4: invert( gm_g_pt3 ) -> gm_g_pt4
453 mat_xfr = invert_a_matrix( gm_g_pt3 );
454 gsl_matrix_memcpy( gm_g_pt4, mat_xfr );
455 gsl_matrix_free( mat_xfr );
456 
457 // so we did gm_k_init # gm_sy_inv as gm_g_pt1
458 // just get the gain: gm_g_pt4 # gm_g_pt1
459 gsl_blas_dgemm( CblasNoTrans, CblasNoTrans, 1., gm_g_pt4, gm_g_pt1, 0., gm_gain );
460 
461 // END Gain matrix comp
462 
463 // Now use this to calculate averaging kernel, A=GKt [nx,nx]
464 // gm_gain # gm_k_init^T = gm_av_kernel[nx,nx]
465 gsl_blas_dgemm( CblasNoTrans, CblasTrans, 1., gm_gain, gm_k_init,
466  0., gm_av_kernel );
467 
468 // Return output
469 // fill a structure with this
470 for( ix = 0; ix < nx; ix++ ) // the 'x'
471  oe_info->x_prd_state[ix] = gsl_vector_get( gv_x_fg, ix );
472 oe_info->gm_sx = gm_sx;
473 oe_info->conv_flag = conv_flag;
474 oe_info->cost = j_cost;
475 oe_info->acost = j_cost_a;
476 oe_info->gm_ak = gm_av_kernel;
477 oe_info->gm_gain = gm_gain;
478 oe_info->nitr = nitr;
479 
480 // And end?
481 gsl_matrix_free( gm_sa_inv );
482 gsl_matrix_free( gm_sy_inv );
483 return 0;
484 
485 // printf( "%s, %d, I after hairy matrix mult\n", __FILE__, __LINE__ );
486  // and done
487  return 0;
488  }
489 
490  // Well, we have to move on to my_lut_funct_3d_oe (not sure it is only '3'
491 
492 int32_t my_lut_funct_3d_oe( gsl_vector *gv_x_fg, double *lut, int32_t *snax,
493  int32_t ny, double *y_init, double *k_init )
494 /*
495  my_lut_funct_3d_oe - Get initial values of y from x
496 
497  Returns int32_t, 0 if all good
498 
499  Parameters: (in calling order)
500  Type Name I/O Description
501  ---- ---- --- -----------
502  gsl_vector * gv_x_fg I LUT indicies
503  double * lut I Specific LUT for this pixel
504  int32_t * snax I size of LUT less last, band dim
505  int32_t ny I # of bands
506  double * y_init O initial y value
507  double * k_init O nx by ny matrix
508 
509  W. Robinson, SAIC, 18 Aug 2023, converted from A. Sayer's IDL code
510 
511 */
512  {
513  int32_t ibnd, icor, nx, iprd;
514  float ar_wt[8], ar_wtu[8], orig_wt[3];
515  double yl, yu, dx, dlut;
516  double xl[3], xu[3];
517  int64_t ar_off[8], ar_offu[8], tmp_off;
518 
519  // printf( "%s, %d, I: entering my_lut_funct_3d_oe\n", __FILE__, __LINE__ );
520  // Interpolate to get Y - no need to re-derive points and weights
521  iint_3d( snax, gv_x_fg->data, ar_off, ar_wt, orig_wt );
522  // apply for each band
523  for( ibnd = 0; ibnd < ny; ibnd++ )
524  {
525  y_init[ibnd] = 0.;
526  for( icor = 0; icor < 8; icor++ )
527  {
528  tmp_off = snax[0] * snax[1] * snax[2];
529  y_init[ibnd] += *( lut + tmp_off * ibnd + ar_off[icor] ) * ar_wt[icor];
530  }
531  }
532  // Estimate K by finite difference
533  dlut = .1;
534  nx = 3;
535 
536  // no need for zeroing the k_init, x_sizes are [snax,ny]
537  // get lower, upper 1st guess value
538  for( iprd = 0; iprd < nx; iprd++ )
539  {
540  xl[iprd] = gv_x_fg->data[iprd]; // lower step
541  xu[iprd] = gv_x_fg->data[iprd]; // upper step
542  }
543  // I'm not sure of the alg below as xl and xu are used only after
544  // changing them in the ibnd location - don't know - WDR
545  for( iprd = 0; iprd < nx; iprd++ )
546  {
547  // Calculate indices to compare for this point in state space
548  // Make sure we don't go out of LUT bounds
549  xl[iprd] = gv_x_fg->data[iprd] - dlut;
550  if( xl[iprd] < 0. ) xl[iprd] = 0.;
551  xu[iprd] = gv_x_fg->data[iprd] + dlut;
552  if( xu[iprd] > snax[iprd]-1 ) xu[iprd] = snax[iprd]-1;
553 
554  dx = xu[iprd] - xl[iprd];
555  // Get weighting function for this parameter for each band
556  // get the offsets and weights for this perturbation
557  iint_3d( snax, xl, ar_off, ar_wt, orig_wt );
558  iint_3d( snax, xu, ar_offu, ar_wtu, orig_wt );
559  // and apply them to get yl, yu
560  for( ibnd = 0; ibnd < ny; ibnd++ )
561  {
562  yl = 0.;
563  yu = 0.;
564  for( icor = 0; icor < 8; icor++ )
565  {
566  tmp_off = snax[0] * snax[1] * snax[2];
567  yl += *( lut + tmp_off * ibnd + ar_off[icor] ) * ar_wt[icor];
568  yu += *( lut + tmp_off * ibnd + ar_offu[icor] ) * ar_wtu[icor];
569  }
570  *( k_init + iprd + 3 * ibnd ) = ( yu - yl ) / dx;
571  }
572  }
573  // and the k_init and y_init are made
574  // printf( "%s, %d, I: end of y_init, k_init set\n", __FILE__, __LINE__ );
575  return 0;
576  }
int32_t ams_oe_inversion(double *rhot, gsl_matrix *gm_sy, double *xa, gsl_matrix *gm_sa, double *lut, int32_t *lut_dims, int32_t *min_cost_loc, oe_info_str *oe_info)
int32_t my_lut_funct_3d_oe(gsl_vector *gv_x_fg, double *lut, int32_t *snax, int32_t ny, double *y_init, double *k_init)
#define fabs(a)
Definition: misc.h:93
int32_t iint_3d(int32_t[3], double[3], int64_t[9], float[9], float[3])
Definition: int_4d.c:117
gsl_matrix * invert_a_matrix(gsl_matrix *matrix)
Definition: get_ctht.c:1722