MODFLOW 6  version 6.5.0.dev2
MODFLOW 6 Code Documentation
MethodDis.f90
Go to the documentation of this file.
2 
3  use kindmodule, only: dp, i4b
4  use constantsmodule, only: done, dzero
10  use prtfmimodule, only: prtfmitype
11  use dismodule, only: distype
13  use geomutilmodule, only: get_ijk, get_jk
14  implicit none
15 
16  private
17  public :: methoddistype
18  public :: create_method_dis
19 
20  type, extends(methodtype) :: methoddistype
21  contains
22  procedure, public :: apply => apply_dis ! apply the method
23  procedure, public :: destroy !< destructor for the method
24  procedure, public :: load => load_dis ! load the method
25  procedure :: load_cell_defn !< load cell definition from the grid
26  procedure, public :: pass => pass_dis !< pass the particle to the next domain
27  procedure, private :: get_top ! get cell top elevation
28  procedure, private :: load_nbrs_to_defn ! load face neighbors
29  procedure, private :: load_flows_to_defn ! loads face flows
30  procedure, private :: load_boundary_flows_to_defn ! loads BoundaryFlows
31  end type methoddistype
32 
33 contains
34 
35  !> @brief Create a new structured grid (DIS) tracking method
36  subroutine create_method_dis(method)
37  ! -- dummy
38  type(methoddistype), pointer :: method
39  ! -- local
40  type(cellrecttype), pointer :: cell
41 
42  allocate (method)
43  allocate (method%type)
44  call create_cell_rect(cell)
45  method%cell => cell
46  method%type = "dis"
47  method%delegates = .true.
48  end subroutine create_method_dis
49 
50  !> @brief Destructor the tracking method
51  subroutine destroy(this)
52  class(methoddistype), intent(inout) :: this
53  deallocate (this%type)
54  end subroutine destroy
55 
56  !> @brief Load the cell geometry and method (tracking strategy)
57  subroutine load_dis(this, particle, next_level, submethod)
58  ! -- dummy
59  class(methoddistype), intent(inout) :: this
60  type(particletype), pointer, intent(inout) :: particle
61  integer(I4B), intent(in) :: next_level
62  class(methodtype), pointer, intent(inout) :: submethod
63  ! -- local
64  integer(I4B) :: ic
65  integer(I4B) :: icu
66  integer(I4B) :: irow
67  integer(I4B) :: jcol
68  integer(I4B) :: klay
69  real(DP) :: areax
70  real(DP) :: areay
71  real(DP) :: areaz
72  real(DP) :: dx
73  real(DP) :: dy
74  real(DP) :: dz
75  real(DP) :: factor
76  real(DP) :: term
77 
78  select type (cell => this%cell)
79  type is (cellrecttype)
80  select type (dis => this%fmi%dis)
81  type is (distype)
82  ic = particle%idomain(next_level)
83  call this%load_cell_defn(ic, cell%defn)
84 
85  ! -- If cell is active but dry, select and initialize
86  ! -- pass-to-bottom method and set cell method pointer
87  if (this%fmi%ibdgwfsat0(ic) == 0) then ! kluge note: use cellDefn%sat == DZERO here instead?
88  call method_cell_ptb%init( &
89  cell=this%cell, &
90  trackfilectl=this%trackfilectl, &
91  tracktimes=this%tracktimes)
92  submethod => method_cell_ptb
93  else
94  ! -- load rectangular cell (todo: refactor into separate routine)
95  icu = dis%get_nodeuser(ic)
96  call get_ijk(icu, dis%nrow, dis%ncol, dis%nlay, &
97  irow, jcol, klay)
98  dx = dis%delr(jcol)
99  dy = dis%delc(irow)
100  dz = cell%defn%top - cell%defn%bot
101  cell%dx = dx
102  cell%dy = dy
103  cell%dz = dz
104  cell%sinrot = dzero
105  cell%cosrot = done
106  cell%xOrigin = cell%defn%polyvert(1, 1) ! kluge note: could avoid using polyvert here
107  cell%yOrigin = cell%defn%polyvert(2, 1)
108  cell%zOrigin = cell%defn%bot
109  cell%ipvOrigin = 1
110  areax = dy * dz
111  areay = dx * dz
112  areaz = dx * dy
113  factor = done / cell%defn%retfactor
114  factor = factor / cell%defn%porosity
115  term = factor / areax
116  cell%vx1 = cell%defn%faceflow(1) * term
117  cell%vx2 = -cell%defn%faceflow(3) * term
118  term = factor / areay
119  cell%vy1 = cell%defn%faceflow(4) * term
120  cell%vy2 = -cell%defn%faceflow(2) * term
121  term = factor / areaz
122  cell%vz1 = cell%defn%faceflow(6) * term
123  cell%vz2 = -cell%defn%faceflow(7) * term
124 
125  ! -- Select and initialize Pollock's method and set method pointer
126  call method_cell_plck%init( &
127  cell=this%cell, &
128  trackfilectl=this%trackfilectl, &
129  tracktimes=this%tracktimes)
130  submethod => method_cell_plck
131  end if
132  end select
133  end select
134  end subroutine load_dis
135 
136  !> @brief Pass a particle to the next cell, if there is one
137  subroutine pass_dis(this, particle)
138  ! -- dummy
139  class(methoddistype), intent(inout) :: this
140  type(particletype), pointer, intent(inout) :: particle
141  ! -- local
142  integer(I4B) :: inface
143  integer(I4B) :: ipos
144  integer(I4B) :: ic
145  integer(I4B) :: icu
146  integer(I4B) :: inbr
147  integer(I4B) :: idiag
148  integer(I4B) :: ilay
149  integer(I4B) :: irow
150  integer(I4B) :: icol
151  real(DP) :: z
152  real(DP) :: zrel
153  real(DP) :: topfrom
154  real(DP) :: botfrom
155  real(DP) :: top
156  real(DP) :: bot
157  real(DP) :: sat
158 
159  inface = particle%iboundary(2)
160  z = particle%z
161 
162  select type (cell => this%cell)
163  type is (cellrecttype)
164  select type (dis => this%fmi%dis)
165  type is (distype)
166  inbr = cell%defn%facenbr(inface)
167  if (inbr .eq. 0) then
168  ! -- Exterior face; no neighbor to map to
169  ! particle%idomain(1) = 0
170  ! particle%idomain(2) = 0 ! kluge note: set a "has_exited" attribute instead???
171  ! particle%idomain(1) = -abs(particle%idomain(1)) ! kluge???
172  ! particle%idomain(2) = -abs(particle%idomain(2)) ! kluge???
173  particle%istatus = 2 ! kluge note: use -2 to allow check for transfer to another model???
174  particle%advancing = .false.
175  call this%save(particle, reason=3) ! reason=3: termination
176  ! particle%iboundary(2) = -1
177  else
178  idiag = dis%con%ia(cell%defn%icell)
179  ipos = idiag + inbr
180  ic = dis%con%ja(ipos) ! kluge note: use PRT model's DIS instead of fmi's???
181  particle%idomain(2) = ic
182 
183  ! compute and set user node number and layer on particle
184  icu = dis%get_nodeuser(ic)
185  call get_ijk(icu, dis%nrow, dis%ncol, dis%nlay, &
186  irow, icol, ilay)
187  particle%icu = icu
188  particle%ilay = ilay
189 
190  ! call this%mapToNbrCell(cellRect%cellDefn,inface,z)
191  if (inface .eq. 1) then
192  inface = 3
193  else if (inface .eq. 2) then
194  inface = 4
195  else if (inface .eq. 3) then
196  inface = 1
197  else if (inface .eq. 4) then
198  inface = 2
199  else if (inface .eq. 6) then
200  inface = 7
201  else if (inface .eq. 7) then
202  inface = 6
203  end if
204  particle%iboundary(2) = inface
205  if (inface < 5) then
206  ! -- Map z between cells
207  topfrom = cell%defn%top
208  botfrom = cell%defn%bot
209  zrel = (z - botfrom) / (topfrom - botfrom)
210  top = dis%top(ic) ! kluge note: use PRT model's DIS instead of fmi's???
211  bot = dis%bot(ic)
212  sat = this%fmi%gwfsat(ic)
213  z = bot + zrel * sat * (top - bot)
214  end if
215  particle%z = z
216  ! -- Update cell-cell flows of particle mass.
217  ! Every particle is currently assigned unit mass.
218  ! -- leaving old cell
219  this%flowja(ipos) = this%flowja(ipos) - done
220  ! -- entering new cell
221  this%flowja(dis%con%isym(ipos)) &
222  = this%flowja(dis%con%isym(ipos)) + done
223  end if
224  end select
225  end select
226  end subroutine pass_dis
227 
228  !> @brief Apply the method to a particle
229  subroutine apply_dis(this, particle, tmax)
230  class(methoddistype), intent(inout) :: this
231  type(particletype), pointer, intent(inout) :: particle
232  real(DP), intent(in) :: tmax
233 
234  call this%track(particle, 1, tmax) ! kluge, hardwired to level 1
235  end subroutine apply_dis
236 
237  !> @brief Returns a top elevation based on index iatop
238  function get_top(this, iatop) result(top)
239  class(methoddistype), intent(inout) :: this
240  integer, intent(in) :: iatop
241  double precision :: top
242 
243  if (iatop .lt. 0) then
244  top = this%fmi%dis%top(-iatop)
245  else
246  top = this%fmi%dis%bot(iatop)
247  end if
248  end function get_top
249 
250  !> @brief Loads cell definition from the grid
251  subroutine load_cell_defn(this, ic, defn)
252  ! -- dummy
253  class(methoddistype), intent(inout) :: this
254  integer(I4B), intent(in) :: ic
255  type(celldefntype), pointer, intent(inout) :: defn
256 
257  select type (dis => this%fmi%dis)
258  type is (distype)
259  ! -- Set basic cell properties
260  defn%icell = ic
261  defn%npolyverts = 4 ! rectangular cell always has 4 vertices
262  defn%iatop = get_iatop(dis%get_ncpl(), &
263  dis%get_nodeuser(ic))
264  defn%top = dis%bot(ic) + &
265  this%fmi%gwfsat(ic) * (dis%top(ic) - dis%bot(ic))
266  defn%bot = dis%bot(ic)
267  defn%sat = this%fmi%gwfsat(ic)
268  defn%porosity = this%porosity(ic)
269  defn%retfactor = this%retfactor(ic)
270  defn%izone = this%izone(ic)
271  defn%can_be_rect = .true.
272  defn%can_be_quad = .false.
273 
274  ! -- Load cell polygon vertices
275  call dis%get_polyverts( &
276  defn%icell, &
277  defn%polyvert, &
278  closed=.true.)
279 
280  ! -- Load face neighbors
281  call this%load_nbrs_to_defn(defn)
282 
283  ! -- Load 180 degree face indicators
284  defn%ispv180(1:defn%npolyverts + 1) = .false.
285 
286  ! -- Load flows (assumes face neighbors already loaded)
287  call this%load_flows_to_defn(defn)
288  end select
289  end subroutine load_cell_defn
290 
291  !> @brief Loads face neighbors to cell definition from the grid.
292  !! Assumes cell index and number of vertices are already loaded.
293  subroutine load_nbrs_to_defn(this, defn)
294  ! -- dummy
295  class(methoddistype), intent(inout) :: this
296  type(celldefntype), pointer, intent(inout) :: defn
297  ! -- local
298  integer(I4B) :: ic1
299  integer(I4B) :: ic2
300  integer(I4B) :: icu1
301  integer(I4B) :: icu2
302  integer(I4B) :: j1
303  integer(I4B) :: iloc
304  integer(I4B) :: ipos
305  integer(I4B) :: irow1
306  integer(I4B) :: irow2
307  integer(I4B) :: jcol1
308  integer(I4B) :: jcol2
309  integer(I4B) :: klay1
310  integer(I4B) :: klay2
311  integer(I4B) :: iedgeface
312 
313  select type (dis => this%fmi%dis)
314  type is (distype)
315  ! -- Load face neighbors
316  defn%facenbr = 0
317  ic1 = defn%icell
318  icu1 = dis%get_nodeuser(ic1)
319  call get_ijk(icu1, dis%nrow, dis%ncol, dis%nlay, &
320  irow1, jcol1, klay1)
321  call get_jk(icu1, dis%get_ncpl(), dis%nlay, j1, klay1)
322  do iloc = 1, dis%con%ia(ic1 + 1) - dis%con%ia(ic1) - 1
323  ipos = dis%con%ia(ic1) + iloc
324  if (dis%con%mask(ipos) == 0) cycle ! kluge note: need mask here???
325  ic2 = dis%con%ja(ipos)
326  icu2 = dis%get_nodeuser(ic2)
327  call get_ijk(icu2, dis%nrow, dis%ncol, dis%nlay, &
328  irow2, jcol2, klay2)
329  if (klay2 == klay1) then
330  ! -- Edge (polygon) face neighbor
331  if (irow2 > irow1) then
332  ! Neighbor to the S
333  iedgeface = 4 ! kluge note: make sure this numbering is consistent with numbering in cell method
334  else if (jcol2 > jcol1) then
335  ! Neighbor to the E
336  iedgeface = 3
337  else if (irow2 < irow1) then
338  ! Neighbor to the N
339  iedgeface = 2
340  else
341  ! Neighbor to the W
342  iedgeface = 1
343  end if
344  defn%facenbr(iedgeface) = int(iloc, 1)
345  else if (klay2 > klay1) then
346  ! -- Bottom face neighbor
347  defn%facenbr(defn%npolyverts + 2) = int(iloc, 1)
348  else
349  ! -- Top face neighbor
350  defn%facenbr(defn%npolyverts + 3) = int(iloc, 1)
351  end if
352  end do
353  end select
354  ! -- List of edge (polygon) faces wraps around
355  ! todo: why need to wrap around? no analog to "closing" a polygon?
356  defn%facenbr(defn%npolyverts + 1) = defn%facenbr(1)
357  end subroutine load_nbrs_to_defn
358 
359  !> @brief Load flows into the cell definition.
360  !! These include face flows and net distributed flows.
361  !! Assumes cell index and number of vertices are already loaded.
362  subroutine load_flows_to_defn(this, defn)
363  ! -- dummy
364  class(methoddistype), intent(inout) :: this
365  type(celldefntype), intent(inout) :: defn
366  ! -- local
367  integer(I4B) :: ic
368  integer(I4B) :: m
369  integer(I4B) :: n
370  integer(I4B) :: npolyverts
371 
372  ic = defn%icell
373  npolyverts = defn%npolyverts
374 
375  ! -- Load face flows.
376  defn%faceflow = 0d0 ! kluge note: eventually use DZERO for 0d0 throughout
377  ! -- As with polygon nbrs, polygon face flows wrap around for
378  ! -- convenience at position npolyverts+1, and bot and top flows
379  ! -- are tacked on the end of the list
380  do m = 1, npolyverts + 3
381  n = defn%facenbr(m)
382  if (n > 0) &
383  defn%faceflow(m) = this%fmi%gwfflowja(this%fmi%dis%con%ia(ic) + n)
384  end do
385 
386  ! -- Add BoundaryFlows to face flows
387  call this%load_boundary_flows_to_defn(defn)
388  ! -- Set inoexitface flag
389  defn%inoexitface = 1
390  do m = 1, npolyverts + 3 ! kluge note: can be streamlined with above code
391  if (defn%faceflow(m) < 0d0) defn%inoexitface = 0
392  end do
393 
394  ! -- Add up net distributed flow
395  defn%distflow = this%fmi%SourceFlows(ic) + this%fmi%SinkFlows(ic) + &
396  this%fmi%StorageFlows(ic)
397 
398  ! -- Set weak sink flag
399  if (this%fmi%SinkFlows(ic) .ne. 0d0) then
400  defn%iweaksink = 1
401  else
402  defn%iweaksink = 0
403  end if
404 
405  end subroutine load_flows_to_defn
406 
407  !> @brief Add boundary flows to the cell definition faceflow array.
408  !! Assumes cell index and number of vertices are already loaded.
409  subroutine load_boundary_flows_to_defn(this, defn)
410  ! -- dummy
411  class(methoddistype), intent(inout) :: this
412  type(celldefntype), intent(inout) :: defn
413  ! -- local
414  integer(I4B) :: ic
415  integer(I4B) :: ioffset
416 
417  ic = defn%icell
418  ioffset = (ic - 1) * 10
419  defn%faceflow(1) = defn%faceflow(1) + &
420  this%fmi%BoundaryFlows(ioffset + 1) ! kluge note: should these be additive (seems so)???
421  defn%faceflow(2) = defn%faceflow(2) + &
422  this%fmi%BoundaryFlows(ioffset + 2)
423  defn%faceflow(3) = defn%faceflow(3) + &
424  this%fmi%BoundaryFlows(ioffset + 3)
425  defn%faceflow(4) = defn%faceflow(4) + &
426  this%fmi%BoundaryFlows(ioffset + 4)
427  defn%faceflow(5) = defn%faceflow(1)
428  defn%faceflow(6) = defn%faceflow(6) + &
429  this%fmi%BoundaryFlows(ioffset + 9)
430  defn%faceflow(7) = defn%faceflow(7) + &
431  this%fmi%BoundaryFlows(ioffset + 10)
432  end subroutine load_boundary_flows_to_defn
433 
434 end module methoddismodule
subroutine, public create_cell_rect(cell)
Create a new rectangular cell.
Definition: CellRect.f90:40
This module contains simulation constants.
Definition: Constants.f90:9
real(dp), parameter dzero
real constant zero
Definition: Constants.f90:64
real(dp), parameter done
real constant 1
Definition: Constants.f90:75
Definition: Dis.f90:1
subroutine, public get_ijk(nodenumber, nrow, ncol, nlay, irow, icol, ilay)
Get row, column and layer indices from node number and grid dimensions. If nodenumber is invalid,...
Definition: GeomUtil.f90:98
subroutine, public get_jk(nodenumber, ncpl, nlay, icpl, ilay)
Get layer index and within-layer node index from node number and grid dimensions. If nodenumber is in...
Definition: GeomUtil.f90:126
This module defines variable data types.
Definition: kind.f90:8
Cell-level tracking methods.
type(methodcellpollocktype), pointer, public method_cell_plck
type(methodcellpasstobottype), pointer, public method_cell_ptb
subroutine load_boundary_flows_to_defn(this, defn)
Add boundary flows to the cell definition faceflow array. Assumes cell index and number of vertices a...
Definition: MethodDis.f90:410
subroutine load_cell_defn(this, ic, defn)
Loads cell definition from the grid.
Definition: MethodDis.f90:252
subroutine load_nbrs_to_defn(this, defn)
Loads face neighbors to cell definition from the grid. Assumes cell index and number of vertices are ...
Definition: MethodDis.f90:294
double precision function get_top(this, iatop)
Returns a top elevation based on index iatop.
Definition: MethodDis.f90:239
subroutine load_flows_to_defn(this, defn)
Load flows into the cell definition. These include face flows and net distributed flows....
Definition: MethodDis.f90:363
subroutine, public create_method_dis(method)
Create a new structured grid (DIS) tracking method.
Definition: MethodDis.f90:37
subroutine apply_dis(this, particle, tmax)
Apply the method to a particle.
Definition: MethodDis.f90:230
subroutine load_dis(this, particle, next_level, submethod)
Load the cell geometry and method (tracking strategy)
Definition: MethodDis.f90:58
subroutine pass_dis(this, particle)
Pass a particle to the next cell, if there is one.
Definition: MethodDis.f90:138
Particle tracking strategies.
Definition: Method.f90:2
integer(i4b) function, public get_iatop(ncpl, icu)
Get the index corresponding to top elevation of a cell in the grid. This is -1 if the cell is in the ...
Definition: Method.f90:231
Base grid cell definition.
Definition: CellDefn.f90:10
Structured grid discretization.
Definition: Dis.f90:23
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