|
Isis 3.0 Object Programmers' Reference |
Home |
00001 #include <cmath> 00002 #include <string> 00003 #include "Pvl.h" 00004 #include "iString.h" 00005 #include "AtmosModel.h" 00006 #include "NumericalApproximation.h" 00007 #include "NumericalAtmosApprox.h" 00008 #include "PhotoModel.h" 00009 #include "Minnaert.h" 00010 #include "LunarLambert.h" 00011 #include "Plugin.h" 00012 #include "iException.h" 00013 #include "Filename.h" 00014 00015 using namespace std; 00016 00017 #define MAX(x,y) (((x) > (y)) ? (x) : (y)) 00018 namespace Isis { 00035 AtmosModel::AtmosModel (Pvl &pvl, PhotoModel &pmodel) { 00036 p_atmosAlgorithmName = "Unknown"; 00037 p_atmosPM = &pmodel; 00038 00039 p_atmosIncTable.resize(91); 00040 p_atmosAhTable.resize(91); 00041 p_atmosHahgtTable.resize(91); 00042 p_atmosHahgt0Table.resize(91); 00043 p_atmosAb = 0.0; 00044 p_atmosCosphi = 0.0; 00045 p_atmosAtmSwitch = 0; 00046 p_atmosEulgam = 0.5772156; 00047 p_atmosHahgsb = 0.0; 00048 p_atmosHga = 0.68; 00049 p_atmosInc = 0.0; 00050 p_atmosMunot = 0.0; 00051 p_atmosNinc = 91; 00052 p_atmosPhi = 0.0; 00053 p_atmosSini = 0.0; 00054 p_atmosTau = 0.28; 00055 p_atmosTauref = 0.0; 00056 p_atmosTauold = -1.0; 00057 p_atmosWha = 0.95; 00058 p_atmosWhaold = 1.0e30; 00059 p_atmosBha = 0.85; 00060 p_pstd = 0.0; 00061 p_sbar = 0.0; 00062 p_trans = 0.0; 00063 p_trans0 = 0.0; 00064 p_standardConditions = false; 00065 00066 PvlGroup &algorithm = pvl.FindObject("AtmosphericModel").FindGroup("Algorithm",Pvl::Traverse); 00067 00068 if (algorithm.HasKeyword("Nulneg")) { 00069 SetAtmosNulneg( ((string)algorithm["Nulneg"]).compare("YES") == 0 ); 00070 } 00071 else { 00072 p_atmosNulneg = false; 00073 } 00074 00075 if (algorithm.HasKeyword("Tau")) { 00076 SetAtmosTau( algorithm["Tau"] ); 00077 } 00078 p_atmosTausave = p_atmosTau; 00079 00080 if (algorithm.HasKeyword("Tauref")) { 00081 SetAtmosTauref( algorithm["Tauref"] ); 00082 } 00083 00084 if (algorithm.HasKeyword("Wha")) { 00085 SetAtmosWha( algorithm["Wha"] ); 00086 } 00087 p_atmosWhasave = p_atmosWha; 00088 00089 if (algorithm.HasKeyword("Wharef")) { 00090 SetAtmosWharef( algorithm["Wharef"] ); 00091 } else { 00092 p_atmosWharef = p_atmosWha; 00093 } 00094 00095 if (algorithm.HasKeyword("Hga")) { 00096 SetAtmosHga( algorithm["Hga"] ); 00097 } 00098 p_atmosHgasave = p_atmosHga; 00099 00100 if (algorithm.HasKeyword("Hgaref")) { 00101 SetAtmosHgaref( algorithm["Hgaref"] ); 00102 } else { 00103 p_atmosHgaref = p_atmosHga; 00104 } 00105 00106 if (algorithm.HasKeyword("Bha")) { 00107 SetAtmosBha( algorithm["Bha"] ); 00108 } 00109 p_atmosBhasave = p_atmosBha; 00110 00111 if (algorithm.HasKeyword("Bharef")) { 00112 SetAtmosBharef( algorithm["Bharef"] ); 00113 } else { 00114 p_atmosBharef = p_atmosBha; 00115 } 00116 00117 if (algorithm.HasKeyword("Inc")) { 00118 SetAtmosInc( algorithm["Inc"] ); 00119 } 00120 00121 if (algorithm.HasKeyword("Phi")) { 00122 SetAtmosPhi( algorithm["Phi"] ); 00123 } 00124 } 00125 00126 00146 double AtmosModel::G11Prime(double tau) { 00147 double sum; 00148 int icnt; 00149 double fac; 00150 double term; 00151 double tol; 00152 double fi; 00153 double elog; 00154 double eulgam; 00155 double e1_2; 00156 double result; 00157 00158 tol = 1.0e-6; 00159 eulgam = 0.5772156; 00160 00161 sum = 0.0; 00162 icnt = 1; 00163 fac = -tau; 00164 term = fac; 00165 while (fabs(term) > fabs(sum)*tol) { 00166 sum = sum + term; 00167 icnt = icnt + 1; 00168 fi = (double) icnt; 00169 fac = fac * (-tau) / fi; 00170 term = fac / (fi * fi); 00171 } 00172 elog = log(MAX(1.0e-30,tau)) + eulgam; 00173 e1_2 = sum + PI * PI / 12.0 + 0.5 * 00174 pow(elog,2.0); 00175 result = 2.0 * (AtmosModel::En(1,tau) 00176 + elog * AtmosModel::En(2,tau) 00177 - tau * e1_2); 00178 return(result); 00179 } 00180 00222 double AtmosModel::Ei (double x) throw (iException &){ 00223 //static double r8ei(double x) in original NumericalMethods class 00224 // This method was derived from an algorithm in the text 00225 // Numerical Recipes in C: The Art of Scientific Computing 00226 // Section 6.3 by Flannery, Press, Teukolsky, and Vetterling 00227 double result; 00228 double fpmin; // close to smallest representable floating-point number 00229 double euler; // Euler's constant, lower-case gamma 00230 double epsilon; // desired relative error (tolerance) 00231 int maxit; // maximum number of iterations allowed 00232 double sum,fact,term,prev; 00233 00234 fpmin = 1.0e-30; 00235 maxit = 100; 00236 epsilon = 6.0e-8; 00237 euler = 0.57721566; 00238 00239 if (x <= 0.0) { 00240 throw iException::Message(iException::Programmer, 00241 "AtmosModel::Ei() - Invalid argument. Definition requires x > 0.0. Entered x = " 00242 + iString(x), 00243 _FILEINFO_); 00244 } 00245 if (x < fpmin) { //special case: avoid failure of convergence test because underflow 00246 result = log(x) + euler; 00247 } 00248 else if (x <= -log(epsilon)) { //Use power series 00249 sum = 0.0; 00250 fact = 1.0; 00251 for (int k=1; k<=maxit; k++) { 00252 fact = fact * x / k; 00253 term = fact / k; 00254 sum = sum + term; 00255 if (term < epsilon*sum) { 00256 result = sum + log(x) + euler; 00257 return(result); 00258 } 00259 } 00260 throw iException::Message(iException::Math, 00261 "AtmosModel::Ei() - Power series failed to converge in " 00262 + iString(maxit) + " iterations. Unable to calculate exponential integral.", 00263 _FILEINFO_); 00264 } 00265 else { // Use asymptotic series 00266 sum = 0.0; 00267 term = 1.0; 00268 for (int k=1; k<=maxit; k++) { 00269 prev = term; 00270 term = term * k / x; 00271 if (term < epsilon) { 00272 result = exp(x) * (1.0 + sum) / x; 00273 return(result); 00274 } 00275 else { 00276 if (term < prev) { // still converging: add new term 00277 sum = sum + term; 00278 } 00279 else { // diverging: subtract previous term and exit 00280 sum = sum - prev; 00281 result = exp(x) * (1.0 + sum) / x; 00282 return(result); 00283 } 00284 } 00285 } 00286 result = exp(x) * (1.0 + sum) / x; 00287 } 00288 return(result); 00289 } 00290 00363 double AtmosModel::En (unsigned int n, double x) throw (iException &){ 00364 //static double r8expint(int n, double x) in original NumericalMethods class 00365 // This method was derived from an algorithm in the text 00366 // Numerical Recipes in C: The Art of Scientific Computing 00367 // Section 6.3 by Flannery, Press, Teukolsky, and Vetterling 00368 int nm1; 00369 double result; 00370 double a,b,c,d,h; 00371 double delta; 00372 double fact; 00373 double psi; 00374 double fpmin; // close to smallest representable floating-point number 00375 double maxit; // maximum number of iterations allowed 00376 double epsilon; // desired relative error (tolerance) 00377 double euler; // Euler's constant, gamma 00378 00379 fpmin = 1.0e-30; 00380 maxit = 100; 00381 epsilon = 1.0e-7; 00382 euler = 0.5772156649; 00383 00384 nm1 = n - 1; 00385 if (n < 0 || x < 0.0 || (x == 0.0 && (n == 0 || n == 1))) { 00386 iString msg = "AtmosModel::En() - Invalid arguments. "; 00387 msg+= "Definition requires (x > 0.0 and n >=0 ) or (x >= 0.0 and n > 1). "; 00388 msg+= "Entered x = " + iString(x) + " and n = " + iString((int) n); 00389 throw iException::Message(iException::Programmer,msg, 00390 _FILEINFO_); 00391 } 00392 else if (n == 0) { // special case, this implies x > 0 by logic above 00393 result = exp(-x) / x; 00394 } 00395 else if (x == 0.0) { // special case, this implies n > 1 00396 result = 1.0 / nm1; 00397 } 00398 else if (x > 1.0) { // Lentz's algorithm, this implies n > 0 00399 b = x + n; 00400 c = 1.0 / fpmin; 00401 d = 1.0 / b; 00402 h = d; 00403 for (int i=1; i<=maxit; i++) { 00404 a = -i * (nm1 + i); 00405 b = b + 2.0; 00406 d = 1.0 / (a * d + b); 00407 c = b + a / c; 00408 delta = c * d; 00409 h = h * delta; 00410 if (fabs(delta-1.0) < epsilon) { 00411 result = h * exp(-x); 00412 return(result); 00413 } 00414 } 00415 throw iException::Message(iException::Math, 00416 "AtmosModel::En() - Continued fraction failed to converge in " 00417 + iString(maxit) + " iterations. Unable to calculate exponential integral.", 00418 _FILEINFO_); 00419 } 00420 else { // evaluate series 00421 if (nm1 != 0) { 00422 result = 1.0 / nm1; 00423 } 00424 else { 00425 result = -log(x) - euler; 00426 } 00427 fact = 1.0; 00428 for (int i=1; i<=maxit; i++) { 00429 fact = -fact * x / i; 00430 if (i != nm1) { 00431 delta = -fact / (i - nm1); 00432 } 00433 else { 00434 psi = -euler; 00435 for (int ii=1; ii<=nm1; ii++) { 00436 psi = psi + 1.0 / ii; 00437 } 00438 delta = fact * (-log(x) + psi); 00439 } 00440 result = result + delta; 00441 if (fabs(delta) < fabs(result)*epsilon) { 00442 return(result); 00443 } 00444 } 00445 throw iException::Message(iException::Math, 00446 "AtmosModel::En() - Series representation failed to converge in " 00447 + iString(maxit) + " iterations. Unable to calculate exponential integral.", 00448 _FILEINFO_); 00449 } 00450 return(result); 00451 } 00452 00469 void AtmosModel::CalcAtmEffect(double pha, double inc, double ema, 00470 double *pstd, double *trans, double *trans0, double *sbar) { 00471 00472 // Check validity of photometric angles 00473 //if (pha > 180.0 || inc > 90.0 || ema > 90.0 || pha < 0.0 || 00474 // inc < 0.0 || ema < 0.0) { 00475 // string msg = "Invalid photometric angles"; 00476 // throw iException::Message(iException::Programmer,msg,_FILEINFO_); 00477 //} 00478 00479 // Apply atmospheric function 00480 AtmosModelAlgorithm(pha,inc,ema); 00481 *pstd = p_pstd; 00482 *trans = p_trans; 00483 *trans0 = p_trans0; 00484 *sbar = p_sbar; 00485 } 00486 00490 void AtmosModel::SetStandardConditions(bool standard) { 00491 p_standardConditions = standard; 00492 if (p_standardConditions) { 00493 p_atmosBhasave = p_atmosBha; 00494 p_atmosBha = p_atmosBharef; 00495 p_atmosHgasave = p_atmosHga; 00496 p_atmosHga = p_atmosHgaref; 00497 p_atmosTausave = p_atmosTau; 00498 p_atmosTau = p_atmosTauref; 00499 p_atmosWhasave = p_atmosWha; 00500 p_atmosWha = p_atmosWharef; 00501 } else { 00502 p_atmosBha = p_atmosBhasave; 00503 p_atmosHga = p_atmosHgasave; 00504 p_atmosTau = p_atmosTausave; 00505 p_atmosWha = p_atmosWhasave; 00506 } 00507 } 00508 00537 void AtmosModel::GenerateAhTable() { 00538 int inccnt; //for loop incidence angle count, 1:ninc 00539 double fun_temp;//temporary variable for integral 00540 double factor; //factor for integration: 1 except at ends of interval where it's 1/2 00541 double yp1,yp2; //first derivatives of first and last x values of @a p_atmosIncTable 00542 00543 NumericalAtmosApprox::IntegFunc sub; 00544 sub = NumericalAtmosApprox::OuterFunction; 00545 00546 p_atmosAb = 0.0; 00547 00548 //Create NumericalAtmosApprox object here for RombergsMethod used in for loop 00549 NumericalAtmosApprox qromb; 00550 00551 for (inccnt=0; inccnt<p_atmosNinc; inccnt++) { 00552 p_atmosInc = (double) inccnt; 00553 p_atmosIncTable[inccnt] = p_atmosInc; 00554 p_atmosMunot = cos((PI/180.0)*p_atmosInc); 00555 p_atmosSini = sin((PI/180.0)*p_atmosInc); 00556 00557 iString phtName = p_atmosPM->AlgorithmName(); 00558 phtName.UpCase(); 00559 if (p_atmosInc == 90.0) { 00560 p_atmosAhTable[inccnt] = 0.0; 00561 } 00562 else if (phtName == "LAMBERT") { 00563 p_atmosAhTable[inccnt] = 1.0; 00564 } 00565 else if (phtName == "LOMMELSEELIGER") { 00566 p_atmosAhTable[inccnt] = 2.0 * log((1.0/p_atmosMunot)/p_atmosMunot); 00567 } 00568 else if (phtName == "MINNAERT") { 00569 p_atmosAhTable[inccnt] = (pow(p_atmosMunot,((Minnaert*)p_atmosPM)->PhotoK()))/((Minnaert*)p_atmosPM)->PhotoK(); 00570 } 00571 else if (phtName == "LUNARLAMBERT") { 00572 p_atmosAhTable[inccnt] = 2.0 * ((LunarLambert*)p_atmosPM)->PhotoL() * 00573 log((1.0+p_atmosMunot)/p_atmosMunot) + 1.0 - 00574 ((LunarLambert*)p_atmosPM)->PhotoL(); 00575 } 00576 else { 00577 // numerically integrate the other photometric models 00578 // outer integral is over phi from 0 to pi rad = 180 deg 00579 p_atmosAtmSwitch = 0; 00580 // integrate AtmosModel function from 0 to 180 00581 fun_temp = qromb.RombergsMethod(this,sub,0,180); 00582 // the correct normalization with phi in degrees is: 00583 p_atmosAhTable[inccnt] = fun_temp / (90.0*p_atmosMunot); 00584 } 00585 // Let's get our estimate of Ab by integrating (summing) 00586 // A (i)sinicosi over our A table 00587 if ((inccnt == 0) || (inccnt == p_atmosNinc-1)) { 00588 factor = 0.5; 00589 } 00590 else { 00591 factor = 1.0; 00592 } 00593 00594 p_atmosAb = p_atmosAb + p_atmosAhTable[inccnt] * p_atmosMunot * p_atmosSini * factor; 00595 } 00596 00597 factor = 2.0 * PI/180.0; 00598 p_atmosAb = p_atmosAb * factor; 00599 00600 // set up clamped cubic spline 00601 yp1 = 1.0e30; 00602 yp2 = 1.0e30; 00603 p_atmosAhSpline.Reset(); 00604 p_atmosAhSpline.SetInterpType(NumericalApproximation::CubicClamped); 00605 p_atmosAhSpline.AddData(p_atmosIncTable,p_atmosAhTable); 00606 p_atmosAhSpline.SetCubicClampedEndptDeriv(yp1,yp2); 00607 } 00608 00637 void AtmosModel::GenerateHahgTables() { 00638 int inccnt; // for loop incidence angle count,1:ninc 00639 double fun_temp; // temporary variable for integral 00640 double hahgsb_temp; // save increment to hahgsb 00641 double factor; // factor for integration: 1 except at ends of interval where it's 1/2 00642 double yp1,yp2; // derivatives of endpoints of data set 00643 00644 NumericalAtmosApprox::IntegFunc sub; 00645 sub = NumericalAtmosApprox::OuterFunction; 00646 00647 p_atmosHahgsb = 0.0; 00648 NumericalAtmosApprox qromb; 00649 00650 for (inccnt = 0; inccnt < p_atmosNinc; inccnt++) { 00651 p_atmosInc = (double) inccnt; 00652 p_atmosIncTable[inccnt] = p_atmosInc; 00653 p_atmosMunot = cos((PI/180.0)*p_atmosInc); 00654 p_atmosSini = sin((PI/180.0)*p_atmosInc); 00655 00656 p_atmosAtmSwitch = 1; 00657 00658 qromb.Reset(); 00659 fun_temp = qromb.RombergsMethod(this,sub,0,180); 00660 00661 p_atmosHahgtTable[inccnt] = fun_temp * AtmosWha() / 360.0; 00662 p_atmosAtmSwitch = 2; 00663 00664 fun_temp = qromb.RombergsMethod(this,sub,0,180); 00665 00666 hahgsb_temp = fun_temp * AtmosWha() / 360.0; 00667 00668 if ((inccnt == 0) || (inccnt == p_atmosNinc-1)) { 00669 factor = 0.5; 00670 } 00671 else { 00672 factor = 1.0; 00673 } 00674 00675 p_atmosHahgsb = p_atmosHahgsb + p_atmosSini * factor * hahgsb_temp; 00676 if (p_atmosInc == 0.0) { 00677 p_atmosHahgt0Table[inccnt] = 0.0; 00678 } 00679 else { 00680 p_atmosAtmSwitch = 3; 00681 fun_temp = qromb.RombergsMethod(this,sub,0,180); 00682 p_atmosHahgt0Table[inccnt] = fun_temp * AtmosWha() * p_atmosMunot / (360.0 * p_atmosSini); 00683 } 00684 } 00685 00686 factor = 2.0 * PI/180.0; 00687 p_atmosHahgsb = p_atmosHahgsb * factor; 00688 00689 // set up clamped cubic splines 00690 yp1 = 1.0e30; 00691 yp2 = 1.0e30; 00692 p_atmosHahgtSpline.Reset(); 00693 p_atmosHahgtSpline.SetInterpType(NumericalApproximation::CubicClamped); 00694 p_atmosHahgtSpline.AddData(p_atmosIncTable,p_atmosHahgtTable); 00695 p_atmosHahgtSpline.SetCubicClampedEndptDeriv(yp1,yp2); 00696 00697 p_atmosHahgt0Spline.Reset(); 00698 p_atmosHahgt0Spline.SetInterpType(NumericalApproximation::CubicClamped); 00699 p_atmosHahgt0Spline.AddData(p_atmosIncTable,p_atmosHahgt0Table); 00700 p_atmosHahgt0Spline.SetCubicClampedEndptDeriv(yp1,yp2); 00701 } 00702 00714 void AtmosModel::SetAtmosAtmSwitch (const int atmswitch) { 00715 if (atmswitch < 0 || atmswitch > 3) { 00716 string msg = "Invalid value of atmospheric atmswitch [" + iString(atmswitch) + "]"; 00717 throw iException::Message(iException::User,msg,_FILEINFO_); 00718 } 00719 00720 p_atmosAtmSwitch = atmswitch; 00721 } 00722 00734 void AtmosModel::SetAtmosBha (const double bha) { 00735 if (bha < -1.0 || bha > 1.0) { 00736 string msg = "Invalid value of Anisotropic atmospheric bha [" + 00737 iString(bha) + "]"; 00738 throw iException::Message(iException::User,msg,_FILEINFO_); 00739 } 00740 00741 p_atmosBha = bha; 00742 } 00743 00755 void AtmosModel::SetAtmosBharef (const double bharef) { 00756 if (bharef < -1.0 || bharef > 1.0) { 00757 string msg = "Invalid value of Atmospheric bharef [" + iString(bharef) + "]"; 00758 throw iException::Message(iException::User,msg,_FILEINFO_); 00759 } 00760 00761 p_atmosBharef = bharef; 00762 } 00763 00775 void AtmosModel::SetAtmosHga (const double hga) { 00776 if (hga <= -1.0 || hga >= 1.0) { 00777 string msg = "Invalid value of Hapke atmospheric hga [" + iString(hga) + "]"; 00778 throw iException::Message(iException::User,msg,_FILEINFO_); 00779 } 00780 00781 p_atmosHga = hga; 00782 } 00783 00795 void AtmosModel::SetAtmosHgaref (const double hgaref) { 00796 if (hgaref <= -1.0 || hgaref >= 1.0) { 00797 string msg = "Invalid value of Atmospheric hgaref [" + iString(hgaref) + "]"; 00798 throw iException::Message(iException::User,msg,_FILEINFO_); 00799 } 00800 00801 p_atmosHgaref = hgaref; 00802 } 00803 00814 void AtmosModel::SetAtmosInc (const double inc) { 00815 if (inc < 0.0 || inc > 90.0) { 00816 string msg = "Invalid value of atmospheric inc [" + iString(inc) + "]"; 00817 throw iException::Message(iException::User,msg,_FILEINFO_); 00818 } 00819 00820 p_atmosInc = inc; 00821 p_atmosMunot = cos(inc*PI/180.0); 00822 p_atmosSini = sin(inc*PI/180.0); 00823 } 00824 00836 void AtmosModel::SetAtmosNulneg (const string nulneg) { 00837 iString temp(nulneg); 00838 temp = temp.UpCase(); 00839 00840 if (temp != "NO" && temp != "YES") { 00841 string msg = "Invalid value of Atmospheric nulneg [" + temp + "]"; 00842 throw iException::Message(iException::User,msg,_FILEINFO_); 00843 } 00844 00845 SetAtmosNulneg( temp.compare("YES") == 0 ); 00846 } 00847 00859 void AtmosModel::SetAtmosPhi (const double phi) { 00860 if (phi < 0.0 || phi > 360.0) { 00861 string msg = "Invalid value of atmospheric phi [" + iString(phi) + "]"; 00862 throw iException::Message(iException::User,msg,_FILEINFO_); 00863 } 00864 00865 p_atmosPhi = phi; 00866 p_atmosCosphi = cos(phi*PI/180.0); 00867 } 00868 00878 void AtmosModel::SetAtmosTau (const double tau) { 00879 if (tau < 0.0) { 00880 string msg = "Invalid value of Atmospheric tau [" + iString(tau) + "]"; 00881 throw iException::Message(iException::User,msg,_FILEINFO_); 00882 } 00883 00884 p_atmosTau = tau; 00885 } 00886 00897 void AtmosModel::SetAtmosTauref (const double tauref) { 00898 if (tauref < 0.0) { 00899 string msg = "Invalid value of Atmospheric tauref [" + iString(tauref) + "]"; 00900 throw iException::Message(iException::User,msg,_FILEINFO_); 00901 } 00902 00903 p_atmosTauref = tauref; 00904 } 00905 00916 void AtmosModel::SetAtmosWha (const double wha) { 00917 if (wha <= 0.0 || wha > 1.0) { 00918 string msg = "Invalid value of Atmospheric wha [" + iString(wha) + "]"; 00919 throw iException::Message(iException::User,msg,_FILEINFO_); 00920 } 00921 00922 p_atmosWha = wha; 00923 } 00924 00935 void AtmosModel::SetAtmosWharef (const double wharef) { 00936 if (wharef <= 0.0 || wharef > 1.0) { 00937 string msg = "Invalid value of Atmospheric wharef [" + iString(wharef) + "]"; 00938 throw iException::Message(iException::User,msg,_FILEINFO_); 00939 } 00940 00941 p_atmosWharef = wharef; 00942 } 00943 00948 bool AtmosModel::TauOrWhaChanged() const { 00949 return (AtmosTau() != p_atmosTauold) || (AtmosWha() != p_atmosWhaold); 00950 } 00951 }