nonlin 1.5.2
A library that provides routines to compute the solutions to systems of nonlinear equations.
Loading...
Searching...
No Matches
nonlin_solve_newton1var.f90
1! nonlin_solve_newton1var.f90
2
3submodule(nonlin_solve) nonlin_solve_newton1var
4 implicit none
5contains
6! ------------------------------------------------------------------------------
7 module subroutine newt1var_solve(this, fcn, x, lim, f, ib, err)
8 ! Arguments
9 class(newton_1var_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 :: p5 = 0.5d0
20 real(real64), parameter :: two = 2.0d0
21
22 ! Local Variables
23 logical :: fcnvrg, xcnvrg, dcnvrg
24 integer(int32) :: neval, ndiff, maxeval, flag, iter
25 real(real64) :: ftol, xtol, dtol, xh, xl, fh, fl, x1, x2, eps, dxold, &
26 dx, df, temp, ff
27 class(errors), pointer :: errmgr
28 type(errors), target :: deferr
29 character(len = 256) :: errmsg
30
31 ! Initialization
32 fcnvrg = .false.
33 xcnvrg = .false.
34 dcnvrg = .false.
35 neval = 0
36 ndiff = 0
37 iter = 0
38 ftol = this%get_fcn_tolerance()
39 xtol = this%get_var_tolerance()
40 dtol = this%get_diff_tolerance()
41 maxeval = this%get_max_fcn_evals()
42 if (present(f)) f = zero
43 if (present(ib)) then
44 ib%iter_count = iter
45 ib%fcn_count = neval
46 ib%jacobian_count = ndiff
47 ib%gradient_count = 0
48 ib%converge_on_fcn = fcnvrg
49 ib%converge_on_chng = xcnvrg
50 ib%converge_on_zero_diff = dcnvrg
51 end if
52 if (present(err)) then
53 errmgr => err
54 else
55 errmgr => deferr
56 end if
57 x1 = min(lim%x1, lim%x2)
58 x2 = max(lim%x1, lim%x2)
59 eps = epsilon(eps)
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(x1 - x2) < eps) then
70 ! ERROR: Search limits are too tight
71 write(errmsg, 100) "Search limits have no " // &
72 "appreciable difference between them. Lower Limit: ", x1, &
73 ", Upper Limit: ", x2
74 call errmgr%report_error("brent_solve", trim(errmsg), &
75 nl_invalid_operation_error)
76 return
77 end if
78
79 ! See if the root is one of the end points
80 flag = 0
81 fl = fcn%fcn(x1)
82 fh = fcn%fcn(x2)
83 neval = 2
84 if (abs(fl) < ftol) then
85 x = x1
86 if (present(f)) f = fl
87 if (present(ib)) then
88 ib%converge_on_fcn = .true.
89 ib%fcn_count = 2
90 end if
91 return
92 end if
93 if (abs(fh) < ftol) then
94 x = x2
95 if (present(f)) f = fh
96 if (present(ib)) then
97 ib%converge_on_fcn = .true.
98 ib%fcn_count = 2
99 end if
100 return
101 end if
102
103 ! Process
104 if (fl < zero) then
105 xl = x1
106 xh = x2
107 else
108 xl = x2
109 xh = x1
110 end if
111 x = p5 * (x1 + x2)
112 dxold = abs(x2 - x1)
113 dx = dxold
114 ff = fcn%fcn(x)
115 df = fcn%diff(x, ff)
116 neval = neval + 1
117 ndiff = ndiff + 1
118 do
119 ! Increment the iteration counter
120 iter = iter + 1
121
122 ! Bisect if the Newton step went out of range, or if the rate
123 ! of change was too slow
124 if ((((x - xh) * df - ff) * ((x - xl) * df - ff) > zero) .or. &
125 (abs(two * ff) > abs(dxold * df))) &
126 then
127 ! Bisection
128 dxold = dx
129 dx = p5 * (xh - xl)
130 x = xl + dx
131 if (abs(xl - x) < xtol) then
132 ! Convergence as the change in root is within tolerance
133 xcnvrg = .true.
134 exit
135 end if
136 else
137 ! Newton's Method
138 dxold = dx
139 dx = ff / df
140 temp = x
141 x = x - dx
142 if (abs(temp - x) < xtol) then
143 ! Convergence as the change in root is within tolerance
144 xcnvrg = .true.
145 exit
146 end if
147 end if
148
149 ! Update function values
150 ff = fcn%fcn(x)
151 df = fcn%diff(x, ff)
152 neval = neval + 1
153 ndiff = ndiff + 1
154
155 ! Check for convergence
156 if (abs(ff) < ftol) then
157 fcnvrg = .true.
158 exit
159 end if
160 if (abs(dx) < xtol) then
161 xcnvrg = .true.
162 exit
163 end if
164 if (abs(df) < dtol) then
165 dcnvrg = .true.
166 exit
167 end if
168
169 ! Update the bracket on the root
170 if (ff < zero) then
171 xl = x
172 else
173 xh = x
174 end if
175
176 ! Print status
177 if (this%get_print_status()) then
178 call print_status(iter, neval, ndiff, dx, ff)
179 end if
180
181 ! Ensure we haven't made too many function evaluations
182 if (neval >= maxeval) then
183 flag = 1
184 exit
185 end if
186 end do
187
188 ! Ensure the function value is current with the estimate of the root
189 if (present(f)) then
190 f = fcn%fcn(x)
191 neval = neval + 1
192 end if
193
194 ! Report out iteration statistics and other optional outputs
195 if (present(f)) f = ff
196 if (present(ib)) then
197 ib%iter_count = iter
198 ib%fcn_count = neval
199 ib%jacobian_count = ndiff
200 ib%gradient_count = 0
201 ib%converge_on_fcn = fcnvrg
202 ib%converge_on_chng = xcnvrg
203 ib%converge_on_zero_diff = dcnvrg
204 end if
205
206 ! Check for convergence issues
207 if (flag /= 0) then
208 write(errmsg, 101) "The algorithm failed to " // &
209 "converge. Function evaluations performed: ", neval, &
210 "." // new_line('c') // "Root estimate: ", x, &
211 new_line('c') // "Residual: ", ff
212 call errmgr%report_error("newt1var_solve", trim(errmsg), &
213 nl_convergence_error)
214 end if
215
216 ! Format
217100 format(a, e10.3, a, e10.3)
218101 format(a, i0, a, e10.3, a, e10.3)
219 end subroutine
220end submodule
nonlin_solve