Main Page   Class Hierarchy   Alphabetical List   Compound List   File List   Compound Members   File Members  

num_integrate.h

Go to the documentation of this file.
00001 // SuperMix version 1.0  C++ source file
00002 //
00003 // Copyright (c) 1999 California Institute of Technology.
00004 // All rights reserved.
00005 //
00006 // Redistribution and use in source and binary forms for noncommercial
00007 // purposes are permitted provided that the above copyright notice and
00008 // this paragraph are duplicated in all such forms and that any
00009 // documentation and other materials related to such distribution and
00010 // use acknowledge that the software was developed by California
00011 // Institute of Technology. Redistribution and/or use in source or
00012 // binary forms is not permitted for any commercial purpose. Use of
00013 // this software does not include a permitted use of the Institute's
00014 // name or trademark for any purpose.
00015 //
00016 // DISCLAIMER:
00017 // THIS SOFTWARE AND/OR RELATED MATERIALS ARE PROVIDED "AS-IS" WITHOUT
00018 // WARRANTY OF ANY KIND INCLUDING ANY WARRANTIES OF PERFORMANCE OR
00019 // MERCHANTABILITY OR FITNESS FOR A PARTICULAR USE OR PURPOSE (AS SET
00020 // FORTH IN UCC 23212-2313) OR FOR ANY PURPOSE WHATSOEVER, FOR THE
00021 // LICENSED PRODUCT, HOWEVER USED.  IN NO EVENT SHALL CALTECH/JPL BE
00022 // LIABLE FOR ANY DAMAGES AND/OR COSTS, INCLUDING BUT NOT LIMITED TO
00023 // INCIDENTAL OR CONSEQUENTIAL DAMAGES OF ANY KIND, INCLUDING ECONOMIC
00024 // DAMAGE OR INJURY TO PROPERTY AND LOST PROFITS, REGARDLESS OF
00025 // WHETHER CALTECH/JPL SHALL BE ADVISED, HAVE REASON TO KNOW, OR IN
00026 // FACT SHALL KNOW OF THE POSSIBILITY.  THE USER BEARS ALL RISK
00027 // RELATING TO QUALITY AND PERFORMANCE OF THE SOFTWARE AND/OR RELATED
00028 // MATERIALS.
00029 //
00030 //
00031 // F. Rice 4/9/99
00032 //
00033 // 6/14/00: Changed the arguments in calls to romberg_closed() and
00034 //          romberg_open() to be more ANSI C++ compliant.
00035 //
00036 // ********************************************************************
00037 
00038 #include "error.h"
00039 
00040 // ********************************************************************
00041 // Definitions of functions in class integrator<>
00042 
00043 // The constructor definition:
00044 template <class Y>
00045 inline integrator<Y>::integrator(integrator<Y>::type t_) :
00046   order(4),
00047   recursion_limit(20),
00048   abs_tolerance(1.0e-100),
00049   rel_tolerance(1.0e-6),
00050   t(t_)
00051 { }
00052 
00053 
00054 // --------------------------------------------------------------------
00055 // integration router: operator()
00056 
00057 
00058 #ifdef __GNUG__
00059 #if __GNUC_MINOR__ < 95
00060 
00061 // We need to keep this version of operator() to maintain compatibility
00062 // with G++ ver 2.8.1
00063 
00064 template <class Y> template <class F, class N>
00065 inline Y integrator<Y>::operator()(F f, double a, double b, N norm)
00066 {
00067   // s will be set true if limits are swapped by qswap. 
00068   int s = 0;
00069 
00070   // integrate using the appropriate method:
00071   switch(t) {
00072   default:
00073   case CLOSED:
00074     s = qswap(a,b); romberg_closed(f,a,b,norm,&cl<F>); break;
00075   case OPEN:
00076     s = qswap(a,b); romberg_open(f,a,b,norm,&op<F>); break;
00077   case SQRTLOWER:
00078     s = qswap(a,b);
00079     if(s) romberg_open(f,a,b,norm,&su<F>);
00080     else  romberg_open(f,a,b,norm,&sl<F>);
00081     break;
00082   case SQRTUPPER:
00083     s = qswap(a,b);
00084     if(s) romberg_open(f,a,b,norm,&sl<F>);
00085     else  romberg_open(f,a,b,norm,&su<F>);
00086     break;
00087   case INFINITE:
00088     s = qswap(a,b); romberg_open(f,a,b,norm,&in<F>); break;
00089   case EXP:
00090     romberg_open(f,a,b,norm,&ex<F>); break;
00091   }
00092 
00093   // and return the result:
00094   if (s) result.first *= -1.0;    // flip sign if limits were swapped
00095   return result.first;
00096 }
00097 
00098 #define OPERATOR_PARENTHESIS_DEFINED
00099 #endif
00100 #endif
00101 
00102 #ifndef OPERATOR_PARENTHESIS_DEFINED
00103 
00104 // This is the ANSI C++ version (look at the calls in the switch)
00105 
00106 template <class Y> template <class F, class N>
00107 inline Y integrator<Y>::operator()(F f, double a, double b, N norm)
00108 {
00109   // s will be set true if limits are swapped by qswap. 
00110   int s = 0;
00111 
00112   // integrate using the appropriate method:
00113   switch(t) {
00114   default:
00115   case CLOSED:
00116     s = qswap(a,b); romberg_closed(f,a,b,norm,&integrator<Y>::cl); break;
00117   case OPEN:
00118     s = qswap(a,b); romberg_open(f,a,b,norm,&integrator<Y>::op); break;
00119   case SQRTLOWER:
00120     s = qswap(a,b);
00121     if(s) romberg_open(f,a,b,norm,&integrator<Y>::su);
00122     else  romberg_open(f,a,b,norm,&integrator<Y>::sl);
00123     break;
00124   case SQRTUPPER:
00125     s = qswap(a,b);
00126     if(s) romberg_open(f,a,b,norm,&integrator<Y>::sl);
00127     else  romberg_open(f,a,b,norm,&integrator<Y>::su);
00128     break;
00129   case INFINITE:
00130     s = qswap(a,b); romberg_open(f,a,b,norm,&integrator<Y>::in); break;
00131   case EXP:
00132     romberg_open(f,a,b,norm,&integrator<Y>::ex); break;
00133   }
00134 
00135   // and return the result:
00136   if (s) result.first *= -1.0;    // flip sign if limits were swapped
00137   return result.first;
00138 }
00139 
00140 #endif
00141 
00142 // --------------------------------------------------------------------
00143 // romberg extrapolation routines: romberg_closed() and romberg_open()
00144 
00145 template <class Y> template <class F, class N>
00146 inline void integrator<Y>::romberg_closed(F f, double a, double b, N norm,
00147                           void (integrator<Y>::*method)(F,double,double,int))
00148 {
00149   double abs_lim = abs_tolerance*abs_tolerance;  // since norm() = magnitude^2
00150   double rel_lim = rel_tolerance*rel_tolerance;
00151   double normy, dy;
00152 
00153   s.erase(s.begin(),s.end());
00154   s.push_back(entry(1.0,Y()));  // s[0] is the only element of s here
00155 
00156   unsigned i = 0;
00157   while ( i < recursion_limit) {
00158     (this->*method)(f,a,b,i);   // method result returned in s[i].second
00159     if(i >= order) {
00160       // enough points to extrapolate
00161       result = p(0.0, s.begin()+(i-order), order+1);
00162 
00163       // check error estimate against tolerances
00164       normy = norm(result.first);
00165       dy = norm(result.second);
00166       if (dy <=  abs_lim || (normy != 0.0 && dy/normy <= rel_lim)) return;
00167     }
00168 
00169     // prepare for next iteration:
00170     s.push_back(s[i++]);  // here's where we increment i
00171     s[i].first *= 0.25;   // stepsize decreases by a factor of 2; 2^2 = 4.
00172   }
00173 
00174   // exiting the loop normally means we didn't achieve accuracy goal
00175   error::warning("Recursion limit reached in integrator.");
00176 }
00177 
00178 
00179 template <class Y> template <class F, class N>
00180 inline void integrator<Y>::romberg_open(F f, double a, double b, N norm,
00181                           void (integrator<Y>::*method)(F,double,double,int))
00182 {
00183   double abs_lim = abs_tolerance*abs_tolerance;  // since norm() = magnitude^2
00184   double rel_lim = rel_tolerance*rel_tolerance;
00185   double normy, dy;
00186 
00187   s.erase(s.begin(),s.end());
00188   s.push_back(entry(1.0,Y()));  // s[0] is the only element of s here
00189 
00190   unsigned i = 0;
00191   while ( i < recursion_limit) {
00192     (this->*method)(f,a,b,i);   // method result returned in s[i].second
00193     if(i >= order) {
00194       // enough points to extrapolate
00195       result = p(0.0, s.begin()+(i-order), order+1);
00196 
00197       // check error estimate against tolerances
00198       normy = norm(result.first);
00199       dy = norm(result.second);
00200       if (dy <=  abs_lim || (normy != 0.0 && dy/normy <= rel_lim)) return;
00201     }
00202 
00203     // prepare for next iteration:
00204     s.push_back(s[i++]);  // here's where we increment i
00205     s[i].first /= 9.0;   // stepsize decreases by a factor of 2; 2^2 = 4.
00206   }
00207 
00208   // exiting the loop normally means we didn't achieve accuracy goal
00209   error::warning("Recursion limit reached in integrator.");
00210 }
00211 
00212 
00213 // --------------------------------------------------------------------
00214 // integration method routines: cl(), op(), etc.
00215 
00216 template <class Y> template <class F>
00217 inline void integrator<Y>::cl(F f, double a, double b, int step)
00218 {
00219   Y & sum = s[step].second;  // the result
00220   Y & s   = result.first;    // use to hold an intermediate
00221   if(step) {
00222     // refine the previous result, which is in sum
00223     int n = 1;             // the number of points to be added
00224     for(int j = 1; j < step; ++j) n <<= 1;  // n == 2^(step-1);
00225     double h = (b-a)/n;    // the interval between the new points (skipping old)
00226     double x = a+0.5*h;    // x coordinate of new points
00227     s = f(x);              // will hold the sum of y's at new points
00228     while((x += h) < b) s += f(x);
00229     sum += (s *= h);       // add in the new points, scaled by interval
00230     sum *= 0.5;            // interval between all points is h/2
00231   }
00232   else {
00233     // step == 0, so this is the first iteration
00234     sum = f(a); sum += f(b); sum *= 0.5*(b-a);
00235   }
00236 }  
00237 
00238 
00239 template <class Y> template <class F>
00240 inline void integrator<Y>::op(F f, double a, double b, int step)
00241 {
00242   Y & sum = s[step].second;  // the result
00243   Y & s   = result.first;    // use to hold an intermediate
00244   if(step) {
00245     // refine the previous result, which is in sum
00246     int n = 3;             // the number of points to be added
00247     for(int j = 1; j < step; ++j) n *= 3;  // n == 3^(step);
00248     double h = (b-a)/n;    // the interval between adjacent new points
00249     double h2 = 2.0*h;     // skips an existing (old) point
00250     double x = a+0.5*h;    // x coordinate of new points
00251     s = f(x);              // will hold the sum of y's at new points
00252     x += h2; s += f(x);    // skip old left-most point and add another
00253     while((x += h) < b) { 
00254       s += f(x);
00255       x += h2;   // skip old point
00256       s += f(x);
00257     }
00258     sum /= 3.0; sum += (s *= h);
00259   }
00260   else {
00261     // step == 0, so this is the first iteration
00262     sum = f(0.5*(a+b)); sum *= (b-a);
00263   }
00264 }
00265 
00266 
00267 template <class Y> template <class F>
00268 inline void integrator<Y>::sl(F f, double a, double b, int step)
00269 {
00270   // change limits for the variable change x -> a+x*x
00271   b = sqrt(b-a);  // assumed lower limit is 0 in code below
00272 
00273   // same algorithm as for op(), only use Func.sql(f,x,a) instead of f(x)
00274   Y & sum = s[step].second;  // the result
00275   Y & s   = result.first;    // use to hold an intermediate
00276   if(step) {
00277     // refine the previous result, which is in sum
00278     int n = 3;             // the number of points to be added
00279     for(int j = 1; j < step; ++j) n *= 3;  // n == 3^(step);
00280     double h = b/n;        // the interval between adjacent new points
00281     double h2 = 2.0*h;     // skips an existing (old) point
00282     double x = 0.5*h;      // x coordinate of new points
00283     s = Func.sql(f,x,a);            // will hold the sum of y's at new points
00284     x += h2; s += Func.sql(f,x,a);  // skip old left-most point and add another
00285     while((x += h) < b) { 
00286       s += Func.sql(f,x,a);
00287       x += h2;   // skip old point
00288       s += Func.sql(f,x,a);
00289     }
00290     sum /= 3.0; sum += (s *= h);
00291   }
00292   else {
00293     // step == 0, so this is the first iteration
00294     sum = Func.sql(f, 0.5*b, a); sum *= b;
00295   }
00296 }
00297 
00298 
00299 template <class Y> template <class F>
00300 inline void integrator<Y>::su(F f, double a, double b, int step)
00301 {
00302   // change limits for the variable change x -> b-x*x
00303   a = sqrt(b-a);  // a is now UPPER limit; lower limit is 0 in code below
00304 
00305   // same algorithm as for op(), only use Func.squ(f,x,b) instead of f(x)
00306   Y & sum = s[step].second;  // the result
00307   Y & s   = result.first;    // use to hold an intermediate
00308   if(step) {
00309     // refine the previous result, which is in sum
00310     int n = 3;             // the number of points to be added
00311     for(int j = 1; j < step; ++j) n *= 3;  // n == 3^(step);
00312     double h = a/n;        // the interval between adjacent new points
00313     double h2 = 2.0*h;     // skips an existing (old) point
00314     double x = 0.5*h;      // x coordinate of new points
00315     s = Func.squ(f,x,b);            // will hold the sum of y's at new points
00316     x += h2; s += Func.squ(f,x,b);  // skip old left-most point and add another
00317     while((x += h) < a) { 
00318       s += Func.squ(f,x,b);
00319       x += h2;   // skip old point
00320       s += Func.squ(f,x,b);
00321     }
00322     sum /= 3.0; sum += (s *= h);
00323   }
00324   else {
00325     // step == 0, so this is the first iteration
00326     sum = Func.squ(f, 0.5*a, b); sum *= a;
00327   }
00328 }
00329 
00330 
00331 template <class Y> template <class F>
00332 inline void integrator<Y>::in(F f, double a, double b, int step)
00333 {
00334   if (a*b <= 0.0)
00335     error::fatal("integrator: integration limits must have the same sign"
00336                  " when using method INFINITE.");
00337 
00338   // change limits to their inverses, for change of variable x -> 1/x:
00339   double temp = 1.0/b; b = 1.0/a; a = temp;
00340 
00341   // same algorithm as for op(), only use Func.inv(f,x) instead of f(x)
00342   Y & sum = s[step].second;  // the result
00343   Y & s   = result.first;    // use to hold an intermediate
00344   if(step) {
00345     // refine the previous result, which is in sum
00346     int n = 3;             // the number of points to be added
00347     for(int j = 1; j < step; ++j) n *= 3;  // n == 3^(step);
00348     double h = (b-a)/n;    // the interval between adjacent new points
00349     double h2 = 2.0*h;     // skips an existing (old) point
00350     double x = a+0.5*h;    // x coordinate of new points
00351     s = Func.inv(f,x);            // will hold the sum of y's at new points
00352     x += h2; s += Func.inv(f,x);  // skip old left-most point and add another
00353     while((x += h) < b) { 
00354       s += Func.inv(f,x);
00355       x += h2;   // skip old point
00356       s += Func.inv(f,x);
00357     }
00358     sum /= 3.0; sum += (s *= h);
00359   }
00360   else {
00361     // step == 0, so this is the first iteration
00362     sum = Func.inv(f, 0.5*(a+b)); sum *= (b-a);
00363   }
00364 }
00365 
00366 
00367 template <class Y> template <class F>
00368 inline void integrator<Y>::ex(F f, double a, double b, int step)
00369 {
00370   // change limits for the variable change x -> -exp(-x)
00371   b = ::exp(-a);  // need :: because of exp() member fcn
00372 
00373   //assumed lower limit is 0 in code below
00374 
00375   // same algorithm as for op(), only use Func.exf(f,x) instead of f(x)
00376   Y & sum = s[step].second;  // the result
00377   Y & s   = result.first;    // use to hold an intermediate
00378   if(step) {
00379     // refine the previous result, which is in sum
00380     int n = 3;             // the number of points to be added
00381     for(int j = 1; j < step; ++j) n *= 3;  // n == 3^(step);
00382     double h = b/n;        // the interval between adjacent new points
00383     double h2 = 2.0*h;     // skips an existing (old) point
00384     double x = 0.5*h;      // x coordinate of new points
00385     s = Func.exf(f,x);            // will hold the sum of y's at new points
00386     x += h2; s += Func.exf(f,x);  // skip old left-most point and add another
00387     while((x += h) < b) { 
00388       s += Func.exf(f,x);
00389       x += h2;   // skip old point
00390       s += Func.exf(f,x);
00391     }
00392     sum /= 3.0; sum += (s *= h);
00393   }
00394   else {
00395     // step == 0, so this is the first iteration
00396     sum = Func.exf(f, 0.5*b); sum *= b;
00397   }
00398 }
00399  
00400 
00401 // ********************************************************************
00402 // Definitions of functions in class CauchyPV
00403 
00404 // The constructor definitions:
00405 
00406 inline CauchyPV::CauchyPV() : 
00407   abs_tolerance(integ.abs_tolerance), 
00408   rel_tolerance(integ.rel_tolerance),
00409   order(integ.order),
00410   recursion_limit(integ.recursion_limit),
00411   integ(I::OPEN) 
00412 { }
00413 
00414 inline CauchyPV::CauchyPV(const CauchyPV & c) : 
00415   abs_tolerance(integ.abs_tolerance), 
00416   rel_tolerance(integ.rel_tolerance),
00417   order(integ.order),
00418   recursion_limit(integ.recursion_limit),
00419   integ(c.integ)
00420 { }
00421 
00422 
00423 // --------------------------------------------------------------------
00424 // A helper function for CauchyPV calculations
00425 
00426 // a function to generate an object of class CauchyPV_calculator<>
00427 // so CauchyPV_calc(f,s)(t) == f(s+t) + f(s-t), thus CauchyPV_calc(f,s)
00428 // may be passed as the function argument to an integrator
00429 
00430 template <class F>
00431 inline CauchyPV_calculator<F> CauchyPV_calc(F f, double s) 
00432 { return CauchyPV_calculator<F>(f,s); }
00433 
00434 
00435 // --------------------------------------------------------------------
00436 // operator() definition:
00437 
00438 template <class F>
00439 inline double CauchyPV::operator()(F f, double a, double s, double b)
00440 {
00441   double  da = s - a, db = b - s, p = da*db;
00442 
00443   if (p == 0.0) {
00444     error::warning("CauchyPV: pole singularity is at an integration limit.");
00445     return 0.0;
00446   }
00447   else if (p < 0) {
00448     // both limits lie to one side of the singularity
00449     return integ(f, a, b);
00450   }
00451   else {
00452     // do the Cauchy PV integral:
00453     double ans;
00454     if (fabs(da) < fabs(db)) {
00455       ans = integ(CauchyPV_calc(f,s), 0.0, da);
00456       ans += integ(f, s+da, b);
00457     }
00458     else {
00459       ans = integ(f, a, s-db);
00460       ans += integ(CauchyPV_calc(f,s), 0.0, db);
00461     }
00462     return ans;
00463   }
00464 }

Please direct comments and corrections to supermix@submm.caltech.edu
Go to the supermix home page
Generated by doxygen1.2.7