nonlin 1.5.2
A library that provides routines to compute the solutions to systems of nonlinear equations.
Loading...
Searching...
No Matches
nonlin_solve_newton.f90
1! nonlin_solve_newton.f90
2
3submodule(nonlin_solve) nonlin_solve_newton
4 implicit none
5contains
6! ------------------------------------------------------------------------------
7 module subroutine ns_solve(this, fcn, x, fvec, ib, err)
8 ! Arguments
9 class(newton_solver), intent(inout) :: this
10 class(vecfcn_helper), intent(in) :: fcn
11 real(real64), intent(inout), dimension(:) :: x
12 real(real64), intent(out), dimension(:) :: fvec
13 type(iteration_behavior), optional :: ib
14 class(errors), intent(inout), optional, target :: err
15
16 ! Parameters
17 real(real64), parameter :: zero = 0.0d0
18 real(real64), parameter :: half = 0.5d0
19 real(real64), parameter :: one = 1.0d0
20 real(real64), parameter :: mintol = 1.0d-12
21 real(real64), parameter :: factor = 1.0d2
22
23 ! Local Variables
24 logical :: check, xcnvrg, fcnvrg, gcnvrg
25 integer(int32) :: i, neqn, nvar, lwork, flag, neval, iter, maxeval, njac
26 integer(int32), allocatable, dimension(:) :: ipvt
27 real(real64), allocatable, dimension(:) :: dir, grad, xold, work
28 real(real64), allocatable, dimension(:,:) :: jac
29 real(real64) :: ftol, xtol, gtol, f, fold, stpmax, xnorm, fnorm, temp, test
30 type(iteration_behavior) :: lib
31 class(errors), pointer :: errmgr
32 type(errors), target :: deferr
33 character(len = 256) :: errmsg
34 class(line_search), allocatable :: ls
35
36 ! Initialization
37 xcnvrg = .false.
38 fcnvrg = .false.
39 gcnvrg = .false.
40 neqn = fcn%get_equation_count()
41 nvar = fcn%get_variable_count()
42 neval = 0
43 iter = 0
44 njac = 0
45 ftol = this%get_fcn_tolerance()
46 xtol = this%get_var_tolerance()
47 gtol = this%get_gradient_tolerance()
48 maxeval = this%get_max_fcn_evals()
49 if (present(ib)) then
50 ib%iter_count = iter
51 ib%fcn_count = neval
52 ib%jacobian_count = njac
53 ib%gradient_count = 0
54 ib%converge_on_fcn = fcnvrg
55 ib%converge_on_chng = xcnvrg
56 ib%converge_on_zero_diff = gcnvrg
57 end if
58 if (present(err)) then
59 errmgr => err
60 else
61 errmgr => deferr
62 end if
63 if (this%get_use_line_search()) then
64 if (.not.this%is_line_search_defined()) &
65 call this%set_default_line_search()
66 call this%get_line_search(ls)
67 end if
68
69 ! Input Checking
70 if (.not.fcn%is_fcn_defined()) then
71 ! ERROR: No function is defined
72 call errmgr%report_error("ns_solve", &
73 "No function has been defined.", &
74 nl_invalid_operation_error)
75 return
76 end if
77 if (nvar /= neqn) then
78 ! ERROR: # of equations doesn't match # of variables
79 write(errmsg, 100) "The number of equations (", neqn, &
80 ") does not match the number of unknowns (", nvar, ")."
81 call errmgr%report_error("ns_solve", trim(errmsg), &
82 nl_invalid_input_error)
83 return
84 end if
85 flag = 0
86 if (size(x) /= nvar) then
87 flag = 3
88 else if (size(fvec) /= neqn) then
89 flag = 4
90 end if
91 if (flag /= 0) then
92 ! One of the input arrays is not sized correctly
93 write(errmsg, 101) "Input number ", flag, &
94 " is not sized correctly."
95 call errmgr%report_error("ns_solve", trim(errmsg), &
96 nl_array_size_error)
97 return
98 end if
99
100 ! Local Memory Allocation
101 allocate(ipvt(nvar), stat = flag)
102 if (flag == 0) allocate(dir(nvar), stat = flag)
103 if (flag == 0) allocate(grad(nvar), stat = flag)
104 if (flag == 0) allocate(xold(nvar), stat = flag)
105 if (flag == 0) allocate(jac(nvar, neqn), stat = flag)
106 if (flag == 0) then
107 call fcn%jacobian(x, jac, fv = fvec, olwork = lwork)
108 allocate(work(lwork), stat = flag)
109 end if
110 if (flag /= 0) then
111 ! ERROR: Out of memory
112 call errmgr%report_error("ns_solve", &
113 "Insufficient memory available.", nl_out_of_memory_error)
114 return
115 end if
116
117 ! Test to see if the initial guess is a root
118 call fcn%fcn(x, fvec)
119 f = half * dot_product(fvec, fvec)
120 neval = neval + 1
121 test = zero
122 do i = 1, neqn
123 test = max(abs(fvec(i)), test)
124 end do
125 if (test < ftol) then
126 fcnvrg = .true.
127 end if
128
129 ! Process
130 flag = 0 ! Used to check for convergence errors
131 if (.not.fcnvrg) then
132 ! Compute the maximum step size for the line search process
133 stpmax = factor * max(norm2(x), real(nvar, real64))
134
135 ! Main Iteration Loop
136 do
137 ! Increment the iteration counter
138 iter = iter + 1
139
140 ! Compute the Jacobian
141 call fcn%jacobian(x, jac, fvec, work)
142 njac = njac + 1
143
144 ! Compute the gradient
145 do i = 1, nvar
146 grad(i) = dot_product(jac(:,i), fvec)
147 end do
148
149 ! Compute the LU factorization of the Jacobian
150 call lu_factor(jac, ipvt, errmgr)
151 if (errmgr%has_warning_occurred()) then
152 ! The Jacobian is singular - warning was issued already, so
153 ! simply exit the routine. Do not return as a return at
154 ! this point would not allow for proper updating of the
155 ! iteration tracking parameters
156 exit
157 end if
158
159 ! Store previous iteration values
160 xold = x
161 fold = f
162
163 ! Define the right-hand-side for the linear system
164 dir = -fvec
165
166 ! Solve the linear system of equations
167 call solve_lu(jac, ipvt, dir)
168
169 ! Apply the line search if needed
170 if (this%get_use_line_search()) then
171 ! Define the step length for the line search
172 temp = dot_product(dir, dir)
173 if (temp > stpmax) dir = dir * (stpmax / temp)
174
175 ! Apply the line search
176 call limit_search_vector(dir, stpmax)
177 call ls%search(fcn, xold, grad, dir, x, fvec, &
178 fold, f, lib, errmgr)
179 neval = neval + lib%fcn_count
180 else
181 ! No line search - just update the solution estimate
182 x = x + dir
183 call fcn%fcn(x, fvec)
184 f = half * dot_product(fvec, fvec)
185 neval = neval + 1
186 end if
187
188 ! Check for convergence
189 call test_convergence(x, xold, fvec, grad, .true., xtol, &
190 ftol, gtol, check, xcnvrg, fcnvrg, gcnvrg, xnorm, fnorm)
191 if (check) then
192 ! The solution has converged
193 exit
194 else if (gcnvrg) then
195 ! The solution appears to have settled at a point where
196 ! the gradient has a zero slope
197 write(errmsg, 102) &
198 "It appears the solution has settled to " // &
199 "a point where the slope of the gradient " // &
200 "is effectively zero. " // new_line('c') // &
201 "Function evaluations performed: ", neval, &
202 "." // new_line('c') // &
203 "Change in Variable: ", xnorm, &
204 new_line('c') // "Residual: ", fnorm
205 call errmgr%report_warning("ns_solve", trim(errmsg), &
206 nl_spurious_convergence_error)
207 end if
208
209 ! Print status
210 if (this%get_print_status()) then
211 call print_status(iter, neval, njac, xnorm, fnorm)
212 end if
213
214 ! Ensure we haven't made too many function evaluations
215 if (neval >= maxeval) then
216 flag = 1
217 exit
218 end if
219 end do
220 end if
221
222 ! Report out iteration statistics
223 if (present(ib)) then
224 ib%iter_count = iter
225 ib%fcn_count = neval
226 ib%jacobian_count = njac
227 ib%gradient_count = 0
228 ib%converge_on_fcn = fcnvrg
229 ib%converge_on_chng = xcnvrg
230 ib%converge_on_zero_diff = gcnvrg
231 end if
232
233 ! Check for convergence issues
234 if (flag /= 0) then
235 write(errmsg, 102) "The algorithm failed to " // &
236 "converge. Function evaluations performed: ", neval, &
237 "." // new_line('c') // "Change in Variable: ", xnorm, &
238 new_line('c') // "Residual: ", fnorm
239 call errmgr%report_error("ns_solve", trim(errmsg), &
240 nl_convergence_error)
241 end if
242
243 ! Formatting
244100 format(a, i0, a, i0, a)
245101 format(a, i0, a)
246102 format(a, i0, a, e10.3, a, e10.3)
247 end subroutine
248end submodule
nonlin_solve