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
1.2.7