MODFLOW 6  version 6.5.0.dev2
MODFLOW 6 Code Documentation
MethodDisv.f90
Go to the documentation of this file.
2 
3  use kindmodule, only: dp, i4b, lgp
4  use errorutilmodule, only: pstop
5  use constantsmodule, only: done
10  use particlemodule
11  use prtfmimodule, only: prtfmitype
12  use disvmodule, only: disvtype
15  use geomutilmodule, only: get_jk
16  implicit none
17 
18  private
19  public :: methoddisvtype
20  public :: create_method_disv
21 
22  type, extends(methodtype) :: methoddisvtype
23  integer(I4B), pointer :: zeromethod
24  contains
25  procedure, public :: apply => apply_disv ! applies the DISV-grid method
26  procedure, public :: destroy ! destructor for the method
27  procedure, public :: load => load_disv ! loads the cell method
28  procedure, public :: load_cell_defn ! loads cell definition from the grid
29  procedure, public :: map_neighbor ! maps a location on the cell face to the shared face of a neighbor
30  procedure, public :: pass => pass_disv ! passes the particle to the next cell
31  procedure, private :: get_npolyverts ! returns the number of polygon vertices for a cell in the grid
32  procedure, private :: get_top ! returns top elevation based on index iatop
33  procedure, private :: load_nbrs_to_defn ! loads face neighbors to a cell object
34  procedure, private :: load_flags_to_defn ! loads 180-degree vertex indicator to a cell object
35  procedure, private :: load_flows_to_defn ! loads flows to a cell object
36  procedure, private :: load_boundary_flows_to_defn_rect ! adds BoundaryFlows from the grid to the faceflow array of a rectangular cell
37  procedure, private :: load_boundary_flows_to_defn_rect_quad ! adds BoundaryFlows from the grid to the faceflow array of a rectangular-quad cell
38  procedure, private :: load_boundary_flows_to_defn_poly ! adds BoundaryFlows from the grid to the faceflow array of a polygonal cell
39  end type methoddisvtype
40 
41 contains
42 
43  !> @brief Create a new vertex grid (DISV) tracking method
44  subroutine create_method_disv(method)
45  ! -- dummy
46  type(methoddisvtype), pointer :: method
47  ! -- local
48  type(cellpolytype), pointer :: cell
49 
50  allocate (method)
51  allocate (method%type)
52  allocate (method%zeromethod)
53  call create_cell_poly(cell)
54  method%cell => cell
55  method%type = "disv"
56  method%delegates = .true.
57  method%zeromethod = 0
58  end subroutine create_method_disv
59 
60  !> @brief Destroy the tracking method
61  subroutine destroy(this)
62  class(methoddisvtype), intent(inout) :: this
63  deallocate (this%type)
64  end subroutine destroy
65 
66  !> @brief Load the cell and the tracking method
67  subroutine load_disv(this, particle, next_level, submethod)
68  use cellmodule
69  use cellrectmodule
71  use cellutilmodule
72  ! -- dummy
73  class(methoddisvtype), intent(inout) :: this
74  type(particletype), pointer, intent(inout) :: particle
75  integer(I4B), intent(in) :: next_level
76  class(methodtype), pointer, intent(inout) :: submethod
77  ! -- local
78  integer(I4B) :: ic
79  class(celltype), pointer :: base
80  type(cellrecttype), pointer :: rect
81  type(cellrectquadtype), pointer :: quad
82 
83  select type (cell => this%cell)
84  type is (cellpolytype)
85  ! load cell definition
86  ic = particle%idomain(next_level) ! kluge note: is cell number always known coming in?
87  call this%load_cell_defn(ic, cell%defn)
88  if (this%fmi%ibdgwfsat0(ic) == 0) then ! kluge note: use cellDefn%sat == DZERO here instead?
89  ! -- Cell is active but dry, so select and initialize pass-to-bottom
90  ! -- cell method and set cell method pointer
91  call method_cell_ptb%init( &
92  cell=this%cell, &
93  trackfilectl=this%trackfilectl, &
94  tracktimes=this%tracktimes)
95  submethod => method_cell_ptb
96  else
97  ! -- Select and initialize cell method and set cell method pointer
98  if (cell%defn%can_be_rect) then
99  call cell_poly_to_rect(cell, rect)
100  base => rect
101  call method_cell_plck%init( &
102  cell=base, &
103  trackfilectl=this%trackfilectl, &
104  tracktimes=this%tracktimes)
105  submethod => method_cell_plck
106  else if (cell%defn%can_be_quad) then
107  call cell_poly_to_quad(cell, quad)
108  base => quad
109  call method_cell_quad%init( &
110  cell=base, &
111  trackfilectl=this%trackfilectl, &
112  tracktimes=this%tracktimes)
113  submethod => method_cell_quad
114  else
115  call method_cell_tern%init( &
116  cell=this%cell, &
117  trackfilectl=this%trackfilectl, &
118  tracktimes=this%tracktimes)
119  submethod => method_cell_tern
120  method_cell_tern%zeromethod = this%zeromethod
121  end if
122  end if
123  end select
124  end subroutine load_disv
125 
126  !> @brief Pass a particle to the next cell, if there is one
127  subroutine pass_disv(this, particle)
128  ! -- modules
129  use disvmodule, only: disvtype
130  ! -- dummy
131  class(methoddisvtype), intent(inout) :: this
132  type(particletype), pointer, intent(inout) :: particle
133  ! -- local
134  integer(I4B) :: inface
135  integer(I4B) :: ipos
136  integer(I4B) :: ic
137  integer(I4B) :: icu
138  integer(I4B) :: inbr
139  integer(I4B) :: idiag
140  integer(I4B) :: icpl
141  integer(I4B) :: ilay
142  real(DP) :: z
143 
144  inface = particle%iboundary(2)
145  z = particle%z
146 
147  select type (cell => this%cell)
148  type is (cellpolytype)
149  select type (dis => this%fmi%dis)
150  type is (disvtype)
151  inbr = cell%defn%facenbr(inface)
152  if (inbr .eq. 0) then
153  ! -- Exterior face; no neighbor to map to
154  ! particle%idomain(1) = 0
155  ! particle%idomain(2) = 0 ! kluge note: "has_exited" attribute instead???
156  ! particle%idomain(1) = -abs(particle%idomain(1)) ! kluge???
157  ! particle%idomain(2) = -abs(particle%idomain(2)) ! kluge???
158  particle%istatus = 2 ! kluge note, todo: use -2 to check for transfer to another model???
159  particle%advancing = .false.
160  call this%save(particle, reason=3) ! reason=3: termination
161  ! particle%iboundary(2) = -1
162  else
163  idiag = dis%con%ia(cell%defn%icell)
164  ipos = idiag + inbr
165  ic = dis%con%ja(ipos) ! kluge note, todo: use PRT model's DIS instead of fmi's??
166  particle%idomain(2) = ic
167 
168  ! compute and set user node number and layer on particle
169  icu = dis%get_nodeuser(ic)
170  call get_jk(icu, dis%ncpl, dis%nlay, icpl, ilay)
171  particle%icu = icu
172  particle%ilay = ilay
173 
174  call this%map_neighbor(cell%defn, inface, z)
175  particle%iboundary(2) = inface
176  particle%idomain(3:) = 0
177  particle%iboundary(3:) = 0
178  particle%z = z
179  ! -- Update cell-cell flows of particle mass.
180  ! Every particle is currently assigned unit mass.
181  ! -- leaving old cell
182  this%flowja(ipos) = this%flowja(ipos) - done
183  ! -- entering new cell
184  this%flowja(dis%con%isym(ipos)) &
185  = this%flowja(dis%con%isym(ipos)) + done
186  end if
187  end select
188  end select
189  end subroutine pass_disv
190 
191  !> @brief Map location on cell face to shared cell face of neighbor
192  subroutine map_neighbor(this, defn, inface, z)
193  ! dummy
194  class(methoddisvtype), intent(inout) :: this
195  type(celldefntype), pointer, intent(inout) :: defn
196  integer(I4B), intent(inout) :: inface
197  double precision, intent(inout) :: z
198  ! local
199  integer(I4B) :: icin
200  integer(I4B) :: npolyvertsin
201  integer(I4B) :: ic
202  integer(I4B) :: npolyverts
203  integer(I4B) :: inbr
204  integer(I4B) :: inbrnbr
205  integer(I4B) :: j
206  integer(I4B) :: m
207  real(DP) :: zrel
208  real(DP) :: topfrom
209  real(DP) :: botfrom
210  real(DP) :: top
211  real(DP) :: bot
212  real(DP) :: sat
213  type(celldefntype), pointer :: cd
214 
215  ! -- Map to shared cell face of neighbor
216  inbr = defn%facenbr(inface)
217  if (inbr .eq. 0) then ! kluge note: redundant check
218  ! -- Exterior face; no neighbor to map to
219  inface = -1 ! kluge???
220  else
221  ! -- Load definition for neighbor cell (neighbor with shared face)
222  icin = defn%icell
223  j = this%fmi%dis%con%ia(icin)
224  ic = this%fmi%dis%con%ja(j + inbr)
225  call create_defn(cd)
226  ! kluge note: really only need to load facenbr and npolyverts for this
227  call this%load_cell_defn(ic, cd) ! kluge
228  npolyvertsin = defn%npolyverts
229  npolyverts = cd%npolyverts
230  if (inface .eq. npolyvertsin + 2) then
231  ! -- Exits through bot, enters through top
232  inface = npolyverts + 3
233  else if (inface .eq. npolyvertsin + 3) then
234  ! -- Exits through top, enters through bot
235  inface = npolyverts + 2
236  else
237  ! -- Exits and enters through shared polygon face
238  j = this%fmi%dis%con%ia(ic)
239  ! kluge note: use shared_edge in DisvGeom to find shared polygon face???
240  do m = 1, npolyverts + 3
241  inbrnbr = cd%facenbr(m)
242  if (this%fmi%dis%con%ja(j + inbrnbr) .eq. icin) then
243  inface = m
244  exit
245  end if
246  end do
247  ! -- Map z between cells
248  topfrom = defn%top
249  botfrom = defn%bot
250  zrel = (z - botfrom) / (topfrom - botfrom)
251  ! kluge note: use PRT model's DIS instead of fmi's???
252  top = this%fmi%dis%top(ic)
253  bot = this%fmi%dis%bot(ic)
254  sat = this%fmi%gwfsat(ic)
255  z = bot + zrel * sat * (top - bot)
256  end if
257  deallocate (cd)
258  end if
259  end subroutine map_neighbor
260 
261  !> @brief Apply the DISV-grid method
262  subroutine apply_disv(this, particle, tmax)
263  class(methoddisvtype), intent(inout) :: this
264  type(particletype), pointer, intent(inout) :: particle
265  real(DP), intent(in) :: tmax
266  call this%track(particle, 1, tmax) ! kluge, hardwired to level 1
267  end subroutine apply_disv
268 
269  !> @brief Return the number of polygon vertices for a cell in the grid
270  function get_npolyverts(this, ic) result(npolyverts)
271  ! -- dummy
272  class(methoddisvtype), intent(inout) :: this
273  integer(I4B), intent(in) :: ic
274  ! -- local
275  integer(I4B) :: icu
276  integer(I4B) :: icu2d
277  integer(I4B) :: ncpl
278  ! -- result
279  integer(I4B) :: npolyverts
280 
281  select type (dis => this%fmi%dis)
282  type is (disvtype)
283  ncpl = dis%get_ncpl()
284  icu = dis%get_nodeuser(ic)
285  icu2d = icu - ((icu - 1) / ncpl) * ncpl ! kluge note: use MOD or MODULO???
286  npolyverts = dis%iavert(icu2d + 1) - dis%iavert(icu2d) - 1
287  if (npolyverts .le. 0) npolyverts = npolyverts + size(dis%javert) ! kluge???
288  end select
289  end function get_npolyverts
290 
291  !> @brief Get top elevation based on index iatop
292  !! kluge note: not needed???
293  function get_top(this, iatop) result(top)
294  ! -- dummy
295  class(methoddisvtype), intent(inout) :: this
296  integer(I4B), intent(in) :: iatop
297  ! -- result
298  real(dp) :: top
299 
300  if (iatop .lt. 0) then
301  top = this%fmi%dis%top(-iatop)
302  else
303  top = this%fmi%dis%bot(iatop)
304  end if
305  end function get_top
306 
307  !> @brief Load cell definition from the grid
308  subroutine load_cell_defn(this, ic, defn)
309  ! -- dummy
310  class(methoddisvtype), intent(inout) :: this
311  integer(I4B), intent(in) :: ic
312  type(celldefntype), pointer, intent(inout) :: defn
313  ! -- local
314  real(DP) :: top
315  real(DP) :: bot
316  real(DP) :: sat
317 
318  ! -- Load basic cell properties
319  defn%icell = ic
320  defn%npolyverts = this%get_npolyverts(ic)
321  defn%iatop = get_iatop(this%fmi%dis%get_ncpl(), &
322  this%fmi%dis%get_nodeuser(ic))
323  top = this%fmi%dis%top(ic)
324  bot = this%fmi%dis%bot(ic)
325  sat = this%fmi%gwfsat(ic)
326  top = bot + sat * (top - bot)
327  defn%top = top
328  defn%bot = bot
329  defn%sat = sat
330  defn%porosity = this%porosity(ic)
331  defn%retfactor = this%retfactor(ic)
332  defn%izone = this%izone(ic)
333 
334  ! -- Load polygon vertices
335  call this%fmi%dis%get_polyverts( &
336  defn%icell, &
337  defn%polyvert, &
338  closed=.true.)
339 
340  ! -- Load face neighbors
341  call this%load_nbrs_to_defn(defn)
342 
343  ! -- Load 180-degree indicator
344  call this%load_flags_to_defn(defn)
345 
346  ! -- Load flows (assumes face neighbors already loaded)
347  call this%load_flows_to_defn(defn)
348  end subroutine load_cell_defn
349 
350  !> @brief Loads face neighbors to cell definition from the grid
351  !! Assumes cell index and number of vertices are already loaded.
352  subroutine load_nbrs_to_defn(this, defn)
353  ! -- dummy
354  class(methoddisvtype), intent(inout) :: this
355  type(celldefntype), pointer, intent(inout) :: defn
356  ! -- local
357  integer(I4B) :: ic
358  integer(I4B) :: npolyverts
359  integer(I4B) :: ic1
360  integer(I4B) :: ic2
361  integer(I4B) :: icu1
362  integer(I4B) :: icu2
363  integer(I4B) :: j1
364  integer(I4B) :: j2
365  integer(I4B) :: k1
366  integer(I4B) :: k2
367  integer(I4B) :: iloc
368  integer(I4B) :: ipos
369  integer(I4B) :: istart1
370  integer(I4B) :: istart2
371  integer(I4B) :: istop1
372  integer(I4B) :: istop2
373  integer(I4B) :: iedgeface
374  integer(I4B) :: ncpl
375 
376  ic = defn%icell
377  npolyverts = defn%npolyverts
378 
379  ! -- expand facenbr array if needed
380  if (size(defn%facenbr) < npolyverts + 3) &
381  call expandarray(defn%facenbr, npolyverts + 3)
382 
383  select type (dis => this%fmi%dis)
384  type is (disvtype)
385  ! -- Load face neighbors.
386  defn%facenbr = 0
387  ic1 = ic
388  icu1 = dis%get_nodeuser(ic1)
389  ncpl = dis%get_ncpl()
390  call get_jk(icu1, ncpl, dis%nlay, j1, k1)
391  istart1 = dis%iavert(j1)
392  istop1 = dis%iavert(j1 + 1) - 1
393  do iloc = 1, dis%con%ia(ic1 + 1) - dis%con%ia(ic1) - 1
394  ipos = dis%con%ia(ic1) + iloc
395  if (dis%con%mask(ipos) == 0) cycle ! kluge note: need mask here???
396  ic2 = dis%con%ja(ipos)
397  icu2 = dis%get_nodeuser(ic2)
398  call get_jk(icu2, ncpl, dis%nlay, j2, k2)
399  istart2 = dis%iavert(j2)
400  istop2 = dis%iavert(j2 + 1) - 1
401  call shared_edgeface(dis%javert(istart1:istop1), &
402  dis%javert(istart2:istop2), &
403  iedgeface)
404  if (iedgeface /= 0) then
405 
406  ! -- Edge (polygon) face neighbor
407  defn%facenbr(iedgeface) = int(iloc, 1)
408  else
409  if (k2 > k1) then
410  ! -- Bottom face neighbor
411  defn%facenbr(npolyverts + 2) = int(iloc, 1)
412  else if (k2 < k1) then
413  ! -- Top face neighbor
414  defn%facenbr(npolyverts + 3) = int(iloc, 1)
415  else
416  call pstop(1, "k2 should be <> k1, since no shared edge face")
417  end if
418  end if
419  end do
420  end select
421  ! -- List of edge (polygon) faces wraps around
422  defn%facenbr(npolyverts + 1) = defn%facenbr(1)
423 
424  end subroutine load_nbrs_to_defn
425 
426  !> @brief Find the edge face shared by two cells
427  !!
428  !! Find the shared edge face of cell1 shared by cell1 and cell2.
429  !! isharedface will return with 0 if there is no shared edge
430  !! face. Proceed forward through ivlist1 and backward through
431  !! ivlist2 as a clockwise face in cell1 must correspond to a
432  !! counter clockwise face in cell2.
433  !!
434  !! kluge note: based on DisvGeom shared_edge
435  !<
436  subroutine shared_edgeface(ivlist1, ivlist2, iedgeface)
437  integer(I4B), dimension(:) :: ivlist1
438  integer(I4B), dimension(:) :: ivlist2
439  integer(I4B), intent(out) :: iedgeface
440  integer(I4B) :: nv1
441  integer(I4B) :: nv2
442  integer(I4B) :: il1
443  integer(I4B) :: il2
444  logical(LGP) :: found
445 
446  found = .false.
447  nv1 = size(ivlist1)
448  nv2 = size(ivlist2)
449  iedgeface = 0
450  outerloop: do il1 = 1, nv1 - 1
451  do il2 = nv2, 2, -1
452  if (ivlist1(il1) == ivlist2(il2) .and. &
453  ivlist1(il1 + 1) == ivlist2(il2 - 1)) then
454  found = .true.
455  iedgeface = il1
456  exit outerloop
457  end if
458  end do
459  if (found) exit
460  end do outerloop
461  end subroutine shared_edgeface
462 
463  !> @brief Load flows into the cell definition.
464  !! These include face flows and net distributed flows.
465  !! Assumes cell index and number of vertices are already loaded.
466  subroutine load_flows_to_defn(this, defn)
467  ! -- dummy
468  class(methoddisvtype), intent(inout) :: this
469  type(celldefntype), intent(inout) :: defn
470  ! -- local
471  integer(I4B) :: ic
472  integer(I4B) :: npolyverts
473  integer(I4B) :: m
474  integer(I4B) :: n
475 
476  ic = defn%icell
477  npolyverts = defn%npolyverts
478 
479  ! -- expand faceflow array if needed
480  if (size(defn%faceflow) < npolyverts + 3) &
481  call expandarray(defn%faceflow, npolyverts + 3)
482 
483  ! -- Load face flows. Note that the faceflow array
484  ! -- does not get reallocated if it is already allocated
485  ! -- to a size greater than or equal to npolyverts+3.
486  defn%faceflow = 0d0
487 
488  ! -- As with polygon nbrs, polygon face flows wrap around for
489  ! -- convenience at position npolyverts+1, and bot and top flows
490  ! -- are tacked on the end of the list
491  do m = 1, npolyverts + 3
492  n = defn%facenbr(m)
493  if (n > 0) &
494  defn%faceflow(m) = this%fmi%gwfflowja(this%fmi%dis%con%ia(ic) + n)
495  end do
496  call this%load_boundary_flows_to_defn_poly(defn)
497  ! -- Set inoexitface flag
498  defn%inoexitface = 1
499  do m = 1, npolyverts + 3 ! kluge note: can be streamlined with above code
500  if (defn%faceflow(m) < 0d0) defn%inoexitface = 0
501  end do
502 
503  ! -- Add up net distributed flow
504  defn%distflow = this%fmi%SourceFlows(ic) + this%fmi%SinkFlows(ic) + &
505  this%fmi%StorageFlows(ic)
506 
507  ! -- Set weak sink flag
508  if (this%fmi%SinkFlows(ic) .ne. 0d0) then
509  defn%iweaksink = 1
510  else
511  defn%iweaksink = 0
512  end if
513 
514  end subroutine load_flows_to_defn
515 
516  !> @brief Load boundary flows from the grid into a rectangular cell.
517  !! Assumes cell index and number of vertices are already loaded.
518  subroutine load_boundary_flows_to_defn_rect(this, defn)
519  ! -- dummy
520  class(methoddisvtype), intent(inout) :: this
521  type(celldefntype), intent(inout) :: defn
522  ! -- local
523  integer(I4B) :: ic
524  integer(I4B) :: npolyverts
525  integer(I4B) :: ioffset
526 
527  ic = defn%icell
528  npolyverts = defn%npolyverts
529 
530  ! kluge note - assignment of BoundaryFlows to faceflow below assumes vertex 1
531  ! is at upper left of rectangular cell, and BoundaryFlows use old iface order
532  ! ioffset = (ic - 1)*6
533  ioffset = (ic - 1) * 10
534  ! kluge note: should these be additive (seems so)???
535  defn%faceflow(1) = defn%faceflow(1) + &
536  this%fmi%BoundaryFlows(ioffset + 4)
537  defn%faceflow(2) = defn%faceflow(2) + &
538  this%fmi%BoundaryFlows(ioffset + 2)
539  defn%faceflow(3) = defn%faceflow(3) + &
540  this%fmi%BoundaryFlows(ioffset + 3)
541  defn%faceflow(4) = defn%faceflow(4) + &
542  this%fmi%BoundaryFlows(ioffset + 1)
543  defn%faceflow(5) = defn%faceflow(1)
544  defn%faceflow(6) = defn%faceflow(6) + &
545  this%fmi%BoundaryFlows(ioffset + 9)
546  defn%faceflow(7) = defn%faceflow(7) + &
547  this%fmi%BoundaryFlows(ioffset + 10)
548 
549  end subroutine load_boundary_flows_to_defn_rect
550 
551  !> @brief Load boundary flows from the grid into rectangular quadcell.
552  !! Assumes cell index and number of vertices are already loaded.
554  ! -- dummy
555  class(methoddisvtype), intent(inout) :: this
556  type(celldefntype), intent(inout) :: defn
557  ! -- local
558  integer(I4B) :: ic
559  integer(I4B) :: npolyverts
560  integer(I4B) :: m
561  integer(I4B) :: n
562  integer(I4B) :: nn
563  integer(I4B) :: ioffset
564  integer(I4B) :: nbf
565  integer(I4B) :: m1
566  integer(I4B) :: m2
567  integer(I4B) :: mdiff
568  real(DP) :: qbf
569  integer(I4B) :: irectvert(5) ! kluge
570 
571  ic = defn%icell
572  npolyverts = defn%npolyverts
573 
574  ! kluge note - assignment of BoundaryFlows to faceflow below assumes vertex 1
575  ! is at upper left of rectangular cell, and BoundaryFlows use old iface order
576  ! ioffset = (ic - 1)*6
577  ioffset = (ic - 1) * 10
578  ! -- Polygon faces in positions 1 through npolyverts
579  do n = 1, 4
580  if (n .eq. 2) then
581  nbf = 4
582  else if (n .eq. 4) then
583  nbf = 1
584  else
585  nbf = n
586  end if
587  qbf = this%fmi%BoundaryFlows(ioffset + nbf)
588  nn = 0 ! kluge ...
589  do m = 1, npolyverts
590  if (.not. defn%ispv180(m)) then
591  nn = nn + 1
592  irectvert(nn) = m
593  end if
594  end do
595  irectvert(5) = irectvert(1) ! ... kluge
596  m1 = irectvert(n)
597  m2 = irectvert(n + 1)
598  if (m2 .lt. m1) m2 = m2 + npolyverts
599  mdiff = m2 - m1
600  if (mdiff .eq. 1) then
601  ! -- Assign BoundaryFlow to corresponding polygon face
602  defn%faceflow(m1) = defn%faceflow(m1) + qbf
603  else
604  ! -- Split BoundaryFlow between two faces on quad-refined edge
605  qbf = 5d-1 * qbf
606  defn%faceflow(m1) = defn%faceflow(m1) + qbf
607  defn%faceflow(m1 + 1) = defn%faceflow(m1 + 1) + qbf
608  end if
609  end do
610  ! -- Wrap around to 1 in position npolyverts+1
611  m = npolyverts + 1
612  defn%faceflow(m) = defn%faceflow(1)
613  ! -- Bottom in position npolyverts+2
614  m = m + 1
615  defn%faceflow(m) = defn%faceflow(m) + &
616  this%fmi%BoundaryFlows(ioffset + 9)
617  ! -- Top in position npolyverts+3
618  m = m + 1
619  defn%faceflow(m) = defn%faceflow(m) + &
620  this%fmi%BoundaryFlows(ioffset + 10)
621 
623 
624  !> @brief Load boundary flows from the grid into a polygonal cell.
625  !! Assumes cell index and number of vertices are already loaded.
626  subroutine load_boundary_flows_to_defn_poly(this, defn)
627  ! -- dummy
628  class(methoddisvtype), intent(inout) :: this
629  type(celldefntype), intent(inout) :: defn
630  ! -- local
631  integer(I4B) :: ic
632  integer(I4B) :: npolyverts
633  integer(I4B) :: ioffset
634  integer(I4B) :: iv
635 
636  ic = defn%icell
637  npolyverts = defn%npolyverts
638 
639  ! kluge note: hardwired for max 8 polygon faces plus top and bottom for now
640  ioffset = (ic - 1) * 10
641  do iv = 1, npolyverts
642  ! kluge note: should these be additive (seems so)???
643  defn%faceflow(iv) = &
644  defn%faceflow(iv) + &
645  this%fmi%BoundaryFlows(ioffset + iv)
646  end do
647  defn%faceflow(npolyverts + 1) = defn%faceflow(1)
648  defn%faceflow(npolyverts + 2) = &
649  defn%faceflow(npolyverts + 2) + &
650  this%fmi%BoundaryFlows(ioffset + 9)
651  defn%faceflow(npolyverts + 3) = &
652  defn%faceflow(npolyverts + 3) + &
653  this%fmi%BoundaryFlows(ioffset + 10)
654 
655  end subroutine load_boundary_flows_to_defn_poly
656 
657  !> @brief Load 180-degree vertex indicator array and set flags
658  !! indicating how cell can be represented (kluge: latter needed?).
659  !! Assumes cell index and number of vertices are already loaded.
660  subroutine load_flags_to_defn(this, defn) ! kluge note: rename???
661  ! -- dummy
662  class(methoddisvtype), intent(inout) :: this
663  type(celldefntype), pointer, intent(inout) :: defn
664  ! -- local
665  integer(I4B) :: npolyverts
666  integer(I4B) :: m
667  integer(I4B) :: m0
668  integer(I4B) :: m1
669  integer(I4B) :: m2
670  integer(I4B) :: ic
671  integer(I4B) :: num90
672  integer(I4B) :: num180
673  integer(I4B) :: numacute
674  real(DP) :: x0
675  real(DP) :: y0
676  real(DP) :: x1
677  real(DP) :: y1
678  real(DP) :: x2
679  real(DP) :: y2
680  real(DP) :: epsang
681  real(DP) :: epslen
682  real(DP) :: s0x
683  real(DP) :: s0y
684  real(DP) :: &
685  s0mag, s2x, s2y, s2mag, sinang, dotprod
686  logical(LGP) last180
687 
688  ic = defn%icell
689  npolyverts = defn%npolyverts
690 
691  ! -- expand ispv180 array if needed
692  if (size(defn%ispv180) < npolyverts + 3) &
693  call expandarray(defn%ispv180, npolyverts + 1)
694 
695  ! -- Load 180-degree indicator.
696  ! -- Also, set flags that indicate how cell can be represented.
697  defn%ispv180(1:npolyverts + 1) = .false.
698  defn%can_be_rect = .false.
699  defn%can_be_quad = .false.
700  epsang = 1d-3 ! kluge hardwire, and using one value for all angles
701  epslen = 1d-3 ! kluge hardwire
702  num90 = 0
703  num180 = 0
704  numacute = 0
705  last180 = .false.
706  ! kluge note: assumes non-self-intersecting polygon;
707  ! no checks for self-intersection (e.g., star)
708  do m = 1, npolyverts
709  m1 = m
710  if (m1 .eq. 1) then
711  m0 = npolyverts
712  m2 = 2
713  else if (m1 .eq. npolyverts) then
714  m0 = npolyverts - 1
715  m2 = 1
716  else
717  m0 = m1 - 1
718  m2 = m1 + 1
719  end if
720  x0 = defn%polyvert(1, m0)
721  y0 = defn%polyvert(2, m0)
722  x1 = defn%polyvert(1, m1)
723  y1 = defn%polyvert(2, m1)
724  x2 = defn%polyvert(1, m2)
725  y2 = defn%polyvert(2, m2)
726  s0x = x0 - x1
727  s0y = y0 - y1
728  s0mag = dsqrt(s0x * s0x + s0y * s0y)
729  s2x = x2 - x1
730  s2y = y2 - y1
731  s2mag = dsqrt(s2x * s2x + s2y * s2y)
732  sinang = (s0x * s2y - s0y * s2x) / (s0mag * s2mag)
733  ! kluge note: is it better to check in terms of angle rather than sin{angle}???
734  if (dabs(sinang) .lt. epsang) then
735  dotprod = s0x * s2x + s0y * s2y
736  if (dotprod .gt. 0d0) then
737  print *, "Cell ", ic, " has a zero angle" ! kluge
738  print *, " (tolerance epsang = ", epsang, ")"
739  call pstop(1)
740  else
741  if (last180) then
742  print *, "Cell ", ic, &
743  " has consecutive 180-deg angles - not supported" ! kluge
744  print *, " (tolerance epsang = ", epsang, ")"
745  call pstop(1)
746  else if (dabs((s2mag - s0mag) / max(s2mag, s0mag)) .gt. epslen) then
747  print *, "Cell ", ic, &
748  " has a non-bisecting 180-deg vertex - not supported" ! kluge
749  print *, " (tolerance epslen = ", epslen, ")"
750  call pstop(1)
751  end if
752  ! kluge note: want to evaluate 180-deg vertex using one criterion implemented in
753  ! one place (procedure) to avoid potential disparities between multiple checks
754  num180 = num180 + 1
755  last180 = .true.
756  defn%ispv180(m) = .true.
757  end if
758  else if (sinang .gt. 0d0) then
759  numacute = numacute + 1
760  if (dabs(1d0 - sinang) .lt. epsang) num90 = num90 + 1
761  last180 = .false.
762  else
763  print *, "Cell ", ic, &
764  " has an obtuse angle and so is nonconvex" ! kluge
765  print *, " (tolerance epsang = ", epsang, ")"
766  call pstop(1)
767  end if
768  end do
769  if ((num90 .ne. 4) .and. (num180 .ne. 0)) then
770  print *, "Cell ", ic, &
771  " is a non-rectangle with a 180-deg angle - not supported" ! kluge
772  print *, " (tolerance epsang = ", epsang, ")"
773  call pstop(1)
774  end if
775  ! -- List of 180-degree indicators wraps around for convenience
776  defn%ispv180(npolyverts + 1) = defn%ispv180(1)
777  !
778  if (num90 .eq. 4) then
779  if (num180 .eq. 0) then
780  defn%can_be_rect = .true.
781  else
782  defn%can_be_quad = .true.
783  end if
784  end if
785 
786  end subroutine load_flags_to_defn
787 
788 end module methoddisvmodule
subroutine, public create_defn(cellDefn)
Create a new cell definition object.
Definition: CellDefn.f90:42
integer function get_npolyverts(this)
Return the number of polygon vertices.
Definition: CellDefn.f90:53
subroutine, public create_cell_poly(cell)
Create a new polygonal cell.
Definition: CellPoly.f90:20
subroutine, public cell_poly_to_rect(poly, rect)
Convert CellPoly representation to CellRect if possible.
Definition: CellUtil.f90:13
subroutine, public cell_poly_to_quad(poly, quad)
Convert CellPoly representation to CellRectQuad if possible.
Definition: CellUtil.f90:115
This module contains simulation constants.
Definition: Constants.f90:9
real(dp), parameter done
real constant 1
Definition: Constants.f90:75
subroutine pstop(status, message)
Stop the program, optionally specifying an error status code.
Definition: ErrorUtil.f90:24
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:125
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
type(methodcellpollockquadtype), pointer, public method_cell_quad
type(methodcellternarytype), pointer, public method_cell_tern
subroutine, public create_method_disv(method)
Create a new vertex grid (DISV) tracking method.
Definition: MethodDisv.f90:45
subroutine load_boundary_flows_to_defn_rect(this, defn)
Load boundary flows from the grid into a rectangular cell. Assumes cell index and number of vertices ...
Definition: MethodDisv.f90:519
subroutine load_nbrs_to_defn(this, defn)
Loads face neighbors to cell definition from the grid Assumes cell index and number of vertices are a...
Definition: MethodDisv.f90:353
subroutine shared_edgeface(ivlist1, ivlist2, iedgeface)
Find the edge face shared by two cells.
Definition: MethodDisv.f90:437
subroutine map_neighbor(this, defn, inface, z)
Map location on cell face to shared cell face of neighbor.
Definition: MethodDisv.f90:193
subroutine load_boundary_flows_to_defn_rect_quad(this, defn)
Load boundary flows from the grid into rectangular quadcell. Assumes cell index and number of vertice...
Definition: MethodDisv.f90:554
subroutine load_flags_to_defn(this, defn)
Load 180-degree vertex indicator array and set flags indicating how cell can be represented (kluge: l...
Definition: MethodDisv.f90:661
subroutine load_disv(this, particle, next_level, submethod)
Load the cell and the tracking method.
Definition: MethodDisv.f90:68
subroutine load_boundary_flows_to_defn_poly(this, defn)
Load boundary flows from the grid into a polygonal cell. Assumes cell index and number of vertices ar...
Definition: MethodDisv.f90:627
real(dp) function get_top(this, iatop)
Get top elevation based on index iatop kluge note: not needed???
Definition: MethodDisv.f90:294
subroutine load_flows_to_defn(this, defn)
Load flows into the cell definition. These include face flows and net distributed flows....
Definition: MethodDisv.f90:467
subroutine load_cell_defn(this, ic, defn)
Load cell definition from the grid.
Definition: MethodDisv.f90:309
subroutine apply_disv(this, particle, tmax)
Apply the DISV-grid method.
Definition: MethodDisv.f90:263
subroutine pass_disv(this, particle)
Pass a particle to the next cell, if there is one.
Definition: MethodDisv.f90:128
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
Base type for grid cells of a concrete type. Contains a cell-definition which is information shared b...
Definition: Cell.f90:10
Vertex grid discretization.
Definition: Disv.f90:24
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