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