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)
 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 373 of file MethodSubcellTernary.f90.

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

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

Definition at line 225 of file MethodSubcellTernary.f90.

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

335  integer(I4B) :: isolv
336  real(DP) :: tol
337  integer(I4B) :: itrifaceenter
338  real(DP) :: alp1
339  real(DP) :: bet1
340  real(DP) :: alp2
341  real(DP) :: bet2
342  real(DP) :: alpi
343  real(DP) :: beti
344  type(BarycentricExitSolutionType) :: solution
345 
346  solution = barycentricexitsolutiontype()
347  call traverse_triangle(isolv, tol, &
348  solution%dt, solution%alpexit, solution%betexit, &
349  itrifaceenter, solution%itrifaceexit, &
350  alp1, bet1, alp2, bet2, alpi, beti)
351  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 354 of file MethodSubcellTernary.f90.

355  real(DP), intent(in) :: v1
356  real(DP), intent(in) :: v2
357  real(DP), intent(in) :: dx
358  real(DP), intent(in) :: xL
359  type(BarycentricExitSolutionType) :: solution
360 
361  solution = barycentricexitsolutiontype()
362  call calculate_dt(v1, v2, dx, xl, solution%v, solution%dvdx, &
363  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 209 of file MethodSubcellTernary.f90.

210  type(BarycentricExitSolutionType), intent(in) :: exit_solutions(2)
211  integer(I4B) :: exit_soln
212 
213  if (exit_solutions(1)%itopbotexit == 0) then
214  exit_soln = 2 ! lateral
215  else if (exit_solutions(2)%itrifaceexit == 0 .or. &
216  exit_solutions(1)%dt < exit_solutions(2)%dt) then
217  exit_soln = 1 ! top/bottom
218  else
219  exit_soln = 2 ! lateral
220  end if
221 
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  z0 = particle%z
112 
113  ! Find exit solutions in lateral and vertical directions
114  call find_exits(particle, subcell, exit_solutions)
115 
116  exit_z = exit_solutions(1)
117  exit_lateral = exit_solutions(2)
118 
119  ! If the subcell has no exit face, terminate the particle.
120  ! TODO: consider ramifications
121  if (exit_z%itopbotexit == 0 .and. &
122  exit_lateral%itrifaceexit == 0) then
123  call this%terminate(particle, status=term_no_exits_sub)
124  return
125  end if
126 
127  ! Determine exit solution, face, travel time, and time
128  exit_soln = pick_exit(exit_solutions)
129  exit_face = exit_solutions(exit_soln)%iboundary
130  dtexit = exit_solutions(exit_soln)%dt
131  if (dtexit < dzero) then
132  call this%terminate(particle, status=term_no_exits_sub)
133  return
134  end if
135  texit = t0 + dtexit
136 
137  ! Select user tracking times to solve. If this is the last time step
138  ! in the simulation, times falling after the simulation end time are
139  ! only included if the 'extend' option is on, otherwise only times in
140  ! the time step are included.
141  call this%tracktimes%advance()
142  if (this%tracktimes%any()) then
143  do i = this%tracktimes%selection(1), this%tracktimes%selection(2)
144  t = this%tracktimes%times(i)
145  if (t < t0) cycle
146  if (t > texit .or. t > tmax) exit
147  dt = t - t0
148  call calculate_xyz_position(dt, &
149  exit_lateral%rxx, exit_lateral%rxy, &
150  exit_lateral%ryx, exit_lateral%ryy, &
151  exit_lateral%sxx, exit_lateral%sxy, &
152  exit_lateral%syy, exit_z%status, &
153  subcell%x0, subcell%y0, &
154  exit_z%dvdx, exit_z%v, &
155  subcell%vzbot, subcell%ztop, subcell%zbot, &
156  z0, x, y, z)
157  particle%x = x
158  particle%y = y
159  particle%z = z
160  particle%ttrack = t
161  particle%istatus = active
162  call this%usertime(particle)
163  end do
164  end if
165 
166  ! Compute exit time and face and update the particle's coordinates
167  ! (local, unscaled) and other properties. The particle may at this
168  ! point lie on a boundary of the subcell or may still be within it.
169  if (texit .gt. tmax) then
170  ! The computed exit time is greater than the maximum time, so set
171  ! final time for particle trajectory equal to maximum time.
172  t = tmax
173  dt = t - t0
174  exit_face = 0
175  particle%istatus = active
176  particle%advancing = .false.
177  event_code = timestep
178  else
179  ! The computed exit time is less than or equal to the maximum time,
180  ! so set final time for particle trajectory equal to exit time.
181  t = texit
182  dt = dtexit
183  event_code = featexit
184  end if
185  call calculate_xyz_position(dt, &
186  exit_lateral%rxx, exit_lateral%rxy, &
187  exit_lateral%ryx, exit_lateral%ryy, &
188  exit_lateral%sxx, exit_lateral%sxy, &
189  exit_lateral%syy, exit_z%status, &
190  subcell%x0, subcell%y0, &
191  exit_z%dvdx, exit_z%v, &
192  subcell%vzbot, subcell%ztop, subcell%zbot, &
193  z0, x, y, z, exit_face)
194  particle%x = x
195  particle%y = y
196  particle%z = z
197  particle%ttrack = t
198  particle%iboundary(level_subfeature) = exit_face
199 
200  if (event_code == timestep) then
201  call this%timestep(particle)
202  else if (event_code == featexit) then
203  call this%subcellexit(particle)
204  end if
205 
@, 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: