Brent's method with hyperbolic extrapolation.
A variation on the classic Brent routine to find a zero of the function f between the arguments ax and bx that uses hyperbolic extrapolation instead of inverse quadratic extrapolation.
brenth.c
Type | Intent | Optional | Attributes | Name | ||
---|---|---|---|---|---|---|
class(brenth_solver), | intent(inout) | :: | me | |||
real(kind=wp), | intent(in) | :: | ax |
left endpoint of initial interval |
||
real(kind=wp), | intent(in) | :: | bx |
right endpoint of initial interval |
||
real(kind=wp), | intent(in) | :: | fax |
|
||
real(kind=wp), | intent(in) | :: | fbx |
|
||
real(kind=wp), | intent(out) | :: | xzero |
abscissa approximating a zero of |
||
real(kind=wp), | intent(out) | :: | fzero |
value of |
||
integer, | intent(out) | :: | iflag |
status flag ( |
subroutine brenth(me,ax,bx,fax,fbx,xzero,fzero,iflag) implicit none class(brenth_solver),intent(inout) :: me real(wp),intent(in) :: ax !! left endpoint of initial interval real(wp),intent(in) :: bx !! right endpoint of initial interval real(wp),intent(in) :: fax !! `f(ax)` real(wp),intent(in) :: fbx !! `f(ax)` real(wp),intent(out) :: xzero !! abscissa approximating a zero of `f` in the interval `ax`,`bx` real(wp),intent(out) :: fzero !! value of `f` at the root (`f(xzero)`) integer,intent(out) :: iflag !! status flag (`0`=root found, `-2`=max iterations reached) real(wp) :: xpre,xcur,xblk,fpre,fcur,fblk,spre,& scur,sbis,delta,stry,dpre,dblk,xdelta integer :: i !! iteration counter iflag = 0 xpre = ax xcur = bx fpre = fax fcur = fbx do i = 1, me%maxiter if (fpre*fcur < 0.0_wp) then xblk = xpre fblk = fpre scur = xcur - xpre spre = scur end if if (abs(fblk) < abs(fcur)) then xpre = xcur xcur = xblk xblk = xpre fpre = fcur fcur = fblk fblk = fpre end if delta = (me%atol + me%rtol*abs(xcur))/2.0_wp sbis = (xblk - xcur)/2.0_wp if (abs(fcur) <= me%ftol .or. abs(sbis) < delta) exit ! converged if (abs(spre) > delta .and. abs(fcur) < abs(fpre)) then if (xpre == xblk) then ! interpolate stry = -fcur*(xcur - xpre)/(fcur - fpre) else ! extrapolate dpre = (fpre - fcur)/(xpre - xcur) dblk = (fblk - fcur)/(xblk - xcur) stry = -fcur*(fblk - fpre)/(fblk*dpre - fpre*dblk) ! only difference from brentq end if if (2.0_wp*abs(stry) < min(abs(spre), 3.0_wp*abs(sbis) - delta)) then ! accept step spre = scur scur = stry else ! bisect spre = sbis scur = sbis end if else ! bisect spre = sbis scur = sbis end if xpre = xcur fpre = fcur if (abs(scur) > delta) then xcur = xcur + scur else if (sbis > 0.0_wp) then xdelta = delta else xdelta = -delta end if xcur = xcur + xdelta end if fcur = me%f(xcur) if (abs(fcur) <= me%ftol) exit ! converged if (i == me%maxiter) iflag = -2 ! max iterations reached end do xzero = xcur fzero = fcur end subroutine brenth