MODFLOW 6  version 6.9.0.dev0
USGS Modular Hydrologic Model
GeomUtil.f90
Go to the documentation of this file.
2  use kindmodule, only: i4b, dp, lgp
3  use errorutilmodule, only: pstop
4  use constantsmodule, only: dzero, done, dtwo, dhalf, &
6 
7  implicit none
8  private
9  public :: between, point_in_polygon, &
13 contains
14 
15  !> @brief Check if a value is between two other values (inclusive).
16  logical function between(x, a, b)
17  real(dp), intent(in) :: x, a, b
18  between = ((x >= a .and. x <= b) .or. (x <= a .and. x >= b))
19  end function between
20 
21  !> @brief Check if a point is within a polygon.
22  !!
23  !! Vertices and edge points are considered in the polygon. By
24  !! default, a point must lie exactly on an edge to be considered
25  !! within the polygon. A tolerance may be specified to instead
26  !! accept coordinates within tol of an edge.
27  !!
28  !! Adapted from https://stackoverflow.com/a/63436180/6514033,
29  !<
30  logical function point_in_polygon(x, y, poly, tol)
31  ! dummy
32  real(dp), intent(in) :: x !< x point coordinate
33  real(dp), intent(in) :: y !< y point coordinate
34  real(dp), allocatable, intent(in) :: poly(:, :) !< polygon vertices (column-major indexing)
35  real(dp), intent(in), optional :: tol !< tolerance (default 0)
36  ! local
37  integer(I4B) :: i, ii, num_verts
38  real(dp) :: xa, xb, ya, yb, c
39  real(dp) :: ltol
40 
41  ltol = dzero
42  if (present(tol)) ltol = tol
43 
44  point_in_polygon = .false.
45  num_verts = size(poly, 2)
46  xa = poly(1, num_verts)
47  ya = poly(2, num_verts)
48 
49  do i = 0, num_verts - 1
50  ii = mod(i, num_verts) + 1
51  xb = poly(1, ii)
52  yb = poly(2, ii)
53 
54  if ((x == xa .and. y == ya) .or. &
55  (x == xb .and. y == yb)) then
56  ! on vertex
57  point_in_polygon = .true.
58  exit
59  else if (ya == yb .and. &
60  abs(y - ya) * abs(xb - xa) <= ltol .and. &
61  between(x, xa, xb)) then
62  ! on (or within tol of) horizontal edge
63  point_in_polygon = .true.
64  exit
65  else if (between(y, ya, yb)) then
66  if ((y == ya .and. yb >= ya) .or. &
67  (y == yb .and. ya >= yb)) then
68  xa = xb
69  ya = yb
70  cycle
71  end if
72  ! cross product
73  c = (xa - x) * (yb - y) - (xb - x) * (ya - y)
74  if (abs(c) <= ltol) then
75  ! on (or within tol of) edge
76  point_in_polygon = .true.
77  exit
78  else if ((ya < yb) .eqv. (c > 0)) then
79  ! ray intersection
81  end if
82  end if
83 
84  xa = xb
85  ya = yb
86  end do
87  end function point_in_polygon
88 
89  !> @brief Get node number, given layer, row, and column indices
90  !! for a structured grid. If any argument is invalid return -1.
91  function get_node(ilay, irow, icol, nlay, nrow, ncol)
92  integer(I4B), intent(in) :: ilay, irow, icol, nlay, nrow, ncol
93  integer(I4B) :: get_node
94 
95  if (nlay > 0 .and. nrow > 0 .and. ncol > 0 .and. &
96  ilay > 0 .and. ilay <= nlay .and. &
97  irow > 0 .and. irow <= nrow .and. &
98  icol > 0 .and. icol <= ncol) then
99  get_node = &
100  icol + ncol * (irow - 1) + (ilay - 1) * nrow * ncol
101  else
102  get_node = -1
103  end if
104  end function get_node
105 
106  !> @brief Get row, column and layer indices from node number and grid
107  !! dimensions. If nodenumber is invalid, irow, icol, and ilay are -1.
108  subroutine get_ijk(nodenumber, nrow, ncol, nlay, irow, icol, ilay)
109  ! -- dummy variables
110  integer(I4B), intent(in) :: nodenumber
111  integer(I4B), intent(in) :: nrow
112  integer(I4B), intent(in) :: ncol
113  integer(I4B), intent(in) :: nlay
114  integer(I4B), intent(out) :: irow
115  integer(I4B), intent(out) :: icol
116  integer(I4B), intent(out) :: ilay
117  ! -- local variables
118  integer(I4B) :: nodes
119  integer(I4B) :: ij
120 
121  nodes = nlay * nrow * ncol
122  if (nodenumber < 1 .or. nodenumber > nodes) then
123  irow = -1
124  icol = -1
125  ilay = -1
126  else
127  ilay = (nodenumber - 1) / (ncol * nrow) + 1
128  ij = nodenumber - (ilay - 1) * ncol * nrow
129  irow = (ij - 1) / ncol + 1
130  icol = ij - (irow - 1) * ncol
131  end if
132  end subroutine get_ijk
133 
134  !> @brief Get layer index and within-layer node index from node number
135  !! and grid dimensions. If nodenumber is invalid, icpl and ilay are -1.
136  subroutine get_jk(nodenumber, ncpl, nlay, icpl, ilay)
137  ! -- dummy variables
138  integer(I4B), intent(in) :: nodenumber
139  integer(I4B), intent(in) :: ncpl
140  integer(I4B), intent(in) :: nlay
141  integer(I4B), intent(out) :: icpl
142  integer(I4B), intent(out) :: ilay
143  ! -- local variables
144  integer(I4B) :: nodes
145 
146  nodes = ncpl * nlay
147  if (nodenumber < 1 .or. nodenumber > nodes) then
148  icpl = -1
149  ilay = -1
150  else
151  ilay = (nodenumber - 1) / ncpl + 1
152  icpl = nodenumber - (ilay - 1) * ncpl
153  end if
154  end subroutine get_jk
155 
156  !> @brief Skew a 2D vector along the x-axis.
157  pure function skew(v, s, invert) result(res)
158  ! -- dummy
159  real(dp), intent(in) :: v(2) !< vector
160  real(dp), intent(in) :: s(3) !< skew matrix entries (top left, top right, bottom right)
161  logical(LGP), intent(in), optional :: invert
162  real(dp) :: res(2)
163  ! -- local
164  logical(LGP) :: linvert
165  real(dp) :: sxx, sxy, syy
166 
167  ! -- process optional arguments
168  if (present(invert)) then
169  linvert = invert
170  else
171  linvert = .false.
172  end if
173 
174  sxx = s(1)
175  sxy = s(2)
176  syy = s(3)
177  if (.not. linvert) then
178  res(1) = sxx * v(1) + sxy * v(2)
179  res(2) = syy * v(2)
180  else
181  res(2) = v(2) / syy
182  res(1) = (v(1) - sxy * res(2)) / sxx
183  end if
184  end function skew
185 
186  !> @brief Apply a 3D translation and optional 2D rotation to coordinates.
187  subroutine transform(xin, yin, zin, &
188  xout, yout, zout, &
189  xorigin, yorigin, zorigin, &
190  sinrot, cosrot, &
191  invert)
192  ! -- dummy
193  real(dp) :: xin, yin, zin !< input coordinates
194  real(dp) :: xout, yout, zout !< output coordinates
195  real(dp), optional :: xorigin, yorigin, zorigin !< origin coordinates
196  real(dp), optional :: sinrot, cosrot !< sine and cosine of rotation
197  logical(LGP), optional :: invert !< whether to invert
198  ! -- local
199  logical(LGP) :: ltranslate, lrotate, linvert
200  real(dp) :: x, y
201  real(dp) :: lxorigin, lyorigin, lzorigin
202  real(dp) :: lsinrot, lcosrot
203 
204  ! -- Process option arguments and set defaults and flags
205  call defaults(lxorigin, lyorigin, lzorigin, &
206  lsinrot, lcosrot, linvert, &
207  ltranslate, lrotate, &
208  xorigin, yorigin, zorigin, &
209  sinrot, cosrot, invert)
210 
211  ! -- Apply transformation or its inverse
212  if (.not. linvert) then
213  ! -- Apply transformation to coordinates
214  if (ltranslate) then
215  xout = xin - lxorigin
216  yout = yin - lyorigin
217  zout = zin - lzorigin
218  else
219  xout = lxorigin
220  yout = lyorigin
221  zout = lzorigin
222  end if
223  if (lrotate) then
224  x = xout
225  y = yout
226  xout = x * lcosrot + y * lsinrot
227  yout = -x * lsinrot + y * lcosrot
228  end if
229  else
230  ! -- Apply inverse of transformation to coordinates
231  if (lrotate) then
232  x = xin * lcosrot - yin * lsinrot
233  y = xin * lsinrot + yin * lcosrot
234  else
235  x = xin
236  y = yin
237  end if
238  if (ltranslate) then
239  xout = x + lxorigin
240  yout = y + lyorigin
241  zout = zin + lzorigin
242  end if
243  end if
244  end subroutine transform
245 
246  !> @brief Compose an affine transform onto an existing one, or remove (invert) it.
247  subroutine compose(xorigin, yorigin, zorigin, &
248  sinrot, cosrot, &
249  xorigin_new, yorigin_new, zorigin_new, &
250  sinrot_new, cosrot_new, &
251  invert)
252  ! -- dummy
253  real(dp) :: xorigin, yorigin, zorigin !< cumulative transform T, origin (in/out)
254  real(dp) :: sinrot, cosrot !< cumulative transform T, rotation as (sin, cos) (in/out)
255  real(dp), optional :: xorigin_new, yorigin_new, zorigin_new !< transform A to compose, origin
256  real(dp), optional :: sinrot_new, cosrot_new !< transform A to compose, rotation as (sin, cos)
257  logical(LGP), optional :: invert !< whether to remove A rather than compose it
258  ! -- local
259  logical(LGP) :: ltranslate, lrotate, linvert
260  real(dp) :: xa, ya, za, sa, ca !< transform A (origin, then sin/cos of phi)
261  real(dp) :: x0, y0, z0, s0, c0 !< incoming T (origin, then sin/cos of its angle)
262 
263  ! -- Process option arguments and set defaults and flags
264  call defaults(xa, ya, za, sa, ca, linvert, &
265  ltranslate, lrotate, &
266  xorigin_new, yorigin_new, zorigin_new, &
267  sinrot_new, cosrot_new, invert)
268 
269  ! -- Copy existing transformation into working copy
270  x0 = xorigin
271  y0 = yorigin
272  z0 = zorigin
273  s0 = sinrot
274  c0 = cosrot
275 
276  ! -- Given transformations A = (a, phi) and T = (t, alpha)
277  ! -- forward: alpha' = alpha + phi, t' = t + R(alpha) a
278  ! -- inverse: alpha = alpha' - phi, t = t' - R(alpha' - phi) a
279  if (.not. linvert) then
280  if (lrotate) then
281  ! -- alpha' = alpha + phi
282  sinrot = ca * s0 + sa * c0
283  cosrot = ca * c0 - sa * s0
284  end if
285  if (ltranslate) then
286  ! -- t' = t + R(alpha) a, with R(alpha) built from the incoming (s0, c0)
287  xorigin = x0 + (c0 * xa - s0 * ya)
288  yorigin = y0 + (s0 * xa + c0 * ya)
289  zorigin = z0 + za
290  end if
291  else
292  if (lrotate) then
293  ! -- alpha = alpha' - phi
294  ! -- (sinrot, cosrot) now hold R(alpha' - phi)
295  sinrot = ca * s0 - sa * c0
296  cosrot = ca * c0 + sa * s0
297  end if
298  if (ltranslate) then
299  ! -- t = t' - R(alpha' - phi) a. For phi = 0 this is t' - R(alpha') a.
300  ! -- if A carries no rotation, (sinrot, cosrot) still hold R(alpha').
301  xorigin = x0 - (cosrot * xa - sinrot * ya)
302  yorigin = y0 - (sinrot * xa + cosrot * ya)
303  zorigin = z0 - za
304  end if
305  end if
306  end subroutine compose
307 
308  !> @brief Process arguments and set defaults. Internal use only.
309  subroutine defaults(xorigin, yorigin, zorigin, &
310  sinrot, cosrot, &
311  invert, translate, rotate, &
312  xorigin_opt, yorigin_opt, zorigin_opt, &
313  sinrot_opt, cosrot_opt, invert_opt)
314  ! -- dummy
315  real(DP) :: xorigin, yorigin, zorigin
316  real(DP) :: sinrot, cosrot
317  logical(LGP) :: invert, translate, rotate
318  real(DP), optional :: xorigin_opt, yorigin_opt, zorigin_opt
319  real(DP), optional :: sinrot_opt, cosrot_opt
320  logical(LGP), optional :: invert_opt
321 
322  translate = .false.
323  xorigin = dzero
324  if (present(xorigin_opt)) then
325  xorigin = xorigin_opt
326  translate = .true.
327  end if
328  yorigin = dzero
329  if (present(yorigin_opt)) then
330  yorigin = yorigin_opt
331  translate = .true.
332  end if
333  zorigin = dzero
334  if (present(zorigin_opt)) then
335  zorigin = zorigin_opt
336  translate = .true.
337  end if
338  rotate = .false.
339  sinrot = dzero
340  cosrot = done
341  if (present(sinrot_opt)) then
342  sinrot = sinrot_opt
343  if (present(cosrot_opt)) then
344  cosrot = cosrot_opt
345  else
346  ! -- If sinrot_opt is specified but cosrot_opt is not,
347  ! -- default to corresponding non-negative cosrot
348  cosrot = dsqrt(done - sinrot * sinrot)
349  end if
350  rotate = .true.
351  else if (present(cosrot_opt)) then
352  cosrot = cosrot_opt
353  ! -- cosrot_opt is specified but sinrot_opt is not, so
354  ! -- default to corresponding non-negative sinrot
355  sinrot = dsqrt(done - cosrot * cosrot)
356  rotate = .true.
357  end if
358  invert = .false.
359  if (present(invert_opt)) invert = invert_opt
360  end subroutine defaults
361 
362  !> @brief Calculate polygon area, with vertices given in CW or CCW order.
363  function area(xv, yv, cw) result(a)
364  ! dummy
365  real(dp), dimension(:), intent(in) :: xv
366  real(dp), dimension(:), intent(in) :: yv
367  logical(LGP), intent(in), optional :: cw
368  ! result
369  real(dp) :: a
370  integer(I4B) :: s
371 
372  if (present(cw)) then
373  if (cw) then
374  s = 1
375  else
376  s = -1
377  end if
378  else
379  s = 1
380  end if
381 
382  a = -dhalf * sum(xv(:) * cshift(yv(:), s) - cshift(xv(:), s) * yv(:))
383 
384  end function area
385 
386  !> @brief Calculate the maximum distance between two polygon vertices.
387  pure function polygon_extent(xv, yv) result(e)
388  ! dummy
389  real(dp), dimension(:), intent(in) :: xv
390  real(dp), dimension(:), intent(in) :: yv
391  ! result
392  real(dp) :: e
393  ! local
394  integer(I4B) :: i
395  integer(I4B) :: j
396  real(dp) :: dx
397  real(dp) :: dy
398 
399  e = dzero
400  do i = 1, size(xv) - 1
401  do j = i + 1, size(xv)
402  dx = xv(i) - xv(j)
403  dy = yv(i) - yv(j)
404  e = max(e, dx * dx + dy * dy)
405  end do
406  end do
407  e = sqrt(e)
408 
409  end function polygon_extent
410 
411  !> @brief Find the lateral face shared by two cells.
412  !!
413  !! Find the lateral (x-y plane) face shared by the given cells.
414  !! The iface return argument will be 0 if they share no such face,
415  !! otherwise the index of the shared face in cell 1's vertex array,
416  !! where face N connects vertex N to vertex N + 1 going clockwise.
417  !!
418  !! Note: assumes the cells are convex and share at most 2 vertices
419  !! and that both vertex arrays are oriented clockwise.
420  !<
421  subroutine shared_face(iverts1, iverts2, iface)
422  integer(I4B), dimension(:) :: iverts1
423  integer(I4B), dimension(:) :: iverts2
424  integer(I4B), intent(out) :: iface
425  integer(I4B) :: nv1
426  integer(I4B) :: nv2
427  integer(I4B) :: il1, iil1
428  integer(I4B) :: il2, iil2
429  logical(LGP) :: found
430  logical(LGP) :: wrapped
431 
432  iface = 0
433  found = .false.
434  nv1 = size(iverts1)
435  nv2 = size(iverts2)
436  wrapped = iverts1(1) == iverts1(nv1)
437 
438  ! Find a vertex shared by the cells, then check the adjacent faces.
439  ! If the cells share a face, it must be one of these. When looking
440  ! forward in the 1st cell's vertices, look backwards in the 2nd's,
441  ! and vice versa, since a clockwise face in cell 1 must correspond
442  ! to a counter-clockwise face in cell 2.
443  outerloop: do il1 = 1, nv1 - 1
444  do il2 = 1, nv2 - 1
445  if (iverts1(il1) == iverts2(il2)) then
446 
447  iil1 = il1 + 1
448  if (il2 == 1) then
449  iil2 = nv2
450  if (wrapped) iil2 = iil2 - 1
451  else
452  iil2 = il2 - 1
453  end if
454  if (iverts1(iil1) == iverts2(iil2)) then
455  found = .true.
456  iface = il1
457  exit outerloop
458  end if
459 
460  iil2 = il2 + 1
461  if (il1 == 1) then
462  iil1 = nv1
463  if (wrapped) iil1 = iil1 - 1
464  else
465  iil1 = il1 - 1
466  end if
467  if (iverts1(iil1) == iverts2(iil2)) then
468  found = .true.
469  iface = iil1
470  exit outerloop
471  end if
472 
473  end if
474  end do
475  if (found) exit
476  end do outerloop
477  end subroutine shared_face
478 
479  !> @brief Clamp barycentric coordinates to the interior of a triangle,
480  !! with optional padding some minimum distance from any face.
481  !!
482  !! This routine requires 0 <= tol <= 1/3 and 1 = alpha + beta + gamma.
483  !<
484  subroutine clamp_bary(alpha, beta, gamma, pad)
485  ! dummy
486  real(dp), intent(inout) :: alpha
487  real(dp), intent(inout) :: beta
488  real(dp), intent(out) :: gamma
489  real(dp), intent(in), optional :: pad
490  ! local
491  real(dp) :: lolimit
492  real(dp) :: hilimit
493  real(dp) :: delta
494  real(dp) :: lpad
495 
496  if (present(pad)) then
497  lpad = pad
498  if (pad < dzero .or. pad > donethird) &
499  call pstop(1, "pad must be between 0 and 1/3, inclusive")
500  else
501  lpad = dzero
502  end if
503 
504  gamma = done - alpha - beta
505  lolimit = lpad
506  hilimit = done - dtwo * lpad
507  ! Check alpha coordinate against lower limit
508  if (alpha < lolimit) then
509  ! Alpha is too low, so nudge alpha to lower limit; this is a move
510  ! parallel to the "alpha axis," which also changes gamma
511  alpha = lolimit
512  gamma = done - alpha - beta
513  ! Check beta coordinate against lower limit (which in this
514  ! case is equivalent to checking gamma coordinate against
515  ! upper limit)
516  if (beta < lolimit) then
517  ! Beta is too low (gamma is too high), so nudge beta to lower limit;
518  ! this is a move parallel to the "beta axis," which also changes gamma
519  beta = lolimit
520  gamma = hilimit
521  ! Check beta coordinate against upper limit (which in this
522  ! case is equivalent to checking gamma coordinate against
523  ! lower limit)
524  else if (beta > hilimit) then
525  ! Beta is too high (gamma is too low), so nudge beta to lower limit;
526  ! this is a move parallel to the "beta axis," which also changes gamma
527  beta = hilimit
528  gamma = lolimit
529  end if
530  end if
531  ! Check beta coordinate against lower limit. (If alpha coordinate
532  ! was nudged to lower limit, beta and gamma coordinates have also
533  ! been adjusted as necessary to place particle within subcell, and
534  ! subsequent checks on beta and gamma will evaluate to false, and
535  ! no further adjustments will be made.)
536  if (beta < lolimit) then
537  ! Beta is too low, so nudge beta to lower limit; this is a move
538  ! parallel to the "beta axis," which also changes gamma
539  beta = lolimit
540  gamma = done - alpha - beta
541  ! Check alpha coordinate against lower limit (which in this
542  ! case is equivalent to checking gamma coordinate against
543  ! upper limit)
544  if (alpha < lolimit) then
545  ! Alpha is too low (gamma is too high), so nudge alpha to lower limit;
546  ! this is a move parallel to the "alpha axis," which also changes gamma
547  alpha = lolimit
548  gamma = hilimit
549  ! Check alpha coordinate against upper limit (which in this
550  ! case is equivalent to checking gamma coordinate against
551  ! lower limit)
552  else if (alpha > hilimit) then
553  ! Alpha is too high (gamma is too low), so nudge alpha to lower limit;
554  ! this is a move parallel to the "alpha axis," which also changes gamma
555  alpha = hilimit
556  gamma = lolimit
557  end if
558  end if
559  ! Check gamma coordinate against lower limit.(If alpha and/or beta
560  ! coordinate was nudged to lower limit, gamma coordinate has also
561  ! been adjusted as necessary to place particle within subcell, and
562  ! subsequent check on gamma will evaluate to false, and no further
563  ! adjustment will be made.)
564  if (gamma < lolimit) then
565  ! Gamma is too low, so nudge gamma to lower limit; this is a move
566  ! parallel to the "gamma axis," which also changes alpha and beta
567  delta = dhalf * (lolimit - gamma)
568  gamma = lpad
569  alpha = alpha - delta
570  beta = beta - delta
571  ! Check beta coordinate against lower limit (which in this
572  ! case is equivalent to checking alpha coordinate against
573  ! upper limit)
574  if (beta < lolimit) then
575  ! Beta is too low (alpha is too high), so nudge beta to lower limit;
576  ! this is a move parallel to the "gamma axis," which also changes alpha
577  beta = lolimit
578  alpha = done - beta - gamma
579  ! Check beta coordinate against upper limit (which in this
580  ! case is equivalent to checking gamma coordinate against
581  ! lower limit)
582  else if (beta > hilimit) then
583  ! Beta is too high (alpha is too low), so nudge beta to lower limit;
584  ! this is a move parallel to the "gamma axis," which also changes alpha
585  beta = hilimit
586  alpha = done - beta - gamma
587  end if
588  end if
589  end subroutine clamp_bary
590 
591 end module geomutilmodule
This module contains simulation constants.
Definition: Constants.f90:9
real(dp), parameter dep3
real constant 1000
Definition: Constants.f90:88
real(dp), parameter donethird
real constant 1/3
Definition: Constants.f90:67
real(dp), parameter dhalf
real constant 1/2
Definition: Constants.f90:68
real(dp), parameter dzero
real constant zero
Definition: Constants.f90:65
real(dp), parameter dtwo
real constant 2
Definition: Constants.f90:79
real(dp), parameter done
real constant 1
Definition: Constants.f90:76
subroutine pstop(status, message)
Stop the program, optionally specifying an error status code.
Definition: ErrorUtil.f90:24
subroutine, public shared_face(iverts1, iverts2, iface)
Find the lateral face shared by two cells.
Definition: GeomUtil.f90:422
subroutine, public transform(xin, yin, zin, xout, yout, zout, xorigin, yorigin, zorigin, sinrot, cosrot, invert)
Apply a 3D translation and optional 2D rotation to coordinates.
Definition: GeomUtil.f90:192
integer(i4b) function, public get_node(ilay, irow, icol, nlay, nrow, ncol)
Get node number, given layer, row, and column indices for a structured grid. If any argument is inval...
Definition: GeomUtil.f90:92
subroutine defaults(xorigin, yorigin, zorigin, sinrot, cosrot, invert, translate, rotate, xorigin_opt, yorigin_opt, zorigin_opt, sinrot_opt, cosrot_opt, invert_opt)
Process arguments and set defaults. Internal use only.
Definition: GeomUtil.f90:314
logical function, public between(x, a, b)
Check if a value is between two other values (inclusive).
Definition: GeomUtil.f90:17
logical function, public point_in_polygon(x, y, poly, tol)
Check if a point is within a polygon.
Definition: GeomUtil.f90:31
subroutine, public clamp_bary(alpha, beta, gamma, pad)
Clamp barycentric coordinates to the interior of a triangle, with optional padding some minimum dista...
Definition: GeomUtil.f90:485
real(dp) function, public area(xv, yv, cw)
Calculate polygon area, with vertices given in CW or CCW order.
Definition: GeomUtil.f90:364
pure real(dp) function, public polygon_extent(xv, yv)
Calculate the maximum distance between two polygon vertices.
Definition: GeomUtil.f90:388
subroutine, public compose(xorigin, yorigin, zorigin, sinrot, cosrot, xorigin_new, yorigin_new, zorigin_new, sinrot_new, cosrot_new, invert)
Compose an affine transform onto an existing one, or remove (invert) it.
Definition: GeomUtil.f90:252
subroutine, public get_ijk(nodenumber, nrow, ncol, nlay, irow, icol, ilay)
Get row, column and layer indices from node number and grid dimensions. If nodenumber is invalid,...
Definition: GeomUtil.f90:109
pure real(dp) function, dimension(2), public skew(v, s, invert)
Skew a 2D vector along the x-axis.
Definition: GeomUtil.f90:158
subroutine, public get_jk(nodenumber, ncpl, nlay, icpl, ilay)
Get layer index and within-layer node index from node number and grid dimensions. If nodenumber is in...
Definition: GeomUtil.f90:137
This module defines variable data types.
Definition: kind.f90:8