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

54  class(MethodSubcellTernaryType), intent(inout) :: this
55  type(ParticleType), pointer, intent(inout) :: particle
56  real(DP), intent(in) :: tmax
57 
58  select type (subcell => this%subcell)
59  type is (subcelltritype)
60  call this%track_subcell(subcell, particle, tmax)
61  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 314 of file MethodSubcellTernary.f90.

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

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

34  ! dummy
35  type(MethodSubcellTernaryType), pointer :: method
36  ! local
37  type(SubcellTriType), pointer :: subcell
38 
39  allocate (method)
40  call create_subcell_tri(subcell)
41  method%subcell => subcell
42  method%name => method%subcell%type
43  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 47 of file MethodSubcellTernary.f90.

48  class(MethodSubcellTernaryType), intent(inout) :: this
49  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 65 of file MethodSubcellTernary.f90.

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