USGS

Isis 3.0 Object Programmers' Reference

Home

AtmosModel.cpp
1 #include <cmath>
2 #include <string>
3 #include "Pvl.h"
4 #include "IString.h"
5 #include "AtmosModel.h"
6 #include "NumericalApproximation.h"
7 #include "NumericalAtmosApprox.h"
8 #include "PhotoModel.h"
9 #include "Minnaert.h"
10 #include "LunarLambert.h"
11 #include "Plugin.h"
12 #include "IException.h"
13 #include "FileName.h"
14 
15 using namespace std;
16 
17 #define MAX(x,y) (((x) > (y)) ? (x) : (y))
18 namespace Isis {
35  AtmosModel::AtmosModel(Pvl &pvl, PhotoModel &pmodel) {
36  p_atmosAlgorithmName = "Unknown";
37  p_atmosPM = &pmodel;
38 
39  p_atmosIncTable.resize(91);
40  p_atmosAhTable.resize(91);
41  p_atmosHahgtTable.resize(91);
42  p_atmosHahgt0Table.resize(91);
43  p_atmosAb = 0.0;
44  p_atmosCosphi = 0.0;
45  p_atmosAtmSwitch = 0;
46  p_atmosEulgam = 0.5772156;
47  p_atmosHahgsb = 0.0;
48  p_atmosHga = 0.68;
49  p_atmosInc = 0.0;
50  p_atmosMunot = 0.0;
51  p_atmosNinc = 91;
52  p_atmosPhi = 0.0;
53  p_atmosSini = 0.0;
54  p_atmosTau = 0.28;
55  p_atmosTauref = 0.0;
56  p_atmosTauold = -1.0;
57  p_atmosWha = 0.95;
58  p_atmosWhaold = 1.0e30;
59  p_atmosBha = 0.85;
60  p_atmosHnorm = 0.003;
61  p_pstd = 0.0;
62  p_sbar = 0.0;
63  p_trans = 0.0;
64  p_trans0 = 0.0;
65  p_transs = 0.0;
66  p_standardConditions = false;
67 
68  PvlGroup &algorithm = pvl.findObject("AtmosphericModel").findGroup("Algorithm", Pvl::Traverse);
69 
70  if(algorithm.hasKeyword("Nulneg")) {
71  SetAtmosNulneg(algorithm["Nulneg"][0] == "YES");
72  }
73  else {
74  p_atmosNulneg = false;
75  }
76 
77  if(algorithm.hasKeyword("Tau")) {
78  SetAtmosTau(algorithm["Tau"]);
79  }
80  p_atmosTausave = p_atmosTau;
81 
82  if(algorithm.hasKeyword("Tauref")) {
83  SetAtmosTauref(algorithm["Tauref"]);
84  }
85 
86  if(algorithm.hasKeyword("Wha")) {
87  SetAtmosWha(algorithm["Wha"]);
88  }
89  p_atmosWhasave = p_atmosWha;
90 
91  if(algorithm.hasKeyword("Hga")) {
92  SetAtmosHga(algorithm["Hga"]);
93  }
94  p_atmosHgasave = p_atmosHga;
95 
96  if(algorithm.hasKeyword("Bha")) {
97  SetAtmosBha(algorithm["Bha"]);
98  }
99  p_atmosBhasave = p_atmosBha;
100 
101  if(algorithm.hasKeyword("Inc")) {
102  SetAtmosInc(algorithm["Inc"]);
103  }
104 
105  if(algorithm.hasKeyword("Phi")) {
106  SetAtmosPhi(algorithm["Phi"]);
107  }
108 
109  if(algorithm.hasKeyword("Hnorm")) {
110  SetAtmosHnorm(algorithm["Hnorm"]);
111  }
112 
113  if(algorithm.hasKeyword("Iord")) {
114  SetAtmosIord(algorithm["Iord"][0] == "YES");
115  }
116  else {
117  p_atmosAddOffset = false;
118  }
119 
120  if(algorithm.hasKeyword("EstTau")) {
121  SetAtmosEstTau(algorithm["EstTau"][0] == "YES");
122  }
123  else {
124  p_atmosEstTau = false;
125  }
126  }
127 
147  double AtmosModel::G11Prime(double tau) {
148  double sum;
149  int icnt;
150  double fac;
151  double term;
152  double tol;
153  double fi;
154  double elog;
155  double eulgam;
156  double e1_2;
157  double result;
158 
159  tol = 1.0e-6;
160  eulgam = 0.5772156;
161 
162  sum = 0.0;
163  icnt = 1;
164  fac = -tau;
165  term = fac;
166  while(fabs(term) > fabs(sum)*tol) {
167  sum = sum + term;
168  icnt = icnt + 1;
169  fi = (double) icnt;
170  fac = fac * (-tau) / fi;
171  term = fac / (fi * fi);
172  }
173  elog = log(MAX(1.0e-30, tau)) + eulgam;
174  e1_2 = sum + PI * PI / 12.0 + 0.5 *
175  pow(elog, 2.0);
176  result = 2.0 * (AtmosModel::En(1, tau)
177  + elog * AtmosModel::En(2, tau)
178  - tau * e1_2);
179  return(result);
180  }
181 
223  double AtmosModel::Ei(double x) {
224  //static double r8ei(double x) in original NumericalMethods class
225  // This method was derived from an algorithm in the text
226  // Numerical Recipes in C: The Art of Scientific Computing
227  // Section 6.3 by Flannery, Press, Teukolsky, and Vetterling
228  double result;
229  double fpmin; // close to smallest representable floating-point number
230  double euler; // Euler's constant, lower-case gamma
231  double epsilon; // desired relative error (tolerance)
232  int maxit; // maximum number of iterations allowed
233  double sum, fact, term, prev;
234 
235  fpmin = 1.0e-30;
236  maxit = 100;
237  epsilon = 6.0e-8;
238  euler = 0.57721566;
239 
240  if(x <= 0.0) {
241  throw IException(IException::Programmer,
242  "AtmosModel::Ei() - Invalid argument. Definition requires x > 0.0. Entered x = "
243  + IString(x),
244  _FILEINFO_);
245  }
246  if(x < fpmin) { //special case: avoid failure of convergence test because underflow
247  result = log(x) + euler;
248  }
249  else if(x <= -log(epsilon)) { //Use power series
250  sum = 0.0;
251  fact = 1.0;
252  for(int k = 1; k <= maxit; k++) {
253  fact = fact * x / k;
254  term = fact / k;
255  sum = sum + term;
256  if(term < epsilon * sum) {
257  result = sum + log(x) + euler;
258  return(result);
259  }
260  }
261  throw IException(IException::Unknown,
262  "AtmosModel::Ei() - Power series failed to converge in "
263  + IString(maxit) + " iterations. Unable to calculate exponential integral.",
264  _FILEINFO_);
265  }
266  else { // Use asymptotic series
267  sum = 0.0;
268  term = 1.0;
269  for(int k = 1; k <= maxit; k++) {
270  prev = term;
271  term = term * k / x;
272  if(term < epsilon) {
273  result = exp(x) * (1.0 + sum) / x;
274  return(result);
275  }
276  else {
277  if(term < prev) { // still converging: add new term
278  sum = sum + term;
279  }
280  else { // diverging: subtract previous term and exit
281  sum = sum - prev;
282  result = exp(x) * (1.0 + sum) / x;
283  return(result);
284  }
285  }
286  }
287  result = exp(x) * (1.0 + sum) / x;
288  }
289  return(result);
290  }
291 
364  double AtmosModel::En(unsigned int n, double x) {
365  //static double r8expint(int n, double x) in original NumericalMethods class
366  // This method was derived from an algorithm in the text
367  // Numerical Recipes in C: The Art of Scientific Computing
368  // Section 6.3 by Flannery, Press, Teukolsky, and Vetterling
369  int nm1;
370  double result;
371  double a, b, c, d, h;
372  double delta;
373  double fact;
374  double psi;
375  double fpmin; // close to smallest representable floating-point number
376  double maxit; // maximum number of iterations allowed
377  double epsilon; // desired relative error (tolerance)
378  double euler; // Euler's constant, gamma
379 
380  fpmin = 1.0e-30;
381  maxit = 100;
382  epsilon = 1.0e-7;
383  euler = 0.5772156649;
384 
385  nm1 = n - 1;
386  if(n < 0 || x < 0.0 || (x == 0.0 && (n == 0 || n == 1))) {
387  IString msg = "AtmosModel::En() - Invalid arguments. ";
388  msg += "Definition requires (x > 0.0 and n >=0 ) or (x >= 0.0 and n > 1). ";
389  msg += "Entered x = " + IString(x) + " and n = " + IString((int) n);
390  throw IException(IException::Programmer, msg, _FILEINFO_);
391  }
392  else if(n == 0) { // special case, this implies x > 0 by logic above
393  result = exp(-x) / x;
394  }
395  else if(x == 0.0) { // special case, this implies n > 1
396  result = 1.0 / nm1;
397  }
398  else if(x > 1.0) { // Lentz's algorithm, this implies n > 0
399  b = x + n;
400  c = 1.0 / fpmin;
401  d = 1.0 / b;
402  h = d;
403  for(int i = 1; i <= maxit; i++) {
404  a = -i * (nm1 + i);
405  b = b + 2.0;
406  d = 1.0 / (a * d + b);
407  c = b + a / c;
408  delta = c * d;
409  h = h * delta;
410  if(fabs(delta - 1.0) < epsilon) {
411  result = h * exp(-x);
412  return(result);
413  }
414  }
415  throw IException(IException::Unknown,
416  "AtmosModel::En() - Continued fraction failed to converge in "
417  + IString(maxit) + " iterations. Unable to calculate exponential integral.",
418  _FILEINFO_);
419  }
420  else { // evaluate series
421  if(nm1 != 0) {
422  result = 1.0 / nm1;
423  }
424  else {
425  result = -log(x) - euler;
426  }
427  fact = 1.0;
428  for(int i = 1; i <= maxit; i++) {
429  fact = -fact * x / i;
430  if(i != nm1) {
431  delta = -fact / (i - nm1);
432  }
433  else {
434  psi = -euler;
435  for(int ii = 1; ii <= nm1; ii++) {
436  psi = psi + 1.0 / ii;
437  }
438  delta = fact * (-log(x) + psi);
439  }
440  result = result + delta;
441  if(fabs(delta) < fabs(result)*epsilon) {
442  return(result);
443  }
444  }
445  throw IException(IException::Unknown,
446  "AtmosModel::En() - Series representation failed to converge in "
447  + IString(maxit) + " iterations. Unable to calculate exponential integral.",
448  _FILEINFO_);
449  }
450  return(result);
451  }
452 
469  void AtmosModel::CalcAtmEffect(double pha, double inc, double ema,
470  double *pstd, double *trans, double *trans0, double *sbar,
471  double *transs) {
472 
473  // Check validity of photometric angles
474  //if (pha > 180.0 || inc > 90.0 || ema > 90.0 || pha < 0.0 ||
475  // inc < 0.0 || ema < 0.0) {
476  // string msg = "Invalid photometric angles";
477  // throw IException::Message(IException::Programmer,msg,_FILEINFO_);
478  //}
479 
480  // Apply atmospheric function
481  AtmosModelAlgorithm(pha, inc, ema);
482  *pstd = p_pstd;
483  *trans = p_trans;
484  *trans0 = p_trans0;
485  *sbar = p_sbar;
486  *transs = p_transs;
487  }
488 
492  void AtmosModel::SetStandardConditions(bool standard) {
493  p_standardConditions = standard;
494  if(p_standardConditions) {
495  p_atmosTausave = p_atmosTau;
496  p_atmosTau = p_atmosTauref;
497  }
498  else {
499  p_atmosTau = p_atmosTausave;
500  }
501  }
502 
531  void AtmosModel::GenerateAhTable() {
532  int inccnt; //for loop incidence angle count, 1:ninc
533  double fun_temp;//temporary variable for integral
534  double factor; //factor for integration: 1 except at ends of interval where it's 1/2
535  double yp1, yp2; //first derivatives of first and last x values of @a p_atmosIncTable
536 
538  sub = NumericalAtmosApprox::OuterFunction;
539 
540  p_atmosAb = 0.0;
541 
542  //Create NumericalAtmosApprox object here for RombergsMethod used in for loop
543  NumericalAtmosApprox qromb;
544 
545  for(inccnt = 0; inccnt < p_atmosNinc; inccnt++) {
546  p_atmosInc = (double) inccnt;
547  p_atmosIncTable[inccnt] = p_atmosInc;
548  p_atmosMunot = cos((PI / 180.0) * p_atmosInc);
549  p_atmosSini = sin((PI / 180.0) * p_atmosInc);
550 
551  IString phtName = p_atmosPM->AlgorithmName();
552  phtName.UpCase();
553  if(p_atmosInc == 90.0) {
554  p_atmosAhTable[inccnt] = 0.0;
555  }
556  else if(phtName == "LAMBERT") {
557  p_atmosAhTable[inccnt] = 1.0;
558  }
559  else if(phtName == "LOMMELSEELIGER") {
560  p_atmosAhTable[inccnt] = 2.0 * log((1.0 / p_atmosMunot) / p_atmosMunot);
561  }
562  else if(phtName == "MINNAERT") {
563  p_atmosAhTable[inccnt] = (pow(p_atmosMunot, ((Minnaert *)p_atmosPM)->PhotoK())) / ((Minnaert *)p_atmosPM)->PhotoK();
564  }
565  else if(phtName == "LUNARLAMBERT") {
566  p_atmosAhTable[inccnt] = 2.0 * ((LunarLambert *)p_atmosPM)->PhotoL() *
567  log((1.0 + p_atmosMunot) / p_atmosMunot) + 1.0 -
568  ((LunarLambert *)p_atmosPM)->PhotoL();
569  }
570  else {
571  // numerically integrate the other photometric models
572  // outer integral is over phi from 0 to pi rad = 180 deg
573  p_atmosAtmSwitch = 0;
574  // integrate AtmosModel function from 0 to 180
575  fun_temp = qromb.RombergsMethod(this, sub, 0, 180);
576  // the correct normalization with phi in degrees is:
577  p_atmosAhTable[inccnt] = fun_temp / (90.0 * p_atmosMunot);
578  }
579  // Let's get our estimate of Ab by integrating (summing)
580  // A (i)sinicosi over our A table
581  if((inccnt == 0) || (inccnt == p_atmosNinc - 1)) {
582  factor = 0.5;
583  }
584  else {
585  factor = 1.0;
586  }
587 
588  p_atmosAb = p_atmosAb + p_atmosAhTable[inccnt] * p_atmosMunot * p_atmosSini * factor;
589  }
590 
591  factor = 2.0 * PI / 180.0;
592  p_atmosAb = p_atmosAb * factor;
593 
594  // set up clamped cubic spline
595  yp1 = 1.0e30;
596  yp2 = 1.0e30;
597  p_atmosAhSpline.Reset();
598  p_atmosAhSpline.SetInterpType(NumericalApproximation::CubicClamped);
599  p_atmosAhSpline.AddData(p_atmosIncTable, p_atmosAhTable);
600  p_atmosAhSpline.SetCubicClampedEndptDeriv(yp1, yp2);
601  }
602 
631  void AtmosModel::GenerateHahgTables() {
632  int inccnt; // for loop incidence angle count,1:ninc
633  double fun_temp; // temporary variable for integral
634  double hahgsb_temp; // save increment to hahgsb
635  double factor; // factor for integration: 1 except at ends of interval where it's 1/2
636  double yp1, yp2; // derivatives of endpoints of data set
637 
639  sub = NumericalAtmosApprox::OuterFunction;
640 
641  p_atmosHahgsb = 0.0;
642  NumericalAtmosApprox qromb;
643 
644  for(inccnt = 0; inccnt < p_atmosNinc; inccnt++) {
645  p_atmosInc = (double) inccnt;
646  p_atmosIncTable[inccnt] = p_atmosInc;
647  p_atmosMunot = cos((PI / 180.0) * p_atmosInc);
648  p_atmosSini = sin((PI / 180.0) * p_atmosInc);
649 
650  p_atmosAtmSwitch = 1;
651 
652  qromb.Reset();
653  fun_temp = qromb.RombergsMethod(this, sub, 0, 180);
654 
655  p_atmosHahgtTable[inccnt] = fun_temp * AtmosWha() / 360.0;
656  p_atmosAtmSwitch = 2;
657 
658  fun_temp = qromb.RombergsMethod(this, sub, 0, 180);
659 
660  hahgsb_temp = fun_temp * AtmosWha() / 360.0;
661 
662  if((inccnt == 0) || (inccnt == p_atmosNinc - 1)) {
663  factor = 0.5;
664  }
665  else {
666  factor = 1.0;
667  }
668 
669  p_atmosHahgsb = p_atmosHahgsb + p_atmosSini * factor * hahgsb_temp;
670  if(p_atmosInc == 0.0) {
671  p_atmosHahgt0Table[inccnt] = 0.0;
672  }
673  else {
674  p_atmosAtmSwitch = 3;
675  fun_temp = qromb.RombergsMethod(this, sub, 0, 180);
676  p_atmosHahgt0Table[inccnt] = fun_temp * AtmosWha() * p_atmosMunot / (360.0 * p_atmosSini);
677  }
678  }
679 
680  factor = 2.0 * PI / 180.0;
681  p_atmosHahgsb = p_atmosHahgsb * factor;
682 
683  // set up clamped cubic splines
684  yp1 = 1.0e30;
685  yp2 = 1.0e30;
686  p_atmosHahgtSpline.Reset();
687  p_atmosHahgtSpline.SetInterpType(NumericalApproximation::CubicClamped);
688  p_atmosHahgtSpline.AddData(p_atmosIncTable, p_atmosHahgtTable);
689  p_atmosHahgtSpline.SetCubicClampedEndptDeriv(yp1, yp2);
690 
691  p_atmosHahgt0Spline.Reset();
692  p_atmosHahgt0Spline.SetInterpType(NumericalApproximation::CubicClamped);
693  p_atmosHahgt0Spline.AddData(p_atmosIncTable, p_atmosHahgt0Table);
694  p_atmosHahgt0Spline.SetCubicClampedEndptDeriv(yp1, yp2);
695  }
696 
720  void AtmosModel::GenerateHahgTablesShadow() {
721  int inccnt; // for loop incidence angle count,1:ninc
722  double fun_temp; // temporary variable for integral
723  double factor; // factor for integration: 1 except at ends of interval where it's 1/2
724  int nincl = 19; // used instead of p_atmosNinc
725  double deltaInc; // limits table size and increment used
726 
728  sub = NumericalAtmosApprox::OuterFunction;
729 
730  p_atmosHahgsb = 0.0;
731  NumericalAtmosApprox qromb;
732  deltaInc = 90.0/(double)(nincl-1.0);
733 
734  for(inccnt = 0; inccnt < nincl; inccnt++) {
735  p_atmosInc = deltaInc * (double) inccnt;
736  p_atmosMunot = cos((PI / 180.0) * p_atmosInc);
737  p_atmosSini = sin((PI / 180.0) * p_atmosInc);
738 
739  p_atmosAtmSwitch = 2;
740 
741  qromb.Reset();
742  if (p_atmosInc >= 90.0) {
743  fun_temp = 0.0;
744  } else {
745  fun_temp = qromb.RombergsMethod(this, sub, 0, 180);
746  }
747 
748  if((inccnt == 0) || (inccnt == nincl - 1)) {
749  factor = 0.5;
750  }
751  else {
752  factor = 1.0;
753  }
754  p_atmosHahgsb = p_atmosHahgsb + p_atmosSini * factor * fun_temp *
755  AtmosWha() / 360.0;
756  }
757 
758  factor = 2.0 * deltaInc * PI / 180.0;
759  p_atmosHahgsb = p_atmosHahgsb * factor;
760  }
761 
773  void AtmosModel::SetAtmosAtmSwitch(const int atmswitch) {
774  if(atmswitch < 0 || atmswitch > 3) {
775  string msg = "Invalid value of atmospheric atmswitch [" + IString(atmswitch) + "]";
776  throw IException(IException::User, msg, _FILEINFO_);
777  }
778 
779  p_atmosAtmSwitch = atmswitch;
780  }
781 
793  void AtmosModel::SetAtmosBha(const double bha) {
794  if(bha < -1.0 || bha > 1.0) {
795  string msg = "Invalid value of Anisotropic atmospheric bha [" +
796  IString(bha) + "]";
797  throw IException(IException::User, msg, _FILEINFO_);
798  }
799 
800  p_atmosBha = bha;
801  }
802 
814  void AtmosModel::SetAtmosHga(const double hga) {
815  if(hga <= -1.0 || hga >= 1.0) {
816  string msg = "Invalid value of Hapke atmospheric hga [" + IString(hga) + "]";
817  throw IException(IException::User, msg, _FILEINFO_);
818  }
819 
820  p_atmosHga = hga;
821  }
822 
833  void AtmosModel::SetAtmosInc(const double inc) {
834  if(inc < 0.0 || inc > 90.0) {
835  string msg = "Invalid value of atmospheric inc [" + IString(inc) + "]";
836  throw IException(IException::User, msg, _FILEINFO_);
837  }
838 
839  p_atmosInc = inc;
840  p_atmosMunot = cos(inc * PI / 180.0);
841  p_atmosSini = sin(inc * PI / 180.0);
842  }
843 
855  void AtmosModel::SetAtmosNulneg(const string nulneg) {
856  IString temp(nulneg);
857  temp = temp.UpCase();
858 
859  if(temp != "NO" && temp != "YES") {
860  string msg = "Invalid value of Atmospheric nulneg [" + temp + "]";
861  throw IException(IException::User, msg, _FILEINFO_);
862  }
863 
864  SetAtmosNulneg(temp.compare("YES") == 0);
865  }
866 
878  void AtmosModel::SetAtmosPhi(const double phi) {
879  if(phi < 0.0 || phi > 360.0) {
880  string msg = "Invalid value of atmospheric phi [" + IString(phi) + "]";
881  throw IException(IException::User, msg, _FILEINFO_);
882  }
883 
884  p_atmosPhi = phi;
885  p_atmosCosphi = cos(phi * PI / 180.0);
886  }
887 
897  void AtmosModel::SetAtmosTau(const double tau) {
898  if(tau < 0.0) {
899  string msg = "Invalid value of Atmospheric tau [" + IString(tau) + "]";
900  throw IException(IException::User, msg, _FILEINFO_);
901  }
902 
903  p_atmosTau = tau;
904  }
905 
916  void AtmosModel::SetAtmosTauref(const double tauref) {
917  if(tauref < 0.0) {
918  string msg = "Invalid value of Atmospheric tauref [" + IString(tauref) + "]";
919  throw IException(IException::User, msg, _FILEINFO_);
920  }
921 
922  p_atmosTauref = tauref;
923  }
924 
935  void AtmosModel::SetAtmosWha(const double wha) {
936  if(wha <= 0.0 || wha > 1.0) {
937  string msg = "Invalid value of Atmospheric wha [" + IString(wha) + "]";
938  throw IException(IException::User, msg, _FILEINFO_);
939  }
940 
941  p_atmosWha = wha;
942  }
943 
948  bool AtmosModel::TauOrWhaChanged() const {
949  return (AtmosTau() != p_atmosTauold) || (AtmosWha() != p_atmosWhaold);
950  }
951 
962  void AtmosModel::SetAtmosHnorm(const double hnorm) {
963  if(hnorm < 0.0) {
964  QString msg = "Invalid value of Atmospheric hnorm [" + toString(hnorm) + "]";
965  throw IException(IException::User, msg, _FILEINFO_);
966  }
967  p_atmosHnorm = hnorm;
968  }
969 
977  void AtmosModel::SetAtmosIord(const string offset) {
978  IString temp(offset);
979  temp = temp.UpCase();
980 
981  if(temp != "NO" && temp != "YES") {
982  string msg = "Invalid value of Atmospheric additive offset[" + temp + "]";
983  throw IException(IException::User, msg, _FILEINFO_);
984  }
985 
986  SetAtmosIord(temp.compare("YES") == 0);
987  }
988 
996  void AtmosModel::SetAtmosEstTau(const string esttau) {
997  IString temp(esttau);
998  temp = temp.UpCase();
999 
1000  if(temp != "NO" && temp != "YES") {
1001  string msg = "Invalid value of Atmospheric optical depth estimation[" + temp + "]";
1002  throw IException(IException::User, msg, _FILEINFO_);
1003  }
1004 
1005  SetAtmosEstTau(temp.compare("YES") == 0);
1006  }
1007 }