MODFLOW 6  version 6.6.0.dev0
USGS Modular Hydrologic Model
TimeSelect.f90
Go to the documentation of this file.
1 !> @brief Specify times for some event to occur.
3 
4  use kindmodule, only: dp, i4b, lgp
5  use constantsmodule, only: dzero, done
7  use errorutilmodule, only: pstop
8  use sortmodule, only: qsort
9 
10  implicit none
11  public :: timeselecttype
12 
13  !> @brief Represents a series of instants at which some event should occur.
14  !!
15  !! Maintains an array of configured times which can be sliced to match e.g.
16  !! the current period & time step. Slicing can be performed manually, with
17  !! the select() routine, or automatically, with the advance() routine, for
18  !! a convenient view onto the applicable subset of the complete time array.
19  !!
20  !! Array storage can be expanded manually. Note: array expansion must take
21  !! place before selection; when expand() is called the selection is wiped.
22  !! Alternatively, the extend() routine will automatically expand the array
23  !! and sort it.
24  !!
25  !! Most use cases likely assume a strictly increasing time selection; this
26  !! can be checked with increasing(). Note that the sort() routine does not
27  !! check for duplicates, and should usually be followed by an increasing()
28  !! check before the time selection is used.
29  !<
31  real(dp), allocatable :: times(:)
32  integer(I4B) :: selection(2)
33  contains
34  procedure :: deallocate
35  procedure :: expand
36  procedure :: init
37  procedure :: increasing
38  procedure :: log
39  procedure :: select
40  procedure :: advance
41  procedure :: any
42  procedure :: count
43  procedure :: sort
44  procedure :: extend
45  end type timeselecttype
46 
47 contains
48 
49  !> @brief Deallocate the time selection object.
50  subroutine deallocate (this)
51  class(timeselecttype) :: this
52  deallocate (this%times)
53  end subroutine deallocate
54 
55  !> @brief Expand capacity by the given amount. Resets the current slice.
56  subroutine expand(this, increment)
57  class(timeselecttype) :: this
58  integer(I4B), optional, intent(in) :: increment
59 
60  call expandarray(this%times, increment=increment)
61  this%selection = (/1, size(this%times)/)
62  end subroutine expand
63 
64  !> @brief Initialize or clear the time selection object.
65  subroutine init(this)
66  class(timeselecttype) :: this
67 
68  if (allocated(this%times)) deallocate (this%times)
69  allocate (this%times(0))
70  this%selection = (/0, 0/)
71  end subroutine
72 
73  !> @brief Determine if times strictly increase.
74  !!
75  !! Returns true if the times array strictly increases,
76  !! as well as if the times array is empty, or not yet
77  !! allocated. Note that this function operates on the
78  !! entire times array, not the current selection. Note
79  !! also that this function conducts exact comparisons;
80  !! deduplication with tolerance must be done manually.
81  !<
82  function increasing(this) result(inc)
83  class(timeselecttype) :: this
84  logical(LGP) :: inc
85  integer(I4B) :: i
86  real(dp) :: l, t
87 
88  inc = .true.
89  if (.not. allocated(this%times)) return
90  do i = 1, size(this%times)
91  t = this%times(i)
92  if (i /= 1) then
93  if (l >= t) then
94  inc = .false.
95  return
96  end if
97  end if
98  l = t
99  end do
100  end function increasing
101 
102  !> @brief Show the current time selection, if any.
103  subroutine log(this, iout, verb)
104  ! dummy
105  class(timeselecttype) :: this !< this instance
106  integer(I4B), intent(in) :: iout !< output unit
107  character(len=*), intent(in) :: verb !< selection name
108  ! formats
109  character(len=*), parameter :: fmt = &
110  &"(6x,'THE FOLLOWING TIMES WILL BE ',A,': ',50(G0,' '))"
111 
112  if (this%any()) then
113  write (iout, fmt) verb, this%times(this%selection(1):this%selection(2))
114  else
115  write (iout, "(a,1x,a)") 'NO TIMES WILL BE', verb
116  end if
117  end subroutine log
118 
119  !> @brief Select times between t0 and t1 (inclusive).
120  !!
121  !! Finds and stores the index of the first time at the same instant
122  !! as or following the start time, and of the last time at the same
123  !! instant as or preceding the end time. Allows filtering the times
124  !! for e.g. a particular stress period and time step. Array indices
125  !! are assumed to start at 1. If no times are found to fall within
126  !! the selection (i.e. it falls entirely between two consecutive
127  !! times or beyond the time range), indices are set to [-1, -1].
128  !!
129  !! The given start and end times are first checked against currently
130  !! stored indices to avoid recalculating them if possible, allowing
131  !! multiple consuming components (e.g., subdomain particle tracking
132  !! solutions) to share the object efficiently, provided all proceed
133  !! through stress periods and time steps in lockstep, i.e. they all
134  !! solve any given period/step before any will proceed to the next.
135  !<
136  subroutine select(this, t0, t1, changed)
137  ! dummy
138  class(timeselecttype) :: this
139  real(DP), intent(in) :: t0, t1
140  logical(LGP), intent(inout), optional :: changed
141  ! local
142  integer(I4B) :: i, i0, i1
143  integer(I4B) :: l, u, lp, up
144  real(DP) :: t
145 
146  ! by default, need to iterate over all times
147  i0 = 1
148  i1 = size(this%times)
149 
150  ! if no times fall within the slice, set to [-1, -1]
151  l = -1
152  u = -1
153 
154  ! previous bounding indices
155  lp = this%selection(1)
156  up = this%selection(2)
157 
158  ! Check if we can reuse either the lower or upper bound.
159  ! The lower doesn't need to change if it indexes the 1st
160  ! time simultaneous with or later than the slice's start.
161  ! The upper doesn't need to change if it indexes the last
162  ! time before or simultaneous with the slice's end.
163  if (lp > 0 .and. up > 0) then
164  if (lp > 1) then
165  if (this%times(lp - 1) < t0 .and. &
166  this%times(lp) >= t0) then
167  l = lp
168  i0 = l
169  end if
170  end if
171  if (up > 1 .and. up < i1) then
172  if (this%times(up + 1) > t1 .and. &
173  this%times(up) <= t1) then
174  u = up
175  i1 = u
176  end if
177  end if
178  if (l == lp .and. u == up) then
179  this%selection = (/l, u/)
180  if (present(changed)) changed = .false.
181  return
182  end if
183  end if
184 
185  ! recompute bounding indices if needed
186  do i = i0, i1
187  t = this%times(i)
188  if (l < 0 .and. t >= t0 .and. t <= t1) l = i
189  if (l > 0 .and. t <= t1) u = i
190  end do
191  this%selection = (/l, u/)
192  if (present(changed)) changed = l /= lp .or. u /= up
193 
194  end subroutine
195 
196  !> @brief Update the selection to the current time step.
197  subroutine advance(this)
198  ! modules
199  use tdismodule, only: kper, kstp, nper, nstp, totimc, delt
200  ! dummy
201  class(timeselecttype) :: this
202  ! local
203  real(DP) :: l, u
204 
205  l = minval(this%times)
206  u = maxval(this%times)
207  if (.not. (kper == 1 .and. kstp == 1)) l = totimc
208  if (.not. (kper == nper .and. kstp == nstp(kper))) u = totimc + delt
209  call this%select(l, u)
210  end subroutine advance
211 
212  !> @brief Check if any times are currently selected.
213  !!
214  !! Indicates whether any times are selected for the
215  !! current time step.
216  !!
217  !! Note that this routine does NOT indicate whether
218  !! the times array has nonzero size; use the size
219  !! intrinsic for that.
220  !<
221  function any(this) result(a)
222  class(timeselecttype) :: this
223  logical(LGP) :: a
224 
225  a = all(this%selection > 0)
226  end function any
227 
228  !> @brief Return the number of times currently selected.
229  !!
230  !! Returns the number of times selected for the current
231  !! time step.
232  !!
233  !! Note that this routine does NOT return the total size
234  !! of the times array; use the size intrinsic for that.
235  !<
236  function count(this) result(n)
237  class(timeselecttype) :: this
238  integer(I4B) :: n
239 
240  if (this%any()) then
241  n = this%selection(2) - this%selection(1)
242  else
243  n = 0
244  end if
245  end function count
246 
247  !> @brief Sort the time selection in increasing order.
248  !!
249  !! Note that this routine does NOT remove duplicate times.
250  !! Call increasing() to check for duplicates in the array.
251  !<
252  subroutine sort(this)
253  class(timeselecttype) :: this
254  integer(I4B), allocatable :: indx(:)
255 
256  allocate (indx(size(this%times)))
257  call qsort(indx, this%times)
258  deallocate (indx)
259  end subroutine sort
260 
261  !> @brief Extend the time selection with the given array.
262  !!
263  !! This routine sorts the selection after appending the
264  !! elements of the given array, but users should likely
265  !! still call increasing() to check for duplicate times.
266  !<
267  subroutine extend(this, a)
268  class(timeselecttype) :: this
269  real(DP) :: a(:)
270 
271  this%times = [this%times, a]
272  call this%sort()
273  end subroutine extend
274 
275 end module timeselectmodule
subroutine init()
Definition: GridSorting.f90:24
This module contains simulation constants.
Definition: Constants.f90:9
real(dp), parameter dzero
real constant zero
Definition: Constants.f90:65
real(dp), parameter done
real constant 1
Definition: Constants.f90:76
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
integer(i4b), dimension(:), pointer, public, contiguous nstp
number of time steps in each stress period
Definition: tdis.f90:39
real(dp), pointer, public totimc
simulation time at start of time step
Definition: tdis.f90:33
integer(i4b), pointer, public kstp
current time step number
Definition: tdis.f90:24
integer(i4b), pointer, public kper
current stress period number
Definition: tdis.f90:23
real(dp), pointer, public delt
length of the current time step
Definition: tdis.f90:29
integer(i4b), pointer, public nper
number of stress period
Definition: tdis.f90:21
Specify times for some event to occur.
Definition: TimeSelect.f90:2
logical(lgp) function increasing(this)
Determine if times strictly increase.
Definition: TimeSelect.f90:83
subroutine advance(this)
Update the selection to the current time step.
Definition: TimeSelect.f90:198
integer(i4b) function count(this)
Return the number of times currently selected.
Definition: TimeSelect.f90:237
subroutine log(this, iout, verb)
Show the current time selection, if any.
Definition: TimeSelect.f90:104
subroutine deallocate(this)
Deallocate the time selection object.
Definition: TimeSelect.f90:51
logical(lgp) function any(this)
Check if any times are currently selected.
Definition: TimeSelect.f90:222
subroutine sort(this)
Sort the time selection in increasing order.
Definition: TimeSelect.f90:253
subroutine expand(this, increment)
Expand capacity by the given amount. Resets the current slice.
Definition: TimeSelect.f90:57
subroutine select(this, t0, t1, changed)
Select times between t0 and t1 (inclusive).
Definition: TimeSelect.f90:137
subroutine extend(this, a)
Extend the time selection with the given array.
Definition: TimeSelect.f90:268
Represents a series of instants at which some event should occur.
Definition: TimeSelect.f90:30