00001 /* 00002 ------------------------------------------------------------------- 00003 00004 Copyright (C) 2006, 2007, 2008, Andrew W. Steiner 00005 00006 This file is part of O2scl. 00007 00008 O2scl is free software; you can redistribute it and/or modify 00009 it under the terms of the GNU General Public License as published by 00010 the Free Software Foundation; either version 3 of the License, or 00011 (at your option) any later version. 00012 00013 O2scl is distributed in the hope that it will be useful, 00014 but WITHOUT ANY WARRANTY; without even the implied warranty of 00015 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00016 GNU General Public License for more details. 00017 00018 You should have received a copy of the GNU General Public License 00019 along with O2scl. If not, see <http://www.gnu.org/licenses/>. 00020 00021 ------------------------------------------------------------------- 00022 */ 00023 #ifndef O2SCL_CERN_ADAPT_H 00024 #define O2SCL_CERN_ADAPT_H 00025 00026 #include <o2scl/inte.h> 00027 #include <o2scl/cern_gauss56.h> 00028 00029 #ifndef DOXYGENP 00030 namespace o2scl { 00031 #endif 00032 00033 /** 00034 \brief Adaptive integration (CERNLIB) 00035 00036 Uses \ref cern_gauss56 to perform adaptive integration by 00037 automatically subdividing the integration interval. At each 00038 step, the interval with the largest absolute uncertainty is 00039 divided in half. The routine stops if the absolute tolerance is 00040 less than \ref tolx, the relative tolerance is less than \ref 00041 tolf, or the number of segments exceeds the template parameter 00042 \c nsub (in which case the error handler is called, since the 00043 integration may not have been successful). The number of 00044 segments used in the last integration can be obtained from 00045 get_nsegments(). 00046 00047 The template parameter \c nsub, is the maximum number of 00048 subdivisions. It is automatically set to 100 in the original 00049 CERNLIB routine, and defaults to 100 here. The default base 00050 integration object is of type \ref cern_gauss56. This is the 00051 CERNLIB default, but can be modified by calling set_inte(). 00052 00053 \todo It looks like the first segment is of zero size, 00054 is this because there's an offset? Double check that 00055 we don't have memory issues if nseg=nsub. 00056 00057 \future More error checking, e.g. ensure the user doesn't try 00058 to get a segment greater than the total number of segments. 00059 \future Allow user to set the initial segments? 00060 00061 */ 00062 template<class param_t, class func_t, size_t nsub=100> 00063 class cern_adapt : public inte<param_t,func_t> { 00064 00065 #ifndef DOXYGEN_INTERNAL 00066 00067 protected: 00068 00069 /// Lower end of subdivision 00070 double xlo[nsub]; 00071 00072 /// High end of subdivision 00073 double xhi[nsub]; 00074 00075 /// Value of integral for subdivision 00076 double tval[nsub]; 00077 00078 /// Squared error for subdivision 00079 double ters[nsub]; 00080 00081 /// Previous number of subdivisions 00082 int nter; 00083 00084 /// Default integration object 00085 cern_gauss56<param_t,func_t> cg56; 00086 00087 /// The base integration object 00088 inte<param_t, func_t> *it; 00089 00090 #endif 00091 00092 public: 00093 00094 cern_adapt() { 00095 nseg=1; 00096 nter=0; 00097 00098 it=&cg56; 00099 } 00100 00101 /// Set the base integration object to use 00102 int set_inte(inte<param_t,func_t> &i) { 00103 it=&i; 00104 return 0; 00105 } 00106 00107 /** 00108 \brief Number of subdivisions 00109 00110 The options are 00111 - 0: Use previous binning and do not subdivide further \n 00112 - 1: Automatic - adapt until tolerance is attained (default) \n 00113 - n: (n>1) split first in n equal segments, then adapt 00114 until tolerance is obtained. 00115 */ 00116 size_t nseg; 00117 00118 /// Return the number of segments used in the last integration 00119 size_t get_nsegments() { 00120 return nter; 00121 } 00122 00123 /// Return the ith segment 00124 int get_ith_segment(size_t i, double &xlow, double &xhigh, double &value, 00125 double &errsq) { 00126 if (i<nter) { 00127 xlow=xlo[i]; 00128 xhigh=xhi[i]; 00129 value=tval[i]; 00130 errsq=ters[i]; 00131 } 00132 return 0; 00133 } 00134 00135 /// Return all of the segments 00136 template<class vec_t> 00137 int get_segments(vec_t &xlow, vec_t &xhigh, vec_t &value, vec_t &errsq) { 00138 for(int i=0;i<nter;i++) { 00139 xlow[i]=xlo[i]; 00140 xhigh[i]=xhi[i]; 00141 value[i]=tval[i]; 00142 errsq[i]=ters[i]; 00143 } 00144 return 0; 00145 } 00146 00147 /** \brief Integrate function \c func from \c a to \c b. 00148 */ 00149 virtual double integ(func_t &func, double a, double b, param_t &pa) { 00150 double res, err; 00151 integ_err(func,a,b,pa,res,err); 00152 return res; 00153 } 00154 00155 /** \brief Integrate function \c func from \c a to \c b 00156 giving result \c res and error \c err 00157 */ 00158 virtual int integ_err(func_t &func, double a, double b, param_t &pa, 00159 double &res, double &err) 00160 { 00161 00162 double tvals=0.0, terss, xlob, xhib, yhib=0.0, te, root=0.0; 00163 int i, nsegd; 00164 00165 if (nseg==0) { 00166 if (nter==0) { 00167 // If the previous binning was requested, but 00168 // there is no previous binning stored, then 00169 // just shift to automatic binning 00170 nsegd=1; 00171 } else { 00172 tvals=0.0; 00173 terss=0.0; 00174 for(i=1;i<=nter;i++) { 00175 it->integ_err(func,xlo[i],xhi[i],pa,tval[i],te); 00176 ters[i]=te*te; 00177 tvals+=tval[i]; 00178 terss+=ters[i]; 00179 } 00180 err=sqrt(2.0*terss); 00181 res=tvals; 00182 return 0; 00183 } 00184 } 00185 00186 // Ensure we're not asking for too many divisions 00187 if (nsub<nseg) { 00188 nsegd=nsub; 00189 } else { 00190 nsegd=nseg; 00191 } 00192 00193 // Compute the initial set of intervals and integral values 00194 xhib=a; 00195 double bin=(b-a)/((double)nsegd); 00196 for(i=1;i<=nsegd;i++) { 00197 xlo[i]=xhib; 00198 xlob=xlo[i]; 00199 xhi[i]=xhib+bin; 00200 if (i==nsegd) xhi[i]=b; 00201 xhib=xhi[i]; 00202 it->integ_err(func,xlob,xhib,pa,tval[i],te); 00203 ters[i]=te*te; 00204 } 00205 nter=nsegd; 00206 00207 for(size_t iter=1;iter<=nsub;iter++) { 00208 00209 // Compute the total value of the integrand 00210 // and the squared uncertainty 00211 tvals=tval[1]; 00212 terss=ters[1]; 00213 for(i=2;i<=nter;i++) { 00214 tvals+=tval[i]; 00215 terss+=ters[i]; 00216 } 00217 00218 // Output iteration information 00219 if (this->verbose>0) { 00220 std::cout << "cern_adapt Iter: " << iter; 00221 std::cout.setf(std::ios::showpos); 00222 std::cout << " Res: " << tvals; 00223 std::cout.unsetf(std::ios::showpos); 00224 std::cout << " Err: " << sqrt(2.0*terss); 00225 if (this->tolx>this->tolf*fabs(tvals)) { 00226 std::cout << " Tol: " << this->tolx << std::endl; 00227 } else { 00228 std::cout << " Tol: " << this->tolf*fabs(tvals) << std::endl; 00229 } 00230 if (this->verbose>1) { 00231 char ch; 00232 std::cout << "Press a key and type enter to continue. " ; 00233 std::cin >> ch; 00234 } 00235 } 00236 00237 // See if we're finished 00238 root=sqrt(2.0*terss); 00239 if (root<=this->tolx || root<=this->tolf*fabs(tvals)) { 00240 res=tvals; 00241 err=root; 00242 return 0; 00243 } 00244 00245 // Test if we've run out of intervals 00246 if (nter==nsub) { 00247 res=tvals; 00248 err=root; 00249 std::string s="Reached maximum number of "; 00250 s+="subdivisions in cern_adapt::integ_err()."; 00251 set_err_ret(s.c_str(),gsl_etable); 00252 } 00253 00254 // Find the segment with the largest error 00255 double bige=ters[1]; 00256 int ibig=1; 00257 for(i=2;i<=nter;i++) { 00258 if (ters[i]>bige) { 00259 bige=ters[i]; 00260 ibig=i; 00261 } 00262 } 00263 00264 // Subdivide that segment further 00265 nter++; 00266 xhi[nter]=xhi[ibig]; 00267 double xnew=(xlo[ibig]+xhi[ibig])/2.0; 00268 xhi[ibig]=xnew; 00269 xlo[nter]=xnew; 00270 it->integ_err(func,xlo[ibig],xhi[ibig],pa,tval[ibig],te); 00271 ters[ibig]=te*te; 00272 it->integ_err(func,xlo[nter],xhi[nter],pa,tval[nter],te); 00273 ters[nter]=te*te; 00274 00275 } 00276 00277 // FIXME: Should we set an error here, or does this 00278 // only happen if we happen to need exactly nsub 00279 // intervals? 00280 res=tvals; 00281 err=root; 00282 return 0; 00283 } 00284 00285 }; 00286 00287 #ifndef DOXYGENP 00288 } 00289 #endif 00290 00291 #endif
Documentation generated with Doxygen and provided under the GNU Free Documentation License. See License Information for details.
Project hosting provided by
,
O2scl Sourceforge Project Page