MODFLOW 6  version 6.8.0.dev0
USGS Modular Hydrologic Model
prt-fmi.f90
Go to the documentation of this file.
2 
3  use kindmodule, only: dp, i4b, lgp
4  use errorutilmodule, only: pstop
6  use simmodule, only: store_error
7  use simvariablesmodule, only: errmsg
9  use basedismodule, only: disbasetype
11 
12  implicit none
13  private
14  public :: prtfmitype
15  public :: fmi_cr
17 
18  character(len=LENPACKAGENAME) :: text = ' PRTFMI'
19 
20  !> @brief IFLOWFACE numbers for top and bottom faces
21  enum, bind(c)
22  enumerator :: iflowface_top = -1
23  enumerator :: iflowface_bottom = -2
24  end enum
25 
27  private
28  integer(I4B), public :: max_faces !< maximum number of 3d cell faces
29  real(dp), allocatable, public :: sourceflows(:) ! cell source flows array
30  real(dp), allocatable, public :: sinkflows(:) ! cell sink flows array
31  real(dp), allocatable, public :: storageflows(:) ! cell storage flows array
32  real(dp), allocatable, public :: boundaryflows(:, :) ! cell boundary flows array
33  integer(I4B), allocatable, public :: boundaryfaces(:) ! bitmask of assigned boundary faces
34 
35  contains
36 
37  procedure :: fmi_ad
38  procedure :: fmi_df => prtfmi_df
39  procedure, private :: accumulate_flows
40  procedure :: mark_boundary_face
41  procedure :: is_boundary_face
43  procedure, private :: iflowface_to_icellface
44 
45  end type prtfmitype
46 
47 contains
48 
49  !> @brief Create a new PrtFmi object
50  subroutine fmi_cr(fmiobj, name_model, input_mempath, inunit, iout)
51  ! dummy
52  type(prtfmitype), pointer :: fmiobj
53  character(len=*), intent(in) :: name_model
54  character(len=*), intent(in) :: input_mempath
55  integer(I4B), intent(inout) :: inunit
56  integer(I4B), intent(in) :: iout
57 
58  ! Create the object
59  allocate (fmiobj)
60 
61  ! create name and memory path
62  call fmiobj%set_names(1, name_model, 'FMI', 'FMI', input_mempath)
63  fmiobj%text = text
64 
65  ! Allocate scalars
66  call fmiobj%allocate_scalars()
67 
68  ! Set variables
69  fmiobj%inunit = inunit
70  fmiobj%iout = iout
71 
72  ! Assign dependent variable label
73  fmiobj%depvartype = 'TRACKS '
74 
75  end subroutine fmi_cr
76 
77  !> @brief Time step advance
78  subroutine fmi_ad(this)
79  ! modules
80  use constantsmodule, only: dhdry
81  ! dummy
82  class(prtfmitype) :: this
83  ! local
84  integer(I4B) :: n
85  character(len=15) :: nodestr
86  character(len=*), parameter :: fmtdry = &
87  &"(/1X,'WARNING: DRY CELL ENCOUNTERED AT ',a,'; RESET AS INACTIVE')"
88  character(len=*), parameter :: fmtrewet = &
89  &"(/1X,'DRY CELL REACTIVATED AT ', a)"
90 
91  ! Set flag to indicated that flows are being updated. For the case where
92  ! flows may be reused (only when flows are read from a file) then set
93  ! the flag to zero to indicated that flows were not updated
94  this%iflowsupdated = 1
95 
96  ! If reading flows from a budget file, read the next set of records
97  if (this%iubud /= 0) call this%advance_bfr()
98 
99  ! If reading heads from a head file, read the next set of records
100  if (this%iuhds /= 0) call this%advance_hfr()
101 
102  ! If mover flows are being read from file, read the next set of records
103  if (this%iumvr /= 0) &
104  call this%mvrbudobj%bfr_advance(this%dis, this%iout)
105 
106  ! Accumulate flows
107  call this%accumulate_flows()
108 
109  ! if flow cell is dry, then set this%ibound = 0
110  do n = 1, this%dis%nodes
111  ! Calculate the ibound-like array that has 0 if saturation
112  ! is zero and 1 otherwise
113  if (this%gwfsat(n) > dzero) then
114  this%ibdgwfsat0(n) = 1
115  else
116  this%ibdgwfsat0(n) = 0
117  end if
118 
119  ! Check if active model cell is inactive for flow
120  if (this%ibound(n) > 0) then
121  if (this%gwfhead(n) == dhdry) then
122  ! cell should be made inactive
123  this%ibound(n) = 0
124  call this%dis%noder_to_string(n, nodestr)
125  write (this%iout, fmtdry) trim(nodestr)
126  end if
127  end if
128 
129  ! Convert dry model cell to active if flow has rewet
130  if (this%ibound(n) == 0) then
131  if (this%gwfhead(n) /= dhdry) then
132  ! cell is now wet
133  this%ibound(n) = 1
134  call this%dis%noder_to_string(n, nodestr)
135  write (this%iout, fmtrewet) trim(nodestr)
136  end if
137  end if
138  end do
139 
140  end subroutine fmi_ad
141 
142  !> @brief Define the flow model interface
143  subroutine prtfmi_df(this, dis, idryinactive)
144  class(prtfmitype) :: this
145  class(disbasetype), pointer, intent(in) :: dis
146  integer(I4B), intent(in) :: idryinactive
147 
148  call this%FlowModelInterfaceType%fmi_df(dis, idryinactive)
149 
150  this%max_faces = this%dis%get_max_npolyverts() + 2
151  if (this%max_faces > 32) then
152  write (errmsg, '(a,i0,a,i0,a)') &
153  'DISV grid contains a cell with ', this%max_faces - 2, &
154  ' lateral faces. Cells may have at most 30 lateral faces.'
155  call store_error(errmsg)
156  call this%parser%StoreErrorUnit()
157  return
158  end if
159 
160  allocate (this%StorageFlows(this%dis%nodes))
161  allocate (this%SourceFlows(this%dis%nodes))
162  allocate (this%SinkFlows(this%dis%nodes))
163  allocate (this%BoundaryFlows(this%dis%nodes, this%max_faces))
164  allocate (this%BoundaryFaces(this%dis%nodes))
165 
166  end subroutine prtfmi_df
167 
168  !> @brief Accumulate flows
169  subroutine accumulate_flows(this)
170  ! dummy
171  class(prtfmitype) :: this
172  ! local
173  integer(I4B) :: j, i, ip, ib
174  integer(I4B) :: iflowface, iauxiflowface, icellface
175  real(DP) :: qbnd
176  character(len=LENAUXNAME) :: auxname
177  integer(I4B) :: naux
178 
179  this%StorageFlows = dzero
180  if (this%igwfstrgss /= 0) &
181  this%StorageFlows = this%StorageFlows + this%gwfstrgss
182  if (this%igwfstrgsy /= 0) &
183  this%StorageFlows = this%StorageFlows + this%gwfstrgsy
184 
185  this%SourceFlows = dzero
186  this%SinkFlows = dzero
187  this%BoundaryFlows = dzero
188  this%BoundaryFaces = 0
189  do ip = 1, this%nflowpack
190  iauxiflowface = 0
191  naux = this%gwfpackages(ip)%naux
192  if (naux > 0) then
193  do j = 1, naux
194  auxname = this%gwfpackages(ip)%auxname(j)
195  if (trim(adjustl(auxname)) == "IFLOWFACE") then
196  iauxiflowface = j
197  exit
198  end if
199  end do
200  end if
201  do ib = 1, this%gwfpackages(ip)%nbound
202  i = this%gwfpackages(ip)%nodelist(ib)
203  if (i <= 0) cycle
204  if (this%ibound(i) <= 0) cycle
205  qbnd = this%gwfpackages(ip)%get_flow(ib)
206  ! todo, after initial release: default iflowface values for different packages
207  iflowface = 0
208  icellface = 0
209  if (iauxiflowface > 0) then
210  iflowface = nint(this%gwfpackages(ip)%auxvar(iauxiflowface, ib))
211  icellface = this%iflowface_to_icellface(iflowface)
212  end if
213  if (icellface > 0) then
214  call this%mark_boundary_face(i, icellface)
215  this%BoundaryFlows(i, icellface) = &
216  this%BoundaryFlows(i, icellface) + qbnd
217  else if (qbnd .gt. dzero) then
218  this%SourceFlows(i) = this%SourceFlows(i) + qbnd
219  else if (qbnd .lt. dzero) then
220  this%SinkFlows(i) = this%SinkFlows(i) + qbnd
221  end if
222  end do
223  end do
224 
225  end subroutine accumulate_flows
226 
227  !> @brief Mark a face as a boundary face.
228  subroutine mark_boundary_face(this, ic, icellface)
229  class(prtfmitype) :: this
230  integer(I4B), intent(in) :: ic !< node number (reduced)
231  integer(I4B), intent(in) :: icellface !< cell face number
232  ! local
233  integer(I4B) :: bit_pos
234 
235  if (ic <= 0 .or. ic > this%dis%nodes) then
236  print *, 'Invalid cell number: ', ic
237  print *, 'Expected a value in range [1, ', this%dis%nodes, ']'
238  call pstop(1)
239  end if
240  if (icellface <= 0) then
241  print *, 'Invalid face number: ', icellface
242  print *, 'Expected a value in range [1, ', this%max_faces, ']'
243  call pstop(1)
244  end if
245  bit_pos = icellface - 1 ! bit position 0-based
246  if (bit_pos < 0 .or. bit_pos > 31) then
247  print *, 'Invalid bitmask position: ', bit_pos
248  print *, 'Expected a value in range [0, 31]'
249  call pstop(1)
250  end if
251  this%BoundaryFaces(ic) = ibset(this%BoundaryFaces(ic), bit_pos)
252  end subroutine mark_boundary_face
253 
254  !> @brief Check if a face is assigned to a boundary package.
255  function is_boundary_face(this, ic, icellface) result(is_boundary)
256  class(prtfmitype) :: this
257  integer(I4B), intent(in) :: ic !< node number (reduced)
258  integer(I4B), intent(in) :: icellface !< cell face number
259  logical(LGP) :: is_boundary
260  ! local
261  integer(I4B) :: bit_pos
262 
263  is_boundary = .false.
264  if (ic <= 0 .or. ic > this%dis%nodes) then
265  print *, 'Invalid cell number: ', ic
266  print *, 'Expected a value in range [1, ', this%dis%nodes, ']'
267  call pstop(1)
268  end if
269  if (icellface <= 0) then
270  print *, 'Invalid face number: ', icellface
271  print *, 'Expected a value in range [1, ', this%max_faces, ']'
272  call pstop(1)
273  end if
274  bit_pos = icellface - 1 ! bit position 0-based
275  if (bit_pos < 0 .or. bit_pos > 31) then
276  print *, 'Invalid bitmask position: ', bit_pos
277  print *, 'Expected a value in range [0, 31]'
278  call pstop(1)
279  end if
280  is_boundary = btest(this%BoundaryFaces(ic), bit_pos)
281  end function is_boundary_face
282 
283  !> @brief Check if a face is an assigned boundary with net outflow.
284  function is_net_out_boundary_face(this, ic, icellface) &
285  result(is_net_out_boundary)
286  class(prtfmitype) :: this
287  integer(I4B), intent(in) :: ic !< node number (reduced)
288  integer(I4B), intent(in) :: icellface !< cell face number
289  logical(LGP) :: is_net_out_boundary
290 
291  is_net_out_boundary = .false.
292  if (.not. this%is_boundary_face(ic, icellface)) return
293  if (this%BoundaryFlows(ic, icellface) < dzero) &
294  is_net_out_boundary = .true.
295  end function is_net_out_boundary_face
296 
297  !> @brief Convert an iflowface number to a cell face number.
298  !! Maps bottom (-2) -> max_faces - 1, top (-1) -> max_faces.
299  function iflowface_to_icellface(this, iflowface) result(icellface)
300  class(prtfmitype), intent(inout) :: this
301  integer(I4B), intent(in) :: iflowface
302  integer(I4B) :: icellface
303 
304  icellface = iflowface
305  if (icellface < 0) icellface = icellface + this%max_faces - iflowface_top
306  end function iflowface_to_icellface
307 
308 end module prtfmimodule
This module contains simulation constants.
Definition: Constants.f90:9
real(dp), parameter dhdry
real dry cell constant
Definition: Constants.f90:94
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
integer(i4b), parameter lenauxname
maximum length of a aux variable
Definition: Constants.f90:35
real(dp), parameter dzero
real constant zero
Definition: Constants.f90:65
subroutine pstop(status, message)
Stop the program, optionally specifying an error status code.
Definition: ErrorUtil.f90:24
This module defines variable data types.
Definition: kind.f90:8
subroutine accumulate_flows(this)
Accumulate flows.
Definition: prt-fmi.f90:170
subroutine mark_boundary_face(this, ic, icellface)
Mark a face as a boundary face.
Definition: prt-fmi.f90:229
integer(i4b) function iflowface_to_icellface(this, iflowface)
Convert an iflowface number to a cell face number. Maps bottom (-2) -> max_faces - 1,...
Definition: prt-fmi.f90:300
subroutine fmi_ad(this)
Time step advance.
Definition: prt-fmi.f90:79
subroutine prtfmi_df(this, dis, idryinactive)
Define the flow model interface.
Definition: prt-fmi.f90:144
logical(lgp) function is_boundary_face(this, ic, icellface)
Check if a face is assigned to a boundary package.
Definition: prt-fmi.f90:256
@, public iflowface_bottom
Definition: prt-fmi.f90:23
@, public iflowface_top
Definition: prt-fmi.f90:22
character(len=lenpackagename) text
Definition: prt-fmi.f90:18
subroutine, public fmi_cr(fmiobj, name_model, input_mempath, inunit, iout)
Create a new PrtFmi object.
Definition: prt-fmi.f90:51
logical(lgp) function is_net_out_boundary_face(this, ic, icellface)
Check if a face is an assigned boundary with net outflow.
Definition: prt-fmi.f90:286
This module contains simulation methods.
Definition: Sim.f90:10
subroutine, public store_error(msg, terminate)
Store an error message.
Definition: Sim.f90:92
This module contains simulation variables.
Definition: SimVariables.f90:9
character(len=maxcharlen) errmsg
error message string