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) :: z_origin
67  real(DP) :: sinrot
68  real(DP) :: cosrot
69 
70  select type (subcell => this%subcell)
71  type is (subcellrecttype)
72  ! Transform particle position into local subcell coordinates,
73  ! track particle across subcell, convert back to model coords
74  ! (sinrot and cosrot should be 0 and 1, respectively, i.e. no
75  ! rotation, also no z translation; only x and y translations)
76  x_origin = subcell%xOrigin
77  y_origin = subcell%yOrigin
78  z_origin = subcell%zOrigin
79  sinrot = subcell%sinrot
80  cosrot = subcell%cosrot
81  call particle%transform(x_origin, y_origin)
82  call this%track_subcell(subcell, particle, tmax)
83  call particle%transform(x_origin, y_origin, invert=.true.)
84  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 320 of file MethodSubcellPollock.f90.

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

301  ! dummy
302  real(DP), intent(in) :: v1
303  real(DP), intent(in) :: v2
304  real(DP), intent(in) :: dx
305  real(DP), intent(in) :: xL
306  type(LinearExitSolutionType) :: solution
307 
308  solution = linearexitsolutiontype()
309  solution%status = calculate_dt(v1, v2, dx, xl, &
310  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

Definition at line 260 of file MethodSubcellPollock.f90.

261  type(ParticleType), pointer, intent(inout) :: particle
262  class(DomainType), intent(in) :: domain
263  type(LinearExitSolutionType), intent(out) :: exit_solutions(3)
264  real(DP), intent(out) :: x0, y0, z0
265 
266  select type (domain)
267  type is (subcellrecttype)
268  ! Initial particle location in scaled subcell coordinates
269  x0 = particle%x / domain%dx
270  y0 = particle%y / domain%dy
271  z0 = particle%z / domain%dz
272 
273  ! Calculate exit solutions for each coordinate direction
274  exit_solutions = [ &
275  find_exit(domain%vx1, domain%vx2, domain%dx, x0), &
276  find_exit(domain%vy1, domain%vy2, domain%dy, y0), &
277  find_exit(domain%vz1, domain%vz2, domain%dz, z0) &
278  ]
279 
280  ! Set exit faces
281  if (exit_solutions(1)%v < dzero) then
282  exit_solutions(1)%iboundary = 1
283  else if (exit_solutions(1)%v > dzero) then
284  exit_solutions(1)%iboundary = 2
285  end if
286  if (exit_solutions(2)%v < dzero) then
287  exit_solutions(2)%iboundary = 3
288  else if (exit_solutions(2)%v > dzero) then
289  exit_solutions(2)%iboundary = 4
290  end if
291  if (exit_solutions(3)%v < dzero) then
292  exit_solutions(3)%iboundary = 5
293  else if (exit_solutions(3)%v > dzero) then
294  exit_solutions(3)%iboundary = 6
295  end if
296  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 454 of file MethodSubcellPollock.f90.

455  ! dummy
456  real(DP), intent(in) :: v
457  real(DP), intent(in) :: dvdx
458  real(DP), intent(in) :: v1
459  real(DP), intent(in) :: v2
460  real(DP), intent(in) :: dt
461  real(DP), intent(in) :: x
462  real(DP), intent(in) :: dx
463  logical(LGP), intent(in), optional :: velocity_profile
464  ! result
465  real(DP) :: newx
466  logical(LGP) :: lprofile
467 
468  ! process optional arguments
469  if (present(velocity_profile)) then
470  lprofile = velocity_profile
471  else
472  lprofile = .false.
473  end if
474 
475  ! recompute coordinate
476  newx = x
477  if (lprofile) then
478  newx = newx + (v1 * dt / dx)
479  else if (v .ne. dzero) then
480  newx = newx + (v * (exp(dvdx * dt) - done) / dvdx / dx)
481  end if
482 
483  ! clamp to [0, 1]
484  if (newx .lt. dzero) newx = dzero
485  if (newx .gt. done) newx = done
486 
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 234 of file MethodSubcellPollock.f90.

235  type(LinearExitSolutionType), intent(in) :: exit_solutions(3)
236  integer(I4B) :: exit_soln
237  ! local
238  real(DP) :: dtmin
239 
240  exit_soln = 0
241  dtmin = 1.0d+30
242 
243  if (exit_solutions(1)%status < 2) then
244  exit_soln = 1 ! x
245  dtmin = exit_solutions(1)%dt
246  end if
247  if (exit_solutions(2)%status < 2 .and. &
248  exit_solutions(2)%dt < dtmin) then
249  exit_soln = 2 ! y
250  dtmin = exit_solutions(2)%dt
251  end if
252  if (exit_solutions(3)%status < 2 .and. &
253  exit_solutions(3)%dt < dtmin) then
254  exit_soln = 3 ! z
255  end if
256 
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 94 of file MethodSubcellPollock.f90.

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