MODFLOW 6  version 6.9.0.dev0
USGS Modular Hydrologic Model
StructArray.f90
Go to the documentation of this file.
1 !> @brief This module contains the StructArrayModule
2 !!
3 !! This module contains the routines for reading a
4 !! structured list, which consists of a separate vector
5 !! for each column in the list.
6 !!
7 !<
9 
10  use kindmodule, only: i4b, dp, lgp
11  use constantsmodule, only: dzero, izero, dnodata, &
14  use simvariablesmodule, only: errmsg
27  use stlvecintmodule, only: stlvecint
28  use idmloggermodule, only: idm_log_var
31 
32  implicit none
33  private
34  public :: structarraytype
36  public :: idm_input_varname
37  public :: is_auxval
38  public :: find_auxname_index
39 
40  !> @brief type for structured array
41  !!
42  !! This type is used to read and store a list
43  !! that consists of multiple one-dimensional
44  !! vectors.
45  !!
46  !<
48  integer(I4B) :: ncol
49  integer(I4B) :: nrow
50  integer(I4B) :: blocknum
51  logical(LGP) :: deferred_shape = .false.
52  integer(I4B) :: deferred_size_init = 5
53  character(len=LENMEMPATH) :: mempath
54  character(len=LENMEMPATH) :: component_mempath
55  type(structvectortype), dimension(:), allocatable :: struct_vectors
56  integer(I4B), dimension(:), allocatable :: startidx
57  integer(I4B), dimension(:), allocatable :: numcols
58  type(modflowinputtype) :: mf6_input
59  contains
60  procedure :: mem_create_vector
62  procedure :: count
63  procedure :: get
64  procedure :: allocate_int_type
65  procedure :: allocate_dbl_type
66  procedure :: allocate_charstr_type
67  procedure :: allocate_int1d_type
68  procedure :: allocate_dbl1d_type
69  procedure :: read_param
70  procedure :: read_from_parser
72  procedure :: read_from_binary
73  procedure :: memload_vectors
74  procedure :: load_deferred_vector
75  procedure :: log_structarray_vars
76  procedure :: check_reallocate
77  procedure :: ts_update
78  procedure :: ts_update_adv
79  procedure :: ts_update_indexed
80  procedure, private :: ts_update_dedup
81 
82  end type structarraytype
83 
84 contains
85 
86  !> @brief constructor for a struct_array
87  !<
88  function constructstructarray(mf6_input, ncol, nrow, blocknum, mempath, &
89  component_mempath, size_init) result(struct_array)
90  type(modflowinputtype), intent(in) :: mf6_input
91  integer(I4B), intent(in) :: ncol !< number of columns in the StructArrayType
92  integer(I4B), intent(in) :: nrow !< number of rows in the StructArrayType
93  integer(I4B), intent(in) :: blocknum !< valid block number or 0
94  character(len=*), intent(in) :: mempath !< memory path for storing the vector
95  character(len=*), intent(in) :: component_mempath
96  integer(I4B), optional, intent(in) :: size_init !< initial deferred allocation size (default 5)
97  type(structarraytype), pointer :: struct_array !< new StructArrayType
98 
99  ! allocate StructArrayType
100  allocate (struct_array)
101 
102  ! set description of input
103  struct_array%mf6_input = mf6_input
104 
105  ! set number of arrays
106  struct_array%ncol = ncol
107 
108  ! set rows if known or set deferred
109  struct_array%nrow = nrow
110  if (struct_array%nrow == -1) then
111  struct_array%nrow = 0
112  struct_array%deferred_shape = .true.
113  if (present(size_init)) then
114  ! ignore a non-sensible value and keep the default deferred_size_init
115  if (size_init >= 1) struct_array%deferred_size_init = size_init
116  end if
117  end if
118 
119  ! set blocknum
120  if (blocknum > 0) then
121  struct_array%blocknum = blocknum
122  else
123  struct_array%blocknum = 0
124  end if
125 
126  ! set mempath
127  struct_array%mempath = mempath
128  struct_array%component_mempath = component_mempath
129 
130  ! allocate StructVectorType objects
131  allocate (struct_array%struct_vectors(ncol))
132  allocate (struct_array%startidx(ncol))
133  allocate (struct_array%numcols(ncol))
134  end function constructstructarray
135 
136  !> @brief destructor for a struct_array
137  !<
138  subroutine destructstructarray(struct_array)
139  type(structarraytype), pointer, intent(inout) :: struct_array !< StructArrayType to destroy
140  deallocate (struct_array%struct_vectors)
141  deallocate (struct_array%startidx)
142  deallocate (struct_array%numcols)
143  deallocate (struct_array)
144  nullify (struct_array)
145  end subroutine destructstructarray
146 
147  !> @brief create new vector in StructArrayType
148  !<
149  subroutine mem_create_vector(this, icol, idt, charlen, varname)
150  class(structarraytype) :: this !< StructArrayType
151  integer(I4B), intent(in) :: icol !< column to create
152  type(inputparamdefinitiontype), pointer :: idt
153  integer(I4B), optional, intent(in) :: charlen !< override character length for charstr1d
154  character(len=*), optional, intent(in) :: varname !< override the memory-manager name (default idt%mf6varname)
155  type(structvectortype) :: sv
156  integer(I4B) :: numcol
157 
158  ! initialize
159  numcol = 1
160  sv%idt => idt
161  sv%icol = icol
162  if (present(charlen)) sv%charlen = charlen
163  if (present(varname)) sv%varname_override = varname
164 
165  ! set size
166  if (this%deferred_shape) then
167  sv%size = this%deferred_size_init
168  else
169  sv%size = this%nrow
170  end if
171 
172  ! allocate array memory for StructVectorType
173  select case (idt%datatype)
174  case ('INTEGER')
175  call this%allocate_int_type(sv)
176  case ('DOUBLE')
177  call this%allocate_dbl_type(sv)
178  case ('STRING', 'KEYWORD')
179  call this%allocate_charstr_type(sv)
180  case ('INTEGER1D')
181  call this%allocate_int1d_type(sv)
182  if (sv%memtype == mtype_int2d) then
183  numcol = sv%intshape
184  end if
185  case ('DOUBLE1D')
186  call this%allocate_dbl1d_type(sv)
187  numcol = sv%intshape
188  case default
189  errmsg = 'IDM unimplemented. StructArray::mem_create_vector &
190  &type='//trim(idt%datatype)
191  call store_error(errmsg, .true.)
192  end select
193 
194  ! set the object in the Struct Array
195  this%struct_vectors(icol) = sv
196  this%numcols(icol) = numcol
197  if (icol == 1) then
198  this%startidx(icol) = 1
199  else
200  this%startidx(icol) = this%startidx(icol - 1) + this%numcols(icol - 1)
201  end if
202  end subroutine mem_create_vector
203 
204  !> @brief Create a metadata-only StructVector for a KEYWORD indicator column
205  !!
206  !! Sets idt, body_start, and head_nbody but allocates no data arrays.
207  !! Used for KEYWORD indicator columns that have been consolidated into the
208  !! SETTING column; these vectors serve only as dispatch-map entries.
209  !<
210  subroutine mem_create_metadata_vector(this, icol, idt, body_start, head_nbody)
211  class(structarraytype) :: this !< StructArrayType
212  integer(I4B), intent(in) :: icol !< column index
213  type(inputparamdefinitiontype), pointer :: idt !< input definition (for tagname lookup)
214  integer(I4B), intent(in) :: body_start !< SA column index of the head's first body (0 if none)
215  integer(I4B), intent(in) :: head_nbody !< number of body columns following the head
216  type(structvectortype) :: sv
217 
218  sv%idt => idt
219  sv%icol = icol
220  sv%body_start = body_start
221  sv%head_nbody = head_nbody
222  sv%memtype = mtype_undef
223  sv%size = 0
224 
225  this%struct_vectors(icol) = sv
226  this%numcols(icol) = 0
227  if (icol == 1) then
228  this%startidx(icol) = 1
229  else
230  this%startidx(icol) = this%startidx(icol - 1) + this%numcols(icol - 1)
231  end if
232  end subroutine mem_create_metadata_vector
233 
234  !> @brief Derive a TS-capable setting's raw input array name, distinct
235  !! from its persistent array (allocated under mf6varname itself).
236  !<
237  function idm_input_varname(idt) result(varname)
238  type(inputparamdefinitiontype), intent(in) :: idt
239  character(len=LENVARNAME) :: varname
240 
241  if (len_trim(idt%mf6varname) + len(idm_input_suffix) > lenvarname) then
242  write (errmsg, '(*(G0))') &
243  'IDM mf6internal "', trim(idt%mf6varname), '" exceeds ', &
244  lenvarname - len(idm_input_suffix), ' characters (IDM reserves ', &
245  len(idm_input_suffix), ' for the internal input-array suffix "', &
246  idm_input_suffix, '").'
247  call store_error(errmsg, terminate=.true.)
248  end if
249  varname = trim(idt%mf6varname)//idm_input_suffix
250  end function idm_input_varname
251 
252  function count(this)
253  class(structarraytype) :: this !< StructArrayType
254  integer(I4B) :: count
255  count = size(this%struct_vectors)
256  end function count
257 
258  subroutine set_pointer(sv, sv_target)
259  type(structvectortype), pointer :: sv
260  type(structvectortype), target :: sv_target
261  sv => sv_target
262  end subroutine set_pointer
263 
264  function get(this, idx) result(sv)
265  class(structarraytype) :: this !< StructArrayType
266  integer(I4B), intent(in) :: idx
267  type(structvectortype), pointer :: sv
268  call set_pointer(sv, this%struct_vectors(idx))
269  end function get
270 
271  !> @brief allocate integer input type
272  !<
273  subroutine allocate_int_type(this, sv)
274  class(structarraytype) :: this !< StructArrayType
275  type(structvectortype), intent(inout) :: sv
276  integer(I4B), dimension(:), pointer, contiguous :: int1d
277  integer(I4B) :: j, nrow
278 
279  if (this%deferred_shape) then
280  ! shape not known, allocate locally
281  nrow = this%deferred_size_init
282  allocate (int1d(this%deferred_size_init))
283  else
284  ! shape known, allocate in managed memory
285  nrow = this%nrow
286  call mem_allocate(int1d, this%nrow, sv%idt%mf6varname, this%mempath)
287  end if
288 
289  ! initialize vector values
290  do j = 1, nrow
291  int1d(j) = izero
292  end do
293 
294  sv%memtype = mtype_int
295  sv%int1d => int1d
296  end subroutine allocate_int_type
297 
298  !> @brief allocate double input type
299  !<
300  subroutine allocate_dbl_type(this, sv)
301  class(structarraytype) :: this !< StructArrayType
302  type(structvectortype), intent(inout) :: sv
303  real(DP), dimension(:), pointer, contiguous :: dbl1d
304  character(len=LENVARNAME) :: varname
305  integer(I4B) :: j, nrow
306 
307  varname = sv%idt%mf6varname
308  if (len_trim(sv%varname_override) > 0) varname = sv%varname_override
309 
310  if (this%deferred_shape) then
311  ! shape not known, allocate locally
312  nrow = this%deferred_size_init
313  allocate (dbl1d(this%deferred_size_init))
314  else
315  ! shape known, allocate in managed memory
316  nrow = this%nrow
317  call mem_allocate(dbl1d, this%nrow, varname, this%mempath)
318  end if
319 
320  ! initialize
321  do j = 1, nrow
322  dbl1d(j) = dzero
323  end do
324 
325  sv%memtype = mtype_dbl
326  sv%dbl1d => dbl1d
327  end subroutine allocate_dbl_type
328 
329  !> @brief allocate charstr input type
330  !<
331  subroutine allocate_charstr_type(this, sv)
332  class(structarraytype) :: this !< StructArrayType
333  type(structvectortype), intent(inout) :: sv
334  type(characterstringtype), dimension(:), pointer, contiguous :: charstr1d
335  integer(I4B) :: j, nrow
336 
337  if (this%deferred_shape) then
338  ! shape not known, allocate locally
339  nrow = this%deferred_size_init
340  allocate (charstr1d(this%deferred_size_init))
341  else
342  ! shape known, allocate in managed memory
343  nrow = this%nrow
344  call mem_allocate(charstr1d, sv%charlen, this%nrow, &
345  sv%idt%mf6varname, this%mempath)
346  end if
347 
348  do j = 1, nrow
349  charstr1d(j) = ''
350  end do
351 
352  sv%memtype = mtype_str
353  sv%charstr1d => charstr1d
354  end subroutine allocate_charstr_type
355 
356  !> @brief allocate int1d input type
357  !<
358  subroutine allocate_int1d_type(this, sv)
359  use constantsmodule, only: lenmodelname
362  class(structarraytype) :: this !< StructArrayType
363  type(structvectortype), intent(inout) :: sv
364  integer(I4B), dimension(:, :), pointer, contiguous :: int2d
365  type(stlvecint), pointer :: intvector
366  type(stlvecint), pointer :: intvector_ia
367  integer(I4B), pointer :: ncelldim, exgid
368  character(len=LENMEMPATH) :: input_mempath
369  character(len=LENMODELNAME) :: mname
370  type(characterstringtype), dimension(:), contiguous, &
371  pointer :: charstr1d
372  integer(I4B) :: nrow, n, m
373 
374  if (sv%idt%shape == 'NCELLDIM') then
375  ! if EXCHANGE set to NCELLDIM of appropriate model
376  if (this%mf6_input%component_type == 'EXG') then
377  ! set pointer to EXGID
378  call mem_setptr(exgid, 'EXGID', this%mf6_input%mempath)
379  ! set pointer to appropriate exchange model array
380  input_mempath = create_mem_path('SIM', 'NAM', idm_context)
381  if (sv%idt%tagname == 'CELLIDM1') then
382  call mem_setptr(charstr1d, 'EXGMNAMEA', input_mempath)
383  else if (sv%idt%tagname == 'CELLIDM2') then
384  call mem_setptr(charstr1d, 'EXGMNAMEB', input_mempath)
385  end if
386 
387  ! set the model name
388  mname = charstr1d(exgid)
389 
390  ! set ncelldim pointer
391  input_mempath = create_mem_path(component=mname, context=idm_context)
392  call mem_setptr(ncelldim, sv%idt%shape, input_mempath)
393  else
394  call mem_setptr(ncelldim, sv%idt%shape, this%component_mempath)
395  end if
396 
397  if (this%deferred_shape) then
398  ! shape not known, allocate locally
399  nrow = this%deferred_size_init
400  allocate (int2d(ncelldim, this%deferred_size_init))
401  else
402  ! shape known, allocate in managed memory
403  nrow = this%nrow
404  call mem_allocate(int2d, ncelldim, this%nrow, &
405  sv%idt%mf6varname, this%mempath)
406  end if
407 
408  ! initialize
409  do m = 1, nrow
410  do n = 1, ncelldim
411  int2d(n, m) = izero
412  end do
413  end do
414 
415  sv%memtype = mtype_int2d
416  sv%int2d => int2d
417  sv%intshape => ncelldim
418  else
419  ! allocate intvector object
420  allocate (intvector)
421  ! initialize STLVecInt
422  call intvector%init()
423  sv%memtype = mtype_intvec
424  sv%intvector => intvector
425  sv%size = -1
426  ! seed the CSR row-offset vector (ia(1) = 1)
427  allocate (intvector_ia)
428  call intvector_ia%init()
429  call intvector_ia%push_back(1)
430  sv%intvector_ia => intvector_ia
431  if (trim(sv%idt%shape) == ':') then
432  ! ragged column: width unknown until read_param reads to end of
433  ! record; intvector_shape stays unassociated
434  sv%intvector_ragged = .true.
435  else
436  ! set pointer to dynamic shape
437  call mem_setptr(sv%intvector_shape, sv%idt%shape, this%mempath)
438  end if
439  end if
440  end subroutine allocate_int1d_type
441 
442  !> @brief allocate dbl1d input type
443  !<
444  subroutine allocate_dbl1d_type(this, sv)
445  use memorymanagermodule, only: get_isize
446  class(structarraytype) :: this !< StructArrayType
447  type(structvectortype), intent(inout) :: sv
448  real(DP), dimension(:, :), pointer, contiguous :: dbl2d
449  integer(I4B), pointer :: naux, nseg, nseg_1
450  integer(I4B) :: nseg1_isize, n, m
451 
452  if (sv%idt%shape == 'NAUX') then
453  call mem_setptr(naux, sv%idt%shape, this%mempath)
454 
455  if (this%deferred_shape) then
456  ! deferred: plain allocate so check_reallocate can grow it safely
457  allocate (dbl2d(naux, sv%size))
458  else
459  call mem_allocate(dbl2d, naux, this%nrow, sv%idt%mf6varname, this%mempath)
460  end if
461 
462  ! initialize
463  do m = 1, sv%size
464  do n = 1, naux
465  dbl2d(n, m) = dzero
466  end do
467  end do
468 
469  sv%memtype = mtype_dbl2d
470  sv%dbl2d => dbl2d
471  sv%intshape => naux
472  else if (sv%idt%shape == 'NSEG-1') then
473  call mem_setptr(nseg, 'NSEG', this%mempath)
474  call get_isize('NSEG_1', this%mempath, nseg1_isize)
475 
476  if (nseg1_isize < 0) then
477  call mem_allocate(nseg_1, 'NSEG_1', this%mempath)
478  nseg_1 = nseg - 1
479  else
480  call mem_setptr(nseg_1, 'NSEG_1', this%mempath)
481  end if
482 
483  if (this%deferred_shape) then
484  ! deferred: plain allocate so check_reallocate can grow it safely
485  allocate (dbl2d(nseg_1, sv%size))
486  else
487  call mem_allocate(dbl2d, nseg_1, sv%size, sv%idt%mf6varname, this%mempath)
488  end if
489 
490  ! initialize
491  do m = 1, sv%size
492  do n = 1, nseg_1
493  dbl2d(n, m) = dzero
494  end do
495  end do
496 
497  sv%memtype = mtype_dbl2d
498  sv%dbl2d => dbl2d
499  sv%intshape => nseg_1
500  else
501  errmsg = 'IDM unimplemented. StructArray::allocate_dbl1d_type &
502  & unsupported shape "'//trim(sv%idt%shape)//'".'
503  call store_error(errmsg, terminate=.true.)
504  end if
505  end subroutine allocate_dbl1d_type
506 
507  subroutine load_deferred_vector(this, icol)
508  use memorymanagermodule, only: get_isize
509  class(structarraytype) :: this !< StructArrayType
510  integer(I4B), intent(in) :: icol
511  integer(I4B) :: i, j, isize
512  integer(I4B), dimension(:), pointer, contiguous :: p_int1d
513  integer(I4B), dimension(:, :), pointer, contiguous :: p_int2d
514  real(DP), dimension(:), pointer, contiguous :: p_dbl1d
515  real(DP), dimension(:, :), pointer, contiguous :: p_dbl2d
516  type(characterstringtype), dimension(:), pointer, contiguous :: p_charstr1d
517  character(len=LENVARNAME) :: varname
518  logical(LGP) :: overwrite
519 
520  overwrite = .true.
521  if (this%struct_vectors(icol)%idt%blockname == 'SOLUTIONGROUP') &
522  overwrite = .false.
523 
524  ! set varname
525  varname = this%struct_vectors(icol)%idt%mf6varname
526  ! check if already mem managed variable
527  call get_isize(varname, this%mempath, isize)
528 
529  ! allocate and load based on memtype
530  select case (this%struct_vectors(icol)%memtype)
531  case (mtype_int)
532  if (isize > -1) then
533  ! variable exists, reallocate and append
534  call mem_setptr(p_int1d, varname, this%mempath)
535 
536  if (overwrite) then
537  ! overwrite existing array
538  if (this%nrow > isize) then
539  ! reallocate
540  call mem_reallocate(p_int1d, this%nrow, varname, this%mempath)
541  end if
542 
543  ! write new data
544  do i = 1, this%nrow
545  p_int1d(i) = this%struct_vectors(icol)%int1d(i)
546  end do
547 
548  if (isize > this%nrow) then
549  ! initialize excess space
550  do i = this%nrow + 1, isize
551  p_int1d(i) = izero
552  end do
553  end if
554  else
555  ! reallocate to new size
556  call mem_reallocate(p_int1d, this%nrow + isize, varname, this%mempath)
557 
558  ! write new data after existing
559  do i = 1, this%nrow
560  p_int1d(isize + i) = this%struct_vectors(icol)%int1d(i)
561  end do
562  end if
563  else
564  ! allocate memory manager vector
565  call mem_allocate(p_int1d, this%nrow, varname, this%mempath)
566 
567  ! load local vector to managed memory
568  do i = 1, this%nrow
569  p_int1d(i) = this%struct_vectors(icol)%int1d(i)
570  end do
571  end if
572 
573  ! deallocate local memory
574  deallocate (this%struct_vectors(icol)%int1d)
575 
576  ! update structvector
577  this%struct_vectors(icol)%int1d => p_int1d
578  this%struct_vectors(icol)%size = this%nrow
579  case (mtype_dbl)
580  if (isize > -1) then
581  call mem_setptr(p_dbl1d, varname, this%mempath)
582 
583  if (overwrite) then
584  if (this%nrow > isize) then
585  call mem_reallocate(p_dbl1d, this%nrow, varname, this%mempath)
586  end if
587 
588  do i = 1, this%nrow
589  p_dbl1d(i) = this%struct_vectors(icol)%dbl1d(i)
590  end do
591 
592  if (isize > this%nrow) then
593  do i = this%nrow + 1, isize
594  p_dbl1d(i) = dzero
595  end do
596  end if
597  else
598  call mem_reallocate(p_dbl1d, this%nrow + isize, varname, &
599  this%mempath)
600  do i = 1, this%nrow
601  p_dbl1d(isize + i) = this%struct_vectors(icol)%dbl1d(i)
602  end do
603  end if
604  else
605  call mem_allocate(p_dbl1d, this%nrow, varname, this%mempath)
606 
607  do i = 1, this%nrow
608  p_dbl1d(i) = this%struct_vectors(icol)%dbl1d(i)
609  end do
610  end if
611 
612  deallocate (this%struct_vectors(icol)%dbl1d)
613 
614  this%struct_vectors(icol)%dbl1d => p_dbl1d
615  this%struct_vectors(icol)%size = this%nrow
616  !
617  case (mtype_str)
618  if (isize > -1) then
619  call mem_setptr(p_charstr1d, varname, this%mempath)
620 
621  if (overwrite) then
622  if (this%nrow > isize) then
623  call mem_reallocate(p_charstr1d, this%struct_vectors(icol)%charlen, &
624  this%nrow, varname, this%mempath)
625  end if
626 
627  do i = 1, this%nrow
628  p_charstr1d(i) = this%struct_vectors(icol)%charstr1d(i)
629  end do
630 
631  if (isize > this%nrow) then
632  do i = this%nrow + 1, isize
633  p_charstr1d(i) = ''
634  end do
635  end if
636  else
637  call mem_reallocate(p_charstr1d, this%struct_vectors(icol)%charlen, &
638  this%nrow + isize, varname, this%mempath)
639  do i = 1, this%nrow
640  p_charstr1d(isize + i) = this%struct_vectors(icol)%charstr1d(i)
641  end do
642  end if
643  else
644  call mem_allocate(p_charstr1d, this%struct_vectors(icol)%charlen, &
645  this%nrow, varname, this%mempath)
646  do i = 1, this%nrow
647  p_charstr1d(i) = this%struct_vectors(icol)%charstr1d(i)
648  call this%struct_vectors(icol)%charstr1d(i)%destroy()
649  end do
650  end if
651 
652  deallocate (this%struct_vectors(icol)%charstr1d)
653 
654  this%struct_vectors(icol)%charstr1d => p_charstr1d
655  this%struct_vectors(icol)%size = this%nrow
656  case (mtype_intvec) ! intvector reallocate unimplemented
657  errmsg = 'StructArray::load_deferred_vector &
658  &intvector reallocate unimplemented.'
659  call store_error(errmsg, terminate=.true.)
660  case (mtype_int2d)
661  if (isize > -1) then
662  errmsg = 'StructArray::load_deferred_vector &
663  &int2d reallocate unimplemented.'
664  call store_error(errmsg, terminate=.true.)
665  else
666  call mem_allocate(p_int2d, this%struct_vectors(icol)%intshape, &
667  this%nrow, varname, this%mempath)
668  do i = 1, this%nrow
669  do j = 1, this%struct_vectors(icol)%intshape
670  p_int2d(j, i) = this%struct_vectors(icol)%int2d(j, i)
671  end do
672  end do
673  end if
674 
675  deallocate (this%struct_vectors(icol)%int2d)
676 
677  this%struct_vectors(icol)%int2d => p_int2d
678  this%struct_vectors(icol)%size = this%nrow
679  case (mtype_dbl2d)
680  if (isize > -1) then
681  errmsg = 'StructArray::load_deferred_vector &
682  &dbl2d reallocate unimplemented.'
683  call store_error(errmsg, terminate=.true.)
684  else
685  call mem_allocate(p_dbl2d, this%struct_vectors(icol)%intshape, &
686  this%nrow, varname, this%mempath)
687  do i = 1, this%nrow
688  do j = 1, this%struct_vectors(icol)%intshape
689  p_dbl2d(j, i) = this%struct_vectors(icol)%dbl2d(j, i)
690  end do
691  end do
692  end if
693 
694  deallocate (this%struct_vectors(icol)%dbl2d)
695 
696  this%struct_vectors(icol)%dbl2d => p_dbl2d
697  this%struct_vectors(icol)%size = this%nrow
698  case default
699  end select
700  end subroutine load_deferred_vector
701 
702  !> @brief load deferred vectors into managed memory
703  !<
704  subroutine memload_vectors(this)
705  class(structarraytype) :: this !< StructArrayType
706  integer(I4B) :: icol, j
707  integer(I4B), dimension(:), pointer, contiguous :: p_intvector
708  integer(I4B), dimension(:), pointer, contiguous :: p_intvector_ia
709  character(len=LENVARNAME) :: varname
710 
711  do icol = 1, this%ncol
712  ! set varname
713  varname = this%struct_vectors(icol)%idt%mf6varname
714 
715  if (this%struct_vectors(icol)%memtype == mtype_intvec) then
716  ! intvectors always need to be loaded
717  ! size intvector to number of values read
718  call this%struct_vectors(icol)%intvector%shrink_to_fit()
719 
720  ! allocate memory manager vector
721  call mem_allocate(p_intvector, &
722  this%struct_vectors(icol)%intvector%size, &
723  varname, this%mempath)
724 
725  ! load local vector to managed memory
726  do j = 1, this%struct_vectors(icol)%intvector%size
727  p_intvector(j) = this%struct_vectors(icol)%intvector%at(j)
728  end do
729 
730  ! cleanup local memory
731  call this%struct_vectors(icol)%intvector%destroy()
732  deallocate (this%struct_vectors(icol)%intvector)
733  nullify (this%struct_vectors(icol)%intvector_shape)
734 
735  ! publish the parallel CSR-style row-offset vector as <TAGNAME>_IA
736  call mem_allocate(p_intvector_ia, &
737  this%struct_vectors(icol)%intvector_ia%size, &
738  trim(varname)//'_IA', this%mempath)
739  do j = 1, this%struct_vectors(icol)%intvector_ia%size
740  p_intvector_ia(j) = this%struct_vectors(icol)%intvector_ia%at(j)
741  end do
742  call this%struct_vectors(icol)%intvector_ia%destroy()
743  deallocate (this%struct_vectors(icol)%intvector_ia)
744  nullify (this%struct_vectors(icol)%intvector_ia)
745  else if (this%deferred_shape) then
746  ! load as shape wasn't known
747  call this%load_deferred_vector(icol)
748  end if
749  end do
750  end subroutine memload_vectors
751 
752  !> @brief log information about the StructArrayType
753  !<
754  subroutine log_structarray_vars(this, iout)
755  class(structarraytype) :: this !< StructArrayType
756  integer(I4B), intent(in) :: iout !< unit number for output
757  integer(I4B) :: j, nts
758  integer(I4B), dimension(:), pointer, contiguous :: int1d
759  character(len=LINELENGTH) :: ts_count_str
760 
761  ! idm variable logging
762  do j = 1, this%ncol
763  ! log based on memtype
764  select case (this%struct_vectors(j)%memtype)
765  case (mtype_int)
766  call idm_log_var(this%struct_vectors(j)%int1d, &
767  this%struct_vectors(j)%idt%tagname, &
768  this%mempath, iout)
769  case (mtype_dbl)
770  nts = this%struct_vectors(j)%ts_strlocs%count()
771  if (nts > 0) then
772  write (ts_count_str, '(i0, " time-series bound entries")') nts
773  call idm_log_var(this%struct_vectors(j)%idt%tagname, &
774  this%mempath, iout, .false., trim(ts_count_str))
775  else
776  call idm_log_var(this%struct_vectors(j)%dbl1d, &
777  this%struct_vectors(j)%idt%tagname, &
778  this%mempath, iout)
779  end if
780  case (mtype_intvec)
781  call mem_setptr(int1d, this%struct_vectors(j)%idt%mf6varname, &
782  this%mempath)
783  call idm_log_var(int1d, this%struct_vectors(j)%idt%tagname, &
784  this%mempath, iout)
785  case (mtype_int2d)
786  call idm_log_var(this%struct_vectors(j)%int2d, &
787  this%struct_vectors(j)%idt%tagname, &
788  this%mempath, iout)
789  case (mtype_dbl2d)
790  nts = this%struct_vectors(j)%ts_strlocs%count()
791  if (nts > 0) then
792  write (ts_count_str, '(i0, " time-series bound entries")') nts
793  call idm_log_var(this%struct_vectors(j)%idt%tagname, &
794  this%mempath, iout, .false., trim(ts_count_str))
795  else
796  call idm_log_var(this%struct_vectors(j)%dbl2d, &
797  this%struct_vectors(j)%idt%tagname, &
798  this%mempath, iout)
799  end if
800  end select
801  end do
802  end subroutine log_structarray_vars
803 
804  !> @brief reallocate local memory for deferred vectors if necessary
805  !<
806  subroutine check_reallocate(this)
807  class(structarraytype) :: this !< StructArrayType
808  integer(I4B) :: i, j, k, newsize
809  integer(I4B), dimension(:), pointer, contiguous :: p_int1d
810  integer(I4B), dimension(:, :), pointer, contiguous :: p_int2d
811  real(DP), dimension(:), pointer, contiguous :: p_dbl1d
812  real(DP), dimension(:, :), pointer, contiguous :: p_dbl2d
813  type(characterstringtype), dimension(:), pointer, contiguous :: p_charstr1d
814  integer(I4B) :: reallocate_mult
815 
816  ! set growth rate
817  reallocate_mult = 2
818 
819  do j = 1, this%ncol
820  ! reallocate based on memtype
821  select case (this%struct_vectors(j)%memtype)
822  case (mtype_int)
823  ! check if more space needed
824  if (this%nrow > this%struct_vectors(j)%size) then
825  ! calculate new size
826  newsize = this%struct_vectors(j)%size * reallocate_mult
827  ! allocate new vector
828  allocate (p_int1d(newsize))
829 
830  ! copy from old to new
831  do i = 1, this%struct_vectors(j)%size
832  p_int1d(i) = this%struct_vectors(j)%int1d(i)
833  end do
834 
835  ! deallocate old vector
836  deallocate (this%struct_vectors(j)%int1d)
837 
838  ! update struct array object
839  this%struct_vectors(j)%int1d => p_int1d
840  this%struct_vectors(j)%size = newsize
841  end if
842  case (mtype_dbl)
843  if (this%nrow > this%struct_vectors(j)%size) then
844  newsize = this%struct_vectors(j)%size * reallocate_mult
845  allocate (p_dbl1d(newsize))
846 
847  do i = 1, this%struct_vectors(j)%size
848  p_dbl1d(i) = this%struct_vectors(j)%dbl1d(i)
849  end do
850 
851  deallocate (this%struct_vectors(j)%dbl1d)
852 
853  this%struct_vectors(j)%dbl1d => p_dbl1d
854  this%struct_vectors(j)%size = newsize
855  end if
856  !
857  case (mtype_str)
858  if (this%nrow > this%struct_vectors(j)%size) then
859  newsize = this%struct_vectors(j)%size * reallocate_mult
860  allocate (p_charstr1d(newsize))
861 
862  do i = 1, this%struct_vectors(j)%size
863  p_charstr1d(i) = this%struct_vectors(j)%charstr1d(i)
864  call this%struct_vectors(j)%charstr1d(i)%destroy()
865  end do
866 
867  deallocate (this%struct_vectors(j)%charstr1d)
868 
869  this%struct_vectors(j)%charstr1d => p_charstr1d
870  this%struct_vectors(j)%size = newsize
871  end if
872  case (mtype_int2d)
873  if (this%nrow > this%struct_vectors(j)%size) then
874  newsize = this%struct_vectors(j)%size * reallocate_mult
875  allocate (p_int2d(this%struct_vectors(j)%intshape, newsize))
876 
877  do i = 1, this%struct_vectors(j)%size
878  do k = 1, this%struct_vectors(j)%intshape
879  p_int2d(k, i) = this%struct_vectors(j)%int2d(k, i)
880  end do
881  end do
882 
883  deallocate (this%struct_vectors(j)%int2d)
884 
885  this%struct_vectors(j)%int2d => p_int2d
886  this%struct_vectors(j)%size = newsize
887  end if
888  case (mtype_dbl2d)
889  if (this%nrow > this%struct_vectors(j)%size) then
890  newsize = this%struct_vectors(j)%size * reallocate_mult
891  allocate (p_dbl2d(this%struct_vectors(j)%intshape, newsize))
892 
893  do i = 1, this%struct_vectors(j)%size
894  do k = 1, this%struct_vectors(j)%intshape
895  p_dbl2d(k, i) = this%struct_vectors(j)%dbl2d(k, i)
896  end do
897  end do
898 
899  deallocate (this%struct_vectors(j)%dbl2d)
900 
901  this%struct_vectors(j)%dbl2d => p_dbl2d
902  this%struct_vectors(j)%size = newsize
903  end if
904  case (mtype_undef, mtype_intvec)
905  ! metadata-only or unsupported: skip reallocation check
906  case default
907  errmsg = 'IDM unimplemented. StructArray::check_reallocate &
908  &unsupported memtype.'
909  call store_error(errmsg, terminate=.true.)
910  end select
911  end do
912  end subroutine check_reallocate
913 
914  subroutine read_param(this, parser, sv_col, irow, timeseries, iout)
915  use inputoutputmodule, only: upcase
916  class(structarraytype) :: this !< StructArrayType
917  type(blockparsertype), intent(inout) :: parser !< block parser to read from
918  integer(I4B), intent(in) :: sv_col
919  integer(I4B), intent(in) :: irow
920  logical(LGP), intent(in) :: timeseries
921  integer(I4B), intent(in) :: iout !< unit number for output
922  integer(I4B) :: n, intval, numval, icol
923  character(len=LINELENGTH) :: str
924  character(len=:), allocatable :: line
925  logical(LGP) :: preserve_case, success
926 
927  select case (this%struct_vectors(sv_col)%memtype)
928  case (mtype_undef)
929  ! MTYPE_UNDEF vectors are metadata-only (KEYWORD dispatch headers).
930  write (errmsg, '(a,i0)') &
931  'IDM read_param called for MTYPE_UNDEF metadata vector at column ', sv_col
932  call store_error(errmsg, .true.)
933  case (mtype_int)
934  ! if reloadable block and first col, store blocknum
935  if (sv_col == 1 .and. this%blocknum > 0) then
936  ! store blocknum
937  this%struct_vectors(sv_col)%int1d(irow) = this%blocknum
938  else
939  ! read and store int
940  this%struct_vectors(sv_col)%int1d(irow) = parser%GetInteger()
941  end if
942  case (mtype_dbl)
943  if (this%struct_vectors(sv_col)%idt%timeseries .and. timeseries) then
944  call parser%GetString(str)
945  this%struct_vectors(sv_col)%dbl1d(irow) = &
946  this%struct_vectors(sv_col)%read_token(str, this%startidx(sv_col), &
947  1, irow)
948  else if (sv_col == this%ncol .and. &
949  .not. this%struct_vectors(sv_col)%idt%required) then
950  call parser%TryGetDouble(this%struct_vectors(sv_col)%dbl1d(irow), success)
951  if (.not. success) &
952  this%struct_vectors(sv_col)%dbl1d(irow) = dnodata
953  else
954  this%struct_vectors(sv_col)%dbl1d(irow) = parser%GetDouble()
955  end if
956  case (mtype_str)
957  if (this%struct_vectors(sv_col)%idt%shape /= '' .and. &
958  sv_col == this%ncol) then
959  ! last column with any shape: store rest of line
960  call parser%GetRemainingLine(line)
961  this%struct_vectors(sv_col)%charstr1d(irow) = line
962  deallocate (line)
963  else
964  ! read string token; a shape on a non-last column marks something
965  ! other than "rest of line", so it reads the same as unshaped
966  preserve_case = (.not. this%struct_vectors(sv_col)%idt%preserve_case)
967  call parser%GetString(str, preserve_case)
968  this%struct_vectors(sv_col)%charstr1d(irow) = str
969  end if
970  case (mtype_intvec)
971  if (this%struct_vectors(sv_col)%intvector_ragged) then
972  ! read to end of record; consumers compare the actual count read
973  ! against whatever width they independently expect
974  numval = 0
975  success = .true.
976  do while (success)
977  call parser%TryGetInteger(intval, success)
978  if (success) then
979  call this%struct_vectors(sv_col)%intvector%push_back(intval)
980  numval = numval + 1
981  end if
982  end do
983  else
984  ! get shape for this row
985  numval = this%struct_vectors(sv_col)%intvector_shape(irow)
986  ! read and store row values
987  do n = 1, numval
988  intval = parser%GetInteger()
989  call this%struct_vectors(sv_col)%intvector%push_back(intval)
990  end do
991  end if
992  ! extend the CSR-style row-offset vector with this row's running total
993  call this%struct_vectors(sv_col)%intvector_ia%push_back( &
994  this%struct_vectors(sv_col)%intvector_ia%at( &
995  this%struct_vectors(sv_col)%intvector_ia%size) + numval)
996  case (mtype_int2d)
997  ! read and store row values
998  ! handle 'NONE' keyword (SFR unconnected reaches) for backward compatibility
999  if (trim(this%mf6_input%subcomponent_type) == 'SFR' .and. &
1000  this%struct_vectors(sv_col)%idt%tagname == 'CELLID') then
1001  call parser%GetString(str)
1002  call upcase(str)
1003  if (str == 'NONE') then
1004  ! NONE means unconnected; store zeros for all dimensions
1005  do n = 1, this%struct_vectors(sv_col)%intshape
1006  this%struct_vectors(sv_col)%int2d(n, irow) = 0
1007  end do
1008  else
1009  ! first token already read as str; parse as integer and read the rest
1010  read (str, *, iostat=numval) intval
1011  if (numval /= 0) then
1012  write (errmsg, '(a,a,a)') &
1013  'CELLID must be an integer or the keyword NONE, got: ', &
1014  trim(str), '.'
1015  call store_error(errmsg, terminate=.true.)
1016  end if
1017  this%struct_vectors(sv_col)%int2d(1, irow) = intval
1018  do n = 2, this%struct_vectors(sv_col)%intshape
1019  this%struct_vectors(sv_col)%int2d(n, irow) = parser%GetInteger()
1020  end do
1021  end if
1022  else
1023  do n = 1, this%struct_vectors(sv_col)%intshape
1024  this%struct_vectors(sv_col)%int2d(n, irow) = parser%GetInteger()
1025  end do
1026  end if
1027  case (mtype_dbl2d)
1028  ! read and store row values
1029  do n = 1, this%struct_vectors(sv_col)%intshape
1030  if (this%struct_vectors(sv_col)%idt%timeseries .and. timeseries) then
1031  call parser%GetString(str)
1032  icol = this%startidx(sv_col) + n - 1
1033  this%struct_vectors(sv_col)%dbl2d(n, irow) = &
1034  this%struct_vectors(sv_col)%read_token(str, icol, n, irow)
1035  else
1036  this%struct_vectors(sv_col)%dbl2d(n, irow) = parser%GetDouble()
1037  end if
1038  end do
1039  end select
1040  end subroutine read_param
1041 
1042  !> @brief True if idt is AUXVAL: its AUX index is resolved dynamically
1043  !! from a sibling AUXNAME, not feature-indexed by its own mf6varname.
1044  !<
1045  function is_auxval(idt) result(res)
1046  type(inputparamdefinitiontype), intent(in) :: idt
1047  logical(LGP) :: res
1048 
1049  res = (trim(idt%tagname) == 'AUXVAL')
1050  end function is_auxval
1051 
1052  !> @brief Find the AUX array position matching auxname, 0 if none.
1053  !<
1054  function find_auxname_index(auxname, auxnames, naux) result(jj)
1055  character(len=*), intent(in) :: auxname
1056  type(characterstringtype), dimension(:), intent(in) :: auxnames
1057  integer(I4B), intent(in) :: naux
1058  integer(I4B) :: jj
1059  character(len=LINELENGTH) :: thisauxname
1060  integer(I4B) :: n
1061 
1062  jj = 0
1063  do n = 1, naux
1064  thisauxname = auxnames(n)
1065  if (trim(auxname) == trim(thisauxname)) then
1066  jj = n
1067  return
1068  end if
1069  end do
1070  end function find_auxname_index
1071 
1072  !> @brief read from the block parser to fill the StructArrayType
1073  !<
1074  function read_from_parser(this, parser, timeseries, iout, input_name) &
1075  result(irow)
1076  class(structarraytype) :: this !< StructArrayType
1077  type(blockparsertype), intent(inout) :: parser !< block parser to read from
1078  logical(LGP), intent(in) :: timeseries
1079  integer(I4B), intent(in) :: iout !< unit number for output
1080  character(len=*), intent(in) :: input_name !< input filename for error messages
1081  integer(I4B) :: irow, j
1082  logical(LGP) :: endofblock
1083 
1084  ! initialize index irow
1085  irow = 0
1086 
1087  ! reset nrow if deferred shape
1088  if (this%deferred_shape) then
1089  this%nrow = 0
1090  end if
1091 
1092  ! read entire block
1093  do
1094  ! read next line
1095  call parser%GetNextLine(endofblock)
1096  if (endofblock) then
1097  ! no more lines
1098  exit
1099  else if (this%deferred_shape) then
1100  ! shape unknown, track lines read
1101  this%nrow = this%nrow + 1
1102  ! check and update memory allocation
1103  call this%check_reallocate()
1104  end if
1105  ! update irow index
1106  irow = irow + 1
1107  if (this%deferred_shape) then
1108  else
1109  ! check allocated array size against user bound
1110  if (irow > this%nrow) then
1111  write (errmsg, '(a,i0,a)') &
1112  'Input error: line count exceeds input dimension. Expected rows=', &
1113  this%nrow, '.'
1114  call store_error(errmsg)
1115  call store_error_filename(input_name)
1116  end if
1117  end if
1118  ! handle line reads by column memtype
1119  do j = 1, this%ncol
1120  call this%read_param(parser, j, irow, timeseries, iout)
1121  end do
1122  end do
1123  ! if deferred shape vectors were read, load to input path
1124  call this%memload_vectors()
1125  ! log loaded variables
1126  if (iout > 0) then
1127  call this%log_structarray_vars(iout)
1128  end if
1129  end function read_from_parser
1130 
1131  !> @brief read keystring period block into the StructArrayType
1132  !!
1133  !! Each input line contains nleading fixed columns followed by a dispatch
1134  !! keyword. Two dispatch modes are supported:
1135  !!
1136  !! Simple dispatch: keyword matches a DOUBLE/STRING/INTEGER column.
1137  !! One value token is read from the parser into that column.
1138  !! All other member columns receive their sentinel for that row.
1139  !!
1140  !! Compound dispatch: keyword matches a KEYWORD-type column (e.g.
1141  !! FLOWING_WELL). No parser token is read for the KEYWORD column —
1142  !! the dispatch keyword itself is stored. Subsequent non-KEYWORD
1143  !! columns (the compound sub-members, e.g. FWELEV/FWCOND/FWRLEN)
1144  !! are read from the parser in order until the next KEYWORD column
1145  !! or the end of the member columns.
1146  !!
1147  !! No-value KEYWORD dispatch: a KEYWORD column with no sub-members
1148  !! immediately following stores the dispatch keyword and reads
1149  !! nothing further.
1150  !!
1151  !<
1152  function read_from_parser_keystring(this, parser, timeseries, nleading, &
1153  iout, input_name) result(irow)
1154  use inputoutputmodule, only: upcase
1155  use simmodule, only: store_error_filename
1156  class(structarraytype) :: this !< StructArrayType
1157  type(blockparsertype), intent(inout) :: parser !< block parser to read from
1158  logical(LGP), intent(in) :: timeseries !< .true. when TS files loaded
1159  integer(I4B), intent(in) :: nleading !< number of leading (fixed) columns
1160  integer(I4B), intent(in) :: iout !< unit number for output
1161  character(len=*), intent(in) :: input_name !< input filename for error messages
1162  integer(I4B) :: irow
1163  logical(LGP) :: endofblock, is_keyword_dispatch
1164  character(len=LINELENGTH) :: keyword
1165  integer(I4B) :: icol, found_col, last_set_col, setting_icol
1166 
1167  irow = 0
1168 
1169  ! SETTING column is always at nleading+1 when present; detect by tagname
1170  setting_icol = 0
1171  if (nleading + 1 <= this%ncol) then
1172  if (trim(this%struct_vectors(nleading + 1)%idt%tagname) == 'SETTING') then
1173  setting_icol = nleading + 1
1174  end if
1175  end if
1176 
1177  ! reset nrow if deferred shape
1178  if (this%deferred_shape) then
1179  this%nrow = 0
1180  end if
1181 
1182  do
1183  call parser%GetNextLine(endofblock)
1184  if (endofblock) exit
1185 
1186  if (this%deferred_shape) then
1187  this%nrow = this%nrow + 1
1188  call this%check_reallocate()
1189  end if
1190  irow = irow + 1
1191 
1192  ! bounds check for pre-allocated (non-deferred) arrays
1193  if (.not. this%deferred_shape) then
1194  if (irow > this%nrow) then
1195  write (errmsg, '(a,i0,a)') &
1196  'Input error: keystring row count exceeds pre-allocated maxbound=', &
1197  this%nrow, '.'
1198  call store_error(errmsg)
1199  call store_error_filename(input_name)
1200  exit
1201  end if
1202  end if
1203 
1204  ! read leading fixed columns (e.g. CELLID, IFNO)
1205  do icol = 1, nleading
1206  call this%read_param(parser, icol, irow, .false., iout)
1207  end do
1208 
1209  ! read dispatch keyword
1210  call parser%GetString(keyword)
1211  call upcase(keyword)
1212 
1213  ! match the keystring-member column, skipping SETTING and a KEYWORD
1214  ! header's own sub-members (reachable only through their header)
1215  found_col = 0
1216  icol = nleading + 1
1217  do while (icol <= this%ncol)
1218  if (icol == setting_icol) then
1219  icol = icol + 1
1220  cycle
1221  end if
1222  if (trim(this%struct_vectors(icol)%idt%tagname) == trim(keyword)) then
1223  found_col = icol
1224  exit
1225  end if
1226  if (this%struct_vectors(icol)%head_nbody > 0) then
1227  icol = icol + this%struct_vectors(icol)%head_nbody + 1
1228  else
1229  icol = icol + 1
1230  end if
1231  end do
1232 
1233  if (found_col < 1) then
1234  write (errmsg, '(a,a,a)') &
1235  'Unrecognized keystring keyword "', trim(keyword), &
1236  '" in PERIOD block.'
1237  call store_error(errmsg)
1238  call store_error_filename(input_name)
1239  cycle
1240  end if
1241 
1242  ! write mf6varname, not tagname, since SETTING is also used as
1243  ! a memory-manager name and tagname can exceed LENVARNAME
1244  if (setting_icol > 0) then
1245  this%struct_vectors(setting_icol)%charstr1d(irow) = &
1246  trim(this%struct_vectors(found_col)%idt%mf6varname)
1247  end if
1248 
1249  ! determine dispatch mode and set/read matched column(s)
1250  is_keyword_dispatch = &
1251  (this%struct_vectors(found_col)%idt%datatype == 'KEYWORD')
1252 
1253  if (is_keyword_dispatch) then
1254  ! compound/no-value KEYWORD dispatch has no data of its own;
1255  ! read its body members starting at body_start instead
1256  last_set_col = found_col
1257  if (this%struct_vectors(found_col)%body_start > 0) then
1258  do icol = this%struct_vectors(found_col)%body_start, &
1259  this%struct_vectors(found_col)%body_start + &
1260  this%struct_vectors(found_col)%head_nbody - 1
1261  if (icol > this%ncol) exit
1262  call this%read_param(parser, icol, irow, timeseries, iout)
1263  last_set_col = icol
1264  end do
1265  end if
1266  else
1267  ! Simple single-value dispatch: read one value for the matched column
1268  call this%read_param(parser, found_col, irow, timeseries, iout)
1269  last_set_col = found_col
1270  end if
1271 
1272  ! fill sentinels for all non-matched member columns
1273  do icol = nleading + 1, this%ncol
1274  ! skip SETTING column (always written above when present)
1275  if (icol == setting_icol) cycle
1276  ! skip metadata vectors (MTYPE_UNDEF, no allocated data arrays)
1277  if (this%struct_vectors(icol)%memtype == mtype_undef) cycle
1278  if (icol >= found_col .and. icol <= last_set_col) cycle
1279  select case (this%struct_vectors(icol)%memtype)
1280  case (mtype_int) ! INTEGER: use IZERO sentinel
1281  this%struct_vectors(icol)%int1d(irow) = izero
1282  case (mtype_dbl) ! DOUBLE: use DNODATA sentinel
1283  this%struct_vectors(icol)%dbl1d(irow) = dnodata
1284  case (mtype_str) ! STRING or KEYWORD: use empty string sentinel
1285  this%struct_vectors(icol)%charstr1d(irow) = ''
1286  end select
1287  end do
1288  end do
1289 
1290  call this%memload_vectors()
1291 
1292  if (iout > 0) then
1293  call this%log_structarray_vars(iout)
1294  end if
1295  end function read_from_parser_keystring
1296 
1297  !> @brief read from binary input to fill the StructArrayType
1298  !<
1299  function read_from_binary(this, inunit, iout) result(irow)
1300  class(structarraytype) :: this !< StructArrayType
1301  integer(I4B), intent(in) :: inunit !< unit number for binary input
1302  integer(I4B), intent(in) :: iout !< unit number for output
1303  integer(I4B) :: irow, ierr
1304  integer(I4B) :: j, k
1305  integer(I4B) :: intval, numval
1306  character(len=LINELENGTH) :: fname
1307  character(len=*), parameter :: fmtlsterronly = &
1308  "('Error reading LIST from file: ',&
1309  &1x,a,1x,' on UNIT: ',I0)"
1310 
1311  ! set error and exit if deferred shape
1312  if (this%deferred_shape) then
1313  errmsg = 'IDM unimplemented. StructArray::read_from_binary deferred shape &
1314  &not supported for binary inputs.'
1315  call store_error(errmsg, terminate=.true.)
1316  end if
1317  ! initialize
1318  irow = 0
1319  ierr = 0
1320  readloop: do
1321  ! update irow index
1322  irow = irow + 1
1323  ! handle line reads by column memtype
1324  do j = 1, this%ncol
1325  select case (this%struct_vectors(j)%memtype)
1326  case (mtype_int)
1327  read (inunit, iostat=ierr) this%struct_vectors(j)%int1d(irow)
1328  case (mtype_dbl)
1329  read (inunit, iostat=ierr) this%struct_vectors(j)%dbl1d(irow)
1330  case (mtype_str)
1331  errmsg = 'List style binary inputs not supported &
1332  &for text columns, tag='// &
1333  trim(this%struct_vectors(j)%idt%tagname)//'.'
1334  call store_error(errmsg, terminate=.true.)
1335  case (mtype_intvec)
1336  if (this%struct_vectors(j)%intvector_ragged) then
1337  errmsg = 'List style binary inputs not supported for &
1338  &self-sizing (ragged) columns, tag='// &
1339  trim(this%struct_vectors(j)%idt%tagname)//'.'
1340  call store_error(errmsg, terminate=.true.)
1341  end if
1342  ! get shape for this row
1343  numval = this%struct_vectors(j)%intvector_shape(irow)
1344  ! read and store row values
1345  do k = 1, numval
1346  if (ierr == 0) then
1347  read (inunit, iostat=ierr) intval
1348  call this%struct_vectors(j)%intvector%push_back(intval)
1349  end if
1350  end do
1351  case (mtype_int2d)
1352  ! read and store row values
1353  do k = 1, this%struct_vectors(j)%intshape
1354  if (ierr == 0) then
1355  read (inunit, iostat=ierr) this%struct_vectors(j)%int2d(k, irow)
1356  end if
1357  end do
1358  case (mtype_dbl2d)
1359  do k = 1, this%struct_vectors(j)%intshape
1360  if (ierr == 0) then
1361  read (inunit, iostat=ierr) this%struct_vectors(j)%dbl2d(k, irow)
1362  end if
1363  end do
1364  end select
1365 
1366  ! handle error cases
1367  select case (ierr)
1368  case (0)
1369  ! no error
1370  case (:-1)
1371  ! End of block was encountered
1372  irow = irow - 1
1373  exit readloop
1374  case (1:)
1375  ! Error
1376  inquire (unit=inunit, name=fname)
1377  write (errmsg, fmtlsterronly) trim(adjustl(fname)), inunit
1378  call store_error(errmsg, terminate=.true.)
1379  case default
1380  end select
1381  end do
1382  if (irow == this%nrow) exit readloop
1383  end do readloop
1384 
1385  ! Stop if errors were detected
1386  !if (count_errors() > 0) then
1387  ! call store_error_unit(inunit)
1388  !end if
1389 
1390  ! if deferred shape vectors were read, load to input path
1391  call this%memload_vectors()
1392 
1393  ! log loaded variables
1394  if (iout > 0) then
1395  call this%log_structarray_vars(iout)
1396  end if
1397  end function read_from_binary
1398 
1399  !> @brief link time-series strings in this struct array to a tsmanager
1400  !!
1401  !! Iterates over struct vectors that carry deferred TS tokens and
1402  !! registers each with the supplied tsmanager. Handles both BND
1403  !! (MTYPE_DBL / dbl1d) and AUX (MTYPE_DBL2D / dbl2d) columns.
1404  !! Pass auxname_cst only when AUX columns may carry time series.
1405  !!
1406  !<
1407  subroutine ts_update(this, tsmanager, subcomp_name, iprpak, input_name, &
1408  auxname_cst, clear_strlocs, ifno_map)
1409  class(structarraytype), intent(inout) :: this
1410  type(timeseriesmanagertype), pointer, intent(inout) :: tsmanager
1411  character(len=*), intent(in) :: subcomp_name
1412  integer(I4B), intent(in) :: iprpak
1413  character(len=*), intent(in) :: input_name
1414  type(characterstringtype), dimension(:), pointer, intent(in), &
1415  optional :: auxname_cst
1416  logical(LGP), optional, intent(in) :: clear_strlocs !< if .false. strlocs are preserved for re-registration (default .true.)
1417  integer(I4B), dimension(:), optional, intent(in) :: ifno_map !< if present, maps ts_strloc%row (PACKAGEDATA row) to its feature number, used as the TS link row address
1418  type(tsstringloctype), pointer :: ts_strloc
1419  type(timeserieslinktype), pointer :: tsLink
1420  real(DP), pointer :: bndElem
1421  character(len=LENBOUNDNAME) :: boundname
1422  integer(I4B) :: m, n, iboundname, irow
1423  logical(LGP) :: do_clear
1424 
1425  do_clear = .true.
1426  if (present(clear_strlocs)) do_clear = clear_strlocs
1427 
1428  ! find BOUNDNAME column (0 = none)
1429  iboundname = 0
1430  do m = 1, this%ncol
1431  if (this%struct_vectors(m)%idt%mf6varname == 'BOUNDNAME') then
1432  iboundname = m
1433  exit
1434  end if
1435  end do
1436 
1437  do m = 1, this%ncol
1438  if (.not. this%struct_vectors(m)%idt%timeseries) cycle
1439  do n = 1, this%struct_vectors(m)%ts_strlocs%count()
1440  ts_strloc => this%struct_vectors(m)%get_ts_strloc(n)
1441  nullify (tslink)
1442  irow = ts_strloc%row
1443  if (present(ifno_map)) irow = ifno_map(ts_strloc%row)
1444  select case (this%struct_vectors(m)%memtype)
1445  case (mtype_dbl) ! dbl1d (BND)
1446  bndelem => this%struct_vectors(m)%dbl1d(ts_strloc%row)
1447  ! -- JCol=0, consistent with ts_update_indexed, so a stale
1448  ! -- PACKAGEDATA-level link is found and cleared like AUX's
1449  call read_value_or_time_series(ts_strloc%token, irow, &
1450  0, bndelem, &
1451  subcomp_name, 'BND', tsmanager, &
1452  iprpak, tslink)
1453  if (associated(tslink)) then
1454  tslink%Text = this%struct_vectors(m)%idt%mf6varname
1455  if (iboundname > 0) then
1456  boundname = &
1457  this%struct_vectors(iboundname)%charstr1d(ts_strloc%row)
1458  tslink%BndName = boundname
1459  end if
1460  end if
1461  case (mtype_dbl2d) ! dbl2d (AUX)
1462  if (.not. present(auxname_cst)) cycle
1463  if (.not. associated(auxname_cst)) cycle
1464  bndelem => this%struct_vectors(m)%dbl2d(ts_strloc%col, ts_strloc%row)
1465  ! -- JCol is the AUX array position (ts_strloc%col), not the
1466  ! -- row-schema column, so remove_existing_link can find it
1467  call read_value_or_time_series(ts_strloc%token, irow, &
1468  ts_strloc%col, bndelem, &
1469  subcomp_name, 'AUX', tsmanager, &
1470  iprpak, tslink)
1471  if (associated(tslink)) then
1472  tslink%Text = auxname_cst(ts_strloc%col)
1473  if (iboundname > 0) then
1474  boundname = &
1475  this%struct_vectors(iboundname)%charstr1d(ts_strloc%row)
1476  tslink%BndName = boundname
1477  end if
1478  end if
1479  end select
1480  end do
1481  if (do_clear) call this%struct_vectors(m)%clear()
1482  end do
1483 
1484  if (count_errors() > 0) then
1485  call store_error_filename(input_name)
1486  end if
1487  end subroutine ts_update
1488 
1489  !> @brief Shared dedup-aware TS resolution for one PERIOD-block column:
1490  !! TS-linked rows resolve via their preserved token, remaining literal
1491  !! rows via remove_existing_link, then clear(). naux=1 (via both rank
1492  !! remaps in ts_update_indexed) covers the single-column BND case.
1493  !<
1494  subroutine ts_update_dedup(this, icol, tsmanager, subcomp_name, iprpak, &
1495  nrows, addr_map, category, names, featarr2d, &
1496  raw2d)
1497  class(structarraytype), intent(inout) :: this
1498  integer(I4B), intent(in) :: icol
1499  type(timeseriesmanagertype), pointer, intent(inout) :: tsmanager
1500  character(len=*), intent(in) :: subcomp_name
1501  character(len=*), intent(in) :: category !< 'AUX' or 'BND'
1502  integer(I4B), intent(in) :: iprpak
1503  integer(I4B), intent(in) :: nrows !< rows read this period
1504  integer(I4B), dimension(:), intent(in) :: addr_map !< PERIOD row -> feature index
1505  character(len=LENVARNAME), dimension(:), intent(in) :: names !< size naux
1506  real(DP), dimension(:, :), pointer, intent(in) :: featarr2d !< (naux, nfeatures) permanent target
1507  real(DP), dimension(:, :), pointer, intent(in) :: raw2d !< (naux, nrows) raw column source
1508  type(tsstringloctype), pointer :: ts_strloc
1509  real(DP), pointer :: bndElem
1510  logical(LGP) :: found
1511  integer(I4B) :: n, j, k, nts, i, naux
1512  logical(LGP), dimension(:, :), allocatable :: handled2
1513 
1514  naux = size(names)
1515  allocate (handled2(naux, nrows))
1516  handled2 = .false.
1517 
1518  ! time-series-linked rows: resolve via their preserved token
1519  nts = this%struct_vectors(icol)%ts_strlocs%count()
1520  do k = 1, nts
1521  ts_strloc => this%struct_vectors(icol)%get_ts_strloc(k)
1522  i = addr_map(ts_strloc%row)
1523  if (i < 1 .or. i > size(featarr2d, 2)) cycle
1524  bndelem => featarr2d(ts_strloc%col, i)
1526  ts_strloc%token, i, ts_strloc%col, bndelem, subcomp_name, &
1527  category, tsmanager, iprpak, trim(names(ts_strloc%col)))
1528  handled2(ts_strloc%col, ts_strloc%row) = .true.
1529  end do
1530 
1531  ! literal rows: clear any stale link, then assign; DNODATA means
1532  ! unset this row -- leave the value and any link untouched
1533  do n = 1, nrows
1534  i = addr_map(n)
1535  if (i < 1 .or. i > size(featarr2d, 2)) cycle
1536  do j = 1, naux
1537  if (handled2(j, n)) cycle
1538  if (raw2d(j, n) == dnodata) cycle
1539  found = remove_existing_link(tsmanager, i, j, subcomp_name, &
1540  category, trim(names(j)))
1541  featarr2d(j, i) = raw2d(j, n)
1542  end do
1543  end do
1544  deallocate (handled2)
1545 
1546  ! clear strlocs so a later period's parse (different row indexing)
1547  ! doesn't reprocess these against a mismatched addr_map
1548  call this%struct_vectors(icol)%clear()
1549  end subroutine ts_update_dedup
1550 
1551  !> @brief Dedup-aware counterpart to ts_update for one MTYPE_DBL2D
1552  !! (AUX) PERIOD-block column.
1553  !<
1554  subroutine ts_update_adv(this, icol, tsmanager, subcomp_name, iprpak, &
1555  nrows, ifno_map, auxname_cst, featarr2d)
1556  class(structarraytype), intent(inout) :: this
1557  integer(I4B), intent(in) :: icol
1558  type(timeseriesmanagertype), pointer, intent(inout) :: tsmanager
1559  character(len=*), intent(in) :: subcomp_name
1560  integer(I4B), intent(in) :: iprpak
1561  integer(I4B), intent(in) :: nrows !< rows read this period
1562  integer(I4B), dimension(:), intent(in) :: ifno_map !< PERIOD row -> feature index
1563  type(characterstringtype), dimension(:), pointer, intent(in) :: auxname_cst !< AUX column names, indexed 1..naux
1564  real(DP), dimension(:, :), pointer, intent(in) :: featarr2d !< permanent dbl2d (AUX) target
1565  character(len=LENVARNAME) :: names(size(auxname_cst))
1566  integer(I4B) :: j
1567 
1568  do j = 1, size(auxname_cst)
1569  names(j) = auxname_cst(j)
1570  end do
1571  call this%ts_update_dedup(icol, tsmanager, subcomp_name, iprpak, &
1572  nrows, ifno_map, 'AUX', names, featarr2d, &
1573  this%struct_vectors(icol)%dbl2d)
1574  end subroutine ts_update_adv
1575 
1576  !> @brief Dedup-aware resolution of one MTYPE_DBL PERIOD-block column
1577  !! into its permanent array, shared by both loaders. nrows is explicit
1578  !! since the list loader's row_addr is a permanent, maxbound-sized
1579  !! array, not sized to nrows.
1580  !<
1581  subroutine ts_update_indexed(this, icol, tsmanager, subcomp_name, &
1582  iprpak, nrows, row_addr, varname, featarr)
1583  class(structarraytype), intent(inout) :: this
1584  integer(I4B), intent(in) :: icol
1585  type(timeseriesmanagertype), pointer, intent(inout) :: tsmanager
1586  character(len=*), intent(in) :: subcomp_name
1587  integer(I4B), intent(in) :: iprpak
1588  integer(I4B), intent(in) :: nrows !< rows to process this period
1589  integer(I4B), dimension(:), intent(in) :: row_addr
1590  character(len=*), intent(in) :: varname
1591  real(DP), dimension(:), pointer, contiguous, intent(in) :: featarr
1592  real(DP), dimension(:, :), pointer :: featarr2d, raw2d
1593  character(len=LENVARNAME) :: names(1)
1594 
1595  ! naux=1 rank remaps onto the shared 2D core; neither is a copy
1596  featarr2d(1:1, 1:size(featarr)) => featarr
1597  raw2d(1:1, 1:nrows) => this%struct_vectors(icol)%dbl1d(1:nrows)
1598  names(1) = varname
1599  call this%ts_update_dedup(icol, tsmanager, subcomp_name, iprpak, &
1600  nrows, row_addr, 'BND', names, featarr2d, &
1601  raw2d)
1602  end subroutine ts_update_indexed
1603 
1604 end module structarraymodule
This module contains block parser methods.
Definition: BlockParser.f90:7
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 lenmodelname
maximum length of the model name
Definition: Constants.f90:22
real(dp), parameter dnodata
real no data constant
Definition: Constants.f90:95
character(len= *), parameter idm_input_suffix
input array suffix applied when data array shares name
Definition: Constants.f90:135
integer(i4b), parameter lenvarname
maximum length of a variable name
Definition: Constants.f90:17
integer(i4b), parameter lenboundname
maximum length of a bound name
Definition: Constants.f90:36
integer(i4b), parameter izero
integer constant zero
Definition: Constants.f90:51
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
This module contains the Input Data Model Logger Module.
Definition: IdmLogger.f90:7
Input definition module.
subroutine, public upcase(word)
Convert to upper case.
This module defines variable data types.
Definition: kind.f90:8
character(len=lenmempath) function create_mem_path(component, subcomponent, context)
returns the path to the memory object
subroutine, public get_isize(name, mem_path, isize)
@ brief Get the number of elements for this variable
This module contains the ModflowInputModule.
Definition: ModflowInput.f90:9
This module contains simulation methods.
Definition: Sim.f90:10
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
This module contains simulation variables.
Definition: SimVariables.f90:9
character(len=maxcharlen) errmsg
error message string
character(len=linelength) idm_context
This module contains the StructArrayModule.
Definition: StructArray.f90:8
integer(i4b) function count(this)
character(len=lenvarname) function, public idm_input_varname(idt)
Derive a TS-capable setting's raw input array name, distinct from its persistent array (allocated und...
integer(i4b) function read_from_binary(this, inunit, iout)
read from binary input to fill the StructArrayType
subroutine memload_vectors(this)
load deferred vectors into managed memory
integer(i4b) function read_from_parser_keystring(this, parser, timeseries, nleading, iout, input_name)
read keystring period block into the StructArrayType
subroutine set_pointer(sv, sv_target)
logical(lgp) function, public is_auxval(idt)
True if idt is AUXVAL: its AUX index is resolved dynamically from a sibling AUXNAME,...
subroutine allocate_dbl1d_type(this, sv)
allocate dbl1d input type
subroutine ts_update_dedup(this, icol, tsmanager, subcomp_name, iprpak, nrows, addr_map, category, names, featarr2d, raw2d)
Shared dedup-aware TS resolution for one PERIOD-block column: TS-linked rows resolve via their preser...
subroutine mem_create_metadata_vector(this, icol, idt, body_start, head_nbody)
Create a metadata-only StructVector for a KEYWORD indicator column.
subroutine check_reallocate(this)
reallocate local memory for deferred vectors if necessary
integer(i4b) function, public find_auxname_index(auxname, auxnames, naux)
Find the AUX array position matching auxname, 0 if none.
subroutine ts_update_adv(this, icol, tsmanager, subcomp_name, iprpak, nrows, ifno_map, auxname_cst, featarr2d)
Dedup-aware counterpart to ts_update for one MTYPE_DBL2D (AUX) PERIOD-block column.
subroutine load_deferred_vector(this, icol)
subroutine allocate_dbl_type(this, sv)
allocate double input type
subroutine allocate_charstr_type(this, sv)
allocate charstr input type
subroutine ts_update_indexed(this, icol, tsmanager, subcomp_name, iprpak, nrows, row_addr, varname, featarr)
Dedup-aware resolution of one MTYPE_DBL PERIOD-block column into its permanent array,...
subroutine mem_create_vector(this, icol, idt, charlen, varname)
create new vector in StructArrayType
integer(i4b) function read_from_parser(this, parser, timeseries, iout, input_name)
read from the block parser to fill the StructArrayType
subroutine allocate_int_type(this, sv)
allocate integer input type
subroutine ts_update(this, tsmanager, subcomp_name, iprpak, input_name, auxname_cst, clear_strlocs, ifno_map)
link time-series strings in this struct array to a tsmanager
subroutine log_structarray_vars(this, iout)
log information about the StructArrayType
subroutine read_param(this, parser, sv_col, irow, timeseries, iout)
subroutine, public destructstructarray(struct_array)
destructor for a struct_array
subroutine allocate_int1d_type(this, sv)
allocate int1d input type
type(structvectortype) function, pointer get(this, idx)
type(structarraytype) function, pointer, public constructstructarray(mf6_input, ncol, nrow, blocknum, mempath, component_mempath, size_init)
constructor for a struct_array
Definition: StructArray.f90:90
This module contains the StructVectorModule.
Definition: StructVector.f90:7
@, public mtype_intvec
intvector column
@, public mtype_dbl
dbl1d column
@, public mtype_int2d
int2d (NCELLDIM) column
@, public mtype_int
int1d column
@, public mtype_dbl2d
dbl2d (NAUX/NSEG) column
@, public mtype_str
charstr1d column
@, public mtype_undef
undefined memtype
subroutine, public read_value_or_time_series_adv(textInput, ii, jj, bndElem, pkgName, auxOrBnd, tsManager, iprpak, varName)
Call this subroutine from advanced packages to define timeseries link for a variable (varName).
logical function, public remove_existing_link(tsManager, ii, jj, pkgName, auxOrBnd, varName)
Remove an existing timeseries link if it is defined.
subroutine, public read_value_or_time_series(textInput, ii, jj, bndElem, pkgName, auxOrBnd, tsManager, iprpak, tsLink)
Call this subroutine if the time-series link is available or needed.
This class is used to store a single deferred-length character string. It was designed to work in an ...
Definition: CharString.f90:23
Input parameter definition. Describes an input parameter.
derived type for storing input definition for a file
type for structured array
Definition: StructArray.f90:47
derived type for generic vector
derived type which describes time series string field