MODFLOW 6  version 6.7.0.dev2
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 constantsmodule, only: dzero, done, dpio180
25  use basedismodule, only: disbasetype
26  use geomutilmodule, only: transform
27 
28  implicit none
29  public :: particletrackfiletype, &
32  private :: log_event, save_event
33 
34  character(len=*), parameter, public :: trackheader = &
35  'kper,kstp,imdl,iprp,irpt,ilay,icell,izone,&
36  &istatus,ireason,trelease,t,x,y,z,name'
37 
38  character(len=*), parameter, public :: trackdtypes = &
39  '<i4,<i4,<i4,<i4,<i4,<i4,<i4,<i4,&
40  &<i4,<i4,<f8,<f8,<f8,<f8,<f8,|S40'
41 
42  !> @brief Output file containing all or some particle pathlines.
43  !!
44  !! Can be associated with a particle release point (PRP) package
45  !! or with an entire model, and can be binary or comma-separated.
46  !<
48  private
49  integer(I4B), public :: iun = 0 !< file unit number
50  logical(LGP), public :: csv = .false. !< whether the file is binary or CSV
51  integer(I4B), public :: iprp = -1 !< -1 is model-level file, 0 is exchange PRP
52  end type particletrackfiletype
53 
54  !> @brief Selection of particle events.
56  logical(LGP) :: release !< track release events
57  logical(LGP) :: cellexit !< track cell 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
63 
64  !> @brief Manages particle track output (logging/writing).
65  !!
66  !! Optionally filters events as selected in the PRT Output Control package.
67  !! An arbitrary number of files can be managed, resizing is done as needed.
68  !<
70  private
71  integer(I4B), public :: iout = -1 !< log file unit
72  integer(I4B), public :: ntrackfiles !< number of track files
73  type(particletrackfiletype), public, allocatable :: files(:) !< track files
74  type(particletrackeventselectiontype), public :: selected !< event selection
75  contains
76  procedure, public :: init_file
77  procedure, public :: is_selected
78  procedure, public :: select_events
79  procedure, public :: destroy
80  procedure :: expand_files
81  procedure :: handle_event
82  procedure :: should_save
83  procedure :: should_log
84  end type particletrackstype
85 
86 contains
87 
88  !> @brief Initialize a binary or CSV file.
89  subroutine init_file(this, iun, csv, iprp)
90  ! dummy
91  class(particletrackstype) :: this
92  integer(I4B), intent(in) :: iun
93  logical(LGP), intent(in), optional :: csv
94  integer(I4B), intent(in), optional :: iprp
95  ! local
96  type(particletrackfiletype), pointer :: file
97 
98  if (.not. allocated(this%files)) then
99  allocate (this%files(1))
100  else
101  call this%expand_files(increment=1)
102  end if
103 
104  allocate (file)
105  file%iun = iun
106  if (present(csv)) file%csv = csv
107  if (present(iprp)) file%iprp = iprp
108  this%ntrackfiles = size(this%files)
109  this%files(this%ntrackfiles) = file
110  end subroutine init_file
111 
112  subroutine destroy(this)
113  class(particletrackstype) :: this
114  if (allocated(this%files)) deallocate (this%files)
115  end subroutine destroy
116 
117  !> @brief Grow the array of track files.
118  subroutine expand_files(this, increment)
119  ! dummy
120  class(particletrackstype) :: this
121  integer(I4B), optional, intent(in) :: increment
122  ! local
123  integer(I4B) :: inclocal
124  integer(I4B) :: isize
125  integer(I4B) :: newsize
126  type(particletrackfiletype), allocatable, dimension(:) :: temp
127 
128  if (present(increment)) then
129  inclocal = increment
130  else
131  inclocal = 1
132  end if
133 
134  if (allocated(this%files)) then
135  isize = size(this%files)
136  newsize = isize + inclocal
137  allocate (temp(newsize))
138  temp(1:isize) = this%files
139  deallocate (this%files)
140  call move_alloc(temp, this%files)
141  else
142  allocate (this%files(inclocal))
143  end if
144  end subroutine expand_files
145 
146  !> @brief Pick events to track.
147  subroutine select_events(this, &
148  release, &
149  cellexit, &
150  timestep, &
151  terminate, &
152  weaksink, &
153  usertime)
154  class(particletrackstype) :: this
155  logical(LGP), intent(in) :: release
156  logical(LGP), intent(in) :: cellexit
157  logical(LGP), intent(in) :: timestep
158  logical(LGP), intent(in) :: terminate
159  logical(LGP), intent(in) :: weaksink
160  logical(LGP), intent(in) :: usertime
161  this%selected%release = release
162  this%selected%cellexit = cellexit
163  this%selected%timestep = timestep
164  this%selected%terminate = terminate
165  this%selected%weaksink = weaksink
166  this%selected%usertime = usertime
167  end subroutine select_events
168 
169  !> @brief Check if a given event code is selected for tracking.
170  logical function is_selected(this, event_code) result(selected)
171  class(particletrackstype), intent(inout) :: this
172  integer(I4B), intent(in) :: event_code
173 
174  selected = (this%selected%release .and. event_code == 0) .or. &
175  (this%selected%cellexit .and. event_code == 1) .or. &
176  (this%selected%timestep .and. event_code == 2) .or. &
177  (this%selected%terminate .and. event_code == 3) .or. &
178  (this%selected%weaksink .and. event_code == 4) .or. &
179  (this%selected%usertime .and. event_code == 5)
180  end function is_selected
181 
182  !> @brief Check whether a particle belongs in a given file i.e.
183  !! if the file is enabled and its group matches the particle's.
184  logical function should_save(this, particle, file) result(save)
185  class(particletrackstype), intent(inout) :: this
186  type(particletype), pointer, intent(in) :: particle
187  type(particletrackfiletype), intent(in) :: file
188  save = (file%iun > 0 .and. &
189  (file%iprp == -1 .or. file%iprp == particle%iprp))
190  end function should_save
191 
192  !> @brief Save an event to a binary or CSV file.
193  subroutine save_event(iun, particle, event, csv)
194  ! dummy
195  integer(I4B), intent(in) :: iun
196  type(particletype), pointer, intent(in) :: particle
197  class(particleeventtype), pointer, intent(in) :: event
198  logical(LGP), intent(in) :: csv
199  ! local
200  real(dp) :: x, y, z
201  integer(I4B) :: status
202 
203  ! Convert from cell-local to model coordinates if needed
204  call particle%get_model_coords(x, y, z)
205 
206  ! Set status
207  if (particle%istatus .lt. 0) then
208  status = active
209  else
210  status = particle%istatus
211  end if
212 
213  if (csv) then
214  write (iun, '(*(G0,:,","))') &
215  event%kper, &
216  event%kstp, &
217  particle%imdl, &
218  particle%iprp, &
219  particle%irpt, &
220  particle%ilay, &
221  particle%icu, &
222  particle%izone, &
223  status, &
224  event%get_code(), &
225  particle%trelease, &
226  particle%ttrack, &
227  x, &
228  y, &
229  z, &
230  trim(adjustl(particle%name))
231  else
232  write (iun) &
233  event%kper, &
234  event%kstp, &
235  particle%imdl, &
236  particle%iprp, &
237  particle%irpt, &
238  particle%ilay, &
239  particle%icu, &
240  particle%izone, &
241  status, &
242  event%get_code(), &
243  particle%trelease, &
244  particle%ttrack, &
245  x, &
246  y, &
247  z, &
248  particle%name
249  end if
250  end subroutine save_event
251 
252  !> @brief Log output unit valid?
253  logical function should_log(this)
254  class(particletrackstype), intent(inout) :: this
255  should_log = this%iout >= 0
256  end function should_log
257 
258  !> @brief Print a particle event summary.
259  subroutine log_event(iun, particle, event)
260  integer(I4B), intent(in) :: iun
261  type(particletype), pointer, intent(in) :: particle
262  class(particleeventtype), pointer, intent(in) :: event
263  ! local
264  character(len=:), allocatable :: particlename
265 
266  particlename = trim(adjustl(particle%name))
267  if (len_trim(particlename) == 0) particlename = 'anonymous'
268 
269  if (iun >= 0) &
270  write (iun, '(*(G0))') &
271  'Particle (Model: ', particle%imdl, &
272  ', Package: ', particle%iprp, &
273  ', Point: ', particle%irpt, ' [', particlename, ']', &
274  ', Time: ', particle%trelease, &
275  ') ', event%get_str(), &
276  ' in (Layer: ', particle%ilay, &
277  ', Cell: ', particle%icu, &
278  ', Zone: ', particle%izone, &
279  ') at (X: ', particle%x, &
280  ', Y: ', particle%y, &
281  ', Z: ', particle%z, &
282  ', Time: ', particle%ttrack, &
283  ', Period: ', event%kper, &
284  ', Timestep: ', event%kstp, &
285  ') with (Status: ', particle%istatus, ')'
286  end subroutine log_event
287 
288  !> @brief Handle a particle event.
289  subroutine handle_event(this, particle, event)
290  ! dummy
291  class(particletrackstype), intent(inout) :: this
292  type(particletype), pointer, intent(in) :: particle
293  class(particleeventtype), pointer, intent(in) :: event
294  ! local
295  integer(I4B) :: i
296  type(particletrackfiletype) :: file
297 
298  if (this%should_log()) &
299  call log_event(this%iout, particle, event)
300 
301  if (this%is_selected(event%get_code())) then
302  do i = 1, this%ntrackfiles
303  file = this%files(i)
304  if (this%should_save(particle, file)) &
305  call save_event(file%iun, particle, event, csv=file%csv)
306  end do
307  end if
308  end subroutine handle_event
309 
310 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, 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.
subroutine destroy(this)
subroutine init_file(this, iun, csv, iprp)
Initialize a binary or CSV file.
subroutine select_events(this, release, cellexit, timestep, terminate, weaksink, usertime)
Pick events to track.
character(len= *), parameter, public trackheader
subroutine expand_files(this, increment)
Grow the array of track files.
logical function is_selected(this, event_code)
Check if a given event code is selected for tracking.
subroutine, private log_event(iun, particle, event)
Print a particle event summary.
subroutine, private save_event(iun, particle, event, csv)
Save an event to a binary or CSV file.
logical function should_log(this)
Log output unit valid?
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.
Particle tracked by the PRT model.
Definition: Particle.f90:59
Output file containing all or some particle pathlines.
Manages particle track output (logging/writing).