function ZBRENT, x1, x2, FUNC_NAME=func_name, $ MAX_ITERATIONS=maxit, TOLERANCE=TOL ;+ ; NAME: ; ZBRENT ; PURPOSE: ; Find the zero of a 1-D function up to specified tolerance. ; EXPLANTION: ; This routine assumes that the function is known to have a zero. ; Adapted from procedure of the same name in "Numerical Recipes" by ; Press et al. (1992), Section 9.3 ; ; CALLING: ; x_zero = ZBRENT( x1, x2, FUNC_NAME="name" ) ; ; INPUTS: ; x1, x2 = scalars, 2 points which bracket location of function zero, ; that is, F(x1) < 0 < F(x2). ; Note: computations are performed with ; same precision (single/double) as the inputs and user supplied function. ; ; REQUIRED INPUT KEYWORD: ; FUNC_NAME = function name (string) ; Calling mechanism should be: F = func_name( px ) ; where: px = scalar independent variable, input. ; F = scalar value of function at px, ; should be same precision (single/double) as input. ; ; OPTIONAL INPUT KEYWORD: ; MAX_ITER = maximum allowed number iterations, default=100. ; TOLERANCE = desired accuracy of minimum location, default = 1.e-3. ; ; OUTPUTS: ; Returns the location of zero, with accuracy of specified tolerance. ; ; PROCEDURE: ; Brent's method to find zero of a function by using bracketing, ; bisection, and inverse quadratic interpolation, ; ; EXAMPLE: ; Find the root of the COSINE function between 1. and 2. radians ; ; IDL> print, zbrent( 1, 2, FUNC = 'COS') ; ; and the result will be !PI/2 within the specified tolerance ; MODIFICATION HISTORY: ; Written, Frank Varosi NASA/GSFC 1992. ; FV.1994, mod to check for single/double prec. and set zeps accordingly. ; Converted to IDL V5.0 W. Landsman September 1997 ;- if N_params() LT 2 then begin print,'Syntax - result = ZBRENT( x1, x2, FUNC_NAME = , print,' [ MAX_ITER = , TOLERANCE = ]) return, -1 endif if N_elements( TOL ) NE 1 then TOL = 1.e-3 if N_elements( maxit ) NE 1 then maxit = 100 sz1 = size( x1 ) sz2 = size( x2 ) if (sz1[sz1[0]+1] EQ 5) OR (sz2[sz2[0]+1] EQ 5) then begin xa = double( x1 ) xb = double( x2 ) zeps = 1.d-14 ;machine epsilon in double precision. endif else begin xa = x1 xb = x2 zeps = 1.e-7 ;machine epsilon, smallest add, single prec. endelse fa = call_function( func_name, xa ) fb = call_function( func_name, xb ) fc = fb if (fb*fa GT 0) then begin message,"root must be bracketed by the 2 inputs",/INFO return,xa endif for iter = 1,maxit do begin if (fb*fc GT 0) then begin xc = xa fc = fa Din = xb - xa Dold = Din endif if (abs( fc ) LT abs( fb )) then begin xa = xb & xb = xc & xc = xa fa = fb & fb = fc & fc = fa endif TOL1 = 0.5*TOL + 2*abs( xb ) * zeps ;Convergence check xm = (xc - xb)/2. if (abs( xm ) LE TOL1) OR (fb EQ 0) then return,xb if (abs( Dold ) GE TOL1) AND (abs( fa ) GT abs( fb )) then begin S = fb/fa ;attempt inverse quadratic interpolation if (xa EQ xc) then begin p = 2 * xm * S q = 1-S endif else begin T = fa/fc R = fb/fc p = S * (2*xm*T*(T-R) - (xb-xa)*(R-1) ) q = (T-1)*(R-1)*(S-1) endelse if (p GT 0) then q = -q p = abs( p ) test = ( 3*xm*q - abs( q*TOL1 ) ) < abs( Dold*q ) if (2*p LT test) then begin Dold = Din ;accept interpolation Din = p/q endif else begin Din = xm ;use bisection instead Dold = xm endelse endif else begin Din = xm ;Bounds decreasing to slowly, use bisection Dold = xm endelse xa = xb fa = fb ;evaluate new trial root. if (abs( Din ) GT TOL1) then xb = xb + Din $ else xb = xb + TOL1 * (1-2*(xm LT 0)) fb = call_function( func_name, xb ) endfor message,"exceeded maximum number of iterations: "+strtrim(iter,2),/INFO return, xb end