MODFLOW 6  version 6.9.0.dev0
USGS Modular Hydrologic Model
BinaryFileReader.f90
Go to the documentation of this file.
2 
3  use kindmodule, only: i4b, i8b, dp, lgp
4  use errorutilmodule, only: pstop
5 
7 
9  integer(I8B) :: pos, size
10  integer(I4B) :: kper, kstp
11  real(dp) :: pertim, totim
12  contains
13  procedure :: get_str
14  end type binaryfileheadertype
15 
16  type, abstract :: binaryfilereadertype
17  integer(I4B) :: inunit
18  integer(I4B) :: nrecords = 0
19  logical(LGP) :: indexed = .false.
20  logical(LGP) :: endoffile = .false.
21  integer(I8B), allocatable :: record_positions(:) !< record start positions
22  class(binaryfileheadertype), allocatable :: header
23  class(binaryfileheadertype), allocatable :: headernext
24  contains
25  procedure(read_header_if), deferred :: read_header
26  procedure(read_record_if), deferred :: read_record
27  procedure :: build_index
28  procedure :: peek_record
29  procedure :: rewind
30  procedure :: seek_to_index
31  end type binaryfilereadertype
32 
33  abstract interface
34  subroutine read_header_if(this, success, iout)
36  import i4b, lgp
37  class(binaryfilereadertype), intent(inout) :: this
38  logical(LGP), intent(out) :: success
39  integer(I4B), intent(in), optional :: iout
40  end subroutine read_header_if
41 
42  subroutine read_record_if(this, success, iout)
44  import i4b, lgp
45  class(binaryfilereadertype), intent(inout) :: this
46  logical(LGP), intent(out) :: success
47  integer(I4B), intent(in), optional :: iout
48  end subroutine read_record_if
49  end interface
50 contains
51 
52  !> @brief Get a string representation of the header.
53  function get_str(this) result(str)
54  class(binaryfileheadertype), intent(in) :: this
55  character(len=:), allocatable :: str
56 
57  write (str, '(*(G0))') &
58  'Binary file header (pos: ', this%pos, &
59  ', kper: ', this%kper, &
60  ', kstp: ', this%kstp, &
61  ', pertim: ', this%pertim, &
62  ', totim: ', this%totim, &
63  ')'
64  str = trim(str)
65  end function get_str
66 
67  !> @brief Build an index of record positions in the file
68  subroutine build_index(this)
69  class(binaryfilereadertype), intent(inout) :: this
70  ! local
71  logical(LGP) :: success
72  integer(I4B) :: i
73 
74  this%indexed = .false.
75  this%nrecords = 0
76  call this%rewind()
77  if (allocated(this%record_positions)) deallocate (this%record_positions)
78 
79  ! first pass: count
80  do
81  call this%read_record(success)
82  if (.not. success) exit
83  this%nrecords = this%nrecords + 1
84  end do
85 
86  ! second pass: save positions (read first so header%pos is set)
87  call this%rewind()
88  allocate (this%record_positions(this%nrecords))
89  i = 0
90  do
91  call this%read_record(success)
92  if (.not. success) exit
93  i = i + 1
94  this%record_positions(i) = this%header%pos
95  end do
96  call this%rewind()
97 
98  this%indexed = .true.
99  end subroutine build_index
100 
101  !> @brief Position the file at the start of an indexed record
102  subroutine seek_to_index(this, index)
103  class(binaryfilereadertype), intent(inout) :: this
104  integer(I4B), intent(in) :: index
105  ! local
106  integer(I4B) :: iostat
107 
108  if (.not. this%indexed) &
109  call pstop(1, 'Cannot seek to index in unindexed file')
110  if (index < 1 .or. index > this%nrecords) then
111  this%endoffile = .true.
112  return
113  end if
114 
115  read (this%inunit, pos=this%record_positions(index), iostat=iostat)
116  if (iostat /= 0) call pstop(1, 'Error seeking to index position')
117  ! peek_record deallocates headernext at end of file
118  if (.not. allocated(this%headernext)) &
119  allocate (this%headernext, mold=this%header)
120  this%endoffile = .false.
121  end subroutine seek_to_index
122 
123  !> @brief Peek to see if another record is available.
124  subroutine peek_record(this)
125  class(binaryfilereadertype), intent(inout) :: this
126  ! local
127  integer(I4B) :: iostat
128 
129  if (.not. this%endoffile) then
130  inquire (this%inunit, pos=this%headernext%pos)
131  read (this%inunit, iostat=iostat) this%headernext%kstp, this%headernext%kper
132  if (iostat == 0) then
133  read (this%inunit, pos=this%headernext%pos, iostat=iostat)
134  else if (iostat < 0) then
135  this%endoffile = .true.
136  if (allocated(this%headernext)) deallocate (this%headernext)
137  end if
138  end if
139  end subroutine peek_record
140 
141  !> @brief Rewind the file to the beginning.
142  subroutine rewind (this)
143  class(binaryfilereadertype), intent(inout) :: this
144 
145  rewind(this%inunit)
146  if (allocated(this%header)) deallocate (this%header)
147  if (allocated(this%headernext)) deallocate (this%headernext)
148  allocate (binaryfileheadertype :: this%header)
149  allocate (binaryfileheadertype :: this%headernext)
150  this%header%pos = 1
151  this%headernext%pos = 1
152  this%endoffile = .false.
153  end subroutine rewind
154 
155 end module binaryfilereadermodule
character(len=:) function, allocatable get_str(this)
Get a string representation of the header.
subroutine seek_to_index(this, index)
Position the file at the start of an indexed record.
subroutine rewind(this)
Rewind the file to the beginning.
subroutine build_index(this)
Build an index of record positions in the file.
subroutine peek_record(this)
Peek to see if another record is available.
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