nonlin 1.5.2
A library that provides routines to compute the solutions to systems of nonlinear equations.
Loading...
Searching...
No Matches
nonlin_solve_brent.f90
1! nonlin_solve_brent.f90
2
3submodule(nonlin_solve) nonlin_solve_brent
4 implicit none
5contains
6! ------------------------------------------------------------------------------
7 module subroutine brent_solve(this, fcn, x, lim, f, ib, err)
8 ! Arguments
9 class(brent_solver), intent(inout) :: this
10 class(fcn1var_helper), intent(in) :: fcn
11 real(real64), intent(inout) :: x
12 type(value_pair), intent(in) :: lim
13 real(real64), intent(out), optional :: f
14 type(iteration_behavior), optional :: ib
15 class(errors), intent(inout), optional, target :: err
16
17 ! Parameters
18 real(real64), parameter :: zero = 0.0d0
19 real(real64), parameter :: half = 0.5d0
20 real(real64), parameter :: one = 1.0d0
21 real(real64), parameter :: two = 2.0d0
22 real(real64), parameter :: three = 3.0d0
23
24 ! Local Variables
25 logical :: fcnvrg, xcnvrg
26 integer(int32) :: neval, maxeval, flag, iter
27 real(real64) :: ftol, xtol, a, b, c, fa, fb, fc, p, q, r, s, xm, e, d, &
28 mn1, mn2, eps, tol1, temp
29 class(errors), pointer :: errmgr
30 type(errors), target :: deferr
31 character(len = 256) :: errmsg
32
33 ! Initialization
34 fcnvrg = .false.
35 xcnvrg = .false.
36 x = zero
37 a = min(lim%x1, lim%x2)
38 b = max(lim%x1, lim%x2)
39 neval = 0
40 iter = 0
41 eps = epsilon(eps)
42 ftol = this%get_fcn_tolerance()
43 xtol = this%get_var_tolerance()
44 maxeval = this%get_max_fcn_evals()
45 if (present(f)) f = zero
46 if (present(ib)) then
47 ib%iter_count = iter
48 ib%fcn_count = neval
49 ib%jacobian_count = 0
50 ib%gradient_count = 0
51 ib%converge_on_fcn = fcnvrg
52 ib%converge_on_chng = xcnvrg
53 ib%converge_on_zero_diff = .false.
54 end if
55 if (present(err)) then
56 errmgr => err
57 else
58 errmgr => deferr
59 end if
60
61 ! Input Check
62 if (.not.fcn%is_fcn_defined()) then
63 ! ERROR: No function is defined
64 call errmgr%report_error("brent_solve", &
65 "No function has been defined.", &
66 nl_invalid_operation_error)
67 return
68 end if
69 if (abs(a - b) < eps) then
70 ! ERROR: Search limits are too tight
71 write(errmsg, 100) "Search limits have no " // &
72 "appreciable difference between them. Lower Limit: ", a, &
73 ", Upper Limit: ", b
74 call errmgr%report_error("brent_solve", trim(errmsg), &
75 nl_invalid_operation_error)
76 return
77 end if
78
79 ! Process
80 flag = 0
81 fa = fcn%fcn(a)
82 fb = fcn%fcn(b)
83 neval = 2
84 fc = fb
85 do
86 ! Increment the iteration counter
87 iter = iter + 1
88
89 ! Adjust the bounding interval
90 if ((fb > zero .and. fc >= zero) .or. &
91 (fb < zero .and. fc < zero)) then
92 c = a
93 fc = fa
94 d = b - a
95 e = d
96 end if
97 if (abs(fc) < abs(fb)) then
98 a = b
99 b = c
100 c = a
101 fa = fb
102 fb = fc
103 fc = fa
104 end if
105
106 ! Convergence Check
107 tol1 = two * eps * abs(b) + half * xtol
108 xm = half * (c - b)
109 if (abs(fb) < ftol) then
110 x = b
111 fcnvrg = .true.
112 exit
113 end if
114 if (abs(xm) <= tol1) then
115 x = b
116 xcnvrg = .true.
117 exit
118 end if
119
120 ! Actual Method
121 if (abs(e) >= tol1 .and. abs(fa) > abs(fb)) then
122 ! Attempt the inverse quadratic interpolation to determine
123 ! the root
124 s = fb / fa
125 if (abs(a - c) < eps) then ! a == c
126 p = two * xm * s
127 q = one - s
128 else
129 q = fa / fc
130 r = fb / fc
131 p = s * (two * xm * q * (q - r) - (b - a) * (r - one))
132 q = (q - one) * (r - one) * (s - one)
133 end if
134
135 ! Ensure we're within bounds
136 if (p > zero) q = -q
137 p = abs(p)
138 mn1 = three * xm * q - abs(tol1 * q)
139 mn2 = abs(e * q)
140 if (mn1 < mn2) then
141 temp = mn1
142 else
143 temp = mn2
144 end if
145 if (two * p < temp) then
146 ! Accept the interpolation
147 e = d
148 d = p / q
149 else
150 ! The interpolation failed, use bisection
151 d = xm
152 e = d
153 end if
154 else
155 ! The bounds are decreasing too slowly, use bisection
156 d = xm
157 e = d
158 end if
159
160 ! Move the last best guess to the lower limit parameter (A)
161 a = b
162 fa = fb
163 if (abs(d) > tol1) then
164 b = b + d
165 else
166 b = b + sign(tol1, xm)
167 end if
168 fb = fcn%fcn(b)
169 neval = neval + 1
170
171 ! Print iteration status
172 if (this%get_print_status()) then
173 call print_status(iter, neval, 0, xm, fb)
174 end if
175
176 ! Ensure we haven't made too many function evaluations
177 if (neval >= maxeval) then
178 flag = 1
179 exit
180 end if
181 end do
182
183 ! Report out iteration statistics and other optional outputs
184 if (present(f)) f = fb
185 if (present(ib)) then
186 ib%iter_count = iter
187 ib%fcn_count = neval
188 ib%jacobian_count = 0
189 ib%gradient_count = 0
190 ib%converge_on_fcn = fcnvrg
191 ib%converge_on_chng = xcnvrg
192 ib%converge_on_zero_diff = .false.
193 end if
194
195 ! Check for convergence issues
196 if (flag /= 0) then
197 write(errmsg, 101) "The algorithm failed to " // &
198 "converge. Function evaluations performed: ", neval, &
199 "." // new_line('c') // "Change in Variable: ", xm, &
200 new_line('c') // "Residual: ", fb
201 call errmgr%report_error("brent_solve", trim(errmsg), &
202 nl_convergence_error)
203 end if
204
205 ! Formatting
206100 format(a, e10.3, a, e10.3)
207101 format(a, i0, a, e10.3, a, e10.3)
208 end subroutine
209end submodule
nonlin_solve