MODFLOW 6  version 6.9.0.dev0
USGS Modular Hydrologic Model
GridFileReader.f90
Go to the documentation of this file.
2 
3  use kindmodule
5  use simvariablesmodule, only: errmsg
6  use constantsmodule, only: linelength
10 
11  implicit none
12 
13  public :: gridfilereadertype
14 
15  integer(I4B), parameter :: typ_int = 1 !< integer variable type
16  integer(I4B), parameter :: typ_dbl = 2 !< double precision variable type
17  integer(I4B), parameter :: typ_chr = 3 !< character variable type
18 
20  private
21  integer(I4B), public :: inunit !< file unit
22  ! header
23  character(len=10), public :: grid_type !< DIS, DISV, DISU, etc
24  integer(I4B), public :: version !< binary grid file format version
25  integer(I4B) :: ntxt !< number of variables
26  integer(I4B) :: lentxt !< header line length per variable
27  ! index
28  type(hashtabletype), pointer :: idx !< map variable name to variable index
29  character(len=10), allocatable, public :: names(:) !< variable names
30  integer(I4B), allocatable :: ndims(:) !< variable number of dims
31  integer(I4B), allocatable :: typs(:) !< variable type (TYP_INT, TYP_DBL, TYP_CHR)
32  integer(I4B), allocatable :: shp_start(:) !< variable shape start in shp
33  integer(I8B), allocatable :: pos(:) !< variable position in file
34  integer(I4B), allocatable :: shp(:) !< flat array of variable shapes
35  contains
36  procedure, public :: initialize
37  procedure, public :: finalize
38  procedure, public :: has_variable
39  ! header-reading subroutines
40  procedure, private :: read_header
41  procedure, private :: read_header_meta
42  procedure, private :: read_header_body
43  procedure, private :: lookup
44  ! scalar read functions
45  procedure, public :: read_int
46  procedure, public :: read_dbl
47  procedure, public :: read_grid_shape
48  ! array read functions (allocate and return)
49  procedure, public :: read_int_1d
50  procedure, public :: read_dbl_1d
51  procedure, public :: read_charstr
52  ! array read subroutines (populate preallocated)
53  procedure, public :: read_int_1d_into
54  procedure, public :: read_dbl_1d_into
55  procedure, public :: read_charstr_into
56  end type gridfilereadertype
57 
58 contains
59 
60  !> @Brief Initialize the grid file reader.
61  subroutine initialize(this, iu)
62  class(gridfilereadertype) :: this
63  integer(I4B), intent(in) :: iu
64 
65  this%inunit = iu
66  call hash_table_cr(this%idx)
67  allocate (this%shp(0))
68  call this%read_header()
69 
70  end subroutine initialize
71 
72  !> @brief Finalize the grid file reader.
73  subroutine finalize(this)
74  class(gridfilereadertype) :: this
75 
76  close (this%inunit)
77  call hash_table_da(this%idx)
78  if (allocated(this%names)) deallocate (this%names)
79  if (allocated(this%ndims)) deallocate (this%ndims)
80  if (allocated(this%typs)) deallocate (this%typs)
81  if (allocated(this%shp_start)) deallocate (this%shp_start)
82  if (allocated(this%pos)) deallocate (this%pos)
83  if (allocated(this%shp)) deallocate (this%shp)
84 
85  end subroutine finalize
86 
87  !> @brief Read the file's self-describing header. Internal use only.
88  subroutine read_header(this)
89  class(gridfilereadertype) :: this
90  call this%read_header_meta()
91  call this%read_header_body()
92  end subroutine read_header
93 
94  !> @brief Read self-describing metadata (first four lines). Internal use only.
95  subroutine read_header_meta(this)
96  ! dummy
97  class(gridfilereadertype) :: this
98  ! local
99  character(len=50) :: line
100  integer(I4B) :: lloc, istart, istop
101  integer(I4B) :: ival
102  real(DP) :: rval
103 
104  ! grid type
105  read (this%inunit) line
106  lloc = 1
107  call urword(line, lloc, istart, istop, 1, ival, rval, 0, 0)
108  if (line(istart:istop) /= 'GRID') then
109  call store_error('Binary grid file must begin with "GRID". '//&
110  &'Found: '//line(istart:istop))
111  call store_error_unit(this%inunit)
112  end if
113  call urword(line, lloc, istart, istop, 1, ival, rval, 0, 0)
114  this%grid_type = line(istart:istop)
115 
116  ! version
117  read (this%inunit) line
118  lloc = 1
119  call urword(line, lloc, istart, istop, 0, ival, rval, 0, 0)
120  call urword(line, lloc, istart, istop, 2, ival, rval, 0, 0)
121  this%version = ival
122 
123  ! ntxt
124  read (this%inunit) line
125  lloc = 1
126  call urword(line, lloc, istart, istop, 0, ival, rval, 0, 0)
127  call urword(line, lloc, istart, istop, 2, ival, rval, 0, 0)
128  this%ntxt = ival
129 
130  ! lentxt
131  read (this%inunit) line
132  lloc = 1
133  call urword(line, lloc, istart, istop, 0, ival, rval, 0, 0)
134  call urword(line, lloc, istart, istop, 2, ival, rval, 0, 0)
135  this%lentxt = ival
136 
137  end subroutine read_header_meta
138 
139  !> @brief Read the header body section (text following first
140  !< four "meta" lines) and build an index. Internal use only.
141  subroutine read_header_body(this)
142  ! dummy
143  class(gridfilereadertype) :: this
144  ! local
145  character(len=:), allocatable :: body
146  character(len=:), allocatable :: line
147  character(len=10) :: name, dtype
148  real(DP) :: rval
149  integer(I4B) :: i, lloc, istart, istop, ival
150  integer(I4B) :: ivar, ndim, dim, ishp, nbytes
151  integer(I8B) :: pos
152 
153  allocate (this%names(this%ntxt))
154  allocate (this%ndims(this%ntxt))
155  allocate (this%typs(this%ntxt))
156  allocate (this%shp_start(this%ntxt))
157  allocate (this%pos(this%ntxt))
158  allocate (character(len=this%lentxt*this%ntxt) :: body)
159  allocate (character(len=this%lentxt) :: line)
160 
161  read (this%inunit) body
162  inquire (this%inunit, pos=pos)
163  do ivar = 1, this%ntxt
164  i = (ivar - 1) * this%lentxt + 1
165  line = body(i:i + this%lentxt - 1)
166 
167  ! name
168  lloc = 1
169  call urword(line, lloc, istart, istop, 1, ival, rval, 0, 0)
170  name = line(istart:istop)
171  this%names(ivar) = name
172  call this%idx%add(name, ivar)
173 
174  ! type
175  call urword(line, lloc, istart, istop, 1, ival, rval, 0, 0)
176  dtype = line(istart:istop)
177  select case (dtype)
178  case ("INTEGER")
179  this%typs(ivar) = typ_int
180  nbytes = 4
181  case ("DOUBLE")
182  this%typs(ivar) = typ_dbl
183  nbytes = 8
184  case ("CHARACTER")
185  this%typs(ivar) = typ_chr
186  nbytes = 1
187  case default
188  this%typs(ivar) = 0
189  nbytes = 0
190  end select
191 
192  ! dims
193  call urword(line, lloc, istart, istop, 0, ival, rval, 0, 0)
194  call urword(line, lloc, istart, istop, 2, ival, rval, 0, 0)
195  ndim = ival
196  this%ndims(ivar) = ndim
197 
198  ! shape
199  this%shp_start(ivar) = 0
200  if (ndim > 0) then
201  ishp = size(this%shp)
202  call expandarray(this%shp, increment=ndim)
203  do dim = 1, ndim
204  call urword(line, lloc, istart, istop, 2, ival, rval, 0, 0)
205  this%shp(ishp + dim) = ival
206  end do
207  this%shp_start(ivar) = ishp + 1
208  end if
209 
210  ! position
211  this%pos(ivar) = pos
212  if (ndim == 0) then
213  pos = pos + nbytes
214  else
215  ishp = this%shp_start(ivar)
216  pos = pos + product(int(this%shp(ishp:ishp + ndim - 1), i8b)) * nbytes
217  end if
218  end do
219 
220  rewind(this%inunit)
221 
222  end subroutine read_header_body
223 
224  !> @brief Look up a variable and check its rank and type.
225  !!
226  !! Returns the variable's index. Terminates with an error if the
227  !! variable does not exist or does not have the expected rank and type.
228  !! Internal use only.
229  !<
230  function lookup(this, name, ndim, typ, desc) result(ivar)
231  class(gridfilereadertype), intent(inout) :: this
232  character(len=*), intent(in) :: name
233  integer(I4B), intent(in) :: ndim !< expected number of dims
234  integer(I4B), intent(in) :: typ !< expected type
235  character(len=*), intent(in) :: desc !< expected kind, for error messages
236  integer(I4B) :: ivar
237 
238  ivar = this%idx%get(name)
239  if (ivar == 0) then
240  write (errmsg, '(a)') 'Variable '//trim(name)//' not found'
241  call store_error(errmsg, terminate=.true.)
242  end if
243  if (this%ndims(ivar) /= ndim .or. this%typs(ivar) /= typ) then
244  write (errmsg, '(a)') 'Variable '//trim(name)//' is not '//desc
245  call store_error(errmsg, terminate=.true.)
246  end if
247  end function lookup
248 
249  !> @brief Read an integer scalar from a grid file.
250  function read_int(this, name) result(v)
251  class(gridfilereadertype), intent(inout) :: this
252  character(len=*), intent(in) :: name
253  integer(I4B) :: v
254  ! local
255  integer(I4B) :: ivar
256 
257  ivar = this%lookup(name, 0, typ_int, 'an integer scalar')
258  read (this%inunit, pos=this%pos(ivar)) v
259  rewind(this%inunit)
260  end function read_int
261 
262  !> @brief Read a double precision scalar from a grid file.
263  function read_dbl(this, name) result(v)
264  class(gridfilereadertype), intent(inout) :: this
265  character(len=*), intent(in) :: name
266  real(dp) :: v
267  ! local
268  integer(I4B) :: ivar
269 
270  ivar = this%lookup(name, 0, typ_dbl, 'a double precision scalar')
271  read (this%inunit, pos=this%pos(ivar)) v
272  rewind(this%inunit)
273  end function read_dbl
274 
275  !> @brief Read a 1D integer array from a grid file.
276  !!
277  !! Allocates and returns a new array containing the data.
278  !<
279  function read_int_1d(this, name) result(v)
280  class(gridfilereadertype), intent(inout) :: this
281  character(len=*), intent(in) :: name
282  integer(I4B), allocatable :: v(:)
283  ! local
284  integer(I4B) :: ivar, nvals
285 
286  ivar = this%lookup(name, 1, typ_int, 'a 1D integer array')
287  nvals = this%shp(this%shp_start(ivar))
288  allocate (v(nvals))
289  read (this%inunit, pos=this%pos(ivar)) v
290  rewind(this%inunit)
291  end function read_int_1d
292 
293  !> @brief Read a 1D integer array into a preallocated array.
294  !!
295  !! Populates a preallocated array. Array must already be allocated to the
296  !! correct size. This version is compatible with both allocatable arrays and
297  !! memory-manager-allocated pointer targets.
298  !<
299  subroutine read_int_1d_into(this, name, v)
300  class(gridfilereadertype), intent(inout) :: this
301  character(len=*), intent(in) :: name
302  integer(I4B), dimension(:), intent(inout) :: v
303  ! local
304  integer(I4B) :: ivar, nvals
305 
306  ivar = this%lookup(name, 1, typ_int, 'a 1D integer array')
307  nvals = this%shp(this%shp_start(ivar))
308  if (size(v) /= nvals) then
309  write (errmsg, '(a,i0,a,i0)') &
310  'Array size mismatch for '//trim(name)//': expected ', &
311  nvals, ', got ', size(v)
312  call store_error(errmsg, terminate=.true.)
313  end if
314  read (this%inunit, pos=this%pos(ivar)) v
315  rewind(this%inunit)
316  end subroutine read_int_1d_into
317 
318  !> @brief Read a 1D double array from a grid file.
319  !!
320  !! Allocates and returns a new array containing the data.
321  !<
322  function read_dbl_1d(this, name) result(v)
323  class(gridfilereadertype), intent(inout) :: this
324  character(len=*), intent(in) :: name
325  real(dp), allocatable :: v(:)
326  ! local
327  integer(I4B) :: ivar, nvals
328 
329  ivar = this%lookup(name, 1, typ_dbl, 'a 1D double array')
330  nvals = this%shp(this%shp_start(ivar))
331  allocate (v(nvals))
332  read (this%inunit, pos=this%pos(ivar)) v
333  rewind(this%inunit)
334  end function read_dbl_1d
335 
336  !> @brief Read a 1D double array into a preallocated array.
337  !!
338  !! Populates a preallocated array. Array must already be allocated to the
339  !! correct size. This version is compatible with both allocatable arrays and
340  !! memory-manager-allocated pointer targets.
341  !<
342  subroutine read_dbl_1d_into(this, name, v)
343  class(gridfilereadertype), intent(inout) :: this
344  character(len=*), intent(in) :: name
345  real(DP), dimension(:), intent(inout) :: v
346  ! local
347  integer(I4B) :: ivar, nvals
348 
349  ivar = this%lookup(name, 1, typ_dbl, 'a 1D double array')
350  nvals = this%shp(this%shp_start(ivar))
351  if (size(v) /= nvals) then
352  write (errmsg, '(a,i0,a,i0)') &
353  'Array size mismatch for '//trim(name)//': expected ', &
354  nvals, ', got ', size(v)
355  call store_error(errmsg, terminate=.true.)
356  end if
357  read (this%inunit, pos=this%pos(ivar)) v
358  rewind(this%inunit)
359  end subroutine read_dbl_1d_into
360 
361  !> @brief Read a character string from a grid file.
362  !!
363  !! Allocates and returns a new character string containing the data.
364  !<
365  function read_charstr(this, name) result(charstr)
366  class(gridfilereadertype), intent(inout) :: this
367  character(len=*), intent(in) :: name
368  character(len=:), allocatable :: charstr
369  ! local
370  integer(I4B) :: ivar, nvals
371 
372  ivar = this%lookup(name, 1, typ_chr, 'a character array')
373  nvals = this%shp(this%shp_start(ivar))
374  allocate (character(nvals) :: charstr)
375  read (this%inunit, pos=this%pos(ivar)) charstr
376  rewind(this%inunit)
377  end function read_charstr
378 
379  !> @brief Read a character string into a preallocated string.
380  !!
381  !! Populates a preallocated character string. If the string is not allocated
382  !! or is the wrong length, it will be (re)allocated to the correct length.
383  !<
384  subroutine read_charstr_into(this, name, charstr)
385  class(gridfilereadertype), intent(inout) :: this
386  character(len=*), intent(in) :: name
387  character(len=:), allocatable, intent(inout) :: charstr
388  ! local
389  integer(I4B) :: ivar, nvals
390 
391  ivar = this%lookup(name, 1, typ_chr, 'a character array')
392  nvals = this%shp(this%shp_start(ivar))
393  if (allocated(charstr)) then
394  if (len(charstr) /= nvals) deallocate (charstr)
395  end if
396  if (.not. allocated(charstr)) allocate (character(nvals) :: charstr)
397  read (this%inunit, pos=this%pos(ivar)) charstr
398  rewind(this%inunit)
399  end subroutine read_charstr_into
400 
401  !> @brief Read the grid shape from a grid file.
402  function read_grid_shape(this) result(v)
403  ! dummy
404  class(gridfilereadertype) :: this
405  integer(I4B), allocatable :: v(:)
406 
407  select case (this%grid_type)
408  case ("DIS")
409  allocate (v(3))
410  v(1) = this%read_int("NLAY")
411  v(2) = this%read_int("NROW")
412  v(3) = this%read_int("NCOL")
413  case ("DISV")
414  allocate (v(2))
415  v(1) = this%read_int("NLAY")
416  v(2) = this%read_int("NCPL")
417  case ("DISU")
418  allocate (v(1))
419  v(1) = this%read_int("NODES")
420  case ("DIS2D")
421  allocate (v(2))
422  v(1) = this%read_int("NROW")
423  v(2) = this%read_int("NCOL")
424  case ("DISV2D")
425  allocate (v(1))
426  v(1) = this%read_int("NODES")
427  case ("DISV1D")
428  allocate (v(1))
429  v(1) = this%read_int("NCELLS")
430  end select
431 
432  end function read_grid_shape
433 
434  !> @brief Check whether the grid file contains a variable.
435  function has_variable(this, name) result(has)
436  class(gridfilereadertype) :: this
437  character(len=*), intent(in) :: name
438  logical(LGP) :: has
439 
440  has = this%idx%get(name) /= 0
441  end function has_variable
442 
443 end module gridfilereadermodule
This module contains simulation constants.
Definition: Constants.f90:9
integer(i4b), parameter linelength
maximum length of a standard line
Definition: Constants.f90:45
subroutine initialize(this, iu)
@Brief Initialize the grid file reader.
subroutine read_header(this)
Read the file's self-describing header. Internal use only.
integer(i4b) function, dimension(:), allocatable read_int_1d(this, name)
Read a 1D integer array from a grid file.
integer(i4b), parameter typ_dbl
double precision variable type
subroutine read_int_1d_into(this, name, v)
Read a 1D integer array into a preallocated array.
subroutine read_header_meta(this)
Read self-describing metadata (first four lines). Internal use only.
integer(i4b), parameter typ_chr
character variable type
subroutine read_charstr_into(this, name, charstr)
Read a character string into a preallocated string.
subroutine read_header_body(this)
Read the header body section (text following first.
integer(i4b), parameter typ_int
integer variable type
subroutine finalize(this)
Finalize the grid file reader.
character(len=:) function, allocatable read_charstr(this, name)
Read a character string from a grid file.
integer(i4b) function read_int(this, name)
Read an integer scalar from a grid file.
real(dp) function read_dbl(this, name)
Read a double precision scalar from a grid file.
integer(i4b) function lookup(this, name, ndim, typ, desc)
Look up a variable and check its rank and type.
subroutine read_dbl_1d_into(this, name, v)
Read a 1D double array into a preallocated array.
logical(lgp) function has_variable(this, name)
Check whether the grid file contains a variable.
real(dp) function, dimension(:), allocatable read_dbl_1d(this, name)
Read a 1D double array from a grid file.
integer(i4b) function, dimension(:), allocatable read_grid_shape(this)
Read the grid shape from a grid file.
A chaining hash map for integers.
Definition: HashTable.f90:7
subroutine, public hash_table_cr(map)
Create a hash table.
Definition: HashTable.f90:46
subroutine, public hash_table_da(map)
Deallocate the hash table.
Definition: HashTable.f90:64
subroutine, public openfile(iu, iout, fname, ftype, fmtarg_opt, accarg_opt, filstat_opt, mode_opt)
Open a file.
Definition: InputOutput.f90:30
subroutine, public urword(line, icol, istart, istop, ncode, n, r, iout, in)
Extract a word from a string.
This module defines variable data types.
Definition: kind.f90:8
This module contains simulation methods.
Definition: Sim.f90:10
subroutine, public store_error(msg, terminate)
Store an error message.
Definition: Sim.f90:92
subroutine, public store_error_unit(iunit, terminate)
Store the file unit number.
Definition: Sim.f90:169
This module contains simulation variables.
Definition: SimVariables.f90:9
character(len=maxcharlen) errmsg
error message string