MODFLOW 6  version 6.8.0.dev0
USGS Modular Hydrologic Model
methodsubcellternarymodule Module Reference

Data Types

type  barycentricexitsolutiontype
 Barycentric velocity interpolation exit solution. Inherit from LinearExitSolutionType to get around array polymorphism limitations in Fortran; the exit_solutions array below needs to be of one type for convenient use. More...
 
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...
 
integer(i4b) function pick_exit (exit_solutions)
 Determine earliest exit face. More...
 
subroutine find_exits (particle, domain, exit_solutions, z0)
 Calculate exit solutions for each coordinate direction. More...
 
type(barycentricexitsolutiontype) function find_lateral_exit (isolv, tol, itrifaceenter, alp1, bet1, alp2, bet2, alpi, beti)
 
type(barycentricexitsolutiontype) function find_vertical_exit (v1, v2, dx, xL)
 
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 77 of file MethodSubcellTernary.f90.

78  class(MethodSubcellTernaryType), intent(inout) :: this
79  type(ParticleType), pointer, intent(inout) :: particle
80  real(DP), intent(in) :: tmax
81 
82  select type (subcell => this%subcell)
83  type is (subcelltritype)
84  call this%track_subcell(subcell, particle, tmax)
85  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 
)
private

This subroutine consists partly of code written by and/or adapted from 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 379 of file MethodSubcellTernary.f90.

381  real(DP) :: v1
382  real(DP) :: v2
383  real(DP) :: dx
384  real(DP) :: xL
385  real(DP) :: v
386  real(DP) :: dvdx
387  real(DP) :: dt
388  real(DP) :: v2a
389  real(DP) :: v1a
390  real(DP) :: dv
391  real(DP) :: dva
392  real(DP) :: vv
393  real(DP) :: vvv
394  real(DP) :: zro
395  real(DP) :: zrom
396  real(DP) :: x
397  real(DP) :: tol
398  real(DP) :: vr1
399  real(DP) :: vr2
400  real(DP) :: vr
401  real(DP) :: v1v2
402  integer(I4B) :: status
403  integer(I4B) :: itopbotexit
404  logical(LGP) :: noOutflow
405 
406  ! Initialize variables
407  status = -1
408  dt = 1.0d+20
409  v2a = v2
410  if (v2a .lt. dzero) v2a = -v2a
411  v1a = v1
412  if (v1a .lt. dzero) v1a = -v1a
413  dv = v2 - v1
414  dva = dv
415  if (dva .lt. dzero) dva = -dva
416 
417  ! Check for a uniform zero velocity in this direction.
418  ! If so, set status = 2 and return (dt = 1.0d+20).
419  tol = 1.0d-15
420  if ((v2a .lt. tol) .and. (v1a .lt. tol)) then
421  v = dzero
422  dvdx = dzero
423  status = no_exit_stationary
424  itopbotexit = 0
425  return
426  end if
427 
428  ! Check for uniform non-zero velocity in this direction.
429  ! If so, set compute dt using the constant velocity,
430  ! set status = 1 and return.
431  vv = v1a
432  if (v2a .gt. vv) vv = v2a
433  vvv = dva / vv
434  if (vvv .lt. 1.0d-4) then
435  zro = tol
436  zrom = -zro
437  v = v1
438  x = xl * dx
439  if (v1 .gt. zro) then
440  dt = (dx - x) / v1
441  itopbotexit = -2
442  end if
443  if (v1 .lt. zrom) then
444  dt = -x / v1
445  itopbotexit = -1
446  end if
447  dvdx = dzero
448  status = ok_exit_constant
449  return
450  end if
451 
452  ! Velocity has a linear variation.
453  ! Compute velocity corresponding to particle position
454  dvdx = dv / dx
455  v = (done - xl) * v1 + xl * v2
456 
457  ! If flow is into the cell from both sides there is no outflow.
458  ! In that case, set status = 3 and return
459  nooutflow = .true.
460  if (v1 .lt. dzero) nooutflow = .false.
461  if (v2 .gt. dzero) nooutflow = .false.
462  if (nooutflow) then
463  status = no_exit_no_outflow
464  itopbotexit = 0
465  return
466  end if
467 
468  ! If there is a divide in the cell for this flow direction, check to see if the
469  ! particle is located exactly on the divide. If it is, move it very slightly to
470  ! get it off the divide. This avoids possible numerical problems related to
471  ! stagnation points.
472  if ((v1 .le. dzero) .and. (v2 .ge. dzero)) then
473  if (abs(v) .le. dzero) then
474  v = 1.0d-20
475  if (v2 .le. dzero) v = -v
476  end if
477  end if
478 
479  ! If there is a flow divide, find out what side of the divide the particle
480  ! is on and set the value of vr appropriately to reflect that location.
481  vr1 = v1 / v
482  vr2 = v2 / v
483  vr = vr1
484  itopbotexit = -1
485  if (vr .le. dzero) then
486  vr = vr2
487  itopbotexit = -2
488  end if
489 
490  ! Check if velocity is in the same direction throughout cell (i.e. no flow divide).
491  ! Check if product v1*v2 > 0 then the velocity is in the same direction throughout
492  ! the cell (i.e. no flow divide). If so, set vr to reflect appropriate direction.
493  v1v2 = v1 * v2
494  if (v1v2 .gt. dzero) then
495  if (v .gt. dzero) then
496  vr = vr2
497  itopbotexit = -2
498  end if
499  if (v .lt. dzero) then
500  vr = vr1
501  itopbotexit = -1
502  end if
503  end if
504 
505  ! Compute travel time to exit face. Return with status = 0
506  dt = log(abs(vr)) / dvdx
507  status = ok_exit
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 511 of file MethodSubcellTernary.f90.

514  ! dummy
515  real(DP) :: dt
516  real(DP) :: rxx
517  real(DP) :: rxy
518  real(DP) :: ryx
519  real(DP) :: ryy
520  real(DP) :: sxx
521  real(DP) :: sxy
522  real(DP) :: syy
523  integer(I4B) :: izstatus
524  real(DP) :: x0
525  real(DP) :: y0
526  real(DP) :: az
527  real(DP) :: vzi
528  real(DP) :: vzbot
529  real(DP) :: ztop
530  real(DP) :: zbot
531  real(DP) :: zi
532  real(DP) :: x
533  real(DP) :: y
534  real(DP) :: z
535  integer(I4B), optional :: exitface
536  ! local
537  integer(I4B) :: lexitface
538  real(DP) :: rot(2, 2), res(2), loc(2)
539  real(DP) :: alp
540  real(DP) :: bet
541 
542  ! process optional exit face argument
543  if (present(exitface)) then
544  lexitface = exitface
545  else
546  lexitface = 0
547  end if
548 
549  ! calculate alpha and beta
550  call step_analytical(dt, alp, bet)
551 
552  ! if exit face is known, set alpha or beta coordinate
553  ! corresponding to the exit face exactly.
554  if (lexitface .eq. 1) then
555  bet = dzero
556  else if (lexitface .eq. 2) then
557  alp = done - bet
558  else if (lexitface .eq. 3) then
559  alp = dzero
560  end if
561 
562  ! if exit face is top or bottom, set z coordinate exactly.
563  if (lexitface .eq. 4) then
564  z = zbot
565  else if (lexitface .eq. 5) then
566  z = ztop
567  else
568  ! otherwise calculate z.
569  if (izstatus .eq. 2) then
570  ! vz uniformly zero
571  z = zi
572  else if (izstatus .eq. 1) then
573  ! vz uniform, nonzero
574  z = zi + vzi * dt
575  else
576  ! vz nonuniform
577  z = zbot + (vzi * dexp(az * dt) - vzbot) / az
578  end if
579  end if
580 
581  ! transform (alp, beta) to (x, y)
582  loc = (/alp, bet/)
583  loc = skew(loc, (/sxx, sxy, syy/), invert=.true.)
584  rot = reshape((/rxx, rxy, ryx, ryy/), shape(rot))
585  res = matmul(rot, loc) ! rotate vector
586  x = res(1) + x0
587  y = res(2) + y0
588 
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 57 of file MethodSubcellTernary.f90.

58  ! dummy
59  type(MethodSubcellTernaryType), pointer :: method
60  ! local
61  type(SubcellTriType), pointer :: subcell
62 
63  allocate (method)
64  call create_subcell_tri(subcell)
65  method%subcell => subcell
66  method%name => method%subcell%type
67  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 71 of file MethodSubcellTernary.f90.

72  class(MethodSubcellTernaryType), intent(inout) :: this
73  deallocate (this%name)

◆ find_exits()

subroutine methodsubcellternarymodule::find_exits ( type(particletype), intent(inout), pointer  particle,
class(domaintype), intent(in)  domain,
type(barycentricexitsolutiontype), dimension(2), intent(out)  exit_solutions,
real(dp), intent(out)  z0 
)
private

Definition at line 227 of file MethodSubcellTernary.f90.

228  type(ParticleType), pointer, intent(inout) :: particle
229  class(DomainType), intent(in) :: domain
230  type(BarycentricExitSolutionType), intent(out) :: exit_solutions(2)
231  real(DP), intent(out) :: z0
232  ! local
233  integer(I4B) :: ntmax
234  real(DP) :: tol
235  real(DP) :: zirel
236  real(DP) :: rxx
237  real(DP) :: rxy
238  real(DP) :: ryx
239  real(DP) :: ryy
240  real(DP) :: sxx
241  real(DP) :: sxy
242  real(DP) :: syy
243  real(DP) :: alp0
244  real(DP) :: bet0
245  real(DP) :: alp1
246  real(DP) :: bet1
247  real(DP) :: alp2
248  real(DP) :: bet2
249  real(DP) :: alpi
250  real(DP) :: beti
251  real(DP) :: gami
252  integer(I4B) :: isolv
253  integer(I4B) :: itrifaceenter
254 
255  ntmax = 10000
256  tol = particle%extol
257 
258  ! Set lateral solution method
259  if (particle%iexmeth == 0) then
260  isolv = 1 ! default to Brent's
261  else
262  isolv = particle%iexmeth
263  end if
264 
265  select type (subcell => domain)
266  type is (subcelltritype)
267  ! Transform coordinates to the "canonical" configuration:
268  ! barycentric in two dimensions with alpha, beta & gamma
269  ! such that at f2 alpha = 0, f0 beta = 0, f1 gamma = 0.
270  !
271  ! v2
272  ! |\
273  ! f2| \f1
274  ! |__\
275  ! v0 f0 v1
276  !
277  call canonical(subcell%x0, subcell%y0, &
278  subcell%x1, subcell%y1, &
279  subcell%x2, subcell%y2, &
280  subcell%v0x, subcell%v0y, &
281  subcell%v1x, subcell%v1y, &
282  subcell%v2x, subcell%v2y, &
283  particle%x, particle%y, &
284  rxx, rxy, ryx, ryy, &
285  sxx, sxy, syy, &
286  alp0, bet0, alp1, bet1, alp2, bet2, alpi, beti)
287 
288  ! Clamp particle coordinates to the canonical triangular
289  ! subcell and nudge it ever so slightly inside if needed.
290  call clamp_bary(alpi, beti, gami, pad=dsame * dep3)
291 
292  ! Do calculations related to the analytical z solution.
293  ! (TODO: just once for each cell? store at cell-level?)
294  ! Clamp the relative z coordinate to the unit interval, and nudge
295  ! the particle's z ever so slightly inside the subcell if needed,
296  ! analogous to the lateral clamp_bary nudge above.
297  zirel = (particle%z - subcell%zbot) / subcell%dz
298  if (zirel > done) then
299  zirel = done
300  else if (zirel < dzero) then
301  zirel = dzero
302  end if
303  z0 = subcell%zbot + zirel * subcell%dz
304  exit_solutions(1) = find_vertical_exit(subcell%vzbot, subcell%vztop, &
305  subcell%dz, zirel)
306 
307  ! Calculate a semi-analytical lateral exit solution
308  itrifaceenter = particle%iboundary(level_subfeature) - 1
309  if (itrifaceenter == -1) itrifaceenter = 999
310  exit_solutions(2) = find_lateral_exit(isolv, tol, &
311  itrifaceenter, &
312  alp1, bet1, alp2, &
313  bet2, alpi, beti)
314  exit_solutions(2)%rxx = rxx
315  exit_solutions(2)%rxy = rxy
316  exit_solutions(2)%ryx = ryx
317  exit_solutions(2)%ryy = ryy
318  exit_solutions(2)%sxx = sxx
319  exit_solutions(2)%sxy = sxy
320  exit_solutions(2)%syy = syy
321 
322  ! Set vertical solution exit face
323  if (exit_solutions(1)%itopbotexit /= 0) then
324  if (exit_solutions(1)%itopbotexit == -1) then
325  exit_solutions(1)%iboundary = 4
326  else
327  exit_solutions(1)%iboundary = 5
328  end if
329  end if
330 
331  ! Set lateral solution exit face
332  if (exit_solutions(2)%itrifaceexit /= 0) &
333  exit_solutions(2)%iboundary = exit_solutions(2)%itrifaceexit
334  end select
Here is the call graph for this function:
Here is the caller graph for this function:

◆ find_lateral_exit()

type(barycentricexitsolutiontype) function methodsubcellternarymodule::find_lateral_exit ( integer(i4b)  isolv,
real(dp)  tol,
integer(i4b)  itrifaceenter,
real(dp)  alp1,
real(dp)  bet1,
real(dp)  alp2,
real(dp)  bet2,
real(dp)  alpi,
real(dp)  beti 
)
private

Definition at line 337 of file MethodSubcellTernary.f90.

341  integer(I4B) :: isolv
342  real(DP) :: tol
343  integer(I4B) :: itrifaceenter
344  real(DP) :: alp1
345  real(DP) :: bet1
346  real(DP) :: alp2
347  real(DP) :: bet2
348  real(DP) :: alpi
349  real(DP) :: beti
350  type(BarycentricExitSolutionType) :: solution
351 
352  solution = barycentricexitsolutiontype()
353  call traverse_triangle(isolv, tol, &
354  solution%dt, solution%alpexit, solution%betexit, &
355  itrifaceenter, solution%itrifaceexit, &
356  alp1, bet1, alp2, bet2, alpi, beti)
357  if (solution%itrifaceexit > 0) solution%status = ok_exit
Here is the call graph for this function:
Here is the caller graph for this function:

◆ find_vertical_exit()

type(barycentricexitsolutiontype) function methodsubcellternarymodule::find_vertical_exit ( real(dp), intent(in)  v1,
real(dp), intent(in)  v2,
real(dp), intent(in)  dx,
real(dp), intent(in)  xL 
)
private

Definition at line 360 of file MethodSubcellTernary.f90.

361  real(DP), intent(in) :: v1
362  real(DP), intent(in) :: v2
363  real(DP), intent(in) :: dx
364  real(DP), intent(in) :: xL
365  type(BarycentricExitSolutionType) :: solution
366 
367  solution = barycentricexitsolutiontype()
368  call calculate_dt(v1, v2, dx, xl, solution%v, solution%dvdx, &
369  solution%dt, solution%status, solution%itopbotexit)
Here is the call graph for this function:
Here is the caller graph for this function:

◆ pick_exit()

integer(i4b) function methodsubcellternarymodule::pick_exit ( type(barycentricexitsolutiontype), dimension(2), intent(in)  exit_solutions)

Definition at line 211 of file MethodSubcellTernary.f90.

212  type(BarycentricExitSolutionType), intent(in) :: exit_solutions(2)
213  integer(I4B) :: exit_soln
214 
215  if (exit_solutions(1)%itopbotexit == 0) then
216  exit_soln = 2 ! lateral
217  else if (exit_solutions(2)%itrifaceexit == 0 .or. &
218  exit_solutions(1)%dt < exit_solutions(2)%dt) then
219  exit_soln = 1 ! top/bottom
220  else
221  exit_soln = 2 ! lateral
222  end if
223 
Here is the caller graph for this function:

◆ 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 89 of file MethodSubcellTernary.f90.

92  ! dummy
93  class(MethodSubcellTernaryType), intent(inout) :: this
94  class(SubcellTriType), intent(in) :: subcell
95  type(ParticleType), pointer, intent(inout) :: particle
96  real(DP), intent(in) :: tmax
97  ! local
98  real(DP) :: dt, dtexit, texit
99  real(DP) :: t0, t, x, y, z0, z
100  integer(I4B) :: exit_face, exit_soln, event_code, i, isolv
101  type(BarycentricExitSolutionType) :: exit_solutions(2)
102  type(BarycentricExitSolutionType) :: exit_z, exit_lateral
103 
104  event_code = -1
105  if (particle%iexmeth == 0) then
106  isolv = 1 ! default to Brent's solution method
107  else
108  isolv = particle%iexmeth
109  end if
110  t0 = particle%ttrack
111 
112  ! Find exit solutions in lateral and vertical directions. z0 comes
113  ! back clamped to [zbot, ztop] (mirroring the nudge clamp_bary applies
114  ! to the lateral coordinates), so it can safely anchor the position
115  ! update even if the raw particle%z lies outside the subcell.
116  call find_exits(particle, subcell, exit_solutions, z0)
117 
118  exit_z = exit_solutions(1)
119  exit_lateral = exit_solutions(2)
120 
121  ! If the subcell has no exit face, terminate the particle.
122  ! TODO: consider ramifications
123  if (exit_z%itopbotexit == 0 .and. &
124  exit_lateral%itrifaceexit == 0) then
125  call this%terminate(particle, status=term_no_exits_sub)
126  return
127  end if
128 
129  ! Determine exit solution, face, travel time, and time
130  exit_soln = pick_exit(exit_solutions)
131  exit_face = exit_solutions(exit_soln)%iboundary
132  dtexit = exit_solutions(exit_soln)%dt
133  if (dtexit < dzero) then
134  call this%terminate(particle, status=term_no_exits_sub)
135  return
136  end if
137  texit = t0 + dtexit
138 
139  ! Select user tracking times to solve. If this is the last time step
140  ! in the simulation, times falling after the simulation end time are
141  ! only included if the 'extend' option is on, otherwise only times in
142  ! the time step are included.
143  call this%tracktimes%advance()
144  if (this%tracktimes%any()) then
145  do i = this%tracktimes%selection(1), this%tracktimes%selection(2)
146  t = this%tracktimes%times(i)
147  if (t < t0) cycle
148  if (t > texit .or. t > tmax) exit
149  dt = t - t0
150  call calculate_xyz_position(dt, &
151  exit_lateral%rxx, exit_lateral%rxy, &
152  exit_lateral%ryx, exit_lateral%ryy, &
153  exit_lateral%sxx, exit_lateral%sxy, &
154  exit_lateral%syy, exit_z%status, &
155  subcell%x0, subcell%y0, &
156  exit_z%dvdx, exit_z%v, &
157  subcell%vzbot, subcell%ztop, subcell%zbot, &
158  z0, x, y, z)
159  particle%x = x
160  particle%y = y
161  particle%z = z
162  particle%ttrack = t
163  particle%istatus = active
164  call this%usertime(particle)
165  end do
166  end if
167 
168  ! Compute exit time and face and update the particle's coordinates
169  ! (local, unscaled) and other properties. The particle may at this
170  ! point lie on a boundary of the subcell or may still be within it.
171  if (texit .gt. tmax) then
172  ! The computed exit time is greater than the maximum time, so set
173  ! final time for particle trajectory equal to maximum time.
174  t = tmax
175  dt = t - t0
176  exit_face = 0
177  particle%istatus = active
178  particle%advancing = .false.
179  event_code = timestep
180  else
181  ! The computed exit time is less than or equal to the maximum time,
182  ! so set final time for particle trajectory equal to exit time.
183  t = texit
184  dt = dtexit
185  event_code = featexit
186  end if
187  call calculate_xyz_position(dt, &
188  exit_lateral%rxx, exit_lateral%rxy, &
189  exit_lateral%ryx, exit_lateral%ryy, &
190  exit_lateral%sxx, exit_lateral%sxy, &
191  exit_lateral%syy, exit_z%status, &
192  subcell%x0, subcell%y0, &
193  exit_z%dvdx, exit_z%v, &
194  subcell%vzbot, subcell%ztop, subcell%zbot, &
195  z0, x, y, z, exit_face)
196  particle%x = x
197  particle%y = y
198  particle%z = z
199  particle%ttrack = t
200  particle%iboundary(level_subfeature) = exit_face
201 
202  if (event_code == timestep) then
203  call this%timestep(particle)
204  else if (event_code == featexit) then
205  call this%subcellexit(particle)
206  end if
207 
@, 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:39
Here is the call graph for this function: