MODFLOW 6  version 6.7.0.dev2
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 (this, particle)
 Determine earliest exit face. More...
 
subroutine find_exits (this, particle, domain)
 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 80 of file MethodSubcellTernary.f90.

81  class(MethodSubcellTernaryType), intent(inout) :: this
82  type(ParticleType), pointer, intent(inout) :: particle
83  real(DP), intent(in) :: tmax
84 
85  select type (subcell => this%subcell)
86  type is (subcelltritype)
87  call this%track_subcell(subcell, particle, tmax)
88  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 376 of file MethodSubcellTernary.f90.

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

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

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

75  class(MethodSubcellTernaryType), intent(inout) :: this
76  deallocate (this%name)

◆ find_exits()

subroutine methodsubcellternarymodule::find_exits ( class(methodsubcellternarytype), intent(inout)  this,
type(particletype), intent(inout), pointer  particle,
class(domaintype), intent(in)  domain 
)
private

Definition at line 228 of file MethodSubcellTernary.f90.

229  class(MethodSubcellTernaryType), intent(inout) :: this
230  type(ParticleType), pointer, intent(inout) :: particle
231  class(DomainType), intent(in) :: domain
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.
295  zirel = (particle%z - subcell%zbot) / subcell%dz
296  if (zirel > done) then
297  zirel = done
298  else if (zirel < dzero) then
299  zirel = dzero
300  end if
301  this%exit_solutions(1) = find_vertical_exit(subcell%vzbot, subcell%vztop, &
302  subcell%dz, zirel)
303 
304  ! Calculate a semi-analytical lateral exit solution
305  itrifaceenter = particle%iboundary(level_subfeature) - 1
306  if (itrifaceenter == -1) itrifaceenter = 999
307  this%exit_solutions(2) = find_lateral_exit(isolv, tol, &
308  itrifaceenter, &
309  alp1, bet1, alp2, &
310  bet2, alpi, beti)
311  this%exit_solutions(2)%rxx = rxx
312  this%exit_solutions(2)%rxy = rxy
313  this%exit_solutions(2)%ryx = ryx
314  this%exit_solutions(2)%ryy = ryy
315  this%exit_solutions(2)%sxx = sxx
316  this%exit_solutions(2)%sxy = sxy
317  this%exit_solutions(2)%syy = syy
318 
319  ! Set vertical solution exit face
320  if (this%exit_solutions(1)%itopbotexit /= 0) then
321  if (this%exit_solutions(1)%itopbotexit == -1) then
322  this%exit_solutions(1)%iboundary = 4
323  else
324  this%exit_solutions(1)%iboundary = 5
325  end if
326  end if
327 
328  ! Set lateral solution exit face
329  if (this%exit_solutions(2)%itrifaceexit /= 0) &
330  this%exit_solutions(2)%iboundary = this%exit_solutions(2)%itrifaceexit
331  end select
Here is the call 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 334 of file MethodSubcellTernary.f90.

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

358  real(DP), intent(in) :: v1
359  real(DP), intent(in) :: v2
360  real(DP), intent(in) :: dx
361  real(DP), intent(in) :: xL
362  type(BarycentricExitSolutionType) :: solution
363 
364  solution = barycentricexitsolutiontype()
365  call calculate_dt(v1, v2, dx, xl, solution%v, solution%dvdx, &
366  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 ( class(methodsubcellternarytype), intent(inout)  this,
type(particletype), intent(inout), pointer  particle 
)

Definition at line 211 of file MethodSubcellTernary.f90.

212  class(MethodSubcellTernaryType), intent(inout) :: this
213  type(ParticleType), pointer, intent(inout) :: particle
214  integer(I4B) :: exit_soln
215 
216  if (this%exit_solutions(1)%itopbotexit == 0) then
217  exit_soln = 2 ! lateral
218  else if (this%exit_solutions(2)%itrifaceexit == 0 .or. &
219  this%exit_solutions(1)%dt < this%exit_solutions(2)%dt) then
220  exit_soln = 1 ! top/bottom
221  else
222  exit_soln = 2 ! lateral
223  end if
224 

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

95  ! dummy
96  class(MethodSubcellTernaryType), intent(inout) :: this
97  class(SubcellTriType), intent(in) :: subcell
98  type(ParticleType), pointer, intent(inout) :: particle
99  real(DP), intent(in) :: tmax
100  ! local
101  real(DP) :: dt, dtexit, texit
102  real(DP) :: t0, t, x, y, z0, z
103  integer(I4B) :: exit_face, exit_soln, event_code, i, isolv
104  type(BarycentricExitSolutionType) :: exit_z, exit_lateral
105 
106  event_code = -1
107  if (particle%iexmeth == 0) then
108  isolv = 1 ! default to Brent's solution method
109  else
110  isolv = particle%iexmeth
111  end if
112  t0 = particle%ttrack
113  z0 = particle%z
114 
115  ! Find exit solutions in lateral and vertical directions
116  call this%find_exits(particle, subcell)
117 
118  exit_z = this%exit_solutions(1)
119  exit_lateral = this%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 = this%pick_exit(particle)
131  exit_face = this%exit_solutions(exit_soln)%iboundary
132  dtexit = this%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:36
Here is the call graph for this function: