MODFLOW 6  version 6.5.0.dev2
MODFLOW 6 Code Documentation
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-method object. More...
 
subroutine destroy (this)
 Destructor for a ternary subcell-method object. More...
 
subroutine apply_mst (this, particle, tmax)
 Apply the ternary subcell method. More...
 
subroutine track_subcell (this, subcell, particle, tmax)
 Track a particle across a triangular subcell using the ternary method. More...
 
subroutine calculate_dt (v1, v2, dx, xL, v, dvdx, dt, status, itopbotexit)
 Do calculations related to analytical z solution. 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 
)
private

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

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

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

◆ destroy()

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

Definition at line 48 of file MethodSubcellTernary.f90.

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

◆ 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.

67  ! dummy
68  class(MethodSubcellTernaryType), intent(inout) :: this
69  class(SubcellTriType), intent(in) :: subcell
70  type(ParticleType), pointer, intent(inout) :: particle
71  real(DP), intent(in) :: tmax
72  ! local
73  integer(I4B) :: exitFace
74  logical(LGP) :: lbary ! kluge
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) :: rot(2, 2), res(2), loc(2)
102  real(DP) :: alp
103  real(DP) :: bet
104  real(DP) :: alp0
105  real(DP) :: bet0
106  real(DP) :: alp1
107  real(DP) :: bet1
108  real(DP) :: alp2
109  real(DP) :: bet2
110  real(DP) :: alpi
111  real(DP) :: beti
112  real(DP) :: vzbot
113  real(DP) :: vztop
114  real(DP) :: vzi
115  real(DP) :: vziodz
116  real(DP) :: az
117  real(DP) :: dtexitz
118  real(DP) :: dt
119  real(DP) :: t
120  real(DP) :: t0
121  real(DP) :: dtexitxy
122  real(DP) :: texit
123  real(DP) :: x
124  real(DP) :: y
125  real(DP) :: z
126  integer(I4B) :: izstatus
127  integer(I4B) :: itopbotexit
128  integer(I4B) :: ntmax
129  integer(I4B) :: nsave
130  integer(I4B) :: isolv
131  integer(I4B) :: itrifaceenter
132  integer(I4B) :: itrifaceexit
133  real(DP) :: tol
134  real(DP) :: step
135  real(DP) :: dtexit
136  real(DP) :: alpexit
137  real(DP) :: betexit
138  integer(I4B) :: ntdebug ! kluge
139  integer(I4B) :: reason
140  integer(I4B) :: i
141  integer(I4B) :: tslice(2)
142 
143  lbary = .true. ! kluge
144  ntmax = 10000
145  nsave = 1 ! needed???
146  isolv = this%zeromethod
147  tol = 1d-7
148  step = 1e-3 ! needed only for euler
149  reason = -1
150 
151  ! -- Set some local variables for convenience
152  xi = particle%x
153  yi = particle%y
154  zi = particle%z
155  x0 = subcell%x0
156  y0 = subcell%y0
157  x1 = subcell%x1
158  y1 = subcell%y1
159  x2 = subcell%x2
160  y2 = subcell%y2
161  v0x = subcell%v0x
162  v0y = subcell%v0y
163  v1x = subcell%v1x
164  v1y = subcell%v1y
165  v2x = subcell%v2x
166  v2y = subcell%v2y
167  zbot = subcell%zbot
168  ztop = subcell%ztop
169  dz = subcell%dz
170  vzbot = subcell%vzbot
171  vztop = subcell%vztop
172 
173  ! -- Translate and rotate coordinates to "canonical" configuration
174  call canonical(x0, y0, x1, y1, x2, y2, &
175  v0x, v0y, v1x, v1y, v2x, v2y, &
176  xi, yi, &
177  rxx, rxy, ryx, ryy, &
178  sxx, sxy, syy, &
179  alp0, bet0, alp1, bet1, alp2, bet2, alpi, beti, &
180  lbary)
181 
182  ! -- Do calculations related to analytical z solution, which can be done
183  ! -- after traverse_triangle call if results not needed for adaptive time
184  ! -- stepping during triangle (subcell) traversal
185  ! kluge note: actually, can probably do z calculation just once for each cell
186  zirel = (zi - zbot) / dz
187  call calculate_dt(vzbot, vztop, dz, zirel, vzi, &
188  az, dtexitz, izstatus, &
189  itopbotexit)
190  vziodz = vzi / dz
191 
192  ! -- Traverse triangular subcell
193  ntdebug = -999 ! kluge debug bludebug
194  itrifaceenter = particle%iboundary(3) - 1
195  if (itrifaceenter .eq. -1) itrifaceenter = 999
196 
197  ! kluge note: can probably avoid calculating alpexit
198  ! here in many cases and wait to calculate it later,
199  ! once the final trajectory time is known
200  call traverse_triangle(isolv, tol, step, &
201  dtexitxy, alpexit, betexit, &
202  itrifaceenter, itrifaceexit, &
203  rxx, rxy, ryx, ryy, &
204  alp0, bet0, alp1, bet1, alp2, bet2, alpi, beti, &
205  vziodz, az, lbary)
206 
207  ! -- Check for no exit face
208  if ((itopbotexit .eq. 0) .and. (itrifaceexit .eq. 0)) then
209  ! exitFace = 0
210  ! particle%iboundary(3) = exitFace
211  ! particle%istatus = 5
212  ! return
213 
214  ! contact the developer situation (for now? always?)
215  print *, "Subcell with no exit face: particle", get_particle_id(particle), &
216  "cell", particle%idomain(2)
217  call pstop(1)
218  end if
219 
220  ! -- Determine (earliest) exit face and corresponding travel time to exit
221  if (itopbotexit .eq. 0) then
222  ! -- Exits through triangle face first
223  exitface = itrifaceexit
224  dtexit = dtexitxy
225  else if (itrifaceexit .eq. 0) then
226  ! -- Exits through top/bottom first
227  exitface = 45
228  dtexit = dtexitz
229  else if (dtexitz .lt. dtexitxy) then
230  ! -- Exits through top/bottom first
231  exitface = 45
232  dtexit = dtexitz
233  else
234  ! -- Exits through triangle face first
235  exitface = itrifaceexit
236  dtexit = dtexitxy
237  end if
238  if (exitface .eq. 45) then
239  if (itopbotexit .eq. -1) then
240  exitface = 4
241  else
242  exitface = 5
243  end if
244  end if
245 
246  ! -- Compute exit time
247  texit = particle%ttrack + dtexit
248  t0 = particle%ttrack
249 
250  ! -- Select user tracking times to solve. If this is the first time step
251  ! of the simulation, include all times before it begins; if it is the
252  ! last time step, include all times after it ends. Otherwise take the
253  ! times within the current period and time step only.
254  call this%tracktimes%try_advance()
255  tslice = this%tracktimes%selection
256  if (all(tslice > 0)) then
257  do i = tslice(1), tslice(2)
258  t = this%tracktimes%times(i)
259  if (t < particle%ttrack .or. t >= texit .or. t >= tmax) cycle
260  dt = t - t0
261  call step_analytical(dt, alp, bet)
262  loc = (/alp, bet/)
263  if (lbary) loc = skew(loc, (/sxx, sxy, syy/), invert=.true.)
264  rot = reshape((/rxx, rxy, ryx, ryy/), shape(rot))
265  res = matmul(rot, loc) ! rotate vector
266  x = res(1) + x0
267  y = res(2) + y0
268  ! kluge note: make this into a function
269  if (izstatus .eq. 2) then
270  ! -- vz uniformly zero
271  z = zi
272  else if (izstatus .eq. 1) then
273  ! -- vz uniform, nonzero
274  z = zi + vzi * dt
275  else
276  ! -- vz nonuniform
277  z = zbot + (vzi * dexp(az * dt) - vzbot) / az
278  end if
279  particle%x = x
280  particle%y = y
281  particle%z = z
282  particle%ttrack = t
283  particle%istatus = 1
284  call this%save(particle, reason=5)
285  end do
286  end if
287 
288  if (texit .gt. tmax) then
289  ! -- The computed exit time is greater than the maximum time, so set
290  ! -- final time for particle trajectory equal to maximum time.
291  t = tmax
292  dt = t - t0
293  exitface = 0
294  particle%istatus = 1
295  particle%advancing = .false.
296  reason = 2 ! timestep end
297  else
298  ! -- The computed exit time is less than or equal to the maximum time,
299  ! -- so set final time for particle trajectory equal to exit time.
300  t = texit
301  dt = dtexit
302  reason = 1 ! cell transition
303  end if
304 
305  ! -- Calculate final particle location
306  ! -- kluge note: need to evaluate both alpha and beta here only
307  ! -- for exitFace=0, otherwise just one or the other
308  call step_analytical(dt, alp, bet)
309  if (exitface .eq. 1) then
310  bet = 0d0
311  else if (exitface .eq. 2) then
312  alp = 1d0 - bet
313  else if (exitface .eq. 3) then
314  alp = 0d0
315  end if
316  loc = (/alp, bet/)
317  if (lbary) loc = skew(loc, (/sxx, sxy, syy/), invert=.true.)
318  rot = reshape((/rxx, rxy, ryx, ryy/), shape(rot))
319  res = matmul(rot, loc) ! rotate vector
320  x = res(1) + x0
321  y = res(2) + y0
322  if (exitface .eq. 4) then
323  z = zbot
324  else if (exitface .eq. 5) then
325  z = ztop
326  else
327  if (izstatus .eq. 2) then ! kluge note: make this into a function
328  ! -- vz uniformly zero
329  z = zi
330  else if (izstatus .eq. 1) then
331  ! -- vz uniform, nonzero
332  z = zi + vzi * dt
333  else
334  ! -- vz nonuniform
335  z = zbot + (vzi * dexp(az * dt) - vzbot) / az
336  end if
337  end if
338 
339  ! -- Set final particle location in local (unscaled) subcell coordinates,
340  ! -- final time for particle trajectory, and exit face
341  particle%x = x
342  particle%y = y
343  particle%z = z
344  particle%ttrack = t
345  particle%iboundary(3) = exitface
346 
347  ! -- Save particle track record
348  if (reason > -1) &
349  call this%save(particle, reason=reason) ! reason=2: timestep
Here is the call graph for this function: