MODFLOW 6  version 6.7.0.dev2
USGS Modular Hydrologic Model
FlowModelInterface.f90
Go to the documentation of this file.
2 
3  use kindmodule, only: dp, i4b, lgp
8  use simvariablesmodule, only: errmsg
10  use basedismodule, only: disbasetype
11  use listmodule, only: listtype
17 
18  implicit none
19  private
20  public :: flowmodelinterfacetype
21 
23 
24  character(len=LENPACKAGENAME) :: text = '' !< text string for package
25  logical, pointer :: flows_from_file => null() !< if .false., then flows come from GWF through GWF-Model exg
26  type(listtype), pointer :: gwfbndlist => null() !< list of gwf stress packages
27  integer(I4B), pointer :: iflowsupdated => null() !< flows were updated for this time step
28  integer(I4B), dimension(:), pointer, contiguous :: ibound => null() !< pointer to this model ibound
29  real(dp), dimension(:), pointer, contiguous :: gwfflowja => null() !< pointer to the GWF flowja array
30  real(dp), dimension(:, :), pointer, contiguous :: gwfspdis => null() !< pointer to npf specific discharge array
31  real(dp), dimension(:), pointer, contiguous :: gwfhead => null() !< pointer to the GWF head array
32  real(dp), dimension(:), pointer, contiguous :: gwfsat => null() !< pointer to the GWF saturation array
33  integer(I4B), dimension(:), pointer, contiguous :: ibdgwfsat0 => null() !< mark cells with saturation = 0 to exclude from dispersion
34  integer(I4B), pointer :: idryinactive => null() !< mark cells with an additional flag to exclude from deactivation (gwe will simulate conduction through dry cells)
35  real(dp), dimension(:), pointer, contiguous :: gwfstrgss => null() !< pointer to flow model QSTOSS
36  real(dp), dimension(:), pointer, contiguous :: gwfstrgsy => null() !< pointer to flow model QSTOSY
37  integer(I4B), pointer :: igwfstrgss => null() !< indicates if gwfstrgss is available
38  integer(I4B), pointer :: igwfstrgsy => null() !< indicates if gwfstrgsy is available
39  integer(I4B), pointer :: iubud => null() !< unit number GWF budget file
40  integer(I4B), pointer :: iuhds => null() !< unit number GWF head file
41  integer(I4B), pointer :: iumvr => null() !< unit number GWF mover budget file
42  integer(I4B), pointer :: iugrb => null() !< unit number binary grid file
43  integer(I4B), pointer :: nflowpack => null() !< number of GWF flow packages
44  integer(I4B), dimension(:), pointer, contiguous :: igwfmvrterm => null() !< flag to indicate that gwf package is a mover term
45  type(budgetfilereadertype) :: bfr !< budget file reader
46  type(headfilereadertype) :: hfr !< head file reader
47  type(gridfilereadertype) :: gfr !< grid file reader
48  type(packagebudgettype), dimension(:), allocatable :: gwfpackages !< used to get flows between a package and gwf
49  type(budgetobjecttype), pointer :: mvrbudobj => null() !< pointer to the mover budget object
50  character(len=16), dimension(:), allocatable :: flowpacknamearray !< array of boundary package names (e.g. LAK-1, SFR-3, etc.)
51  character(len=LENVARNAME) :: depvartype = ''
52 
53  contains
54 
55  procedure :: advance_bfr
56  procedure :: advance_hfr
57  procedure :: allocate_arrays
58  procedure :: allocate_gwfpackages
59  procedure :: allocate_scalars
61  procedure :: finalize_bfr
62  procedure :: finalize_hfr
63  procedure :: fmi_ar
64  procedure :: fmi_da
65  procedure :: fmi_df
66  procedure :: get_package_index
67  procedure :: initialize_bfr
70  procedure :: initialize_hfr
71  procedure :: source_options
72  procedure :: source_packagedata
73  procedure :: read_grid
74 
75  end type flowmodelinterfacetype
76 
77 contains
78 
79  !> @brief Define the flow model interface
80  !<
81  subroutine fmi_df(this, dis, idryinactive)
82  ! -- modules
83  ! -- dummy
84  class(flowmodelinterfacetype) :: this
85  class(disbasetype), pointer, intent(in) :: dis
86  integer(I4B), intent(in) :: idryinactive
87  ! -- formats
88  character(len=*), parameter :: fmtfmi = &
89  "(1x,/1x,'FMI -- FLOW MODEL INTERFACE, VERSION 2, 8/17/2023', &
90  &' INPUT READ FROM MEMPATH: ', A, //)"
91  character(len=*), parameter :: fmtfmi0 = &
92  "(1x,/1x,'FMI -- FLOW MODEL INTERFACE,'&
93  &' VERSION 2, 8/17/2023')"
94  !
95  ! --print a message identifying the FMI package.
96  if (this%iout > 0) then
97  if (this%inunit /= 0) then
98  write (this%iout, fmtfmi) this%input_mempath
99  else
100  write (this%iout, fmtfmi0)
101  if (this%flows_from_file) then
102  write (this%iout, '(a)') ' FLOWS ARE ASSUMED TO BE ZERO.'
103  else
104  write (this%iout, '(a)') ' FLOWS PROVIDED BY A GWF MODEL IN THIS &
105  &SIMULATION'
106  end if
107  end if
108  end if
109  !
110  ! -- Store pointers
111  this%dis => dis
112  !
113  ! -- Read fmi options
114  if (this%inunit /= 0) then
115  call this%source_options()
116  end if
117  !
118  ! -- Read packagedata options
119  if (this%inunit /= 0 .and. this%flows_from_file) then
120  call this%source_packagedata()
121  call this%initialize_gwfterms_from_bfr()
122  end if
123  !
124  ! -- If GWF-Model exchange is active, setup flow terms
125  if (.not. this%flows_from_file) then
126  call this%initialize_gwfterms_from_gwfbndlist()
127  end if
128  !
129  ! -- Set flag that stops dry flows from being deactivated in a GWE
130  ! transport model since conduction will still be simulated.
131  ! 0: GWE (skip deactivation step); 1: GWT (default: use existing code)
132  this%idryinactive = idryinactive
133  end subroutine fmi_df
134 
135  !> @brief Allocate the package
136  !<
137  subroutine fmi_ar(this, ibound)
138  ! -- modules
139  ! -- dummy
140  class(flowmodelinterfacetype) :: this
141  integer(I4B), dimension(:), pointer, contiguous :: ibound
142  !
143  ! -- store pointers to arguments that were passed in
144  this%ibound => ibound
145  !
146  ! -- Allocate arrays
147  call this%allocate_arrays(this%dis%nodes)
148  end subroutine fmi_ar
149 
150  !> @brief Deallocate variables
151  !<
152  subroutine fmi_da(this)
153  ! -- modules
155  ! -- dummy
156  class(flowmodelinterfacetype) :: this
157  ! -- todo: finalize hfr and bfr either here or in a finalize routine
158  !
159  ! -- deallocate any memory stored with gwfpackages
160  call this%deallocate_gwfpackages()
161  !
162  ! -- deallocate fmi arrays
163  deallocate (this%gwfpackages)
164  deallocate (this%flowpacknamearray)
165  call mem_deallocate(this%igwfmvrterm)
166  call mem_deallocate(this%ibdgwfsat0)
167  !
168  if (this%flows_from_file) then
169  call mem_deallocate(this%gwfstrgss)
170  call mem_deallocate(this%gwfstrgsy)
171  end if
172  !
173  ! -- special treatment, these could be from mem_checkin
174  call mem_deallocate(this%gwfhead, 'GWFHEAD', this%memoryPath)
175  call mem_deallocate(this%gwfsat, 'GWFSAT', this%memoryPath)
176  call mem_deallocate(this%gwfspdis, 'GWFSPDIS', this%memoryPath)
177  call mem_deallocate(this%gwfflowja, 'GWFFLOWJA', this%memoryPath)
178  !
179  ! -- deallocate scalars
180  call mem_deallocate(this%flows_from_file)
181  call mem_deallocate(this%iflowsupdated)
182  call mem_deallocate(this%igwfstrgss)
183  call mem_deallocate(this%igwfstrgsy)
184  call mem_deallocate(this%iubud)
185  call mem_deallocate(this%iuhds)
186  call mem_deallocate(this%iumvr)
187  call mem_deallocate(this%iugrb)
188  call mem_deallocate(this%nflowpack)
189  call mem_deallocate(this%idryinactive)
190  !
191  ! -- deallocate parent
192  call this%NumericalPackageType%da()
193  end subroutine fmi_da
194 
195  !> @brief Allocate scalars
196  !<
197  subroutine allocate_scalars(this)
198  ! -- modules
201  ! -- dummy
202  class(flowmodelinterfacetype) :: this
203  ! -- local
204  !
205  ! -- allocate scalars in NumericalPackageType
206  call this%NumericalPackageType%allocate_scalars()
207  !
208  ! -- Allocate
209  call mem_allocate(this%flows_from_file, 'FLOWS_FROM_FILE', this%memoryPath)
210  call mem_allocate(this%iflowsupdated, 'IFLOWSUPDATED', this%memoryPath)
211  call mem_allocate(this%igwfstrgss, 'IGWFSTRGSS', this%memoryPath)
212  call mem_allocate(this%igwfstrgsy, 'IGWFSTRGSY', this%memoryPath)
213  call mem_allocate(this%iubud, 'IUBUD', this%memoryPath)
214  call mem_allocate(this%iuhds, 'IUHDS', this%memoryPath)
215  call mem_allocate(this%iumvr, 'IUMVR', this%memoryPath)
216  call mem_allocate(this%iugrb, 'IUGRB', this%memoryPath)
217  call mem_allocate(this%nflowpack, 'NFLOWPACK', this%memoryPath)
218  call mem_allocate(this%idryinactive, "IDRYINACTIVE", this%memoryPath)
219  !
220  ! !
221  ! -- Initialize
222  this%flows_from_file = .true.
223  this%iflowsupdated = 1
224  this%igwfstrgss = 0
225  this%igwfstrgsy = 0
226  this%iubud = 0
227  this%iuhds = 0
228  this%iumvr = 0
229  this%iugrb = 0
230  this%nflowpack = 0
231  this%idryinactive = 1
232  end subroutine allocate_scalars
233 
234  !> @brief Allocate arrays
235  !<
236  subroutine allocate_arrays(this, nodes)
238  !modules
239  use constantsmodule, only: dzero
240  ! -- dummy
241  class(flowmodelinterfacetype) :: this
242  integer(I4B), intent(in) :: nodes
243  ! -- local
244  integer(I4B) :: n
245  !
246  ! -- Allocate ibdgwfsat0, which is an indicator array marking cells with
247  ! saturation greater than 0.0 with a value of 1
248  call mem_allocate(this%ibdgwfsat0, nodes, 'IBDGWFSAT0', this%memoryPath)
249  do n = 1, nodes
250  this%ibdgwfsat0(n) = 1
251  end do
252  !
253  ! -- Allocate differently depending on whether or not flows are
254  ! being read from a file.
255  if (this%flows_from_file) then
256  call mem_allocate(this%gwfflowja, this%dis%con%nja, &
257  'GWFFLOWJA', this%memoryPath)
258  call mem_allocate(this%gwfsat, nodes, 'GWFSAT', this%memoryPath)
259  call mem_allocate(this%gwfhead, nodes, 'GWFHEAD', this%memoryPath)
260  call mem_allocate(this%gwfspdis, 3, nodes, 'GWFSPDIS', this%memoryPath)
261  do n = 1, nodes
262  this%gwfsat(n) = done
263  this%gwfhead(n) = dzero
264  this%gwfspdis(:, n) = dzero
265  end do
266  do n = 1, size(this%gwfflowja)
267  this%gwfflowja(n) = dzero
268  end do
269  !
270  ! -- allocate and initialize storage arrays
271  if (this%igwfstrgss == 0) then
272  call mem_allocate(this%gwfstrgss, 1, 'GWFSTRGSS', this%memoryPath)
273  else
274  call mem_allocate(this%gwfstrgss, nodes, 'GWFSTRGSS', this%memoryPath)
275  end if
276  if (this%igwfstrgsy == 0) then
277  call mem_allocate(this%gwfstrgsy, 1, 'GWFSTRGSY', this%memoryPath)
278  else
279  call mem_allocate(this%gwfstrgsy, nodes, 'GWFSTRGSY', this%memoryPath)
280  end if
281  do n = 1, size(this%gwfstrgss)
282  this%gwfstrgss(n) = dzero
283  end do
284  do n = 1, size(this%gwfstrgsy)
285  this%gwfstrgsy(n) = dzero
286  end do
287  !
288  ! -- If there is no fmi package, then there are no flows at all or a
289  ! connected GWF model, so allocate gwfpackages to zero
290  if (this%inunit == 0) call this%allocate_gwfpackages(this%nflowpack)
291  end if
292  end subroutine allocate_arrays
293 
294  !> @ brief Source input options for package
295  !<
296  subroutine source_options(this)
297  ! -- modules
299  ! -- dummy
300  class(flowmodelinterfacetype) :: this
301  ! -- local
302  logical(LGP) :: found_ipakcb
303  character(len=*), parameter :: fmtisvflow = &
304  "(4x,'CELL-BY-CELL FLOW INFORMATION WILL BE SAVED TO BINARY FILE &
305  &WHENEVER ICBCFL IS NOT ZERO AND FLOW IMBALANCE CORRECTION ACTIVE.')"
306 
307  ! -- source package input
308  call mem_set_value(this%ipakcb, 'SAVE_FLOWS', this%input_mempath, &
309  found_ipakcb)
310 
311  write (this%iout, '(1x,a)') 'PROCESSING FMI OPTIONS'
312 
313  if (found_ipakcb) then
314  this%ipakcb = -1
315  write (this%iout, fmtisvflow)
316  end if
317 
318  write (this%iout, '(1x,a)') 'END OF FMI OPTIONS'
319  end subroutine source_options
320 
321  !> @ brief Source input options for package
322  !<
323  subroutine source_packagedata(this)
324  ! -- modules
328  use openspecmodule, only: access, form
331  ! -- dummy
332  class(flowmodelinterfacetype) :: this
333  ! -- local
334  type(characterstringtype), dimension(:), contiguous, &
335  pointer :: flowtypes
336  type(characterstringtype), dimension(:), contiguous, &
337  pointer :: fileops
338  type(characterstringtype), dimension(:), contiguous, &
339  pointer :: fnames
340  character(len=LINELENGTH) :: flowtype, fileop, fname
341  integer(I4B) :: inunit, n
342  logical(LGP) :: exist
343 
344  call mem_setptr(flowtypes, 'FLOWTYPE', this%input_mempath)
345  call mem_setptr(fileops, 'FILEIN', this%input_mempath)
346  call mem_setptr(fnames, 'FNAME', this%input_mempath)
347 
348  write (this%iout, '(1x,a)') 'PROCESSING FMI PACKAGEDATA'
349 
350  do n = 1, size(flowtypes)
351  flowtype = flowtypes(n)
352  fileop = fileops(n)
353  fname = fnames(n)
354 
355  inquire (file=trim(fname), exist=exist)
356  if (.not. exist) then
357  call store_error('Could not find file '//trim(fname))
358  cycle
359  end if
360 
361  if (fileop /= 'FILEIN') then
362  call store_error('Unexpected packagedata input keyword read: "' &
363  //trim(fileop)//'".')
364  cycle
365  end if
366 
367  select case (flowtype)
368  case ('GWFBUDGET')
369  inunit = getunit()
370  call openfile(inunit, this%iout, fname, 'DATA(BINARY)', form, &
371  access, 'UNKNOWN')
372  this%iubud = inunit
373  call this%initialize_bfr()
374  case ('GWFHEAD')
375  inunit = getunit()
376  call openfile(inunit, this%iout, fname, 'DATA(BINARY)', form, &
377  access, 'UNKNOWN')
378  this%iuhds = inunit
379  call this%initialize_hfr()
380  case ('GWFMOVER')
381  inunit = getunit()
382  call openfile(inunit, this%iout, fname, 'DATA(BINARY)', form, &
383  access, 'UNKNOWN')
384  this%iumvr = inunit
385  call budgetobject_cr_bfr(this%mvrbudobj, 'MVT', this%iumvr, &
386  this%iout)
387  call this%mvrbudobj%fill_from_bfr(this%dis, this%iout)
388  case ('GWFGRID')
389  inunit = getunit()
390  call openfile(inunit, this%iout, fname, 'DATA(BINARY)', &
391  form, access, 'UNKNOWN')
392  this%iugrb = inunit
393  call this%read_grid()
394  case default
395  write (errmsg, '(a,3(1x,a))') &
396  'UNKNOWN', trim(adjustl(this%text)), 'PACKAGEDATA:', trim(flowtype)
397  call store_error(errmsg)
398  end select
399  end do
400 
401  write (this%iout, '(1x,a)') 'END OF FMI PACKAGEDATA'
402 
403  if (count_errors() > 0) then
404  call store_error_filename(this%input_fname)
405  end if
406  end subroutine source_packagedata
407 
408  !> @brief Validate flow model grid
409  !<
410  subroutine read_grid(this)
411  ! -- modules
412  use dismodule, only: distype
413  use disvmodule, only: disvtype
414  use disumodule, only: disutype
415  use dis2dmodule, only: dis2dtype
416  use disv2dmodule, only: disv2dtype
417  use disv1dmodule, only: disv1dtype
418  ! -- dummy
419  class(flowmodelinterfacetype) :: this
420  ! -- local
421  integer(I4B) :: user_nodes
422  integer(I4B), allocatable :: idomain1d(:), idomain2d(:, :), idomain3d(:, :, :)
423  ! -- formats
424  character(len=*), parameter :: fmtdiserr = &
425  "('Error in ',a,': Models do not have the same discretization. &
426  &GWF model has ', i0, ' user nodes, this model has ', i0, '. &
427  &Ensure discretization packages, including IDOMAIN, are identical.')"
428  character(len=*), parameter :: fmtidomerr = &
429  "('Error in ',a,': models do not have the same discretization. &
430  &Models have different IDOMAIN arrays. &
431  &Ensure discretization packages, including IDOMAIN, are identical.')"
432 
433  call this%gfr%initialize(this%iugrb)
434 
435  ! check grid equivalence
436  select case (this%gfr%grid_type)
437  case ('DIS')
438  select type (dis => this%dis)
439  type is (distype)
440  user_nodes = this%gfr%read_int("NCELLS")
441  if (user_nodes /= this%dis%nodesuser) then
442  write (errmsg, fmtdiserr) &
443  trim(this%text), user_nodes, this%dis%nodesuser
444  call store_error(errmsg, terminate=.true.)
445  end if
446  idomain1d = this%gfr%read_int_1d("IDOMAIN")
447  idomain3d = reshape(idomain1d, [ &
448  this%gfr%read_int("NCOL"), &
449  this%gfr%read_int("NROW"), &
450  this%gfr%read_int("NLAY") &
451  ])
452  if (.not. all(dis%idomain == idomain3d)) then
453  write (errmsg, fmtidomerr) trim(this%text)
454  call store_error(errmsg, terminate=.true.)
455  end if
456  end select
457  case ('DISV')
458  select type (dis => this%dis)
459  type is (disvtype)
460  user_nodes = this%gfr%read_int("NCELLS")
461  if (user_nodes /= this%dis%nodesuser) then
462  write (errmsg, fmtdiserr) &
463  trim(this%text), user_nodes, this%dis%nodesuser
464  call store_error(errmsg, terminate=.true.)
465  end if
466  idomain1d = this%gfr%read_int_1d("IDOMAIN")
467  idomain2d = reshape(idomain1d, [ &
468  this%gfr%read_int("NCPL"), &
469  this%gfr%read_int("NLAY") &
470  ])
471  if (.not. all(dis%idomain == idomain2d)) then
472  write (errmsg, fmtidomerr) trim(this%text)
473  call store_error(errmsg, terminate=.true.)
474  end if
475  end select
476  case ('DISU')
477  select type (dis => this%dis)
478  type is (disutype)
479  user_nodes = this%gfr%read_int("NODES")
480  if (user_nodes /= this%dis%nodesuser) then
481  write (errmsg, fmtdiserr) &
482  trim(this%text), user_nodes, this%dis%nodesuser
483  call store_error(errmsg, terminate=.true.)
484  end if
485  idomain1d = this%gfr%read_int_1d("IDOMAIN")
486  if (.not. all(dis%idomain == idomain1d)) then
487  write (errmsg, fmtidomerr) trim(this%text)
488  call store_error(errmsg, terminate=.true.)
489  end if
490  end select
491  case ('DIS2D')
492  select type (dis => this%dis)
493  type is (dis2dtype)
494  user_nodes = this%gfr%read_int("NCELLS")
495  if (user_nodes /= this%dis%nodesuser) then
496  write (errmsg, fmtdiserr) &
497  trim(this%text), user_nodes, this%dis%nodesuser
498  call store_error(errmsg, terminate=.true.)
499  end if
500  idomain1d = this%gfr%read_int_1d("IDOMAIN")
501  idomain2d = reshape(idomain1d, [ &
502  this%gfr%read_int("NCOL"), &
503  this%gfr%read_int("NROW") &
504  ])
505  if (.not. all(dis%idomain == idomain2d)) then
506  write (errmsg, fmtidomerr) trim(this%text)
507  call store_error(errmsg, terminate=.true.)
508  end if
509  end select
510  case ('DISV2D')
511  select type (dis => this%dis)
512  type is (disv2dtype)
513  user_nodes = this%gfr%read_int("NODES")
514  if (user_nodes /= this%dis%nodesuser) then
515  write (errmsg, fmtdiserr) &
516  trim(this%text), user_nodes, this%dis%nodesuser
517  call store_error(errmsg, terminate=.true.)
518  end if
519  idomain1d = this%gfr%read_int_1d("IDOMAIN")
520  if (.not. all(dis%idomain == idomain1d)) then
521  write (errmsg, fmtidomerr) trim(this%text)
522  call store_error(errmsg, terminate=.true.)
523  end if
524  end select
525  case ('DISV1D')
526  select type (dis => this%dis)
527  type is (disv1dtype)
528  user_nodes = this%gfr%read_int("NCELLS")
529  if (user_nodes /= this%dis%nodesuser) then
530  write (errmsg, fmtdiserr) &
531  trim(this%text), user_nodes, this%dis%nodesuser
532  call store_error(errmsg, terminate=.true.)
533  end if
534  idomain1d = this%gfr%read_int_1d("IDOMAIN")
535  if (.not. all(dis%idomain == idomain1d)) then
536  write (errmsg, fmtidomerr) trim(this%text)
537  call store_error(errmsg, terminate=.true.)
538  end if
539  end select
540  end select
541 
542  if (allocated(idomain3d)) deallocate (idomain3d)
543  if (allocated(idomain2d)) deallocate (idomain2d)
544  if (allocated(idomain1d)) deallocate (idomain1d)
545 
546  call this%gfr%finalize()
547  end subroutine read_grid
548 
549  !> @brief Initialize the budget file reader
550  subroutine initialize_bfr(this)
551  class(flowmodelinterfacetype) :: this
552  integer(I4B) :: ncrbud
553  call this%bfr%initialize(this%iubud, this%iout, ncrbud)
554  ! todo: need to run through the budget terms
555  ! and do some checking
556  end subroutine initialize_bfr
557 
558  !> @brief Advance the budget file reader
559  !!
560  !! Advance the budget file reader by reading the next chunk
561  !! of information for the current time step and stress period.
562  !<
563  subroutine advance_bfr(this)
564  ! -- modules
565  use tdismodule, only: kstp, kper
566  ! -- dummy
567  class(flowmodelinterfacetype) :: this
568  ! -- local
569  logical :: success
570  integer(I4B) :: n
571  integer(I4B) :: ipos
572  integer(I4B) :: nu, nr
573  integer(I4B) :: ip, i
574  logical :: readnext
575  ! -- format
576  character(len=*), parameter :: fmtkstpkper = &
577  "(1x,/1x,'FMI READING BUDGET TERMS &
578  &FOR KSTP ', i0, ' KPER ', i0)"
579  character(len=*), parameter :: fmtbudkstpkper = &
580  "(1x,/1x, 'FMI SETTING BUDGET TERMS &
581  &FOR KSTP ', i0, ' AND KPER ', &
582  &i0, ' TO BUDGET FILE TERMS FROM &
583  &KSTP ', i0, ' AND KPER ', i0)"
584  !
585  ! -- If the latest record read from the budget file is from a stress
586  ! -- period with only one time step, reuse that record (do not read a
587  ! -- new record) if the running model is still in that same stress period,
588  ! -- or if that record is the last one in the budget file.
589  readnext = .true.
590  if (kstp * kper > 1) then
591  if (this%bfr%header%kstp == 1) then
592  if (this%bfr%endoffile) then
593  readnext = .false.
594  else if (this%bfr%headernext%kper == kper + 1) then
595  readnext = .false.
596  end if
597  else if (this%bfr%endoffile) then
598  write (errmsg, '(4x,a)') 'REACHED END OF GWF BUDGET &
599  &FILE BEFORE READING SUFFICIENT BUDGET INFORMATION FOR THIS &
600  &GWT SIMULATION.'
601  call store_error(errmsg)
602  call store_error_unit(this%iubud)
603  end if
604  end if
605  !
606  ! -- Read the next record
607  if (readnext) then
608  !
609  ! -- Write the current time step and stress period
610  write (this%iout, fmtkstpkper) kstp, kper
611  !
612  ! -- loop through the budget terms for this stress period
613  ! i is the counter for gwf flow packages
614  ip = 1
615  do n = 1, this%bfr%nbudterms
616  call this%bfr%read_record(success, this%iout)
617  if (.not. success) then
618  write (errmsg, '(4x,a)') 'GWF BUDGET READ NOT SUCCESSFUL'
619  call store_error(errmsg)
620  call store_error_unit(this%iubud)
621  end if
622  !
623  ! -- Ensure kper is same between model and budget file
624  if (kper /= this%bfr%header%kper) then
625  write (errmsg, '(4x,a)') 'PERIOD NUMBER IN BUDGET FILE &
626  &DOES NOT MATCH PERIOD NUMBER IN TRANSPORT MODEL. IF THERE &
627  &IS MORE THAN ONE TIME STEP IN THE BUDGET FILE FOR A GIVEN &
628  &STRESS PERIOD, BUDGET FILE TIME STEPS MUST MATCH GWT MODEL &
629  &TIME STEPS ONE-FOR-ONE IN THAT STRESS PERIOD.'
630  call store_error(errmsg)
631  call store_error_unit(this%iubud)
632  end if
633  !
634  ! -- if budget file kstp > 1, then kstp must match
635  if (this%bfr%header%kstp > 1 .and. (kstp /= this%bfr%header%kstp)) then
636  write (errmsg, '(4x,a)') 'TIME STEP NUMBER IN BUDGET FILE &
637  &DOES NOT MATCH TIME STEP NUMBER IN TRANSPORT MODEL. IF THERE &
638  &IS MORE THAN ONE TIME STEP IN THE BUDGET FILE FOR A GIVEN STRESS &
639  &PERIOD, BUDGET FILE TIME STEPS MUST MATCH GWT MODEL TIME STEPS &
640  &ONE-FOR-ONE IN THAT STRESS PERIOD.'
641  call store_error(errmsg)
642  call store_error_unit(this%iubud)
643  end if
644  !
645  ! -- parse based on the type of data, and compress all user node
646  ! numbers into reduced node numbers
647  select type (h => this%bfr%header)
648  type is (budgetfileheadertype)
649  select case (trim(adjustl(h%budtxt)))
650  case ('FLOW-JA-FACE')
651  !
652  ! -- bfr%flowja contains only reduced connections so there is
653  ! a one-to-one match with this%gwfflowja
654  do ipos = 1, size(this%bfr%flowja)
655  this%gwfflowja(ipos) = this%bfr%flowja(ipos)
656  end do
657  case ('DATA-SPDIS')
658  do i = 1, h%nlist
659  nu = this%bfr%nodesrc(i)
660  nr = this%dis%get_nodenumber(nu, 0)
661  if (nr <= 0) cycle
662  this%gwfspdis(1, nr) = this%bfr%auxvar(1, i)
663  this%gwfspdis(2, nr) = this%bfr%auxvar(2, i)
664  this%gwfspdis(3, nr) = this%bfr%auxvar(3, i)
665  end do
666  case ('DATA-SAT')
667  do i = 1, h%nlist
668  nu = this%bfr%nodesrc(i)
669  nr = this%dis%get_nodenumber(nu, 0)
670  if (nr <= 0) cycle
671  this%gwfsat(nr) = this%bfr%auxvar(1, i)
672  end do
673  case ('STO-SS')
674  do nu = 1, this%dis%nodesuser
675  nr = this%dis%get_nodenumber(nu, 0)
676  if (nr <= 0) cycle
677  this%gwfstrgss(nr) = this%bfr%flow(nu)
678  end do
679  case ('STO-SY')
680  do nu = 1, this%dis%nodesuser
681  nr = this%dis%get_nodenumber(nu, 0)
682  if (nr <= 0) cycle
683  this%gwfstrgsy(nr) = this%bfr%flow(nu)
684  end do
685  case default
686  call this%gwfpackages(ip)%copy_values( &
687  h%nlist, &
688  this%bfr%nodesrc, &
689  this%bfr%flow, &
690  this%bfr%auxvar)
691  do i = 1, this%gwfpackages(ip)%nbound
692  nu = this%gwfpackages(ip)%nodelist(i)
693  nr = this%dis%get_nodenumber(nu, 0)
694  this%gwfpackages(ip)%nodelist(i) = nr
695  end do
696  ip = ip + 1
697  end select
698  end select
699  end do
700  else
701  !
702  ! -- write message to indicate that flows are being reused
703  write (this%iout, fmtbudkstpkper) kstp, kper, &
704  this%bfr%header%kstp, this%bfr%header%kper
705  !
706  ! -- set the flag to indicate that flows were not updated
707  this%iflowsupdated = 0
708  end if
709  end subroutine advance_bfr
710 
711  !> @brief Finalize the budget file reader
712  subroutine finalize_bfr(this)
713  class(flowmodelinterfacetype) :: this
714  call this%bfr%finalize()
715  end subroutine finalize_bfr
716 
717  !> @brief Initialize the head file reader
718  subroutine initialize_hfr(this)
719  class(flowmodelinterfacetype) :: this
720  call this%hfr%initialize(this%iuhds, this%iout)
721  ! todo: need to run through the head terms
722  ! and do some checking
723  end subroutine initialize_hfr
724 
725  !> @brief Advance the head file reader
726  subroutine advance_hfr(this)
727  ! modules
728  use tdismodule, only: kstp, kper
729  class(flowmodelinterfacetype) :: this
730  integer(I4B) :: nu, nr, i, ilay
731  integer(I4B) :: ncpl
732  real(DP) :: val
733  logical :: readnext
734  logical :: success
735  character(len=*), parameter :: fmtkstpkper = &
736  "(1x,/1x,'FMI READING HEAD FOR &
737  &KSTP ', i0, ' KPER ', i0)"
738  character(len=*), parameter :: fmthdskstpkper = &
739  "(1x,/1x, 'FMI SETTING HEAD FOR KSTP ', i0, ' AND KPER ', &
740  &i0, ' TO BINARY FILE HEADS FROM KSTP ', i0, ' AND KPER ', i0)"
741  !
742  ! -- If the latest record read from the head file is from a stress
743  ! -- period with only one time step, reuse that record (do not read a
744  ! -- new record) if the running model is still in that same stress period,
745  ! -- or if that record is the last one in the head file.
746  readnext = .true.
747  if (kstp * kper > 1) then
748  if (this%hfr%header%kstp == 1) then
749  if (this%hfr%endoffile) then
750  readnext = .false.
751  else if (this%hfr%headernext%kper == kper + 1) then
752  readnext = .false.
753  end if
754  else if (this%hfr%endoffile) then
755  write (errmsg, '(4x,a)') 'REACHED END OF GWF HEAD &
756  &FILE BEFORE READING SUFFICIENT HEAD INFORMATION FOR THIS &
757  &GWT SIMULATION.'
758  call store_error(errmsg)
759  call store_error_unit(this%iuhds)
760  end if
761  end if
762  !
763  ! -- Read the next record
764  if (readnext) then
765  !
766  ! -- write to list file that heads are being read
767  write (this%iout, fmtkstpkper) kstp, kper
768  !
769  ! -- loop through the layered heads for this time step
770  do ilay = 1, this%hfr%nlay
771  !
772  ! -- read next head chunk
773  call this%hfr%read_record(success, this%iout)
774  if (.not. success) then
775  write (errmsg, '(4x,a)') 'GWF HEAD READ NOT SUCCESSFUL'
776  call store_error(errmsg)
777  call store_error_unit(this%iuhds)
778  end if
779  !
780  ! -- Ensure kper is same between model and head file
781  if (kper /= this%hfr%header%kper) then
782  write (errmsg, '(4x,a)') 'PERIOD NUMBER IN HEAD FILE &
783  &DOES NOT MATCH PERIOD NUMBER IN TRANSPORT MODEL. IF THERE &
784  &IS MORE THAN ONE TIME STEP IN THE HEAD FILE FOR A GIVEN STRESS &
785  &PERIOD, HEAD FILE TIME STEPS MUST MATCH GWT MODEL TIME STEPS &
786  &ONE-FOR-ONE IN THAT STRESS PERIOD.'
787  call store_error(errmsg)
788  call store_error_unit(this%iuhds)
789  end if
790  !
791  ! -- if head file kstp > 1, then kstp must match
792  if (this%hfr%header%kstp > 1 .and. (kstp /= this%hfr%header%kstp)) then
793  write (errmsg, '(4x,a)') 'TIME STEP NUMBER IN HEAD FILE &
794  &DOES NOT MATCH TIME STEP NUMBER IN TRANSPORT MODEL. IF THERE &
795  &IS MORE THAN ONE TIME STEP IN THE HEAD FILE FOR A GIVEN STRESS &
796  &PERIOD, HEAD FILE TIME STEPS MUST MATCH GWT MODEL TIME STEPS &
797  &ONE-FOR-ONE IN THAT STRESS PERIOD.'
798  call store_error(errmsg)
799  call store_error_unit(this%iuhds)
800  end if
801  !
802  ! -- fill the head array for this layer and
803  ! compress into reduced form
804  ncpl = size(this%hfr%head)
805  do i = 1, ncpl
806  nu = (ilay - 1) * ncpl + i
807  nr = this%dis%get_nodenumber(nu, 0)
808  val = this%hfr%head(i)
809  if (nr > 0) this%gwfhead(nr) = val
810  end do
811  end do
812  else
813  write (this%iout, fmthdskstpkper) kstp, kper, &
814  this%hfr%header%kstp, this%hfr%header%kper
815  end if
816  end subroutine advance_hfr
817 
818  !> @brief Finalize the head file reader
819  subroutine finalize_hfr(this)
820  class(flowmodelinterfacetype) :: this
821  close (this%iuhds)
822  end subroutine finalize_hfr
823 
824  !> @brief Initialize gwf terms from budget file
825  !!
826  !! initialize terms and figure out how many
827  !! different terms and packages are contained within the file
828  !<
830  ! -- modules
832  ! -- dummy
833  class(flowmodelinterfacetype) :: this
834  ! -- local
835  integer(I4B) :: nflowpack
836  integer(I4B) :: i, ip
837  integer(I4B) :: naux
838  logical :: found_flowja
839  logical :: found_dataspdis
840  logical :: found_datasat
841  logical :: found_stoss
842  logical :: found_stosy
843  integer(I4B), dimension(:), allocatable :: imap
844  !
845  ! -- Calculate the number of gwf flow packages
846  allocate (imap(this%bfr%nbudterms))
847  imap(:) = 0
848  nflowpack = 0
849  found_flowja = .false.
850  found_dataspdis = .false.
851  found_datasat = .false.
852  found_stoss = .false.
853  found_stosy = .false.
854  do i = 1, this%bfr%nbudterms
855  select case (trim(adjustl(this%bfr%budtxtarray(i))))
856  case ('FLOW-JA-FACE')
857  found_flowja = .true.
858  case ('DATA-SPDIS')
859  found_dataspdis = .true.
860  case ('DATA-SAT')
861  found_datasat = .true.
862  case ('STO-SS')
863  found_stoss = .true.
864  this%igwfstrgss = 1
865  case ('STO-SY')
866  found_stosy = .true.
867  this%igwfstrgsy = 1
868  case default
869  nflowpack = nflowpack + 1
870  imap(i) = 1
871  end select
872  end do
873  !
874  ! -- allocate gwfpackage arrays
875  call this%allocate_gwfpackages(nflowpack)
876  !
877  ! -- Copy the package name and aux names from budget file reader
878  ! to the gwfpackages derived-type variable
879  ip = 1
880  do i = 1, this%bfr%nbudterms
881  if (imap(i) == 0) cycle
882  call this%gwfpackages(ip)%set_name(this%bfr%dstpackagenamearray(i), &
883  this%bfr%budtxtarray(i))
884  naux = this%bfr%nauxarray(i)
885  call this%gwfpackages(ip)%set_auxname(naux, this%bfr%auxtxtarray(1:naux, i))
886  ip = ip + 1
887  end do
888  !
889  ! -- Copy just the package names for the boundary packages into
890  ! the flowpacknamearray
891  ip = 1
892  do i = 1, size(imap)
893  if (imap(i) == 1) then
894  this%flowpacknamearray(ip) = this%bfr%dstpackagenamearray(i)
895  ip = ip + 1
896  end if
897  end do
898  !
899  ! -- Error if specific discharge, saturation or flowja not found
900  if (.not. found_dataspdis) then
901  write (errmsg, '(4x,a)') 'SPECIFIC DISCHARGE NOT FOUND IN &
902  &BUDGET FILE. SAVE_SPECIFIC_DISCHARGE AND &
903  &SAVE_FLOWS MUST BE ACTIVATED IN THE NPF PACKAGE.'
904  call store_error(errmsg)
905  end if
906  if (.not. found_datasat) then
907  write (errmsg, '(4x,a)') 'SATURATION NOT FOUND IN &
908  &BUDGET FILE. SAVE_SATURATION AND &
909  &SAVE_FLOWS MUST BE ACTIVATED IN THE NPF PACKAGE.'
910  call store_error(errmsg)
911  end if
912  if (.not. found_flowja) then
913  write (errmsg, '(4x,a)') 'FLOWJA NOT FOUND IN &
914  &BUDGET FILE. SAVE_FLOWS MUST &
915  &BE ACTIVATED IN THE NPF PACKAGE.'
916  call store_error(errmsg)
917  end if
918  if (count_errors() > 0) then
919  call store_error_filename(this%input_fname)
920  end if
921  end subroutine initialize_gwfterms_from_bfr
922 
923  !> @brief Initialize gwf terms from a GWF exchange
925  ! -- modules
926  use bndmodule, only: bndtype, getbndfromlist
927  ! -- dummy
928  class(flowmodelinterfacetype) :: this
929  ! -- local
930  integer(I4B) :: ngwfpack
931  integer(I4B) :: ngwfterms
932  integer(I4B) :: ip
933  integer(I4B) :: imover
934  integer(I4B) :: ntomvr
935  integer(I4B) :: iterm
936  character(len=LENPACKAGENAME) :: budtxt
937  class(bndtype), pointer :: packobj => null()
938  !
939  ! -- determine size of gwf terms
940  ngwfpack = this%gwfbndlist%Count()
941  !
942  ! -- Count number of to-mvr terms, but do not include advanced packages
943  ! as those mover terms are not losses from the cell, but rather flows
944  ! within the advanced package
945  ntomvr = 0
946  do ip = 1, ngwfpack
947  packobj => getbndfromlist(this%gwfbndlist, ip)
948  imover = packobj%imover
949  if (packobj%isadvpak /= 0) imover = 0
950  if (imover /= 0) then
951  ntomvr = ntomvr + 1
952  end if
953  end do
954  !
955  ! -- Allocate arrays in fmi of size ngwfterms, which is the number of
956  ! packages plus the number of packages with mover terms.
957  ngwfterms = ngwfpack + ntomvr
958  call this%allocate_gwfpackages(ngwfterms)
959  !
960  ! -- Assign values in the fmi package
961  iterm = 1
962  do ip = 1, ngwfpack
963  !
964  ! -- set and store names
965  packobj => getbndfromlist(this%gwfbndlist, ip)
966  budtxt = adjustl(packobj%text)
967  call this%gwfpackages(iterm)%set_name(packobj%packName, budtxt)
968  this%flowpacknamearray(iterm) = packobj%packName
969  iterm = iterm + 1
970  !
971  ! -- if this package has a mover associated with it, then add another
972  ! term that corresponds to the mover flows
973  imover = packobj%imover
974  if (packobj%isadvpak /= 0) imover = 0
975  if (imover /= 0) then
976  budtxt = trim(adjustl(packobj%text))//'-TO-MVR'
977  call this%gwfpackages(iterm)%set_name(packobj%packName, budtxt)
978  this%flowpacknamearray(iterm) = packobj%packName
979  this%igwfmvrterm(iterm) = 1
980  iterm = iterm + 1
981  end if
982  end do
984 
985  !> @brief Allocate budget packages
986  !!
987  !! gwfpackages is an array of PackageBudget objects.
988  !! This routine allocates gwfpackages to the proper size and initializes some
989  !! member variables.
990  !<
991  subroutine allocate_gwfpackages(this, ngwfterms)
992  ! -- modules
993  use constantsmodule, only: lenmempath
995  ! -- dummy
996  class(flowmodelinterfacetype) :: this
997  integer(I4B), intent(in) :: ngwfterms
998  ! -- local
999  integer(I4B) :: n
1000  character(len=LENMEMPATH) :: memPath
1001  !
1002  ! -- direct allocate
1003  allocate (this%gwfpackages(ngwfterms))
1004  allocate (this%flowpacknamearray(ngwfterms))
1005  !
1006  ! -- mem_allocate
1007  call mem_allocate(this%igwfmvrterm, ngwfterms, 'IGWFMVRTERM', this%memoryPath)
1008  !
1009  ! -- initialize
1010  this%nflowpack = ngwfterms
1011  do n = 1, this%nflowpack
1012  this%igwfmvrterm(n) = 0
1013  this%flowpacknamearray(n) = ''
1014  !
1015  ! -- Create a mempath for each individual flow package data set
1016  ! of the form, MODELNAME/FMI-FTn
1017  write (mempath, '(a, i0)') trim(this%memoryPath)//'-FT', n
1018  call this%gwfpackages(n)%initialize(mempath)
1019  end do
1020  end subroutine allocate_gwfpackages
1021 
1022  !> @brief Deallocate memory in the gwfpackages array
1023  subroutine deallocate_gwfpackages(this)
1024  class(flowmodelinterfacetype) :: this
1025  integer(I4B) :: n
1026 
1027  do n = 1, this%nflowpack
1028  call this%gwfpackages(n)%da()
1029  end do
1030  end subroutine deallocate_gwfpackages
1031 
1032  !> @brief Find the package index for the package with the given name
1033  subroutine get_package_index(this, name, idx)
1034  use bndmodule, only: bndtype, getbndfromlist
1035  class(flowmodelinterfacetype) :: this
1036  character(len=*), intent(in) :: name
1037  integer(I4B), intent(inout) :: idx
1038  ! -- local
1039  integer(I4B) :: ip
1040  !
1041  ! -- Look through all the packages and return the index with name
1042  idx = 0
1043  do ip = 1, size(this%flowpacknamearray)
1044  if (this%flowpacknamearray(ip) == name) then
1045  idx = ip
1046  exit
1047  end if
1048  end do
1049  if (idx == 0) then
1050  call store_error('Error in get_package_index. Could not find '//name, &
1051  terminate=.true.)
1052  end if
1053  end subroutine get_package_index
1054 
1055 end module flowmodelinterfacemodule
This module contains the base boundary package.
class(bndtype) function, pointer, public getbndfromlist(list, idx)
Get boundary from package list.
subroutine, public budgetobject_cr_bfr(this, name, ibinun, iout, colconv1, colconv2)
Create a new budget object from a binary flow file.
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 lenpackagename
maximum length of the package name
Definition: Constants.f90:23
integer(i4b), parameter lenvarname
maximum length of a variable name
Definition: Constants.f90:17
real(dp), parameter dhalf
real constant 1/2
Definition: Constants.f90:68
real(dp), parameter dem6
real constant 1e-6
Definition: Constants.f90:109
real(dp), parameter dzero
real constant zero
Definition: Constants.f90:65
integer(i4b), parameter lenbudtxt
maximum length of a budget component names
Definition: Constants.f90:37
integer(i4b), parameter lenmempath
maximum length of the memory path
Definition: Constants.f90:27
real(dp), parameter done
real constant 1
Definition: Constants.f90:76
Definition: Dis.f90:1
subroutine allocate_scalars(this)
Allocate scalars.
subroutine fmi_ar(this, ibound)
Allocate the package.
subroutine allocate_gwfpackages(this, ngwfterms)
Allocate budget packages.
subroutine read_grid(this)
Validate flow model grid.
subroutine deallocate_gwfpackages(this)
Deallocate memory in the gwfpackages array.
subroutine finalize_hfr(this)
Finalize the head file reader.
subroutine fmi_df(this, dis, idryinactive)
Define the flow model interface.
subroutine get_package_index(this, name, idx)
Find the package index for the package with the given name.
subroutine advance_bfr(this)
Advance the budget file reader.
subroutine initialize_gwfterms_from_gwfbndlist(this)
Initialize gwf terms from a GWF exchange.
subroutine initialize_hfr(this)
Initialize the head file reader.
subroutine source_options(this)
@ brief Source input options for package
subroutine fmi_da(this)
Deallocate variables.
subroutine source_packagedata(this)
@ brief Source input options for package
subroutine advance_hfr(this)
Advance the head file reader.
subroutine initialize_gwfterms_from_bfr(this)
Initialize gwf terms from budget file.
subroutine finalize_bfr(this)
Finalize the budget file reader.
subroutine allocate_arrays(this, nodes)
Allocate arrays.
subroutine initialize_bfr(this)
Initialize the budget file reader.
subroutine, public urdaux(naux, inunit, iout, lloc, istart, istop, auxname, line, text)
Read auxiliary variables from an input line.
integer(i4b) function, public getunit()
Get a free unit number.
subroutine, public openfile(iu, iout, fname, ftype, fmtarg_opt, accarg_opt, filstat_opt, mode_opt)
Open a file.
Definition: InputOutput.f90:30
This module defines variable data types.
Definition: kind.f90:8
This module contains the base numerical package type.
character(len=20) access
Definition: OpenSpec.f90:7
character(len=20) form
Definition: OpenSpec.f90:7
This module contains the PackageBudgetModule Module.
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:203
subroutine, public store_error_unit(iunit, terminate)
Store the file unit number.
Definition: Sim.f90:168
This module contains simulation variables.
Definition: SimVariables.f90:9
character(len=maxcharlen) errmsg
error message string
integer(i4b), pointer, public kstp
current time step number
Definition: tdis.f90:24
integer(i4b), pointer, public kper
current stress period number
Definition: tdis.f90:23
@ brief BndType
This class is used to store a single deferred-length character string. It was designed to work in an ...
Definition: CharString.f90:23
Structured grid discretization.
Definition: Dis2d.f90:23
Structured grid discretization.
Definition: Dis.f90:23
Unstructured grid discretization.
Definition: Disu.f90:28
Vertex grid discretization.
Definition: Disv2d.f90:24
Vertex grid discretization.
Definition: Disv.f90:24
A generic heterogeneous doubly-linked list.
Definition: List.f90:14
Derived type for storing flows.