USGS

Isis 3.0 Object Programmers' Reference

Home

NumericalAtmosApprox.cpp
Go to the documentation of this file.
1 
23 #include <cmath>
24 #include "AtmosModel.h"
25 #include "NumericalAtmosApprox.h"
26 #include "NumericalApproximation.h"
27 #include "IException.h"
28 #include "IString.h"
29 
30 using namespace std;
31 namespace Isis {
68  double NumericalAtmosApprox::RombergsMethod(AtmosModel *am, IntegFunc sub, double a, double b) {
69  // This method was derived from an algorithm in the text
70  // Numerical Recipes in C: The Art of Scientific Computing
71  // Section 4.3 by Flannery, Press, Teukolsky, and Vetterling
72  int maxits = 20; // maximium number of iterations allowed to converge
73  double dss = 0; // error estimate for
74  double h[maxits+1]; // relative stepsizes for trap
75  double trap[maxits+1]; // successive trapeziodal approximations
76  double epsilon; // desired fractional accuracy
77  double epsilon2; // desired fractional accuracy
78  double ss; // result
79 
80  epsilon = 1.0e-4;
81  epsilon2 = 1.0e-6;
82 
83  h[0] = 1.0;
84  try {
85  NumericalApproximation interp(NumericalApproximation::PolynomialNeville);
86  for(int i = 0; i < maxits; i++) {
87  // i will determine number of trapezoidal partitions of area
88  // under curve for "integration" using refined trapezoidal rule
89  trap[i] = RefineExtendedTrap(am, sub, a, b, trap[i], i + 1); // validates data here
90  if(i >= 4) {
91  interp.AddData(5, &h[i-4], &trap[i-4]);
92  ss = interp.Evaluate(0.0, NumericalApproximation::Extrapolate);
93  dss = interp.PolynomialNevilleErrorEstimate()[0];
94  interp.Reset();
95  // we work only until our necessary accuracy is achieved
96  if(fabs(dss) <= epsilon * fabs(ss)) return ss;
97  if(fabs(dss) <= epsilon2) return ss;
98  }
99  trap[i+1] = trap[i];
100  h[i+1] = 0.25 * h[i];
101  // This is a key step: the factor is 0.25d0 even though
102  // the stepsize is decreased by 0.5d0. This makes the
103  // extraplolation a polynomial in h-squared as allowed
104  // by the equation from Numerical Recipes 4.2.1 pg.132,
105  // not just a polynomial in h.
106  }
107  }
108  catch(IException &e) { // catch error from RefineExtendedTrap, Constructor, Evaluate, PolynomialNevilleErrorEstimate
109  throw IException(e,
110  e.errorType(),
111  "NumericalAtmosApprox::RombergsMethod() - Caught the following error: ",
112  _FILEINFO_);
113  }
114  throw IException(IException::Programmer,
115  "NumericalAtmosApprox::RombergsMethod() - Failed to converge in "
116  + IString(maxits) + " iterations.",
117  _FILEINFO_);
118  }
119 
152  double NumericalAtmosApprox::RefineExtendedTrap(AtmosModel *am, IntegFunc sub, double a, double b, double s, unsigned int n) {
153  // This method was derived from an algorithm in the text
154  // Numerical Recipes in C: The Art of Scientific Computing
155  // Section 4.2 by Flannery, Press, Teukolsky, and Vetterling
156  try {
157  if(n == 1) {
158  double begin;
159  double end;
160  if(sub == InnerFunction) {
161  begin = InrFunc2Bint(am, a);
162  end = InrFunc2Bint(am, b);
163  }
164  else {
165  begin = OutrFunc2Bint(am, a);
166  end = OutrFunc2Bint(am, b);
167  }
168  return (0.5 * (b - a) * (begin + end));
169  }
170  else {
171  int it;
172  double delta, tnm, x, sum;
173  it = (int)(pow(2.0, (double)(n - 2)));
174  tnm = it;
175  delta = (b - a) / tnm; // spacing of the points to be added
176  x = a + 0.5 * delta;
177  sum = 0.0;
178  for(int i = 0; i < it; i++) {
179  if(sub == InnerFunction) {
180  sum = sum + InrFunc2Bint(am, x);
181  }
182  else {
183  sum = sum + OutrFunc2Bint(am, x);
184  }
185  x = x + delta;
186  }
187  return (0.5 * (s + (b - a) * sum / tnm));// replace s with refined value
188  }
189  }
190  catch(IException &e) { // catch exception from Evaluate()
191  throw IException(e,
192  e.errorType(),
193  "NumericalAtmosApprox::RefineExtendedTrap() - Caught the following error: ",
194  _FILEINFO_);
195  }
196  }
197 
217  double NumericalAtmosApprox::OutrFunc2Bint(AtmosModel *am, double phi) {
218  double result;
220  sub = NumericalAtmosApprox::InnerFunction;
221 
222  am->p_atmosPhi = phi;
223  am->p_atmosCosphi = cos((PI / 180.0) * phi);
224 
225  NumericalAtmosApprox qromb;
226  try {
227  result = qromb.RombergsMethod(am, sub, 1.0e-6, 1.0);
228  return result;
229  }
230  catch(IException &e) { // catch exception from RombergsMethod()
231  throw IException(e,
232  e.errorType(),
233  "NumericalAtmosApprox::OutrFunc2Bint() - Caught the following error: ",
234  _FILEINFO_);
235  }
236  }
237 
266  double NumericalAtmosApprox::InrFunc2Bint(AtmosModel *am, double mu) {
267  double ema; //pass in mu, get emission angle
268  double sine; //sin(ema)
269  double alpha;
270  double phase; //angle between sun and camera
271  double result;
272  double phasefn;//Henyey-Greenstein phase fn
273  double xx; //temp var to calculate emunot, emu
274  double emunot; //exp(-tau/munot)
275  double emu; //exp(-tau/mu)
276  double tfac; //factor that occurs in the integrals for transmission
277 
278  // calculate the phase angle
279  // also calculate any of the other redundant parameters
280  ema = acos(mu) * (180.0 / PI);
281  sine = sin(ema * (PI / 180.0));
282  if((am->p_atmosAtmSwitch == 0) || (am->p_atmosAtmSwitch == 2)) { // Reflection phase <= 90
283  alpha = am->p_atmosSini * sine * am->p_atmosCosphi +
284  am->p_atmosMunot * mu;
285  }
286  else { // Transmission phase <= 90
287  alpha = am->p_atmosSini * sine * am->p_atmosCosphi -
288  am->p_atmosMunot * mu;
289  }
290  phase = acos(alpha) * (180.0 / PI);
291  // now evaluate the integrand; all needed parameters
292  // have been hidden separately and passed to it in COMMON.
293  if(am->p_atmosAtmSwitch == 0) {
294  // integrand for hemispheric albedo
295  result = mu * am->p_atmosPM->CalcSurfAlbedo(phase, am->p_atmosInc, ema);
296  }
297  else {
298  phasefn = (1.0 - am->AtmosHga() * am->AtmosHga()) / pow((1.0 + 2.0 * am->AtmosHga() * alpha + am->AtmosHga() * am->AtmosHga()), 1.5);
299  xx = -am->AtmosTau() / max(am->p_atmosMunot, 1.0e-30);
300  if(xx < -69.0) {
301  emunot = 0.0;
302  }
303  else if(xx > 69.0) {
304  emunot = 1.0e30;
305  }
306  else {
307  emunot = exp(xx);
308  }
309  xx = -am->AtmosTau() / max(mu, 1.0e-30);
310 
311  if(xx < -69.0) {
312  emu = 0.0;
313  }
314  else if(xx > 69.0) {
315  emu = 1.0e30;
316  }
317  else {
318  emu = exp(xx);
319  }
320  if(mu == am->p_atmosMunot) {
321  tfac = am->AtmosTau() * emunot / (am->p_atmosMunot * am->p_atmosMunot);
322  }
323  else {
324  tfac = (emunot - emu) / (am->p_atmosMunot - mu);
325  }
326  if(am->p_atmosAtmSwitch == 1) {
327  result = mu * (phasefn - 1.0) * tfac;
328  }
329  else if(am->p_atmosAtmSwitch == 2) {
330  result = am->p_atmosMunot * mu * (phasefn - 1.0) * (1.0 - emunot * emu) / (am->p_atmosMunot + mu);
331  }
332  else if(am->p_atmosAtmSwitch == 3) {
333  result = -sine * am->p_atmosCosphi * (phasefn - 1.0) * tfac;
334  }
335  else {
336  string msg = "NumericalAtmosApprox::InrFunc2Bint() - Invalid value of atmospheric ";
337  msg += "switch used as argument to this function";
338  throw IException(IException::Programmer, msg, _FILEINFO_);
339  }
340  }
341  return result;
342  }
343 }//end NumericalAtmosApprox.cpp