![]() |
MechSys
1.0
Computing library for simulations in continuum and discrete mechanics
|
00001 /************************************************************************ 00002 * MechSys - Open Library for Mechanical Systems * 00003 * Copyright (C) 2005 Dorival M. Pedroso, Raul Durand * 00004 * Copyright (C) 2009 Sergio Galindo * 00005 * * 00006 * This program is free software: you can redistribute it and/or modify * 00007 * it under the terms of the GNU General Public License as published by * 00008 * the Free Software Foundation, either version 3 of the License, or * 00009 * any later version. * 00010 * * 00011 * This program is distributed in the hope that it will be useful, * 00012 * but WITHOUT ANY WARRANTY; without even the implied warranty of * 00013 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * 00014 * GNU General Public License for more details. * 00015 * * 00016 * You should have received a copy of the GNU General Public License * 00017 * along with this program. If not, see <http://www.gnu.org/licenses/> * 00018 ************************************************************************/ 00019 00020 #ifndef MECHSYS_ROOT_H 00021 #define MECHSYS_ROOT_H 00022 00023 // STL 00024 #include <cmath> 00025 #include <cfloat> // for DBL_EPSILON 00026 00027 // MechSys 00028 #include <mechsys/util/fatal.h> 00029 #include <mechsys/util/string.h> 00030 00031 namespace Numerical 00032 { 00033 00034 template<typename Instance> 00035 class Root 00036 { 00037 public: 00038 // Typedefs 00039 typedef double (Instance::*pF) (double x, void * UserData); 00040 typedef double (Instance::*pdFdx) (double x, void * UserData); 00041 00043 Root (Instance * Inst, pF F, pdFdx dFdx=NULL); 00044 00045 // Methods 00046 double Solve (double xa, double xb, double * x_guess=NULL, void * UserData=NULL); 00047 00048 // Data 00049 double Tol; 00050 int It; 00051 int MaxIt; 00052 String Scheme; 00053 bool Verbose; 00054 00055 private: 00056 Instance * _pInst; 00057 pF _pF; 00058 pdFdx _pdFdx; 00059 }; 00060 00061 00063 00064 00065 template<typename Instance> 00066 inline Root<Instance>::Root (Instance * Inst, pF F, pdFdx dFdx) 00067 : Tol (sqrt(DBL_EPSILON)), 00068 It (0), 00069 MaxIt (30), 00070 Scheme ("Brent"), 00071 Verbose (false), 00072 _pInst (Inst), 00073 _pF (F), 00074 _pdFdx (dFdx) 00075 { 00076 } 00077 00078 template<typename Instance> 00079 inline double Root<Instance>::Solve (double xa, double xb, double * x_guess, void * UserData) 00080 { 00081 if (Scheme=="Brent") 00082 { 00083 /* Based on ZEROIN C math library: http://www.netlib.org/c/ 00084 00085 Algorithm: 00086 G.Forsythe, M.Malcolm, C.Moler, Computer methods for mathematical 00087 computations. M., Mir, 1980, p.180 of the Russian edition 00088 00089 The function makes use of the bissection procedure combined with 00090 the linear or quadric inverse interpolation. 00091 At every step program operates on three abscissae: a, b, and c: 00092 a: the last but one approximation 00093 b: the last and the best approximation to the root 00094 c: the last but one or even earlier approximation than a such that: 00095 1) |f(b)| <= |f(c)| 00096 2) f(b) and f(c) have opposite signs, i.e. b and c confine the root 00097 00098 At every step this algorithm selects one of the two new approximations, the former being 00099 obtained by the bissection procedure and the latter resulting in the interpolation. 00100 If a,b and c are all different, the quadric interpolation is utilized, otherwise the linear one. 00101 If the latter (i.e. obtained by the interpolation) point is reasonable (i.e. lies within 00102 the current interval [b,c] not being too close to the boundaries) it is accepted. 00103 The bissection result is used in the other case. Therefore, the range of uncertainty is 00104 ensured to be reduced at least by the factor 1.6. 00105 00106 Examples: <a href="http://cvs.savannah.gnu.org/viewvc/mechsys/mechsys/lib/numerical/tst/tbrentroot.cpp?view=markup"> ttbrentroot.cpp Test Brent's method</a> 00107 */ 00108 00109 double a = xa; // the last but one approximation 00110 double b = xb; // the last and the best approximation to the root 00111 double c = a; // the last but one or even earlier approximation than a that 00112 double fa = (_pInst->*_pF) (a, UserData); 00113 double fb = (_pInst->*_pF) (b, UserData); 00114 double fc = fa; 00115 00116 // Check input 00117 if ((fa>0.0 && fb>0.0) || (fa<0.0 && fb<0.0)) throw new Fatal("Root::Solve: Brent method: Root must be bracketed."); 00118 00119 // Solve 00120 for (It=0; It<MaxIt; ++It) 00121 { 00122 // Distance from the last but one to the last approximation 00123 double prev_step = b-a; 00124 00125 // Swap data for b to be the best approximation 00126 if (fabs(fc)<fabs(fb)) 00127 { 00128 a = b; b = c; c = a; 00129 fa = fb; fb = fc; fc = fa; 00130 } 00131 double tol_act = 2.0*DBL_EPSILON*fabs(b) + Tol/2.0; // Actual tolerance 00132 double new_step = (c-b)/2.0; // Step at this iteration 00133 00134 // Check for convergence 00135 if (Verbose) printf("It=%d, xnew=%g, f(xnew)=%g, err=%g\n", It, b, fb, fabs(new_step)); 00136 if (fabs(new_step)<=tol_act || fb==0.0) return b; // Acceptable approx. is found 00137 00138 // Decide if the interpolation can be tried 00139 if (fabs(prev_step)>=tol_act && fabs(fa)>fabs(fb)) // If prev_step was large enough and was in true direction 00140 { 00141 // Interpolation may be tried 00142 double p,q; // Interpolation step is calculated in the form p/q; division operations is delayed until the last moment 00143 double t1,cb,t2; 00144 cb = c-b; 00145 if(a==c) // If we have only two distinct points, linear interpolation can only be applied 00146 { 00147 t1 = fb/fa; 00148 p = cb*t1; 00149 q = 1.0-t1; 00150 } 00151 else // Quadric inverse interpolation 00152 { 00153 q = fa/fc; t1 = fb/fc; t2 = fb/fa; 00154 p = t2 * ( cb*q*(q-t1) - (b-a)*(t1-1.0) ); 00155 q = (q-1.0) * (t1-1.0) * (t2-1.0); 00156 } 00157 00158 // p was calculated with the oposite sign; make p positive and assign possible minus to q 00159 if (p>0.0) q = -q; 00160 else p = -p; 00161 00162 // If b+p/q falls in [b,c] and isn't too large 00163 if (p<(0.75*cb*q-fabs(tol_act*q)/2.0) && p<fabs(prev_step*q/2.0)) 00164 new_step = p/q;// it is accepted 00165 00166 // If p/q is too large then the bissection procedure can reduce [b,c] range to more extent 00167 } 00168 00169 // Adjust the step to be not less than tolerance 00170 if (fabs(new_step)<tol_act) 00171 { 00172 if (new_step>0.0) new_step = tol_act; 00173 else new_step = -tol_act; 00174 } 00175 00176 // Save the previous approx 00177 a = b; 00178 fa = fb; 00179 00180 // Do step to a new approxim. 00181 b += new_step; 00182 fb = (_pInst->*_pF) (b, UserData); 00183 00184 // Adjust c for it to have a sign opposite to that of b 00185 if ((fb>0.0 && fc>0.0) || (fb<0.0 && fc<0.0)) 00186 { 00187 c = a; 00188 fc = fa; 00189 } 00190 } 00191 00192 // Did not converge 00193 throw new Fatal("Root::Solve: Brent method did not converge after %d iterations",It); 00194 } 00195 else if (Scheme=="Newton") 00196 { 00197 if (_pdFdx==NULL) throw new Fatal("Root:Solve: Newton method: For this method, dFdx cannot be NULL"); 00198 double fa = (_pInst->*_pF) (xa, UserData); 00199 double fb = (_pInst->*_pF) (xb, UserData); 00200 if (fabs(xb-xa)<Tol) throw new Fatal("Root:Solve: Newton method: |xb-xa| must be greater than %g",Tol); 00201 if (fabs(fb-fa)<Tol) throw new Fatal("Root:Solve: Newton method: |fb-fa| must be greater than %g",Tol); 00202 double alp = (x_guess==NULL ? fa/(fa-fb) : ((*x_guess)-xa)/(xb-xa)); 00203 double xsol = xa + alp*(xb-xa); 00204 for (It=0; It<MaxIt; ++It) 00205 { 00206 double F = (_pInst->*_pF) (xsol, UserData); 00207 double dFdx = (_pInst->*_pdFdx) (xsol, UserData); 00208 double xnew = xsol - F/dFdx; 00209 double err = fabs((xnew-xsol)/(1.0+xnew)); 00210 if (Verbose) printf("alp=%g, xsol=%g, F=%g, dFdx=%g, F/dFdx=%g, xnew=%g, err=%g\n", alp, xsol, F, dFdx, F/dFdx, xnew, err); 00211 if (err<Tol) return xnew; 00212 xsol = xnew; 00213 } 00214 00215 // Did not converge 00216 throw new Fatal("Root::Solve: Newton-Rhapson method did not converge after %d iterations",It); 00217 } 00218 else throw new Fatal("Root::Solve: scheme==%s is not available",Scheme.CStr()); 00219 return 0; 00220 } 00221 00222 }; // namespace Numerical 00223 00224 #endif // MECHSYS_ROOT_H