MODFLOW 6  version 6.5.0.dev2
MODFLOW 6 Code Documentation
TimeSelect.f90
Go to the documentation of this file.
1 !> @brief Specify times for some event(s) to occur.
3 
4  use kindmodule, only: dp, i4b, lgp
5  use constantsmodule, only: dzero, done
7  use errorutilmodule, only: pstop
8  implicit none
9  public :: timeselecttype
10 
11  !> @brief Represents a series of instants at which some event should occur.
12  !!
13  !! Supports selection e.g. to filter times in a selected period & time step.
14  !! Array storage can be expanded as needed. Note: array expansion must take
15  !! place before selection; when expand() is invoked the selection is cleared.
16  !! The time series is assumed to strictly increase, increasing() checks this.
17  !<
19  real(dp), allocatable :: times(:)
20  integer(I4B) :: selection(2)
21  contains
22  procedure :: destroy
23  procedure :: expand
24  procedure :: init
25  procedure :: increasing
26  procedure :: select
27  procedure :: try_advance
28  end type timeselecttype
29 
30 contains
31 
32  !> @brief Destroy the time selection object.
33  subroutine destroy(this)
34  class(timeselecttype) :: this
35  deallocate (this%times)
36  end subroutine destroy
37 
38  !> @brief Expand capacity by the given amount. Resets the current slice.
39  subroutine expand(this, increment)
40  class(timeselecttype) :: this
41  integer(I4B), optional, intent(in) :: increment
42  call expandarray(this%times, increment=increment)
43  this%selection = (/1, size(this%times)/)
44  end subroutine expand
45 
46  !> @brief Initialize or clear the time selection object.
47  subroutine init(this)
48  class(timeselecttype) :: this
49  if (.not. allocated(this%times)) allocate (this%times(0))
50  this%selection = (/0, 0/)
51  end subroutine
52 
53  !> @brief Determine if times strictly increase.
54  !! Returns true if empty or not yet allocated.
55  function increasing(this) result(inc)
56  class(timeselecttype) :: this
57  logical(LGP) :: inc
58  integer(I4B) :: i
59  real(dp) :: l, t
60 
61  inc = .true.
62  if (.not. allocated(this%times)) return
63  do i = 1, size(this%times)
64  t = this%times(i)
65  if (i /= 1) then
66  if (l >= t) then
67  inc = .false.
68  return
69  end if
70  end if
71  l = t
72  end do
73  end function increasing
74 
75  !> @brief Select times between t0 and t1 (inclusive).
76  !!
77  !! Finds and stores the index of the first time at the same instant
78  !! as or following the start time, and of the last time at the same
79  !! instant as or preceding the end time. Allows filtering the times
80  !! for e.g. a particular stress period and time step. Array indices
81  !! are assumed to start at 1. If no times are found to fall within
82  !! the selection (i.e. it falls entirely between two consecutive
83  !! times or beyond the time range), indices are set to [-1, -1].
84  !!
85  !! The given start and end times are first checked against currently
86  !! stored indices to avoid recalculating them if possible, allowing
87  !! multiple consuming components (e.g., subdomain particle tracking
88  !! solutions) to share the object efficiently, provided all proceed
89  !! through stress periods and time steps in lockstep, i.e. they all
90  !! solve any given period/step before any will proceed to the next.
91  !<
92  subroutine select(this, t0, t1, changed)
93  ! -- dummy
94  class(timeselecttype) :: this
95  real(DP), intent(in) :: t0, t1
96  logical(LGP), intent(inout), optional :: changed
97  ! -- local
98  integer(I4B) :: i, i0, i1
99  integer(I4B) :: l, u, lp, up
100  real(DP) :: t
101 
102  ! -- by default, need to iterate over all times
103  i0 = 1
104  i1 = size(this%times)
105 
106  ! -- if no times fall within the slice, set to [-1, -1]
107  l = -1
108  u = -1
109 
110  ! -- previous bounding indices
111  lp = this%selection(1)
112  up = this%selection(2)
113 
114  ! -- Check if we can reuse either the lower or upper bound.
115  ! The lower doesn't need to change if it indexes the 1st
116  ! time simultaneous with or later than the slice's start.
117  ! The upper doesn't need to change if it indexes the last
118  ! time before or simultaneous with the slice's end.
119  if (lp > 0 .and. up > 0) then
120  if (lp > 1) then
121  if (this%times(lp - 1) < t0 .and. &
122  this%times(lp) >= t0) then
123  l = lp
124  i0 = l
125  end if
126  end if
127  if (up > 1 .and. up < i1) then
128  if (this%times(up + 1) > t1 .and. &
129  this%times(up) <= t1) then
130  u = up
131  i1 = u
132  end if
133  end if
134  if (l == lp .and. u == up) then
135  this%selection = (/l, u/)
136  if (present(changed)) changed = .false.
137  return
138  end if
139  end if
140 
141  ! -- recompute bounding indices if needed
142  do i = i0, i1
143  t = this%times(i)
144  if (l < 0 .and. t >= t0 .and. t <= t1) l = i
145  if (l > 0 .and. t <= t1) u = i
146  end do
147  this%selection = (/l, u/)
148  if (present(changed)) changed = l /= lp .or. u /= up
149 
150  end subroutine
151 
152  !> @brief Update the selection to match the current time step.
153  subroutine try_advance(this)
154  ! -- modules
155  use tdismodule, only: kper, kstp, nper, nstp, totimc, delt
156  ! -- dummy
157  class(timeselecttype) :: this
158  ! -- local
159  real(DP) :: l, u
160  l = minval(this%times)
161  u = maxval(this%times)
162  if (.not. (kper == 1 .and. kstp == 1)) l = totimc
163  if (.not. (kper == nper .and. kstp == nstp(kper))) u = totimc + delt
164  call this%select(l, u)
165  end subroutine try_advance
166 
167 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:64
real(dp), parameter done
real constant 1
Definition: Constants.f90:75
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(s) to occur.
Definition: TimeSelect.f90:2
logical(lgp) function increasing(this)
Determine if times strictly increase. Returns true if empty or not yet allocated.
Definition: TimeSelect.f90:56
subroutine destroy(this)
Destroy the time selection object.
Definition: TimeSelect.f90:34
subroutine try_advance(this)
Update the selection to match the current time step.
Definition: TimeSelect.f90:154
subroutine expand(this, increment)
Expand capacity by the given amount. Resets the current slice.
Definition: TimeSelect.f90:40
subroutine select(this, t0, t1, changed)
Select times between t0 and t1 (inclusive).
Definition: TimeSelect.f90:93
Represents a series of instants at which some event should occur.
Definition: TimeSelect.f90:18