|
Isis 3.0 Object Programmers' Reference |
Home |
00001 00023 #include <cmath> 00024 #include "AtmosModel.h" 00025 #include "NumericalAtmosApprox.h" 00026 #include "NumericalApproximation.h" 00027 #include "iException.h" 00028 #include "iString.h" 00029 00030 using namespace std; 00031 namespace Isis { 00068 double NumericalAtmosApprox::RombergsMethod (AtmosModel *am, IntegFunc sub, double a, double b) throw (iException &){ 00069 // This method was derived from an algorithm in the text 00070 // Numerical Recipes in C: The Art of Scientific Computing 00071 // Section 4.3 by Flannery, Press, Teukolsky, and Vetterling 00072 int maxits = 20; // maximium number of iterations allowed to converge 00073 double dss = 0; // error estimate for 00074 double h[maxits+1]; // relative stepsizes for trap 00075 double trap[maxits+1]; // successive trapeziodal approximations 00076 double epsilon; // desired fractional accuracy 00077 double epsilon2; // desired fractional accuracy 00078 double ss; // result 00079 00080 epsilon = 1.0e-4; 00081 epsilon2 = 1.0e-6; 00082 00083 h[0] = 1.0; 00084 try{ 00085 NumericalApproximation interp(NumericalApproximation::PolynomialNeville); 00086 for (int i=0; i<maxits; i++) { 00087 // i will determine number of trapezoidal partitions of area 00088 // under curve for "integration" using refined trapezoidal rule 00089 trap[i] = RefineExtendedTrap(am,sub,a,b,trap[i],i+1); // validates data here 00090 if (i >= 4) { 00091 interp.AddData(5, &h[i-4], &trap[i-4]); 00092 ss = interp.Evaluate(0.0,NumericalApproximation::Extrapolate); 00093 dss = interp.PolynomialNevilleErrorEstimate()[0]; 00094 interp.Reset(); 00095 // we work only until our necessary accuracy is achieved 00096 if (fabs(dss) <= epsilon*fabs(ss)) return ss; 00097 if (fabs(dss) <= epsilon2) return ss; 00098 } 00099 trap[i+1] = trap[i]; 00100 h[i+1] = 0.25 * h[i]; 00101 // This is a key step: the factor is 0.25d0 even though 00102 // the stepsize is decreased by 0.5d0. This makes the 00103 // extraplolation a polynomial in h-squared as allowed 00104 // by the equation from Numerical Recipes 4.2.1 pg.132, 00105 // not just a polynomial in h. 00106 } 00107 } 00108 catch (iException e){ // catch error from RefineExtendedTrap, Constructor, Evaluate, PolynomialNevilleErrorEstimate 00109 throw e.Message(e.Type(), 00110 "NumericalAtmosApprox::RombergsMethod() - Caught the following error: ", 00111 _FILEINFO_); 00112 } 00113 throw iException::Message(iException::Programmer, 00114 "NumericalAtmosApprox::RombergsMethod() - Failed to converge in " 00115 + iString(maxits) + " iterations.", 00116 _FILEINFO_); 00117 } 00118 00151 double NumericalAtmosApprox::RefineExtendedTrap(AtmosModel *am, IntegFunc sub, double a, double b, double s, unsigned int n) throw (iException &){ 00152 // This method was derived from an algorithm in the text 00153 // Numerical Recipes in C: The Art of Scientific Computing 00154 // Section 4.2 by Flannery, Press, Teukolsky, and Vetterling 00155 try{ 00156 if (n == 1) { 00157 double begin; 00158 double end; 00159 if (sub == InnerFunction) { 00160 begin = InrFunc2Bint(am,a); 00161 end = InrFunc2Bint(am,b); 00162 } 00163 else { 00164 begin = OutrFunc2Bint(am,a); 00165 end = OutrFunc2Bint(am,b); 00166 } 00167 return (0.5 * (b - a) * (begin + end)); 00168 } 00169 else { 00170 int it; 00171 double delta,tnm,x,sum; 00172 it = (int)(pow(2.0,(double)(n-2))); 00173 tnm = it; 00174 delta = (b - a) / tnm; // spacing of the points to be added 00175 x = a + 0.5 * delta; 00176 sum = 0.0; 00177 for (int i=0; i<it; i++) { 00178 if (sub == InnerFunction) { 00179 sum = sum + InrFunc2Bint(am,x); 00180 } 00181 else { 00182 sum = sum + OutrFunc2Bint(am,x); 00183 } 00184 x = x + delta; 00185 } 00186 return (0.5 * (s + (b - a) * sum / tnm));// replace s with refined value 00187 } 00188 } 00189 catch (iException e){ // catch exception from Evaluate() 00190 throw e.Message(e.Type(), 00191 "NumericalAtmosApprox::RefineExtendedTrap() - Caught the following error: ", 00192 _FILEINFO_); 00193 } 00194 } 00195 00215 double NumericalAtmosApprox::OutrFunc2Bint(AtmosModel *am, double phi) { 00216 double result; 00217 NumericalAtmosApprox::IntegFunc sub; 00218 sub = NumericalAtmosApprox::InnerFunction; 00219 00220 am->p_atmosPhi = phi; 00221 am->p_atmosCosphi = cos((PI/180.0)*phi); 00222 00223 NumericalAtmosApprox qromb; 00224 try{ 00225 result = qromb.RombergsMethod(am,sub,1.0e-6,1.0); 00226 return result; 00227 } 00228 catch (iException e){ // catch exception from RombergsMethod() 00229 throw e.Message(e.Type(), 00230 "NumericalAtmosApprox::OutrFunc2Bint() - Caught the following error: ", 00231 _FILEINFO_); 00232 } 00233 } 00234 00263 double NumericalAtmosApprox::InrFunc2Bint(AtmosModel *am, double mu) { 00264 double ema; //pass in mu, get emission angle 00265 double sine; //sin(ema) 00266 double alpha; 00267 double phase; //angle between sun and camera 00268 double result; 00269 double phasefn;//Henyey-Greenstein phase fn 00270 double xx; //temp var to calculate emunot, emu 00271 double emunot; //exp(-tau/munot) 00272 double emu; //exp(-tau/mu) 00273 double tfac; //factor that occurs in the integrals for transmission 00274 00275 // calculate the phase angle 00276 // also calculate any of the other redundant parameters 00277 ema = acos(mu) * (180.0/PI); 00278 sine = sin(ema*(PI/180.0)); 00279 if ((am->p_atmosAtmSwitch == 0) || (am->p_atmosAtmSwitch == 2)) { // Reflection phase <= 90 00280 alpha = am->p_atmosSini * sine * am->p_atmosCosphi + 00281 am->p_atmosMunot * mu; 00282 } 00283 else { // Transmission phase <= 90 00284 alpha = am->p_atmosSini * sine * am->p_atmosCosphi - 00285 am->p_atmosMunot * mu; 00286 } 00287 phase = acos(alpha) * (180.0/PI); 00288 // now evaluate the integrand; all needed parameters 00289 // have been hidden separately and passed to it in COMMON. 00290 if (am->p_atmosAtmSwitch == 0) { 00291 // integrand for hemispheric albedo 00292 result = mu * am->p_atmosPM->CalcSurfAlbedo(phase,am->p_atmosInc,ema); 00293 } 00294 else { 00295 phasefn = (1.0 - am->AtmosHga() * am->AtmosHga()) /pow((1.0+2.0*am->AtmosHga()*alpha+am->AtmosHga()*am->AtmosHga()),1.5); 00296 xx = -am->AtmosTau() / max(am->p_atmosMunot,1.0e-30); 00297 if (xx < -69.0) { 00298 emunot = 0.0; 00299 } 00300 else if (xx > 69.0) { 00301 emunot = 1.0e30; 00302 } 00303 else { 00304 emunot = exp(xx); 00305 } 00306 xx = -am->AtmosTau() / max(mu,1.0e-30); 00307 00308 if (xx < -69.0) { 00309 emu = 0.0; 00310 } 00311 else if (xx > 69.0) { 00312 emu = 1.0e30; 00313 } 00314 else { 00315 emu = exp(xx); 00316 } 00317 if (mu == am->p_atmosMunot) { 00318 tfac = am->AtmosTau() * emunot / (am->p_atmosMunot * am->p_atmosMunot); 00319 } 00320 else { 00321 tfac = (emunot - emu) / (am->p_atmosMunot - mu); 00322 } 00323 if (am->p_atmosAtmSwitch == 1) { 00324 result = mu * (phasefn - 1.0) * tfac; 00325 } 00326 else if (am->p_atmosAtmSwitch == 2) { 00327 result = am->p_atmosMunot * mu * (phasefn - 1.0) * (1.0 - emunot * emu) / (am->p_atmosMunot + mu); 00328 } 00329 else if (am->p_atmosAtmSwitch == 3) { 00330 result = -sine * am->p_atmosCosphi * (phasefn - 1.0) * tfac; 00331 } 00332 else { 00333 string msg = "NumericalAtmosApprox::InrFunc2Bint() - Invalid value of atmospheric "; 00334 msg += "switch used as argument to this function"; 00335 throw iException::Message(iException::Programmer,msg,_FILEINFO_); 00336 } 00337 } 00338 return result; 00339 } 00340 }//end NumericalAtmosApprox.cpp