ReFRACtor
absorber_absco.cc
Go to the documentation of this file.
1 #include "absorber_absco.h"
2 #include "fp_exception.h"
3 #include "linear_algebra.h"
4 #include "old_constant.h"
5 #include "ostream_pad.h"
6 #include "absco.h"
7 #include <boost/lexical_cast.hpp>
8 #include <boost/bind.hpp>
9 using namespace FullPhysics;
10 using namespace blitz;
11 inline double sqr(double x) {return x * x;}
12 
13 #ifdef HAVE_LUA
14 #include "register_lua.h"
16 .def(luabind::constructor<const std::vector<boost::shared_ptr<AbsorberVmr> >,
19  const std::vector<boost::shared_ptr<Altitude> >&,
20  const std::vector<boost::shared_ptr<GasAbsorption> >&,
22 .def(luabind::constructor<const std::vector<boost::shared_ptr<AbsorberVmr> >,
25  const std::vector<boost::shared_ptr<Altitude> >&,
26  const std::vector<boost::shared_ptr<GasAbsorption> >&,
28  int>())
30 #endif
31 
32 //----------------------------------------------------------------
38 //----------------------------------------------------------------
39 
41 (int Spec_index, int Species_index, const DoubleWithUnit& P)
42 const
43 {
44  return vmr_func(Species_index, P) * c->avogadro_constant() /
45  (gravity_func(Spec_index, P) *
46  (1 + h2o_vmr_func(P) * c->molar_weight_water() /
47  c->molar_weight_dry_air()) * c->molar_weight_dry_air());
48 }
49 
50 //----------------------------------------------------------------
53 //----------------------------------------------------------------
54 
56 (double wn, double P, int Spec_index, int Species_index) const
57 {
58  range_check(Species_index, 0, number_species());
59  DoubleWithUnit punit(P, "Pa");
60  double v1 =
61  integrand_independent_wn(Spec_index, Species_index,
62  punit).convert(Unit("cm^-2 / Pa")).value.value();
63  AutoDerivativeWithUnit<double> tder = temp_func(punit);
64  DoubleWithUnit t(tder.value.value(), tder.units);
65  AutoDerivativeWithUnit<double> bvmrder = h2o_vmr_func(punit);
66  DoubleWithUnit bvmr(bvmrder.value.value(), bvmrder.units);
67  if(gas_absorption[Species_index]->have_data(wn)) {
68  DoubleWithUnit cross_sec = gas_absorption[Species_index]->absorption_cross_section(wn, punit, t, bvmr);
69  return v1 * cross_sec.value;
70  } else
71  return 0.0;
72 }
73 
74 //----------------------------------------------------------------
81 //----------------------------------------------------------------
82 
84 double wn, int Spec_index, int Species_index, int Layer_index,
85 double eps_abs, double eps_rel) const
86 {
87  range_check(Spec_index, 0, number_spectrometer());
88  range_check(Species_index, 0, number_species());
89  range_check(Layer_index, 0, press->number_layer());
90  if(!gas_absorption[Species_index]->have_data(wn))
91  return 0.0;
92  boost::function<double (double)> f;
93  f = boost::bind(&AbsorberAbsco::integrand, this, wn, _1, Spec_index,
94  Species_index);
95  ArrayAdWithUnit<double, 1> pgrid(press->pressure_grid().convert(units::Pa));
96  std::vector<double> bp;
97  Array<double, 1> imp =
98  temp->important_pressure_level(). convert(units::Pa).value;
99  for(int i = 0; i < imp.rows(); ++i)
100  bp.push_back(imp(i));
101  return intg.integrate(f, pgrid.value.value()(Layer_index),
102  pgrid.value.value()(Layer_index + 1),
103  bp,
104  eps_abs, eps_rel);
105 }
106 
107 //----------------------------------------------------------------
110 //----------------------------------------------------------------
111 
112 blitz::Array<double, 2>
114 (double wn, int Spec_index, double eps_abs, double eps_rel) const
115 {
116  blitz::Array<double, 2> res(press->number_layer(), number_species());
117  for(int i = 0; i < res.rows(); ++i)
118  for(int j = 0; j < res.cols(); ++j)
119  res(i, j) = optical_depth_each_layer_direct_integrate
120  (wn, Spec_index, j, i, eps_abs, eps_rel);
121  return res;
122 }
123 
124 //----------------------------------------------------------------
146 //----------------------------------------------------------------
147 
148 void AbsorberAbsco::create_sublayer() const
149 {
150  firstIndex i1;
151 
152  // ****** IMPORTANT **********
153  // Note that there is an implicit assumption in tau_gas_der that the end
154  // point of the range are at the edge of the integral, so for a
155  // layer going from p1 to p2 the edges are p1 and p2. This is the
156  // case for Simpson's rule. But if you change this, you may need to
157  // modify tau_gas_der
158 
159 
160  pgrid.reference(press->pressure_grid().convert(units::Pa));
161 
162  // Note that although the pressure grid itself depends on the state vector,
163  // the grid psub we create here does *not*. This is because this just the
164  // points we happened to pick for doing the integral on. The
165  // dependency on pressure actually comes in when we calculate the
166  // definite integral integral p1 to p2 (integrand) dp, where we have
167  // the derivative with respect to p1 and p2.
168 
169  int npoint = nsub / 2; // We'll double this when we add
170  // midpoints for Simpsons rule.
171  Array<double, 1> sublay_fac(npoint);
172  sublay_fac = (i1 + 1.0) / (npoint);
173 
174  // "Important" pressure levels that should be included in the
175  // integral.
176  Array<double, 1> pimp_arr =
177  temp->important_pressure_level().convert(units::Pa).value;
178  // Might not be sorted, so stick into a list that we will sort.
179  std::list<double> pimp;
180  for(int i = 0; i < pimp_arr.rows(); ++i)
181  pimp.push_back(pimp_arr(i));
182  pimp.sort();
183 
184  // We set up the pressure grid for the Simpsons rule. For integrating
185  // from p1 to p2, we have the points p1, p1 + spacing, ... p2. Note
186  // that we reuse p2 in the next interval, because to integrate from
187  // p2 to p3 we have p2, p2 + spacing, ... p3. This is taken care of
188  // by having overlapping layer_range.
189  layer_range.clear();
190  std::vector<double> psub_vec;
191  psub_vec.push_back(pgrid.value.value()(0));
192  // Drop all "important" pressure point that come before the first
193  // pressure level.
194  while(pimp.size() > 0 && pimp.front() <= psub_vec.front())
195  pimp.pop_front();
196 
197  // The logic here is a little complicated. In words we do the
198  // following:
199  // 1. Go through the nsub / 2 equally spaced points between p1 and
200  // p2.
201  // 2. When we get a point, first check if there is any "important"
202  // points that come before it. If there are, then add the important
203  // point + the midpoint used by simpsons rule. So 2 points get
204  // added, not just one. The midpoint is the f((a+b)/2) point
205  // mentioned above in Simpsons rule.
206  // 3. After dealing with the important points, if any, add in our
207  // equally spaced point, along with the midpoint (so again 2
208  // points).
209  // At the same time we are doing this, we keep track of what points
210  // fall in a particular layer. We create a Range object that covers
211  // this, and save it in layer_range.
212 
213  int start = 0;
214  for(int i = 0; i < press->number_layer(); ++i) {
215  for(int j = 0; j < sublay_fac.rows(); ++j) {
216  double pv = pgrid.value.value()(i) * (1 - sublay_fac(j)) +
217  pgrid.value.value()(i + 1) * sublay_fac(j);
218  while(pimp.size() > 0 && pimp.front() < pv) {
219  double midpoint = (psub_vec.back() + pimp.front()) / 2.0;
220  psub_vec.push_back(midpoint);
221  psub_vec.push_back(pimp.front());
222  pimp.pop_front();
223  }
224  // Unlikely, but possible case where a important pressure is
225  // exactly the same as pv. Drop the point if this is the case.
226  while(pimp.size() > 0 && pimp.front() == pv)
227  pimp.pop_front();
228  // Add midpoint, and then the point
229  double midpoint = (psub_vec.back() + pv) / 2.0;
230  psub_vec.push_back(midpoint);
231  psub_vec.push_back(pv);
232  }
233  int end = ((int) psub_vec.size()) - 1;
234  layer_range.push_back(Range(start, end));
235  // Start next layer with the last pressure in the current layer.
236  start = end;
237  }
238  // Move points stored in a std::vector into a Array. This is the
239  // same data, just in a different structure.
240  psub.units = pgrid.units;
241  psub.value.resize((int) psub_vec.size());
242  for(int i = 0; i < psub.rows(); ++i)
243  psub.value(i) = psub_vec[i];
244 
245  // Now go through and figure out the weights for all our
246  // points. This is just the weights for Simpson's rule, but with the
247  // complication that the spacing between regions we apply Simpson's
248  // rule may change.
249  weight.resize(layer_range.size());
250  for(int i = 0; i < (int) weight.size(); ++i) {
251  Array<double, 1> play(psub.value(layer_range[i]));
252  weight[i].value.resize(play.shape());
253  weight[i].units = units::Pa;
254  weight[i].value = 0;
255  for(int j = 0; j < play.rows() - 1; j += 2) {
256  double delta = play(j + 2) - play(j);
257  weight[i].value(j) += delta / 6.0;
258  weight[i].value(j + 1) += 4 * delta / 6.0;
259  weight[i].value(j + 2) += delta / 6.0;
260  }
261  }
262 
263  // For each layer, store the nonzero columns of p1 and p2.
264  pressure_nonzero_column.clear();
265  p1_grad.clear();
266  p2_grad.clear();
267  for(int i = 0; i < number_layer(); ++i) {
268  Array<double, 1> p1g = pgrid.value(i + 1).gradient();
269  Array<double, 1> p2g = pgrid.value(i).gradient();
270  std::vector<int> nzero_col;
271  for(int j = 0; j < p1g.rows(); ++j)
272  if(abs(p1g(j)) > 1e-20 ||
273  abs(p2g(j)) > 1e-20)
274  nzero_col.push_back(j);
275  Array<double, 1> p1g_sparse((int) nzero_col.size());
276  Array<double, 1> p2g_sparse(p1g_sparse.shape());
277  int k = 0;
278  BOOST_FOREACH(int j, nzero_col) {
279  p1g_sparse(k) = p1g(j);
280  p2g_sparse(k) = p2g(j);
281  ++k;
282  }
283  pressure_nonzero_column.push_back(nzero_col);
284  p1_grad.push_back(p1g_sparse);
285  p2_grad.push_back(p2g_sparse);
286  }
287 }
288 
289 //----------------------------------------------------------------
294 //----------------------------------------------------------------
295 
296 void AbsorberAbsco::fill_tau_gas_cache() const
297 {
298  if(!cache_tau_gas_stale)
299  return;
300 
301  firstIndex i1; secondIndex i2; thirdIndex i3;
302  Range ra(Range::all());
303 
304  //----------------------------------------------------------------
305  // Fill in pressure, temperature, gravity and mixing ratio for
306  // the sublayers.
307  //----------------------------------------------------------------
308 
309  create_sublayer();
310  Array<AutoDerivative<double>, 3>
311  integrand_independent_wn_sub_t(number_spectrometer(), number_species(),
312  psub.rows());
313  for(int i = 0; i < integrand_independent_wn_sub_t.rows(); ++i)
314  for(int j = 0; j < integrand_independent_wn_sub_t.cols(); ++j)
315  for(int k = 0; k < integrand_independent_wn_sub_t.depth(); ++k)
316  integrand_independent_wn_sub_t(i, j, k) =
317  integrand_independent_wn(i, j, psub(k)).
318  convert(Unit("cm^-2 / Pa")).value;
319  integrand_independent_wn_sub.units = Unit("cm^-2 / Pa");
320  integrand_independent_wn_sub.value.
321  reference(ArrayAd<double, 3>(integrand_independent_wn_sub_t));
322 
323  // The integrand tends to be fairly sparse. Set up the nonzero
324  // columns and compress jacobian for each layer.
325  integrand_nonzero_column.clear();
326  integrand_jac.clear();
327  for(int i = 0; i < number_layer(); ++i) {
328  Range r = layer_range[i];
329  ArrayAd<double, 3> piv(integrand_independent_wn_sub.value(ra, ra,r));
330  Array<double, 4> jac = piv.jacobian();
331  std::vector<int> nz;
332  for(int j = 0; j < piv.number_variable(); ++j) {
333  double mv = max(abs(jac(ra, ra, ra, j)));
334  if(mv > 1e-20)
335  nz.push_back(j);
336  }
337  int k = 0;
338  Array<double, 4> ijac(jac.rows(), jac.cols(), jac.depth(), (int) nz.size());
339  BOOST_FOREACH(int m, nz) {
340  ijac(ra, ra, ra, k) = jac(ra,ra,ra,m);
341  ++k;
342  }
343  integrand_nonzero_column.push_back(nz);
344  integrand_jac.push_back(ijac);
345  }
346 
347  // Set up Absco interpolator for our sublayers
348  Array<AutoDerivative<double>, 1> tsub_t(psub.rows());
349  Array<AutoDerivative<double>, 1> h2o_vmr_t(psub.rows());
350  for(int i = 0; i < psub.rows(); ++i) {
351  tsub_t(i) = temp_func(psub(i)).convert(units::K).value;
352  h2o_vmr_t(i) = h2o_vmr_func(psub(i)).value;
353  }
355  ArrayAdWithUnit<double, 1> h2o_vmr(ArrayAd<double,1>(h2o_vmr_t),
357  // Absco derivatives tend to be fairly sparse. They depend only on
358  // tsub and h2o_vmr, so we can go through and see which columns are
359  // nonzero.
360  absco_nonzero_column.clear();
361  for(int i = 0; i < std::max(tsub.value.number_variable(),
362  h2o_vmr.value.number_variable());
363  ++i) {
364  double mv1;
365  double mv2;
366  if(tsub.value.is_constant())
367  mv1 = 0;
368  else
369  mv1 = max(abs(tsub.value.jacobian()(ra, i)));
370  if(h2o_vmr.value.is_constant())
371  mv2 = 0;
372  else
373  mv2 = max(abs(h2o_vmr.value.jacobian()(ra, i)));
374  if(mv1 > 1e-20 || mv2 > 1e-20)
375  absco_nonzero_column.push_back(i);
376  }
377  Array<double, 2> tsub_sjac(tsub.value.rows(),
378  (int) absco_nonzero_column.size());
379  Array<double, 2> h2o_vmr_sjac(tsub_sjac.shape());
380  int k = 0;
381  tsub_sjac = 0;
382  h2o_vmr_sjac = 0;
383  BOOST_FOREACH(int m, absco_nonzero_column) {
384  if(!tsub.value.is_constant())
385  tsub_sjac(ra, k) = tsub.value.jacobian()(ra, m);
386  if(!h2o_vmr.value.is_constant())
387  h2o_vmr_sjac(ra, k) = h2o_vmr.value.jacobian()(ra, m);
388  ++k;
389  }
390 
392  tsub_sparse(ArrayAd<double,1>(tsub.value.value(), tsub_sjac), units::K);
394  h2o_vmr_sparse(ArrayAd<double,1>(h2o_vmr.value.value(), h2o_vmr_sjac),
396 
397  absco_interp.resize(gas_absorption.size());
398  for(int i = 0; i < (int) absco_interp.size(); ++i) {
400  boost::dynamic_pointer_cast<Absco>(gas_absorption[i]);
401  if(!a)
402  throw Exception("AbsorberAbsco requires Absco, not just any GasAbsorption object");
403  absco_interp[i].reset(new AbscoInterpolator(a, psub, tsub_sparse,
404  h2o_vmr_sparse));
405  }
406 
407  taug.resize((int) layer_range.size(), number_species(),
408  std::max(integrand_independent_wn_sub.number_variable(),
409  tsub.number_variable()));
410  taug.jacobian() = 0;
411  cache_tau_gas_stale = false;
412 }
413 
414 //----------------------------------------------------------------
419 //----------------------------------------------------------------
420 
421 Array<double, 2>
422 AbsorberAbsco::tau_gas_nder(double wn, int spec_index) const
423 {
424  firstIndex i1; secondIndex i2; thirdIndex i3;
425  Range ra(Range::all());
426  fill_tau_gas_cache(); // Calculate part independent of wn if
427  // needed.
428 
429  //----------------------------------------------------------------
430  // Calculate tau for each sublayer, and then combine to generate
431  // taug.
432  //
433  // Note that this calculation is a bottle neck, since it gets called
434  // for every high resolution wavelength. To speed the calculation up,
435  // we have explicit calculation of the Jacobian, rather than
436  // automatically doing this with AutoDerivative. This code is less
437  // clear, but faster.
438  //
439  // You can time this by using the unit test
440  // atmosphere_oco/optical_depth_timing (or of course by just
441  // profiling a l2_fp run).
442  //----------------------------------------------------------------
443 
444  for(int i = 0; i < taug.cols(); ++i)
445  if(gas_absorption[i]->have_data(wn)) {
446  Array<double, 1> abcsub =
447  absco_interp[i]->absorption_cross_section_noderiv(wn);
448  for(int j = 0; j < taug.rows(); ++j) {
449  Range r = layer_range[j];
450  Array<double, 1> av(abcsub(r));
451  Array<double, 1> piv(integrand_independent_wn_sub.value.value()(spec_index,i,r));
452  taug.value()(j, i) = sum(av * piv * weight[j].value);
453  }
454  } else
455  for(int j =0; j < taug.rows(); ++j) {
456  taug.value()(j, i) = 0;
457  }
458  return taug.value();
459 }
460 
461 //----------------------------------------------------------------
466 //----------------------------------------------------------------
467 
469 AbsorberAbsco::tau_gas_der(double wn, int spec_index) const
470 {
471  firstIndex i1; secondIndex i2; thirdIndex i3;
472  Range ra(Range::all());
473  fill_tau_gas_cache(); // Calculate part independent of wn if
474  // needed.
475 
476  //----------------------------------------------------------------
477  // Calculate tau for each sublayer, and then combine to generate
478  // taug.
479  //
480  // Note that this calculation is a bottle neck, since it gets called
481  // for every high resolution wavelength. To speed the calculation up,
482  // we have explicit calculation of the Jacobian, rather than
483  // automatically doing this with AutoDerivative. This code is less
484  // clear, but faster.
485  //
486  // You can time this by using the unit test
487  // atmosphere_oco/optical_depth_timing (or of course by just
488  // profiling a l2_fp run).
489  //----------------------------------------------------------------
490 
491  // Temporary, if this works we can cache this
492  ArrayAdWithUnit<double, 1> pgrid(press->pressure_grid().convert(Unit("Pa")));
493 
494  for(int i = 0; i < taug.cols(); ++i)
495  if(gas_absorption[i]->have_data(wn)) {
496  ArrayAd<double, 1> abcsub =
497  absco_interp[i]->absorption_cross_section_deriv(wn);
498  for(int j = 0; j < taug.rows(); ++j) {
499  Range r = layer_range[j];
500  ArrayAd<double, 1> av(abcsub(r));
501  Array<double, 1> piv(integrand_independent_wn_sub.value(spec_index,i,r).value());
502  Array<double, 1> jacv(taug.jacobian()(j, i, Range::all()));
503  taug.value()(j, i) = sum(av.value() * piv * weight[j].value);
504 
505  // Zero out taug so we can add in the jacobian. Note that the
506  // rest of the Jacobian is already zero from when we sized it.
507  BOOST_FOREACH(int m, pressure_nonzero_column[j])
508  jacv(m) = 0;
509 
510  BOOST_FOREACH(int m, absco_nonzero_column)
511  jacv(m) = 0;
512 
513  // Can skip integrand_nonzero_column because we do that first,
514  // and just assign to it.
515 
516  // BOOST_FOREACH(int m, integrand_nonzero_column[j])
517  // jacv(m) = 0;
518 
519  int k = 0;
520  BOOST_FOREACH(int m, integrand_nonzero_column[j]) {
521  Array<double, 1> intjac(integrand_jac[j](spec_index, i, ra, k));
522  jacv(m) = sum(av.value() * intjac * weight[j].value);
523  ++k;
524  }
525 
526  k = 0;
527  BOOST_FOREACH(int m, absco_nonzero_column) {
528  Array<double, 1> avjac(av.jacobian()(ra, k));
529  jacv(m) += sum(avjac * piv * weight[j].value);
530  ++k;
531  }
532 
533  // If p1 and p2 are the pressures at the edge of the layer,
534  // then we want to calculate (d tau / dp1) * (dp1 / dstate) +
535  // d (tau / dp2) * dp2 / dstate. The fundamental theorem of
536  // calculus then gives us integrand(p1) * dp 1 /state -
537  // integrand(p2) * dp 1 / state
538 
539  // Note assumption here that ends of range are for p1 and
540  // p2. This is true for Simpsons rule, but if you change
541  // create_sublayer this may need to be modified.
542  int i1 = av.rows() - 1;
543  int i2 = 0;
544  k = 0;
545  BOOST_FOREACH(int m, pressure_nonzero_column[j]) {
546  jacv(m) += av.value()(i1) * piv(i1) * p1_grad[j](k) -
547  av.value()(i2) * piv(i2) * p2_grad[j](k);
548  ++k;
549  }
550  }
551  } else
552  for(int j =0; j < taug.rows(); ++j) {
553  taug.value()(j, i) = 0;
554  taug.jacobian()(j, i, Range::all()) = 0;
555  }
556  return taug;
557 }
558 
559 //-----------------------------------------------------------------------
561 //-----------------------------------------------------------------------
562 
564 (const std::vector<boost::shared_ptr<AbsorberVmr> > Vmr,
565  const boost::shared_ptr<Pressure>& Press,
566  const boost::shared_ptr<Temperature>& Temp,
567  const std::vector<boost::shared_ptr<Altitude> >& Alt,
568  const std::vector<boost::shared_ptr<GasAbsorption> >& Gas_absorption,
570  int Nsub)
571 : press(Press), temp(Temp), alt(Alt), vmr(Vmr),
572  gas_absorption(Gas_absorption), c(C), nsub(Nsub),
573  cache_tau_gas_stale(true)
574 {
575  Range ra(Range::all());
576  press->add_observer(*this);
577  temp->add_observer(*this);
578  BOOST_FOREACH(boost::shared_ptr<Altitude>& a, alt)
579  a->add_observer(*this);
580  BOOST_FOREACH(boost::shared_ptr<AbsorberVmr>& a, vmr)
581  a->add_observer(*this);
582  h2o_index = gas_index("H2O");
583  // Right now, we only support H2O as a broadener.
584  BOOST_FOREACH(boost::shared_ptr<GasAbsorption>& ga, gas_absorption)
585  if(ga->broadener_name() != "" &&
586  ga->broadener_name() != "h2o") {
587  Exception e;
588  e << "Right now, we only support H2O as a broadener. "
589  << "The GasAbsorption has a broadener of \""
590  << ga->broadener_name() << "\"";
591  throw e;
592  }
593 }
594 
595 // See base class for description
597 AbsorberAbsco::optical_depth_each_layer(double wn, int spec_index) const
598 {
599  FunctionTimer ft(timer.function_timer());
600  return tau_gas_der(wn,spec_index);
601 }
602 
603 Array<double, 2>
604 AbsorberAbsco::optical_depth_each_layer_nder(double wn, int spec_index) const
605 {
606  FunctionTimer ft(timer.function_timer());
607  return tau_gas_nder(wn,spec_index);
608 }
609 
610 // See base class for description
612 {
613  boost::shared_ptr<Pressure> pressure_clone = press->clone();
614  boost::shared_ptr<Temperature> temperature_clone =
615  temp->clone(pressure_clone);
616  std::vector<boost::shared_ptr<Altitude> > alt_clone;
617  BOOST_FOREACH(const boost::shared_ptr<Altitude>& a, alt)
618  alt_clone.push_back(a->clone(pressure_clone, temperature_clone));
619  return clone(pressure_clone, temperature_clone, alt_clone);
620 }
621 
622 // See base class for description
625  const boost::shared_ptr<Temperature>& Temp,
626  const std::vector<boost::shared_ptr<Altitude> >& Alt) const
627 {
628  std::vector<boost::shared_ptr<AbsorberVmr> > vmr_clone;
629  BOOST_FOREACH(const boost::shared_ptr<AbsorberVmr>& a, vmr)
630  vmr_clone.push_back(a->clone(Press));
632  (new AbsorberAbsco(vmr_clone, Press, Temp, Alt,
633  gas_absorption, c, nsub));
634  return res;
635 }
636 
637 //-----------------------------------------------------------------------
639 //-----------------------------------------------------------------------
640 
642 {
643  ArrayAdWithUnit<double, 1> pgrid(press->pressure_grid());
645  spec_hum.units = units::dimensionless;
646  Array<AutoDerivative<double>, 1> spec_hum_t(pgrid.rows() - 1);
647  DoubleWithUnit epsilon = c->molar_weight_water() / c->molar_weight_dry_air();
648  for(int i = 0; i < spec_hum_t.rows(); ++i) {
649  AutoDerivativeWithUnit<double> dp = pgrid(i + 1) - pgrid(i);
650  AutoDerivativeWithUnit<double> play = pgrid(i) + dp / 2;
652  ((h2o_index < 0 ? 0 : vmr[h2o_index]->volume_mixing_ratio(play.value)),
654  spec_hum_t(i) = (h2o_lay / (1.0 / epsilon + h2o_lay)).
655  convert(spec_hum.units).value;
656  }
657  spec_hum.value.reference(ArrayAd<double, 1>(spec_hum_t));
658  return spec_hum;
659 }
660 
661 //-----------------------------------------------------------------------
667 //-----------------------------------------------------------------------
668 
670 {
671  ArrayAdWithUnit<double, 1> pgrid(press->pressure_grid());
672  // We pick the first spectrometer to calculate gravity out. For
673  // GOSAT this doesn't matter since all the spectrometers look at the
674  // same point. For a different instrument, this might matter and
675  // we'll need to revisit this.
676  int spec_index = 0;
678  mol_dens.units = Unit("m^-2");
679  Array<AutoDerivative<double>, 1> mol_dens_t(pgrid.rows() - 1);
680  for(int i = 0; i < mol_dens_t.rows(); ++i) {
681  AutoDerivativeWithUnit<double> dp = pgrid(i + 1) - pgrid(i);
682  AutoDerivativeWithUnit<double> play(pgrid(i) + dp / 2);
683  AutoDerivativeWithUnit<double> grav_lay = alt[spec_index]->gravity(play);
684  mol_dens_t(i) = (dp / (c->molar_weight_dry_air() * grav_lay) *
685  c->avogadro_constant()).convert(mol_dens.units).value;
686  }
687  mol_dens.value.reference(ArrayAd<double, 1>(mol_dens_t));
688  return mol_dens;
689 }
690 
691 //-----------------------------------------------------------------------
694 //-----------------------------------------------------------------------
695 
697 {
698  ArrayAdWithUnit<double, 1> spec_hum(specific_humidity_layer());
699  ArrayAdWithUnit<double, 1> mol_dens(dry_air_molecular_density_layer());
701  dry_am.units = mol_dens.units;
702  Array<AutoDerivative<double>, 1> dry_am_t(mol_dens.rows());
703  for(int i = 0; i < dry_am_t.rows(); ++i)
704  dry_am_t(i) = (mol_dens(i) * (1 - spec_hum(i))).convert(dry_am.units).value;
705  dry_am.value.reference(ArrayAd<double, 1>(dry_am_t));
706  return dry_am;
707 }
708 
709 //-----------------------------------------------------------------------
712 //-----------------------------------------------------------------------
713 
716 {
717  ArrayAdWithUnit<double, 1> spec_hum(specific_humidity_layer());
718  ArrayAdWithUnit<double, 1> mol_dens(dry_air_molecular_density_layer());
719  DoubleWithUnit epsilon = c->molar_weight_water() / c->molar_weight_dry_air();
721  wet_am.units = mol_dens.units;
722  Array<AutoDerivative<double>, 1> wet_am_t(mol_dens.rows());
723  for(int i = 0; i < wet_am_t.rows(); ++i)
724  wet_am_t(i) =
725  (mol_dens(i) * (1 + spec_hum(i) * (1 - epsilon) / epsilon)).
726  convert(wet_am.units).value;
727  wet_am.value.reference(ArrayAd<double, 1>(wet_am_t));
728  return wet_am;
729 }
730 
731 //-----------------------------------------------------------------------
734 //-----------------------------------------------------------------------
735 
737 {
738  Array<AutoDerivative<double>, 1> dry_am(dry_air_column_thickness_layer().
739  value.to_array());
740  Array<AutoDerivative<double>, 1> pwf(dry_am.rows());
741  pwf = dry_am / sum(dry_am);
742  return ArrayAd<double, 1>(pwf);
743 }
744 
745 //-----------------------------------------------------------------------
749 //-----------------------------------------------------------------------
750 
752 {
753  // Note assumption here that pressure varies linearly over layer
754  ArrayAd<double, 1> pwlay = pressure_weighting_function_layer();
755  int nactive = press->number_level();
756  Array<AutoDerivative<double>, 1> pwlev(nactive);
757  pwlev(0) = pwlay(0) / 2;
758  for(int i = 1; i < nactive - 1; ++i)
759  pwlev(i) = (pwlay(i-1) + pwlay(i)) / 2;
760  pwlev(nactive - 1) = pwlay(nactive - 2) / 2;
761  return ArrayAd<double, 1>(pwlev);
762 }
763 
764 //-----------------------------------------------------------------------
767 //-----------------------------------------------------------------------
768 
770 AbsorberAbsco::gas_column_thickness_layer(const std::string& Gas_name) const
771 {
772  ArrayAdWithUnit<double, 1> pgrid(press->pressure_grid());
773  ArrayAdWithUnit<double, 1> dry_am = dry_air_column_thickness_layer();
774  ArrayAdWithUnit<double, 1> gas_thickness;
775  gas_thickness.units = dry_am.units;
776  Array<AutoDerivative<double>, 1> gas_thickness_t(dry_am.rows());
777  for(int i = 0; i < gas_thickness_t.rows(); ++i) {
778  AutoDerivativeWithUnit<double> dp = pgrid(i + 1) - pgrid(i);
779  AutoDerivativeWithUnit<double> play = pgrid(i) + dp / 2;
780  gas_thickness_t(i) =
781  (dry_am(i) * absorber_vmr(Gas_name)->volume_mixing_ratio(play.convert(units::Pa).value)).
782  convert(gas_thickness.units).value;
783  }
784  gas_thickness.value.reference(ArrayAd<double, 1>(gas_thickness_t));
785  return gas_thickness;
786 }
787 
788 //-----------------------------------------------------------------------
790 //-----------------------------------------------------------------------
791 
793 AbsorberAbsco::gas_total_column_thickness(const std::string& Gas_name) const
794 {
795  ArrayAdWithUnit<double, 1> gas_thickness =
796  gas_column_thickness_layer(Gas_name);
797  return AutoDerivativeWithUnit<double>(sum(gas_thickness.value.to_array()),
798  gas_thickness.units);
799 }
800 
801 // See base class for description
802 AutoDerivative<double> AbsorberAbsco::xgas(const std::string& Gas_name) const
803 {
804  ArrayAd<double, 1> pwf(pressure_weighting_function_grid());
805  ArrayAdWithUnit<double, 1> pgrid = press->pressure_grid();
807  res = pwf(0) * absorber_vmr(Gas_name)->volume_mixing_ratio(pgrid(0).convert(units::Pa).value);
808  for(int i = 1; i < pwf.rows(); ++i)
809  res += pwf(i) * absorber_vmr(Gas_name)->volume_mixing_ratio(pgrid(i).convert(units::Pa).value);
810  return res;
811 }
812 
813 //-----------------------------------------------------------------------
816 //-----------------------------------------------------------------------
817 
818 AutoDerivative<double> AbsorberAbsco::average_vmr(const std::string& Gas_name) const
819 {
820  ArrayAdWithUnit<double, 1> pgrid(press->pressure_grid());
821  AutoDerivative<double> avg_vmr = absorber_vmr(Gas_name)->volume_mixing_ratio(pgrid(0).convert(units::Pa).value);
822  for(int i = 1; i < press->number_level(); ++i) {
823  avg_vmr += absorber_vmr(Gas_name)->volume_mixing_ratio(pgrid(i).convert(units::Pa).value);
824  }
825  return avg_vmr / press->number_level();
826 }
827 
828 void AbsorberAbsco::print(std::ostream& Os) const
829 {
830  OstreamPad opad(Os, " ");
831  Os << "AbsorberAbsco:\n";
832  for(int i = 0; i < (int) gas_absorption.size(); ++i) {
833  Os << " Gas Absorber[" << i << "]:\n";
834  opad << *gas_absorption[i] << "\n";
835  opad.strict_sync();
836  Os << " Absorber VMR[" << i << "]:\n";
837  opad << *vmr[i] << "\n";
838  opad.strict_sync();
839  }
840 }
841 
842 //----------------------------------------------------------------
845 //----------------------------------------------------------------
846 
848 {
849  fill_tau_gas_cache();
850  range_check(Spec_index, 0, number_spectrometer());
851  blitz::Array<AutoDerivative<double>, 1> gsub(psub.rows());
852  for(int i = 0; i < gsub.rows(); ++i)
853  gsub(i) = gravity_func(Spec_index, psub(i)).convert(Unit("m/s^2")).value;
855  Unit("m/s^2"));
856 }
857 
858 //----------------------------------------------------------------
861 //----------------------------------------------------------------
862 
863 ArrayAdWithUnit<double, 1> AbsorberAbsco::vmr_sublayer(const std::string& Gas_name) const
864 {
865  fill_tau_gas_cache();
866  int j = gas_index(Gas_name);
867  if(j < 0) {
868  Exception err;
869  err << "Gas named " << Gas_name << " not present in vmr list";
870  throw err;
871  }
872  Array<AutoDerivative<double>, 1> vmrsub(psub.rows());
873  for(int i = 0; i < psub.rows(); ++i)
874  vmrsub(i) = vmr_func(j, psub(i)).value;
877 }
878 
879 // See base class for description
881 {
882  int i = gas_index(Gas_name);
883  if(i < 0) {
884  Exception err;
885  err << "Gas named " << Gas_name << " not present in vmr list";
886  throw err;
887  }
888  return vmr[i];
889 }
890 
891 //----------------------------------------------------------------
893 //----------------------------------------------------------------
894 
896 (const std::string& Gas_name) const
897 {
898  int i = gas_index(Gas_name);
899  if(i < 0) {
900  Exception err;
901  err << "Gas named " << Gas_name << " not present in vmr list";
902  throw err;
903  }
904  return gas_absorption[i];
905 }
906 
907 //----------------------------------------------------------------
910 //----------------------------------------------------------------
911 
913 {
914  fill_tau_gas_cache();
915  Array<AutoDerivative<double>, 1> tsub_t(psub.rows());
916  for(int i = 0; i < psub.rows(); ++i)
917  tsub_t(i) = temp_func(psub(i)).convert(units::K).value;
919  units::K);
920 }
921 
922 //----------------------------------------------------------------
925 //----------------------------------------------------------------
926 
928 {
929  fill_tau_gas_cache();
930  Array<AutoDerivative<double>, 1> h2o_vmr_t(psub.rows());
931  for(int i = 0; i < psub.rows(); ++i)
932  h2o_vmr_t(i) = h2o_vmr_func(psub(i)).value;
935 }
936 
937 
938 
939 
940 
941 
942 
943 
944 
This class is used to read the absco tables.
Definition: absco.h:56
#define range_check(V, Min, Max)
Range check.
Definition: fp_exception.h:140
int cols() const
Definition: array_ad.h:369
bool is_constant() const
Definition: array_ad.h:371
This class maintains the absorber portion of the state.
int depth() const
Definition: array_ad.h:370
ArrayAd< double, 1 > pressure_weighting_function_grid() const
This is the pressure weighting function by grid level.
This is a AutoDerivative that also has units associated with it.
virtual boost::shared_ptr< Absorber > clone() const
Clone an Absorber object.
ArrayAdWithUnit< double, 1 > h2o_vmr_sublayer() const
Return the H2O volume mixing ratio we use for each sublayer.
This is a filtering stream that adds a pad to the front of every line written out.
Definition: ostream_pad.h:32
AutoDerivativeWithUnit< T > convert(const Unit &R) const
Convert to the given units.
double integrand(double wn, double p, int Spec_index, int Species_index) const
Integrand used in the absorption calculation.
AbsorberAbsco(const std::vector< boost::shared_ptr< AbsorberVmr > > Vmr, const boost::shared_ptr< Pressure > &Press, const boost::shared_ptr< Temperature > &Temp, const std::vector< boost::shared_ptr< Altitude > > &Alt, const std::vector< boost::shared_ptr< GasAbsorption > > &Gas_absorption, const boost::shared_ptr< Constant > &C, int Nsub=10)
Create an absorber.
ArrayAdWithUnit< double, 1 > dry_air_molecular_density_layer() const
This is a helper function to compute the common part of the dry air mass and wet air mass routines...
const Unit dimensionless("dimensionless", 1.0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0)
virtual void print(std::ostream &Os) const
virtual boost::shared_ptr< AbsorberVmr > absorber_vmr(const std::string &Gas_name) const
Returns the AbsorberVmr object for a given species index.
const Unit Pa("Pa", N/(m *m))
virtual blitz::Array< double, 2 > optical_depth_each_layer_nder(double wn, int spec_index) const
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
const blitz::Array< T, D+1 > jacobian() const
Definition: array_ad.h:307
AutoDerivative< double > average_vmr(const std::string &Gas_name) const
Returns the simple average volume mixing ratio for a gas.
ArrayAdWithUnit< double, 1 > wet_air_column_thickness_layer() const
This is the wet air column thickness by layer.
blitz::Array< AutoDerivative< T >, D > to_array() const
Definition: array_ad.h:316
AutoDerivativeWithUnit< double > integrand_independent_wn(int Spec_index, int Species_index, const DoubleWithUnit &P) const
This is the portion of the optical depth calculation integrand that is independent on the wave number...
Apply value function to a blitz array.
ArrayAd< double, 1 > pressure_weighting_function_layer() const
This is the pressure weighting function by layer.
const blitz::Array< T, D > & value() const
Definition: array_ad.h:306
The AutoDerivative<T> works well, and it works with blitz if you create a blitz::Array<AutoDerivative...
Definition: array_ad.h:40
const Unit K("K", 1.0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0)
This is a helper class that calculates the absorption cross section for a fixed set of Pressure...
Definition: absco.h:20
const Unit m("m", 1.0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0)
virtual ArrayAd< double, 2 > optical_depth_each_layer(double wn, int spec_index) const
This gives the optical depth for each layer, for the given wave number.
We frequently have a double with units associated with it.
double optical_depth_each_layer_direct_integrate(double wn, int Spec_index, int Species_index, int Layer_index, double eps_abs=0, double eps_rel=1e-3) const
Find the optical depth for a layer by directly integrating the integrand function.
int number_variable() const
Definition: array_ad.h:376
ArrayAdWithUnit< double, 1 > vmr_sublayer(const std::string &Gas_name) const
Return the volume mixing ratio we use for each sublayer.
double sqr(double x)
void reference(const ArrayAd< T, D > &V)
Definition: array_ad.h:372
Libraries such as boost::units allow unit handling where we know the units at compile time...
Definition: unit.h:25
Contains classes to abstract away details in various Spurr Radiative Transfer software.
Definition: doxygen_python.h:1
const T & value() const
Convert to type T.
#define REGISTER_LUA_END()
Definition: register_lua.h:134
ArrayAdWithUnit< double, 1 > specific_humidity_layer() const
Returns specific humidity by layer.
ArrayAdWithUnit< double, 1 > gravity_sublayer(int Spec_index) const
Return the gravity we use for each sublayer.
This class maintains the absorber portion of the state.
Definition: absorber.h:27
Helper class for AccumulatedTimer.
ArrayAdWithUnit< double, 1 > gas_column_thickness_layer(const std::string &Gas_name) const
This is the column thickness of a gas by layer.
ArrayAdWithUnit< double, 1 > temperature_sublayer() const
Return the temperature we use for each sublayer.
double value(const FullPhysics::AutoDerivative< double > &Ad)
virtual AutoDerivative< double > xgas(const std::string &Gas_name) const
This calculates the gas column, e.g., XCO2.
AutoDerivativeWithUnit< double > gas_total_column_thickness(const std::string &Gas_name) const
This is the total column thickness of a gas.
ArrayAdWithUnit< double, 1 > dry_air_column_thickness_layer() const
This is the dry air column thickness by layer.
boost::shared_ptr< GasAbsorption > gas_absorption_ptr(const std::string &Gas_name) const
Return GasAbsorption as a pointer.
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