MODFLOW 6  version 6.9.0.dev0
USGS Modular Hydrologic Model
Disu.f90
Go to the documentation of this file.
1 module disumodule
2 
4  use kindmodule, only: dp, i4b, lgp
14  use basedismodule, only: disbasetype
19  use tdismodule, only: kstp, kper, pertim, totim, delt
20  use disvgeom, only: line_unit_vector
21 
22  implicit none
23 
24  private
25  public :: disutype
26  public :: disu_cr
27  public :: castasdisutype
28 
29  !> @brief Unstructured grid discretization
30  type, extends(disbasetype) :: disutype
31  integer(I4B), pointer :: njausr => null() ! user-specified nja size
32  integer(I4B), pointer :: nvert => null() ! number of x,y vertices
33  integer(I4B), pointer :: nangldegxerr => null() ! number of horizontal connections with an ANGLDEGX value that is inconsistent with the reverse connection or with the vertices
34  real(dp), pointer :: voffsettol => null() ! vertical offset tolerance
35  real(dp), dimension(:, :), pointer, contiguous :: vertices => null() ! cell vertices stored as 2d array of x and y
36  real(dp), dimension(:, :), pointer, contiguous :: cellxy => null() ! cell center stored as 2d array of x and y
37  real(dp), dimension(:), pointer, contiguous :: top1d => null() ! (size:nodesuser) cell top elevation
38  real(dp), dimension(:), pointer, contiguous :: bot1d => null() ! (size:nodesuser) cell bottom elevation
39  real(dp), dimension(:), pointer, contiguous :: area1d => null() ! (size:nodesuser) cell area, in plan view
40  integer(I4B), dimension(:), pointer, contiguous :: iainp => null() ! (size:nodesuser+1) user iac converted ia
41  integer(I4B), dimension(:), pointer, contiguous :: jainp => null() ! (size:njausr) user-input ja array
42  integer(I4B), dimension(:), pointer, contiguous :: ihcinp => null() ! (size:njausr) user-input ihc array
43  real(dp), dimension(:), pointer, contiguous :: cl12inp => null() ! (size:njausr) user-input cl12 array
44  real(dp), dimension(:), pointer, contiguous :: hwvainp => null() ! (size:njausr) user-input hwva array
45  real(dp), dimension(:), pointer, contiguous :: angldegxinp => null() ! (size:njausr) user-input angldegx array
46  integer(I4B), pointer :: iangledegx => null() ! =1 when angle information was present in input, 0 otherwise
47  integer(I4B), dimension(:), pointer, contiguous :: iavert => null() ! cell vertex pointer ia array
48  integer(I4B), dimension(:), pointer, contiguous :: javert => null() ! cell vertex pointer ja array
49  integer(I4B), dimension(:), pointer, contiguous :: idomain => null() ! idomain (nodes)
50  logical(LGP) :: readfromfile ! True, when DIS is read from file (almost always)
51 
52  contains
53 
54  procedure :: dis_df => disu_df
55  procedure :: disu_load
56  procedure :: dis_da => disu_da
57  procedure :: get_dis_type => get_dis_type
58  procedure :: get_dis_enum => get_dis_enum
59  procedure :: disu_ck
60  procedure :: disu_write_warning
61  procedure :: disu_face_normal
62  procedure :: grid_finalize
63  procedure :: get_nodenumber_idx1
64  procedure :: nodeu_to_string
65  procedure :: nodeu_to_array
66  procedure :: nodeu_from_string
67  procedure :: nodeu_from_cellid
68  procedure :: connection_normal
69  procedure :: connection_vector
70  procedure :: supports_layers
71  procedure :: get_ncpl
72  procedure :: get_polyverts
73  procedure :: get_npolyverts
74  procedure :: get_max_npolyverts
75  procedure, public :: record_array
76  procedure, public :: record_srcdst_list_header
77  ! -- private
78  procedure :: allocate_scalars
79  procedure :: allocate_arrays
80  procedure :: allocate_arrays_mem
81  procedure :: source_options
82  procedure :: source_dimensions
83  procedure :: source_griddata
84  procedure :: source_connectivity
85  procedure :: source_vertices
86  procedure :: source_cell2d
87  procedure :: log_options
88  procedure :: log_dimensions
89  procedure :: log_griddata
90  procedure :: log_connectivity
91  procedure :: define_cellverts
92  procedure :: write_grb
93  !
94  ! -- Read a node-sized model array (reduced or not)
95  procedure :: read_int_array
96  procedure :: read_dbl_array
97 
98  end type disutype
99 
101  logical :: length_units = .false.
102  logical :: nogrb = .false.
103  logical :: xorigin = .false.
104  logical :: yorigin = .false.
105  logical :: angrot = .false.
106  logical :: voffsettol = .false.
107  logical :: nodes = .false.
108  logical :: nja = .false.
109  logical :: nvert = .false.
110  logical :: top = .false.
111  logical :: bot = .false.
112  logical :: area = .false.
113  logical :: idomain = .false.
114  logical :: iac = .false.
115  logical :: ja = .false.
116  logical :: ihc = .false.
117  logical :: cl12 = .false.
118  logical :: hwva = .false.
119  logical :: angldegx = .false.
120  logical :: iv = .false.
121  logical :: xv = .false.
122  logical :: yv = .false.
123  logical :: icell2d = .false.
124  logical :: xc = .false.
125  logical :: yc = .false.
126  logical :: ncvert = .false.
127  logical :: icvert = .false.
128  end type disufoundtype
129 
130 contains
131 
132  !> @brief Create a new unstructured discretization object
133  !<
134  subroutine disu_cr(dis, name_model, input_mempath, inunit, iout)
135  ! -- dummy
136  class(disbasetype), pointer :: dis
137  character(len=*), intent(in) :: name_model
138  character(len=*), intent(in) :: input_mempath
139  integer(I4B), intent(in) :: inunit
140  integer(I4B), intent(in) :: iout
141  ! -- local
142  type(disutype), pointer :: disnew
143  character(len=*), parameter :: fmtheader = &
144  "(1X, /1X, 'DISU -- UNSTRUCTURED GRID DISCRETIZATION PACKAGE,', &
145  &' VERSION 2 : 3/27/2014 - INPUT READ FROM MEMPATH: ', A, //)"
146  !
147  ! -- Create a new discretization object
148  allocate (disnew)
149  dis => disnew
150  !
151  ! -- Allocate scalars and assign data
152  call dis%allocate_scalars(name_model, input_mempath)
153  dis%inunit = inunit
154  dis%iout = iout
155  !
156  ! -- If disu is enabled
157  if (inunit > 0) then
158  !
159  ! -- Identify package
160  if (iout > 0) then
161  write (iout, fmtheader) dis%input_mempath
162  end if
163  !
164  ! -- load disu
165  call disnew%disu_load()
166  end if
167  !
168  end subroutine disu_cr
169 
170  !> @brief Transfer IDM data into this discretization object
171  !<
172  subroutine disu_load(this)
173  ! -- dummy
174  class(disutype) :: this
175  !
176  ! -- source input data
177  call this%source_options()
178  call this%source_dimensions()
179  call this%source_griddata()
180  call this%source_connectivity()
181  !
182  ! -- If NVERT specified and greater than 0, then source VERTICES and CELL2D
183  if (this%nvert > 0) then
184  call this%source_vertices()
185  call this%source_cell2d()
186  else
187  ! -- connection direction information cannot be calculated
188  this%icondir = 0
189  end if
190  !
191  ! -- Make some final disu checks on the non-reduced user-provided
192  ! input
193  call this%disu_ck()
194  !
195  end subroutine disu_load
196 
197  !> @brief Define the discretization
198  !<
199  subroutine disu_df(this)
200  ! -- dummy
201  class(disutype) :: this
202  !
203  call this%grid_finalize()
204  !
205  end subroutine disu_df
206 
207  !> @brief Finalize the grid
208  !<
209  subroutine grid_finalize(this)
210  ! -- dummy
211  class(disutype) :: this
212  ! -- locals
213  integer(I4B) :: n
214  integer(I4B) :: node
215  integer(I4B) :: noder
216  integer(I4B) :: nrsize
217  ! -- formats
218  character(len=*), parameter :: fmtdz = &
219  "('CELL (',i0,',',i0,',',i0,') THICKNESS <= 0. ', &
220  &'TOP, BOT: ',2(1pg24.15))"
221  character(len=*), parameter :: fmtnr = &
222  "(/1x, 'The specified IDOMAIN results in a reduced number of cells.',&
223  &/1x, 'Number of user nodes: ',I0,&
224  &/1X, 'Number of nodes in solution: ', I0, //)"
225  !
226  ! -- count active cells
227  this%nodes = 0
228  do n = 1, this%nodesuser
229  if (this%idomain(n) > 0) this%nodes = this%nodes + 1
230  end do
231  !
232  ! -- Check to make sure nodes is a valid number
233  if (this%nodes == 0) then
234  call store_error('Model does not have any active nodes. &
235  &Ensure IDOMAIN array has some values greater &
236  &than zero.')
237  call store_error_filename(this%input_fname)
238  end if
239  !
240  ! -- Write message if reduced grid
241  if (this%nodes < this%nodesuser) then
242  write (this%iout, fmtnr) this%nodesuser, this%nodes
243  end if
244  !
245  ! -- Array size is now known, so allocate
246  call this%allocate_arrays()
247  !
248  ! -- Fill the nodereduced array with the reduced nodenumber, or
249  ! a negative number to indicate it is a pass-through cell, or
250  ! a zero to indicate that the cell is excluded from the
251  ! solution. (negative idomain not supported for disu)
252  if (this%nodes < this%nodesuser) then
253  noder = 1
254  do node = 1, this%nodesuser
255  if (this%idomain(node) > 0) then
256  this%nodereduced(node) = noder
257  noder = noder + 1
258  elseif (this%idomain(node) < 0) then
259  this%nodereduced(node) = -1
260  else
261  this%nodereduced(node) = 0
262  end if
263  end do
264  end if
265  !
266  ! -- Fill nodeuser if a reduced grid
267  if (this%nodes < this%nodesuser) then
268  noder = 1
269  do node = 1, this%nodesuser
270  if (this%idomain(node) > 0) then
271  this%nodeuser(noder) = node
272  noder = noder + 1
273  end if
274  end do
275  end if
276  !
277  ! -- Move top1d, bot1d, and area1d into top, bot, and area
278  do node = 1, this%nodesuser
279  noder = node
280  if (this%nodes < this%nodesuser) noder = this%nodereduced(node)
281  if (noder <= 0) cycle
282  this%top(noder) = this%top1d(node)
283  this%bot(noder) = this%bot1d(node)
284  this%area(noder) = this%area1d(node)
285  end do
286  !
287  ! -- fill cell center coordinates
288  if (this%nvert > 0) then
289  do node = 1, this%nodesuser
290  noder = node
291  if (this%nodes < this%nodesuser) noder = this%nodereduced(node)
292  if (noder <= 0) cycle
293  this%xc(noder) = this%cellxy(1, node)
294  this%yc(noder) = this%cellxy(2, node)
295  end do
296  else
297  call mem_reallocate(this%xc, 0, 'XC', this%memoryPath)
298  call mem_reallocate(this%yc, 0, 'YC', this%memoryPath)
299  end if
300  !
301  ! -- create and fill the connections object
302  nrsize = 0
303  if (this%nodes < this%nodesuser) nrsize = this%nodes
304  allocate (this%con)
305  call this%con%disuconnections(this%name_model, this%nodes, &
306  this%nodesuser, nrsize, &
307  this%nodereduced, this%nodeuser, &
308  this%iainp, this%jainp, &
309  this%ihcinp, this%cl12inp, &
310  this%hwvainp, this%angldegxinp, &
311  this%iangledegx)
312  this%nja = this%con%nja
313  this%njas = this%con%njas
314  !
315  end subroutine grid_finalize
316 
317  !> @brief Check discretization info
318  !<
319  subroutine disu_ck(this)
320  ! -- dummy
321  class(disutype) :: this
322  ! -- local
323  integer(I4B) :: n, m
324  integer(I4B) :: ipos, jpos, kpos
325  integer(I4B) :: ihc
326  integer(I4B) :: nsym, ndir, nvrt, nwarn
327  logical :: found
328  real(DP) :: dz
329  real(DP) :: angn, angm, dang
330  real(DP) :: dx, dy, dist, cosang, angc
331  real(DP) :: xnorm, ynorm, angv
332  real(DP) :: angn_sym, angm_sym
333  real(DP) :: angn_dir, angc_dir
334  real(DP) :: angn_vrt, angv_vrt
335  real(DP) :: angn_warn, angc_warn
336  integer(I4B) :: n_sym, m_sym
337  integer(I4B) :: n_dir, m_dir
338  integer(I4B) :: n_vrt, m_vrt
339  integer(I4B) :: n_warn, m_warn
340  character(len=LENBIGLINE) :: bigmsg
341  real(DP), parameter :: angtol = 0.01_dp !< tolerance (degrees) for the 180-degree reciprocity check
342  real(DP), parameter :: angvtol = 1.0_dp !< tolerance (degrees) for the comparison with the normal computed from the vertices
343  real(DP), parameter :: cos45 = 0.70710678118654752_dp
344  ! -- formats
345  character(len=*), parameter :: fmtidm = &
346  &"('Invalid idomain value ', i0, ' specified for node ', i0)"
347  character(len=*), parameter :: fmtangsym = &
348  "('ANGLDEGX values for ', i0, ' cell faces associated with &
349  &horizontal connections in the DISU Package are inconsistent with &
350  &the values for the reverse connections (for example, ANGLDEGX = ', &
351  &f0.3, ' for the connection from cell ', i0, ' to cell ', i0, &
352  &' and ANGLDEGX = ', f0.3, ' for the connection from cell ', i0, &
353  &' to cell ', i0, '). The two values must differ by 180 degrees &
354  &(within 0.01 degrees) because they are the outward normals of the &
355  &two sides of the same cell face. Only the value for the connection &
356  &from the lower to the higher cell number is used.')"
357  character(len=*), parameter :: fmtangdir = &
358  "('ANGLDEGX values for ', i0, ' cell faces associated with &
359  &horizontal connections in the DISU Package point away from the &
360  &connected cell (for example, ANGLDEGX = ', f0.3, ' for the &
361  &connection from cell ', i0, ' to cell ', i0, ', but the direction &
362  &from the center of cell ', i0, ' to the center of cell ', i0, &
363  &' is ', f0.3, ' degrees). The centers of two connected cells must &
364  &lie on opposite sides of their shared face, so ANGLDEGX, the &
365  &outward normal of the face, must be within 90 degrees of the &
366  &direction from the cell center to the center of the connected &
367  &cell, in the coordinate system of the VERTICES.')"
368  character(len=*), parameter :: fmtangvrt = &
369  "('ANGLDEGX values for ', i0, ' cell faces associated with &
370  &horizontal connections in the DISU Package differ by more than 1 &
371  &degree from the outward normal of the face computed from the two &
372  &VERTICES shared by the connected cells (for example, ANGLDEGX = ', &
373  &f0.3, ' for the connection from cell ', i0, ' to cell ', i0, &
374  &', but the normal computed from the vertices is ', f0.3, &
375  &' degrees). ANGLDEGX must be expressed in the coordinate system of &
376  &the VERTICES.')"
377  character(len=*), parameter :: fmtangwarn = &
378  "('ANGLDEGX values for ', i0, ' cell faces associated with &
379  &horizontal connections in the DISU Package deviate by more than 45 &
380  &degrees from the direction between the centers of the connected &
381  &cells (for example, ANGLDEGX = ', f0.3, ' for the connection from &
382  &cell ', i0, ' to cell ', i0, ', but the direction between the cell &
383  &centers is ', f0.3, ' degrees). This is not necessarily an error, &
384  &but such connections depart substantially from the assumptions of &
385  &the control-volume finite-difference method and may warrant a &
386  &closer look at the grid.')"
387  character(len=*), parameter :: fmtdz = &
388  &"('Cell ', i0, ' with thickness <= 0. Top, bot: ', 2(1pg24.15))"
389  character(len=*), parameter :: fmtarea = &
390  &"('Cell ', i0, ' with area <= 0. Area: ', 1(1pg24.15))"
391  character(len=*), parameter :: fmtjan = &
392  &"('Cell ', i0, ' must have its first connection be itself. Found: ', i0)"
393  character(len=*), parameter :: fmtjam = &
394  &"('Cell ', i0, ' has invalid connection in JA. Found: ', i0)"
395  character(len=*), parameter :: fmterrmsg = &
396  "('Top elevation (', 1pg15.6, ') for cell ', i0, ' is above bottom &
397  &elevation (', 1pg15.6, ') for cell ', i0, '. Based on node numbering &
398  &rules cell ', i0, ' must be below cell ', i0, '.')"
399  !
400  ! -- Check connectivity
401  do n = 1, this%nodesuser
402  !
403  ! -- Ensure first connection is to itself, and
404  ! that ja(ia(n)) is positive
405  ipos = this%iainp(n)
406  m = this%jainp(ipos)
407  if (m < 0) then
408  m = abs(m)
409  this%jainp(ipos) = m
410  end if
411  if (n /= m) then
412  write (errmsg, fmtjan) n, m
413  call store_error(errmsg)
414  end if
415  !
416  ! -- Check for valid node numbers in connected cells
417  do ipos = this%iainp(n) + 1, this%iainp(n + 1) - 1
418  m = this%jainp(ipos)
419  if (m < 0 .or. m > this%nodesuser) then
420  ! -- make sure first connection is to itself
421  write (errmsg, fmtjam) n, m
422  call store_error(errmsg)
423  end if
424  end do
425  end do
426  !
427  ! -- terminate if errors found
428  if (count_errors() > 0) then
429  if (this%inunit > 0) then
430  call store_error_filename(this%input_fname)
431  end if
432  end if
433  !
434  ! -- Ensure idomain values are valid
435  do n = 1, this%nodesuser
436  if (this%idomain(n) > 1 .or. this%idomain(n) < 0) then
437  write (errmsg, fmtidm) this%idomain(n), n
438  call store_error(errmsg)
439  end if
440  end do
441  !
442  ! -- Check for zero and negative thickness and zero or negative areas
443  ! for cells with idomain == 1
444  do n = 1, this%nodesuser
445  if (this%idomain(n) == 1) then
446  dz = this%top1d(n) - this%bot1d(n)
447  if (dz <= dzero) then
448  write (errmsg, fmt=fmtdz) n, this%top1d(n), this%bot1d(n)
449  call store_error(errmsg)
450  end if
451  if (this%area1d(n) <= dzero) then
452  write (errmsg, fmt=fmtarea) n, this%area1d(n)
453  call store_error(errmsg)
454  end if
455  end if
456  end do
457  !
458  ! -- check to make sure voffsettol is >= 0
459  if (this%voffsettol < dzero) then
460  write (errmsg, '(a, 1pg15.6)') &
461  'Vertical offset tolerance must be greater than zero. Found ', &
462  this%voffsettol
463  call store_error(errmsg)
464  if (this%inunit > 0) then
465  call store_error_filename(this%input_fname)
466  end if
467  end if
468  !
469  ! -- For cell n, ensure that underlying cells have tops less than
470  ! or equal to the bottom of cell n
471  do n = 1, this%nodesuser
472  do ipos = this%iainp(n) + 1, this%iainp(n + 1) - 1
473  m = this%jainp(ipos)
474  ihc = this%ihcinp(ipos)
475  if (ihc == 0 .and. m > n) then
476  dz = this%top1d(m) - this%bot1d(n)
477  if (dz > this%voffsettol) then
478  write (errmsg, fmterrmsg) this%top1d(m), m, this%bot1d(n), n, m, n
479  call store_error(errmsg)
480  end if
481  end if
482  end do
483  end do
484  !
485  ! -- Check ANGLDEGX for horizontal connections between active cells.
486  ! ANGLDEGX is the outward normal of the shared face, so the values
487  ! for a connection and its reverse connection must differ by 180
488  ! degrees. If VERTICES and CELL2D are available, the normal must
489  ! also point into the connected cell (the centers of the two cells
490  ! lie on opposite sides of the shared face) and must agree with the
491  ! normal computed from the two vertices shared by the cells. These
492  ! three conditions are reported as warnings here and counted in
493  ! nangldegxerr; the NPF Package terminates with an error if the count
494  ! is nonzero and ANGLDEGX is required input (XT3D, K22, or
495  ! SAVE_SPECIFIC_DISCHARGE), because ANGLDEGX has no effect otherwise.
496  ! A fourth check flags normals that deviate by more than 45 degrees
497  ! from the direction between the cell centers. That is not an error,
498  ! but such connections depart from the assumptions of the CVFD
499  ! method and are reported as a warning only.
500  if (this%iangledegx == 1) then
501  nsym = 0
502  ndir = 0
503  nvrt = 0
504  nwarn = 0
505  n_sym = 0
506  m_sym = 0
507  n_dir = 0
508  m_dir = 0
509  n_vrt = 0
510  m_vrt = 0
511  n_warn = 0
512  m_warn = 0
513  angn_sym = dzero
514  angm_sym = dzero
515  angn_dir = dzero
516  angc_dir = dzero
517  angn_vrt = dzero
518  angv_vrt = dzero
519  angn_warn = dzero
520  angc_warn = dzero
521  do n = 1, this%nodesuser
522  if (this%idomain(n) == 0) cycle
523  do ipos = this%iainp(n) + 1, this%iainp(n + 1) - 1
524  m = this%jainp(ipos)
525  if (m < 1 .or. m > this%nodesuser) cycle
526  if (this%ihcinp(ipos) == 0) cycle
527  if (this%idomain(m) == 0) cycle
528  angn = this%angldegxinp(ipos)
529  !
530  ! -- the reverse connection must have the opposite normal;
531  ! check each pair once
532  if (m > n) then
533  jpos = 0
534  do kpos = this%iainp(m) + 1, this%iainp(m + 1) - 1
535  if (this%jainp(kpos) == n) then
536  jpos = kpos
537  exit
538  end if
539  end do
540  if (jpos > 0) then
541  angm = this%angldegxinp(jpos)
542  dang = modulo(angm - angn, 360.0_dp)
543  if (abs(dang - 180.0_dp) > angtol) then
544  nsym = nsym + 1
545  if (nsym == 1) then
546  n_sym = n
547  m_sym = m
548  angn_sym = angn
549  angm_sym = angm
550  end if
551  end if
552  end if
553  end if
554  !
555  ! -- the normal must point from cell n toward cell m
556  if (this%nvert > 0) then
557  dx = this%cellxy(1, m) - this%cellxy(1, n)
558  dy = this%cellxy(2, m) - this%cellxy(2, n)
559  dist = sqrt(dx * dx + dy * dy)
560  if (dist > dzero) then
561  cosang = (cos(angn * dpio180) * dx + &
562  sin(angn * dpio180) * dy) / dist
563  angc = modulo(atan2(dy, dx) / dpio180, 360.0_dp)
564  if (cosang <= dzero) then
565  ndir = ndir + 1
566  if (ndir == 1) then
567  n_dir = n
568  m_dir = m
569  angn_dir = angn
570  angc_dir = angc
571  end if
572  else if (cosang < cos45) then
573  nwarn = nwarn + 1
574  if (nwarn == 1) then
575  angn_warn = angn
576  angc_warn = angc
577  n_warn = n
578  m_warn = m
579  end if
580  end if
581  end if
582  !
583  ! -- the normal must agree with the normal of the face computed
584  ! from the vertices; check each pair once, and skip pairs
585  ! that do not share exactly two vertices
586  if (m > n) then
587  call this%disu_face_normal(n, m, found, xnorm, ynorm)
588  if (found) then
589  angv = modulo(atan2(ynorm, xnorm) / dpio180, 360.0_dp)
590  dang = abs(modulo(angn - angv + 180.0_dp, 360.0_dp) - 180.0_dp)
591  if (dang > angvtol) then
592  nvrt = nvrt + 1
593  if (nvrt == 1) then
594  n_vrt = n
595  m_vrt = m
596  angn_vrt = angn
597  angv_vrt = angv
598  end if
599  end if
600  end if
601  end if
602  end if
603  end do
604  end do
605  this%nangldegxerr = nsym + ndir + nvrt
606  !
607  ! -- store warnings for the final report and also write them to the
608  ! model listing file now, so that they are available if the run
609  ! later terminates abnormally because of an invalid matrix
610  if (nsym > 0) then
611  write (bigmsg, fmtangsym) nsym, angn_sym, n_sym, m_sym, &
612  angm_sym, m_sym, n_sym
613  call store_warning(bigmsg)
614  call this%disu_write_warning(bigmsg)
615  end if
616  if (ndir > 0) then
617  write (bigmsg, fmtangdir) ndir, angn_dir, n_dir, m_dir, &
618  n_dir, m_dir, angc_dir
619  call store_warning(bigmsg)
620  call this%disu_write_warning(bigmsg)
621  end if
622  if (nvrt > 0) then
623  write (bigmsg, fmtangvrt) nvrt, angn_vrt, n_vrt, m_vrt, angv_vrt
624  call store_warning(bigmsg)
625  call this%disu_write_warning(bigmsg)
626  end if
627  if (nwarn > 0) then
628  write (bigmsg, fmtangwarn) nwarn, angn_warn, n_warn, m_warn, angc_warn
629  call store_warning(bigmsg)
630  call this%disu_write_warning(bigmsg)
631  end if
632  end if
633  !
634  ! -- terminate if errors found
635  if (count_errors() > 0) then
636  if (this%inunit > 0) then
637  call store_error_filename(this%input_fname)
638  end if
639  end if
640  !
641  end subroutine disu_ck
642 
643  !> @brief Write a warning message to the model listing file
644  !<
645  subroutine disu_write_warning(this, msg)
646  ! -- dummy
647  class(disutype) :: this
648  character(len=*), intent(in) :: msg
649  !
650  if (this%iout > 0) then
651  call write_message_counter('WARNING: '//trim(msg), iunit=this%iout, &
652  skipbefore=1, skipafter=1)
653  end if
654  end subroutine disu_write_warning
655 
656  !> @brief Outward normal of the face shared by two cells from the vertices
657  !!
658  !! Finds the vertices shared by cells n and m. Vertices are matched by
659  !! coordinates rather than by vertex number, so that grids that list a
660  !! separate set of vertices for every cell are handled the same as grids
661  !! with shared vertex numbers. If exactly two vertices are shared, they
662  !! define the shared face, and the unit normal to that face that points
663  !! from cell n toward cell m is returned with found set to true.
664  !! Otherwise found is set to false. Requires VERTICES and CELL2D.
665  !<
666  subroutine disu_face_normal(this, n, m, found, xnorm, ynorm)
667  ! -- dummy
668  class(disutype) :: this
669  integer(I4B), intent(in) :: n !< cell (user node number)
670  integer(I4B), intent(in) :: m !< connected cell (user node number)
671  logical, intent(out) :: found !< true if the cells share exactly two vertices
672  real(DP), intent(out) :: xnorm !< x component of the unit normal from n toward m
673  real(DP), intent(out) :: ynorm !< y component of the unit normal from n toward m
674  ! -- local
675  integer(I4B) :: i, j, iv, jv, nshared
676  integer(I4B), dimension(2) :: ivshared
677  real(DP) :: ex, ey, elen, dx, dy, tol
678  !
679  found = .false.
680  xnorm = dzero
681  ynorm = dzero
682  nshared = 0
683  !
684  ! -- Vertices are shared if their coordinates agree to within a small
685  ! fraction of the distance between the two cell centers
686  dx = this%cellxy(1, m) - this%cellxy(1, n)
687  dy = this%cellxy(2, m) - this%cellxy(2, n)
688  tol = dem6 * sqrt(dx * dx + dy * dy)
689  !
690  ! -- Count the distinct vertices of cell n that also belong to cell m
691  ! (the vertex list of a cell repeats its first vertex at the end)
692  do i = this%iavert(n), this%iavert(n + 1) - 1
693  iv = this%javert(i)
694  if (any(this%javert(this%iavert(n):i - 1) == iv)) cycle
695  do j = this%iavert(m), this%iavert(m + 1) - 1
696  jv = this%javert(j)
697  if (abs(this%vertices(1, jv) - this%vertices(1, iv)) <= tol .and. &
698  abs(this%vertices(2, jv) - this%vertices(2, iv)) <= tol) then
699  nshared = nshared + 1
700  if (nshared > 2) return
701  ivshared(nshared) = iv
702  exit
703  end if
704  end do
705  end do
706  if (nshared /= 2) return
707  !
708  ! -- Unit normal to the shared face, oriented from cell n toward cell m
709  ex = this%vertices(1, ivshared(2)) - this%vertices(1, ivshared(1))
710  ey = this%vertices(2, ivshared(2)) - this%vertices(2, ivshared(1))
711  elen = sqrt(ex * ex + ey * ey)
712  if (elen <= dzero) return
713  xnorm = ey / elen
714  ynorm = -ex / elen
715  if (xnorm * dx + ynorm * dy < dzero) then
716  xnorm = -xnorm
717  ynorm = -ynorm
718  end if
719  found = .true.
720  end subroutine disu_face_normal
721 
722  !> @brief Deallocate variables
723  !<
724  subroutine disu_da(this)
725  ! -- dummy
726  class(disutype) :: this
727  !
728  ! -- Deallocate idm memory
729  call memorystore_remove(this%name_model, 'DISU', idm_context)
730  call memorystore_remove(component=this%name_model, &
731  context=idm_context)
732  !
733  ! -- scalars
734  call mem_deallocate(this%njausr)
735  call mem_deallocate(this%nvert)
736  call mem_deallocate(this%nangldegxerr)
737  call mem_deallocate(this%voffsettol)
738  call mem_deallocate(this%iangledegx)
739  !
740  ! -- arrays
741  if (this%readFromFile) then
742  call mem_deallocate(this%top1d)
743  call mem_deallocate(this%bot1d)
744  call mem_deallocate(this%area1d)
745  if (associated(this%iavert)) then
746  call mem_deallocate(this%iavert)
747  call mem_deallocate(this%javert)
748  end if
749  call mem_deallocate(this%vertices)
750  call mem_deallocate(this%iainp)
751  call mem_deallocate(this%jainp)
752  call mem_deallocate(this%ihcinp)
753  call mem_deallocate(this%cl12inp)
754  call mem_deallocate(this%hwvainp)
755  call mem_deallocate(this%angldegxinp)
756  end if
757  !
758  call mem_deallocate(this%idomain)
759  call mem_deallocate(this%cellxy)
760  !
761  call mem_deallocate(this%nodeuser)
762  call mem_deallocate(this%nodereduced)
763  !
764  ! -- DisBaseType deallocate
765  call this%DisBaseType%dis_da()
766  !
767  end subroutine disu_da
768 
769  !> @brief Convert a user nodenumber to a string (nodenumber)
770  !<
771  subroutine nodeu_to_string(this, nodeu, str)
772  ! -- dummy
773  class(disutype) :: this
774  integer(I4B), intent(in) :: nodeu
775  character(len=*), intent(inout) :: str
776  ! -- local
777  character(len=10) :: nstr
778  !
779  write (nstr, '(i0)') nodeu
780  str = '('//trim(adjustl(nstr))//')'
781  !
782  end subroutine nodeu_to_string
783 
784  !> @brief Convert a user nodenumber to an array (nodenumber)
785  !<
786  subroutine nodeu_to_array(this, nodeu, arr)
787  class(disutype) :: this
788  integer(I4B), intent(in) :: nodeu
789  integer(I4B), dimension(:), intent(inout) :: arr
790  ! -- local
791  integer(I4B) :: isize
792  !
793  ! -- check the size of arr
794  isize = size(arr)
795  if (isize /= this%ndim) then
796  write (errmsg, '(a,i0,a,i0,a)') &
797  'Program error: nodeu_to_array size of array (', isize, &
798  ') is not equal to the discretization dimension (', this%ndim, ')'
799  call store_error(errmsg, terminate=.true.)
800  end if
801  !
802  ! -- fill array
803  arr(1) = nodeu
804  !
805  end subroutine nodeu_to_array
806 
807  !> @brief Copy options from IDM into package
808  !<
809  subroutine source_options(this)
810  ! -- dummy
811  class(disutype) :: this
812  ! -- locals
813  character(len=LENVARNAME), dimension(3) :: lenunits = &
814  &[character(len=LENVARNAME) :: 'FEET', 'METERS', 'CENTIMETERS']
815  type(disufoundtype) :: found
816  !
817  ! -- update defaults with idm sourced values
818  call mem_set_value(this%lenuni, 'LENGTH_UNITS', this%input_mempath, &
819  lenunits, found%length_units)
820  call mem_set_value(this%nogrb, 'NOGRB', this%input_mempath, found%nogrb)
821  call mem_set_value(this%xorigin, 'XORIGIN', this%input_mempath, found%xorigin)
822  call mem_set_value(this%yorigin, 'YORIGIN', this%input_mempath, found%yorigin)
823  call mem_set_value(this%angrot, 'ANGROT', this%input_mempath, found%angrot)
824  call mem_set_value(this%voffsettol, 'VOFFSETTOL', this%input_mempath, &
825  found%voffsettol)
826  !
827  ! -- log values to list file
828  if (this%iout > 0) then
829  call this%log_options(found)
830  end if
831  !
832  end subroutine source_options
833 
834  !> @brief Write user options to list file
835  !<
836  subroutine log_options(this, found)
837  ! -- dummy
838  class(disutype) :: this
839  type(disufoundtype), intent(in) :: found
840  !
841  write (this%iout, '(1x,a)') 'Setting Discretization Options'
842  !
843  if (found%length_units) then
844  write (this%iout, '(4x,a,i0)') 'Model length unit [0=UND, 1=FEET, &
845  &2=METERS, 3=CENTIMETERS] set as ', this%lenuni
846  end if
847  !
848  if (found%nogrb) then
849  write (this%iout, '(4x,a,i0)') 'Binary grid file [0=GRB, 1=NOGRB] &
850  &set as ', this%nogrb
851  end if
852  !
853  if (found%xorigin) then
854  write (this%iout, '(4x,a,G0)') 'XORIGIN = ', this%xorigin
855  end if
856  !
857  if (found%yorigin) then
858  write (this%iout, '(4x,a,G0)') 'YORIGIN = ', this%yorigin
859  end if
860  !
861  if (found%angrot) then
862  write (this%iout, '(4x,a,G0)') 'ANGROT = ', this%angrot
863  end if
864  !
865  if (found%voffsettol) then
866  write (this%iout, '(4x,a,G0)') 'VERTICAL_OFFSET_TOLERANCE = ', &
867  this%voffsettol
868  end if
869  !
870  write (this%iout, '(1x,a,/)') 'End Setting Discretization Options'
871  !
872  end subroutine log_options
873 
874  !> @brief Copy dimensions from IDM into package
875  !<
876  subroutine source_dimensions(this)
877  ! -- dummy
878  class(disutype) :: this
879  ! -- locals
880  integer(I4B) :: n
881  type(disufoundtype) :: found
882  !
883  ! -- update defaults with idm sourced values
884  call mem_set_value(this%nodesuser, 'NODES', this%input_mempath, found%nodes)
885  call mem_set_value(this%njausr, 'NJA', this%input_mempath, found%nja)
886  call mem_set_value(this%nvert, 'NVERT', this%input_mempath, found%nvert)
887  !
888  ! -- log simulation values
889  if (this%iout > 0) then
890  call this%log_dimensions(found)
891  end if
892  !
893  ! -- verify dimensions were set
894  if (this%nodesuser < 1) then
895  call store_error( &
896  'NODES was not specified or was specified incorrectly.')
897  end if
898  if (this%njausr < 1) then
899  call store_error( &
900  'NJA was not specified or was specified incorrectly.')
901  end if
902  !
903  ! -- terminate if errors were detected
904  if (count_errors() > 0) then
905  call store_error_filename(this%input_fname)
906  end if
907  !
908  ! -- allocate vectors that are the size of nodesuser
909  this%readFromFile = .true.
910  call mem_allocate(this%top1d, this%nodesuser, 'TOP1D', this%memoryPath)
911  call mem_allocate(this%bot1d, this%nodesuser, 'BOT1D', this%memoryPath)
912  call mem_allocate(this%area1d, this%nodesuser, 'AREA1D', this%memoryPath)
913  call mem_allocate(this%idomain, this%nodesuser, 'IDOMAIN', this%memoryPath)
914  call mem_allocate(this%vertices, 2, this%nvert, 'VERTICES', this%memoryPath)
915  call mem_allocate(this%iainp, this%nodesuser + 1, 'IAINP', this%memoryPath)
916  call mem_allocate(this%jainp, this%njausr, 'JAINP', this%memoryPath)
917  call mem_allocate(this%ihcinp, this%njausr, 'IHCINP', this%memoryPath)
918  call mem_allocate(this%cl12inp, this%njausr, 'CL12INP', this%memoryPath)
919  call mem_allocate(this%hwvainp, this%njausr, 'HWVAINP', this%memoryPath)
920  call mem_allocate(this%angldegxinp, this%njausr, 'ANGLDEGXINP', &
921  this%memoryPath)
922  if (this%nvert > 0) then
923  call mem_allocate(this%cellxy, 2, this%nodesuser, 'CELLXY', this%memoryPath)
924  else
925  call mem_allocate(this%cellxy, 2, 0, 'CELLXY', this%memoryPath)
926  end if
927  !
928  ! -- initialize all cells to be active (idomain = 1)
929  do n = 1, this%nodesuser
930  this%idomain(n) = 1
931  end do
932  !
933  end subroutine source_dimensions
934 
935  !> @brief Write dimensions to list file
936  !<
937  subroutine log_dimensions(this, found)
938  class(disutype) :: this
939  type(disufoundtype), intent(in) :: found
940  !
941  write (this%iout, '(1x,a)') 'Setting Discretization Dimensions'
942  !
943  if (found%nodes) then
944  write (this%iout, '(4x,a,i0)') 'NODES = ', this%nodesuser
945  end if
946  !
947  if (found%nja) then
948  write (this%iout, '(4x,a,i0)') 'NJA = ', this%njausr
949  end if
950  !
951  if (found%nvert) then
952  write (this%iout, '(4x,a,i0)') 'NVERT = ', this%nvert
953  end if
954  !
955  write (this%iout, '(1x,a,/)') 'End Setting Discretization Dimensions'
956  !
957  end subroutine log_dimensions
958 
959  !> @brief Copy grid data from IDM into package
960  !<
961  subroutine source_griddata(this)
962  ! -- dummy
963  class(disutype) :: this
964  ! -- locals
965  type(disufoundtype) :: found
966  !
967  ! -- update defaults with idm sourced values
968  call mem_set_value(this%top1d, 'TOP', this%input_mempath, found%top)
969  call mem_set_value(this%bot1d, 'BOT', this%input_mempath, found%bot)
970  call mem_set_value(this%area1d, 'AREA', this%input_mempath, found%area)
971  call mem_set_value(this%idomain, 'IDOMAIN', this%input_mempath, found%idomain)
972  !
973  ! -- log simulation values
974  if (this%iout > 0) then
975  call this%log_griddata(found)
976  end if
977  !
978  end subroutine source_griddata
979 
980  !> @brief Write griddata found to list file
981  !<
982  subroutine log_griddata(this, found)
983  ! -- dummy
984  class(disutype) :: this
985  type(disufoundtype), intent(in) :: found
986  !
987  write (this%iout, '(1x,a)') 'Setting Discretization Griddata'
988  !
989  if (found%top) then
990  write (this%iout, '(4x,a)') 'TOP set from input file'
991  end if
992  !
993  if (found%bot) then
994  write (this%iout, '(4x,a)') 'BOT set from input file'
995  end if
996  !
997  if (found%area) then
998  write (this%iout, '(4x,a)') 'AREA set from input file'
999  end if
1000  !
1001  if (found%idomain) then
1002  write (this%iout, '(4x,a)') 'IDOMAIN set from input file'
1003  end if
1004  !
1005  write (this%iout, '(1x,a,/)') 'End Setting Discretization Griddata'
1006  !
1007  end subroutine log_griddata
1008 
1009  !> @brief Copy grid connectivity info from IDM into package
1010  !<
1011  subroutine source_connectivity(this)
1012  ! -- dummy
1013  class(disutype) :: this
1014  ! -- locals
1015  type(disufoundtype) :: found
1016  integer(I4B), dimension(:), contiguous, pointer :: iac => null()
1017  ! -- formats
1018  !
1019  ! -- update defaults with idm sourced values
1020  call mem_set_value(this%jainp, 'JA', this%input_mempath, found%ja)
1021  call mem_set_value(this%ihcinp, 'IHC', this%input_mempath, found%ihc)
1022  call mem_set_value(this%cl12inp, 'CL12', this%input_mempath, found%cl12)
1023  call mem_set_value(this%hwvainp, 'HWVA', this%input_mempath, found%hwva)
1024  call mem_set_value(this%angldegxinp, 'ANGLDEGX', this%input_mempath, &
1025  found%angldegx)
1026  !
1027  ! -- set pointer to iac input array
1028  call mem_setptr(iac, 'IAC', this%input_mempath)
1029  !
1030  ! -- Convert iac to ia
1031  if (associated(iac)) call iac_to_ia(iac, this%iainp)
1032  !
1033  ! -- Set angldegx flag if found
1034  if (found%angldegx) this%iangledegx = 1
1035  !
1036  ! -- log simulation values
1037  if (this%iout > 0) then
1038  call this%log_connectivity(found, iac)
1039  end if
1040  !
1041  end subroutine source_connectivity
1042 
1043  !> @brief Write griddata found to list file
1044  !<
1045  subroutine log_connectivity(this, found, iac)
1046  class(disutype) :: this
1047  type(disufoundtype), intent(in) :: found
1048  integer(I4B), dimension(:), contiguous, pointer, intent(in) :: iac
1049  !
1050  write (this%iout, '(1x,a)') 'Setting Discretization Connectivity'
1051  !
1052  if (associated(iac)) then
1053  write (this%iout, '(4x,a)') 'IAC set from input file'
1054  end if
1055  !
1056  if (found%ja) then
1057  write (this%iout, '(4x,a)') 'JA set from input file'
1058  end if
1059  !
1060  if (found%ihc) then
1061  write (this%iout, '(4x,a)') 'IHC set from input file'
1062  end if
1063  !
1064  if (found%cl12) then
1065  write (this%iout, '(4x,a)') 'CL12 set from input file'
1066  end if
1067  !
1068  if (found%hwva) then
1069  write (this%iout, '(4x,a)') 'HWVA set from input file'
1070  end if
1071  !
1072  if (found%angldegx) then
1073  write (this%iout, '(4x,a)') 'ANGLDEGX set from input file'
1074  end if
1075  !
1076  write (this%iout, '(1x,a,/)') 'End Setting Discretization Connectivity'
1077  !
1078  end subroutine log_connectivity
1079 
1080  !> @brief Copy grid vertex data from IDM into package
1081  !<
1082  subroutine source_vertices(this)
1083  ! -- dummy
1084  class(disutype) :: this
1085  ! -- local
1086  integer(I4B) :: i
1087  real(DP), dimension(:), contiguous, pointer :: vert_x => null()
1088  real(DP), dimension(:), contiguous, pointer :: vert_y => null()
1089  ! -- formats
1090  !
1091  ! -- set pointers to memory manager input arrays
1092  call mem_setptr(vert_x, 'XV', this%input_mempath)
1093  call mem_setptr(vert_y, 'YV', this%input_mempath)
1094  !
1095  ! -- set vertices 2d array
1096  if (associated(vert_x) .and. associated(vert_y)) then
1097  do i = 1, this%nvert
1098  this%vertices(1, i) = vert_x(i)
1099  this%vertices(2, i) = vert_y(i)
1100  end do
1101  else
1102  call store_error('Required Vertex arrays not found.')
1103  end if
1104  !
1105  ! -- log
1106  if (this%iout > 0) then
1107  write (this%iout, '(1x,a)') 'Discretization Vertex data loaded'
1108  end if
1109  !
1110  call memorystore_release('XV', this%input_mempath)
1111  call memorystore_release('YV', this%input_mempath)
1112  end subroutine source_vertices
1113 
1114  !> @brief Build data structures to hold cell vertex info
1115  !<
1116  subroutine define_cellverts(this, icell2d, ncvert, icvert)
1117  ! -- modules
1118  use sparsemodule, only: sparsematrix
1119  ! -- dummy
1120  class(disutype) :: this
1121  integer(I4B), dimension(:), contiguous, pointer, intent(in) :: icell2d
1122  integer(I4B), dimension(:), contiguous, pointer, intent(in) :: ncvert
1123  integer(I4B), dimension(:), contiguous, pointer, intent(in) :: icvert
1124  ! -- locals
1125  type(sparsematrix) :: vert_spm
1126  integer(I4B) :: i, j, ierr
1127  integer(I4B) :: icv_idx, startvert, maxnnz = 5
1128  !
1129  ! -- initialize sparse matrix
1130  call vert_spm%init(this%nodesuser, this%nvert, maxnnz)
1131  !
1132  ! -- add sparse matrix connections from input memory paths
1133  icv_idx = 1
1134  do i = 1, this%nodesuser
1135  if (icell2d(i) /= i) call store_error('ICELL2D input sequence violation.')
1136  do j = 1, ncvert(i)
1137  call vert_spm%addconnection(i, icvert(icv_idx), 0)
1138  if (j == 1) then
1139  startvert = icvert(icv_idx)
1140  elseif (j == ncvert(i) .and. (icvert(icv_idx) /= startvert)) then
1141  call vert_spm%addconnection(i, startvert, 0)
1142  end if
1143  icv_idx = icv_idx + 1
1144  end do
1145  end do
1146  !
1147  ! -- allocate and fill iavert and javert
1148  call mem_allocate(this%iavert, this%nodesuser + 1, 'IAVERT', this%memoryPath)
1149  call mem_allocate(this%javert, vert_spm%nnz, 'JAVERT', this%memoryPath)
1150  call vert_spm%filliaja(this%iavert, this%javert, ierr)
1151  call vert_spm%destroy()
1152  !
1153  end subroutine define_cellverts
1154 
1155  !> @brief Copy cell2d data from IDM into package
1156  !<
1157  subroutine source_cell2d(this)
1158  ! -- dummy
1159  class(disutype) :: this
1160  ! -- locals
1161  integer(I4B), dimension(:), contiguous, pointer :: icell2d => null()
1162  integer(I4B), dimension(:), contiguous, pointer :: ncvert => null()
1163  integer(I4B), dimension(:), contiguous, pointer :: icvert => null()
1164  real(DP), dimension(:), contiguous, pointer :: cell_x => null()
1165  real(DP), dimension(:), contiguous, pointer :: cell_y => null()
1166  integer(I4B) :: i
1167  !
1168  ! -- set pointers to input path ncvert and icvert
1169  call mem_setptr(icell2d, 'ICELL2D', this%input_mempath)
1170  call mem_setptr(ncvert, 'NCVERT', this%input_mempath)
1171  call mem_setptr(icvert, 'ICVERT', this%input_mempath)
1172  !
1173  ! --
1174  if (associated(icell2d) .and. associated(ncvert) &
1175  .and. associated(icvert)) then
1176  call this%define_cellverts(icell2d, ncvert, icvert)
1177  else
1178  call store_error('Required cell vertex arrays not found.')
1179  end if
1180  !
1181  ! -- set pointers to cell center arrays
1182  call mem_setptr(cell_x, 'XC', this%input_mempath)
1183  call mem_setptr(cell_y, 'YC', this%input_mempath)
1184  !
1185  ! -- set cell centers
1186  if (associated(cell_x) .and. associated(cell_y)) then
1187  do i = 1, this%nodesuser
1188  this%cellxy(1, i) = cell_x(i)
1189  this%cellxy(2, i) = cell_y(i)
1190  end do
1191  else
1192  call store_error('Required cell center arrays not found.')
1193  end if
1194  !
1195  ! -- log
1196  if (this%iout > 0) then
1197  write (this%iout, '(1x,a)') 'Discretization Cell2d data loaded'
1198  end if
1199  !
1200  call memorystore_release('ICELL2D', this%input_mempath)
1201  call memorystore_release('NCVERT', this%input_mempath)
1202  call memorystore_release('ICVERT', this%input_mempath)
1203  call memorystore_release('XC', this%input_mempath)
1204  call memorystore_release('YC', this%input_mempath)
1205  end subroutine source_cell2d
1206 
1207  !> @brief Write a binary grid file
1208  !<
1209  subroutine write_grb(this, icelltype)
1210  ! -- modules
1211  use openspecmodule, only: access, form
1212  use constantsmodule, only: lenbigline
1213  ! -- dummy
1214  class(disutype) :: this
1215  integer(I4B), dimension(:), intent(in) :: icelltype
1216  ! -- local
1217  integer(I4B) :: i, iunit, ntxt, version
1218  integer(I4B), parameter :: lentxt = 100
1219  character(len=50) :: txthdr
1220  character(len=lentxt) :: txt
1221  character(len=LINELENGTH) :: fname
1222  character(len=LENBIGLINE) :: crs
1223  logical(LGP) :: found_crs
1224  ! -- formats
1225  character(len=*), parameter :: fmtgrdsave = &
1226  "(4X,'BINARY GRID INFORMATION WILL BE WRITTEN TO:', &
1227  &/,6X,'UNIT NUMBER: ', I0,/,6X, 'FILE NAME: ', A)"
1228  !
1229  ! -- Initialize
1230  version = 1
1231  ntxt = 11
1232  if (this%nvert > 0) ntxt = ntxt + 5
1233  !
1234  call mem_set_value(crs, 'CRS', this%input_mempath, found_crs)
1235  !
1236  ! -- set version
1237  if (found_crs) then
1238  ntxt = ntxt + 1
1239  version = 2
1240  end if
1241  !
1242  ! -- Open the file
1243  fname = trim(this%output_fname)
1244  iunit = getunit()
1245  write (this%iout, fmtgrdsave) iunit, trim(adjustl(fname))
1246  call openfile(iunit, this%iout, trim(adjustl(fname)), 'DATA(BINARY)', &
1247  form, access, 'REPLACE')
1248  !
1249  ! -- write header information
1250  write (txthdr, '(a)') 'GRID DISU'
1251  txthdr(50:50) = new_line('a')
1252  write (iunit) txthdr
1253  write (txthdr, '(a, i0)') 'VERSION ', version
1254  txthdr(50:50) = new_line('a')
1255  write (iunit) txthdr
1256  write (txthdr, '(a, i0)') 'NTXT ', ntxt
1257  txthdr(50:50) = new_line('a')
1258  write (iunit) txthdr
1259  write (txthdr, '(a, i0)') 'LENTXT ', lentxt
1260  txthdr(50:50) = new_line('a')
1261  write (iunit) txthdr
1262  !
1263  ! -- write variable definitions
1264  write (txt, '(3a, i0)') 'NODES ', 'INTEGER ', 'NDIM 0 # ', this%nodesuser
1265  txt(lentxt:lentxt) = new_line('a')
1266  write (iunit) txt
1267  write (txt, '(3a, i0)') 'NJA ', 'INTEGER ', 'NDIM 0 # ', this%con%nja
1268  txt(lentxt:lentxt) = new_line('a')
1269  write (iunit) txt
1270  write (txt, '(3a, 1pg24.15)') 'XORIGIN ', 'DOUBLE ', 'NDIM 0 # ', this%xorigin
1271  txt(lentxt:lentxt) = new_line('a')
1272  write (iunit) txt
1273  write (txt, '(3a, 1pg24.15)') 'YORIGIN ', 'DOUBLE ', 'NDIM 0 # ', this%yorigin
1274  txt(lentxt:lentxt) = new_line('a')
1275  write (iunit) txt
1276  write (txt, '(3a, 1pg24.15)') 'ANGROT ', 'DOUBLE ', 'NDIM 0 # ', this%angrot
1277  txt(lentxt:lentxt) = new_line('a')
1278  write (iunit) txt
1279  write (txt, '(3a, i0)') 'TOP ', 'DOUBLE ', 'NDIM 1 ', this%nodesuser
1280  txt(lentxt:lentxt) = new_line('a')
1281  write (iunit) txt
1282  write (txt, '(3a, i0)') 'BOT ', 'DOUBLE ', 'NDIM 1 ', this%nodesuser
1283  txt(lentxt:lentxt) = new_line('a')
1284  write (iunit) txt
1285  write (txt, '(3a, i0)') 'IA ', 'INTEGER ', 'NDIM 1 ', this%nodesuser + 1
1286  txt(lentxt:lentxt) = new_line('a')
1287  write (iunit) txt
1288  write (txt, '(3a, i0)') 'JA ', 'INTEGER ', 'NDIM 1 ', this%con%nja
1289  txt(lentxt:lentxt) = new_line('a')
1290  write (iunit) txt
1291  write (txt, '(3a, i0)') 'IDOMAIN ', 'INTEGER ', 'NDIM 1 ', this%nodesuser
1292  txt(lentxt:lentxt) = new_line('a')
1293  write (iunit) txt
1294  write (txt, '(3a, i0)') 'ICELLTYPE ', 'INTEGER ', 'NDIM 1 ', this%nodesuser
1295  txt(lentxt:lentxt) = new_line('a')
1296  write (iunit) txt
1297  !
1298  ! -- if vertices have been read then write additional header information
1299  if (this%nvert > 0) then
1300  write (txt, '(3a, i0)') 'VERTICES ', 'DOUBLE ', 'NDIM 2 2 ', this%nvert
1301  txt(lentxt:lentxt) = new_line('a')
1302  write (iunit) txt
1303  write (txt, '(3a, i0)') 'CELLX ', 'DOUBLE ', 'NDIM 1 ', this%nodesuser
1304  txt(lentxt:lentxt) = new_line('a')
1305  write (iunit) txt
1306  write (txt, '(3a, i0)') 'CELLY ', 'DOUBLE ', 'NDIM 1 ', this%nodesuser
1307  txt(lentxt:lentxt) = new_line('a')
1308  write (iunit) txt
1309  write (txt, '(3a, i0)') 'IAVERT ', 'INTEGER ', 'NDIM 1 ', this%nodesuser + 1
1310  txt(lentxt:lentxt) = new_line('a')
1311  write (iunit) txt
1312  write (txt, '(3a, i0)') 'JAVERT ', 'INTEGER ', 'NDIM 1 ', size(this%javert)
1313  txt(lentxt:lentxt) = new_line('a')
1314  write (iunit) txt
1315  end if
1316  !
1317  ! -- if version 2 write character array headers
1318  if (version == 2) then
1319  if (found_crs) then
1320  write (txt, '(3a, i0)') 'CRS ', 'CHARACTER ', 'NDIM 1 ', &
1321  len_trim(crs)
1322  txt(lentxt:lentxt) = new_line('a')
1323  write (iunit) txt
1324  end if
1325  end if
1326  !
1327  ! -- write data
1328  write (iunit) this%nodesuser ! nodes
1329  write (iunit) this%nja ! nja
1330  write (iunit) this%xorigin ! xorigin
1331  write (iunit) this%yorigin ! yorigin
1332  write (iunit) this%angrot ! angrot
1333  write (iunit) this%top1d ! top
1334  write (iunit) this%bot1d ! bot
1335  write (iunit) this%con%iausr ! ia
1336  write (iunit) this%con%jausr ! ja
1337  write (iunit) this%idomain ! idomain
1338  write (iunit) icelltype ! icelltype
1339  !
1340  ! -- if vertices have been read then write additional data
1341  if (this%nvert > 0) then
1342  write (iunit) this%vertices ! vertices
1343  write (iunit) (this%cellxy(1, i), i=1, this%nodesuser) ! cellx
1344  write (iunit) (this%cellxy(2, i), i=1, this%nodesuser) ! celly
1345  write (iunit) this%iavert ! iavert
1346  write (iunit) this%javert ! javert
1347  end if
1348  !
1349  ! -- if version 2 write character array data
1350  if (version == 2) then
1351  if (found_crs) write (iunit) trim(crs) ! crs user input
1352  end if
1353  !
1354  ! -- Close the file
1355  close (iunit)
1356  !
1357  end subroutine write_grb
1358 
1359  !> @brief Get reduced node number from user node number
1360  !<
1361  function get_nodenumber_idx1(this, nodeu, icheck) result(nodenumber)
1362  class(disutype), intent(in) :: this
1363  integer(I4B), intent(in) :: nodeu
1364  integer(I4B), intent(in) :: icheck
1365  integer(I4B) :: nodenumber
1366  !
1367  if (icheck /= 0) then
1368  if (nodeu < 1 .or. nodeu > this%nodesuser) then
1369  write (errmsg, '(a,i0,a,i0,a)') &
1370  'Node number (', nodeu, ') is less than 1 or greater than nodes (', &
1371  this%nodesuser, ').'
1372  call store_error(errmsg)
1373  end if
1374  end if
1375  !
1376  ! -- set node number to passed in nodenumber since there is a one to one
1377  ! mapping for an unstructured grid
1378  if (this%nodes == this%nodesuser) then
1379  nodenumber = nodeu
1380  else
1381  nodenumber = this%nodereduced(nodeu)
1382  end if
1383  !
1384  end function get_nodenumber_idx1
1385 
1386  !> @brief Get normal vector components between the cell and a given neighbor
1387  !!
1388  !! The normal points outward from the shared face between noden and nodem.
1389  !<
1390  subroutine connection_normal(this, noden, nodem, ihc, xcomp, ycomp, zcomp, &
1391  ipos)
1392  ! -- dummy
1393  class(disutype) :: this
1394  integer(I4B), intent(in) :: noden !< cell (reduced nn)
1395  integer(I4B), intent(in) :: nodem !< neighbor (reduced nn)
1396  integer(I4B), intent(in) :: ihc !< horizontal connection flag
1397  real(DP), intent(inout) :: xcomp
1398  real(DP), intent(inout) :: ycomp
1399  real(DP), intent(inout) :: zcomp
1400  integer(I4B), intent(in) :: ipos
1401  ! -- local
1402  real(DP) :: angle, dmult
1403  !
1404  ! -- Set vector components based on ihc
1405  if (ihc == 0) then
1406  !
1407  ! -- connection is vertical
1408  xcomp = dzero
1409  ycomp = dzero
1410  if (nodem < noden) then
1411  !
1412  ! -- nodem must be above noden, so upward connection
1413  zcomp = done
1414  else
1415  !
1416  ! -- nodem must be below noden, so downward connection
1417  zcomp = -done
1418  end if
1419  else
1420  ! -- find from anglex, since anglex is symmetric, need to flip vector
1421  ! for lower triangle (nodem < noden)
1422  angle = this%con%anglex(this%con%jas(ipos))
1423  dmult = done
1424  if (nodem < noden) dmult = -done
1425  xcomp = cos(angle) * dmult
1426  ycomp = sin(angle) * dmult
1427  zcomp = dzero
1428  end if
1429  !
1430  end subroutine connection_normal
1431 
1432  !> @brief Get unit vector components between the cell and a given neighbor
1433  !!
1434  !! Saturation must be provided to compute cell center vertical coordinates.
1435  !! Also return the straight-line connection length.
1436  !<
1437  subroutine connection_vector(this, noden, nodem, nozee, satn, satm, ihc, &
1438  xcomp, ycomp, zcomp, conlen)
1439  ! -- dummy
1440  class(disutype) :: this
1441  integer(I4B), intent(in) :: noden
1442  integer(I4B), intent(in) :: nodem
1443  logical, intent(in) :: nozee
1444  real(DP), intent(in) :: satn
1445  real(DP), intent(in) :: satm
1446  integer(I4B), intent(in) :: ihc
1447  real(DP), intent(inout) :: xcomp
1448  real(DP), intent(inout) :: ycomp
1449  real(DP), intent(inout) :: zcomp
1450  real(DP), intent(inout) :: conlen
1451  ! -- local
1452  real(DP) :: xn, xm, yn, ym, zn, zm
1453  !
1454  ! -- Terminate with error if requesting unit vector components for problems
1455  ! without cell data
1456  if (size(this%cellxy, 2) < 1) then
1457  write (errmsg, '(a)') &
1458  'Cannot calculate unit vector components for DISU grid if VERTEX '// &
1459  'data are not specified'
1460  call store_error(errmsg, terminate=.true.)
1461  end if
1462  !
1463  ! -- get xy center coords
1464  xn = this%xc(noden)
1465  yn = this%yc(noden)
1466  xm = this%xc(nodem)
1467  ym = this%yc(nodem)
1468  !
1469  ! -- Set vector components based on ihc
1470  if (ihc == 0) then
1471  !
1472  ! -- vertical connection, calculate z as cell center elevation
1473  zn = this%bot(noden) + dhalf * (this%top(noden) - this%bot(noden))
1474  zm = this%bot(nodem) + dhalf * (this%top(nodem) - this%bot(nodem))
1475  else
1476  !
1477  ! -- horizontal connection, with possible z component due to cell offsets
1478  ! and/or water table conditions
1479  if (nozee) then
1480  zn = dzero
1481  zm = dzero
1482  else
1483  zn = this%bot(noden) + dhalf * satn * (this%top(noden) - this%bot(noden))
1484  zm = this%bot(nodem) + dhalf * satm * (this%top(nodem) - this%bot(nodem))
1485  end if
1486  end if
1487  !
1488  ! -- Use coords to find vector components and connection length
1489  call line_unit_vector(xn, yn, zn, xm, ym, zm, xcomp, ycomp, zcomp, &
1490  conlen)
1491  !
1492  end subroutine connection_vector
1493 
1494  !> @brief Get the discretization type
1495  !<
1496  subroutine get_dis_type(this, dis_type)
1497  ! -- dummy
1498  class(disutype), intent(in) :: this
1499  character(len=*), intent(out) :: dis_type
1500  !
1501  dis_type = "DISU"
1502  !
1503  end subroutine get_dis_type
1504 
1505  !> @brief Get the discretization type enumeration
1506  function get_dis_enum(this) result(dis_enum)
1507  use constantsmodule, only: disu
1508  class(disutype), intent(in) :: this
1509  integer(I4B) :: dis_enum
1510  dis_enum = disu
1511  end function get_dis_enum
1512 
1513  !> @brief Allocate and initialize scalar variables
1514  !<
1515  subroutine allocate_scalars(this, name_model, input_mempath)
1516  ! -- dummy
1517  class(disutype) :: this
1518  character(len=*), intent(in) :: name_model
1519  character(len=*), intent(in) :: input_mempath
1520  !
1521  ! -- Allocate parent scalars
1522  call this%DisBaseType%allocate_scalars(name_model, input_mempath)
1523  !
1524  ! -- Allocate variables for DISU
1525  call mem_allocate(this%njausr, 'NJAUSR', this%memoryPath)
1526  call mem_allocate(this%nvert, 'NVERT', this%memoryPath)
1527  call mem_allocate(this%nangldegxerr, 'NANGLDEGXERR', this%memoryPath)
1528  call mem_allocate(this%voffsettol, 'VOFFSETTOL', this%memoryPath)
1529  call mem_allocate(this%iangledegx, 'IANGLEDEGX', this%memoryPath)
1530  !
1531  ! -- Set values
1532  this%ndim = 1
1533  this%njausr = 0
1534  this%nvert = 0
1535  this%nangldegxerr = 0
1536  this%voffsettol = dzero
1537  this%iangledegx = 0
1538  this%readFromFile = .false.
1539  !
1540  end subroutine allocate_scalars
1541 
1542  !> @brief Allocate and initialize arrays
1543  !<
1544  subroutine allocate_arrays(this)
1545  ! -- dummy
1546  class(disutype) :: this
1547  !
1548  ! -- Allocate arrays in DisBaseType (mshape, top, bot, area)
1549  call this%DisBaseType%allocate_arrays()
1550  !
1551  ! -- Allocate arrays in DISU
1552  if (this%nodes < this%nodesuser) then
1553  call mem_allocate(this%nodeuser, this%nodes, 'NODEUSER', this%memoryPath)
1554  call mem_allocate(this%nodereduced, this%nodesuser, 'NODEREDUCED', &
1555  this%memoryPath)
1556  else
1557  call mem_allocate(this%nodeuser, 1, 'NODEUSER', this%memoryPath)
1558  call mem_allocate(this%nodereduced, 1, 'NODEREDUCED', this%memoryPath)
1559  end if
1560  !
1561  ! -- Initialize
1562  this%mshape(1) = this%nodesuser
1563  !
1564  end subroutine allocate_arrays
1565 
1566  !> @brief Allocate arrays in memory manager
1567  !<
1568  subroutine allocate_arrays_mem(this)
1569  ! -- modules
1571  ! -- dummy
1572  class(disutype) :: this
1573  !
1574  call mem_allocate(this%idomain, this%nodes, 'IDOMAIN', this%memoryPath)
1575  call mem_allocate(this%vertices, 2, this%nvert, 'VERTICES', this%memoryPath)
1576  if (this%icondir > 0) then
1577  call mem_allocate(this%cellxy, 2, this%nodes, 'CELLXY', this%memoryPath)
1578  else
1579  call mem_allocate(this%cellxy, 2, 0, 'CELLXY', this%memoryPath)
1580  end if
1581  !
1582  end subroutine allocate_arrays_mem
1583 
1584  !> @brief Convert a string to a user nodenumber
1585  !!
1586  !! Parse and return user nodenumber.
1587  !! If flag_string is present and true, the first token may be
1588  !! non-numeric (e.g. boundary name). In this case, return -2.
1589  !<
1590  function nodeu_from_string(this, lloc, istart, istop, in, iout, line, &
1591  flag_string, allow_zero) result(nodeu)
1592  ! -- dummy
1593  class(disutype) :: this
1594  integer(I4B), intent(inout) :: lloc
1595  integer(I4B), intent(inout) :: istart
1596  integer(I4B), intent(inout) :: istop
1597  integer(I4B), intent(in) :: in
1598  integer(I4B), intent(in) :: iout
1599  character(len=*), intent(inout) :: line
1600  logical, optional, intent(in) :: flag_string
1601  logical, optional, intent(in) :: allow_zero
1602  integer(I4B) :: nodeu
1603  ! -- local
1604  integer(I4B) :: lloclocal, ndum, istat, n
1605  real(dp) :: r
1606  !
1607  if (present(flag_string)) then
1608  if (flag_string) then
1609  ! Check to see if first token in line can be read as an integer.
1610  lloclocal = lloc
1611  call urword(line, lloclocal, istart, istop, 1, ndum, r, iout, in)
1612  read (line(istart:istop), *, iostat=istat) n
1613  if (istat /= 0) then
1614  ! First token in line is not an integer; return flag to this effect.
1615  nodeu = -2
1616  return
1617  end if
1618  end if
1619  end if
1620  !
1621  call urword(line, lloc, istart, istop, 2, nodeu, r, iout, in)
1622  !
1623  if (nodeu == 0) then
1624  if (present(allow_zero)) then
1625  if (allow_zero) then
1626  return
1627  end if
1628  end if
1629  end if
1630  !
1631  if (nodeu < 1 .or. nodeu > this%nodesuser) then
1632  write (errmsg, '(a,i0,a)') &
1633  "Node number in list (", nodeu, ") is outside of the grid. "// &
1634  "Cell number cannot be determined in line '"// &
1635  trim(adjustl(line))//"'."
1636  call store_error(errmsg)
1637  call store_error_unit(in)
1638  end if
1639  !
1640  end function nodeu_from_string
1641 
1642  !> @brief Convert a cellid string to a user nodenumber
1643  !!
1644  !! If flag_string is present and true, the first token may be
1645  !! non-numeric (e.g. boundary name). In this case, return -2.
1646  !!
1647  !! If allow_zero is present and true, and all indices are zero, the
1648  !! result can be zero. If allow_zero is false, a zero in any index is an error.
1649  !<
1650  function nodeu_from_cellid(this, cellid, inunit, iout, flag_string, &
1651  allow_zero) result(nodeu)
1652  ! -- return
1653  integer(I4B) :: nodeu
1654  ! -- dummy
1655  class(disutype) :: this
1656  character(len=*), intent(inout) :: cellid
1657  integer(I4B), intent(in) :: inunit
1658  integer(I4B), intent(in) :: iout
1659  logical, optional, intent(in) :: flag_string
1660  logical, optional, intent(in) :: allow_zero
1661  ! -- local
1662  integer(I4B) :: lloclocal, istart, istop, ndum, n
1663  integer(I4B) :: istat
1664  real(dp) :: r
1665  !
1666  if (present(flag_string)) then
1667  if (flag_string) then
1668  ! Check to see if first token in cellid can be read as an integer.
1669  lloclocal = 1
1670  call urword(cellid, lloclocal, istart, istop, 1, ndum, r, iout, inunit)
1671  read (cellid(istart:istop), *, iostat=istat) n
1672  if (istat /= 0) then
1673  ! First token in cellid is not an integer; return flag to this effect.
1674  nodeu = -2
1675  return
1676  end if
1677  end if
1678  end if
1679  !
1680  lloclocal = 1
1681  call urword(cellid, lloclocal, istart, istop, 2, nodeu, r, iout, inunit)
1682  !
1683  if (nodeu == 0) then
1684  if (present(allow_zero)) then
1685  if (allow_zero) then
1686  return
1687  end if
1688  end if
1689  end if
1690  !
1691  if (nodeu < 1 .or. nodeu > this%nodesuser) then
1692  write (errmsg, '(a,i0,a)') &
1693  "Cell number cannot be determined for cellid ("// &
1694  trim(adjustl(cellid))//") and results in a user "// &
1695  "node number (", nodeu, ") that is outside of the grid."
1696  call store_error(errmsg)
1697  call store_error_unit(inunit)
1698  end if
1699  !
1700  end function nodeu_from_cellid
1701 
1702  !> @brief Indicates whether the grid discretization supports layers
1703  !<
1704  logical function supports_layers(this)
1705  ! -- dummy
1706  class(disutype) :: this
1707  !
1708  supports_layers = .false.
1709  !
1710  end function supports_layers
1711 
1712  !> @brief Get number of cells per layer (total nodes since DISU isn't layered)
1713  !<
1714  function get_ncpl(this)
1715  ! -- return
1716  integer(I4B) :: get_ncpl
1717  ! -- dummy
1718  class(disutype) :: this
1719  !
1720  get_ncpl = this%nodesuser
1721  !
1722  end function get_ncpl
1723 
1724  !> @brief Read an integer array
1725  !<
1726  subroutine read_int_array(this, line, lloc, istart, istop, iout, in, &
1727  iarray, aname)
1728  ! -- dummy
1729  class(disutype), intent(inout) :: this
1730  character(len=*), intent(inout) :: line
1731  integer(I4B), intent(inout) :: lloc
1732  integer(I4B), intent(inout) :: istart
1733  integer(I4B), intent(inout) :: istop
1734  integer(I4B), intent(in) :: in
1735  integer(I4B), intent(in) :: iout
1736  integer(I4B), dimension(:), pointer, contiguous, intent(inout) :: iarray
1737  character(len=*), intent(in) :: aname
1738  ! -- local
1739  integer(I4B) :: nval
1740  integer(I4B), dimension(:), pointer, contiguous :: itemp
1741  !
1742  ! -- Point the temporary pointer array, which is passed to the reading
1743  ! subroutine. The temporary array will point to ibuff if it is a
1744  ! reduced structured system, or to iarray if it is an unstructured
1745  ! model.
1746  if (this%nodes < this%nodesuser) then
1747  nval = this%nodesuser
1748  itemp => this%ibuff
1749  else
1750  nval = this%nodes
1751  itemp => iarray
1752  end if
1753  !
1754  ! -- Read the array
1755  ! -- Read unstructured input
1756  call readarray(in, itemp, aname, this%ndim, nval, iout, 0)
1757  !
1758  ! -- If reduced model, then need to copy from itemp(=>ibuff) to iarray
1759  if (this%nodes < this%nodesuser) then
1760  call this%fill_grid_array(itemp, iarray)
1761  end if
1762  !
1763  end subroutine read_int_array
1764 
1765  !> @brief Read a double precision array
1766  !<
1767  subroutine read_dbl_array(this, line, lloc, istart, istop, iout, in, &
1768  darray, aname)
1769  ! -- dummy
1770  class(disutype), intent(inout) :: this
1771  character(len=*), intent(inout) :: line
1772  integer(I4B), intent(inout) :: lloc
1773  integer(I4B), intent(inout) :: istart
1774  integer(I4B), intent(inout) :: istop
1775  integer(I4B), intent(in) :: in
1776  integer(I4B), intent(in) :: iout
1777  real(DP), dimension(:), pointer, contiguous, intent(inout) :: darray
1778  character(len=*), intent(in) :: aname
1779  ! -- local
1780  integer(I4B) :: nval
1781  real(DP), dimension(:), pointer, contiguous :: dtemp
1782  !
1783  ! -- Point the temporary pointer array, which is passed to the reading
1784  ! subroutine. The temporary array will point to dbuff if it is a
1785  ! reduced structured system, or to darray if it is an unstructured
1786  ! model.
1787  if (this%nodes < this%nodesuser) then
1788  nval = this%nodesuser
1789  dtemp => this%dbuff
1790  else
1791  nval = this%nodes
1792  dtemp => darray
1793  end if
1794  !
1795  ! -- Read the array
1796  call readarray(in, dtemp, aname, this%ndim, nval, iout, 0)
1797  !
1798  ! -- If reduced model, then need to copy from dtemp(=>dbuff) to darray
1799  if (this%nodes < this%nodesuser) then
1800  call this%fill_grid_array(dtemp, darray)
1801  end if
1802  !
1803  end subroutine read_dbl_array
1804 
1805  !> @brief Record a double precision array
1806  !!
1807  !! The array is written to a formatted or unformatted external file
1808  !! depending on the arguments.
1809  !<
1810  subroutine record_array(this, darray, iout, iprint, idataun, aname, &
1811  cdatafmp, nvaluesp, nwidthp, editdesc, dinact)
1812  ! -- dummy
1813  class(disutype), intent(inout) :: this
1814  real(DP), dimension(:), pointer, contiguous, intent(inout) :: darray !< double precision array to record
1815  integer(I4B), intent(in) :: iout !< ascii output unit number
1816  integer(I4B), intent(in) :: iprint !< whether to print the array
1817  integer(I4B), intent(in) :: idataun !< binary output unit number
1818  character(len=*), intent(in) :: aname !< text descriptor
1819  character(len=*), intent(in) :: cdatafmp ! write format
1820  integer(I4B), intent(in) :: nvaluesp !< values per line
1821  integer(I4B), intent(in) :: nwidthp !< number width
1822  character(len=*), intent(in) :: editdesc !< format type (I, G, F, S, E)
1823  real(DP), intent(in) :: dinact !< double precision value for cells excluded from model domain
1824  ! -- local
1825  integer(I4B) :: k, ifirst
1826  integer(I4B) :: nlay
1827  integer(I4B) :: nrow
1828  integer(I4B) :: ncol
1829  integer(I4B) :: nval
1830  integer(I4B) :: nodeu, noder
1831  integer(I4B) :: istart, istop
1832  real(DP), dimension(:), pointer, contiguous :: dtemp
1833  ! -- formats
1834  character(len=*), parameter :: fmthsv = &
1835  "(1X,/1X,a,' WILL BE SAVED ON UNIT ',I4, &
1836  &' AT END OF TIME STEP',I5,', STRESS PERIOD ',I4)"
1837  !
1838  ! -- set variables
1839  nlay = 1
1840  nrow = 1
1841  ncol = this%mshape(1)
1842  !
1843  ! -- If this is a reduced model, then copy the values from darray into
1844  ! dtemp.
1845  if (this%nodes < this%nodesuser) then
1846  nval = this%nodes
1847  dtemp => this%dbuff
1848  do nodeu = 1, this%nodesuser
1849  noder = this%get_nodenumber(nodeu, 0)
1850  if (noder <= 0) then
1851  dtemp(nodeu) = dinact
1852  cycle
1853  end if
1854  dtemp(nodeu) = darray(noder)
1855  end do
1856  else
1857  nval = this%nodes
1858  dtemp => darray
1859  end if
1860  !
1861  ! -- Print to iout if iprint /= 0
1862  if (iprint /= 0) then
1863  istart = 1
1864  do k = 1, nlay
1865  istop = istart + nrow * ncol - 1
1866  call ulaprufw(ncol, nrow, kstp, kper, k, iout, dtemp(istart:istop), &
1867  aname, cdatafmp, nvaluesp, nwidthp, editdesc)
1868  istart = istop + 1
1869  end do
1870  end if
1871  !
1872  ! -- Save array to an external file.
1873  if (idataun > 0) then
1874  ! -- write to binary file by layer
1875  ifirst = 1
1876  istart = 1
1877  do k = 1, nlay
1878  istop = istart + nrow * ncol - 1
1879  if (ifirst == 1) write (iout, fmthsv) &
1880  trim(adjustl(aname)), idataun, &
1881  kstp, kper
1882  ifirst = 0
1883  call ulasav(dtemp(istart:istop), aname, kstp, kper, &
1884  pertim, totim, ncol, nrow, k, idataun)
1885  istart = istop + 1
1886  end do
1887  elseif (idataun < 0) then
1888  !
1889  ! -- write entire array as one record
1890  call ubdsv1(kstp, kper, aname, -idataun, dtemp, ncol, nrow, nlay, &
1891  iout, delt, pertim, totim)
1892  end if
1893  !
1894  end subroutine record_array
1895 
1896  !> @brief Record list header for imeth=6
1897  !<
1898  subroutine record_srcdst_list_header(this, text, textmodel, textpackage, &
1899  dstmodel, dstpackage, naux, auxtxt, &
1900  ibdchn, nlist, iout)
1901  ! -- dummy
1902  class(disutype) :: this
1903  character(len=16), intent(in) :: text
1904  character(len=16), intent(in) :: textmodel
1905  character(len=16), intent(in) :: textpackage
1906  character(len=16), intent(in) :: dstmodel
1907  character(len=16), intent(in) :: dstpackage
1908  integer(I4B), intent(in) :: naux
1909  character(len=16), dimension(:), intent(in) :: auxtxt
1910  integer(I4B), intent(in) :: ibdchn
1911  integer(I4B), intent(in) :: nlist
1912  integer(I4B), intent(in) :: iout
1913  ! -- local
1914  integer(I4B) :: nlay, nrow, ncol
1915  !
1916  nlay = 1
1917  nrow = 1
1918  ncol = this%mshape(1)
1919  !
1920  ! -- Use ubdsv06 to write list header
1921  call ubdsv06(kstp, kper, text, textmodel, textpackage, dstmodel, dstpackage, &
1922  ibdchn, naux, auxtxt, ncol, nrow, nlay, &
1923  nlist, iout, delt, pertim, totim)
1924  !
1925  end subroutine record_srcdst_list_header
1926 
1927  !> @brief Cast base to DISU
1928  !<
1929  !> @brief Get a 2D array of polygon vertices, listed in
1930  !!
1931  !! clockwise order beginning with the lower left corner.
1932  !! The array is empty if the optional cell vertices were
1933  !! not provided.
1934  !<
1935  subroutine get_polyverts(this, ic, polyverts, closed)
1936  ! -- dummy
1937  class(disutype), intent(inout) :: this
1938  integer(I4B), intent(in) :: ic !< cell number (reduced)
1939  real(DP), allocatable, intent(out) :: polyverts(:, :) !< polygon vertices (column-major indexing)
1940  logical(LGP), intent(in), optional :: closed !< whether to close the polygon, duplicating a vertex
1941  ! -- local
1942  integer(I4B) :: icu, iavert, nverts, m, j
1943  logical(LGP) :: lclosed
1944 
1945  ! count vertices
1946  nverts = this%get_npolyverts(ic)
1947  if (nverts == 0) then
1948  allocate (polyverts(2, 0))
1949  return
1950  end if
1951 
1952  ! check closed option
1953  if (.not. (present(closed))) then
1954  lclosed = .false.
1955  else
1956  lclosed = closed
1957  end if
1958 
1959  ! allocate vertices array
1960  if (lclosed) then
1961  allocate (polyverts(2, nverts + 1))
1962  else
1963  allocate (polyverts(2, nverts))
1964  end if
1965 
1966  ! set vertices
1967  icu = this%get_nodeuser(ic)
1968  iavert = this%iavert(icu)
1969  do m = 1, nverts
1970  j = this%javert(iavert - 1 + m)
1971  polyverts(:, m) = (/this%vertices(1, j), this%vertices(2, j)/)
1972  end do
1973 
1974  ! close if enabled
1975  if (lclosed) &
1976  polyverts(:, nverts + 1) = polyverts(:, 1)
1977 
1978  end subroutine
1979 
1980  !> @brief Get the number of cell polygon vertices, 0 if none are defined.
1981  function get_npolyverts(this, ic, closed) result(npolyverts)
1982  class(disutype), intent(inout) :: this
1983  integer(I4B), intent(in) :: ic !< cell number (reduced)
1984  logical(LGP), intent(in), optional :: closed !< whether to close the polygon, duplicating a vertex
1985  integer(I4B) :: npolyverts
1986  ! local
1987  integer(I4B) :: icu
1988 
1989  ! the vertices and cell2d blocks are optional
1990  if (this%nvert < 1) then
1991  npolyverts = 0
1992  return
1993  end if
1994 
1995  icu = this%get_nodeuser(ic)
1996  npolyverts = this%iavert(icu + 1) - this%iavert(icu) - 1
1997  if (present(closed)) then
1998  if (closed) npolyverts = npolyverts + 1
1999  end if
2000  end function get_npolyverts
2001 
2002  !> @brief Get the maximum number of cell polygon vertices.
2003  function get_max_npolyverts(this, closed) result(max_npolyverts)
2004  class(disutype), intent(inout) :: this
2005  logical(LGP), intent(in), optional :: closed !< whether to close the polygon, duplicating a vertex
2006  integer(I4B) :: max_npolyverts
2007  integer(I4B) :: ic
2008 
2009  max_npolyverts = 0
2010  do ic = 1, this%nodes
2011  max_npolyverts = max(max_npolyverts, this%get_npolyverts(ic, closed))
2012  end do
2013  end function get_max_npolyverts
2014 
2015  function castasdisutype(dis) result(disu)
2016  ! -- dummy
2017  class(*), pointer :: dis !< base pointer to DISU object
2018  ! -- return
2019  class(disutype), pointer :: disu !< the resulting DISU pointer
2020  !
2021  disu => null()
2022  select type (dis)
2023  class is (disutype)
2024  disu => dis
2025  end select
2026  !
2027  end function castasdisutype
2028 
2029 end module disumodule
subroutine, public iac_to_ia(iac, ia)
Convert an iac array into an ia array.
This module contains simulation constants.
Definition: Constants.f90:9
integer(i4b), parameter linelength
maximum length of a standard line
Definition: Constants.f90:45
integer(i4b), parameter lenbigline
maximum length of a big line
Definition: Constants.f90:15
@ disu
DISV6 discretization.
Definition: Constants.f90:158
integer(i4b), parameter lenvarname
maximum length of a variable name
Definition: Constants.f90:17
real(dp), parameter dhalf
real constant 1/2
Definition: Constants.f90:68
real(dp), parameter dpio180
real constant
Definition: Constants.f90:130
real(dp), parameter dem6
real constant 1e-6
Definition: Constants.f90:109
real(dp), parameter dzero
real constant zero
Definition: Constants.f90:65
integer(i4b), parameter lenmempath
maximum length of the memory path
Definition: Constants.f90:27
real(dp), parameter done
real constant 1
Definition: Constants.f90:76
subroutine allocate_scalars(this, name_model, input_mempath)
Allocate and initialize scalar variables.
Definition: Disu.f90:1516
subroutine write_grb(this, icelltype)
Write a binary grid file.
Definition: Disu.f90:1210
subroutine allocate_arrays(this)
Allocate and initialize arrays.
Definition: Disu.f90:1545
subroutine disu_load(this)
Transfer IDM data into this discretization object.
Definition: Disu.f90:173
subroutine get_polyverts(this, ic, polyverts, closed)
Cast base to DISU.
Definition: Disu.f90:1936
subroutine source_connectivity(this)
Copy grid connectivity info from IDM into package.
Definition: Disu.f90:1012
subroutine, public disu_cr(dis, name_model, input_mempath, inunit, iout)
Create a new unstructured discretization object.
Definition: Disu.f90:135
subroutine source_dimensions(this)
Copy dimensions from IDM into package.
Definition: Disu.f90:877
subroutine source_options(this)
Copy options from IDM into package.
Definition: Disu.f90:810
subroutine log_dimensions(this, found)
Write dimensions to list file.
Definition: Disu.f90:938
subroutine disu_da(this)
Deallocate variables.
Definition: Disu.f90:725
class(disutype) function, pointer, public castasdisutype(dis)
Definition: Disu.f90:2016
integer(i4b) function get_ncpl(this)
Get number of cells per layer (total nodes since DISU isn't layered)
Definition: Disu.f90:1715
subroutine define_cellverts(this, icell2d, ncvert, icvert)
Build data structures to hold cell vertex info.
Definition: Disu.f90:1117
subroutine read_dbl_array(this, line, lloc, istart, istop, iout, in, darray, aname)
Read a double precision array.
Definition: Disu.f90:1769
integer(i4b) function nodeu_from_cellid(this, cellid, inunit, iout, flag_string, allow_zero)
Convert a cellid string to a user nodenumber.
Definition: Disu.f90:1652
subroutine disu_face_normal(this, n, m, found, xnorm, ynorm)
Outward normal of the face shared by two cells from the vertices.
Definition: Disu.f90:667
subroutine log_options(this, found)
Write user options to list file.
Definition: Disu.f90:837
subroutine record_srcdst_list_header(this, text, textmodel, textpackage, dstmodel, dstpackage, naux, auxtxt, ibdchn, nlist, iout)
Record list header for imeth=6.
Definition: Disu.f90:1901
subroutine get_dis_type(this, dis_type)
Get the discretization type.
Definition: Disu.f90:1497
subroutine source_vertices(this)
Copy grid vertex data from IDM into package.
Definition: Disu.f90:1083
integer(i4b) function get_max_npolyverts(this, closed)
Get the maximum number of cell polygon vertices.
Definition: Disu.f90:2004
subroutine source_cell2d(this)
Copy cell2d data from IDM into package.
Definition: Disu.f90:1158
subroutine log_griddata(this, found)
Write griddata found to list file.
Definition: Disu.f90:983
subroutine allocate_arrays_mem(this)
Allocate arrays in memory manager.
Definition: Disu.f90:1569
subroutine source_griddata(this)
Copy grid data from IDM into package.
Definition: Disu.f90:962
subroutine nodeu_to_array(this, nodeu, arr)
Convert a user nodenumber to an array (nodenumber)
Definition: Disu.f90:787
subroutine connection_vector(this, noden, nodem, nozee, satn, satm, ihc, xcomp, ycomp, zcomp, conlen)
Get unit vector components between the cell and a given neighbor.
Definition: Disu.f90:1439
subroutine record_array(this, darray, iout, iprint, idataun, aname, cdatafmp, nvaluesp, nwidthp, editdesc, dinact)
Record a double precision array.
Definition: Disu.f90:1812
subroutine disu_df(this)
Define the discretization.
Definition: Disu.f90:200
logical function supports_layers(this)
Indicates whether the grid discretization supports layers.
Definition: Disu.f90:1705
subroutine read_int_array(this, line, lloc, istart, istop, iout, in, iarray, aname)
Read an integer array.
Definition: Disu.f90:1728
subroutine grid_finalize(this)
Finalize the grid.
Definition: Disu.f90:210
subroutine disu_write_warning(this, msg)
Write a warning message to the model listing file.
Definition: Disu.f90:646
integer(i4b) function get_nodenumber_idx1(this, nodeu, icheck)
Get reduced node number from user node number.
Definition: Disu.f90:1362
subroutine nodeu_to_string(this, nodeu, str)
Convert a user nodenumber to a string (nodenumber)
Definition: Disu.f90:772
integer(i4b) function nodeu_from_string(this, lloc, istart, istop, in, iout, line, flag_string, allow_zero)
Convert a string to a user nodenumber.
Definition: Disu.f90:1592
subroutine disu_ck(this)
Check discretization info.
Definition: Disu.f90:320
integer(i4b) function get_dis_enum(this)
Get the discretization type enumeration.
Definition: Disu.f90:1507
subroutine log_connectivity(this, found, iac)
Write griddata found to list file.
Definition: Disu.f90:1046
subroutine connection_normal(this, noden, nodem, ihc, xcomp, ycomp, zcomp, ipos)
Get normal vector components between the cell and a given neighbor.
Definition: Disu.f90:1392
integer(i4b) function get_npolyverts(this, ic, closed)
Get the number of cell polygon vertices, 0 if none are defined.
Definition: Disu.f90:1982
subroutine, public line_unit_vector(x0, y0, z0, x1, y1, z1, xcomp, ycomp, zcomp, vmag)
Calculate the vector components (xcomp, ycomp, and zcomp) for a line defined by two points,...
Definition: DisvGeom.f90:475
subroutine, public ubdsv1(kstp, kper, text, ibdchn, buff, ncol, nrow, nlay, iout, delt, pertim, totim)
Record cell-by-cell flow terms for one component of flow as a 3-D array with extra record to indicate...
integer(i4b) function, public getunit()
Get a free unit number.
subroutine, public ulaprufw(ncol, nrow, kstp, kper, ilay, iout, buf, text, userfmt, nvalues, nwidth, editdesc)
Print 1 layer array with user formatting in wrap format.
subroutine, public ubdsv06(kstp, kper, text, modelnam1, paknam1, modelnam2, paknam2, ibdchn, naux, auxtxt, ncol, nrow, nlay, nlist, iout, delt, pertim, totim)
Write header records for cell-by-cell flow terms for one component of flow.
subroutine, public ulasav(buf, text, kstp, kper, pertim, totim, ncol, nrow, ilay, ichn)
Save 1 layer array on disk.
subroutine, public openfile(iu, iout, fname, ftype, fmtarg_opt, accarg_opt, filstat_opt, mode_opt)
Open a file.
Definition: InputOutput.f90:30
subroutine, public urword(line, icol, istart, istop, ncode, n, r, iout, in)
Extract a word from a string.
This module defines variable data types.
Definition: kind.f90:8
subroutine, public memorystore_remove(component, subcomponent, context)
subroutine, public memorystore_release(varname, memory_path)
Release a single variable from the memory store.
Store and issue logging messages to output units.
Definition: Message.f90:2
subroutine, public write_message_counter(text, iunit, icount, iwidth, skipbefore, skipafter)
Write a message with configurable indentation and numbering.
Definition: Message.f90:286
character(len=20) access
Definition: OpenSpec.f90:7
character(len=20) form
Definition: OpenSpec.f90:7
This module contains simulation methods.
Definition: Sim.f90:10
subroutine, public store_warning(msg, substring)
Store warning message.
Definition: Sim.f90:237
subroutine, public store_error(msg, terminate)
Store an error message.
Definition: Sim.f90:92
integer(i4b) function, public count_errors()
Return number of errors.
Definition: Sim.f90:59
subroutine, public store_error_filename(filename, terminate)
Store the erroring file name.
Definition: Sim.f90:204
subroutine, public store_error_unit(iunit, terminate)
Store the file unit number.
Definition: Sim.f90:169
This module contains simulation variables.
Definition: SimVariables.f90:9
character(len=maxcharlen) errmsg
error message string
character(len=linelength) idm_context
real(dp), pointer, public pertim
time relative to start of stress period
Definition: tdis.f90:33
real(dp), pointer, public totim
time relative to start of simulation
Definition: tdis.f90:35
integer(i4b), pointer, public kstp
current time step number
Definition: tdis.f90:27
integer(i4b), pointer, public kper
current stress period number
Definition: tdis.f90:26
real(dp), pointer, public delt
length of the current time step
Definition: tdis.f90:32
Unstructured grid discretization.
Definition: Disu.f90:30