MODFLOW 6  version 6.5.0.dev2
MODFLOW 6 Code Documentation
MethodSubcellPollock.f90
Go to the documentation of this file.
2  use kindmodule, only: dp, i4b, lgp
3  use errorutilmodule, only: pstop
4  use methodmodule, only: methodtype
7  use prtfmimodule, only: prtfmitype
9  use basedismodule, only: disbasetype
10  use cellmodule, only: celltype
11  implicit none
12  private
13  public :: methodsubcellpollocktype
15  public :: calculate_dt
16 
17  !> @brief Rectangular subcell tracking method
19  private
20  real(dp), allocatable, public :: qextl1(:), qextl2(:), qintl(:) !< external and internal subcell flows
21  contains
22  procedure, public :: apply => apply_msp
23  procedure, public :: destroy => destroy_msp
24  procedure, private :: track_subcell
26 
27 contains
28 
29  !> @brief Create a new Pollock's subcell-method object
30  subroutine create_method_subcell_pollock(method)
31  ! -- dummy
32  type(methodsubcellpollocktype), pointer :: method
33  ! -- local
34  type(subcellrecttype), pointer :: subcell
35 
36  allocate (method)
37  call create_subcell_rect(subcell)
38  method%subcell => subcell
39  method%type => method%subcell%type
40  method%delegates = .false.
41  end subroutine create_method_subcell_pollock
42 
43  !> @brief Destructor for a Pollock's subcell-method object
44  subroutine destroy_msp(this)
45  class(methodsubcellpollocktype), intent(inout) :: this
46  deallocate (this%type)
47  end subroutine destroy_msp
48 
49  !> @brief Apply Pollock's method to a rectangular subcell
50  subroutine apply_msp(this, particle, tmax)
51  ! -- dummy
52  class(methodsubcellpollocktype), intent(inout) :: this
53  type(particletype), pointer, intent(inout) :: particle
54  real(DP), intent(in) :: tmax
55  ! -- local
56  real(DP) :: xOrigin
57  real(DP) :: yOrigin
58  real(DP) :: zOrigin
59  real(DP) :: sinrot
60  real(DP) :: cosrot
61 
62  select type (subcell => this%subcell)
63  type is (subcellrecttype)
64  ! -- Transform particle position into local subcell coordinates,
65  ! track particle across subcell, convert back to model coords
66  ! (sinrot and cosrot should be 0 and 1, respectively, i.e. no
67  ! rotation, also no z translation; only x and y translations)
68  xorigin = subcell%xOrigin
69  yorigin = subcell%yOrigin
70  zorigin = subcell%zOrigin
71  sinrot = subcell%sinrot
72  cosrot = subcell%cosrot
73  call particle%transform(xorigin, yorigin)
74  call this%track_subcell(subcell, particle, tmax)
75  call particle%transform(xorigin, yorigin, invert=.true.)
76  end select
77  end subroutine apply_msp
78 
79  !> @brief Track a particle across a rectangular subcell using Pollock's method
80  !!
81  !! This subroutine consists partly of code written by
82  !! David W. Pollock of the USGS for MODPATH 7. PRT's
83  !! authors take responsibility for its application in
84  !! this context and for any modifications or errors.
85  !<
86  subroutine track_subcell(this, subcell, particle, tmax)
87  ! modules
89  ! dummy
90  class(methodsubcellpollocktype), intent(inout) :: this
91  class(subcellrecttype), intent(in) :: subcell
92  type(particletype), pointer, intent(inout) :: particle
93  real(DP), intent(in) :: tmax
94  ! local
95  real(DP) :: vx
96  real(DP) :: dvxdx
97  real(DP) :: vy
98  real(DP) :: dvydy
99  real(DP) :: vz
100  real(DP) :: dvzdz
101  real(DP) :: dtexitx
102  real(DP) :: dtexity
103  real(DP) :: dtexitz
104  real(DP) :: dtexit
105  real(DP) :: texit
106  real(DP) :: dt
107  real(DP) :: t
108  real(DP) :: t0
109  real(DP) :: x
110  real(DP) :: y
111  real(DP) :: z
112  integer(I4B) :: statusVX
113  integer(I4B) :: statusVY
114  integer(I4B) :: statusVZ
115  integer(I4B) :: i
116  real(DP) :: initialX
117  real(DP) :: initialY
118  real(DP) :: initialZ
119  integer(I4B) :: exitFace
120  integer(I4B) :: reason
121  integer(I4B) :: tslice(2) !< user-time slice for the current time step
122 
123  reason = -1
124 
125  ! -- Initial particle location in scaled subcell coordinates
126  initialx = particle%x / subcell%dx
127  initialy = particle%y / subcell%dy
128  initialz = particle%z / subcell%dz
129 
130  ! -- Compute time of travel to each possible exit face
131  statusvx = calculate_dt(subcell%vx1, subcell%vx2, subcell%dx, &
132  initialx, vx, dvxdx, dtexitx)
133  statusvy = calculate_dt(subcell%vy1, subcell%vy2, subcell%dy, &
134  initialy, vy, dvydy, dtexity)
135  statusvz = calculate_dt(subcell%vz1, subcell%vz2, subcell%dz, &
136  initialz, vz, dvzdz, dtexitz)
137 
138  ! -- Subcell with no exit face, terminate the particle
139  if ((statusvx .eq. 3) .and. (statusvy .eq. 3) .and. (statusvz .eq. 3)) then
140  call this%save(particle, reason=9)
141  return
142  end if
143 
144  ! -- Determine (earliest) exit face and corresponding travel time to exit
145  exitface = 0
146  dtexit = 1.0d+30
147  if ((statusvx .lt. 2) .or. (statusvy .lt. 2) .or. (statusvz .lt. 2)) then
148  ! -- Consider x-oriented faces
149  dtexit = dtexitx
150  if (vx .lt. 0d0) then
151  exitface = 1
152  else if (vx .gt. 0) then
153  exitface = 2
154  end if
155  ! -- Consider y-oriented faces
156  if (dtexity .lt. dtexit) then
157  dtexit = dtexity
158  if (vy .lt. 0d0) then
159  exitface = 3
160  else if (vy .gt. 0d0) then
161  exitface = 4
162  end if
163  end if
164  ! -- Consider z-oriented faces
165  if (dtexitz .lt. dtexit) then
166  dtexit = dtexitz
167  if (vz .lt. 0d0) then
168  exitface = 5
169  else if (vz .gt. 0d0) then
170  exitface = 6
171  end if
172  end if
173  else
174  end if
175 
176  ! -- Compute exit time
177  texit = particle%ttrack + dtexit
178  t0 = particle%ttrack
179 
180  ! -- Select user tracking times to solve. If this is the first time step
181  ! of the simulation, include all times before it begins; if it is the
182  ! last time step include all times after it ends, otherwise the times
183  ! within the current period and time step only.
184  call this%tracktimes%try_advance()
185  tslice = this%tracktimes%selection
186 
187  if (all(tslice > 0)) then
188  do i = tslice(1), tslice(2)
189  t = this%tracktimes%times(i)
190  if (t < particle%ttrack .or. t >= texit .or. t >= tmax) cycle
191  dt = t - t0
192  x = new_x(vx, dvxdx, subcell%vx1, subcell%vx2, &
193  dt, initialx, subcell%dx, statusvx == 1)
194  y = new_x(vy, dvydy, subcell%vy1, subcell%vy2, &
195  dt, initialy, subcell%dy, statusvy == 1)
196  z = new_x(vz, dvzdz, subcell%vz1, subcell%vz2, &
197  dt, initialz, subcell%dz, statusvz == 1)
198  particle%x = x * subcell%dx
199  particle%y = y * subcell%dy
200  particle%z = z * subcell%dz
201  particle%ttrack = t
202  particle%istatus = 1
203  call this%save(particle, reason=5)
204  end do
205  end if
206 
207  if (texit .gt. tmax) then
208  ! -- The computed exit time is greater than the maximum time, so set
209  ! -- final time for particle trajectory equal to maximum time and
210  ! -- calculate particle location at that final time.
211  t = tmax
212  dt = t - t0
213  x = new_x(vx, dvxdx, subcell%vx1, subcell%vx2, &
214  dt, initialx, subcell%dx, statusvx == 1)
215  y = new_x(vy, dvydy, subcell%vy1, subcell%vy2, &
216  dt, initialy, subcell%dy, statusvy == 1)
217  z = new_x(vz, dvzdz, subcell%vz1, subcell%vz2, &
218  dt, initialz, subcell%dz, statusvz == 1)
219  exitface = 0
220  particle%istatus = 1
221  particle%advancing = .false.
222  reason = 2 ! timestep end
223  else
224  ! -- The computed exit time is less than or equal to the maximum time,
225  ! -- so set final time for particle trajectory equal to exit time and
226  ! -- calculate exit location.
227  t = texit
228  dt = dtexit
229  if ((exitface .eq. 1) .or. (exitface .eq. 2)) then
230  x = 0d0
231  y = new_x(vy, dvydy, subcell%vy1, subcell%vy2, &
232  dt, initialy, subcell%dy, statusvy == 1)
233  z = new_x(vz, dvzdz, subcell%vz1, subcell%vz2, &
234  dt, initialz, subcell%dz, statusvz == 1)
235  if (exitface .eq. 2) x = 1.0d0
236  else if ((exitface .eq. 3) .or. (exitface .eq. 4)) then
237  x = new_x(vx, dvxdx, subcell%vx1, subcell%vx2, dt, &
238  initialx, subcell%dx, statusvx == 1)
239  y = 0d0
240  z = new_x(vz, dvzdz, subcell%vz1, subcell%vz2, dt, &
241  initialz, subcell%dz, statusvz == 1)
242  if (exitface .eq. 4) y = 1.0d0
243  else if ((exitface .eq. 5) .or. (exitface .eq. 6)) then
244  x = new_x(vx, dvxdx, subcell%vx1, subcell%vx2, &
245  dt, initialx, subcell%dx, statusvx == 1)
246  y = new_x(vy, dvydy, subcell%vy1, subcell%vy2, &
247  dt, initialy, subcell%dy, statusvy == 1)
248  z = 0d0
249  if (exitface .eq. 6) z = 1.0d0
250  else
251  print *, "programmer error, invalid exit face", exitface
252  call pstop(1)
253  end if
254  reason = 1 ! cell transition
255  end if
256 
257  ! -- Set final particle location in local (unscaled) subcell coordinates,
258  ! -- final time for particle trajectory, and exit face
259  particle%x = x * subcell%dx
260  particle%y = y * subcell%dy
261  particle%z = z * subcell%dz
262  particle%ttrack = t
263  particle%iboundary(3) = exitface
264 
265  ! -- Save particle track record
266  if (reason >= 0) call this%save(particle, reason=reason)
267 
268  end subroutine track_subcell
269 
270  !> @brief Calculate particle travel time to exit and exit status.
271  !!
272  !! This subroutine consists partly or entirely of code written by
273  !! David W. Pollock of the USGS for MODPATH 7. The authors of the present
274  !! code are responsible for its appropriate application in this context
275  !! and for any modifications or errors.
276  !<
277  function calculate_dt(v1, v2, dx, xL, v, dvdx, dt) result(status)
278  ! dummy
279  real(dp) :: v1
280  real(dp) :: v2
281  real(dp) :: dx
282  real(dp) :: xl
283  real(dp) :: v
284  real(dp) :: dvdx
285  real(dp) :: dt
286  ! result
287  integer(I4B) :: status
288  ! local
289  real(dp) :: v2a
290  real(dp) :: v1a
291  real(dp) :: dv
292  real(dp) :: dva
293  real(dp) :: vv
294  real(dp) :: vvv
295  real(dp) :: zro
296  real(dp) :: zrom
297  real(dp) :: x
298  real(dp) :: tol
299  real(dp) :: vr1
300  real(dp) :: vr2
301  real(dp) :: vr
302  real(dp) :: v1v2
303  logical(LGP) :: nooutflow
304 
305  ! -- Initialize variables.
306  status = -1
307  dt = 1.0d+20
308  v2a = v2
309  if (v2a .lt. 0d0) v2a = -v2a
310  v1a = v1
311  if (v1a .lt. 0d0) v1a = -v1a
312  dv = v2 - v1
313  dva = dv
314  if (dva .lt. 0d0) dva = -dva
315 
316  ! -- Check for a uniform zero velocity in this direction.
317  ! -- If so, set status = 2 and return (dt = 1.0d+20).
318  tol = 1.0d-15
319  if ((v2a .lt. tol) .and. (v1a .lt. tol)) then
320  v = 0d0
321  dvdx = 0d0
322  status = 2
323  return
324  end if
325 
326  ! -- Check for uniform non-zero velocity in this direction.
327  ! -- If so, set compute dt using the constant velocity,
328  ! -- set status = 1 and return.
329  vv = v1a
330  if (v2a .gt. vv) vv = v2a
331  vvv = dva / vv
332  if (vvv .lt. 1.0d-4) then
333  zro = tol
334  zrom = -zro
335  v = v1
336  x = xl * dx
337  if (v1 .gt. zro) dt = (dx - x) / v1
338  if (v1 .lt. zrom) dt = -x / v1
339  dvdx = 0d0
340  status = 1
341  return
342  end if
343 
344  ! -- Velocity has a linear variation.
345  ! -- Compute velocity corresponding to particle position.
346  dvdx = dv / dx
347  v = (1.0d0 - xl) * v1 + xl * v2
348 
349  ! -- If flow is into the cell from both sides there is no outflow.
350  ! -- In that case, set status = 3 and return.
351  nooutflow = .true.
352  if (v1 .lt. 0d0) nooutflow = .false.
353  if (v2 .gt. 0d0) nooutflow = .false.
354  if (nooutflow) then
355  status = 3
356  return
357  end if
358 
359  ! -- If there is a divide in the cell for this flow direction, check to
360  ! -- see if the particle is located exactly on the divide. If it is, move
361  ! -- it very slightly to get it off the divide. This avoids possible
362  ! -- numerical problems related to stagnation points.
363  if ((v1 .le. 0d0) .and. (v2 .ge. 0d0)) then
364  if (abs(v) .le. 0d0) then
365  v = 1.0d-20
366  if (v2 .le. 0d0) v = -v
367  end if
368  end if
369 
370  ! -- If there is a flow divide, this check finds out what side of the
371  ! -- divide the particle is on and sets the value of vr appropriately
372  ! -- to reflect that location.
373  vr1 = v1 / v
374  vr2 = v2 / v
375  vr = vr1
376  if (vr .le. 0d0) then
377  vr = vr2
378  end if
379 
380  ! -- If the product v1*v2 > 0, the velocity is in the same direction
381  ! -- throughout the cell (i.e. no flow divide). If so, set the value
382  ! -- of vr to reflect the appropriate direction.
383  v1v2 = v1 * v2
384  if (v1v2 .gt. 0d0) then
385  if (v .gt. 0d0) vr = vr2
386  if (v .lt. 0d0) vr = vr1
387  end if
388 
389  ! -- Check if vr is (very close to) zero.
390  ! -- If so, set status = 2 and return (dt = 1.0d+20).
391  if (dabs(vr) .lt. 1.0d-10) then
392  v = 0d0
393  dvdx = 0d0
394  status = 2
395  return
396  end if
397 
398  ! -- Compute travel time to exit face. Return with status = 0.
399  dt = log(vr) / dvdx
400  status = 0
401 
402  end function calculate_dt
403 
404  !> @brief Update a cell-local coordinate based on a time increment.
405  !!
406  !! This subroutine consists partly or entirely of code written by
407  !! David W. Pollock of the USGS for MODPATH 7. The authors of the present
408  !! code are responsible for its appropriate application in this context
409  !! and for any modifications or errors.
410  !<
411  pure function new_x(v, dvdx, v1, v2, dt, x, dx, velocity_profile) result(newx)
412  ! dummy
413  real(dp), intent(in) :: v
414  real(dp), intent(in) :: dvdx
415  real(dp), intent(in) :: v1
416  real(dp), intent(in) :: v2
417  real(dp), intent(in) :: dt
418  real(dp), intent(in) :: x
419  real(dp), intent(in) :: dx
420  logical(LGP), intent(in), optional :: velocity_profile
421  ! result
422  real(dp) :: newx
423  logical(LGP) :: lprofile
424 
425  ! -- process optional arguments
426  if (present(velocity_profile)) then
427  lprofile = velocity_profile
428  else
429  lprofile = .false.
430  end if
431 
432  ! -- recompute coordinate
433  newx = x
434  if (lprofile) then
435  newx = newx + (v1 * dt / dx)
436  else if (v .ne. 0d0) then
437  newx = newx + (v * (exp(dvdx * dt) - 1.0d0) / dvdx / dx)
438  end if
439 
440  ! -- clamp to [0, 1]
441  if (newx .lt. 0d0) newx = 0d0
442  if (newx .gt. 1.0d0) newx = 1.0d0
443 
444  end function new_x
445 
subroutine pstop(status, message)
Stop the program, optionally specifying an error status code.
Definition: ErrorUtil.f90:24
This module defines variable data types.
Definition: kind.f90:8
Particle tracking strategies.
Definition: Method.f90:2
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.
integer(i4b) function, public calculate_dt(v1, v2, dx, xL, v, dvdx, dt)
Calculate particle travel time to exit and exit status.
subroutine track_subcell(this, subcell, particle, tmax)
Track a particle across a rectangular subcell using Pollock's method.
subroutine, public create_method_subcell_pollock(method)
Create a new Pollock's subcell-method object.
subroutine apply_msp(this, particle, tmax)
Apply Pollock's method to a rectangular subcell.
subroutine destroy_msp(this)
Destructor for a Pollock's subcell-method object.
pure character(len=lenmempath) function, public get_particle_id(particle)
Return the particle's composite ID.
Definition: Particle.f90:354
subroutine, public create_subcell_rect(subcell)
Create a new rectangular subcell.
Definition: SubcellRect.f90:28
Base type for grid cells of a concrete type. Contains a cell-definition which is information shared b...
Definition: Cell.f90:10
Base type for particle tracking methods.
Definition: Method.f90:29
A particle tracked by the PRT model.
Definition: Particle.f90:32
Manages particle track (i.e. pathline) files.
Definition: TrackData.f90:38