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 !<
18 
19  use kindmodule, only: dp, i4b, lgp
20  use errorutilmodule, only: pstop
21  use constantsmodule, only: dzero, done, dpio180
33  use basedismodule, only: disbasetype
34  use geomutilmodule, only: transform
35 
36  implicit none
37  public :: particletrackfiletype, &
41  private :: save_event
42 
43  character(len=*), parameter, public :: trackheader = &
44  'kper,kstp,imdl,iprp,irpt,ilay,icell,izone,&
45  &istatus,ireason,trelease,t,x,y,z,name'
46 
47  character(len=*), parameter, public :: trackdtypes = &
48  '<i4,<i4,<i4,<i4,<i4,<i4,<i4,<i4,&
49  &<i4,<i4,<f8,<f8,<f8,<f8,<f8,|S40'
50 
51  !> @brief Output file containing all or some particle pathlines.
52  !!
53  !! Can be associated with a particle release point (PRP) package
54  !! or with an entire model, and can be binary or text (CSV).
55  !<
57  private
58  integer(I4B), public :: iun = 0 !< file unit number
59  logical(LGP), public :: csv = .false. !< whether the file is binary or CSV
60  integer(I4B), public :: iprp = -1 !< -1 is model-level file, 0 is exchange PRP
61  end type particletrackfiletype
62 
63  !> @brief Selection of particle events.
65  logical(LGP) :: release !< track release events
66  logical(LGP) :: featexit !< track grid feature exits
67  logical(LGP) :: timestep !< track timestep ends
68  logical(LGP) :: terminate !< track termination events
69  logical(LGP) :: weaksink !< track weak sink exit events
70  logical(LGP) :: usertime !< track user-selected times
71  logical(LGP) :: subfexit !< track subfeature exits
72  logical(LGP) :: dropped !< track water table drops
74 
75  !> @brief Particle track output manager. Handles printing as well as writing
76  !! to files. One output unit can be configured for printing. Multiple files
77  !! can be configured for writing, with each file optionally associated with
78  !! a PRP package or with the full model. Events can be filtered by type, so
79  !! that only certain event types are printed or written to files.
80  !<
82  private
83  integer(I4B), public :: iout = -1 !< log file unit
84  integer(I4B), public :: ntrackfiles !< number of track files
85  type(particletrackfiletype), public, allocatable :: files(:) !< track files
86  type(particletrackeventselectiontype), public :: selected !< event selection
87  contains
88  procedure, public :: init_file
89  procedure, public :: is_selected
90  procedure, public :: select_events
91  procedure, public :: destroy
92  procedure :: expand_files
93  procedure :: should_save
94  procedure :: should_print
95  end type particletrackstype
96 
97 contains
98 
99  !> @brief Initialize a binary or CSV file.
100  subroutine init_file(this, iun, csv, iprp)
101  ! dummy
102  class(particletrackstype) :: this
103  integer(I4B), intent(in) :: iun
104  logical(LGP), intent(in), optional :: csv
105  integer(I4B), intent(in), optional :: iprp
106  ! local
107  type(particletrackfiletype), pointer :: file
108 
109  if (.not. allocated(this%files)) then
110  allocate (this%files(1))
111  else
112  call this%expand_files(increment=1)
113  end if
114 
115  allocate (file)
116  file%iun = iun
117  if (present(csv)) file%csv = csv
118  if (present(iprp)) file%iprp = iprp
119  this%ntrackfiles = size(this%files)
120  this%files(this%ntrackfiles) = file
121  end subroutine init_file
122 
123  !> @brief Destroy the particle track manager.
124  subroutine destroy(this)
125  class(particletrackstype) :: this
126  if (allocated(this%files)) deallocate (this%files)
127  end subroutine destroy
128 
129  !> @brief Grow the array of track files.
130  subroutine expand_files(this, increment)
131  ! dummy
132  class(particletrackstype) :: this
133  integer(I4B), optional, intent(in) :: increment
134  ! local
135  integer(I4B) :: inclocal
136  integer(I4B) :: isize
137  integer(I4B) :: 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 
214  end function is_selected
215 
216  !> @brief Check whether a particle belongs in a given file i.e.
217  !! if the file is enabled and its group matches the particle's.
218  logical function should_save(this, particle, file) result(save)
219  class(particletrackstype), intent(inout) :: this
220  type(particletype), pointer, intent(in) :: particle
221  type(particletrackfiletype), intent(in) :: file
222  save = (file%iun > 0 .and. &
223  (file%iprp == -1 .or. file%iprp == particle%iprp))
224  end function should_save
225 
226  !> @brief Save an event to a binary or CSV file.
227  subroutine save_event(iun, particle, event, csv)
228  ! dummy
229  integer(I4B), intent(in) :: iun
230  type(particletype), pointer, intent(in) :: particle
231  class(particleeventtype), pointer, intent(in) :: event
232  logical(LGP), intent(in) :: csv
233 
234  if (csv) then
235  write (iun, '(*(G0,:,","))') &
236  event%kper, &
237  event%kstp, &
238  event%imdl, &
239  event%iprp, &
240  event%irpt, &
241  event%ilay, &
242  event%icu, &
243  event%izone, &
244  event%istatus, &
245  event%get_code(), &
246  event%trelease, &
247  event%ttrack, &
248  event%x, &
249  event%y, &
250  event%z, &
251  trim(adjustl(particle%name))
252  else
253  write (iun) &
254  event%kper, &
255  event%kstp, &
256  event%imdl, &
257  event%iprp, &
258  event%irpt, &
259  event%ilay, &
260  event%icu, &
261  event%izone, &
262  event%istatus, &
263  event%get_code(), &
264  event%trelease, &
265  event%ttrack, &
266  event%x, &
267  event%y, &
268  event%z, &
269  particle%name
270  end if
271  end subroutine save_event
272 
273  !> @brief Is the output unit valid?
274  logical function should_print(this)
275  class(particletrackstype), intent(inout) :: this
276  should_print = this%iout >= 0
277  end function should_print
278 
279  !> @brief Write a particle event to files for which the particle
280  !! is eligible, and print the event to output unit if requested.
281  !! This function is the module's main entry point. It should be
282  !! subscribed as an event handler to particle event dispatchers.
283  function write_particle_event(context, particle, event) result(handled)
284  ! dummy
285  class(*), pointer :: context
286  type(particletype), pointer, intent(inout) :: particle
287  class(particleeventtype), pointer, intent(in) :: event
288  logical(LGP) :: handled
289  ! local
290  integer(I4B) :: i
291  type(particletrackfiletype) :: file
292 
293  select type (context)
294  type is (particletrackstype)
295  if (context%should_print()) &
296  call event%log(context%iout)
297  if (context%is_selected(event)) then
298  do i = 1, context%ntrackfiles
299  file = context%files(i)
300  if (context%should_save(particle, file)) &
301  call save_event(file%iun, particle, event, csv=file%csv)
302  end do
303  end if
304  handled = .true.
305  end select
306  end function write_particle_event
307 
308 end module particletracksmodule
This module contains simulation constants.
Definition: Constants.f90:9
real(dp), parameter dpio180
real constant
Definition: Constants.f90:130
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
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
Particle track output module.
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 file.
character(len= *), parameter, public trackheader
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.
logical function should_print(this)
Is the output unit valid?
subroutine, private save_event(iun, particle, event, csv)
Save an event to a binary or CSV file.
logical(lgp) function, public write_particle_event(context, particle, event)
Write a particle event to files for which the particle is eligible, and print the event to output uni...
character(len= *), parameter, public trackdtypes
logical function should_save(this, particle, file)
Check whether a particle belongs in a given file i.e. if the file is enabled and its group matches th...
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:62
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...