MODFLOW 6  version 6.7.0.dev2
USGS Modular Hydrologic Model
methodsubcellternarymodule Module Reference

Data Types

type  methodsubcellternarytype
 Ternary triangular subcell tracking method. More...
 

Functions/Subroutines

subroutine, public create_method_subcell_ternary (method)
 Create a new ternary subcell tracking method. More...
 
subroutine deallocate (this)
 Deallocate the ternary subcell tracking method. More...
 
subroutine apply_mst (this, particle, tmax)
 Apply the ternary subcell tracking method. More...
 
subroutine track_subcell (this, subcell, particle, tmax)
 Track a particle across a triangular subcell. More...
 
subroutine calculate_dt (v1, v2, dx, xL, v, dvdx, dt, status, itopbotexit)
 Do calculations related to analytical z solution. More...
 
subroutine calculate_xyz_position (dt, rxx, rxy, ryx, ryy, sxx, sxy, syy, izstatus, x0, y0, az, vzi, vzbot, ztop, zbot, zi, x, y, z, exitFace)
 Calculate the particle's local unscaled xyz coordinates after dt. More...
 

Function/Subroutine Documentation

◆ apply_mst()

subroutine methodsubcellternarymodule::apply_mst ( class(methodsubcellternarytype), intent(inout)  this,
type(particletype), intent(inout), pointer  particle,
real(dp), intent(in)  tmax 
)
private

Definition at line 54 of file MethodSubcellTernary.f90.

55  class(MethodSubcellTernaryType), intent(inout) :: this
56  type(ParticleType), pointer, intent(inout) :: particle
57  real(DP), intent(in) :: tmax
58 
59  select type (subcell => this%subcell)
60  type is (subcelltritype)
61  call this%track_subcell(subcell, particle, tmax)
62  end select

◆ calculate_dt()

subroutine methodsubcellternarymodule::calculate_dt ( real(dp)  v1,
real(dp)  v2,
real(dp)  dx,
real(dp)  xL,
real(dp)  v,
real(dp)  dvdx,
real(dp)  dt,
integer(i4b)  status,
integer(i4b)  itopbotexit 
)

This subroutine consists partly or entirely of code written by David W. Pollock of the USGS for MODPATH 7. The authors of the present code are responsible for its appropriate application in this context and for any modifications or errors.

Definition at line 319 of file MethodSubcellTernary.f90.

321  real(DP) :: v1
322  real(DP) :: v2
323  real(DP) :: dx
324  real(DP) :: xL
325  real(DP) :: v
326  real(DP) :: dvdx
327  real(DP) :: dt
328  real(DP) :: v2a
329  real(DP) :: v1a
330  real(DP) :: dv
331  real(DP) :: dva
332  real(DP) :: vv
333  real(DP) :: vvv
334  real(DP) :: zro
335  real(DP) :: zrom
336  real(DP) :: x
337  real(DP) :: tol
338  real(DP) :: vr1
339  real(DP) :: vr2
340  real(DP) :: vr
341  real(DP) :: v1v2
342  integer(I4B) :: status
343  integer(I4B) :: itopbotexit
344  logical(LGP) :: noOutflow
345 
346  ! Initialize variables
347  status = -1
348  dt = 1.0d+20
349  v2a = v2
350  if (v2a .lt. dzero) v2a = -v2a
351  v1a = v1
352  if (v1a .lt. dzero) v1a = -v1a
353  dv = v2 - v1
354  dva = dv
355  if (dva .lt. dzero) dva = -dva
356 
357  ! Check for a uniform zero velocity in this direction.
358  ! If so, set status = 2 and return (dt = 1.0d+20).
359  tol = 1.0d-15
360  if ((v2a .lt. tol) .and. (v1a .lt. tol)) then
361  v = dzero
362  dvdx = dzero
363  status = 2
364  itopbotexit = 0
365  return
366  end if
367 
368  ! Check for uniform non-zero velocity in this direction.
369  ! If so, set compute dt using the constant velocity,
370  ! set status = 1 and return.
371  vv = v1a
372  if (v2a .gt. vv) vv = v2a
373  vvv = dva / vv
374  if (vvv .lt. 1.0d-4) then
375  zro = tol
376  zrom = -zro
377  v = v1
378  x = xl * dx
379  if (v1 .gt. zro) then
380  dt = (dx - x) / v1
381  itopbotexit = -2
382  end if
383  if (v1 .lt. zrom) then
384  dt = -x / v1
385  itopbotexit = -1
386  end if
387  dvdx = dzero
388  status = 1
389  return
390  end if
391 
392  ! Velocity has a linear variation.
393  ! Compute velocity corresponding to particle position
394  dvdx = dv / dx
395  v = (done - xl) * v1 + xl * v2
396 
397  ! If flow is into the cell from both sides there is no outflow.
398  ! In that case, set status = 3 and return
399  nooutflow = .true.
400  if (v1 .lt. dzero) nooutflow = .false.
401  if (v2 .gt. dzero) nooutflow = .false.
402  if (nooutflow) then
403  status = 3
404  itopbotexit = 0
405  return
406  end if
407 
408  ! If there is a divide in the cell for this flow direction, check to see if the
409  ! particle is located exactly on the divide. If it is, move it very slightly to
410  ! get it off the divide. This avoids possible numerical problems related to
411  ! stagnation points.
412  if ((v1 .le. dzero) .and. (v2 .ge. dzero)) then
413  if (abs(v) .le. dzero) then
414  v = 1.0d-20
415  if (v2 .le. dzero) v = -v
416  end if
417  end if
418 
419  ! If there is a flow divide, find out what side of the divide the particle
420  ! is on and set the value of vr appropriately to reflect that location.
421  vr1 = v1 / v
422  vr2 = v2 / v
423  vr = vr1
424  itopbotexit = -1
425  if (vr .le. dzero) then
426  vr = vr2
427  itopbotexit = -2
428  end if
429 
430  ! Check if velocity is in the same direction throughout cell (i.e. no flow divide).
431  ! Check if product v1*v2 > 0 then the velocity is in the same direction throughout
432  ! the cell (i.e. no flow divide). If so, set vr to reflect appropriate direction.
433  v1v2 = v1 * v2
434  if (v1v2 .gt. dzero) then
435  if (v .gt. dzero) then
436  vr = vr2
437  itopbotexit = -2
438  end if
439  if (v .lt. dzero) then
440  vr = vr1
441  itopbotexit = -1
442  end if
443  end if
444 
445  ! Compute travel time to exit face. Return with status = 0
446  dt = log(abs(vr)) / dvdx
447  status = 0
Here is the caller graph for this function:

◆ calculate_xyz_position()

subroutine methodsubcellternarymodule::calculate_xyz_position ( real(dp)  dt,
real(dp)  rxx,
real(dp)  rxy,
real(dp)  ryx,
real(dp)  ryy,
real(dp)  sxx,
real(dp)  sxy,
real(dp)  syy,
integer(i4b)  izstatus,
real(dp)  x0,
real(dp)  y0,
real(dp)  az,
real(dp)  vzi,
real(dp)  vzbot,
real(dp)  ztop,
real(dp)  zbot,
real(dp)  zi,
real(dp)  x,
real(dp)  y,
real(dp)  z,
integer(i4b), optional  exitFace 
)
private

Definition at line 451 of file MethodSubcellTernary.f90.

454  ! dummy
455  real(DP) :: dt
456  real(DP) :: rxx
457  real(DP) :: rxy
458  real(DP) :: ryx
459  real(DP) :: ryy
460  real(DP) :: sxx
461  real(DP) :: sxy
462  real(DP) :: syy
463  integer(I4B) :: izstatus
464  real(DP) :: x0
465  real(DP) :: y0
466  real(DP) :: az
467  real(DP) :: vzi
468  real(DP) :: vzbot
469  real(DP) :: ztop
470  real(DP) :: zbot
471  real(DP) :: zi
472  real(DP) :: x
473  real(DP) :: y
474  real(DP) :: z
475  integer(I4B), optional :: exitFace
476  ! local
477  integer(I4B) :: lexitface
478  real(DP) :: rot(2, 2), res(2), loc(2)
479  real(DP) :: alp
480  real(DP) :: bet
481 
482  ! process optional exit face argument
483  if (present(exitface)) then
484  lexitface = exitface
485  else
486  lexitface = 0
487  end if
488 
489  ! calculate alpha and beta
490  call step_analytical(dt, alp, bet)
491 
492  ! if exit face is known, set alpha or beta coordinate
493  ! corresponding to the exit face exactly.
494  if (lexitface .eq. 1) then
495  bet = dzero
496  else if (lexitface .eq. 2) then
497  alp = done - bet
498  else if (lexitface .eq. 3) then
499  alp = dzero
500  end if
501 
502  ! if exit face is top or bottom, set z coordinate exactly.
503  if (lexitface .eq. 4) then
504  z = zbot
505  else if (lexitface .eq. 5) then
506  z = ztop
507  else
508  ! otherwise calculate z.
509  if (izstatus .eq. 2) then
510  ! vz uniformly zero
511  z = zi
512  else if (izstatus .eq. 1) then
513  ! vz uniform, nonzero
514  z = zi + vzi * dt
515  else
516  ! vz nonuniform
517  z = zbot + (vzi * dexp(az * dt) - vzbot) / az
518  end if
519  end if
520 
521  ! transform (alp, beta) to (x, y)
522  loc = (/alp, bet/)
523  loc = skew(loc, (/sxx, sxy, syy/), invert=.true.)
524  rot = reshape((/rxx, rxy, ryx, ryy/), shape(rot))
525  res = matmul(rot, loc) ! rotate vector
526  x = res(1) + x0
527  y = res(2) + y0
528 
Here is the call graph for this function:
Here is the caller graph for this function:

◆ create_method_subcell_ternary()

subroutine, public methodsubcellternarymodule::create_method_subcell_ternary ( type(methodsubcellternarytype), pointer  method)

Definition at line 34 of file MethodSubcellTernary.f90.

35  ! dummy
36  type(MethodSubcellTernaryType), pointer :: method
37  ! local
38  type(SubcellTriType), pointer :: subcell
39 
40  allocate (method)
41  call create_subcell_tri(subcell)
42  method%subcell => subcell
43  method%name => method%subcell%type
44  method%delegates = .false.
Here is the call graph for this function:
Here is the caller graph for this function:

◆ deallocate()

subroutine methodsubcellternarymodule::deallocate ( class(methodsubcellternarytype), intent(inout)  this)
private

Definition at line 48 of file MethodSubcellTernary.f90.

49  class(MethodSubcellTernaryType), intent(inout) :: this
50  deallocate (this%name)

◆ track_subcell()

subroutine methodsubcellternarymodule::track_subcell ( class(methodsubcellternarytype), intent(inout)  this,
class(subcelltritype), intent(in)  subcell,
type(particletype), intent(inout), pointer  particle,
real(dp), intent(in)  tmax 
)
private

Definition at line 66 of file MethodSubcellTernary.f90.

69  ! dummy
70  class(MethodSubcellTernaryType), intent(inout) :: this
71  class(SubcellTriType), intent(in) :: subcell
72  type(ParticleType), pointer, intent(inout) :: particle
73  real(DP), intent(in) :: tmax
74  ! local
75  integer(I4B) :: exitFace
76  real(DP) :: x0
77  real(DP) :: y0
78  real(DP) :: x1
79  real(DP) :: y1
80  real(DP) :: x2
81  real(DP) :: y2
82  real(DP) :: v0x
83  real(DP) :: v0y
84  real(DP) :: v1x
85  real(DP) :: v1y
86  real(DP) :: v2x
87  real(DP) :: v2y
88  real(DP) :: xi
89  real(DP) :: yi
90  real(DP) :: zi
91  real(DP) :: zirel
92  real(DP) :: ztop
93  real(DP) :: zbot
94  real(DP) :: dz
95  real(DP) :: rxx
96  real(DP) :: rxy
97  real(DP) :: ryx
98  real(DP) :: ryy
99  real(DP) :: sxx
100  real(DP) :: sxy
101  real(DP) :: syy
102  real(DP) :: alp0
103  real(DP) :: bet0
104  real(DP) :: alp1
105  real(DP) :: bet1
106  real(DP) :: alp2
107  real(DP) :: bet2
108  real(DP) :: alpi
109  real(DP) :: beti
110  real(DP) :: gami
111  real(DP) :: vzbot
112  real(DP) :: vztop
113  real(DP) :: vzi
114  real(DP) :: vziodz
115  real(DP) :: az
116  real(DP) :: dtexitz
117  real(DP) :: dt
118  real(DP) :: t
119  real(DP) :: t0
120  real(DP) :: dtexitxy
121  real(DP) :: texit
122  real(DP) :: x
123  real(DP) :: y
124  real(DP) :: z
125  integer(I4B) :: izstatus
126  integer(I4B) :: itopbotexit
127  integer(I4B) :: ntmax
128  integer(I4B) :: isolv
129  integer(I4B) :: itrifaceenter
130  integer(I4B) :: itrifaceexit
131  real(DP) :: tol
132  real(DP) :: dtexit
133  real(DP) :: alpexit
134  real(DP) :: betexit
135  integer(I4B) :: event_code
136  integer(I4B) :: i
137 
138  ! Set solution method
139  if (particle%iexmeth == 0) then
140  isolv = 1 ! default to Brent's
141  else
142  isolv = particle%iexmeth
143  end if
144 
145  ntmax = 10000
146  tol = particle%extol
147  event_code = -1
148 
149  ! Set some local variables for convenience.
150  xi = particle%x
151  yi = particle%y
152  zi = particle%z
153  x0 = subcell%x0
154  y0 = subcell%y0
155  x1 = subcell%x1
156  y1 = subcell%y1
157  x2 = subcell%x2
158  y2 = subcell%y2
159  v0x = subcell%v0x
160  v0y = subcell%v0y
161  v1x = subcell%v1x
162  v1y = subcell%v1y
163  v2x = subcell%v2x
164  v2y = subcell%v2y
165  zbot = subcell%zbot
166  ztop = subcell%ztop
167  dz = subcell%dz
168  vzbot = subcell%vzbot
169  vztop = subcell%vztop
170 
171  ! Transform coordinates to the "canonical" configuration:
172  ! barycentric in two dimensions with alpha, beta & gamma
173  ! such that at f2 alpha = 0, f0 beta = 0, f1 gamma = 0.
174  !
175  ! v2
176  ! |\
177  ! f2| \f1
178  ! |__\
179  ! v0 f0 v1
180  !
181  call canonical(x0, y0, x1, y1, x2, y2, &
182  v0x, v0y, v1x, v1y, v2x, v2y, &
183  xi, yi, &
184  rxx, rxy, ryx, ryy, &
185  sxx, sxy, syy, &
186  alp0, bet0, alp1, bet1, alp2, bet2, alpi, beti)
187 
188  ! Clamp particle coordinates to the canonical triangular
189  ! subcell and nudge it ever so slightly inside if needed.
190  call clamp_bary(alpi, beti, gami, pad=dsame * dep3)
191 
192  ! Do calculations related to the analytical z solution.
193  ! (TODO: just once for each cell? store at cell-level?)
194  ! Clamp the relative z coordinate to the unit interval.
195  zirel = (zi - zbot) / dz
196  if (zirel > done) then
197  zirel = done
198  else if (zirel < dzero) then
199  zirel = dzero
200  end if
201  call calculate_dt(vzbot, vztop, dz, zirel, vzi, &
202  az, dtexitz, izstatus, &
203  itopbotexit)
204  vziodz = vzi / dz
205 
206  ! If possible, track the particle across the subcell.
207  itrifaceenter = particle%iboundary(level_subfeature) - 1
208  if (itrifaceenter == -1) itrifaceenter = 999
209  call traverse_triangle(isolv, tol, &
210  dtexitxy, alpexit, betexit, &
211  itrifaceenter, itrifaceexit, &
212  alp1, bet1, alp2, bet2, alpi, beti)
213 
214  ! If the subcell has no exit face, terminate the particle.
215  ! todo: after initial release, consider ramifications
216  if (itopbotexit == 0 .and. itrifaceexit == 0) then
217  call this%terminate(particle, &
218  status=term_no_exits_sub)
219  return
220  end if
221 
222  ! -- Determine (earliest) exit face and corresponding travel time to exit
223  if (itopbotexit == 0) then
224  ! -- Exits through triangle face first
225  exitface = itrifaceexit
226  dtexit = dtexitxy
227  else if (itrifaceexit == 0 .or. dtexitz < dtexitxy) then
228  ! -- Exits through top/bottom first
229  exitface = 45
230  dtexit = dtexitz
231  else
232  ! -- Exits through triangle face first
233  exitface = itrifaceexit
234  dtexit = dtexitxy
235  end if
236  if (exitface == 45) then
237  if (itopbotexit == -1) then
238  exitface = 4
239  else
240  exitface = 5
241  end if
242  end if
243 
244  ! -- Make sure dt is positive
245  if (dtexit < dzero) then
246  call this%terminate(particle, &
247  status=term_no_exits_sub)
248  return
249  end if
250 
251  texit = particle%ttrack + dtexit
252  t0 = particle%ttrack
253 
254  ! Select user tracking times to solve. If this is the last time step
255  ! in the simulation, times falling after the simulation end time are
256  ! only included if the 'extend' option is on, otherwise only times in
257  ! the time step are included.
258  call this%tracktimes%advance()
259  if (this%tracktimes%any()) then
260  do i = this%tracktimes%selection(1), this%tracktimes%selection(2)
261  t = this%tracktimes%times(i)
262  if (t < particle%ttrack) cycle
263  if (t >= texit .or. t >= tmax) exit
264  dt = t - t0
265  call calculate_xyz_position(dt, rxx, rxy, ryx, ryy, sxx, sxy, syy, &
266  izstatus, x0, y0, az, vzi, vzbot, &
267  ztop, zbot, zi, x, y, z)
268  particle%x = x
269  particle%y = y
270  particle%z = z
271  particle%ttrack = t
272  particle%istatus = active
273  call this%usertime(particle)
274  end do
275  end if
276 
277  ! Compute exit time and face and update the particle's coordinates
278  ! (local, unscaled) and other properties. The particle may at this
279  ! point lie on a boundary of the subcell or may still be within it.
280  if (texit .gt. tmax) then
281  ! The computed exit time is greater than the maximum time, so set
282  ! final time for particle trajectory equal to maximum time.
283  t = tmax
284  dt = t - t0
285  exitface = 0
286  particle%istatus = active
287  particle%advancing = .false.
288  event_code = timestep
289  else
290  ! The computed exit time is less than or equal to the maximum time,
291  ! so set final time for particle trajectory equal to exit time.
292  t = texit
293  dt = dtexit
294  event_code = featexit
295  end if
296  call calculate_xyz_position(dt, rxx, rxy, ryx, ryy, sxx, sxy, syy, &
297  izstatus, x0, y0, az, vzi, vzbot, &
298  ztop, zbot, zi, x, y, z, exitface)
299  particle%x = x
300  particle%y = y
301  particle%z = z
302  particle%ttrack = t
303  particle%iboundary(level_subfeature) = exitface
304 
305  if (event_code == timestep) then
306  call this%timestep(particle)
307  else if (event_code == featexit) then
308  call this%subcellexit(particle)
309  end if
@, public featexit
particle exited a grid feature
@, public usertime
user-specified tracking time
@, public terminate
particle terminated
@, public timestep
time step ended
@ term_no_exits_sub
terminated in a subcell with no exit face
Definition: Particle.f90:36
Here is the call graph for this function: