Let’s look at the Brent’s method: https://github.com/jacobwilliams/opt-lib/blob/0ce2825f87d511bc33764e0aadd3f3061c9c8ca6/src/stdlib_root_core.F90#L1300
The function signature is:
subroutine brentq(me,ax,bx,fax,fbx,xzero,fzero,iflag)
class(brentq_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)
Which to me looks very good except the first me
argument which is of type:
type,abstract,public :: root_solver
!! abstract class for the root solver methods
private
procedure(func),pointer :: f => null() !! user function to find the root of
real(wp) :: ftol = 0.0_wp !! absolute tolerance for `f=0`
real(wp) :: rtol = 1.0e-6_wp !! relative tol for x
real(wp) :: atol = 1.0e-12_wp !! absolute tol for x [not used by all methods]
integer :: maxiter = 2000 !! maximum number of iterations [not used by all methods]
contains
private
procedure,public :: initialize => initialize_root_solver !! initialize the class [must be called first]
procedure,public :: solve !! main routine for finding the root
procedure(root_f),deferred :: find_root !! root solver function. Meant to be
!! called from [[solve]], which handles some common
!! startup tasks. All these routines assume that
!! \( f(a_x) \) and \( f(b_x) \) have opposite signs.
procedure :: get_fa_fb
procedure :: converged
end type root_solver
type,extends(root_solver),public :: brentq_solver
!! brentq root solver
private
contains
private
procedure,public :: find_root => brentq
end type brentq_solver
So I personally would not like this API, because one has to first setup the derived type before calling the function. However, we can do both. We can refactor the brent
function as:
subroutine brentq(f,ftol,rtol,atol,maxiter,ax,bx,fax,fbx,xzero,fzero,iflag)
procedure(func),pointer :: f !! user function to find the root of
real(wp), intent(in) :: ftol !! absolute tolerance for `f=0`
real(wp), intent(in) :: rtol !! relative tol for x
real(wp), intent(in) :: atol !! absolute tol for x [not used by all methods]
integer, intent(in) :: maxiter = 2000 !! maximum number of iterations [not used by all methods]
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)
And then this is standalone and it will work. And then we can create a simple wrapper in the brentq_solver
class for those who prefer the OO interface.