ReFRACtor
l_rad_rt.cc
Go to the documentation of this file.
1 #include "l_rad_rt.h"
2 #include "ostream_pad.h"
3 
4 #include "ground_lambertian.h"
5 #include "ground_coxmunk.h"
7 #include "ground_brdf.h"
8 
9 using namespace FullPhysics;
10 using namespace blitz;
11 
12 #ifdef HAVE_LUA
13 #include "register_lua.h"
15 l_rad_rt_create(const boost::shared_ptr<RadiativeTransfer>& Rt,
16  const SpectralBound& Spec_bound,
17  const blitz::Array<double, 1>& Sza,
18  const blitz::Array<double, 1>& Zen,
19  const blitz::Array<double, 1>& Azm,
20  bool Pure_nadir,
21  bool Use_first_order_scatt_calc,
22  bool Do_second_order)
23 {
25  boost::dynamic_pointer_cast<RadiativeTransferSingleWn>(Rt);
26 
28  (new LRadRt(rts, Spec_bound, Sza, Zen, Azm, Pure_nadir, Use_first_order_scatt_calc,
29  Do_second_order));
30 }
31 
33 .scope
34 [
35  luabind::def("create", &l_rad_rt_create)
36 ]
38 #endif
39 
40 //-----------------------------------------------------------------------
65 //-----------------------------------------------------------------------
66 
68  const SpectralBound& Spec_bound,
69  const blitz::Array<double, 1>& Sza,
70  const blitz::Array<double, 1>& Zen,
71  const blitz::Array<double, 1>& Azm,
72  bool Pure_nadir,
73  bool Use_first_order_scatt_calc,
74  bool Do_second_order,
75  double Spectrum_spacing,
76  const LRadDriver::PsMode ps_mode)
77  : RadiativeTransferSingleWn(Rt->stokes_coefficient(),
78  Rt->atmosphere_ptr()),
79  use_first_order_scatt_calc(Use_first_order_scatt_calc),
80  do_second_order(Do_second_order),
81  sza(Sza.copy()), zen(Zen.copy()), azm(Azm.copy()),
82  rt(Rt),
83  alt_spec_index_cache(-1)
84 {
85  int nstream = rt->number_stream();
86 
87  // There seems to a bug in l_rad if nstokes = 4,
88  // it will not compute the third stokes value,
89  // so for now the maximum value is 3
90  int nstokes = min(rt->number_stokes(), 3);
91 
92  initialize(Spec_bound, Spectrum_spacing);
93 
94  driver.reset(new LRadDriver(nstream, nstokes,
95  surface_type(),
96  true, // Use tms_correction, have rt
97  Pure_nadir, ps_mode));
98 }
99 
100 //-----------------------------------------------------------------------
126 //-----------------------------------------------------------------------
127 
130  const SpectralBound& Spec_bound,
131  const blitz::Array<double, 1>& Sza,
132  const blitz::Array<double, 1>& Zen,
133  const blitz::Array<double, 1>& Azm,
134  bool Pure_nadir,
135  int Number_stokes,
136  bool Do_second_order,
137  int Number_stream,
138  double Spectrum_spacing,
139  const LRadDriver::PsMode ps_mode)
140  : RadiativeTransferSingleWn(Stokes_coef, Atm),
141  use_first_order_scatt_calc(true),
142  do_second_order(Do_second_order),
143  sza(Sza.copy()), zen(Zen.copy()), azm(Azm.copy()),
144  alt_spec_index_cache(-1)
145 {
146  initialize(Spec_bound, Spectrum_spacing);
147 
148  driver.reset(new LRadDriver(Number_stream, Number_stokes,
149  surface_type(),
150  false, // Do not use TMS correction, no RT
151  Pure_nadir, ps_mode));
152 
153 }
154 
155 void LRadRt::initialize(const SpectralBound& Spec_bound, double Spectrum_spacing)
156 {
157  if(Spec_bound.number_spectrometer() != number_spectrometer() ||
158  sza.rows() != number_spectrometer() ||
159  zen.rows() != number_spectrometer() ||
160  azm.rows() != number_spectrometer()) {
161  throw Exception("Spec_bound, Sza, Zen, and Atm all need to be size number_spectrometer()");
162  }
163 
164  for(int i = 0; i < number_spectrometer(); ++i) {
165  range_check(sza(i), 0.0, 90.0);
166  range_check(azm(i), 0.0, 360.0);
167  range_check(zen(i), 0.0, 90.0);
168  }
169 
170  if(!atm->ground()) {
171  throw Exception("LRadDriver cannot be used without a ground");
172  }
173 
174  for(int i = 0; i < number_spectrometer(); ++i) {
175  // Some versions of the absco tables do not allow interpolation in
176  // the wn direction. This means that only values with a wavenumber
177  // a multiple of the grid spacing should be used.
178  double t = Spec_bound.lower_bound(i, units::inv_cm).value;
179  t = round(t / Spectrum_spacing) * Spectrum_spacing;
180  wmin.push_back(t);
181 
182  t = Spec_bound.upper_bound(i, units::inv_cm).value;
183  t = round(t / Spectrum_spacing) * Spectrum_spacing;
184  wmax.push_back(t);
185  }
186 
187  // Looks at the type of the Ground class to determine the surface
188  // type integer for use in the Spurr RT Fortran code
189  // Do this in the consturctor since dynamic casting is an expensive operation
190  // Used BRDF Types from LRadRt
191  if(dynamic_cast<GroundLambertian*>(atm->ground().get())) {
192  surface_type_int= LRadRt::LAMBERTIAN;
193  } else if(dynamic_cast<GroundCoxmunk*>(atm->ground().get())) {
194  surface_type_int = LRadRt::COXMUNK;
195  } else if(dynamic_cast<GroundCoxmunkPlusLambertian*>(atm->ground().get())) {
196  surface_type_int = LRadRt::COXMUNK;
197  } else if(dynamic_cast<GroundBrdfVeg*>(atm->ground().get())) {
198  surface_type_int = LRadRt::BPDFVEGN;
199  } else if(dynamic_cast<GroundBrdfSoil*>(atm->ground().get())) {
200  surface_type_int = LRadRt::BPDFSOIL;
201  } else {
202  Exception err_msg;
203  err_msg << "Spurr RT can not determine surface type integer from ground class: "
204  << atm->ground();
205  throw(err_msg);
206  }
207 
208  // Watch atmosphere for changes, so we clear cache if needed.
209  atm->add_observer(*this);
210 }
211 
212 void LRadRt::setup_z_matrix_interpol(const double wmin, const ArrayAd<double, 3>& pf_min, const double wmax, const ArrayAd<double, 3>& pf_max) const
213 {
214  ArrayAd<double, 2> z_matrix_min(driver->z_matrix(pf_min));
215  ArrayAd<double, 2> z_matrix_max(driver->z_matrix(pf_max));
216 
217  // Note that timings showed it was faster to have the value and
218  // Jacobian interpolation done separately. This isn't dramatically
219  // faster, but it is easy enough to do. The other approach would be
220  // a single interpolation of an Array<AutoDerivative<double>, 2>.
221  zmat_interpolate.reset(new
222  LinearInterpolate2Point<double, Array<double, 2> >
223  (wmin, z_matrix_min.value(),
224  wmax, z_matrix_max.value()));
225 
226  if(!z_matrix_min.is_constant()) {
227  l_zmat_interpolate.reset(new
228  LinearInterpolate2Point<double, Array<double, 3> >
229  (wmin, z_matrix_min.jacobian(),
230  wmax, z_matrix_max.jacobian()));
231  }
232 }
233 
234 //-----------------------------------------------------------------------
237 //-----------------------------------------------------------------------
238 
239 void LRadRt::update_altitude(int spec_index) const
240 {
241  if(spec_index == alt_spec_index_cache) {
242  return;
243  }
244 
245  alt_spec_index_cache = spec_index;
246 
247  Array<double, 1> alt(atm->altitude(spec_index).convert(units::km).value.value());
248 
249  driver->setup_geometry(alt, sza(spec_index), zen(spec_index), azm(spec_index));
250 
251  ArrayAd<double, 3> pf_min(atm->scattering_moment_wrt_iv(wmin[spec_index], spec_index));
252  ArrayAd<double, 3> pf_max(atm->scattering_moment_wrt_iv(wmax[spec_index], spec_index));
253 
254  setup_z_matrix_interpol(wmin[spec_index], pf_min, wmax[spec_index], pf_max);
255 }
256 
257 ArrayAd<double, 2> LRadRt::get_z_matrix(const double Wn, int Spec_index, const ArrayAd<double, 2>& Iv) const
258 {
259  ArrayAd<double, 2> zmat;
260 
261  // If we aren't passed in intermediate variables to use by the LSI,
262  // then we can use our interpolated value.
263  if(Iv.rows() == 0) {
264  zmat.value().reference((*zmat_interpolate)(Wn));
265 
266  if(l_zmat_interpolate) {
267  zmat.jacobian().reference((*l_zmat_interpolate)(Wn));
268  }
269  } else {
270  ArrayAd<double, 3> pf(atm->scattering_moment_wrt_iv(Wn, Spec_index, Iv));
271  zmat.reference(driver->z_matrix(pf));
272  }
273 
274  return zmat;
275 }
276 
277 void LRadRt::apply_jacobians(double Wn, int Spec_index, ArrayAd<double, 1>& stokes, const Array<double, 3>& jac_atm, const Array<double, 2>& jac_surf, const ArrayAd<double, 2>& Iv) const
278 {
279  //-----------------------------------------------------------------------
287  //-----------------------------------------------------------------------
288 
289  Array<double, 3> jac_iv(0, 0, 0);
290 
291  if(Iv.rows() == 0) {
292  ArrayAd<double, 2> t(atm->intermediate_variable(Wn, Spec_index));
293 
294  if(!t.is_constant()) {
295  jac_iv.reference(t.jacobian());
296  }
297  } else if(!Iv.is_constant()) {
298  jac_iv.reference(Iv.jacobian());
299  }
300 
301  if (stokes.number_variable() != jac_iv.depth()) {
302  stokes.resize_number_variable(jac_iv.depth());
303  }
304 
305  ArrayAd<double, 1> surface_param(atm->ground()->surface_parameter(Wn, Spec_index));
306 
307  for(int i = 0; i < stokes.rows(); ++i) {
308  for(int j = 0; j < stokes.number_variable(); ++j) {
309  double val = 0;
310 
311  if(jac_atm.depth() != 0)
312  for(int m = 0; m < jac_iv.rows(); ++m)
313  for(int n = 0; n < jac_iv.cols(); ++n) {
314  val += jac_atm(i, m, n) * jac_iv(m, n, j);
315  }
316 
317  if(!surface_param.is_constant())
318  for(int m = 0; m < surface_param.jacobian().rows(); ++m)
319  val +=
320  jac_surf(i, m) * surface_param.jacobian()(m, j);
321 
322  stokes.jacobian()(i, j) = val;
323  }
324  }
325 
326 }
327 
328 // See base class for description of this.
329 blitz::Array<double, 1> LRadRt::stokes_single_wn
330 (double Wn, int Spec_index, const ArrayAd<double, 2>& Iv) const
331 {
332  update_altitude(Spec_index);
333 
334  driver->setup_surface_params(atm->ground()->surface_parameter(Wn, Spec_index).value());
335 
336  Array<double, 1> tau;
337  Array<double, 1> omega;
338  if(Iv.rows() == 0) {
339  tau.reference(atm->optical_depth_wrt_iv(Wn, Spec_index).value());
340  omega.reference(atm->single_scattering_albedo_wrt_iv(Wn, Spec_index).value());
341  } else {
342  tau.reference(atm->optical_depth_wrt_iv(Wn, Spec_index, Iv).value());
343  omega.reference(atm->single_scattering_albedo_wrt_iv(Wn, Spec_index, Iv).value());
344  }
345 
346  Array<double, 3> pf(0, 0, 0);
347 
348  if(rt || do_second_order) {
349  // -1 for numscat means get them all.
350  int nscat = (do_second_order ? -1 : 1);
351  int nmom = 2 * number_stream();
352 
353  if(Iv.rows() == 0)
354  pf.reference(atm->scattering_moment_wrt_iv(Wn, Spec_index, nmom,
355  nscat).value());
356  else
357  pf.reference(atm->scattering_moment_wrt_iv(Wn, Spec_index, Iv, nmom,
358  nscat).value());
359  }
360 
361  // For second order corrections, we need all the scattering
362  // elements. However for first order we only need the first element,
363  // and then only if we are calculating fscale. The simplest thing
364  // would be to just get the full scattering matrix each time - but
365  // this turns out to be a bit of a bottle neck so it is worth the
366  // more complicated logic to only get what we need.
367  Array<double, 2> zmat = get_z_matrix(Wn, Spec_index, Iv).value();
368 
369  driver->setup_optical_inputs(tau, omega, pf, zmat);
370 
371  driver->clear_linear_inputs();
372 
373  driver->calculate_first_order();
374 
375  // Not using first order calculation, so zero out first stokes term, turn this off
376  // in cases where first order of scattering is contained in RT code
377  if(!use_first_order_scatt_calc) {
378  driver->stokes()(0) = 0;
379  }
380 
381  if(do_second_order) {
382  driver->calculate_second_order();
383  }
384 
385  // Copy value of stokes from driver so changes to it do not impact our answer
386  // if we had used a reference
387  Array<double, 1> stokes(driver->stokes().shape());
388  stokes = driver->stokes();
389 
390  // We either are correcting a multi-scatter RT code, or just doing a
391  // single scatter alone.
392  if(rt) {
393  Array<double, 1> t(rt->stokes_single_wn(Wn, Spec_index, Iv));
394 
395  for(int i = 0; i < number_stokes(); ++i) {
396  stokes(i) = stokes(i) + t(i);
397  }
398  }
399 
400  return stokes;
401 }
402 
403 // See base class for description of this.
405 (double Wn, int Spec_index, const ArrayAd<double, 2>& Iv) const
406 {
407  update_altitude(Spec_index);
408 
409  driver->setup_surface_params(atm->ground()->surface_parameter(Wn, Spec_index).value());
410 
411  ArrayAd<double, 1> tau;
412  ArrayAd<double, 1> omega;
413  Array<double, 3> jac_iv(0, 0, 0);
414 
415  if(Iv.rows() == 0) {
416  tau.reference(atm->optical_depth_wrt_iv(Wn, Spec_index));
417  omega.reference(atm->single_scattering_albedo_wrt_iv(Wn, Spec_index));
418  ArrayAd<double, 2> t(atm->intermediate_variable(Wn, Spec_index));
419 
420  if(!t.is_constant()) {
421  jac_iv.reference(t.jacobian());
422  }
423  } else {
424  tau.reference(atm->optical_depth_wrt_iv(Wn, Spec_index, Iv));
425  omega.reference(atm->single_scattering_albedo_wrt_iv(Wn, Spec_index, Iv));
426 
427  if(!Iv.is_constant()) {
428  jac_iv.reference(Iv.jacobian());
429  }
430  }
431 
432  ArrayAd<double, 3> pf(0, 0, 0, 0);
433 
434  if(rt || do_second_order) {
435  // -1 for numscat means get them all.
436  int nscat = (do_second_order ? -1 : 1);
437  int nmom = 2 * number_stream();
438 
439  if(Iv.rows() == 0)
440  pf.reference(atm->scattering_moment_wrt_iv(Wn, Spec_index, nmom,
441  nscat));
442  else
443  pf.reference(atm->scattering_moment_wrt_iv(Wn, Spec_index, Iv, nmom,
444  nscat));
445  }
446 
447  // For second order corrections, we need all the scattering
448  // elements. However for first order we only need the first element,
449  // and then only if we are calculating fscale. The simplest thing
450  // would be to just get the full scattering matrix each time - but
451  // this turns out to be a bit of a bottle neck so it is worth the
452  // more complicated logic to only get what we need.
453  ArrayAd<double, 2> zmat = get_z_matrix(Wn, Spec_index, Iv);
454 
455  driver->setup_optical_inputs(tau.value(), omega.value(), pf.value(), zmat.value());
456 
457  driver->setup_linear_inputs(tau, omega, pf, zmat);
458 
459  driver->calculate_first_order();
460 
461  // Not using first order calculation, so zero out first stokes term, turn this off
462  // in cases where first order of scattering is contained in RT code
463  if(!use_first_order_scatt_calc) {
464  driver->stokes()(0) = 0;
465  driver->surface_jacobian()(0, Range::all()) = 0;
466  driver->atmospheric_jacobian()(0, Range::all(), Range::all()) = 0;
467  }
468 
469  if(do_second_order) {
470  driver->calculate_second_order();
471  }
472 
473  // Calculate ArrayAd version of l_rad calculations that includes surface
474  // and atmospheric jacobian components applied through the chain rule
475  // using the intermediate jacobians
476  ArrayAd<double, 1> stokes(driver->stokes().shape(), jac_iv.depth());
477  stokes.value() = driver->stokes();
478 
479  apply_jacobians(Wn, Spec_index, stokes, driver->atmospheric_jacobian(), driver->surface_jacobian(), Iv);
480 
481  // We either are correcting a multi-scatter RT code, or just doing a
482  // single scatter alone.
483  if(rt) {
484  ArrayAd<double, 1> t(rt->stokes_and_jacobian_single_wn(Wn, Spec_index, Iv));
485 
486  for(int i = 0; i < number_stokes(); ++i) {
487  stokes(i) = stokes(i) + t(i);
488  }
489  }
490 
491  return stokes;
492 }
493 void LRadRt::print(std::ostream& Os, bool Short_form) const
494 {
495  OstreamPad opad(Os, " ");
496  Os << "LRadRt:\n"
497  << " Solar zenith angle: \n";
498  opad << sza << "\n";
499  opad.strict_sync();
500  Os << " Zenith angle: \n";
501  opad << zen << "\n";
502  opad.strict_sync();
503  Os << " Azimuth angle: \n";
504  opad << azm << "\n";
505  opad.strict_sync();
506 
507  // Output print from parent class
508  RadiativeTransferSingleWn::print(opad, Short_form);
509  opad.strict_sync();
510 
511  Os << " Radiative Transfer:\n";
512  OstreamPad opad1(Os, " ");
513  rt->print(opad1, true);
514  opad1 << "\n";
515  opad1.strict_sync();
516 
517  // Output print from buffer class
518  Os << " Driver:\n";
519  driver->print(opad1, Short_form);
520  opad1 << "\n";
521  opad1.strict_sync();
522 }
void resize_number_variable(int nvar)
Definition: array_ad.h:165
#define range_check(V, Min, Max)
Range check.
Definition: fp_exception.h:140
virtual int number_stream() const
Number of streams to use in processing.
Definition: l_rad_rt.h:56
bool is_constant() const
Definition: array_ad.h:371
virtual int number_spectrometer() const
Number of spectrometer we have.
This is a filtering stream that adds a pad to the front of every line written out.
Definition: ostream_pad.h:32
virtual blitz::Array< double, 1 > stokes_single_wn(double Wn, int Spec_index, const ArrayAd< double, 2 > &Iv) const
Calculate stokes vector for the given wavenumber.
Definition: l_rad_rt.cc:330
LRadRt(const boost::shared_ptr< RadiativeTransferSingleWn > &Rt, const SpectralBound &Spec_bound, const blitz::Array< double, 1 > &Sza, const blitz::Array< double, 1 > &Zen, const blitz::Array< double, 1 > &Azm, bool Pure_nadir, bool Use_first_order_scatt_calc=true, bool Do_second_order=false, double Spectrum_spacing=0.01, const LRadDriver::PsMode ps_mode=LRadDriver::DETECT)
Constructor.
Definition: l_rad_rt.cc:67
virtual ArrayAd< double, 1 > stokes_and_jacobian_single_wn(double Wn, int Spec_index, const ArrayAd< double, 2 > &Iv) const
Calculate stokes vector and Jacobian for the given wavenumber.
Definition: l_rad_rt.cc:405
This is the base of the exception hierarchy for Full Physics code.
Definition: fp_exception.h:16
#define REGISTER_LUA_DERIVED_CLASS(X, Y)
Definition: register_lua.h:136
This class drives the LRAD code, which gives a polarization correction to scalar intensity and jacobi...
Definition: l_rad_rt.h:19
const blitz::Array< T, D+1 > jacobian() const
Definition: array_ad.h:307
virtual int surface_type() const
Returns an integer with l_rad&#39;s representation of surface type.
Definition: l_rad_rt.h:59
This is a RadiativeTransfer that supplies an interface that can be called for a single wavenumber...
Apply value function to a blitz array.
DoubleWithUnit lower_bound(int Spec_index) const
Lower bound of window slot.
const blitz::Array< T, D > & value() const
Definition: array_ad.h:306
const Unit inv_cm("cm^-1", pow(cm, -1))
This runs a Radiative Transfer code to determine the reflectance for a given set of wavelengths...
The AutoDerivative<T> works well, and it works with blitz if you create a blitz::Array<AutoDerivative...
Definition: array_ad.h:40
This gives the upper and lower bounds of the SpectralWindow.
This does linear interpolate between two points.
virtual blitz::Array< double, 2 > stokes(const SpectralDomain &Spec_domain, int Spec_index) const
Calculate stokes vector for the given set of wavenumbers/wavelengths.
const Unit m("m", 1.0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0)
int number_variable() const
Definition: array_ad.h:376
This class drives the LRAD code, which gives a polarization correction to scalar intensity and jacobi...
Definition: l_rad_driver.h:24
virtual int number_stokes() const
Number of stokes parameters we will return in stokes and stokes_and_jacobian.
Definition: l_rad_rt.h:55
void reference(const ArrayAd< T, D > &V)
Definition: array_ad.h:372
Contains classes to abstract away details in various Spurr Radiative Transfer software.
Definition: doxygen_python.h:1
#define REGISTER_LUA_END()
Definition: register_lua.h:134
def(luabind::constructor< int >()) .def("rows"
int number_spectrometer() const
Number of spectrometers.
const Unit km("km", 1e3 *m)
virtual void print(std::ostream &Os, bool Short_form=false) const
Print to stream.
Definition: l_rad_rt.cc:493
DoubleWithUnit upper_bound(int Spec_index) const
Upper bound of window slot.
virtual void print(std::ostream &Os, bool Short_form=false) const
Print to stream.
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