ReFRACtor
lsi_rt.cc
Go to the documentation of this file.
1 #include "lsi_rt.h"
2 #include "ifstream_cs.h"
3 #include "bin_map.h"
4 #include "ostream_pad.h"
5 #include <boost/lexical_cast.hpp>
6 #include <boost/bind.hpp>
7 
8 using namespace FullPhysics;
9 using namespace blitz;
10 
11 #ifdef HAVE_LUA
12 #include "register_lua.h"
14 lsi_rt_create(const boost::shared_ptr<RadiativeTransfer>& Rt_low,
16  const HdfFile& Config_file,
17  const std::string& Lsi_group)
18 {
20  boost::dynamic_pointer_cast<RadiativeTransferSingleWn>(Rt_low);
22  boost::dynamic_pointer_cast<RadiativeTransferSingleWn>(Rt_high);
24  (new LsiRt(rt_lows, rt_highs, Config_file, Lsi_group));
25 }
26 
28 .scope
29 [
30  luabind::def("create", &lsi_rt_create)
31 ]
33 #endif
34 
35 // Threshold for switching from log linear to linear interpolation.
36 const double log_threshold = 100;
37 
38 //-----------------------------------------------------------------------
61 //-----------------------------------------------------------------------
62 
65  const std::string& Lsi_fname,
66  double Water_vapor_fraction_threshold)
67 : RadiativeTransferFixedStokesCoefficient(High_stream_rt->stokes_coefficient()),
68  low_stream_rt(Low_stream_rt), high_stream_rt(High_stream_rt),
69  wv_threshold(Water_vapor_fraction_threshold)
70 {
71  if(low_stream_rt->number_stokes() != high_stream_rt->number_stokes())
72  throw Exception("Low stream and high stream need to have the same number of stokes parameters");
73 
74  IfstreamCs in(Lsi_fname);
75  if(!in.good())
76  throw Exception("Trouble reading LSI file " + Lsi_fname);
77  in.exceptions(std::ifstream::badbit);
78  for(int i = 0; i < number_spectrometer(); ++i) {
79  std::string main_gas_in;
80  int nbound;
81  in >> main_gas_in >> nbound;
82  main_gas.push_back(main_gas_in);
83  std::vector<double> t(nbound);
84  for(int j = 0; j < nbound; ++j)
85  in >> t[j];
86  optical_depth_boundary.push_back(t);
87  }
88 }
89 
90 //-----------------------------------------------------------------------
114 //-----------------------------------------------------------------------
115 
117  const boost::shared_ptr<RadiativeTransferSingleWn>& High_stream_rt,
118  const HdfFile& Config_file,
119  const std::string& Lsi_group,
120  double Water_vapor_fraction_threshold)
122  low_stream_rt(Low_stream_rt), high_stream_rt(High_stream_rt),
123  wv_threshold(Water_vapor_fraction_threshold)
124 {
125  if(low_stream_rt->number_stokes() != high_stream_rt->number_stokes())
126  throw Exception("Low stream and high stream need to have the same number of stokes parameters");
127 
128  Array<std::string, 1> mg = Config_file.read_field<std::string, 1>(Lsi_group + "/main_gas");
129  for(int i = 0; i < number_spectrometer(); ++i) {
130  Array<double, 1> d = Config_file.read_field<double, 1>
131  (Lsi_group + "/optical_depth_boundary_" +
132  boost::lexical_cast<std::string>(i + 1));
133  optical_depth_boundary.push_back(std::vector<double>(d.begin(), d.end()));
134  main_gas.push_back(mg(i));
135  }
136 }
137 
138 void LsiRt::print(std::ostream& Os, bool Short_form) const
139 {
140  OstreamPad opad(Os, " ");
141  Os << "LsiRt\n";
142  Os << " ";
144  opad.strict_sync();
145  Os << "\n Water vapor fraction threshold: " << wv_threshold << "\n";
146  for(int i = 0; i < (int) optical_depth_boundary.size(); ++i) {
147  Os << " Main gas[" << i << "]: " << main_gas[i] << "\n";
148  Os << " Optical depth boundary[" << i << "]:\n";
149  Os << " (";
150  for(int j = 0; j < (int) optical_depth_boundary[i].size(); ++j) {
151  Os << optical_depth_boundary[i][j];
152  if(j != (int) optical_depth_boundary[i].size() - 1)
153  Os << ", ";
154  else
155  Os << ")\n";
156  }
157  }
158  Os << "\n High stream:\n";
159  high_stream_rt->print(opad, Short_form);
160  opad.strict_sync();
161  Os << " Low stream:\n";
162  low_stream_rt->print(opad, true);
163  opad.strict_sync();
164 }
165 
166 // See base class for description
167 blitz::Array<double, 2> LsiRt::stokes(const SpectralDomain& Spec_domain,
168  int Spec_index) const
169 
170 {
172  Logger::info() << "RT for band " << Spec_index + 1 << "\n";
173  calc_correction(Spec_domain, Spec_index, false, false);
174  for(int i = 0; i < Spec_domain.data().rows(); ++i) {
175  Array<AutoDerivative<double>, 1> err_est;
176  if(gas_opd.value()(i) > log_threshold)
177  err_est.reference(linear_interp[wv_index(i)](gas_opd(i)));
178  else
179  err_est.reference(log_linear_interp[wv_index(i)](gas_opd(i)));
180  // Intensity is scaled correction, others are additive.
181  stokes_only(i,0) = stokes_only(i,0) / (1 + err_est(0).value());
182  for(int j = 1; j < stokes_only.cols(); ++j)
183  stokes_only(i,j) = stokes_only(i,j) - err_est(j).value();
184  }
185  Logger::info() << low_stream_rt->atmosphere_ptr()->timer_info();
186  return stokes_only;
187 }
188 
189 // See base class for description
191 (const SpectralDomain& Spec_domain, int Spec_index) const
192 {
194  Logger::info() << "RT + Jac for band " << Spec_index + 1 << "\n";
195  calc_correction(Spec_domain, Spec_index, true, false);
196  for(int i = 0; i < Spec_domain.data().rows(); ++i) {
197  Array<AutoDerivative<double>, 1> err_est;
198  if(gas_opd.value()(i) > log_threshold)
199  err_est.reference(linear_interp[wv_index(i)](gas_opd(i)));
200  else
201  err_est.reference(log_linear_interp[wv_index(i)](gas_opd(i)));
202  // Intensity is scaled correction, others are additive.
203  stokes_and_jac(i,0) = stokes_and_jac(i,0) / (1 + err_est(0));
204  for(int j = 1; j < stokes_and_jac.cols(); ++j)
205  stokes_and_jac(i,j) = stokes_and_jac(i,j) - err_est(j);
206  }
207  Logger::info() << low_stream_rt->atmosphere_ptr()->timer_info();
208  return stokes_and_jac;
209 }
210 
211 //-----------------------------------------------------------------------
217 //-----------------------------------------------------------------------
218 
220 (const SpectralDomain& Spec_domain, int Spec_index) const
221 {
223  Logger::info() << "LSI corrrection only for band " << Spec_index + 1 << "\n";
224  calc_correction(Spec_domain, Spec_index, false, true);
225  ArrayAd<double, 2> res;
226  bool first = true;
227  for(int i = 0; i < Spec_domain.data().rows(); ++i) {
228  ArrayAd<double, 1> err_est;
229  if(gas_opd.value()(i) > log_threshold)
230  err_est.reference
231  (ArrayAd<double, 1>(linear_interp[wv_index(i)](gas_opd(i))));
232  else
233  err_est.reference
234  (ArrayAd<double, 1>(log_linear_interp[wv_index(i)](gas_opd(i))));
235  if(first) {
236  res.resize(Spec_domain.data().rows(), err_est.rows(), err_est.number_variable());
237  first = false;
238  }
239  res(i, Range::all()) = err_est;
240  }
241  Logger::info() << low_stream_rt->atmosphere_ptr()->timer_info();
242  return res;
243 }
244 
245 //-----------------------------------------------------------------------
264 //-----------------------------------------------------------------------
265 
266 void LsiRt::calc_correction(const SpectralDomain& Spec_domain,
267  int Spec_index, bool Calc_jacobian,
268  bool Skip_stokes_calc) const
269 {
270  Range ra(Range::all());
271  Array<double, 1> wn(Spec_domain.wavenumber());
273  const RtAtmosphere& atm = *low_stream_rt->atmosphere_ptr();
274  const std::vector<double>& odb = optical_depth_boundary[Spec_index];
275 
276 //-----------------------------------------------------------------------
277 // Go through all the wave numbers and collect the atmosphere
278 // properties, bin them by the optical_depth_boundary bins, and
279 // average them.
280 //
281 // We collect information for "water vapor wavenumbers" and
282 // for regular wavenumbers separately. By convention, there are two of
283 // each kind of bin, and index "0" is used for water and index "1"
284 // used for normal wave numbers.
285 //
286 // There are a number of variables created here:
287 //
288 // atm_sum -
289 // Sum of all the intermediate variables (e.g., taug, taur,
290 // taua_i). We use this to create the average value used in the
291 // correction calculation. This is indexed by wv_index and then
292 // optical depth range.
293 // log_opd_sum -
294 // Sum of the log(gas_opd). This is indexed by wv_index and then
295 // optical depth range.
296 // cnt -
297 // Number of entries added to atm_sum and log_opd_sum. May be 0 if
298 // we don't have any data for a particular bin.
299 // wv_index -
300 // 0 if this is a water vapor wave number, 1 otherwise. This is the
301 // same size as wn.
302 // gas_opd
303 // The optical depth for the wavenumber. This is the sum of the
304 // water vapor gas column optical depth and the main gas column
305 // optical depth.
306 //-----------------------------------------------------------------------
307 
308 
309 //-----------------------------------------------------------------------
310 // Initialize each of the variables mentioned above. We start
311 // everything out at 0.
312 //
313 // Note that "BinMap" is a class that handles binning. If "b" is a bin
314 // map, then b[x] return the bin that x falls into.
315 //-----------------------------------------------------------------------
316 
317  // Get first intermediate variable so we can figure out the size of
318  // the intermediate variables (e.g., number_layer x number
319  // variables). The initialize this to all 0s.
320 
322  init_atm_sum_value(atm.intermediate_variable(wn(0), Spec_index).copy());
323  init_atm_sum_value = 0;
324  // Need at least 1 or else FM only mode doesn't work
325  int numvar = std::max(init_atm_sum_value.number_variable(), 1);
326  std::vector<BinMap<int> > cnt;
327  std::vector<BinMap<AutoDerivative<double> > > log_opd_sum;
328  std::vector<BinMap<ArrayAd<double, 2> > > atm_sum;
329  for(int i = 0; i < 2; ++i) {
330  cnt.push_back(BinMap<int>(odb.begin(), odb.end(), 0));
331  log_opd_sum.push_back
332  (BinMap<AutoDerivative<double> >(odb.begin(), odb.end(),
333  AutoDerivative<double>(0.0)));
334  atm_sum.push_back(BinMap<ArrayAd<double, 2> > (odb.begin(), odb.end(),
335  boost::bind(&ArrayAd<double, 2>::copy, &init_atm_sum_value)));
336  }
337 
338  gas_opd.resize(wn.rows(), numvar);
339  wv_index.resize(wn.rows());
340  if(!Skip_stokes_calc) {
341  if(Calc_jacobian)
342  stokes_and_jac.resize(wn.rows(), number_stokes(), numvar);
343  else
344  stokes_only.resize(wn.rows(), number_stokes());
345  }
346 
347 //-----------------------------------------------------------------------
348 // Now, go through and collect the sums of all the things we are
349 // averaging.
350 //-----------------------------------------------------------------------
351 
352  for(int i = 0; i < wn.rows(); ++i) {
353  // We want to move this to metadata
354  AutoDerivative<double> maingas_opd =
355  atm.column_optical_depth(wn(i), Spec_index, main_gas[Spec_index]);
356  AutoDerivative<double> wv_opd =
357  atm.column_optical_depth(wn(i), Spec_index, "H2O");
358  gas_opd(i) = maingas_opd + wv_opd;
359  // Algorithm can't actually handle gas_opd == 0. We get this
360  // sometimes if we go past the end of the ABSCO tables, so just
361  // pick a small value if we are zero.
362  if(gas_opd(i).value() < 1e-10)
363  gas_opd(i) = 1e-10;
364  wv_index(i) = (wv_opd / gas_opd(i) >= wv_threshold ? 0 : 1);
365  ArrayAd<double, 2> atmv(atm.intermediate_variable(wn(i), Spec_index));
366  ++cnt[wv_index(i)][gas_opd.value()(i)];
367  log_opd_sum[wv_index(i)][gas_opd.value()(i)] += log(gas_opd(i));
368  atm_sum[wv_index(i)][gas_opd.value()(i)].value() += atmv.value();
369  atm_sum[wv_index(i)][gas_opd.value()(i)].jacobian() += atmv.jacobian();
370 
371  // Since we are already calculating the atmosphere parameters, go
372  // ahead and collect low streams data if requested.
373  if(!Skip_stokes_calc) {
374  if(Calc_jacobian)
375  stokes_and_jac(i, ra) = low_stream_rt->stokes_and_jacobian_single_wn
376  (wn(i), Spec_index);
377  else
378  stokes_only(i, ra) = low_stream_rt->stokes_single_wn
379  (wn(i), Spec_index);
380  }
381 
382  // Update progress meter in log file, if we are using it.
383  if(disp)
384  *disp += 1;
385  }
386 
387 //-----------------------------------------------------------------------
388 // Now average them, and use to calculate the high and low stream
389 // values using these averaged values. We then use this to create
390 // the corrections, and then class to handle interpolating these
391 // corrections between the gas_opd_avg values.
392 //
393 // We actually need two linear interpolations, one that interpolates
394 // based on gas_opd and one that interpolates based on log(gas_opd).
395 // We'll use these two separate interpolations when we do the
396 // correction for each wavenumber. Small gas_opd values get log-linear
397 // interpolated, and large values get linearly interpolated.
398 //-----------------------------------------------------------------------
399 
400  // We do the RT on the middle wavenumber when calculating the correction.
401  double wn_mid = (max(wn) + min(wn)) / 2;
402  double wn_mid_closest = wn(0);
403  double wn_dist = fabs(wn_mid_closest - wn_mid);
404  for(int i = 0; i < wn.rows(); ++i)
405  if(fabs(wn(i) - wn_mid) < wn_dist) {
406  wn_dist = fabs(wn(i) - wn_mid);
407  wn_mid_closest = wn(i);
408  }
409  linear_interp.clear();
410  log_linear_interp.clear();
411  for(int i = 0; i < 2; ++i) {
412  std::vector<int> cnt_w = cnt[i].value();
413  std::vector<AutoDerivative<double> > log_opd_sum_w = log_opd_sum[i].value();
414  std::vector<ArrayAd<double, 2> > atm_avg_w = atm_sum[i].value();
415  std::vector<AutoDerivative<double> > gas_opd_avg;
416  std::vector<Array<AutoDerivative<double>, 1> > err_est;
417  for(int j = 0; j < (int) cnt_w.size(); ++j) {
418  // We only do a correction for a bin that has data.
419  if(cnt_w[j] != 0) {
420  gas_opd_avg.push_back(exp(log_opd_sum_w[j] / cnt_w[j]));
421 
422  // Get average atmosphere values
423  atm_avg_w[j].value() /= cnt_w[j];
424  atm_avg_w[j].jacobian() /= cnt_w[j];
425 
426  // And use to get high and low stream values.
427  Array<AutoDerivative<double>, 1> low =
428  low_stream_rt->stokes_and_jacobian_single_wn
429  (wn_mid_closest, Spec_index, atm_avg_w[j]).to_array();
430  Array<AutoDerivative<double>, 1> high =
431  high_stream_rt->stokes_and_jacobian_single_wn
432  (wn_mid_closest, Spec_index, atm_avg_w[j]).to_array();
433  // Use high and low to calculate correction.
434  Array<AutoDerivative<double>, 1> c(low.rows());
435  // We do a scaled correction for the first stokes coefficient
436  // which is the intensity. For the others, we do just a
437  // difference. Note sure exactly how much difference this
438  // makes, but it is what the original Fortran did. Note that
439  // you do *not* want to do a scaled correction for the Q and U
440  // stokes parameters because these can be negative so
441  // 1/1+err_est can produce infinity if err_est is -1 (or a
442  // large number of it is close).
443  c(0) = (fabs(high(0).value()) > 1e-15 ?
444  (low(0) - high(0)) / high(0) : 0);
445  for(int i =1; i < c.rows(); ++i)
446  c(i) = low(i) - high(i);
447  err_est.push_back(c);
448  }
449  }
450  // Now use corrections values to create Linear and Log-linear
451  // interpolaters.
452  linear_interp.push_back
453  (linear_interp_type(gas_opd_avg.begin(), gas_opd_avg.end(),
454  err_est.begin(),
456  log_linear_interp.push_back
457  (log_linear_interp_type(gas_opd_avg.begin(), gas_opd_avg.end(),
458  err_est.begin(),
460  }
461 }
462 
virtual void print(std::ostream &Os, bool Short_form=false) const
Print to stream.
Definition: lsi_rt.cc:138
static LogHelper info()
Definition: logger.h:109
static AccumulatedTimer timer
virtual blitz::Array< double, 2 > stokes(const SpectralDomain &Spec_domain, int Spec_index) const
Calculate stokes vector for the given set of wavenumbers/wavelengths.
Definition: lsi_rt.cc:167
virtual int number_stokes() const
Number of stokes parameters we will return in stokes and stokes_and_jacobian.
Definition: lsi_rt.h:32
LsiRt(const boost::shared_ptr< RadiativeTransferSingleWn > &Low_stream_rt, const boost::shared_ptr< RadiativeTransferSingleWn > &High_stream_rt, const std::string &Lsi_fname, double Water_vapor_fraction_threshold=0.8)
Create a object that uses the low stream RT + LSI corrections based on the low and high stream RT...
Definition: lsi_rt.cc:63
int cols() const
Definition: array_ad.h:369
virtual int number_spectrometer() const
Number of spectrometer we have.
This does a Low Stream Interpolator correction to another RadiativeTransfer object.
Definition: lsi_rt.h:20
This class is responsible for setting up the atmosphere and ground information needed to run the Radi...
Definition: rt_atmosphere.h:51
virtual ArrayAd< double, 2 > correction_only(const SpectralDomain &Spec_domain, int Spec_index) const
Normally we calculate both the low streams stokes parameters, the LSI correction, and we apply them...
Definition: lsi_rt.cc:220
This is a filtering stream that adds a pad to the front of every line written out.
Definition: ostream_pad.h:32
blitz::Array< T, D > read_field(const std::string &Dataname) const
Read a given field.
Definition: hdf_file.h:564
For different instruments, it is more natural to either work with wavenumbers (e.g., GOSAT) or wavelength (e.g., OCO).
This class takes a set of points and values, and linearly interpolates between those values...
This is the base of the exception hierarchy for Full Physics code.
Definition: fp_exception.h:16
blitz::Array< T, 2 > jacobian(const blitz::Array< AutoDerivative< T >, 1 > &Ad)
Utility function to extract the Jacobian as a separate matrix from an array of AutoDerivative.
Wrapper around LinearInterpolate that uses log(x) rather than x in interpolating. ...
#define REGISTER_LUA_DERIVED_CLASS(X, Y)
Definition: register_lua.h:136
const double log_threshold
Definition: lsi_rt.cc:36
This is a RadiativeTransfer that supplies an interface that can be called for a single wavenumber...
This class reads and writes a HDF5 file.
Definition: hdf_file.h:39
virtual AutoDerivative< double > column_optical_depth(double wn, int spec_index, const std::string &Gas_name) const =0
Total column optical depth for the given gas.
Apply value function to a blitz array.
const blitz::Array< T, D > & value() const
Definition: array_ad.h:306
virtual void print(std::ostream &Os, bool Short_form=false) const
Print to stream.
void resize(const blitz::TinyVector< int, D > &Shape, int nvar)
Definition: array_ad.h:177
This runs a Radiative Transfer code to determine the reflectance for a given set of wavelengths...
FunctionTimer function_timer(bool Auto_log=false) const
Function timer.
This is a map that takes and index and returns a value.
Definition: bin_map.h:11
It can be convenient to allow comments in a text data file that are ignored by the program reading it...
Definition: ifstream_cs.h:17
ArrayAd< T, D > copy() const
Definition: array_ad.h:374
virtual ArrayAd< double, 2 > intermediate_variable(double wn, int spec_index) const =0
This gives the values of the intermediate variables and the Jacobian with respect to the state vector...
boost::shared_ptr< boost::progress_display > progress_display(const blitz::Array< double, 1 > &wn) const
Helper routine, creates a progress meter.
int number_variable() const
Definition: array_ad.h:376
void reference(const ArrayAd< T, D > &V)
Definition: array_ad.h:372
blitz::Array< double, 1 > wavenumber(const Unit &Units=units::inv_cm) const
Return data as wavenumbers.
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"
virtual ArrayAd< double, 2 > stokes_and_jacobian(const SpectralDomain &Spec_domain, int Spec_index) const
Calculate stokes vector for the given set of wavenumbers/wavelengths.
Definition: lsi_rt.cc:191
Helper class for AccumulatedTimer.
const boost::shared_ptr< StokesCoefficient > & stokes_coefficient() const
Stokes coefficients used to go from Stokes vector to scalar reflectance.
double value(const FullPhysics::AutoDerivative< double > &Ad)
For GOSAT and OCO, we have a set of stokes coefficients to go from Stokes vector to radiation...
const blitz::Array< double, 1 > & data() const
Return data.
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