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 a base integration object (default is \ref cern_gauss56) to 00037 perform adaptive integration by automatically subdividing the 00038 integration interval. At each step, the interval with the 00039 largest absolute uncertainty is divided in half. The routine 00040 stops if the absolute tolerance is less than \ref tolx, the 00041 relative tolerance is less than \ref tolf, or the number of 00042 segments exceeds the template parameter \c nsub (in which case 00043 the error handler is called, since the integration may not have 00044 been successful). The number of segments used in the last 00045 integration can be obtained from 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 This class is based on the CERNLIB routines RADAPT and 00054 DADAPT which are documented at 00055 http://wwwasdoc.web.cern.ch/wwwasdoc/shortwrupsdir/d102/top.html 00056 00057 \future Allow user to set the initial segments? 00058 */ 00059 template<class param_t, class func_t=funct<param_t>, size_t nsub=100> 00060 class cern_adapt : public inte<param_t,func_t> { 00061 00062 #ifndef DOXYGEN_INTERNAL 00063 00064 protected: 00065 00066 /// Lower end of subdivision 00067 double xlo[nsub]; 00068 00069 /// High end of subdivision 00070 double xhi[nsub]; 00071 00072 /// Value of integral for subdivision 00073 double tval[nsub]; 00074 00075 /// Squared error for subdivision 00076 double ters[nsub]; 00077 00078 /// Previous number of subdivisions 00079 int nter; 00080 00081 /// Default integration object 00082 cern_gauss56<param_t,func_t> cg56; 00083 00084 /// The base integration object 00085 inte<param_t, func_t> *it; 00086 00087 #endif 00088 00089 public: 00090 00091 cern_adapt() { 00092 nseg=1; 00093 nter=0; 00094 00095 it=&cg56; 00096 } 00097 00098 /// Set the base integration object to use 00099 int set_inte(inte<param_t,func_t> &i) { 00100 it=&i; 00101 return 0; 00102 } 00103 00104 /** 00105 \brief Number of subdivisions 00106 00107 The options are 00108 - 0: Use previous binning and do not subdivide further \n 00109 - 1: Automatic - adapt until tolerance is attained (default) \n 00110 - n: (n>1) split first in n equal segments, then adapt 00111 until tolerance is obtained. 00112 */ 00113 size_t nseg; 00114 00115 /// Return the number of segments used in the last integration 00116 size_t get_nsegments() { 00117 return nter; 00118 } 00119 00120 /// Return the ith segment 00121 int get_ith_segment(size_t i, double &xlow, double &xhigh, double &value, 00122 double &errsq) { 00123 if (i<nter) { 00124 xlow=xlo[i]; 00125 xhigh=xhi[i]; 00126 value=tval[i]; 00127 errsq=ters[i]; 00128 } 00129 return 0; 00130 } 00131 00132 /// Return all of the segments 00133 template<class vec_t> 00134 int get_segments(vec_t &xlow, vec_t &xhigh, vec_t &value, vec_t &errsq) { 00135 for(int i=0;i<nter;i++) { 00136 xlow[i]=xlo[i]; 00137 xhigh[i]=xhi[i]; 00138 value[i]=tval[i]; 00139 errsq[i]=ters[i]; 00140 } 00141 return 0; 00142 } 00143 00144 /** \brief Integrate function \c func from \c a to \c b. 00145 */ 00146 virtual double integ(func_t &func, double a, double b, param_t &pa) { 00147 double res; 00148 integ_err(func,a,b,pa,res,this->interror); 00149 return res; 00150 } 00151 00152 /** \brief Integrate function \c func from \c a to \c b 00153 giving result \c res and error \c err 00154 */ 00155 virtual int integ_err(func_t &func, double a, double b, param_t &pa, 00156 double &res, double &err) 00157 { 00158 00159 double tvals=0.0, terss, xlob, xhib, yhib=0.0, te, root=0.0; 00160 int i, nsegd; 00161 00162 if (nseg==0) { 00163 if (nter==0) { 00164 // If the previous binning was requested, but 00165 // there is no previous binning stored, then 00166 // just shift to automatic binning 00167 nsegd=1; 00168 } else { 00169 tvals=0.0; 00170 terss=0.0; 00171 for(i=0;i<nter;i++) { 00172 it->integ_err(func,xlo[i],xhi[i],pa,tval[i],te); 00173 ters[i]=te*te; 00174 tvals+=tval[i]; 00175 terss+=ters[i]; 00176 } 00177 err=sqrt(2.0*terss); 00178 res=tvals; 00179 return 0; 00180 } 00181 } 00182 00183 // Ensure we're not asking for too many divisions 00184 if (nsub<nseg) { 00185 nsegd=nsub; 00186 } else { 00187 nsegd=nseg; 00188 } 00189 00190 // Compute the initial set of intervals and integral values 00191 xhib=a; 00192 double bin=(b-a)/((double)nsegd); 00193 for(i=0;i<nsegd;i++) { 00194 xlo[i]=xhib; 00195 xlob=xlo[i]; 00196 xhi[i]=xhib+bin; 00197 if (i==nsegd-1) xhi[i]=b; 00198 xhib=xhi[i]; 00199 it->integ_err(func,xlob,xhib,pa,tval[i],te); 00200 ters[i]=te*te; 00201 } 00202 nter=nsegd; 00203 00204 for(size_t iter=1;iter<=nsub;iter++) { 00205 00206 // Compute the total value of the integrand 00207 // and the squared uncertainty 00208 tvals=tval[0]; 00209 terss=ters[0]; 00210 for(i=1;i<nter;i++) { 00211 tvals+=tval[i]; 00212 terss+=ters[i]; 00213 } 00214 00215 // Output iteration information 00216 if (this->verbose>0) { 00217 std::cout << "cern_adapt Iter: " << iter; 00218 std::cout.setf(std::ios::showpos); 00219 std::cout << " Res: " << tvals; 00220 std::cout.unsetf(std::ios::showpos); 00221 std::cout << " Err: " << sqrt(2.0*terss); 00222 if (this->tolx>this->tolf*fabs(tvals)) { 00223 std::cout << " Tol: " << this->tolx << std::endl; 00224 } else { 00225 std::cout << " Tol: " << this->tolf*fabs(tvals) << std::endl; 00226 } 00227 if (this->verbose>1) { 00228 char ch; 00229 std::cout << "Press a key and type enter to continue. " ; 00230 std::cin >> ch; 00231 } 00232 } 00233 00234 // See if we're finished 00235 root=sqrt(2.0*terss); 00236 if (root<=this->tolx || root<=this->tolf*fabs(tvals)) { 00237 res=tvals; 00238 err=root; 00239 this->last_iter=iter; 00240 return 0; 00241 } 00242 00243 // Test if we've run out of intervals 00244 if (nter==nsub) { 00245 res=tvals; 00246 err=root; 00247 this->last_iter=iter; 00248 std::string s="Reached maximum number of "; 00249 s+="subdivisions in cern_adapt::integ_err()."; 00250 set_err_ret(s.c_str(),gsl_etable); 00251 } 00252 00253 // Find the segment with the largest error 00254 double bige=ters[0]; 00255 int ibig=0; 00256 for(i=1;i<nter;i++) { 00257 if (ters[i]>bige) { 00258 bige=ters[i]; 00259 ibig=i; 00260 } 00261 } 00262 00263 // Subdivide that segment further 00264 xhi[nter]=xhi[ibig]; 00265 double xnew=(xlo[ibig]+xhi[ibig])/2.0; 00266 xhi[ibig]=xnew; 00267 xlo[nter]=xnew; 00268 it->integ_err(func,xlo[ibig],xhi[ibig],pa,tval[ibig],te); 00269 ters[ibig]=te*te; 00270 it->integ_err(func,xlo[nter],xhi[nter],pa,tval[nter],te); 00271 ters[nter]=te*te; 00272 nter++; 00273 00274 } 00275 00276 // FIXME: Should we set an error here, or does this 00277 // only happen if we happen to need exactly nsub 00278 // intervals? 00279 res=tvals; 00280 err=root; 00281 return 0; 00282 } 00283 00284 }; 00285 00286 #ifndef DOXYGENP 00287 } 00288 #endif 00289 00290 #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