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