libSUFR
a LIBrary of Some Useful Fortran Routines
All Classes Namespaces Files Functions Variables Pages
solvers.f90
Go to the documentation of this file.
1!> \file solvers.f90 Procedures to solve equations
2
3
4! Copyright (c) 2002-2025 Marc van der Sluys - Nikhef/Utrecht University - marc.vandersluys.nl
5!
6! This file is part of the libSUFR package,
7! see: http://libsufr.sourceforge.net/
8!
9! This is free software: you can redistribute it and/or modify it under the terms of the European Union
10! Public Licence 1.2 (EUPL 1.2). This software is distributed in the hope that it will be useful, but
11! WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
12! PURPOSE. See the EU Public Licence for more details. You should have received a copy of the European
13! Union Public Licence along with this code. If not, see <https://www.eupl.eu/1.2/en/>.
14!
15!
16
17
18
19
20!***********************************************************************************************************************************
21!> \brief Procedures to solve equations
22
24 implicit none
25 save
26
27contains
28
29
30 !*********************************************************************************************************************************
31 !> \brief Using Brent's method, find the root of a function func that lies between x1 and x2.
32 !!
33 !! \param func Function to find the root of
34 !! \param x1 Lower limit in x for root: x1 < root < x2; func(x1) and func(x2) must be positive and negative or vice versa
35 !! \param x2 Upper limit in x for root: x1 < root < x2; func(x1) and func(x2) must be positive and negative or vice versa
36 !! \param accur The accuracy with which the root is to be determined
37 !!
38 !! \param status Status: 0-ok, 1-maximum number of iterations exceeded, 2-root not bracketed (output, optional)
39 !! \param verbose Verbosity: 0-print nothing, 1-print errors, 2-print warnings, 3-print info (output, optional, default=2)
40 !!
41 !! \retval root_solver The value of the root of func, between x1 and x2 and with accuracy accur. If a root was not bracketed by (output)
42 !! x1 and x2, -huge is returned and status=1.
43 !! \see Numerical Recipes in Fortran 77, Sect.9.3.
44
45 function root_solver(func,x1,x2,accur, status,verbose)
46 use sufr_kinds, only: double, dbl
47 use sufr_numerics, only: deq
48
49 implicit none
50 real(double), intent(in) :: x1,x2,accur
51 integer, intent(in), optional :: verbose
52 integer, intent(out), optional :: status
53
54 real(double) :: root_solver
55 real(double), external :: func
56
57 integer, parameter :: max_iter = 100 ! Maximum allowed number of iterations
58 real(double), parameter :: eps = epsilon(0.0_dbl) ! Machine precision
59
60 integer :: iter,verbosity
61 real(double) :: a,b,c,d,e, fa,fb,fc
62 real(double) :: p,q,r,s, accur1,xm
63
64 if(present(status)) status = 0
65 verbosity = 2
66 if(present(verbose)) verbosity = verbose
67
68 a = x1
69 b = x2
70 fa = func(a)
71 fb = func(b)
72
73 !Prevent compiler complaints:
74 d = 1.0_dbl
75 e = 2.0_dbl
76
77 if(fa*fb .gt. 0.0_dbl) then ! func(a) and func(b) have the same sign
78 if(verbosity.gt.0) write(0,'(2(A,2ES12.3))') ' libSUFR - root_solver(): root is not bracketed by x1 and x2: ', &
79 x1,x2,' - ',fa,fb
80 root_solver = -huge(0.0_dbl) ! return -huge: the smallest number for this kind
81 if(present(status)) status = 2
82 return
83 end if
84
85 c = b
86 fc = fb
87
88 do iter = 1,max_iter
89 if(fb*fc.gt.0.0_dbl) then ! func(b) and func(c) have the same sign
90 c = a
91 fc = fa
92 d = b-a
93 e = d
94 end if
95
96 if(abs(fc).lt.abs(fb)) then
97 a = b
98 b = c
99 c = a
100 fa = fb
101 fb = fc
102 fc = fa
103 end if
104
105 accur1 = 2*eps*abs(b) + 0.5_dbl*accur
106 xm = 0.5_dbl*(c-b)
107
108 if(abs(xm).le.accur1 .or. deq(fb,0.0_dbl)) then ! Then we have a sufficiently accurate solution
109 root_solver = b
110 return
111 end if
112
113 if(abs(e).ge.accur1 .and. abs(fa).gt.abs(fb)) then
114 s = fb/fa
115 if(deq(a,c)) then
116 p = 2*xm*s
117 q = 1.0_dbl - s
118 else
119 q = fa/fc
120 r = fb/fc
121 p = s * ( 2*xm*q*(q-r) - (b-a)*(r-1.0_dbl) )
122 q = (q-1.0_dbl) * (r-1.0_dbl) * (s-1.0_dbl)
123 end if
124
125 if(p.gt.0.0_dbl) q = -q
126 p = abs(p)
127
128 if(2*p .lt. min(3*xm*q-abs(accur1*q),abs(e*q))) then
129 e = d
130 d = p/q
131 else
132 d = xm
133 e = d
134 end if
135 else
136 d = xm
137 e = d
138 end if
139
140 a = b
141 fa = fb
142
143 if(abs(d) .gt. accur1) then
144 b = b + d
145 else
146 b = b + sign(accur1,xm)
147 end if
148
149 fb = func(b) ! Evaluate the function func
150
151 end do
152
153
154 if(verbosity.gt.0) write(0,'(A)') ' libSUFR - root_solver(): maximum number of iterations exceeded'
155 if(present(status)) status = 1
156
157 root_solver = b
158
159 end function root_solver
160 !*********************************************************************************************************************************
161
162
163
164
165 !*********************************************************************************************************************************
166 !> \brief Use Brent's method and parabolic interpolation to find the minimum of function func that lies between xa and xc.
167 !!
168 !! \param func The function of which the minimum is to be found
169 !! \param ax The lower limit for the x-value of the minimum: xa < x_min < xc
170 !! \param bx A good guess for the x-value of the minimum: xa < xb < xc and func(xb) < min(funx(xa),func(xc))
171 !! \param cx The upper limit for the x-value of the minimum: xa < x_min < xc
172 !! \param accur Relative accuracy with which the minimum is to be found
173 !!
174 !! \param xmin X-value of the minimum (output)
175 !! \param status Status: 0-ok, 1-maximum number of iterations exceeded (output, optional)
176 !! \param verbose Verbosity: 0-print nothing, 1-print errors, 2-print warnings, 3-print info
177 !! (output, optional argument, default=2)
178 !!
179 !! \retval minimum_solver The value of the minimum of the function func, to accuracy accur
180 !!
181 !! \see Numerical Recipes in Fortran 77, Sect.10.2.
182
183 function minimum_solver(func,ax,bx,cx,accur,xmin, status,verbose)
184 use sufr_kinds, only: double, dbl
185 use sufr_numerics, only: deq
186
187 implicit none
188 real(double), intent(in) :: ax,bx,cx,accur
189 real(double), intent(out) :: xmin
190 integer, intent(out), optional :: status
191 integer, intent(in), optional :: verbose
192 real(double) :: minimum_solver
193 real(double), external :: func
194
195 integer, parameter :: max_iter = 100
196 real(double), parameter :: eps = epsilon(0.0_dbl) ! Machine precision
197
198 integer :: iter,verbosity
199 real(double) :: a,b,d,e,e1, p,q,r, accur1,accur2
200 real(double) :: u,v,w,x,xm, fu,fv,fw,fx
201
202
203 if(present(status)) status = 0
204 verbosity = 2
205 if(present(verbose)) verbosity = verbose
206
207 a = min(ax,cx)
208 b = max(ax,cx)
209
210 x = bx
211 v = bx
212 w = bx
213
214 fx = func(x)
215 fv = fx
216 fw = fx
217
218 ! Prevent compiler complaints:
219 d = 1.0_dbl
220 e = 2.0_dbl
221
222
223 accur1 = accur + eps ! Use absolute accuracy
224 accur2 = 2*accur1
225
226 do iter=1,max_iter
227 xm = 0.5_dbl*(a+b)
228
229 if( abs(x-xm) .le. accur2 - 0.5_dbl*(b-a) ) then ! Then we have a sufficiently accurate solution
230 xmin = x
231 minimum_solver = fx
232 return
233 end if
234
235
236 if(abs(e).gt.accur1) then
237 r = (x-w) * (fx-fv)
238 q = (x-v) * (fx-fw)
239 p = (x-v)*q - (x-w)*r
240 q = 2*(q-r)
241 if(q.gt.0.0_dbl) p = -p
242 q = abs(q)
243 e1 = e
244 e = d
245
246 if(abs(p).ge.abs(0.5_dbl*q*e1) .or. p.le.q*(a-x) .or. p.ge.q*(b-x)) then
247 call golden_section(x,xm,a,b,d,e) ! Do a golden-section step
248 else
249 d = p/q
250 u = x+d
251 if(u-a.lt.accur2 .or. b-u.lt.accur2) d = sign(accur1,xm-x)
252 end if
253 else
254 call golden_section(x,xm,a,b,d,e) ! Do a golden-section step
255 end if
256
257 ! Determine the point u where func must be evaluated next:
258 if(abs(d).ge.accur1) then
259 u = x + d
260 else
261 u = x + sign(accur1,d)
262 end if
263
264
265 fu = func(u)
266
267 if(fu.le.fx) then ! func(u) <= func(x): replace x with u and one of the boundaries with x
268 if(u.ge.x) then ! Replace a with x
269 a = x
270 else
271 b = x ! Replace b with x
272 end if
273
274 v = w
275 fv = fw
276 w = x
277 fw = fx
278 x = u ! Replace x with the new point u
279 fx = fu
280
281 else ! func(u) > func(x): keep x, and replace one of the boundaries with u
282
283 if(u.lt.x) then ! Replace a with u
284 a = u
285 else ! Replace b with u
286 b = u
287 end if
288
289 if(fu.le.fw .or. deq(w,x)) then
290 v = w
291 fv = fw
292 w = u
293 fw = fu
294 else if(fu.le.fv .or. deq(v,x) .or. deq(v,w)) then
295 v = u
296 fv = fu
297 end if
298
299 end if
300
301 end do
302
303
304 if(verbosity.gt.0) write(0,'(A)') ' libSUFR - minimum_solver(): maximum number of iterations exceeded'
305 if(present(status)) status = 1
306
307 ! Return the best we have anyway:
308 xmin = x
309 minimum_solver = fx
310
311 end function minimum_solver
312 !*********************************************************************************************************************************
313
314
315
316end module sufr_solvers
317!***********************************************************************************************************************************
318
319
320
321
322!***********************************************************************************************************************************
323!> \brief Do a golden-section step for minimum_solver(): find the point that is a fraction 0.382 into the larger of two intervals
324!!
325!! \param x1
326!! \param x2
327!! \param a1
328!! \param a2
329!!
330!! \param y1 (output)
331!! \param y2 (output)
332
333
334pure subroutine golden_section(x1,x2, a1,a2, y1,y2)
335 use sufr_kinds, only: double, dbl
336
337 implicit none
338 real(double), intent(in) :: x1,x2, a1,a2
339 real(double), intent(out) :: y1,y2
340 real(double), parameter :: golden_ratio = 0.3819660_dbl ! The golden ratio
341
342 if(x1.ge.x2) then
343 y2 = a1 - x1
344 else
345 y2 = a2 - x1
346 end if
347 y1 = y2 * golden_ratio
348
349end subroutine golden_section
350!***********************************************************************************************************************************
351
352
353
Provides kinds and related constants/routines.
Definition kinds.f90:26
integer, parameter double
Double-precision float. Precision = 15, range = 307.
Definition kinds.f90:35
integer, parameter dbl
Double-precision float. Precision = 15, range = 307.
Definition kinds.f90:36
Procedures for numerical operations.
Definition numerics.f90:23
elemental logical function deq(x1, x2, eps)
Test whether two double-precision variables are equal to better than a given value (default: 2x machi...
Definition numerics.f90:90
Procedures to solve equations.
Definition solvers.f90:23
real(double) function root_solver(func, x1, x2, accur, status, verbose)
Using Brent's method, find the root of a function func that lies between x1 and x2.
Definition solvers.f90:46
real(double) function minimum_solver(func, ax, bx, cx, accur, xmin, status, verbose)
Use Brent's method and parabolic interpolation to find the minimum of function func that lies between...
Definition solvers.f90:184
pure subroutine golden_section(x1, x2, a1, a2, y1, y2)
Do a golden-section step for minimum_solver(): find the point that is a fraction 0....
Definition solvers.f90:335