ReFRACtor
spurr_rt.cc
Go to the documentation of this file.
1 #include "spurr_rt.h"
2 #include "ostream_pad.h"
3 #include <cmath>
4 
5 #include "spurr_brdf_types.h"
6 #include "ground_lambertian.h"
7 #include "ground_coxmunk.h"
9 #include "ground_brdf.h"
10 
11 using namespace FullPhysics;
12 using namespace blitz;
13 
14 // For debugging purposes, it can be useful to dump out the input
15 // used by this class. We'll leave this code in place, in case we
16 // need it again, but normally this turned off.
17 const bool dump_data = false;
18 
19 //-----------------------------------------------------------------------
30 //-----------------------------------------------------------------------
31 
33  const boost::shared_ptr<StokesCoefficient>& Stokes_coef,
34  const blitz::Array<double, 1>& Sza,
35  const blitz::Array<double, 1>& Zen,
36  const blitz::Array<double, 1>& Azm,
37  const bool do_solar,
38  const bool do_thermal)
39 : RadiativeTransferSingleWn(Stokes_coef, Atm),
40  sza(Sza.copy()), zen(Zen.copy()), azm(Azm.copy()),
41  do_solar_sources(do_solar), do_thermal_emission(do_thermal),
42  alt_spec_index_cache(-1), geo_spec_index_cache(-1)
43 {
44  if(sza.rows() != number_spectrometer() ||
45  zen.rows() != number_spectrometer() ||
46  azm.rows() != number_spectrometer())
47  throw Exception("Sza, Zen, and Atm all need to be size number_spectrometer()");
48  for(int i = 0; i < number_spectrometer(); ++i) {
49  range_check(sza(i), 0.0, 90.0);
50  range_check(azm(i), 0.0, 360.0);
51  range_check(zen(i), 0.0, 90.0);
52  }
53 
54  // Watch atmosphere for changes, so we clear cache if needed.
55  atm->add_observer(*this);
56 
57  // Looks at the type of the Ground class to determine the surface
58  // type integer for use in the Spurr RT Fortran code
59  // Do this in the consturctor since dynamic casting is an expensive operation
60  if(dynamic_cast<GroundLambertian*>(atm->ground().get())) {
62  } else if(dynamic_cast<GroundCoxmunk*>(atm->ground().get())) {
64  } else if(dynamic_cast<GroundCoxmunkPlusLambertian*>(atm->ground().get())) {
66  } else if(dynamic_cast<GroundBrdfVeg*>(atm->ground().get())) {
68  } else if(dynamic_cast<GroundBrdfSoil*>(atm->ground().get())) {
70  } else {
71  Exception err_msg;
72  err_msg << "Spurr RT can not determine surface type integer from ground class: "
73  << atm->ground();
74  throw(err_msg);
75  }
76 }
77 
78 //-----------------------------------------------------------------------
81 //-----------------------------------------------------------------------
82 
83 void SpurrRt::update_altitude(int spec_index) const
84 {
85  if(spec_index == alt_spec_index_cache)
86  return;
87  alt_spec_index_cache = spec_index;
88 
89  // Set layers into LIDORT inputs
90  Array<double, 1> atm_alt(atm->altitude(spec_index).convert(units::km).value.value());
91 
92  rt_driver_->setup_height_grid(atm_alt);
93  if(dump_data)
94  std::cout << "# atm_alt:\n" << atm_alt << "\n";
95 }
96 
97 //-----------------------------------------------------------------------
100 //-----------------------------------------------------------------------
101 
102 void SpurrRt::update_geometry(int spec_index) const
103 {
104  if(spec_index == geo_spec_index_cache)
105  return;
106  geo_spec_index_cache = spec_index;
107 
108  rt_driver_->brdf_driver()->setup_geometry(sza(spec_index), azm(spec_index), zen(spec_index));
109  rt_driver_->setup_geometry(sza(spec_index), azm(spec_index), zen(spec_index));
110  if(dump_data)
111  std::cout << "# Geometry:\n" << sza(spec_index) << "\n"
112  << azm(spec_index) << "\n"
113  << zen(spec_index) << "\n";
114 }
115 
116 void SpurrRt::setup_thermal_inputs(double wn, int spec_index) const
117 {
119  return;
120 
121  rt_driver_->setup_thermal_inputs(atm->surface_blackbody(wn, spec_index).value(), atm->atmosphere_blackbody(wn, spec_index).value());
122 }
123 
124 // See base class for description of this
125 Array<double,1> SpurrRt::stokes_single_wn(double Wn, int Spec_index, const ArrayAd<double, 2>& Iv) const
126 {
127  // Obtain Wn and Spec_index dependent inputs
128  Range ra(Range::all());
129 
130  Array<double, 1> od_in, ssa_in;
131  Array<double, 2> pf;
132  if(Iv.rows() != 0) {
133  od_in.reference( atm->optical_depth_wrt_iv(Wn, Spec_index, Iv).value() );
134  ssa_in.reference( atm->single_scattering_albedo_wrt_iv(Wn, Spec_index, Iv).value() );
135  if (number_moment() > 0)
136  pf.reference( atm->scattering_moment_wrt_iv(Wn, Spec_index, Iv, number_moment(), 1)(ra, ra, 0).value() );
137  } else {
138  od_in.reference( atm->optical_depth_wrt_iv(Wn, Spec_index).value() );
139  ssa_in.reference( atm->single_scattering_albedo_wrt_iv(Wn, Spec_index).value() );
140  if (number_moment() > 0)
141  pf.reference( atm->scattering_moment_wrt_iv(Wn, Spec_index, number_moment(), 1)(ra, ra, 0).value() );
142  }
143 
144  // Update user levels if necessary
145  update_altitude(Spec_index);
146 
147  // Update geometry if necessary
148  update_geometry(Spec_index);
149 
150  // Updae thermal emission inputs if enabled
151  setup_thermal_inputs(Wn, Spec_index);
152 
153  // Set up BRDF inputs, here we throw away the jacobian
154  // value of the surface parameters
155  ArrayAd<double, 1> surface_parameters(atm->ground()->surface_parameter(Wn, Spec_index));
156  ArrayAd<double, 1> lidort_surface = rt_driver_->brdf_driver()->setup_brdf_inputs(surface_type(), surface_parameters);
157 
158  // Set up LIDORT inputs and run
159  rt_driver_->setup_optical_inputs(od_in, ssa_in, pf);
160  rt_driver_->clear_linear_inputs();
161  rt_driver_->calculate_rt();
162 
163  // Copy values from LIDORT
164  Array<double, 1> stokes(number_stokes());
165  stokes = 0;
166  stokes(0) = rt_driver_->get_intensity();
167  // Check for NaN from lidort
168  if(std::isnan(stokes(0)))
169  throw Exception("SpurrRt encountered a NaN in the radiance");
170  return stokes;
171 }
172 
173 // See base class for description of this
175 {
176 
177  // Obtain Wn and Spec_index dependent inputs
178  Range ra(Range::all());
179  ArrayAd<double, 1> od, ssa;
181  Array<double, 3> jac_iv(0,0,0);
182  if(Iv.rows() != 0) {
183  od.reference(atm->optical_depth_wrt_iv(Wn, Spec_index, Iv));
184  ssa.reference(atm->single_scattering_albedo_wrt_iv(Wn, Spec_index, Iv));
185  if (number_moment() > 0)
186  pf.reference(atm->scattering_moment_wrt_iv(Wn, Spec_index, Iv, number_moment(), 1)(ra, ra, 0));
187  if(!Iv.is_constant())
188  jac_iv.reference(Iv.jacobian());
189  } else {
190  od.reference(atm->optical_depth_wrt_iv(Wn, Spec_index));
191  ssa.reference(atm->single_scattering_albedo_wrt_iv(Wn, Spec_index));
192  if (number_moment() > 0)
193  pf.reference(atm->scattering_moment_wrt_iv(Wn, Spec_index, number_moment(), 1)(ra, ra, 0));
194  ArrayAd<double, 2> inter_var(atm->intermediate_variable(Wn, Spec_index));
195  if(!inter_var.is_constant())
196  jac_iv.reference(inter_var.jacobian());
197  }
198 
199  // Update user levels if necessary
200  update_altitude(Spec_index);
201 
202  // Update geometry if necessary
203  update_geometry(Spec_index);
204 
205  // Updae thermal emission inputs if enabled
206  setup_thermal_inputs(Wn, Spec_index);
207 
208  // Setup surface
209  ArrayAd<double, 1> surface_parameters(atm->ground()->surface_parameter(Wn, Spec_index));
210  ArrayAd<double, 1> lidort_surface = rt_driver_->brdf_driver()->setup_brdf_inputs(surface_type(), surface_parameters);
211 
212  // Set up LIDORT inputs and run
213  rt_driver_->setup_optical_inputs(od.value(), ssa.value(), pf.value());
214 
215  bool do_surface_pd = !surface_parameters.is_constant();
216  if(dump_data &&
217  fabs(Wn - 13050.47) < 0.005)
218  std::cout << "# Surface type:\n " << surface_type() << "\n"
219  << "# Surface paramters:\n" << surface_parameters << "\n"
220  << "# Od:\n" << od << "\n"
221  << "# SSA:\n" << ssa << "\n"
222  << "# PF:\n" << pf << "\n"
223  << "# Do surface pd:\n" << do_surface_pd << "\n";
224 
225  rt_driver_->setup_linear_inputs(od, ssa, pf, do_surface_pd);
226  rt_driver_->calculate_rt();
227 
228  // Copy values from LIDORT
229  double rad = rt_driver_->get_intensity();
230 
231  Array<double, 2> jac_atm;
232  Array<double, 1> jac_surf;
233  rt_driver_->copy_jacobians(jac_atm, jac_surf);
234 
235  //-----------------------------------------------------------------------
243  //-----------------------------------------------------------------------
244  Array<double, 1> jac(jac_iv.depth());
245  for(int i = 0; i < jac.rows(); ++i) {
246  double val = 0;
247  // dimensions swapped on jac_iv and jac_atm
248  // jac_atm is njac x nlayer
249  // jac_iv is nlayer x njac
250  for(int m = 0; m < jac_iv.rows(); ++m)
251  for(int n = 0; n < jac_iv.cols(); ++n)
252  val += jac_atm(n,m) * jac_iv(m, n, i);
253  if(do_surface_pd)
254  // The min() here ensures that we only loop over the number of parameters that
255  // either the RT or source parameters both have
256  // LIDORT will have a jac_surf larger insize (hardcoded allocation) than
257  // lidort_surface.jacobian()
258  // 2stream has a jac_surf smaller than lidort_surface because due to the way
259  // it is set up no parameter index is available for shadowing when using
260  // coxmunk mode
261  for(int m = 0; m < min(jac_surf.rows(), lidort_surface.jacobian().rows()); ++m) {
262  val += jac_surf(m) * lidort_surface.jacobian()(m, i);
263  }
264  jac(i) = val;
265  }
266 
267  ArrayAd<double, 1> rad_jac(number_stokes(), jac.rows());
268  rad_jac = 0;
269  rad_jac(0) = AutoDerivative<double>(rad, jac);
270  // Check for NaN from lidort
271  if(std::isnan(rad))
272  throw Exception("SpurrRt encountered a NaN in the radiance");
273  if(any(blitz_isnan(jac)))
274  throw Exception("SpurrRt encountered a NaN in the jacobian");
275  return rad_jac;
276 }
277 
278 //-----------------------------------------------------------------------
280 //-----------------------------------------------------------------------
281 
282 void SpurrRt::print(std::ostream& Os, bool Short_form) const
283 {
284  Os << "SpurrRt\n";
285  OstreamPad opad1(Os, " ");
286  RadiativeTransferSingleWn::print(opad1, Short_form);
287  opad1.strict_sync();
288  OstreamPad opad(Os, " ");
289  Os << " Solar zenith angle: \n";
290  opad << sza << "\n";
291  opad.strict_sync();
292  Os << " Zenith angle: \n";
293  opad << zen << "\n";
294  opad.strict_sync();
295  Os << " Azimuth angle: \n";
296  opad << azm << "\n";
297  opad.strict_sync();
298 
299  opad << "\n";
300  opad.strict_sync();
301 }
virtual void setup_thermal_inputs(double wn, int spec_index) const
Definition: spurr_rt.cc:116
#define range_check(V, Min, Max)
Range check.
Definition: fp_exception.h:140
const bool dump_data
Definition: spurr_rt.cc:17
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 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: spurr_rt.cc:174
boost::shared_ptr< SpurrRtDriver > rt_driver_
Definition: spurr_rt.h:61
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: spurr_rt.cc:125
SpurrRt(const boost::shared_ptr< RtAtmosphere > &Atm, const boost::shared_ptr< StokesCoefficient > &Stokes_coef, const blitz::Array< double, 1 > &Sza, const blitz::Array< double, 1 > &Zen, const blitz::Array< double, 1 > &Azm, bool do_solar=true, bool do_thermal=false)
Constructor.
Definition: spurr_rt.cc:32
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_moment() const =0
Number of moments for scattering matrix.
This is a RadiativeTransfer that supplies an interface that can be called for a single wavenumber...
Apply value function to a blitz array.
const blitz::Array< T, D > & value() const
Definition: array_ad.h:306
virtual void update_geometry(int spec_index) const
Update the geometry if necessary, only needs to change when spectrometer index changes.
Definition: spurr_rt.cc:102
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)
blitz::Array< double, 1 > zen
Definition: spurr_rt.h:59
blitz::Array< double, 1 > azm
Definition: spurr_rt.h:59
void reference(const ArrayAd< T, D > &V)
Definition: array_ad.h:372
bool do_thermal_emission
Definition: spurr_rt.h:57
blitz::Array< double, 1 > sza
Definition: spurr_rt.h:59
virtual void print(std::ostream &Os, bool Short_form=false) const
Print to a stream.
Definition: spurr_rt.cc:282
Contains classes to abstract away details in various Spurr Radiative Transfer software.
Definition: doxygen_python.h:1
virtual void update_altitude(int spec_index) const
Update the altitude information.
Definition: spurr_rt.cc:83
virtual int number_stokes() const
Number of stokes in returned stokes values Note that LIDORT will only ever calculate the first stoke ...
Definition: spurr_rt.h:38
const Unit km("km", 1e3 *m)
const Unit rad("rad", 1.0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0)
double value(const FullPhysics::AutoDerivative< double > &Ad)
virtual void print(std::ostream &Os, bool Short_form=false) const
Print to stream.
virtual int surface_type() const
Integer representing the surface type using the LIDORT indexing nomenclature.
Definition: spurr_rt.h:47
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:10