MODFLOW 6  version 6.5.0.dev2
MODFLOW 6 Code Documentation
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. More...
 
pure integer(i4b) function mod_offset_int (a, n, d)
 Modulo with offset for integer values. More...
 
pure real(dp) function mod_offset_dbl (a, n, d)
 Modulo with offset for double precision values. More...
 
real(dp) function, public zero_ch (x0, x1, f, epsa)
 Compute zeros on an interval using Chadrupatla's method. More...
 
real(dp) function, public zero_br (ax, bx, f, tol)
 Compute a zero of the function f(x) in the interval (x0, x1). More...
 
real(dp) function, public zero_test (x0, x1, f, epsa)
 Compute a zero of f(x) in the interval (x0, x1) with a test method. More...
 
real(dp) function, public get_perturbation (x)
 Calculate a numerical perturbation given the value of x. More...
 

Function/Subroutine Documentation

◆ 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 445 of file MathUtil.f90.

446  use constantsmodule, only: dprecsqrt, done
447  real(DP), intent(in) :: x !< value that will be perturbed by the result
448  real(DP) :: res !< calculated perturbation value
449  res = dprecsqrt * max(abs(x), done) * sign(done, x)
This module contains simulation constants.
Definition: Constants.f90:9
real(dp), parameter dprecsqrt
Definition: Constants.f90:120
real(dp), parameter done
real constant 1
Definition: Constants.f90:75
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 caller graph for this function:

◆ 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).

Definition at line 256 of file MathUtil.f90.

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

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

◆ zero_test()

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

Definition at line 359 of file MathUtil.f90.

360  ! -- dummy
361  real(DP) :: x0, x1
362  procedure(f1d), pointer, intent(in) :: f
363  real(DP) :: epsa
364  real(DP) :: z
365  ! -- local
366  real(DP) :: epsm
367  real(DP) :: ema, emb
368  real(DP) :: f0
369  real(DP) :: tol
370  real(DP) :: xa, xb, xl
371  real(DP) :: ya, yb, yl
372  logical(LGP) :: retainedxa, retainedxb
373 
374  epsm = epsilon(x0)
375  f0 = f(x0)
376  if (f0 .eq. 0d0) then
377  z = x0
378  return
379  else if (f0 .lt. 0d0) then
380  ya = x0
381  yb = x1
382  xa = f0
383  xb = f(yb)
384  else
385  ya = x1
386  yb = x0
387  xa = f(ya)
388  xb = f0
389  end if
390  ema = 1d0
391  emb = 1d0
392  retainedxa = .false.
393  retainedxb = .false.
394 
395  do while (.true.)
396  ! yl = ya - xa*(yb - ya)/(xb - xa)
397  yl = (ya * xb * emb - yb * xa * ema) / (xb * emb - xa * ema)
398  tol = 4d0 * epsm * dabs(yl) + epsa
399  if (dabs(yb - ya) .le. tol) then
400  z = yl
401  return
402  else
403  xl = f(yl)
404  if (xl .eq. 0d0) then
405  z = yl
406  return
407  else if (xl .gt. 0d0) then
408  if (retainedxa) then
409  ! ema = 1d0 - xl/xb
410  ! if (ema <= 0d0) ema = 5d-1
411  ema = 5d-1 ! kluge illinois
412  else
413  ema = 1d0
414  end if
415  emb = 1d0
416  yb = yl
417  xb = xl
418  retainedxa = .true.
419  retainedxb = .false.
420  else
421  if (retainedxb) then
422  ! emb = 1d0 - xl/xa
423  ! if (emb <= 0d0) emb = 5d-1
424  emb = 5d-1 ! kluge illinois
425  else
426  emb = 1d0
427  end if
428  ema = 1d0
429  ya = yl
430  xa = xl
431  retainedxa = .false.
432  retainedxb = .true.
433  end if
434  end if
435  end do
Here is the caller graph for this function: