MODFLOW 6  version 6.6.0.dev0
USGS Modular Hydrologic Model
Xt3dAlgorithm.f90
Go to the documentation of this file.
1 !
2 ! -- Mathematical core of the XT3D method.
3 !
5 
6  use kindmodule, only: dp, i4b
7  use constantsmodule, only: dprec, done
8  implicit none
9 
10 contains
11 
12  !> @brief Compute the "conductances" in the normal-flux expression for an
13  !! interface (modflow-usg version). The cell on one side of the interface is
14  !! "cell 0", and the one on the other side is "cell 1".
15  !!
16  !! nnbrmx = maximum number of neighbors allowed for a cell.
17  !! nnbr0 = number of neighbors (local connections) for cell 0.
18  !! inbr0 = array with the list of neighbors for cell 0.
19  !! il01 = local node number of cell 1 with respect to cell 0.
20  !! vc0 = array of connection unit-vectors for cell 0 with its neighbors.
21  !! vn0 = array of unit normal vectors for cell 0's interfaces.
22  !! dl0 = array of lengths contributed by cell 0 to its connections with its
23  !! neighbors.
24  !! dl0n = array of lengths contributed by cell 0's neighbors to their
25  !! connections with cell 0.
26  !! ck0 = conductivity tensor for cell 0.
27  !! nnbr1 = number of neighbors (local connections) for cell 1.
28  !! inbr1 = array with the list of neighbors for cell 1.
29  !! il10 = local node number of cell 0 with respect to cell 1.
30  !! vc1 = array of connection unit-vectors for cell 1 with its neighbors.
31  !! vn1 = array of unit normal vectors for cell 1's interfaces.
32  !! dl1 = array of lengths contributed by cell 1 to its connections with its
33  !! neighbors.
34  !! dl1n = array of lengths contributed by cell 1's neighbors to their
35  !! connections with cell 1.
36  !! ck1 = conductivity tensor for cell1.
37  !! ar01 = area of interface (0,1).
38  !! ar10 = area of interface (1,0).
39  !! chat01 = "conductance" for connection (0,1).
40  !! chati0 = array of "conductances" for connections of cell 0.
41  !! (zero for connection with cell 1, as this connection is
42  !! already covered by chat01.)
43  !! chat1j = array of "conductances" for connections of cell 1.
44  !! (zero for connection with cell 0., as this connection is
45  !! already covered by chat01.)
46  !<
47  subroutine qconds(nnbrmx, nnbr0, inbr0, il01, vc0, vn0, dl0, dl0n, ck0, &
48  nnbr1, inbr1, il10, vc1, vn1, dl1, dl1n, ck1, ar01, ar10, &
49  vcthresh, allhc0, allhc1, chat01, chati0, chat1j)
50  ! -- dummy
51  integer(I4B) :: nnbrmx
52  integer(I4B) :: nnbr0
53  integer(I4B), dimension(nnbrmx) :: inbr0
54  integer(I4B) :: il01
55  real(DP), dimension(nnbrmx, 3) :: vc0
56  real(DP), dimension(nnbrmx, 3) :: vn0
57  real(DP), dimension(nnbrmx) :: dl0
58  real(DP), dimension(nnbrmx) :: dl0n
59  real(DP), dimension(3, 3) :: ck0
60  integer(I4B) :: nnbr1
61  integer(I4B), dimension(nnbrmx) :: inbr1
62  integer(I4B) :: il10
63  real(DP), dimension(nnbrmx) :: vc1
64  real(DP), dimension(nnbrmx) :: vn1
65  real(DP), dimension(nnbrmx) :: dl1
66  real(DP), dimension(nnbrmx) :: dl1n
67  real(DP), dimension(3, 3) :: ck1
68  real(DP) :: ar01
69  real(DP) :: ar10
70  real(DP) :: vcthresh
71  logical :: allhc0
72  logical :: allhc1
73  real(DP) :: chat01
74  real(DP), dimension(nnbrmx) :: chati0
75  real(DP), dimension(nnbrmx) :: chat1j
76  ! -- local
77  integer(I4B) :: i1
78  integer(I4B) :: i
79  real(DP) :: ahat0
80  real(DP) :: ahat1
81  real(DP) :: wght1
82  real(DP) :: wght0
83  real(DP), dimension(nnbrmx) :: bhat0
84  real(DP), dimension(nnbrmx) :: bhat1
85  real(DP) :: denom
86  !
87  ! -- Set the global cell number for cell 1, as found in the neighbor list
88  ! for cell 0. It is assumed that cells 0 and 1 are both active, or else
89  ! this subroutine would not have been called.
90  i1 = inbr0(il01)
91  !
92  ! -- If area ar01 is zero (in which case ar10 is also zero, since this can
93  ! only happen here in the case of Newton), then the "conductances" are
94  ! all zero.
95  if (ar01 .eq. 0d0) then
96  chat01 = 0d0
97  do i = 1, nnbrmx
98  chati0(i) = 0d0
99  chat1j(i) = 0d0
100  end do
101  ! -- Else compute "conductances."
102  else
103  ! -- Compute contributions from cell 0.
104  call abhats(nnbrmx, nnbr0, inbr0, il01, vc0, vn0, dl0, dl0n, ck0, &
105  vcthresh, allhc0, ar01, ahat0, bhat0)
106  ! -- Compute contributions from cell 1.
107  call abhats(nnbrmx, nnbr1, inbr1, il10, vc1, vn1, dl1, dl1n, ck1, &
108  vcthresh, allhc1, ar10, ahat1, bhat1)
109  ! -- Compute "conductances" based on the two flux estimates.
110  denom = (ahat0 + ahat1)
111  if (abs(denom) > dprec) then
112  wght1 = ahat0 / (ahat0 + ahat1)
113  else
114  wght1 = done
115  end if
116  wght0 = 1d0 - wght1
117  chat01 = wght1 * ahat1
118  do i = 1, nnbrmx
119  chati0(i) = wght0 * bhat0(i)
120  chat1j(i) = wght1 * bhat1(i)
121  end do
122  end if
123  end subroutine qconds
124 
125  !> @brief Compute "ahat" and "bhat" coefficients for one side of an interface
126  !<
127  subroutine abhats(nnbrmx, nnbr, inbr, il01, vc, vn, dl0, dln, ck, &
128  vcthresh, allhc, ar01, ahat, bhat)
129  ! -- dummy
130  integer(I4B) :: nnbrmx
131  integer(I4B) :: nnbr
132  integer(I4B), dimension(nnbrmx) :: inbr
133  integer(I4B) :: il01
134  real(DP), dimension(nnbrmx, 3) :: vc
135  real(DP), dimension(nnbrmx, 3) :: vn
136  real(DP), dimension(nnbrmx) :: dl0
137  real(DP), dimension(nnbrmx) :: dln
138  real(DP), dimension(3, 3) :: ck
139  real(DP) :: vcthresh
140  logical :: allhc
141  real(DP) :: ar01
142  real(DP) :: ahat
143  real(DP), dimension(nnbrmx) :: bhat
144  ! -- local
145  logical :: iscomp
146  real(DP), dimension(nnbrmx, 3) :: vccde
147  real(DP), dimension(3, 3) :: rmat
148  real(DP), dimension(3) :: sigma
149  real(DP), dimension(nnbrmx) :: bd
150  real(DP), dimension(nnbrmx) :: be
151  real(DP), dimension(nnbrmx) :: betad
152  real(DP), dimension(nnbrmx) :: betae
153  integer(I4B) :: iml0
154  integer(I4B) :: il
155  real(DP) :: acd
156  real(DP) :: add
157  real(DP) :: aed
158  real(DP) :: ace
159  real(DP) :: aee
160  real(DP) :: ade
161  real(DP) :: determ
162  real(DP) :: oodet
163  real(DP) :: alphad
164  real(DP) :: alphae
165  real(DP) :: dl0il
166  !
167  ! -- Determine the basis vectors for local "(c, d, e)" coordinates
168  ! associated with the connection between cells 0 and 1, and set the
169  ! rotation matrix that transforms vectors from model coordinates to
170  ! (c, d, e) coordinates. (If no active connection is found that has a
171  ! non-negligible component perpendicular to the primary connection,
172  ! ilmo=0 is returned.)
173  call getrot(nnbrmx, nnbr, inbr, vc, il01, rmat, iml0)
174  !
175  ! -- If no active connection with a non-negligible perpendicular
176  ! component, assume no perpendicular gradient and base gradient solely
177  ! on the primary connection. Otherwise, proceed with basing weights on
178  ! information from neighboring connections.
179  if (iml0 .eq. 0) then
180  !
181  ! -- Compute ahat and bhat coefficients assuming perpendicular components
182  ! of gradient are zero.
183  sigma(1) = dot_product(vn(il01, :), matmul(ck, rmat(:, 1)))
184  ahat = sigma(1) / dl0(il01)
185  bhat = 0d0
186  else
187  !
188  ! -- Transform local connection unit-vectors from model coordinates to
189  ! "(c, d, e)" coordinates associated with the connection between cells
190  ! 0 and 1.
191  call tranvc(nnbrmx, nnbr, rmat, vc, vccde)
192  !
193  ! -- Get "a" and "b" weights for first perpendicular direction.
194  call abwts(nnbrmx, nnbr, inbr, il01, 2, vccde, &
195  vcthresh, dl0, dln, acd, add, aed, bd)
196  !
197  ! -- If all neighboring connections are user-designated as horizontal, or
198  ! if none have a non-negligible component in the second perpendicular
199  ! direction, assume zero gradient in the second perpendicular direction.
200  ! Otherwise, get "a" and "b" weights for second perpendicular direction
201  ! based on neighboring connections.
202  if (allhc) then
203  ace = 0d0
204  aee = 1d0
205  ade = 0d0
206  be = 0d0
207  else
208  iscomp = .false.
209  do il = 1, nnbr
210  if ((il == il01) .or. (inbr(il) == 0)) then
211  cycle
212  else if (dabs(vccde(il, 3)) > 1d-10) then
213  iscomp = .true.
214  exit
215  end if
216  end do
217  if (iscomp) then
218  call abwts(nnbrmx, nnbr, inbr, il01, 3, vccde, &
219  vcthresh, dl0, dln, ace, aee, ade, be)
220  else
221  ace = 0d0
222  aee = 1d0
223  ade = 0d0
224  be = 0d0
225  end if
226  end if
227  !
228  ! -- Compute alpha and beta coefficients.
229  determ = add * aee - ade * aed
230  oodet = 1d0 / determ
231  alphad = (acd * aee - ace * aed) * oodet
232  alphae = (ace * add - acd * ade) * oodet
233  betad = 0d0
234  betae = 0d0
235  do il = 1, nnbr
236  ! -- If this is connection (0,1) or inactive, skip.
237  if ((il == il01) .or. (inbr(il) == 0)) cycle
238  betad(il) = (bd(il) * aee - be(il) * aed) * oodet
239  betae(il) = (be(il) * add - bd(il) * ade) * oodet
240  end do
241  !
242  ! -- Compute sigma coefficients.
243  sigma = matmul(vn(il01, :), matmul(ck, rmat))
244  !
245  ! -- Compute ahat and bhat coefficients.
246  ahat = (sigma(1) - sigma(2) * alphad - sigma(3) * alphae) / dl0(il01)
247  bhat = 0d0
248  do il = 1, nnbr
249  ! -- If this is connection (0,1) or inactive, skip.
250  if ((il == il01) .or. (inbr(il) == 0)) cycle
251  dl0il = dl0(il) + dln(il)
252  bhat(il) = (sigma(2) * betad(il) + sigma(3) * betae(il)) / dl0il
253  end do
254  ! -- Set the bhat for connection (0,1) to zero here, since we have been
255  ! skipping it in our do loops to avoiding explicitly computing it.
256  ! This will carry through to the corresponding chati0 and chat1j value,
257  ! so that they too are zero.
258  bhat(il01) = 0d0
259  !
260  end if
261  !
262  ! -- Multiply by area.
263  ahat = ahat * ar01
264  bhat = bhat * ar01
265  end subroutine abhats
266 
267  !> @brief Compute the matrix that rotates the model-coordinate axes to the
268  !! "(c, d, e)-coordinate" axes associated with a connection.
269  !!
270  !! This is also the matrix that transforms the components of a vector
271  !! from (c, d, e) coordinates to model coordinates. [Its transpose
272  !! transforms from model to (c, d, e) coordinates.]
273  !!
274  !! vcc = unit vector for the primary connection, in model coordinates.
275  !! vcd = unit vector for the first perpendicular direction, in model
276  !! coordinates.
277  !! vce = unit vector for the second perpendicular direction, in model
278  !! coordinates.
279  !! vcmax = unit vector for the connection with the maximum component
280  !! perpendicular to the primary connection, in model coordinates.
281  !! rmat = rotation matrix from model to (c, d, e) coordinates.
282  !<
283  subroutine getrot(nnbrmx, nnbr, inbr, vc, il01, rmat, iml0)
284  ! -- dummy
285  integer(I4B) :: nnbrmx
286  integer(I4B) :: nnbr
287  integer(I4B), dimension(nnbrmx) :: inbr
288  real(DP), dimension(nnbrmx, 3) :: vc
289  integer(I4B) :: il01
290  real(DP), dimension(3, 3) :: rmat
291  integer(I4B) :: iml0
292  ! -- local
293  real(DP), dimension(3) :: vcc
294  real(DP), dimension(3) :: vcd
295  real(DP), dimension(3) :: vce
296  real(DP), dimension(3) :: vcmax
297  integer(I4B) :: il
298  real(DP) :: acmpmn
299  real(DP) :: cmp
300  real(DP) :: acmp
301  real(DP) :: cmpmn
302  !
303  ! -- Set vcc.
304  vcc(:) = vc(il01, :)
305  !
306  ! -- Set vcmax. (If no connection has a perpendicular component greater
307  ! than some tiny threshold, return with iml0=0 and the first column of
308  ! rmat set to vcc -- the other columns are not needed.)
309  acmpmn = 1d0 - 1d-10
310  iml0 = 0
311  do il = 1, nnbr
312  if ((il .eq. il01) .or. (inbr(il) .eq. 0)) then
313  cycle
314  else
315  cmp = dot_product(vc(il, :), vcc)
316  acmp = dabs(cmp)
317  if (acmp .lt. acmpmn) then
318  cmpmn = cmp
319  acmpmn = acmp
320  iml0 = il
321  end if
322  end if
323  end do
324  if (iml0 == 0) then
325  rmat(:, 1) = vcc(:)
326  goto 999
327  else
328  vcmax(:) = vc(iml0, :)
329  end if
330  !
331  ! -- Set the first perpendicular direction as the direction that is coplanar
332  ! with vcc and vcmax and perpendicular to vcc.
333  vcd = vcmax - cmpmn * vcc
334  vcd = vcd / dsqrt(1d0 - cmpmn * cmpmn)
335  !
336  ! -- Set the second perpendicular direction as the cross product of the
337  ! primary and first-perpendicular directions.
338  vce(1) = vcc(2) * vcd(3) - vcc(3) * vcd(2)
339  vce(2) = vcc(3) * vcd(1) - vcc(1) * vcd(3)
340  vce(3) = vcc(1) * vcd(2) - vcc(2) * vcd(1)
341  !
342  ! -- Set the rotation matrix as the matrix with vcc, vcd, and vce as its
343  ! columns.
344  rmat(:, 1) = vcc(:)
345  rmat(:, 2) = vcd(:)
346  rmat(:, 3) = vce(:)
347  !
348  ! -- Return
349 999 return
350  end subroutine getrot
351 
352  !> @brief Transform local connection unit-vectors from model coordinates to
353  !! "(c, d, e)" coordinates associated with a connection.
354  !!
355  !! nnbrs = number of neighbors (local connections)
356  !! rmat = rotation matrix from (c, d, e) to model coordinates.
357  !! vc = array of connection unit-vectors with respect to model coordinates.
358  !! vccde = array of connection unit-vectors with respect to (c, d, e)
359  !! coordinates.
360  subroutine tranvc(nnbrmx, nnbrs, rmat, vc, vccde)
361  ! -- dummy
362  integer(I4B) :: nnbrmx
363  integer(I4B) :: nnbrs
364  real(DP), dimension(3, 3) :: rmat
365  real(DP), dimension(nnbrmx, 3) :: vc
366  real(DP), dimension(nnbrmx, 3) :: vccde
367  ! -- local
368  integer(I4B) :: il
369  !
370  ! -- Loop over the local connections, transforming the unit vectors.
371  ! Note that we are multiplying by the transpose of the rotation matrix
372  ! so that the transformation is from model to (c, d, e) coordinates.
373  do il = 1, nnbrs
374  vccde(il, :) = matmul(transpose(rmat), vc(il, :))
375  end do
376  end subroutine tranvc
377 
378  !> @brief Compute "a" and "b" weights for the local connections with respect
379  !! to the perpendicular direction of primary interest.
380  !!
381  !! nde1 = number that indicates the perpendicular direction of primary
382  !! interest on this call: "d" (2) or "e" (3).
383  !! vccde = array of connection unit-vectors with respect to (c, d, e)
384  !! coordinates.
385  !! bd = array of "b" weights.
386  !! aed = "a" weight that goes on the matrix side of the 2x2 problem.
387  !! acd = "a" weight that goes on the right-hand side of the 2x2 problem.
388  !<
389  subroutine abwts(nnbrmx, nnbr, inbr, il01, nde1, vccde, &
390  vcthresh, dl0, dln, acd, add, aed, bd)
391  ! -- dummy
392  integer(I4B) :: nnbrmx
393  integer(I4B) :: nnbr
394  integer(I4B), dimension(nnbrmx) :: inbr
395  integer(I4B) :: il01
396  integer(I4B) :: nde1
397  real(DP), dimension(nnbrmx, 3) :: vccde
398  real(DP) :: vcthresh
399  real(DP), dimension(nnbrmx) :: dl0
400  real(DP), dimension(nnbrmx) :: dln
401  real(DP) :: acd
402  real(DP) :: add
403  real(DP) :: aed
404  real(DP), dimension(nnbrmx) :: bd
405  ! -- local
406  integer(I4B) :: nde2
407  integer(I4B) :: il
408  real(DP) :: vcmx
409  real(DP) :: dlm
410  real(DP) :: cosang
411  real(DP) :: dl4wt
412  real(DP) :: fact
413  real(DP) :: dsum
414  real(DP) :: oodsum
415  real(DP) :: fatten
416  real(DP), dimension(nnbrmx) :: omwt
417  !
418  ! -- Set the perpendicular direction of secondary interest.
419  nde2 = 5 - nde1
420  !
421  ! -- Begin computing "omega" weights.
422  omwt = 0d0
423  dsum = 0d0
424  vcmx = 0d0
425  do il = 1, nnbr
426  ! -- If this is connection (0,1) or inactive, skip.
427  if ((il .eq. il01) .or. (inbr(il) .eq. 0)) cycle
428  vcmx = max(dabs(vccde(il, nde1)), vcmx)
429  dlm = 5d-1 * (dl0(il) + dln(il))
430  ! -- Distance-based weighting. dl4wt is the distance between the point
431  ! supplying the gradient information and the point at which the flux is
432  ! being estimated. Could be coded as a special case of resistance-based
433  ! weighting (by setting the conductivity matrix to be the identity
434  ! matrix), but this is more efficient.
435  cosang = vccde(il, 1)
436  dl4wt = dsqrt(dlm * dlm + dl0(il01) * dl0(il01) &
437  - 2d0 * dlm * dl0(il01) * cosang)
438  omwt(il) = dabs(vccde(il, nde1)) * dl4wt
439  dsum = dsum + omwt(il)
440  end do
441  !
442  ! -- Finish computing non-normalized "omega" weights. [Add a tiny bit to
443  ! dsum so that the normalized omega weight later evaluates to
444  ! (essentially) 1 in the case of a single relevant connection, avoiding
445  ! 0/0.]
446  dsum = dsum + 1d-10 * dsum
447  do il = 1, nnbr
448  ! -- If this is connection (0,1) or inactive, skip.
449  if ((il .eq. il01) .or. (inbr(il) .eq. 0)) cycle
450  fact = dsum - omwt(il)
451  omwt(il) = fact * dabs(vccde(il, nde1))
452  end do
453  !
454  ! -- Compute "b" weights.
455  bd = 0d0
456  dsum = 0d0
457  do il = 1, nnbr
458  ! -- If this is connection (0,1) or inactive, skip.
459  if ((il .eq. il01) .or. (inbr(il) .eq. 0)) cycle
460  bd(il) = omwt(il) * sign(1d0, vccde(il, nde1))
461  dsum = dsum + omwt(il) * dabs(vccde(il, nde1))
462  end do
463  !
464  oodsum = 1d0 / dsum
465  do il = 1, nnbr
466  ! -- If this is connection (0,1) or inactive, skip.
467  if ((il .eq. il01) .or. (inbr(il) .eq. 0)) cycle
468  bd(il) = bd(il) * oodsum
469  end do
470  !
471  ! -- Compute "a" weights.
472  add = 1d0
473  acd = 0d0
474  aed = 0d0
475  do il = 1, nnbr
476  ! -- If this is connection (0,1) or inactive, skip.
477  if ((il .eq. il01) .or. (inbr(il) .eq. 0)) cycle
478  acd = acd + bd(il) * vccde(il, 1)
479  aed = aed + bd(il) * vccde(il, nde2)
480  end do
481  !
482  ! -- Apply attenuation function to acd, aed, and bd.
483  if (vcmx .lt. vcthresh) then
484  fatten = vcmx / vcthresh
485  acd = acd * fatten
486  aed = aed * fatten
487  bd = bd * fatten
488  end if
489  !
490  end subroutine abwts
491 
492 end module xt3dalgorithmmodule
This module contains simulation constants.
Definition: Constants.f90:9
real(dp), parameter dprec
real constant machine precision
Definition: Constants.f90:120
real(dp), parameter done
real constant 1
Definition: Constants.f90:76
This module defines variable data types.
Definition: kind.f90:8
subroutine qconds(nnbrmx, nnbr0, inbr0, il01, vc0, vn0, dl0, dl0n, ck0, nnbr1, inbr1, il10, vc1, vn1, dl1, dl1n, ck1, ar01, ar10, vcthresh, allhc0, allhc1, chat01, chati0, chat1j)
Compute the "conductances" in the normal-flux expression for an interface (modflow-usg version)....
subroutine abwts(nnbrmx, nnbr, inbr, il01, nde1, vccde, vcthresh, dl0, dln, acd, add, aed, bd)
Compute "a" and "b" weights for the local connections with respect to the perpendicular direction of ...
subroutine getrot(nnbrmx, nnbr, inbr, vc, il01, rmat, iml0)
Compute the matrix that rotates the model-coordinate axes to the "(c, d, e)-coordinate" axes associat...
subroutine tranvc(nnbrmx, nnbrs, rmat, vc, vccde)
Transform local connection unit-vectors from model coordinates to "(c, d, e)" coordinates associated ...
subroutine abhats(nnbrmx, nnbr, inbr, il01, vc, vn, dl0, dln, ck, vcthresh, allhc, ar01, ahat, bhat)
Compute "ahat" and "bhat" coefficients for one side of an interface.