MODFLOW 6 version 6.9.0.dev0
USGS Modular Hydrologic Model
Loading...
Searching...
No Matches
mathutilmodule Module Reference

Data Types

interface  mod_offset
interface  f1d

Functions/Subroutines

pure logical function, public is_close (a, b, rtol, atol, symmetric)
 Check if a real value is approximately equal to another.
pure integer(i4b) function mod_offset_int (a, n, d)
 Modulo with offset for integer values.
pure real(dp) function mod_offset_dbl (a, n, d)
 Modulo with offset for double precision values.
real(dp) function, public zero_ch (x0, x1, f, epsa)
 Compute zeros on an interval using Chadrupatla's method.
real(dp) function, public zero_br (ax, bx, f, tol)
 Compute a zero of the function f(x) in the interval (x0, x1).
real(dp) function, public get_perturbation (x)
 Calculate a numerical perturbation given the value of x.
pure real(dp) function, dimension(:), allocatable, public arange (start, stop, step)
 Return reals separated by the given step over the given interval.
pure real(dp) function, dimension(:), allocatable, public linspace (start, stop, num)
 Return evenly spaced reals over the given interval.

Function/Subroutine Documentation

◆ arange()

pure real(dp) function, dimension(:), allocatable, public mathutilmodule::arange ( real(dp), intent(in) start,
real(dp), intent(in) stop,
real(dp), intent(in), optional step )

This function is designed to behave like NumPy's arange. Adapted from https://stackoverflow.com/a/65839162/6514033.

Definition at line 383 of file MathUtil.f90.

384 ! dummy
385 real(DP), intent(in) :: start, stop
386 real(DP), intent(in), optional :: step
387 real(DP), allocatable :: array(:)
388 ! local
389 real(DP) :: lstep
390 integer(I4B) :: i, n
391
392 if (present(step)) then
393 lstep = step
394 else
395 lstep = done
396 end if
397
398 if (step <= 0) then
399 allocate (array(0))
400 else
401 n = ceiling((stop - start) / step)
402 allocate (array(n))
403 do i = 0, n - 1
404 array(i + 1) = start + step * i
405 end do
406 end if
Here is the caller graph for this function:

◆ get_perturbation()

real(dp) function, public mathutilmodule::get_perturbation ( real(dp), intent(in) x)

Calculate a perturbation value to use for a numerical derivative calculation. Taken from the book "Solving Nonlinear Equations with Newton's Method" 2003, by C.T. Kelley. Method also used in the SWR Process for MODFLOW-2005.

Parameters
[in]xvalue that will be perturbed by the result
Returns
calculated perturbation value

Definition at line 371 of file MathUtil.f90.

372 use constantsmodule, only: dprecsqrt, done
373 real(DP), intent(in) :: x !< value that will be perturbed by the result
374 real(DP) :: res !< calculated perturbation value
375 res = dprecsqrt * max(abs(x), done) * sign(done, x)
This module contains simulation constants.
Definition Constants.f90:9
real(dp), parameter dprecsqrt
real(dp), parameter done
real constant 1
Definition Constants.f90:76
Here is the caller graph for this function:

◆ is_close()

pure logical function, public mathutilmodule::is_close ( real(dp), intent(in) a,
real(dp), intent(in) b,
real(dp), intent(in), optional rtol,
real(dp), intent(in), optional atol,
logical(lgp), intent(in), optional symmetric )

By default the determination is symmetric in a and b, as in Python's math.isclose, with relative difference scaled by a factor of the larger absolute value of a and b. The formula is: abs(a - b) <= max(rtol * max(abs(a), abs(b)), atol).

If symmetric is set to false the test is asymmetric in a and b, with b taken to be the reference value, and the alternate formula (abs(a - b) <= (atol + rtol * abs(b))) is used. This is the approach taken by numpy.allclose.

Defaults for rtol and atol are DSAME and DZERO, respectively. If a or b are near 0 (especially if either is 0), an absolute tolerance suitable for the particular case should be provided. For a justification of a zero absolute tolerance default see: https://peps.python.org/pep-0485/#absolute-tolerance-default

Parameters
[in]afirst real
[in]bsecond real (reference value if asymmetric)
[in]rtolrelative tolerance (default=DSAME)
[in]atolabsolute tolerance (default=DZERO)
[in]symmetrictoggle (a)symmetric comparison

Definition at line 45 of file MathUtil.f90.

46 ! dummy
47 real(DP), intent(in) :: a !< first real
48 real(DP), intent(in) :: b !< second real (reference value if asymmetric)
49 real(DP), intent(in), optional :: rtol !< relative tolerance (default=DSAME)
50 real(DP), intent(in), optional :: atol !< absolute tolerance (default=DZERO)
51 logical(LGP), intent(in), optional :: symmetric !< toggle (a)symmetric comparison
52 ! local
53 real(DP) :: lrtol, latol
54 logical(LGP) :: lsymmetric
55
56 ! check for exact equality
57 if (a == b) then
58 is_close = .true.
59 return
60 end if
61
62 ! process optional arguments
63 if (.not. present(rtol)) then
64 lrtol = dsame
65 else
66 lrtol = rtol
67 end if
68 if (.not. present(atol)) then
69 latol = dzero
70 else
71 latol = atol
72 end if
73 if (.not. present(symmetric)) then
74 lsymmetric = .true.
75 else
76 lsymmetric = symmetric
77 end if
78
79 if (lsymmetric) then
80 ! "weak" symmetric test, https://peps.python.org/pep-0485/#which-symmetric-test
81 is_close = abs(a - b) <= max(lrtol * max(abs(a), abs(b)), latol)
82 else
83 ! asymmetric, https://numpy.org/doc/stable/reference/generated/numpy.isclose.html
84 is_close = (abs(a - b) <= (latol + lrtol * abs(b)))
85 end if
Here is the call graph for this function:
Here is the caller graph for this function:

◆ linspace()

pure real(dp) function, dimension(:), allocatable, public mathutilmodule::linspace ( real(dp), intent(in) start,
real(dp), intent(in) stop,
integer(i4b), intent(in) num )

This function is designed to behave like NumPy's linspace. Adapted from https://stackoverflow.com/a/57211848/6514033.

Definition at line 414 of file MathUtil.f90.

415 ! dummy
416 real(DP), intent(in) :: start, stop
417 integer(I4B), intent(in) :: num
418 real(DP), allocatable :: array(:)
419 ! local
420 real(DP) :: range
421 integer(I4B) :: i
422
423 allocate (array(num))
424 range = stop - start
425
426 if (num == 0) return
427 if (num == 1) then
428 array(1) = start
429 return
430 end if
431 do i = 1, num
432 array(i) = start + range * (i - 1) / (num - 1)
433 end do

◆ mod_offset_dbl()

pure real(dp) function mathutilmodule::mod_offset_dbl ( real(dp), intent(in) a,
real(dp), intent(in) n,
real(dp), intent(in), optional d )
private
Parameters
[in]adividend
[in]ndivisor
[in]doffset

Definition at line 107 of file MathUtil.f90.

108 ! -- dummy
109 real(DP), intent(in) :: a !< dividend
110 real(DP), intent(in) :: n !< divisor
111 real(DP), intent(in), optional :: d !< offset
112 real(DP) :: mo
113 ! -- local
114 real(DP) :: ld
115
116 if (present(d)) then
117 ld = d
118 else
119 ld = 0
120 end if
121 mo = a - n * floor((a - ld) / n)

◆ mod_offset_int()

pure integer(i4b) function mathutilmodule::mod_offset_int ( integer(i4b), intent(in) a,
integer(i4b), intent(in) n,
integer(i4b), intent(in), optional d )
private
Parameters
[in]adividend
[in]ndivisor
[in]doffset

Definition at line 89 of file MathUtil.f90.

90 ! -- dummy
91 integer(I4B), intent(in) :: a !< dividend
92 integer(I4B), intent(in) :: n !< divisor
93 integer(I4B), intent(in), optional :: d !< offset
94 integer(I4B) :: mo
95 ! -- local
96 integer(I4B) :: ld
97
98 if (present(d)) then
99 ld = d
100 else
101 ld = 0
102 end if
103 mo = a - n * floor(real(a - ld) / n)

◆ zero_br()

real(dp) function, public mathutilmodule::zero_br ( real(dp) ax,
real(dp) bx,
procedure(f1d), intent(in), pointer f,
real(dp) tol )

A zero of the function f(x) is computed in the interval ax,bx.

Input:

ax left endpoint of initial interval bx right endpoint of initial interval f function subprogram which evaluates f(x) for any x in the interval ax,bx tol desired length of the interval of uncertainty of the final result (.ge.0.)

Output:

zero_br abscissa approximating a zero of f in the interval ax,bx

it is assumed  that   f(ax)   and   f(bx)   have  opposite  signs

this is checked, and an error message is printed if this is not satisfied. zero_br returns a zero x in the given interval ax,bx to within a tolerance 4*macheps*abs(x)+tol, where macheps is the relative machine precision defined as the smallest representable number such that 1.+macheps .gt. 1. this function subprogram is a slightly modified translation of the algol 60 procedure zero given in richard brent, algorithms for minimization without derivatives, prentice-hall, inc. (1973).

This subroutine was obtained by the authors of MODFLOW 6 from netlib.org with the understanding that it is freely available. It has subsequently undergone minor modification to suit the current application.

Definition at line 254 of file MathUtil.f90.

255 ! -- dummy
256 real(DP) :: ax, bx
257 procedure(f1d), pointer, intent(in) :: f
258 real(DP) :: tol
259 real(DP) :: z
260 ! -- local
261 real(DP) :: eps
262 real(DP) :: a, b, c, d, e, s, p, q
263 real(DP) :: fa, fb, fc, r, tol1, xm
264 logical(LGP) :: rs
265
266 eps = epsilon(ax)
267 tol1 = eps + done
268
269 a = ax
270 b = bx
271 fa = f(a)
272 fb = f(b)
273
274 ! check if a or b is the solution
275 if (fa == dzero) then
276 z = a
277 return
278 else if (fb == dzero) then
279 z = b
280 return
281 end if
282
283 ! check that f(ax) and f(bx) have opposite sign
284 if (fa * (fb / dabs(fb)) .ge. dzero) &
285 call pstop(1, 'f(ax) and f(bx) do not have different signs,')
286
287 rs = .true. ! var reset
288 do while (.true.)
289 if (rs) then
290 c = a
291 fc = fa
292 d = b - a
293 e = d
294 end if
295
296 if (.not. (dabs(fc) .ge. dabs(fb))) then
297 a = b
298 b = c
299 c = a
300 fa = fb
301 fb = fc
302 fc = fa
303 end if
304
305 tol1 = dtwo * eps * dabs(b) + dhalf * tol
306 xm = dhalf * (c - b)
307 if ((dabs(xm) .le. tol1) .or. (fb .eq. dzero)) then
308 z = b
309 return
310 end if
311
312 ! see if a bisection is forced
313 if ((dabs(e) .ge. tol1) .and. (dabs(fa) .gt. dabs(fb))) then
314 s = fb / fa
315 if (a .ne. c) then
316 ! inverse quadratic interpolation
317 q = fa / fc
318 r = fb / fc
319 p = s * (dtwo * xm * q * (q - r) - (b - a) * (r - done))
320 q = (q - done) * (r - done) * (s - done)
321 else
322 ! linear interpolation
323 p = dtwo * xm * s
324 q = done - s
325 end if
326
327 if (p .le. dzero) then
328 p = -p
329 else
330 q = -q
331 end if
332 s = e
333 e = d
334 if (((dtwo * p) .ge. (dthree * xm * q - dabs(tol1 * q))) .or. &
335 (p .ge. dabs(dhalf * s * q))) then
336 d = xm
337 e = d
338 else
339 d = p / q
340 end if
341 else
342 d = xm
343 e = d
344 end if
345
346 a = b
347 fa = fb
348
349 if (dabs(d) .le. tol1) then
350 if (xm .le. dzero) then
351 b = b - tol1
352 else
353 b = b + tol1
354 end if
355 else
356 b = b + d
357 end if
358
359 fb = f(b)
360 rs = (fb * (fc / dabs(fc))) .gt. dzero
361 end do
Here is the call graph for this function:
Here is the caller graph for this function:

◆ zero_ch()

real(dp) function, public mathutilmodule::zero_ch ( real(dp) x0,
real(dp) x1,
procedure(f1d), intent(in), pointer f,
real(dp) epsa )

A zero of the function f{x} is computed in the interval (x0, x1) given tolerance epsa using Chandrupatla's method. FORTRAN code based generally on pseudocode in Scherer, POJ (2013) "Computational Physics: Simulation of Classical and Quantum Systems," 2nd ed., Springer, New York.

Definition at line 131 of file MathUtil.f90.

132 ! -- dummy
133 real(DP) :: x0, x1
134 procedure(f1d), pointer, intent(in) :: f
135 real(DP) :: epsa
136 real(DP) :: z
137 ! -- local
138 real(DP) :: epsm
139 real(DP) :: a, b, c, t
140 real(DP) :: aminusb, cminusb
141 real(DP) :: fa, fb, fc, fm, ft
142 real(DP) :: faminusfb, fcminusfb
143 real(DP) :: phi, philo, phihi
144 real(DP) :: racb, rcab, rbca
145 real(DP) :: tol, tl, tlc
146 real(DP) :: xi, xm, xt
147
148 epsm = epsilon(x0)
149 b = x0
150 a = x1
151 c = x1
152 aminusb = a - b
153 fb = f(b)
154 fa = f(a)
155 fc = f(c)
156 t = 5d-1
157
158 ! check whether a or b is the solution
159 if (fa == dzero) then
160 z = a
161 return
162 else if (fb == dzero) then
163 z = b
164 return
165 end if
166
167 do while (.true.)
168 xt = a - t * aminusb
169 ft = f(xt)
170 if (sign(ft, fa) == ft) then
171 c = a
172 fc = fa
173 a = xt
174 fa = ft
175 else
176 c = b
177 b = a
178 a = xt
179 fc = fb
180 fb = fa
181 fa = ft
182 end if
183 aminusb = a - b
184 cminusb = c - b
185 faminusfb = fa - fb
186 fcminusfb = fc - fb
187 xm = a
188 fm = fa
189 if (dabs(fb) < dabs(fa)) then
190 xm = b
191 fm = fb
192 end if
193 tol = dtwo * epsm * dabs(xm) + epsa
194 tl = tol / dabs(cminusb)
195 if ((tl > 5d-1) .or. (fm == dzero)) then
196 z = xm
197 return
198 end if
199 xi = aminusb / cminusb
200 phi = faminusfb / fcminusfb
201 philo = done - dsqrt(done - xi)
202 phihi = dsqrt(xi)
203 if ((phi > philo) .and. (phi < phihi)) then
204 racb = fa / fcminusfb
205 rcab = fc / faminusfb
206 rbca = fb / (fc - fa)
207 t = racb * (rcab - rbca * (c - a) / aminusb)
208 if (t < tl) then
209 t = tl
210 else
211 tlc = done - tl
212 if (t > tlc) then
213 t = tlc
214 end if
215 end if
216 else
217 t = 5d-1
218 end if
219 end do
Here is the caller graph for this function: