ReFRACtor
l_rad_driver.cc
Go to the documentation of this file.
1 #include "l_rad_driver.h"
2 
3 #include "wgs84_constant.h"
4 #include "linear_algebra.h"
5 #include "ostream_pad.h"
6 
7 using namespace FullPhysics;
8 using namespace blitz;
9 
10 // l_rad Fortran code functions
11 extern "C" {
12  void lr_init(const int* nstream,
13  const int* nstokes, const int* surftype,
14  void** l_rad_struct_c);
15  void lr_cleanup(void** l_rad_struct_c);
16  void init_l_rad(void** l_rad_struct_c, const double* sza,
17  const double* zen, const double* azm, const double* alt,
18  const double* radius_km, const int* nlayer,
19  const bool* regular_ps, const bool* enhanced_ps, const bool* pure_nadir);
20  void l_rad_first_driver(void* const* l_rad_struct_c, const int* nlayer,
21  const int* natm_der,
22  const int* nstokes, const double* tau,
23  const double* L_tau, const double* omega,
24  const double* L_omega,
25  const double* Zmat, const double* L_Zmat,
26  const double* fscale, const double* L_fscale,
27  const double* spars, const int* nspars,
28  const int* need_jacobians_i,
29  double* R1, double* L_R1,
30  double* Ls_R1);
31 
32  void l_rad_second_driver(void* const* l_rad_struct_c, const int* n_layers,
33  const int* n_params, const int* nstokes, const int* n_streams,
34  const int*n_scatt, const double* tau, const double* L_tau,
35  const double* omega, const double* L_omega,
36  const double* spars, const int* nspars,
37  const double* coefs, const double* L_coefs, const int* n_eff_coefs,
38  const int* need_jacobians_i,
39  double* R2, double* L_R2, double* Ls_R2,
40  double* ICorr, double* L_ICorr, double* Ls_ICorr);
41 
42  void calc_z(void* const* l_rad_struct_c, const int* nlay, const int* nstokes,
43  const int* nmom, const int* npar, const double* dcoef,
44  const double* l_dcoef, double* zmat, double* l_zmat);
45 }
46 
47 LRadDriver::LRadDriver(int Number_stream, int Number_stokes,
48  int Surface_type,
49  bool Tms_correction,
50  bool Pure_nadir, const PsMode ps_mode)
51  : nstream(Number_stream),
52  nstokes(Number_stokes),
53  surface_type(Surface_type),
54  use_tms_correction(Tms_correction),
55  pure_nadir(Pure_nadir)
56 {
57  // There seems to a bug in l_rad if nstokes = 4,
58  // it will not compute the third stokes value,
59  // so for now the maximum value is 3
60  range_check(nstokes, 1, 4);
61 
62  initialize(ps_mode);
63 }
64 
65 //-----------------------------------------------------------------------
67 //-----------------------------------------------------------------------
68 
69 void LRadDriver::initialize(const PsMode ps_mode)
70 {
71  range_min_check(nstream, 1);
72 
73  // Determine which pseudo-spherical mode to use based on the zenith angle
74  // ie, how close are we to nadir, or use preset value
75  set_ps_mode(ps_mode);
76 
77  // Initially we are not using jacobians until linear inputs are setup
78  need_jacobians_i = 0;
79 
80  lr_init(&nstream, &nstokes, &surface_type, &l_rad_struct);
81 }
82 
83 //-----------------------------------------------------------------------
85 //-----------------------------------------------------------------------
86 
88 {
89  lr_cleanup(&l_rad_struct);
90 }
91 
92 //-----------------------------------------------------------------------
95 //-----------------------------------------------------------------------
96 
97 void LRadDriver::setup_geometry(Array<double, 1> alt, double sza, double zen, double azm) const
98 {
99  int nlayer = alt.rows() - 1;
100 
101  double radius_km = OldConstant::wgs84_a.convert(units::km).value;
102 
103  init_l_rad(&l_rad_struct, &sza, &zen, &azm, alt.dataFirst(),
104  &radius_km, &nlayer, &regular_ps, &enhanced_ps, &pure_nadir);
105 }
106 
107 //-----------------------------------------------------------------------
113 //-----------------------------------------------------------------------
114 
117 {
118  int nlayer = pf.cols();
119  int natm_jac = pf.number_variable();
120 
121  ArrayAd<double, 2> zmat(nlayer, nstokes, natm_jac);
122  zmat.value().reference
123  (Array<double, 2>(nlayer, nstokes, ColumnMajorArray<2>()));
124  zmat.jacobian().reference
125  (Array<double, 3>(nlayer, nstokes, natm_jac, ColumnMajorArray<3>()));
126  Array<double, 3> pf_f(to_fortran(pf.value()));
127  Array<double, 4> l_pf_f(to_fortran(pf.jacobian()));
128  int nmom = pf.rows() - 1;
129  calc_z(&l_rad_struct, &nlayer, &nstokes, &nmom, &natm_jac, pf_f.dataFirst(),
130  l_pf_f.dataFirst(), zmat.value().dataFirst(),
131  zmat.jacobian().dataFirst());
132  return zmat;
133 }
134 
135 void LRadDriver::setup_surface_params(const Array<double, 1>& surface_param)
136 {
137  if (surface_param_f.rows() != surface_param.rows()) {
138  surface_param_f.reference(Array<double, 1>(surface_param.rows(), ColumnMajorArray<1>()));
139  }
140  surface_param_f = surface_param;
141 }
142 
143 void LRadDriver::setup_optical_inputs(const Array<double, 1>& od,
144  const Array<double, 1>& ssa,
145  const Array<double, 3>& pf,
146  const Array<double, 2>& zmat)
147 {
148 
149  if (tau_f.rows() != od.rows()) {
150  tau_f.reference(Array<double, 1>(od.shape(), ColumnMajorArray<1>()));
151  }
152  tau_f = od;
153 
154  if (omega_f.rows() != ssa.rows()) {
155  omega_f.reference(Array<double, 1>(ssa.shape(), ColumnMajorArray<1>()));
156  }
157  omega_f = ssa;
158 
159  if(pf_f.rows() != pf.rows() or pf_f.cols() != pf.cols() or pf_f.depth() != pf.depth()) {
160  pf_f.reference(Array<double, 3>(pf.shape(), ColumnMajorArray<3>()));
161  }
162  pf_f = pf;
163 
164  if (zmat_f.rows() != zmat.rows() or zmat_f.cols() != zmat.cols()) {
165  zmat_f.reference(Array<double, 2>(zmat.shape(), ColumnMajorArray<2>()));
166  }
167  zmat_f = zmat;
168 
169  // Calculate factor needed for TMS correction. This is the
170  // correction needed when we are combining with a multi-scattering
171  // Corrects for errors due to delta-M scaling in intensity RT.
172  // Compute single scattering using ALL themoments of the phase function
173  // but using the truncated phase function to compute the multiple scattering
174  // contribution.
175  //
176  // If we aren't using an underlying multiscattering RT, then this
177  // should be zero.
178  int nmom = 2 * nstream;
179 
180  if(fscale_f.rows() != od.rows()) {
181  fscale_f.reference(Array<double, 1>(od.shape(), ColumnMajorArray<1>()));
182  }
183 
184  if (use_tms_correction) {
185  fscale_f = pf(nmom, Range::all(), 0) / (2 * nmom + 1);
186  } else {
187  fscale_f = 0;
188  }
189 
190 }
191 
193 {
194  // We no longer need jacobians
195  need_jacobians_i = 0;
196 
197  // These need to be resized to what the fortran interface is expecting
198  // Also zero them out
199  if(jac_atm_f.rows() != number_stokes() or jac_atm_f.cols() != tau_f.rows()) {
200  jac_atm_f.resize(number_stokes(), tau_f.rows(), 1);
201  }
202  jac_atm_f = 0;
203 
204  if (jac_surf_f.rows() != number_stokes() or jac_surf_f.cols() != surface_param_f.rows()) {
205  jac_surf_f.resize(number_stokes(), surface_param_f.rows());
206  }
207  jac_surf_f = 0;
208 
209  if(l_tau_f.rows() != tau_f.rows()) {
210  l_tau_f.resize(tau_f.rows(), 1);
211  }
212  l_tau_f = 0;
213 
214  if(l_omega_f.rows() != omega_f.rows()) {
215  l_omega_f.resize(omega_f.rows(), 1);
216  }
217  l_omega_f = 0;
218 
219  if(l_pf_f.rows() != pf_f.rows() or l_pf_f.cols() != pf_f.cols() or l_pf_f.depth() != pf_f.depth()) {
220  l_pf_f.resize(pf_f.rows(), pf_f.cols(), pf_f.depth(), 1);
221  }
222  l_pf_f = 0;
223 
224  if(l_zmat_f.rows() != zmat_f.rows() or l_zmat_f.cols() != zmat_f.cols()) {
225  l_zmat_f.resize(zmat_f.rows(), zmat_f.cols(), 1);
226  }
227  l_zmat_f = 0;
228 
229  if(l_fscale_f.rows() != fscale_f.rows()) {
230  l_fscale_f.resize(fscale_f.rows(), 1);
231  }
232  l_fscale_f = 0;
233 }
234 
236  const ArrayAd<double, 1>& ssa,
237  const ArrayAd<double, 3>& pf,
238  const ArrayAd<double, 2>& zmat)
239 {
240  // Flag that we will be requesting jacobians
241  need_jacobians_i = 1;
242 
243  int natm_jac = std::max(pf.number_variable(),
244  std::max(od.number_variable(),
245  ssa.number_variable()));
246 
247  // Initialize output variables for jacobians
248  if(jac_atm_f.rows() != number_stokes() or jac_atm_f.cols() != od.rows() or jac_atm_f.depth() != natm_jac) {
249  jac_atm_f.reference(Array<double, 3>(number_stokes(), od.rows(), natm_jac, ColumnMajorArray<3>()));
250  }
251 
252  if (jac_surf_f.rows() != number_stokes() or jac_surf_f.cols() != surface_param_f.rows()) {
253  jac_surf_f.reference(Array<double, 2>(number_stokes(), surface_param_f.rows(), ColumnMajorArray<2>()));
254  }
255 
256  // Copy over input jacobian arrays
257  if(l_tau_f.rows() != od.jacobian().rows() or l_tau_f.cols() != od.jacobian().cols()) {
258  l_tau_f.reference(Array<double, 2>(od.jacobian().shape(), ColumnMajorArray<2>()));
259  }
260  l_tau_f = od.jacobian();
261 
262  if(l_omega_f.rows() != ssa.jacobian().rows() or l_omega_f.cols() != ssa.jacobian().cols()) {
263  l_omega_f.reference(Array<double, 2>(ssa.jacobian().shape(), ColumnMajorArray<2>()));
264  }
265  l_omega_f = ssa.jacobian();
266 
267  if(l_pf_f.rows() != pf.jacobian().rows() or l_pf_f.cols() != pf.jacobian().cols() or l_pf_f.depth() != pf.jacobian().extent(fourthDim) or l_pf_f.extent(fourthDim)) {
268  l_pf_f.reference(Array<double, 4>(pf.jacobian().shape(), ColumnMajorArray<4>()));
269  }
270  l_pf_f = pf.jacobian();
271 
272  if(l_zmat_f.rows() != zmat.jacobian().rows() or l_zmat_f.cols() != zmat.jacobian().cols() or l_zmat_f.depth() != zmat.jacobian().depth()) {
273  l_zmat_f.reference(Array<double, 3>(zmat.jacobian().shape(), ColumnMajorArray<3>()));
274  }
275  l_zmat_f = zmat.jacobian();
276 
277  // Calculate factor needed for TMS correction. This is the
278  int nmom = 2 * nstream;
279  Range ra(Range::all());
280 
281  if(l_fscale_f.rows() != od.rows() or l_fscale_f.cols() != natm_jac) {
282  l_fscale_f.reference(Array<double, 2>(od.rows(), natm_jac, ColumnMajorArray<2>()));
283  }
284 
285  if (use_tms_correction) {
286  l_fscale_f = pf.jacobian()(nmom, ra, 0, ra) / (2 * nmom + 1);
287  } else {
288  l_fscale_f = 0;
289  }
290 
291 }
292 
294 void LRadDriver::check_rt_inputs()
295 {
296  if(surface_param_f.rows() == 0) {
297  throw Exception("surface_param_f not allocated");
298  }
299 
300  if(tau_f.rows() == 0) {
301  throw Exception("tau_f not allocated");
302  }
303 
304  if(omega_f.rows() == 0) {
305  throw Exception("omega_f not allocated");
306  }
307 
308  if(zmat_f.rows() == 0) {
309  throw Exception("zmat_f not allocated");
310  }
311 
312  if(fscale_f.rows() == 0) {
313  throw Exception("fscale_f not allocated");
314  }
315 
316  if (need_jacobians_i == 1) {
317  if(l_tau_f.rows() == 0) {
318  throw Exception("l_tau_f not allocated");
319  }
320 
321  if(l_omega_f.rows() == 0) {
322  throw Exception("l_omega_f not allocated");
323  }
324 
325  if(l_zmat_f.rows() == 0) {
326  throw Exception("l_zmat_f not allocated");
327  }
328 
329  if(l_fscale_f.rows() == 0) {
330  throw Exception("l_fscale_f not allocated");
331  }
332 
333  if(jac_atm_f.rows() == 0) {
334  throw Exception("jac_atm_f not allocated");
335  }
336 
337  if(jac_surf_f.rows() == 0) {
338  throw Exception("jac_surf_f not allocated");
339  }
340  }
341 
342 }
343 
345 {
346  check_rt_inputs();
347 
348  if(stokes_val_f.rows() != number_stokes()) {
349  stokes_val_f.reference(Array<double, 1>(number_stokes(), ColumnMajorArray<1>()));
350  }
351 
352  // Dump r1 value directly into stokes_val array
353  Array<double, 1> r1(stokes_val_f);
354 
355  int nlayer = tau_f.rows();
356  int nspars = surface_param_f.rows();
357  int natm_jac = jac_atm_f.depth();
358 
359  l_rad_first_driver(&l_rad_struct, &nlayer, &natm_jac, &nstokes,
360  tau_f.dataFirst(), l_tau_f.dataFirst(),
361  omega_f.dataFirst(),
362  l_omega_f.dataFirst(), zmat_f.dataFirst(),
363  l_zmat_f.dataFirst(), fscale_f.dataFirst(),
364  l_fscale_f.dataFirst(), surface_param_f.dataFirst(),
365  &nspars,
366  &need_jacobians_i,
367  r1.dataFirst(), jac_atm_f.dataFirst(), jac_surf_f.dataFirst());
368 }
369 
371 {
372  check_rt_inputs();
373 
374  // Only require pf_f to not be size of 0 for second order calculations
375  if(pf_f.rows() == 0) {
376  throw Exception("pf_f not allocated");
377  }
378 
379  if (need_jacobians_i == 1) {
380  if(l_pf_f.rows() == 0) {
381  throw Exception("l_pf_f not allocated");
382  }
383  }
384 
385  if(stokes_val_f.rows() != number_stokes()) {
386  stokes_val_f.reference(Array<double, 1>(number_stokes(), ColumnMajorArray<1>()));
387  }
388 
389  int nscatt = pf_f.depth();
390  int nlayer = tau_f.rows();
391  int nmom = 2 * nstream;
392  int nspars = surface_param_f.rows();
393  int natm_jac = jac_atm_f.depth();
394 
395  Range ra(Range::all());
396  Range rpol(1, number_stokes() - 1);
397 
398  // We only supply nstream coefficients to l_rad_second, so that is
399  // essentially the number of effective coefficients
400  Array<int, 1> neff_coefs(nlayer, ColumnMajorArray<1>());
401  neff_coefs = nmom - 1;
402 
403  Array<double, 1> r2(number_stokes(), ColumnMajorArray<1>());
404  Array<double, 2> ls_r2(number_stokes(), nspars, ColumnMajorArray<2>());
405  Array<double, 3> l_r2(number_stokes(), nlayer,
406  natm_jac, ColumnMajorArray<3>());
407  double icorr;
408  Array<double, 1> ls_icorr(nspars);
409  Array<double, 2> l_icorr(nlayer, natm_jac,
410  ColumnMajorArray<2>());
411 
412  // Use only the parts of the phase function expected by l_rad_second
413  // for coeffs variables. We need to use a to_fortran command here
414  // to ensure we have an array with column first ordering that is also
415  // contiguous. Using a reference to pf will not work.
416  Array<double, 3> coefs_f(to_fortran(pf_f(Range(0, nmom - 1), ra, ra)));
417  Array<double, 4> l_coefs_f(to_fortran(l_pf_f(Range(0, nmom - 1), ra, ra)));
418 
419  l_rad_second_driver(&l_rad_struct, &nlayer, &natm_jac, &nstokes,
420  &nmom, &nscatt,
421  tau_f.dataFirst(), l_tau_f.dataFirst(),
422  omega_f.dataFirst(), l_omega_f.dataFirst(),
423  surface_param_f.dataFirst(), &nspars,
424  coefs_f.dataFirst(), l_coefs_f.dataFirst(),
425  neff_coefs.dataFirst(),
426  &need_jacobians_i,
427  r2.dataFirst(), l_r2.dataFirst(), ls_r2.dataFirst(),
428  &icorr, l_icorr.dataFirst(), ls_icorr.dataFirst());
429 
430  // Second order calculation is split into parts, combine into how
431  // they influence the stokes array and jacobians
432  stokes_val_f(0) += icorr;
433  stokes_val_f(rpol) += r2(rpol);
434 
435  if (need_jacobians_i == 1) {
436  jac_surf_f(0, ra) += ls_icorr;
437  jac_surf_f(rpol, ra) += ls_r2(rpol, ra);
438  jac_atm_f(0, ra, ra) += l_icorr;
439  jac_atm_f(rpol, ra, ra) += l_r2(rpol, ra, ra);
440  }
441 }
442 
443 Array<double, 1> LRadDriver::stokes() const
444 {
445  if (stokes_val_f.rows() == 0) {
446  throw Exception("Stokes have not yet been allocated, use an RT operation first");
447  }
448 
449  return stokes_val_f;
450 }
451 
452 Array<double, 3> LRadDriver::atmospheric_jacobian() const
453 {
454  if (jac_atm_f.rows() == 0) {
455  throw Exception("Atmospheric jacobians array has not yet been allocated. Set up linear inputs first.");
456  }
457 
458  return jac_atm_f;
459 }
460 
461 Array<double, 2> LRadDriver::surface_jacobian() const
462 {
463  if (jac_surf_f.rows() == 0) {
464  throw Exception("Surface jacobians array has not yet been allocated. Set up linear inputs first.");
465  }
466 
467  return jac_surf_f;
468 }
469 
470 void LRadDriver::print(std::ostream& Os, bool Short_form) const
471 {
472  Os << "LRadDriver:\n";
473  Os << " Number stream: " << nstream << "\n"
474  << " Number stokes: " << nstokes << "\n"
475  << " Surface type: " << surface_type << "\n"
476  << " Use TMS Correction: "
477  << (use_tms_correction ? "True\n" : "False\n")
478  << " Pure nadir mode: "
479  << (pure_nadir ? "True" : "False") << "\n";
480 }
#define range_check(V, Min, Max)
Range check.
Definition: fp_exception.h:140
virtual blitz::Array< double, 3 > atmospheric_jacobian() const
Atmospheric jacobian from last calculation.
void l_rad_first_driver(void *const *l_rad_struct_c, const int *nlayer, const int *natm_der, const int *nstokes, const double *tau, const double *L_tau, const double *omega, const double *L_omega, const double *Zmat, const double *L_Zmat, const double *fscale, const double *L_fscale, const double *spars, const int *nspars, const int *need_jacobians_i, double *R1, double *L_R1, double *Ls_R1)
int cols() const
Definition: array_ad.h:369
virtual void calculate_first_order()
Perform radiative transfer calculation with the values setup by setup_optical_inputs and setup_linear...
virtual void setup_geometry(blitz::Array< double, 1 > alt, double sza, double zen, double azm) const
Setup viewing geometry, should only be called once per instance or if the viewing geometry changes...
Definition: l_rad_driver.cc:97
const DoubleWithUnit wgs84_a(6378137.0000, units::m)
Equatorial radius.
This is the base of the exception hierarchy for Full Physics code.
Definition: fp_exception.h:16
const blitz::Array< T, D+1 > jacobian() const
Definition: array_ad.h:307
virtual int number_stokes() const
Definition: l_rad_driver.h:37
virtual void print(std::ostream &Os, bool Short_form=false) const
virtual void setup_linear_inputs(const ArrayAd< double, 1 > &od, const ArrayAd< double, 1 > &ssa, const ArrayAd< double, 3 > &pf, const ArrayAd< double, 2 > &zmat)
Set up linearization, weighting functions.
Apply value function to a blitz array.
void lr_cleanup(void **l_rad_struct_c)
const blitz::Array< T, D > & value() const
Definition: array_ad.h:306
blitz::Array< T, D > to_fortran(const blitz::Array< T, D > &In)
Ensure that a given blitz::Array is contiguous, not reversed, and in fortran ColumnMajorArray format...
virtual blitz::Array< double, 2 > surface_jacobian() const
Surface jacobian.
virtual blitz::Array< double, 1 > stokes() const
Retrieve the stokes values calculated.
LRadDriver(int Number_stream, int Number_stokes, int surface_type, bool Tms_Correction=false, bool Pure_nadir=false, const PsMode ps_mode=DETECT)
Definition: l_rad_driver.cc:47
virtual void setup_surface_params(const blitz::Array< double, 1 > &surface_param)
Set up surface parameters for spectral point.
ArrayAd< double, 2 > z_matrix(const ArrayAd< double, 3 > &pf) const
Calculate the z matrix.
int number_variable() const
Definition: array_ad.h:376
DoubleWithUnit convert(const Unit &R) const
Convert to the given units.
virtual void setup_optical_inputs(const blitz::Array< double, 1 > &od, const blitz::Array< double, 1 > &ssa, const blitz::Array< double, 3 > &pf, const blitz::Array< double, 2 > &zmat)
Set up optical depth, single scattering albedo and scattering matrix Should be called per spectral po...
void l_rad_second_driver(void *const *l_rad_struct_c, const int *n_layers, const int *n_params, const int *nstokes, const int *n_streams, const int *n_scatt, const double *tau, const double *L_tau, const double *omega, const double *L_omega, const double *spars, const int *nspars, const double *coefs, const double *L_coefs, const int *n_eff_coefs, const int *need_jacobians_i, double *R2, double *L_R2, double *Ls_R2, double *ICorr, double *L_ICorr, double *Ls_ICorr)
void init_l_rad(void **l_rad_struct_c, const double *sza, const double *zen, const double *azm, const double *alt, const double *radius_km, const int *nlayer, const bool *regular_ps, const bool *enhanced_ps, const bool *pure_nadir)
Contains classes to abstract away details in various Spurr Radiative Transfer software.
Definition: doxygen_python.h:1
#define range_min_check(V, Min)
Range check.
Definition: fp_exception.h:167
const Unit km("km", 1e3 *m)
void lr_init(const int *nstream, const int *nstokes, const int *surftype, void **l_rad_struct_c)
virtual ~LRadDriver()
Destructor.
Definition: l_rad_driver.cc:87
void calc_z(void *const *l_rad_struct_c, const int *nlay, const int *nstokes, const int *nmom, const int *npar, const double *dcoef, const double *l_dcoef, double *zmat, double *l_zmat)
virtual void clear_linear_inputs()
Mark that we are not retrieving weighting functions.
virtual void calculate_second_order()
Perform radiative transfer calculation with the values setup by setup_optical_inputs and setup_linear...
int rows() const
Definition: array_ad.h:368

Copyright © 2017, California Institute of Technology.
ALL RIGHTS RESERVED.
U.S. Government Sponsorship acknowledged.
Generated Fri Aug 24 2018 15:44:08