MODFLOW 6  version 6.8.0.dev0
USGS Modular Hydrologic Model
ParticleTracks.f90
Go to the documentation of this file.
1 !> @brief Particle track output module.
2 !!
3 !! Each particle's track consists of events reported as the particle is
4 !! advected through the model domain. Events are snapshots of particle
5 !! state, along with optional metadata, at a particular moment in time.
6 !!
7 !! Particles have no ID property. A particle can be uniquely identified
8 !! by the unique combination of its release attributes (model, package,
9 !! position, and time). This is possible because only one particle may
10 !! be released from a given point at a given time.
11 !!
12 !! This module consumes particle events and is responsible for writing
13 !! them to one or more track files, binary or CSV, and for logging the
14 !! events if requested. Each track file is associated with either a PRP
15 !! package or with the full PRT model (there may only be 1 such latter).
16 !!
17 !! Events can be buffered in memory or in a scratch file, and in either
18 !! case are flushed to disk only when a time step successfully finishes.
19 !<
21 
22  use kindmodule, only: dp, i4b, lgp
23  use errorutilmodule, only: pstop
35  use basedismodule, only: disbasetype
36  use geomutilmodule, only: transform
40 
41  implicit none
42  public :: particletrackstype, &
45 
46  character(len=*), parameter, public :: trackheader = &
47  'kper,kstp,imdl,iprp,irpt,ilay,icell,izone,&
48  &istatus,ireason,trelease,t,x,y,z,name'
49 
50  character(len=*), parameter, public :: trackdtypes = &
51  '<i4,<i4,<i4,<i4,<i4,<i4,<i4,<i4,&
52  &<i4,<i4,<f8,<f8,<f8,<f8,<f8,|S40'
53 
54  !> @brief Selection of particle events.
56  logical(LGP) :: release !< track release events
57  logical(LGP) :: featexit !< track grid feature exits
58  logical(LGP) :: timestep !< track timestep ends
59  logical(LGP) :: terminate !< track termination events
60  logical(LGP) :: weaksink !< track weak sink exit events
61  logical(LGP) :: usertime !< track user-selected times
62  logical(LGP) :: subfexit !< track subfeature exits
63  logical(LGP) :: dropped !< track water table drops
65 
66  !> @brief Particle track output manager. Handles printing as well as writing
67  !! to files. One output unit can be configured for printing. Multiple files
68  !! can be configured for writing, with each file optionally associated with
69  !! a PRP package or with the full model. Events can be filtered by type, so
70  !! that only certain event types are printed or written to files. Particle
71  !! events are buffered in memory or in a scratch file, flushed to disk only
72  !! when the time step is successfully solved for the last time (there may
73  !! be multiple solves per time step, depending on ATS and Picard options).
74  !<
76  private
77  integer(I4B), public :: iout = -1 !< log file unit
78  integer(I4B), public :: ntrackfiles !< number of track files
79  type(particletrackfiletype), public, allocatable :: files(:) !< track files
80  type(particletrackeventselectiontype), public :: selected !< event selection
81  class(particletrackeventbuffertype), allocatable :: buffer !< event buffer
82  contains
83  procedure, public :: init_file
84  procedure, public :: init_buffer
85  procedure, public :: is_selected
86  procedure, public :: select_events
87  procedure, public :: buffer_event
88  procedure, public :: flush_buffer
89  procedure, public :: discard_buffer
90  procedure, public :: destroy
91  procedure :: expand_files
92  end type particletrackstype
93 
94 contains
95 
96  !> @brief Initialize a binary or CSV output file.
97  subroutine init_file(this, iun, csv, iprp)
98  class(particletrackstype) :: this
99  integer(I4B), intent(in) :: iun
100  logical(LGP), intent(in), optional :: csv
101  integer(I4B), intent(in), optional :: iprp
102  type(particletrackfiletype), pointer :: file
103 
104  call this%expand_files(increment=1)
105 
106  allocate (file)
107  file%iun = iun
108  if (present(csv)) file%csv = csv
109  if (present(iprp)) file%iprp = iprp
110  this%ntrackfiles = size(this%files)
111  this%files(this%ntrackfiles) = file
112  end subroutine init_file
113 
114  !> @brief Initialize the event buffer strategy
115  subroutine init_buffer(this, scratch)
116  class(particletrackstype) :: this
117  logical(LGP), intent(in) :: scratch
118 
119  if (scratch) then
120  allocate (scratchfilebuffertype :: this%buffer)
121  else
122  allocate (memorybuffertype :: this%buffer)
123  end if
124  call this%buffer%init()
125  end subroutine init_buffer
126 
127  !> @brief Destroy the particle track manager.
128  subroutine destroy(this)
129  class(particletrackstype) :: this
130  call this%buffer%destroy()
131  end subroutine destroy
132 
133  !> @brief Grow the array of track files.
134  subroutine expand_files(this, increment)
135  class(particletrackstype) :: this
136  integer(I4B), optional, intent(in) :: increment
137  integer(I4B) :: inclocal, isize, newsize
138  type(particletrackfiletype), allocatable, dimension(:) :: temp
139 
140  if (present(increment)) then
141  inclocal = increment
142  else
143  inclocal = 1
144  end if
145 
146  if (allocated(this%files)) then
147  isize = size(this%files)
148  newsize = isize + inclocal
149  allocate (temp(newsize))
150  temp(1:isize) = this%files
151  deallocate (this%files)
152  call move_alloc(temp, this%files)
153  else
154  allocate (this%files(inclocal))
155  end if
156  end subroutine expand_files
157 
158  !> @brief Pick events to track.
159  subroutine select_events(this, &
160  release, &
161  featexit, &
162  timestep, &
163  terminate, &
164  weaksink, &
165  usertime, &
166  subfexit, &
167  dropped)
168  class(particletrackstype) :: this
169  logical(LGP), intent(in) :: release
170  logical(LGP), intent(in) :: featexit
171  logical(LGP), intent(in) :: timestep
172  logical(LGP), intent(in) :: terminate
173  logical(LGP), intent(in) :: weaksink
174  logical(LGP), intent(in) :: usertime
175  logical(LGP), intent(in) :: subfexit
176  logical(LGP), intent(in) :: dropped
177  this%selected%release = release
178  this%selected%featexit = featexit
179  this%selected%timestep = timestep
180  this%selected%terminate = terminate
181  this%selected%weaksink = weaksink
182  this%selected%usertime = usertime
183  this%selected%subfexit = subfexit
184  this%selected%dropped = dropped
185  end subroutine select_events
186 
187  !> @brief Check if a given event code is selected for tracking.
188  logical function is_selected(this, event) result(selected)
189  class(particletrackstype), intent(inout) :: this
190  class(particleeventtype), intent(in) :: event
191 
192  select type (event)
193  type is (releaseeventtype)
194  selected = this%selected%release
195  type is (cellexiteventtype)
196  selected = this%selected%featexit
197  type is (subcellexiteventtype)
198  selected = this%selected%subfexit
199  type is (timestepeventtype)
200  selected = this%selected%timestep
201  type is (terminationeventtype)
202  selected = this%selected%terminate
203  type is (weaksinkeventtype)
204  selected = this%selected%weaksink
205  type is (usertimeeventtype)
206  selected = this%selected%usertime
207  type is (droppedeventtype)
208  selected = this%selected%dropped
209  class default
210  call pstop(1, "unknown event type")
211  selected = .false.
212  end select
213  end function is_selected
214 
215  !> @brief Buffer an event for deferred write.
216  subroutine buffer_event(this, particle, event)
217  class(particletrackstype) :: this
218  type(particletype), pointer, intent(in) :: particle
219  class(particleeventtype), pointer, intent(in) :: event
220  type(particletrackrecordtype) :: rec
221 
222  rec%kper = event%kper; rec%kstp = event%kstp
223  rec%imdl = event%imdl; rec%iprp = event%iprp
224  rec%irpt = event%irpt; rec%ilay = event%ilay
225  rec%icu = event%icu; rec%izone = event%izone
226  rec%istatus = event%istatus; rec%ireason = event%get_code()
227  rec%trelease = event%trelease; rec%ttrack = event%ttrack
228  rec%x = event%x; rec%y = event%y; rec%z = event%z
229  rec%name = trim(adjustl(particle%name))
230 
231  call this%buffer%append(rec)
232  end subroutine buffer_event
233 
234  !> @brief Flush the event buffer to disk.
235  subroutine flush_buffer(this)
236  class(particletrackstype) :: this
237  call this%buffer%flush(this%files)
238  end subroutine flush_buffer
239 
240  !> @brief Discard buffered events without writing.
241  subroutine discard_buffer(this)
242  class(particletrackstype) :: this
243  call this%buffer%discard()
244  end subroutine discard_buffer
245 
246  !> @brief Add a particle event to be written to eligible
247  !! files and printed to an output file unit if requested.
248  !! This function should be subscribed as an event handler
249  !! to particle event dispatchers. Events are buffered to
250  !! be written to output files upon successful completion
251  !! of a time step when the framework OT hook is executed.
252  function add_particle_event(context, particle, event) result(handled)
253  class(*), pointer :: context
254  type(particletype), pointer, intent(inout) :: particle
255  class(particleeventtype), pointer, intent(in) :: event
256  logical(LGP) :: handled
257 
258  select type (context)
259  type is (particletrackstype)
260  if (context%iout >= 0) &
261  call event%log(context%iout)
262  if (context%is_selected(event)) &
263  call context%buffer_event(particle, event)
264  handled = .true.
265  end select
266  end function add_particle_event
267 
268 end module particletracksmodule
subroutine pstop(status, message)
Stop the program, optionally specifying an error status code.
Definition: ErrorUtil.f90:24
subroutine, public transform(xin, yin, zin, xout, yout, zout, xorigin, yorigin, zorigin, sinrot, cosrot, invert)
Apply a 3D translation and optional 2D rotation to coordinates.
Definition: GeomUtil.f90:183
This module defines variable data types.
Definition: kind.f90:8
In-memory particle event buffer module.
Definition: MemoryBuffer.f90:3
Particle event buffering strategies.
Particle track output module.
subroutine discard_buffer(this)
Discard buffered events without writing.
logical function is_selected(this, event)
Check if a given event code is selected for tracking.
subroutine destroy(this)
Destroy the particle track manager.
subroutine init_file(this, iun, csv, iprp)
Initialize a binary or CSV output file.
subroutine buffer_event(this, particle, event)
Buffer an event for deferred write.
logical(lgp) function, public add_particle_event(context, particle, event)
Add a particle event to be written to eligible files and printed to an output file unit if requested....
character(len= *), parameter, public trackheader
subroutine flush_buffer(this)
Flush the event buffer to disk.
subroutine select_events(this, release, featexit, timestep, terminate, weaksink, usertime, subfexit, dropped)
Pick events to track.
subroutine expand_files(this, increment)
Grow the array of track files.
subroutine init_buffer(this, scratch)
Initialize the event buffer strategy.
character(len= *), parameter, public trackdtypes
Scratch-file particle event buffer module.
In-memory particle event buffer. Records are held in a dynamically growing array that doubles in capa...
Base type for particle events.
Dispatcher for particle events. Consumers subscribe handlers to the dispatcher. Events may be dispatc...
Particle tracked by the PRT model.
Definition: Particle.f90:64
Output file containing all or some particle pathlines.
Particle track output manager. Handles printing as well as writing to files. One output unit can be c...
Scratch-file particle event buffer. Records are written to an unformatted sequential scratch file tha...