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

Data Types

type  methodsubcellpollocktype
 Rectangular subcell tracking method. More...
 

Functions/Subroutines

subroutine, public create_method_subcell_pollock (method)
 Create a new Pollock's subcell method. More...
 
subroutine deallocate (this)
 Deallocate the Pollock's subcell method. More...
 
subroutine apply_msp (this, particle, tmax)
 Apply Pollock's method to a rectangular subcell. More...
 
subroutine track_subcell (this, subcell, particle, tmax)
 Track a particle across a rectangular subcell using Pollock's method. More...
 
integer(i4b) function pick_exit (exit_solutions)
 Pick the exit solution with the shortest travel time. More...
 
subroutine find_exits (particle, domain, exit_solutions, x0, y0, z0)
 Compute candidate exit solutions. More...
 
type(linearexitsolutiontype) function find_exit (v1, v2, dx, xL)
 Find an exit solution for one dimension. More...
 
integer(i4b) function, public calculate_dt (v1, v2, dx, xL, v, dvdx, dt)
 Calculate particle travel time to exit and exit status. More...
 
pure real(dp) function new_x (v, dvdx, v1, v2, dt, x, dx, velocity_profile)
 Update a cell-local coordinate based on a time increment. More...
 

Function/Subroutine Documentation

◆ apply_msp()

subroutine methodsubcellpollockmodule::apply_msp ( class(methodsubcellpollocktype), intent(inout)  this,
type(particletype), intent(inout), pointer  particle,
real(dp), intent(in)  tmax 
)
private

Definition at line 58 of file MethodSubcellPollock.f90.

59  ! dummy
60  class(MethodSubcellPollockType), intent(inout) :: this
61  type(ParticleType), pointer, intent(inout) :: particle
62  real(DP), intent(in) :: tmax
63  ! local
64  real(DP) :: x_origin
65  real(DP) :: y_origin
66  real(DP) :: sinrot
67  real(DP) :: cosrot
68 
69  select type (subcell => this%subcell)
70  type is (subcellrecttype)
71  ! Transform particle position into local subcell coordinates,
72  ! track particle across subcell, convert back to model coords
73  ! (sinrot and cosrot should be 0 and 1, respectively, i.e. no
74  ! rotation, also no z translation; only x and y translations,
75  ! since subcell%zOrigin is always 0 -- z is translated once,
76  ! at the cell level, not again here)
77  x_origin = subcell%xOrigin
78  y_origin = subcell%yOrigin
79  sinrot = subcell%sinrot
80  cosrot = subcell%cosrot
81  call particle%transform(x_origin, y_origin)
82 
83  ! Clamp the particle into the subcell in case roundoff in the
84  ! model-to-local coordinate transform (e.g. under grid rotation)
85  ! left it just outside, analogous to the nudge applied to the
86  ! ternary method's barycentric coordinates.
87  particle%x = min(max(particle%x, dzero), subcell%dx)
88  particle%y = min(max(particle%y, dzero), subcell%dy)
89  particle%z = min(max(particle%z, dzero), subcell%dz)
90 
91  call this%track_subcell(subcell, particle, tmax)
92  call particle%transform(x_origin, y_origin, invert=.true.)
93  end select

◆ calculate_dt()

integer(i4b) function, public methodsubcellpollockmodule::calculate_dt ( real(dp)  v1,
real(dp)  v2,
real(dp)  dx,
real(dp)  xL,
real(dp)  v,
real(dp)  dvdx,
real(dp)  dt 
)

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 335 of file MethodSubcellPollock.f90.

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

◆ create_method_subcell_pollock()

subroutine, public methodsubcellpollockmodule::create_method_subcell_pollock ( type(methodsubcellpollocktype), pointer  method)

Definition at line 38 of file MethodSubcellPollock.f90.

39  ! dummy
40  type(MethodSubcellPollockType), pointer :: method
41  ! local
42  type(SubcellRectType), pointer :: subcell
43 
44  allocate (method)
45  call create_subcell_rect(subcell)
46  method%subcell => subcell
47  method%name => method%subcell%type
48  method%delegates = .false.
Here is the call graph for this function:
Here is the caller graph for this function:

◆ deallocate()

subroutine methodsubcellpollockmodule::deallocate ( class(methodsubcellpollocktype), intent(inout)  this)
private

Definition at line 52 of file MethodSubcellPollock.f90.

53  class(MethodSubcellPollockType), intent(inout) :: this
54  deallocate (this%name)

◆ find_exit()

type(linearexitsolutiontype) function methodsubcellpollockmodule::find_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 315 of file MethodSubcellPollock.f90.

316  ! dummy
317  real(DP), intent(in) :: v1
318  real(DP), intent(in) :: v2
319  real(DP), intent(in) :: dx
320  real(DP), intent(in) :: xL
321  type(LinearExitSolutionType) :: solution
322 
323  solution = linearexitsolutiontype()
324  solution%status = calculate_dt(v1, v2, dx, xl, &
325  solution%v, solution%dvdx, solution%dt)
Here is the call graph for this function:
Here is the caller graph for this function:

◆ find_exits()

subroutine methodsubcellpollockmodule::find_exits ( type(particletype), intent(inout), pointer  particle,
class(domaintype), intent(in)  domain,
type(linearexitsolutiontype), dimension(3), intent(out)  exit_solutions,
real(dp), intent(out)  x0,
real(dp), intent(out)  y0,
real(dp), intent(out)  z0 
)
private

Also returns the local subcell coordinates used (x0, y0, z0), so track_subcell doesn't need to recompute them from the particle's position.

Definition at line 275 of file MethodSubcellPollock.f90.

276  type(ParticleType), pointer, intent(inout) :: particle
277  class(DomainType), intent(in) :: domain
278  type(LinearExitSolutionType), intent(out) :: exit_solutions(3)
279  real(DP), intent(out) :: x0, y0, z0
280 
281  select type (domain)
282  type is (subcellrecttype)
283  ! Initial particle location in scaled subcell coordinates
284  x0 = particle%x / domain%dx
285  y0 = particle%y / domain%dy
286  z0 = particle%z / domain%dz
287 
288  ! Calculate exit solutions for each coordinate direction
289  exit_solutions = [ &
290  find_exit(domain%vx1, domain%vx2, domain%dx, x0), &
291  find_exit(domain%vy1, domain%vy2, domain%dy, y0), &
292  find_exit(domain%vz1, domain%vz2, domain%dz, z0) &
293  ]
294 
295  ! Set exit faces
296  if (exit_solutions(1)%v < dzero) then
297  exit_solutions(1)%iboundary = 1
298  else if (exit_solutions(1)%v > dzero) then
299  exit_solutions(1)%iboundary = 2
300  end if
301  if (exit_solutions(2)%v < dzero) then
302  exit_solutions(2)%iboundary = 3
303  else if (exit_solutions(2)%v > dzero) then
304  exit_solutions(2)%iboundary = 4
305  end if
306  if (exit_solutions(3)%v < dzero) then
307  exit_solutions(3)%iboundary = 5
308  else if (exit_solutions(3)%v > dzero) then
309  exit_solutions(3)%iboundary = 6
310  end if
311  end select
Here is the call graph for this function:
Here is the caller graph for this function:

◆ new_x()

pure real(dp) function methodsubcellpollockmodule::new_x ( real(dp), intent(in)  v,
real(dp), intent(in)  dvdx,
real(dp), intent(in)  v1,
real(dp), intent(in)  v2,
real(dp), intent(in)  dt,
real(dp), intent(in)  x,
real(dp), intent(in)  dx,
logical(lgp), intent(in), optional  velocity_profile 
)
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 469 of file MethodSubcellPollock.f90.

470  ! dummy
471  real(DP), intent(in) :: v
472  real(DP), intent(in) :: dvdx
473  real(DP), intent(in) :: v1
474  real(DP), intent(in) :: v2
475  real(DP), intent(in) :: dt
476  real(DP), intent(in) :: x
477  real(DP), intent(in) :: dx
478  logical(LGP), intent(in), optional :: velocity_profile
479  ! result
480  real(DP) :: newx
481  logical(LGP) :: lprofile
482 
483  ! process optional arguments
484  if (present(velocity_profile)) then
485  lprofile = velocity_profile
486  else
487  lprofile = .false.
488  end if
489 
490  ! recompute coordinate
491  newx = x
492  if (lprofile) then
493  newx = newx + (v1 * dt / dx)
494  else if (v .ne. dzero) then
495  newx = newx + (v * (exp(dvdx * dt) - done) / dvdx / dx)
496  end if
497 
498  ! clamp to [0, 1]
499  if (newx .lt. dzero) newx = dzero
500  if (newx .gt. done) newx = done
501 
Here is the caller graph for this function:

◆ pick_exit()

integer(i4b) function methodsubcellpollockmodule::pick_exit ( type(linearexitsolutiontype), dimension(3), intent(in)  exit_solutions)

Definition at line 244 of file MethodSubcellPollock.f90.

245  type(LinearExitSolutionType), intent(in) :: exit_solutions(3)
246  integer(I4B) :: exit_soln
247  ! local
248  real(DP) :: dtmin
249 
250  exit_soln = 0
251  dtmin = 1.0d+30
252 
253  if (exit_solutions(1)%status < 2) then
254  exit_soln = 1 ! x
255  dtmin = exit_solutions(1)%dt
256  end if
257  if (exit_solutions(2)%status < 2 .and. &
258  exit_solutions(2)%dt < dtmin) then
259  exit_soln = 2 ! y
260  dtmin = exit_solutions(2)%dt
261  end if
262  if (exit_solutions(3)%status < 2 .and. &
263  exit_solutions(3)%dt < dtmin) then
264  exit_soln = 3 ! z
265  end if
266 
Here is the caller graph for this function:

◆ track_subcell()

subroutine methodsubcellpollockmodule::track_subcell ( class(methodsubcellpollocktype), intent(inout)  this,
class(subcellrecttype), intent(in)  subcell,
type(particletype), intent(inout), pointer  particle,
real(dp), intent(in)  tmax 
)
private

This subroutine consists partly of code written by David W. Pollock of the USGS for MODPATH 7. PRT's authors take responsibility for its application in this context and for any modifications or errors.

Definition at line 103 of file MethodSubcellPollock.f90.

106  ! dummy
107  class(MethodSubcellPollockType), intent(inout) :: this
108  class(SubcellRectType), intent(in) :: subcell
109  type(ParticleType), pointer, intent(inout) :: particle
110  real(DP), intent(in) :: tmax
111  ! local
112  real(DP) :: dt, dtexit, texit
113  real(DP) :: t, x, y, z
114  real(DP) :: t0, x0, y0, z0
115  integer(I4B) :: i, exit_face, exit_soln
116  type(LinearExitSolutionType) :: exit_solutions(3)
117  type(LinearExitSolutionType) :: exit_x, exit_y, exit_z
118 
119  t0 = particle%ttrack
120 
121  ! Find exit solution in each direction; also returns the
122  ! local subcell coordinates used, so we don't recompute them
123  call find_exits(particle, subcell, exit_solutions, x0, y0, z0)
124  exit_x = exit_solutions(1)
125  exit_y = exit_solutions(2)
126  exit_z = exit_solutions(3)
127 
128  ! Set solution, face, & travel time
129  exit_soln = pick_exit(exit_solutions)
130  if (exit_soln == 0) then
131  exit_face = 0
132  dtexit = 1.0d+30
133  else
134  exit_face = exit_solutions(exit_soln)%iboundary
135  dtexit = exit_solutions(exit_soln)%dt
136  end if
137  texit = particle%ttrack + dtexit
138 
139  ! Terminate if no valid exit solution was found.
140  ! MP7 is more nuanced in determining what to do
141  ! here. It considers whether this stress period
142  ! is steady state or transient, and whether the
143  ! flow is stationary, in determining whether to
144  ! terminate or allow the particle to survive to
145  ! the next time step. It does not compute paths
146  ! within the subcell even for particles it lets
147  ! remain active under this circumstance, though.
148  ! While we may consider that someday, we simply
149  ! terminate and sidestep the complexity for now.
150  if (all([exit_solutions%status] >= no_exit_stationary)) then
151  call this%terminate(particle, status=term_no_exits_sub)
152  return
153  end if
154 
155  ! Select user tracking times to solve. If this is the first time step
156  ! of the simulation, include all times before it begins; if it is the
157  ! last time step include all times after it ends only if the 'extend'
158  ! option is on, otherwise times in this period and time step only.
159  call this%tracktimes%advance()
160  if (this%tracktimes%any()) then
161  do i = this%tracktimes%selection(1), this%tracktimes%selection(2)
162  t = this%tracktimes%times(i)
163  if (t < particle%ttrack) cycle
164  if (t > texit .or. t > tmax) exit
165  dt = t - t0
166  x = new_x(exit_x%v, exit_x%dvdx, subcell%vx1, subcell%vx2, &
167  dt, x0, subcell%dx, exit_x%status == 1)
168  y = new_x(exit_y%v, exit_y%dvdx, subcell%vy1, subcell%vy2, &
169  dt, y0, subcell%dy, exit_y%status == 1)
170  z = new_x(exit_z%v, exit_z%dvdx, subcell%vz1, subcell%vz2, &
171  dt, z0, subcell%dz, exit_z%status == 1)
172  particle%x = x * subcell%dx
173  particle%y = y * subcell%dy
174  particle%z = z * subcell%dz
175  particle%ttrack = t
176  particle%istatus = active
177  call this%usertime(particle)
178  end do
179  end if
180 
181  ! Computed exit time greater than the maximum time? Set the
182  ! tracking time to tmax and calculate the particle location.
183  if (texit .gt. tmax) then
184  t = tmax
185  dt = t - t0
186  x = new_x(exit_x%v, exit_x%dvdx, subcell%vx1, subcell%vx2, &
187  dt, x0, subcell%dx, exit_x%status == 1)
188  y = new_x(exit_y%v, exit_y%dvdx, subcell%vy1, subcell%vy2, &
189  dt, y0, subcell%dy, exit_y%status == 1)
190  z = new_x(exit_z%v, exit_z%dvdx, subcell%vz1, subcell%vz2, &
191  dt, z0, subcell%dz, exit_z%status == 1)
192  exit_face = 0
193  particle%istatus = active
194  particle%advancing = .false.
195  particle%x = x * subcell%dx
196  particle%y = y * subcell%dy
197  particle%z = z * subcell%dz
198  particle%ttrack = t
199  particle%iboundary(level_subfeature) = exit_face
200  call this%timestep(particle)
201  return
202  end if
203 
204  ! If we get to here, the particle is exiting the subcell.
205  ! Its exit time is less than or equal to the maximum time.
206  ! Set tracking time to the exit time and set exit location.
207  t = texit
208  dt = dtexit
209  if ((exit_face .eq. 1) .or. (exit_face .eq. 2)) then
210  x = dzero
211  y = new_x(exit_y%v, exit_y%dvdx, subcell%vy1, subcell%vy2, &
212  dt, y0, subcell%dy, exit_y%status == 1)
213  z = new_x(exit_z%v, exit_z%dvdx, subcell%vz1, subcell%vz2, &
214  dt, z0, subcell%dz, exit_z%status == 1)
215  if (exit_face .eq. 2) x = done
216  else if ((exit_face .eq. 3) .or. (exit_face .eq. 4)) then
217  x = new_x(exit_x%v, exit_x%dvdx, subcell%vx1, subcell%vx2, dt, &
218  x0, subcell%dx, exit_x%status == 1)
219  y = dzero
220  z = new_x(exit_z%v, exit_z%dvdx, subcell%vz1, subcell%vz2, dt, &
221  z0, subcell%dz, exit_z%status == 1)
222  if (exit_face .eq. 4) y = done
223  else if ((exit_face .eq. 5) .or. (exit_face .eq. 6)) then
224  x = new_x(exit_x%v, exit_x%dvdx, subcell%vx1, subcell%vx2, &
225  dt, x0, subcell%dx, exit_x%status == 1)
226  y = new_x(exit_y%v, exit_y%dvdx, subcell%vy1, subcell%vy2, &
227  dt, y0, subcell%dy, exit_y%status == 1)
228  z = dzero
229  if (exit_face .eq. 6) z = done
230  else
231  print *, "programmer error, invalid exit face", exit_face
232  call pstop(1)
233  end if
234  particle%x = x * subcell%dx
235  particle%y = y * subcell%dy
236  particle%z = z * subcell%dz
237  particle%ttrack = t
238  particle%iboundary(level_subfeature) = exit_face
239  call this%subcellexit(particle)
240 
@, public featexit
particle exited a grid feature
@, public timestep
time step ended
@ term_timeout
terminated at stop time or end of simulation
Definition: Particle.f90:40
@ term_no_exits_sub
terminated in a subcell with no exit face
Definition: Particle.f90:39
Here is the call graph for this function: