MODFLOW 6  version 6.9.0.dev0
USGS Modular Hydrologic Model
LoadContext.f90
Go to the documentation of this file.
1 !> @brief This module contains the LoadContextModule
2 !!
3 !! This module creates a load context for IDM generic
4 !! loaders (ListLoadType, LayerArrayLoadType, GridArrayLoadType)
5 !! that supports consistent package side access. It also
6 !! determines in scope parameters for the generic dynamic
7 !! loaders and all structarray based static loads.
8 !!
9 !<
11 
12  use kindmodule, only: dp, i4b, lgp
15  use simvariablesmodule, only: errmsg
20 
21  implicit none
22  private
23  public :: loadcontexttype
24  public :: readstatevartype
25  public :: rsv_name
26  public :: is_keystring_period
27  public :: has_dimensions_block
28  public :: is_cellid_addressed
29 
30  enum, bind(C)
31  enumerator :: load_undef = 0 !< undefined load type
32  enumerator :: list = 1 !< list (structarray) based load
33  enumerator :: layerarray = 2 !< readasarrays load
34  enumerator :: gridarray = 3 !< readarraygrid load
35  enumerator :: keystring = 4 !< basic keystring period block load
36  end enum
37 
38  enum, bind(C)
39  enumerator :: context_undef = 0 !< undefined context type
40  enumerator :: root = 1 !< root context type
41  enumerator :: sim = 2 !< sim context type
42  enumerator :: model = 3 !< model context type
43  enumerator :: modelpkg = 4 !< model package context type
44  enumerator :: stresspkg = 5 !< model stress package context type
45  enumerator :: exchange = 6 !< exchange context type
46  end enum
47 
48  !> @brief Pointer type for read state variable
49  !<
51  integer(I4B), pointer :: invar
52  end type readstatevartype
53 
54  interface setptr
55  module procedure setptr_int, setptr_charstr1d, &
57  end interface setptr
58 
59  !> @brief derived type for boundary package input context
60  !!
61  !! Input Load Context for generic dynamic loaders and
62  !! StructArray based static loads
63  !!
64  !<
66  character(len=LENVARNAME) :: blockname !< load block name
67  character(len=LENVARNAME), allocatable :: named_bound(:) !< dimension variable names to sum for maxbound
68  integer(I4B), pointer :: naux => null() !< number of auxiliary variables
69  integer(I4B), pointer :: maxbound => null() !< value associated with named_bound
70  integer(I4B), pointer :: boundnames => null() !< are bound names optioned
71  integer(I4B), pointer :: iprpak => null() ! print input option
72  integer(I4B), pointer :: nbound => null() !< number of bounds in period
73  integer(I4B), pointer :: ncpl => null() !< ncpl associated with model shape
74  integer(I4B), pointer :: nodes => null() !< nodes associated with model shape
75  integer(I4B) :: loadtype !< enum load type
76  integer(I4B) :: ctxtype !< enum context type
77  integer(I4B) :: nleading = 0 !< count of leading (pre-keystring) columns; LIST packages only
78  logical(LGP) :: readarray !< is this an array based load
79  logical(LGP) :: is_dimensions_scoped = .false. !< .true. for DIMENSIONS-block-paired (e.g. SPC) KEYSTRING loadtype
80  logical(LGP) :: is_cellid_scoped = .false. !< .true. for CELLID-addressed (e.g. TVK/TVS) KEYSTRING loadtype
81  logical(LGP) :: has_setting_dispatch = .false. !< .true. when is_dimensions_scoped .or. is_cellid_scoped
82  type(inputparamdefinitiontype), pointer :: setting_idt => null() !< internal idt for SETTING column
83  type(characterstringtype), dimension(:), pointer, &
84  contiguous :: auxname_cst => null() !< array of auxiliary names
85  type(characterstringtype), dimension(:), pointer, &
86  contiguous :: boundname_cst => null() !< array of bound names
87  real(dp), dimension(:, :), pointer, &
88  contiguous :: auxvar => null() !< auxiliary variable array
89  integer(I4B), dimension(:), pointer, contiguous :: mshape => null() !< model shape
90  character(len=LINELENGTH), dimension(:), allocatable :: params !< in scope param tags
91  type(modflowinputtype) :: mf6_input !< description of input
92  contains
93  procedure :: init
94  procedure :: allocate_scalars
95  procedure :: allocate_arrays
96  procedure :: allocate_param
97  procedure :: tags
98  procedure :: in_scope
99  procedure :: set_params
101  procedure :: rsv_alloc
102  procedure :: destroy
103  end type loadcontexttype
104 
105 contains
106 
107  !> @brief init loader context object
108  !<
109  subroutine init(this, mf6_input, blockname, named_bound)
110  use inputoutputmodule, only: upcase
113  class(loadcontexttype) :: this
114  type(modflowinputtype), intent(in) :: mf6_input
115  character(len=*), optional, intent(in) :: blockname
116  character(len=*), dimension(:), optional, intent(in) :: named_bound
117  type(inputparamdefinitiontype), pointer :: idt
118  integer(I4B) :: n
119 
120  this%mf6_input = mf6_input
121  this%readarray = .false.
122  this%loadtype = load_undef
123  this%ctxtype = context_undef
124 
125  select case (mf6_input%load_scope)
126  case ('ROOT')
127  this%ctxtype = root
128  case ('SIM')
129  if (mf6_input%subcomponent_type == 'NAM') then
130  this%ctxtype = model
131  else if (mf6_input%subcomponent_type == 'TDIS' .or. &
132  mf6_input%subcomponent_type == 'HPC') then
133  this%ctxtype = sim
134  else if (mf6_input%component_type == 'EXG') then
135  this%ctxtype = exchange
136  end if
137  case ('MODEL')
138  if (mf6_input%subcomponent_type == 'OC' .or. &
139  mf6_input%subcomponent_type == 'STO') then
140  this%ctxtype = modelpkg
141  else
142  this%ctxtype = stresspkg
143  end if
144  case default
145  end select
146 
147  if (this%ctxtype == context_undef) then
148  errmsg = 'LoadContext unidentified context for mempath: '// &
149  trim(mf6_input%mempath)
150  call store_error(errmsg, .true.)
151  end if
152 
153  if (present(blockname)) then
154  this%blockname = blockname
155  call upcase(this%blockname)
156  else
157  this%blockname = 'PERIOD'
158  end if
159 
160  if (present(named_bound)) then
161  allocate (this%named_bound(size(named_bound)))
162  do n = 1, size(named_bound)
163  this%named_bound(n) = named_bound(n)
164  call upcase(this%named_bound(n))
165  end do
166  else
167  allocate (this%named_bound(1))
168  this%named_bound(1) = 'MAXBOUND'
169  end if
170 
171  ! determine aggregate load type
172  do n = 1, size(mf6_input%block_dfns)
173  if (mf6_input%block_dfns(n)%blockname == this%blockname) then
174  if (mf6_input%block_dfns(n)%aggregate) then
175  if (this%blockname == 'PERIOD' .and. &
176  is_keystring_period(mf6_input)) then
177  this%loadtype = keystring
178  else
179  this%loadtype = list
180  end if
181  exit
182  end if
183  end if
184  end do
185 
186  ! classify KEYSTRING subtypes needing sticky PERIOD-setting persistence:
187  ! DIMENSIONS-block-paired (e.g. SPC) and CELLID-addressed (e.g. TVK/TVS)
188  if (this%loadtype == keystring) then
189  this%is_dimensions_scoped = has_dimensions_block(mf6_input)
190  this%is_cellid_scoped = is_cellid_addressed(mf6_input)
191  this%has_setting_dispatch = &
192  this%is_dimensions_scoped .or. this%is_cellid_scoped
193  if (this%has_setting_dispatch) then
194  this%setting_idt => &
195  idt_default(mf6_input%component_type, mf6_input%subcomponent_type, &
196  'PERIOD', 'SETTING', 'SETTING', 'STRING')
197  end if
198  end if
199 
200  ! determine if array based load
201  if (this%loadtype == load_undef) then
202  do n = 1, size(mf6_input%param_dfns)
203  idt => mf6_input%param_dfns(n)
204  if (idt%blockname == 'OPTIONS') then
205  select case (idt%tagname)
206  case ('READASARRAYS')
207  this%loadtype = layerarray
208  this%readarray = .true.
209  case ('READARRAYGRID')
210  this%loadtype = gridarray
211  this%readarray = .true.
212  case default
213  ! no-op
214  end select
215  end if
216  end do
217  end if
218 
219  ! set in scope params for load
220  call this%set_params()
221 
222  ! allocate load context scalars
223  call this%allocate_scalars()
224  end subroutine init
225 
226  !> @brief allocate scalars
227  !<
228  subroutine allocate_scalars(this)
232  class(loadcontexttype) :: this
233  type(inputparamdefinitiontype), pointer :: aidt, ks_aidt
234  character(len=LINELENGTH), allocatable :: cols(:), ks_cols(:)
235  integer(I4B) :: nmembers, ncol, isize
236  integer(I4B), pointer :: maxbound_ptr
237 
238  if (this%ctxtype == exchange .or. &
239  this%ctxtype == modelpkg .or. &
240  this%ctxtype == stresspkg) then
241 
242  call setptr(this%nbound, 'NBOUND', this%mf6_input%mempath)
243  call setval(this%naux, 'NAUX', this%mf6_input%mempath)
244  call setval(this%ncpl, 'NCPL', this%mf6_input%mempath)
245  call setval(this%nodes, 'NODES', this%mf6_input%mempath)
246  call setval(this%boundnames, 'BOUNDNAMES', this%mf6_input%mempath)
247  call setval(this%iprpak, 'IPRPAK', this%mf6_input%mempath)
248 
249  ! resolve maxbound: sum all named_bound variable values
250  allocate (this%maxbound)
251  this%maxbound = 0
252  call sum_named_bounds(this%named_bound, this%mf6_input%mempath, &
253  this%maxbound)
254  ! fallback: try MAXBOUND directly when named_bound tokens yield nothing
255  if (this%maxbound == 0) then
256  call get_isize('MAXBOUND', this%mf6_input%mempath, isize)
257  if (isize > -1) then
258  call mem_setptr(maxbound_ptr, 'MAXBOUND', this%mf6_input%mempath)
259  this%maxbound = maxbound_ptr
260  nullify (maxbound_ptr)
261  end if
262  end if
263 
264  ! reset nbound
265  this%nbound = 0
266  end if
267 
268  if (this%ctxtype == stresspkg .and. &
269  this%blockname == 'PERIOD') then
270  call mem_setptr(this%mshape, 'MODEL_SHAPE', &
271  this%mf6_input%component_mempath)
272 
273  if (this%ncpl == 0) then
274  if (size(this%mshape) == 2) then
275  this%ncpl = this%mshape(2)
276  else if (size(this%mshape) == 3) then
277  this%ncpl = this%mshape(2) * this%mshape(3)
278  end if
279  end if
280 
281  if (this%nodes == 0) this%nodes = product(this%mshape)
282 
283  ! scale maxbound by keystring member count; fall back to nodes * nmembers
284  ! when no DIMENSIONS block is present (e.g. TVK/TVS)
285  if (this%loadtype == keystring) then
286  ! count members from the KEYSTRING aggregate type definition, which
287  ! names exactly the dispatchable members
288  nmembers = 0
289  aidt => get_aggregate_definition_type(this%mf6_input%aggregate_dfns, &
290  this%mf6_input%component_type, &
291  this%mf6_input%subcomponent_type, &
292  'PERIOD')
293  call idt_parse_rectype(aidt, cols, ncol)
294  ks_aidt => find_setting_aggregate(this%mf6_input, cols, ncol)
295  if (associated(ks_aidt)) then
296  call idt_parse_rectype(ks_aidt, ks_cols, nmembers)
297  end if
298  if (allocated(cols)) deallocate (cols)
299  if (allocated(ks_cols)) deallocate (ks_cols)
300  if (this%maxbound == 0) then
301  this%maxbound = this%nodes * nmembers
302  else if (this%is_dimensions_scoped .and. nmembers > 0) then
303  ! DIMENSIONS-block-paired (e.g. SPC): scale the raw feature-count
304  ! maxbound by member count; resolve_nfeatures divides it back out
305  this%maxbound = this%maxbound * nmembers
306  end if
307  end if
308  end if
309  end subroutine allocate_scalars
310 
311  !> @brief allocate arrays
312  !!
313  !! call this routine after input parameters have been allocated,
314  !! e.g. after load_params() with create has been called for array
315  !! based loaders or after all mem_create_vector() calls have
316  !! been made for list based load.
317  !!
318  !<
319  subroutine allocate_arrays(this)
321  class(loadcontexttype) :: this
322  integer(I4B), dimension(:, :), pointer, contiguous :: cellid
323  integer(I4B), dimension(:), pointer, contiguous :: nodeulist
324 
325  if (this%ctxtype == stresspkg .and. &
326  this%blockname == 'PERIOD') then
327  ! allocate cellid if this is not list input
328  if (this%readarray) then
329  call mem_allocate(cellid, 0, 0, 'CELLID', this%mf6_input%mempath)
330  end if
331 
332  ! allocate nodeulist for list and layerarray packages only;
333  ! keystring packages do not use a flat nodeulist
334  if (this%loadtype /= gridarray .and. &
335  this%loadtype /= keystring) then
336  call mem_allocate(nodeulist, 0, 'NODEULIST', this%mf6_input%mempath)
337  end if
338 
339  ! set pointers to aux/bound arrays for list and layerarray packages only;
340  ! keystring packages manage aux through struct array columns
341  if (this%loadtype /= keystring) then
342  call setptr(this%auxname_cst, 'AUXILIARY', &
343  this%mf6_input%mempath, lenauxname)
344  call setptr(this%boundname_cst, 'BOUNDNAME', &
345  this%mf6_input%mempath, lenboundname)
346  call setptr(this%auxvar, this%mf6_input%mempath)
347  end if
348 
349  else if (this%ctxtype == exchange) then
350  ! set pointers to arrays
351  call setptr(this%auxname_cst, 'AUXILIARY', &
352  this%mf6_input%mempath, lenauxname)
353  call setptr(this%boundname_cst, 'BOUNDNAME', &
354  this%mf6_input%mempath, lenboundname)
355  call setptr(this%auxvar, this%mf6_input%mempath)
356  end if
357  end subroutine allocate_arrays
358 
359  !> @brief allocate a package dynamic input parameter
360  !<
361  subroutine allocate_param(this, idt)
363  class(loadcontexttype) :: this
364  type(inputparamdefinitiontype), pointer :: idt
365  integer(I4B) :: dimsize
366 
367  ! initialize
368  dimsize = 0
369 
370  if (this%readarray) then
371  select case (idt%shape)
372  case ('NCPL', 'NAUX NCPL')
373  dimsize = this%ncpl
374  case ('NODES', 'NAUX NODES')
375  dimsize = this%maxbound
376  case default
377  end select
378  end if
379 
380  select case (idt%datatype)
381  case ('INTEGER')
382  if (this%loadtype == list) then
383  call allocate_int1d(this%maxbound, idt%mf6varname, &
384  this%mf6_input%mempath)
385  end if
386  case ('DOUBLE')
387  if (this%loadtype == list) then
388  call allocate_dbl1d(this%maxbound, idt%mf6varname, &
389  this%mf6_input%mempath)
390  end if
391  case ('STRING')
392  if (this%loadtype == list) then
393  call allocate_charstr1d(lenboundname, this%maxbound, idt%mf6varname, &
394  this%mf6_input%mempath)
395  end if
396  case ('INTEGER1D')
397  if (this%loadtype == list) then
398  if (idt%shape == 'NCELLDIM') then
399  call allocate_int2d(size(this%mshape), this%maxbound, &
400  idt%mf6varname, this%mf6_input%mempath)
401  end if
402  else if (this%readarray) then
403  call allocate_int1d(dimsize, idt%mf6varname, &
404  this%mf6_input%mempath)
405  end if
406  case ('DOUBLE1D')
407  if (idt%shape == 'NAUX') then
408  call allocate_dbl2d(this%naux, this%maxbound, &
409  idt%mf6varname, this%mf6_input%mempath)
410  else if (this%readarray) then
411  call allocate_dbl1d(dimsize, idt%mf6varname, &
412  this%mf6_input%mempath)
413  end if
414  case ('DOUBLE2D')
415  if (this%readarray) then
416  call allocate_dbl2d(this%naux, dimsize, idt%mf6varname, &
417  this%mf6_input%mempath)
418  end if
419  case default
420  end select
421  end subroutine allocate_param
422 
423  !> @brief get in scope package params
424  !!
425  !! set input array to tagnames of in scope params, optionally
426  !! allocate the parameters based on datatype.
427  !!
428  !<
429  subroutine tags(this, params, nparam, input_name, create)
431  use simvariablesmodule, only: iout
433  class(loadcontexttype) :: this
434  character(len=LINELENGTH), dimension(:), allocatable, &
435  intent(inout) :: params
436  integer(I4B), intent(inout) :: nparam
437  character(len=*), intent(in) :: input_name
438  logical(LGP), optional, intent(in) :: create
439  type(inputparamdefinitiontype), pointer :: idt
440  character(len=LINELENGTH) :: dev_msg
441  logical(LGP) :: allocate_params
442  integer(I4B) :: n
443 
444  ! initialize allocate_params
445  allocate_params = .false.
446 
447  ! override default if provided
448  if (present(create)) then
449  allocate_params = create
450  end if
451 
452  if (allocated(params)) deallocate (params)
453  nparam = size(this%params)
454  allocate (params(nparam))
455  do n = 1, nparam
456  idt => &
457  get_param_definition_type(this%mf6_input%param_dfns, &
458  this%mf6_input%component_type, &
459  this%mf6_input%subcomponent_type, &
460  this%blockname, this%params(n), '')
461 
462  ! check if input param is developmode
463  if (idt%developmode) then
464  dev_msg = 'Input tag "'//trim(idt%tagname)// &
465  &'" read from file "'//trim(input_name)// &
466  &'" is still under development. Install the &
467  &nightly build or compile from source with IDEVELOPMODE = 1.'
468  call developmode(dev_msg, iout)
469  end if
470 
471  params(n) = this%params(n)
472  if (allocate_params) call this%allocate_param(idt)
473  end do
474  end subroutine tags
475 
476  !> @brief establish if input parameter is in scope for package load
477  !<
478  function in_scope(this, mf6_input, blockname, tagname)
481  class(loadcontexttype) :: this
482  type(modflowinputtype), intent(in) :: mf6_input
483  character(len=*), intent(in) :: blockname
484  character(len=*), intent(in) :: tagname
485  logical(LGP) :: in_scope
486  type(inputparamdefinitiontype), pointer :: idt
487  character(len=LENVARNAME) :: checkname
488  character(len=LINELENGTH) :: datatype
489  integer(I4B) :: isize, checksize
490  integer(I4B), pointer :: intptr
491 
492  idt => &
493  get_param_definition_type(mf6_input%param_dfns, &
494  mf6_input%component_type, &
495  mf6_input%subcomponent_type, &
496  blockname, tagname, '')
497  if (idt%required) then
498  in_scope = .true.
499  return
500  else
501  in_scope = .false.
502  datatype = idt_datatype(idt)
503  if (datatype == 'KEYSTRING' .or. &
504  datatype == 'RECARRAY' .or. &
505  datatype == 'RECORD') return
506  end if
507 
508  ! initialize
509  checkname = ''
510  checksize = 0
511 
512  if (tagname == 'AUXVAR' .or. &
513  tagname == 'AUX') then
514  checkname = 'NAUX'
515  else if (tagname == 'BOUNDNAME') then
516  checkname = 'BOUNDNAMES'
517  else if (tagname == 'I'//trim(mf6_input%subcomponent_type(1:3))) then
518  if (this%loadtype == layerarray) in_scope = .true.
519  else
520  select case (mf6_input%subcomponent_type)
521  case ('EVT')
522  if (tagname == 'PXDP' .or. tagname == 'PETM') then
523  checkname = 'NSEG'
524  checksize = 1
525  else if (tagname == 'PETM0') then
526  checkname = 'SURFRATESPEC'
527  end if
528  case ('MVR', 'MVT', 'MVE')
529  if (tagname == 'MNAME' .or. &
530  tagname == 'MNAME1' .or. &
531  tagname == 'MNAME2') then
532  checkname = 'MODELNAMES'
533  end if
534  case ('NAM')
535  in_scope = .true.
536  case ('SSM')
537  if (tagname == 'MIXED') in_scope = .true.
538  case ('SPC', 'SPCA')
539  in_scope = .true.
540  case default
541  errmsg = 'LoadContext in_scope needs new check for: '// &
542  trim(mf6_input%subcomponent_type)//'/'//trim(idt%tagname)
543  call store_error(errmsg, .true.)
544  end select
545  end if
546 
547  ! apply checks
548  if (.not. in_scope) then
549  call get_isize(checkname, mf6_input%mempath, isize)
550  if (isize > 0) then
551  call mem_setptr(intptr, checkname, mf6_input%mempath)
552  if (intptr > checksize) in_scope = .true.
553  end if
554  end if
555  end function in_scope
556 
557  !> @brief set set of in scope parameters for package
558  !<
559  subroutine set_params(this)
564  class(loadcontexttype) :: this
565  type(inputparamdefinitiontype), pointer :: idt, aidt
566  character(len=LINELENGTH), dimension(:), allocatable :: tags
567  character(len=LINELENGTH), dimension(:), allocatable :: cols
568  integer(I4B) :: keepcnt, iparam, nparam
569  logical(LGP) :: keep, tag_found
570 
571  ! initialize
572  keepcnt = 0
573 
574  if (this%loadtype == list .or. &
575  this%loadtype == keystring) then
576  ! get aggregate param definition for period block
577  aidt => &
578  get_aggregate_definition_type(this%mf6_input%aggregate_dfns, &
579  this%mf6_input%component_type, &
580  this%mf6_input%subcomponent_type, &
581  this%blockname)
582  ! split recarray definition
583  call idt_parse_rectype(aidt, cols, nparam)
584  else
585  nparam = size(this%mf6_input%param_dfns)
586  end if
587 
588  ! allocate dfn input params
589  do iparam = 1, nparam
590  if (this%loadtype == list .or. &
591  this%loadtype == keystring) then
592  ! use found so keystring placeholders are silently skipped
593  idt => get_param_definition_type(this%mf6_input%param_dfns, &
594  this%mf6_input%component_type, &
595  this%mf6_input%subcomponent_type, &
596  this%blockname, cols(iparam), '', &
597  found=tag_found)
598  else
599  tag_found = .true.
600  idt => this%mf6_input%param_dfns(iparam)
601  end if
602 
603  if (.not. tag_found) then
604  keep = .false.
605  else if (idt%blockname /= this%blockname) then
606  keep = .false.
607  else
608  keep = this%in_scope(this%mf6_input, this%blockname, idt%tagname)
609  end if
610 
611  if (keep) then
612  keepcnt = keepcnt + 1
613  call expandarray(tags)
614  tags(keepcnt) = trim(idt%tagname)
615  end if
616  end do
617 
618  ! update nparam
619  nparam = keepcnt
620 
621  ! for LIST/KEYSTRING packages record the leading-column count; this
622  ! is the count of aggregate columns before the keystring placeholder
623  if (this%loadtype == list .or. &
624  this%loadtype == keystring) this%nleading = nparam
625 
626  ! allocate filtcols
627  allocate (this%params(nparam))
628 
629  ! set filtcols
630  do iparam = 1, nparam
631  this%params(iparam) = trim(tags(iparam))
632  end do
633 
634  ! cleanup
635  if (allocated(tags)) deallocate (tags)
636  end subroutine set_params
637 
638  !> @brief allocate a read state variable
639  !!
640  !! Create and set a read state variable, e.g. 'INRECHARGE',
641  !! which are updated per iper load as follows:
642  !! -1: unset, not in use
643  !! 0: not read in most recent period block
644  !! 1: numeric input read in most recent period block
645  !! 2: time series input read in most recent period block
646  !!
647  !<
648  function rsv_alloc(this, mf6varname) result(varname)
649  use constantsmodule, only: lenvarname
651  class(loadcontexttype) :: this
652  character(len=*), intent(in) :: mf6varname
653  character(len=LENVARNAME) :: varname
654  integer(I4B), pointer :: intvar
655  varname = rsv_name(mf6varname)
656  call mem_allocate(intvar, varname, this%mf6_input%mempath)
657  intvar = -1
658  end function rsv_alloc
659 
660  !> @brief destroy input context object
661  !<
662  subroutine destroy(this)
663  class(loadcontexttype) :: this
664 
665  if (allocated(this%named_bound)) deallocate (this%named_bound)
666 
667  if (associated(this%setting_idt)) then
668  deallocate (this%setting_idt)
669  nullify (this%setting_idt)
670  end if
671 
672  if (this%ctxtype == exchange .or. &
673  this%ctxtype == stresspkg) then
674  ! deallocate local
675  deallocate (this%naux)
676  deallocate (this%ncpl)
677  deallocate (this%nodes)
678  deallocate (this%maxbound)
679  deallocate (this%boundnames)
680  deallocate (this%iprpak)
681  end if
682 
683  ! nullify
684  nullify (this%naux)
685  nullify (this%nbound)
686  nullify (this%ncpl)
687  nullify (this%nodes)
688  nullify (this%maxbound)
689  nullify (this%boundnames)
690  nullify (this%iprpak)
691  nullify (this%auxname_cst)
692  nullify (this%boundname_cst)
693  nullify (this%auxvar)
694  nullify (this%mshape)
695  end subroutine destroy
696 
697  !> @brief Return the KEYSTRING aggregate for the SETTING token in rec_cols, or null().
698  !<
699  function find_setting_aggregate(mf6_input, rec_cols, nrec_col) result(ks_aidt)
700  use inputoutputmodule, only: upcase
702  type(modflowinputtype), intent(in) :: mf6_input
703  character(len=LINELENGTH), intent(in) :: rec_cols(:)
704  integer(I4B), intent(in) :: nrec_col
705  type(inputparamdefinitiontype), pointer :: ks_aidt
706  character(len=LINELENGTH) :: token, tagname
707  integer(I4B) :: m, n, ilen
708  ks_aidt => null()
709  do m = 1, nrec_col
710  token = trim(rec_cols(m))
711  call upcase(token)
712  ilen = len_trim(token)
713  if (ilen < 8) cycle
714  if (token(ilen - 6:ilen) /= 'SETTING') cycle
715  do n = 1, size(mf6_input%aggregate_dfns)
716  tagname = mf6_input%aggregate_dfns(n)%tagname
717  call upcase(tagname)
718  if (trim(tagname) == trim(token)) then
719  ks_aidt => mf6_input%aggregate_dfns(n)
720  if (idt_datatype(ks_aidt) /= 'KEYSTRING') ks_aidt => null()
721  exit
722  end if
723  end do
724  exit
725  end do
726  end function find_setting_aggregate
727 
728  !> @brief Append sub-member column names from a RECORD compound entry to member_names.
729  !<
730  subroutine expand_record_submembers(mf6_input, rec_idt, member_names, nmembers)
731  use inputoutputmodule, only: upcase
734  type(modflowinputtype), intent(in) :: mf6_input
735  type(inputparamdefinitiontype), pointer, intent(in) :: rec_idt
736  character(len=LINELENGTH), allocatable, intent(inout) :: member_names(:)
737  integer(I4B), intent(inout) :: nmembers
738  type(inputparamdefinitiontype), pointer :: sub_idt
739  character(len=LINELENGTH), allocatable :: sub_cols(:)
740  character(len=LINELENGTH) :: token, tagname
741  integer(I4B) :: k, j, nsub_col
742  call idt_parse_rectype(rec_idt, sub_cols, nsub_col)
743  do k = 1, nsub_col
744  token = trim(sub_cols(k))
745  call upcase(token)
746  do j = 1, size(mf6_input%param_dfns)
747  sub_idt => mf6_input%param_dfns(j)
748  if (sub_idt%blockname /= 'PERIOD') cycle
749  tagname = sub_idt%tagname
750  call upcase(tagname)
751  if (trim(tagname) /= trim(token)) cycle
752  if (idt_datatype(sub_idt) == 'RECORD') cycle
753  nmembers = nmembers + 1
754  call expandarray(member_names)
755  member_names(nmembers) = trim(sub_idt%tagname)
756  exit
757  end do
758  end do
759  if (allocated(sub_cols)) deallocate (sub_cols)
760  end subroutine expand_record_submembers
761 
762  !> @brief Return .true. if mf6_input's PERIOD block uses keystring dispatch.
763  !<
764  function is_keystring_period(mf6_input) result(res)
767  type(modflowinputtype), intent(in) :: mf6_input
768  logical(LGP) :: res, has_period
769  type(inputparamdefinitiontype), pointer :: aidt, ks_aidt
770  character(len=LINELENGTH), allocatable :: cols(:)
771  integer(I4B) :: n, ncol
772  res = .false.
773  has_period = .false.
774  do n = 1, size(mf6_input%block_dfns)
775  if (mf6_input%block_dfns(n)%blockname == 'PERIOD') then
776  has_period = .true.
777  end if
778  end do
779  if (.not. has_period) return
780  aidt => get_aggregate_definition_type(mf6_input%aggregate_dfns, &
781  mf6_input%component_type, &
782  mf6_input%subcomponent_type, &
783  'PERIOD')
784  call idt_parse_rectype(aidt, cols, ncol)
785  if (ncol >= 2) then
786  ks_aidt => find_setting_aggregate(mf6_input, cols, ncol)
787  if (associated(ks_aidt)) res = .true.
788  end if
789  if (allocated(cols)) deallocate (cols)
790  end function is_keystring_period
791 
792  !> @brief Return .true. if mf6_input is a keystring PERIOD dispatch paired
793  !! with a DIMENSIONS block (e.g. SPC) rather than PACKAGEDATA.
794  !<
795  function has_dimensions_block(mf6_input) result(res)
796  type(modflowinputtype), intent(in) :: mf6_input
797  logical(LGP) :: res
798  integer(I4B) :: n
799  logical(LGP) :: has_dimensions
800  res = .false.
801  if (.not. is_keystring_period(mf6_input)) return
802  has_dimensions = .false.
803  do n = 1, size(mf6_input%block_dfns)
804  if (mf6_input%block_dfns(n)%blockname == 'DIMENSIONS') then
805  has_dimensions = .true.
806  end if
807  ! PACKAGEDATA-paired (e.g. LAK/MAW/SFR/UZF) takes precedence over a
808  ! coincidental DIMENSIONS block (e.g. NOUTLETS, NTABLES)
809  if (mf6_input%block_dfns(n)%blockname == 'PACKAGEDATA') return
810  end do
811  res = has_dimensions
812  end function has_dimensions_block
813 
814  !> @brief Return .true. if mf6_input is a keystring PERIOD dispatch whose
815  !! leading column is CELLID (e.g. TVK/TVS) rather than a stable integer
816  !! feature number.
817  !<
818  function is_cellid_addressed(mf6_input) result(res)
819  type(modflowinputtype), intent(in) :: mf6_input
820  logical(LGP) :: res
821  integer(I4B) :: n
822  res = .false.
823  if (.not. is_keystring_period(mf6_input)) return
824  do n = 1, size(mf6_input%param_dfns)
825  if (mf6_input%param_dfns(n)%blockname == 'PERIOD' .and. &
826  mf6_input%param_dfns(n)%tagname == 'CELLID') then
827  res = .true.
828  exit
829  end if
830  end do
831  end function is_cellid_addressed
832 
833  !> @brief Return keystring member column names for the PERIOD block
834  !!
835  !! Column order follows the KEYSTRING aggregate definition token list,
836  !! which is the single authoritative source of order — independent of
837  !! the order in which individual params appear in param_dfns.
838  !! For each token in the aggregate:
839  !! - RECORD compound group: sub-members are expanded in RECORD type order
840  !! - direct-dispatch param: appended as-is
841  !<
842  subroutine keystring_member_names(this, member_names, nmembers)
843  use inputoutputmodule, only: upcase
847  class(loadcontexttype) :: this
848  character(len=LINELENGTH), allocatable, intent(out) :: member_names(:)
849  integer(I4B), intent(out) :: nmembers
850  type(inputparamdefinitiontype), pointer :: aidt, ks_aidt, idt
851  character(len=LINELENGTH), allocatable :: rec_cols(:), ks_cols(:)
852  character(len=LINELENGTH) :: rec_token, tagname
853  integer(I4B) :: m, n, nrec_col, nks_col
854 
855  nmembers = 0
856 
857  ! get RECARRAY aggregate for period block and parse its column tokens
858  aidt => get_aggregate_definition_type(this%mf6_input%aggregate_dfns, &
859  this%mf6_input%component_type, &
860  this%mf6_input%subcomponent_type, &
861  this%blockname)
862  call idt_parse_rectype(aidt, rec_cols, nrec_col)
863 
864  ! find the KEYSTRING aggregate for the SETTING token
865  ks_aidt => find_setting_aggregate(this%mf6_input, rec_cols, nrec_col)
866  if (allocated(rec_cols)) deallocate (rec_cols)
867  if (.not. associated(ks_aidt)) return
868 
869  ! parse the KEYSTRING aggregate to get member token list — canonical order
870  call idt_parse_rectype(ks_aidt, ks_cols, nks_col)
871 
872  ! walk the keystring token list in aggregate order
873  do m = 1, nks_col
874  rec_token = trim(ks_cols(m))
875  call upcase(rec_token)
876 
877  ! locate matching param_dfns entry for this token
878  do n = 1, size(this%mf6_input%param_dfns)
879  if (this%mf6_input%param_dfns(n)%blockname /= 'PERIOD') cycle
880  tagname = this%mf6_input%param_dfns(n)%tagname
881  call upcase(tagname)
882  if (trim(tagname) /= trim(rec_token)) cycle
883 
884  idt => this%mf6_input%param_dfns(n)
885  if (idt_datatype(idt) == 'RECORD') then
886  ! compound group: expand sub-members in RECORD type order
887  call expand_record_submembers(this%mf6_input, idt, member_names, &
888  nmembers)
889  else
890  ! direct-dispatch param
891  nmembers = nmembers + 1
892  call expandarray(member_names)
893  member_names(nmembers) = trim(this%mf6_input%param_dfns(n)%tagname)
894  end if
895  exit
896  end do
897  end do
898 
899  if (allocated(ks_cols)) deallocate (ks_cols)
900  end subroutine keystring_member_names
901 
902  !> @brief create read state variable name
903  !<
904  function rsv_name(mf6varname) result(varname)
905  use constantsmodule, only: lenvarname
906  character(len=*), intent(in) :: mf6varname
907  character(len=LENVARNAME) :: varname
908  integer(I4B) :: ilen
909  character(len=2) :: prefix = 'IN'
910  ilen = len_trim(mf6varname)
911  if (ilen > (lenvarname - len(prefix))) then
912  varname = prefix//mf6varname(1:(lenvarname - len(prefix)))
913  else
914  varname = prefix//trim(mf6varname)
915  end if
916  end function rsv_name
917 
918  !> @brief allocate character string type array
919  !<
920  subroutine allocate_charstr1d(strlen, nrow, varname, mempath)
922  integer(I4B), intent(in) :: strlen !< string number of characters
923  integer(I4B), intent(in) :: nrow !< integer array number of rows
924  character(len=*), intent(in) :: varname !< variable name
925  character(len=*), intent(in) :: mempath !< variable mempath
926  type(characterstringtype), dimension(:), pointer, &
927  contiguous :: charstr1d
928  integer(I4B) :: n
929  call mem_allocate(charstr1d, strlen, nrow, varname, mempath)
930  do n = 1, nrow
931  charstr1d(n) = ''
932  end do
933  end subroutine allocate_charstr1d
934 
935  !> @brief allocate int1d
936  !<
937  subroutine allocate_int1d(nrow, varname, mempath)
939  integer(I4B), intent(in) :: nrow !< integer array number of rows
940  character(len=*), intent(in) :: varname !< variable name
941  character(len=*), intent(in) :: mempath !< variable mempath
942  integer(I4B), dimension(:), pointer, contiguous :: int1d
943  integer(I4B) :: n
944  call mem_allocate(int1d, nrow, varname, mempath)
945  do n = 1, nrow
946  int1d(n) = izero
947  end do
948  end subroutine allocate_int1d
949 
950  !> @brief allocate int2d
951  !<
952  subroutine allocate_int2d(ncol, nrow, varname, mempath)
954  integer(I4B), intent(in) :: ncol !< integer array number of cols
955  integer(I4B), intent(in) :: nrow !< integer array number of rows
956  character(len=*), intent(in) :: varname !< variable name
957  character(len=*), intent(in) :: mempath !< variable mempath
958  integer(I4B), dimension(:, :), pointer, contiguous :: int2d
959  integer(I4B) :: n, m
960  call mem_allocate(int2d, ncol, nrow, varname, mempath)
961  do m = 1, nrow
962  do n = 1, ncol
963  int2d(n, m) = izero
964  end do
965  end do
966  end subroutine allocate_int2d
967 
968  !> @brief allocate dbl1d
969  !<
970  subroutine allocate_dbl1d(nrow, varname, mempath)
972  integer(I4B), intent(in) :: nrow !< integer array number of rows
973  character(len=*), intent(in) :: varname !< variable name
974  character(len=*), intent(in) :: mempath !< variable mempath
975  real(DP), dimension(:), pointer, contiguous :: dbl1d
976  integer(I4B) :: n
977  call mem_allocate(dbl1d, nrow, varname, mempath)
978  do n = 1, nrow
979  dbl1d(n) = dzero
980  end do
981  end subroutine allocate_dbl1d
982 
983  !> @brief allocate dbl2d
984  !<
985  subroutine allocate_dbl2d(ncol, nrow, varname, mempath)
987  integer(I4B), intent(in) :: ncol !< integer array number of cols
988  integer(I4B), intent(in) :: nrow !< integer array number of rows
989  character(len=*), intent(in) :: varname !< variable name
990  character(len=*), intent(in) :: mempath !< variable mempath
991  real(DP), dimension(:, :), pointer, contiguous :: dbl2d
992  integer(I4B) :: n, m
993  call mem_allocate(dbl2d, ncol, nrow, varname, mempath)
994  do m = 1, nrow
995  do n = 1, ncol
996  dbl2d(n, m) = dzero
997  end do
998  end do
999  end subroutine allocate_dbl2d
1000 
1001  !> @brief sum named dimension variables from mempath
1002  !!
1003  !! Loops over each name in named_bound and accumulates its value
1004  !! from mempath into total. Variables not present in mempath are
1005  !! silently skipped.
1006  !!
1007  !<
1008  subroutine sum_named_bounds(named_bound, mempath, total)
1010  character(len=*), dimension(:), intent(in) :: named_bound
1011  character(len=*), intent(in) :: mempath
1012  integer(I4B), intent(inout) :: total
1013  integer(I4B), pointer :: dimptr
1014  integer(I4B) :: n, isize
1015 
1016  do n = 1, size(named_bound)
1017  call get_isize(trim(named_bound(n)), mempath, isize)
1018  if (isize > -1) then
1019  call mem_setptr(dimptr, trim(named_bound(n)), mempath)
1020  total = total + dimptr
1021  nullify (dimptr)
1022  end if
1023  end do
1024  end subroutine sum_named_bounds
1025 
1026  !> @brief allocate intptr and update from input contextset intptr to varname
1027  !!
1028  !<
1029  subroutine setval(intptr, varname, mempath)
1031  integer(I4B), pointer, intent(inout) :: intptr
1032  character(len=*), intent(in) :: varname
1033  character(len=*), intent(in) :: mempath
1034  logical(LGP) :: found
1035  allocate (intptr)
1036  intptr = 0
1037  call mem_set_value(intptr, varname, mempath, found, release=.false.)
1038  end subroutine setval
1039 
1040  !> @brief set intptr to varname
1041  !!
1042  !<
1043  subroutine setptr_int(intptr, varname, mempath)
1045  integer(I4B), pointer, intent(inout) :: intptr
1046  character(len=*), intent(in) :: varname
1047  character(len=*), intent(in) :: mempath
1048  integer(I4B) :: isize
1049  call get_isize(varname, mempath, isize)
1050  if (isize > -1) then
1051  call mem_setptr(intptr, varname, mempath)
1052  else
1053  call mem_allocate(intptr, varname, mempath)
1054  intptr = 0
1055  end if
1056  end subroutine setptr_int
1057 
1058  !> @brief set charstr1d pointer to varname
1059  !<
1060  subroutine setptr_charstr1d(charstr1d, varname, mempath, strlen)
1062  type(characterstringtype), dimension(:), pointer, &
1063  contiguous, intent(inout) :: charstr1d
1064  character(len=*), intent(in) :: varname
1065  character(len=*), intent(in) :: mempath
1066  integer(I4B), intent(in) :: strlen
1067  integer(I4B) :: isize
1068  call get_isize(varname, mempath, isize)
1069  if (isize > -1) then
1070  call mem_setptr(charstr1d, varname, mempath)
1071  else
1072  call mem_allocate(charstr1d, strlen, 0, varname, mempath)
1073  end if
1074  end subroutine setptr_charstr1d
1075 
1076  !> @brief set auxvar pointer
1077  !!
1078  !<
1079  subroutine setptr_auxvar(auxvar, mempath)
1081  real(DP), dimension(:, :), pointer, &
1082  contiguous, intent(inout) :: auxvar
1083  character(len=*), intent(in) :: mempath
1084  integer(I4B) :: isize
1085  call get_isize('AUXVAR', mempath, isize)
1086  if (isize > -1) then
1087  call mem_setptr(auxvar, 'AUXVAR', mempath)
1088  else
1089  call mem_allocate(auxvar, 0, 0, 'AUXVAR', mempath)
1090  end if
1091  end subroutine setptr_auxvar
1092 
1093 end module loadcontextmodule
subroutine init()
Definition: GridSorting.f90:25
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 lenvarname
maximum length of a variable name
Definition: Constants.f90:17
integer(i4b), parameter lenauxname
maximum length of a aux variable
Definition: Constants.f90:35
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
This module contains the DefinitionSelectModule.
type(inputparamdefinitiontype) function, pointer, public idt_default(component_type, subcomponent_type, blockname, tagname, mf6varname, datatype)
return allocated input definition type
type(inputparamdefinitiontype) function, pointer, public get_aggregate_definition_type(input_definition_types, component_type, subcomponent_type, blockname)
Return aggregate definition.
subroutine, public idt_parse_rectype(idt, cols, ncol)
allocate and set RECARRAY, KEYSTRING or RECORD param list
character(len=linelength) function, public idt_datatype(idt)
return input definition type datatype
type(inputparamdefinitiontype) function, pointer, public get_param_definition_type(input_definition_types, component_type, subcomponent_type, blockname, tagname, filename, found)
Return parameter definition.
Disable development features in release mode.
Definition: FeatureFlags.f90:2
subroutine, public developmode(errmsg, iunit)
Terminate if in release mode (guard development features)
Input definition module.
subroutine, public upcase(word)
Convert to upper case.
This module defines variable data types.
Definition: kind.f90:8
This module contains the LoadContextModule.
Definition: LoadContext.f90:10
subroutine set_params(this)
set set of in scope parameters for package
subroutine allocate_dbl2d(ncol, nrow, varname, mempath)
allocate dbl2d
subroutine keystring_member_names(this, member_names, nmembers)
Return keystring member column names for the PERIOD block.
subroutine tags(this, params, nparam, input_name, create)
get in scope package params
subroutine setptr_auxvar(auxvar, mempath)
set auxvar pointer
subroutine allocate_charstr1d(strlen, nrow, varname, mempath)
allocate character string type array
subroutine allocate_int1d(nrow, varname, mempath)
allocate int1d
@ load_undef
undefined load type
Definition: LoadContext.f90:31
@ gridarray
readarraygrid load
Definition: LoadContext.f90:34
@ keystring
basic keystring period block load
Definition: LoadContext.f90:35
@ layerarray
readasarrays load
Definition: LoadContext.f90:33
@ list
list (structarray) based load
Definition: LoadContext.f90:32
type(inputparamdefinitiontype) function, pointer find_setting_aggregate(mf6_input, rec_cols, nrec_col)
Return the KEYSTRING aggregate for the SETTING token in rec_cols, or null().
subroutine allocate_dbl1d(nrow, varname, mempath)
allocate dbl1d
@ stresspkg
model stress package context type
Definition: LoadContext.f90:44
@ exchange
exchange context type
Definition: LoadContext.f90:45
@ model
model context type
Definition: LoadContext.f90:42
@ root
root context type
Definition: LoadContext.f90:40
@ modelpkg
model package context type
Definition: LoadContext.f90:43
@ sim
sim context type
Definition: LoadContext.f90:41
@ context_undef
undefined context type
Definition: LoadContext.f90:39
logical(lgp) function, public is_cellid_addressed(mf6_input)
Return .true. if mf6_input is a keystring PERIOD dispatch whose leading column is CELLID (e....
subroutine setval(intptr, varname, mempath)
allocate intptr and update from input contextset intptr to varname
subroutine allocate_scalars(this)
allocate scalars
subroutine allocate_param(this, idt)
allocate a package dynamic input parameter
logical(lgp) function, public has_dimensions_block(mf6_input)
Return .true. if mf6_input is a keystring PERIOD dispatch paired with a DIMENSIONS block (e....
subroutine allocate_arrays(this)
allocate arrays
subroutine setptr_int(intptr, varname, mempath)
set intptr to varname
subroutine allocate_int2d(ncol, nrow, varname, mempath)
allocate int2d
subroutine destroy(this)
destroy input context object
character(len=lenvarname) function, public rsv_name(mf6varname)
create read state variable name
logical(lgp) function in_scope(this, mf6_input, blockname, tagname)
establish if input parameter is in scope for package load
subroutine sum_named_bounds(named_bound, mempath, total)
sum named dimension variables from mempath
character(len=lenvarname) function rsv_alloc(this, mf6varname)
allocate a read state variable
subroutine setptr_charstr1d(charstr1d, varname, mempath, strlen)
set charstr1d pointer to varname
logical(lgp) function, public is_keystring_period(mf6_input)
Return .true. if mf6_input's PERIOD block uses keystring dispatch.
subroutine expand_record_submembers(mf6_input, rec_idt, member_names, nmembers)
Append sub-member column names from a RECORD compound entry to member_names.
subroutine, public get_isize(name, mem_path, isize)
@ brief Get the number of elements for this variable
This module contains the ModelPackageInputsModule.
logical(lgp) function, public supported_model(ctype)
is this a supported MODFLOW 6 model type
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
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
integer(i4b) iout
file unit number for simulation output
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 boundary package input context
Definition: LoadContext.f90:65
Pointer type for read state variable.
Definition: LoadContext.f90:50
derived type for storing input definition for a file