MODFLOW 6  version 6.7.0.dev3
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 
16  private
17  integer(I4B), public :: inunit !< file unit
18  ! header
19  character(len=10), public :: grid_type !< DIS, DISV, DISU, etc
20  integer(I4B), public :: version !< binary grid file format version
21  integer(I4B) :: ntxt !< number of variables
22  integer(I4B) :: lentxt !< header line length per variable
23  ! index
24  type(hashtabletype), pointer :: dim !< map variable name to number of dims
25  type(hashtabletype), pointer :: pos !< map variable name to position in file
26  type(hashtabletype), pointer :: typ !< map variable name to type (1=int, 2=dbl)
27  type(hashtabletype), pointer :: shp_idx !< map variable name to index in shp
28  integer(I4B), allocatable :: shp(:) !< flat array of variable shapes
29  character(len=10), allocatable, public :: keys(:) !< variable names
30  contains
31  procedure, public :: initialize
32  procedure, public :: finalize
33  procedure, public :: has_variable
34  ! header-reading subroutines
35  procedure, private :: read_header
36  procedure, private :: read_header_meta
37  procedure, private :: read_header_body
38  ! scalar read functions
39  procedure, public :: read_int
40  procedure, public :: read_dbl
41  procedure, public :: read_grid_shape
42  ! array read functions (allocate and return)
43  procedure, public :: read_int_1d
44  procedure, public :: read_dbl_1d
45  procedure, public :: read_charstr
46  ! array read subroutines (populate preallocated)
47  procedure, public :: read_int_1d_into
48  procedure, public :: read_dbl_1d_into
49  procedure, public :: read_charstr_into
50  end type gridfilereadertype
51 
52 contains
53 
54  !> @Brief Initialize the grid file reader.
55  subroutine initialize(this, iu)
56  class(gridfilereadertype) :: this
57  integer(I4B), intent(in) :: iu
58 
59  this%inunit = iu
60  call hash_table_cr(this%dim)
61  call hash_table_cr(this%pos)
62  call hash_table_cr(this%typ)
63  call hash_table_cr(this%shp_idx)
64  allocate (this%shp(0))
65  call this%read_header()
66 
67  end subroutine initialize
68 
69  !> @brief Finalize the grid file reader.
70  subroutine finalize(this)
71  class(gridfilereadertype) :: this
72 
73  close (this%inunit)
74  call hash_table_da(this%dim)
75  call hash_table_da(this%pos)
76  call hash_table_da(this%typ)
77  call hash_table_da(this%shp_idx)
78  deallocate (this%shp)
79 
80  end subroutine finalize
81 
82  !> @brief Read the file's self-describing header. Internal use only.
83  subroutine read_header(this)
84  class(gridfilereadertype) :: this
85  call this%read_header_meta()
86  call this%read_header_body()
87  end subroutine read_header
88 
89  !> @brief Read self-describing metadata (first four lines). Internal use only.
90  subroutine read_header_meta(this)
91  ! dummy
92  class(gridfilereadertype) :: this
93  ! local
94  character(len=50) :: line
95  integer(I4B) :: lloc, istart, istop
96  integer(I4B) :: ival
97  real(DP) :: rval
98 
99  ! grid type
100  read (this%inunit) line
101  lloc = 1
102  call urword(line, lloc, istart, istop, 1, ival, rval, 0, 0)
103  if (line(istart:istop) /= 'GRID') then
104  call store_error('Binary grid file must begin with "GRID". '//&
105  &'Found: '//line(istart:istop))
106  call store_error_unit(this%inunit)
107  end if
108  call urword(line, lloc, istart, istop, 1, ival, rval, 0, 0)
109  this%grid_type = line(istart:istop)
110 
111  ! version
112  read (this%inunit) line
113  lloc = 1
114  call urword(line, lloc, istart, istop, 0, ival, rval, 0, 0)
115  call urword(line, lloc, istart, istop, 2, ival, rval, 0, 0)
116  this%version = ival
117 
118  ! ntxt
119  read (this%inunit) line
120  lloc = 1
121  call urword(line, lloc, istart, istop, 0, ival, rval, 0, 0)
122  call urword(line, lloc, istart, istop, 2, ival, rval, 0, 0)
123  this%ntxt = ival
124 
125  ! lentxt
126  read (this%inunit) line
127  lloc = 1
128  call urword(line, lloc, istart, istop, 0, ival, rval, 0, 0)
129  call urword(line, lloc, istart, istop, 2, ival, rval, 0, 0)
130  this%lentxt = ival
131 
132  end subroutine read_header_meta
133 
134  !> @brief Read the header body section (text following first
135  !< four "meta" lines) and build an index. Internal use only.
136  subroutine read_header_body(this)
137  ! dummy
138  class(gridfilereadertype) :: this
139  ! local
140  character(len=:), allocatable :: body
141  character(len=:), allocatable :: line
142  character(len=10) :: key, dtype
143  real(DP) :: rval
144  integer(I4B) :: i, lloc, istart, istop, ival, pos
145  integer(I4B) :: nvars, ndim, dim, ishp
146  integer(I4B), allocatable :: shp(:)
147 
148  allocate (this%keys(this%ntxt))
149  allocate (character(len=this%lentxt*this%ntxt) :: body)
150  allocate (character(len=this%lentxt) :: line)
151 
152  nvars = 0
153  read (this%inunit) body
154  inquire (this%inunit, pos=pos)
155  do i = 1, this%lentxt * this%ntxt, this%lentxt
156  line = body(i:i + this%lentxt - 1)
157  lloc = 1
158 
159  ! key
160  lloc = 1
161  call urword(line, lloc, istart, istop, 1, ival, rval, 0, 0)
162  key = line(istart:istop)
163  nvars = nvars + 1
164  this%keys(nvars) = key
165 
166  ! type
167  call urword(line, lloc, istart, istop, 1, ival, rval, 0, 0)
168  dtype = line(istart:istop)
169  if (dtype == "INTEGER") then
170  call this%typ%add(key, 1)
171  else if (dtype == "DOUBLE") then
172  call this%typ%add(key, 2)
173  else if (dtype == "CHARACTER") then
174  call this%typ%add(key, 3)
175  end if
176 
177  ! dims
178  call urword(line, lloc, istart, istop, 0, ival, rval, 0, 0)
179  call urword(line, lloc, istart, istop, 2, ival, rval, 0, 0)
180  ndim = ival
181  call this%dim%add(key, ndim)
182 
183  ! shape
184  if (allocated(shp)) deallocate (shp)
185  allocate (shp(ndim))
186  if (ndim > 0) then
187  do dim = 1, ndim
188  call urword(line, lloc, istart, istop, 2, ival, rval, 0, 0)
189  shp(dim) = ival
190  end do
191  ishp = size(this%shp)
192  call expandarray(this%shp, increment=ndim)
193  this%shp(ishp + 1:ishp + ndim) = shp
194  call this%shp_idx%add(key, ishp + 1)
195  end if
196 
197  ! position
198  call this%pos%add(key, pos)
199  if (ndim == 0) then
200  if (dtype == "INTEGER") then
201  pos = pos + 4
202  else if (dtype == "DOUBLE") then
203  pos = pos + 8
204  end if
205  else
206  if (dtype == "INTEGER") then
207  pos = pos + (product(shp) * 4)
208  else if (dtype == "DOUBLE") then
209  pos = pos + (product(shp) * 8)
210  else if (dtype == "CHARACTER") then
211  pos = pos + (product(shp) * 8)
212  end if
213  end if
214  end do
215 
216  rewind(this%inunit)
217 
218  end subroutine read_header_body
219 
220  !> @brief Read an integer scalar from a grid file.
221  function read_int(this, key) result(v)
222  class(gridfilereadertype), intent(inout) :: this
223  character(len=*), intent(in) :: key
224  integer(I4B) :: v
225  ! local
226  integer(I4B) :: ndim, pos, typ
227  character(len=:), allocatable :: msg
228 
229  msg = 'Variable '//trim(key)//' is not an integer scalar'
230  ndim = this%dim%get(key)
231  if (ndim /= 0) then
232  write (errmsg, '(a)') msg
233  call store_error(errmsg, terminate=.true.)
234  end if
235  typ = this%typ%get(key)
236  if (typ /= 1) then
237  write (errmsg, '(a)') msg
238  call store_error(errmsg, terminate=.true.)
239  end if
240  pos = this%pos%get(key)
241  read (this%inunit, pos=pos) v
242  rewind(this%inunit)
243 
244  end function read_int
245 
246  !> @brief Read a double precision scalar from a grid file.
247  function read_dbl(this, key) result(v)
248  class(gridfilereadertype), intent(inout) :: this
249  character(len=*), intent(in) :: key
250  real(dp) :: v
251  ! local
252  integer(I4B) :: ndim, pos, typ
253  character(len=:), allocatable :: msg
254 
255  msg = 'Variable '//trim(key)//' is not a double precision scalar'
256  ndim = this%dim%get(key)
257  if (ndim /= 0) then
258  write (errmsg, '(a)') msg
259  call store_error(errmsg, terminate=.true.)
260  end if
261  typ = this%typ%get(key)
262  if (typ /= 2) then
263  write (errmsg, '(a)') msg
264  call store_error(errmsg, terminate=.true.)
265  end if
266  pos = this%pos%get(key)
267  read (this%inunit, pos=pos) v
268  rewind(this%inunit)
269 
270  end function read_dbl
271 
272  !> @brief Read a 1D integer array from a grid file.
273  !!
274  !! Allocates and returns a new array containing the data.
275  function read_int_1d(this, key) result(v)
276  class(gridfilereadertype), intent(inout) :: this
277  character(len=*), intent(in) :: key
278  integer(I4B), allocatable :: v(:)
279  ! local
280  integer(I4B) :: idx, ndim, nvals, pos, typ
281  character(len=:), allocatable :: msg
282 
283  msg = 'Variable '//trim(key)//' is not a 1D integer array'
284  ndim = this%dim%get(key)
285  if (ndim /= 1) then
286  write (errmsg, '(a)') msg
287  call store_error(errmsg, terminate=.true.)
288  end if
289  typ = this%typ%get(key)
290  if (typ /= 1) then
291  write (errmsg, '(a)') msg
292  call store_error(errmsg, terminate=.true.)
293  end if
294  idx = this%shp_idx%get(key)
295  pos = this%pos%get(key)
296  nvals = this%shp(idx)
297  allocate (v(nvals))
298  read (this%inunit, pos=pos) v
299  rewind(this%inunit)
300 
301  end function read_int_1d
302 
303  !> @brief Read a 1D integer array into a preallocated array.
304  !!
305  !! Populates a preallocated array. Array must already be allocated to the
306  !! correct size. This version is compatible with both allocatable arrays and
307  !! memory-manager-allocated pointer targets.
308  subroutine read_int_1d_into(this, key, v)
309  class(gridfilereadertype), intent(inout) :: this
310  character(len=*), intent(in) :: key
311  integer(I4B), dimension(:), intent(inout) :: v
312  ! local
313  integer(I4B) :: idx, ndim, nvals, pos, typ
314  character(len=:), allocatable :: msg
315 
316  msg = 'Variable '//trim(key)//' is not a 1D integer array'
317  ndim = this%dim%get(key)
318  if (ndim /= 1) then
319  write (errmsg, '(a)') msg
320  call store_error(errmsg, terminate=.true.)
321  end if
322  typ = this%typ%get(key)
323  if (typ /= 1) then
324  write (errmsg, '(a)') msg
325  call store_error(errmsg, terminate=.true.)
326  end if
327  idx = this%shp_idx%get(key)
328  pos = this%pos%get(key)
329  nvals = this%shp(idx)
330  ! verify array is correct size
331  if (size(v) /= nvals) then
332  write (errmsg, '(a,i0,a,i0)') &
333  'Array size mismatch for '//trim(key)//': expected ', &
334  nvals, ', got ', size(v)
335  call store_error(errmsg, terminate=.true.)
336  end if
337  read (this%inunit, pos=pos) v
338  rewind(this%inunit)
339 
340  end subroutine read_int_1d_into
341 
342  !> @brief Read a 1D double array from a grid file.
343  !!
344  !! Allocates and returns a new array containing the data.
345  function read_dbl_1d(this, key) result(v)
346  class(gridfilereadertype), intent(inout) :: this
347  character(len=*), intent(in) :: key
348  real(dp), allocatable :: v(:)
349  ! local
350  integer(I4B) :: idx, ndim, nvals, pos, typ
351  character(len=:), allocatable :: msg
352 
353  msg = 'Variable '//trim(key)//' is not a 1D double array'
354  ndim = this%dim%get(key)
355  if (ndim /= 1) then
356  write (errmsg, '(a)') msg
357  call store_error(errmsg, terminate=.true.)
358  end if
359  typ = this%typ%get(key)
360  if (typ /= 2) then
361  write (errmsg, '(a)') msg
362  call store_error(errmsg, terminate=.true.)
363  end if
364  idx = this%shp_idx%get(key)
365  pos = this%pos%get(key)
366  nvals = this%shp(idx)
367  allocate (v(nvals))
368  read (this%inunit, pos=pos) v
369  rewind(this%inunit)
370 
371  end function read_dbl_1d
372 
373  !> @brief Read a 1D double array into a preallocated array.
374  !!
375  !! Populates a preallocated array. Array must already be allocated to the
376  !! correct size. This version is compatible with both allocatable arrays and
377  !! memory-manager-allocated pointer targets.
378  subroutine read_dbl_1d_into(this, key, v)
379  class(gridfilereadertype), intent(inout) :: this
380  character(len=*), intent(in) :: key
381  real(DP), dimension(:), intent(inout) :: v
382  ! local
383  integer(I4B) :: idx, ndim, nvals, pos, typ
384  character(len=:), allocatable :: msg
385 
386  msg = 'Variable '//trim(key)//' is not a 1D double array'
387  ndim = this%dim%get(key)
388  if (ndim /= 1) then
389  write (errmsg, '(a)') msg
390  call store_error(errmsg, terminate=.true.)
391  end if
392  typ = this%typ%get(key)
393  if (typ /= 2) then
394  write (errmsg, '(a)') msg
395  call store_error(errmsg, terminate=.true.)
396  end if
397  idx = this%shp_idx%get(key)
398  pos = this%pos%get(key)
399  nvals = this%shp(idx)
400  ! verify array is correct size
401  if (size(v) /= nvals) then
402  write (errmsg, '(a,i0,a,i0)') &
403  'Array size mismatch for '//trim(key)//': expected ', &
404  nvals, ', got ', size(v)
405  call store_error(errmsg, terminate=.true.)
406  end if
407  read (this%inunit, pos=pos) v
408  rewind(this%inunit)
409 
410  end subroutine read_dbl_1d_into
411 
412  !> @brief Read a character string from a grid file.
413  !!
414  !! Allocates and returns a new character string containing the data.
415  function read_charstr(this, key) result(charstr)
416  class(gridfilereadertype), intent(inout) :: this
417  character(len=*), intent(in) :: key
418  character(len=:), allocatable :: charstr
419  ! local
420  integer(I4B) :: idx, ndim, nvals, pos, typ
421  character(len=:), allocatable :: msg
422 
423  msg = 'Variable '//trim(key)//' is not a character array'
424  ndim = this%dim%get(key)
425  if (ndim /= 1) then
426  write (errmsg, '(a)') msg
427  call store_error(errmsg, terminate=.true.)
428  end if
429  typ = this%typ%get(key)
430  if (typ /= 3) then
431  write (errmsg, '(a)') msg
432  call store_error(errmsg, terminate=.true.)
433  end if
434  idx = this%shp_idx%get(key)
435  pos = this%pos%get(key)
436  nvals = this%shp(idx)
437  allocate (character(nvals) :: charstr)
438  read (this%inunit, pos=pos) charstr
439  rewind(this%inunit)
440  end function read_charstr
441 
442  !> @brief Read a character string into a preallocated string.
443  !!
444  !! Populates a preallocated character string. If the string is not allocated
445  !! or is the wrong length, it will be (re)allocated to the correct length.
446  subroutine read_charstr_into(this, key, charstr)
447  class(gridfilereadertype), intent(inout) :: this
448  character(len=*), intent(in) :: key
449  character(len=:), allocatable, intent(inout) :: charstr
450  ! local
451  integer(I4B) :: idx, ndim, nvals, pos, typ
452  character(len=:), allocatable :: msg
453 
454  msg = 'Variable '//trim(key)//' is not a character array'
455  ndim = this%dim%get(key)
456  if (ndim /= 1) then
457  write (errmsg, '(a)') msg
458  call store_error(errmsg, terminate=.true.)
459  end if
460  typ = this%typ%get(key)
461  if (typ /= 3) then
462  write (errmsg, '(a)') msg
463  call store_error(errmsg, terminate=.true.)
464  end if
465  idx = this%shp_idx%get(key)
466  pos = this%pos%get(key)
467  nvals = this%shp(idx)
468  ! reallocate if not allocated or wrong length
469  if (allocated(charstr)) then
470  if (len(charstr) /= nvals) then
471  deallocate (charstr)
472  allocate (character(nvals) :: charstr)
473  end if
474  else
475  allocate (character(nvals) :: charstr)
476  end if
477  read (this%inunit, pos=pos) charstr
478  rewind(this%inunit)
479  end subroutine read_charstr_into
480 
481  !> @brief Read the grid shape from a grid file.
482  function read_grid_shape(this) result(v)
483  ! dummy
484  class(gridfilereadertype) :: this
485  integer(I4B), allocatable :: v(:)
486 
487  select case (this%grid_type)
488  case ("DIS")
489  allocate (v(3))
490  v(1) = this%read_int("NLAY")
491  v(2) = this%read_int("NROW")
492  v(3) = this%read_int("NCOL")
493  case ("DISV")
494  allocate (v(2))
495  v(1) = this%read_int("NLAY")
496  v(2) = this%read_int("NCPL")
497  case ("DISU")
498  allocate (v(1))
499  v(1) = this%read_int("NODES")
500  case ("DIS2D")
501  allocate (v(2))
502  v(1) = this%read_int("NROW")
503  v(2) = this%read_int("NCOL")
504  case ("DISV2D")
505  allocate (v(1))
506  v(1) = this%read_int("NODES")
507  case ("DISV1D")
508  allocate (v(1))
509  v(1) = this%read_int("NCELLS")
510  end select
511 
512  end function read_grid_shape
513 
514  function has_variable(this, key) result(has)
515  class(gridfilereadertype) :: this
516  character(len=*), intent(in) :: key
517  logical(LGP), allocatable :: has
518 
519  has = this%dim%get(key) /= 0
520  end function has_variable
521 
522 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.
subroutine read_header_meta(this)
Read self-describing metadata (first four lines). Internal use only.
subroutine read_charstr_into(this, key, charstr)
Read a character string into a preallocated string.
integer(i4b) function read_int(this, key)
Read an integer scalar from a grid file.
subroutine read_header_body(this)
Read the header body section (text following first.
subroutine finalize(this)
Finalize the grid file reader.
real(dp) function, dimension(:), allocatable read_dbl_1d(this, key)
Read a 1D double array from a grid file.
character(len=:) function, allocatable read_charstr(this, key)
Read a character string from a grid file.
integer(i4b) function, dimension(:), allocatable read_int_1d(this, key)
Read a 1D integer array from a grid file.
real(dp) function read_dbl(this, key)
Read a double precision scalar from a grid file.
subroutine read_int_1d_into(this, key, v)
Read a 1D integer array into a preallocated array.
logical(lgp) function, allocatable has_variable(this, key)
subroutine read_dbl_1d_into(this, key, v)
Read a 1D double array into a preallocated array.
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:168
This module contains simulation variables.
Definition: SimVariables.f90:9
character(len=maxcharlen) errmsg
error message string