MODFLOW 6  version 6.6.0.dev0
USGS Modular Hydrologic Model
Particle.f90
Go to the documentation of this file.
2 
3  use kindmodule, only: dp, i4b, lgp
7  implicit none
8 
9  private
10  public :: particletype, particlestoretype, &
12 
13  ! tracking levels (1: model, 2: cell, 3: subcell)
14  integer, parameter, public :: levelmax = 4
15 
16  !> @brief Particle tracked by the PRT model.
17  !!
18  !! Record-type to conveniently shuffle a particle's
19  !! state to/from storage before/after its trajectory
20  !! is solved for each time step.
21  !!
22  !! Particle coordinates may be local to the cell or
23  !! global/model. Routines are provided to convert a
24  !! particle's global coordinates to/from cell-local
25  !! coordinates for tracking through cell subdomains.
26  !!
27  !! Particles are identified by composite key, i.e.,
28  !! combinations of properties imdl, iprp, irpt, and
29  !! trelease. An optional label may be provided, but
30  !! need not be unique
31  !<
33  private
34  ! identity
35  character(len=LENBOUNDNAME), public :: name = '' !< optional particle name
36  integer(I4B), public :: imdl !< index of model the particle originated in
37  integer(I4B), public :: iprp !< index of release package the particle is from
38  integer(I4B), public :: irpt !< index of release point the particle is from
39  integer(I4B), public :: ip !< index of particle in the particle list
40  ! stop criteria
41  integer(I4B), public :: istopweaksink !< weak sink option (0: do not stop, 1: stop)
42  integer(I4B), public :: istopzone !< stop zone number
43  ! state
44  integer(I4B), allocatable, public :: idomain(:) !< tracking domain hierarchy
45  integer(I4B), allocatable, public :: iboundary(:) !< tracking domain boundaries
46  integer(I4B), public :: icu !< user cell (node) number
47  integer(I4B), public :: ilay !< grid layer
48  integer(I4B), public :: izone !< zone number
49  integer(I4B), public :: istatus !< tracking status
50  real(dp), public :: x !< x coordinate
51  real(dp), public :: y !< y coordinate
52  real(dp), public :: z !< z coordinate
53  real(dp), public :: trelease !< release time
54  real(dp), public :: tstop !< stop time
55  real(dp), public :: ttrack !< time tracked so far
56  real(dp), public :: xorigin !< x origin for coordinate transformation from model to local
57  real(dp), public :: yorigin !< y origin for coordinate transformation from model to local
58  real(dp), public :: zorigin !< z origin for coordinate transformation from model to local
59  real(dp), public :: sinrot !< sine of rotation angle for coordinate transformation from model to local
60  real(dp), public :: cosrot !< cosine of rotation angle for coordinate transformation from model to local
61  real(dp), public :: extol !< tolerance for iterative solution of particle exit location and time in generalized Pollock's method
62  logical(LGP), public :: transformed !< whether coordinates have been transformed from model to local
63  logical(LGP), public :: advancing !< whether particle is still being tracked for current time step
64  integer(I4B), public :: ifrctrn !< whether to force solving the particle with the ternary method
65  integer(I4B), public :: iexmeth !< method for iterative solution of particle exit location and time in generalized Pollock's method
66  integer(I4B), public :: iextend !< whether to extend tracking beyond the end of the simulation
67  contains
68  procedure, public :: get_model_coords
69  procedure, public :: load_particle
70  procedure, public :: transform => transform_coords
71  end type particletype
72 
73  !> @brief Structure of arrays to store particles.
75  private
76  ! identity
77  character(len=LENBOUNDNAME), dimension(:), pointer, public, contiguous :: name !< optional particle label
78  integer(I4B), dimension(:), pointer, public, contiguous :: imdl !< index of model particle originated in
79  integer(I4B), dimension(:), pointer, public, contiguous :: iprp !< index of release package the particle originated in
80  integer(I4B), dimension(:), pointer, public, contiguous :: irpt !< index of release point in the particle release package the particle originated in
81  ! stopping criteria
82  integer(I4B), dimension(:), pointer, public, contiguous :: istopweaksink !< weak sink option: 0 = do not stop, 1 = stop
83  integer(I4B), dimension(:), pointer, public, contiguous :: istopzone !< stop zone number
84  ! state
85  integer(I4B), dimension(:, :), pointer, public, contiguous :: idomain !< array of indices for domains in the tracking domain hierarchy
86  integer(I4B), dimension(:, :), pointer, public, contiguous :: iboundary !< array of indices for tracking domain boundaries
87  integer(I4B), dimension(:), pointer, public, contiguous :: icu !< cell number (user, not reduced)
88  integer(I4B), dimension(:), pointer, public, contiguous :: ilay !< layer
89  integer(I4B), dimension(:), pointer, public, contiguous :: izone !< current zone number
90  integer(I4B), dimension(:), pointer, public, contiguous :: istatus !< particle status
91  real(dp), dimension(:), pointer, public, contiguous :: x !< model x coord of particle
92  real(dp), dimension(:), pointer, public, contiguous :: y !< model y coord of particle
93  real(dp), dimension(:), pointer, public, contiguous :: z !< model z coord of particle
94  real(dp), dimension(:), pointer, public, contiguous :: trelease !< particle release time
95  real(dp), dimension(:), pointer, public, contiguous :: tstop !< particle stop time
96  real(dp), dimension(:), pointer, public, contiguous :: ttrack !< current tracking time
97  integer(I4B), dimension(:), pointer, public, contiguous :: ifrctrn !< force ternary method
98  integer(I4B), dimension(:), pointer, public, contiguous :: iexmeth !< method for iterative solution of particle exit location and time in generalized Pollock's method
99  real(dp), dimension(:), pointer, public, contiguous :: extol !< tolerance for iterative solution of particle exit location and time in generalized Pollock's method
100  integer(LGP), dimension(:), pointer, public, contiguous :: extend !< whether to extend tracking beyond the end of the simulation
101  contains
102  procedure, public :: deallocate
103  procedure, public :: num_stored
104  procedure, public :: resize
105  procedure, public :: save_particle
106  end type particlestoretype
107 
108 contains
109 
110  !> @brief Create a new particle
111  subroutine create_particle(particle)
112  type(particletype), pointer :: particle !< particle
113  allocate (particle)
114  allocate (particle%idomain(levelmax))
115  allocate (particle%iboundary(levelmax))
116  end subroutine create_particle
117 
118  !> @brief Create a new particle store
119  subroutine allocate_particle_store(this, np, mempath)
120  type(particlestoretype), pointer :: this !< store
121  integer(I4B), intent(in) :: np !< number of particles
122  character(*), intent(in) :: mempath !< path to memory
123 
124  allocate (this)
125  call mem_allocate(this%imdl, np, 'PLIMDL', mempath)
126  call mem_allocate(this%irpt, np, 'PLIRPT', mempath)
127  call mem_allocate(this%iprp, np, 'PLIPRP', mempath)
128  call mem_allocate(this%name, lenboundname, np, 'PLNAME', mempath)
129  call mem_allocate(this%icu, np, 'PLICU', mempath)
130  call mem_allocate(this%ilay, np, 'PLILAY', mempath)
131  call mem_allocate(this%izone, np, 'PLIZONE', mempath)
132  call mem_allocate(this%istatus, np, 'PLISTATUS', mempath)
133  call mem_allocate(this%x, np, 'PLX', mempath)
134  call mem_allocate(this%y, np, 'PLY', mempath)
135  call mem_allocate(this%z, np, 'PLZ', mempath)
136  call mem_allocate(this%trelease, np, 'PLTRELEASE', mempath)
137  call mem_allocate(this%tstop, np, 'PLTSTOP', mempath)
138  call mem_allocate(this%ttrack, np, 'PLTTRACK', mempath)
139  call mem_allocate(this%istopweaksink, np, 'PLISTOPWEAKSINK', mempath)
140  call mem_allocate(this%istopzone, np, 'PLISTOPZONE', mempath)
141  call mem_allocate(this%ifrctrn, np, 'PLIFRCTRN', mempath)
142  call mem_allocate(this%iexmeth, np, 'PLIEXMETH', mempath)
143  call mem_allocate(this%extol, np, 'PLEXTOL', mempath)
144  call mem_allocate(this%extend, np, 'PLIEXTEND', mempath)
145  call mem_allocate(this%idomain, np, levelmax, 'PLIDOMAIN', mempath)
146  call mem_allocate(this%iboundary, np, levelmax, 'PLIBOUNDARY', mempath)
147  end subroutine allocate_particle_store
148 
149  !> @brief Deallocate particle arrays
150  subroutine deallocate (this, mempath)
151  class(particlestoretype), intent(inout) :: this !< store
152  character(*), intent(in) :: mempath !< path to memory
153 
154  call mem_deallocate(this%imdl, 'PLIMDL', mempath)
155  call mem_deallocate(this%iprp, 'PLIPRP', mempath)
156  call mem_deallocate(this%irpt, 'PLIRPT', mempath)
157  call mem_deallocate(this%name, 'PLNAME', mempath)
158  call mem_deallocate(this%icu, 'PLICU', mempath)
159  call mem_deallocate(this%ilay, 'PLILAY', mempath)
160  call mem_deallocate(this%izone, 'PLIZONE', mempath)
161  call mem_deallocate(this%istatus, 'PLISTATUS', mempath)
162  call mem_deallocate(this%x, 'PLX', mempath)
163  call mem_deallocate(this%y, 'PLY', mempath)
164  call mem_deallocate(this%z, 'PLZ', mempath)
165  call mem_deallocate(this%trelease, 'PLTRELEASE', mempath)
166  call mem_deallocate(this%tstop, 'PLTSTOP', mempath)
167  call mem_deallocate(this%ttrack, 'PLTTRACK', mempath)
168  call mem_deallocate(this%istopweaksink, 'PLISTOPWEAKSINK', mempath)
169  call mem_deallocate(this%istopzone, 'PLISTOPZONE', mempath)
170  call mem_deallocate(this%ifrctrn, 'PLIFRCTRN', mempath)
171  call mem_deallocate(this%iexmeth, 'PLIEXMETH', mempath)
172  call mem_deallocate(this%extol, 'PLEXTOL', mempath)
173  call mem_deallocate(this%extend, 'PLIEXTEND', mempath)
174  call mem_deallocate(this%idomain, 'PLIDOMAIN', mempath)
175  call mem_deallocate(this%iboundary, 'PLIBOUNDARY', mempath)
176  end subroutine deallocate
177 
178  !> @brief Reallocate particle arrays
179  subroutine resize(this, np, mempath)
180  ! -- dummy
181  class(particlestoretype), intent(inout) :: this !< particle store
182  integer(I4B), intent(in) :: np !< number of particles
183  character(*), intent(in) :: mempath !< path to memory
184 
185  ! resize arrays
186  call mem_reallocate(this%imdl, np, 'PLIMDL', mempath)
187  call mem_reallocate(this%iprp, np, 'PLIPRP', mempath)
188  call mem_reallocate(this%irpt, np, 'PLIRPT', mempath)
189  call mem_reallocate(this%name, lenboundname, np, 'PLNAME', mempath)
190  call mem_reallocate(this%icu, np, 'PLICU', mempath)
191  call mem_reallocate(this%ilay, np, 'PLILAY', mempath)
192  call mem_reallocate(this%izone, np, 'PLIZONE', mempath)
193  call mem_reallocate(this%istatus, np, 'PLISTATUS', mempath)
194  call mem_reallocate(this%x, np, 'PLX', mempath)
195  call mem_reallocate(this%y, np, 'PLY', mempath)
196  call mem_reallocate(this%z, np, 'PLZ', mempath)
197  call mem_reallocate(this%trelease, np, 'PLTRELEASE', mempath)
198  call mem_reallocate(this%tstop, np, 'PLTSTOP', mempath)
199  call mem_reallocate(this%ttrack, np, 'PLTTRACK', mempath)
200  call mem_reallocate(this%istopweaksink, np, 'PLISTOPWEAKSINK', mempath)
201  call mem_reallocate(this%istopzone, np, 'PLISTOPZONE', mempath)
202  call mem_reallocate(this%ifrctrn, np, 'PLIFRCTRN', mempath)
203  call mem_reallocate(this%iexmeth, np, 'PLIEXMETH', mempath)
204  call mem_reallocate(this%extol, np, 'PLEXTOL', mempath)
205  call mem_reallocate(this%extend, np, 'PLIEXTEND', mempath)
206  call mem_reallocate(this%idomain, np, levelmax, 'PLIDOMAIN', mempath)
207  call mem_reallocate(this%iboundary, np, levelmax, 'PLIBOUNDARY', mempath)
208  end subroutine resize
209 
210  !> @brief Load a particle from the particle store.
211  !!
212  !! This routine is used to initialize a particle for tracking.
213  !! The advancing flag and coordinate transformation are reset.
214  !<
215  subroutine load_particle(this, store, imdl, iprp, ip)
216  class(particletype), intent(inout) :: this !< particle
217  type(particlestoretype), intent(in) :: store !< particle storage
218  integer(I4B), intent(in) :: imdl !< index of model particle originated in
219  integer(I4B), intent(in) :: iprp !< index of particle release package particle originated in
220  integer(I4B), intent(in) :: ip !< index into the particle list
221 
222  call this%transform(reset=.true.)
223  this%imdl = imdl
224  this%iprp = iprp
225  this%irpt = store%irpt(ip)
226  this%ip = ip
227  this%name = store%name(ip)
228  this%istopweaksink = store%istopweaksink(ip)
229  this%istopzone = store%istopzone(ip)
230  this%icu = store%icu(ip)
231  this%ilay = store%ilay(ip)
232  this%izone = store%izone(ip)
233  this%istatus = store%istatus(ip)
234  this%x = store%x(ip)
235  this%y = store%y(ip)
236  this%z = store%z(ip)
237  this%trelease = store%trelease(ip)
238  this%tstop = store%tstop(ip)
239  this%ttrack = store%ttrack(ip)
240  this%advancing = .true.
241  this%idomain(1:levelmax) = &
242  store%idomain(ip, 1:levelmax)
243  this%idomain(1) = imdl
244  this%iboundary(1:levelmax) = &
245  store%iboundary(ip, 1:levelmax)
246  this%ifrctrn = store%ifrctrn(ip)
247  this%iexmeth = store%iexmeth(ip)
248  this%extol = store%extol(ip)
249  this%iextend = store%extend(ip)
250  end subroutine load_particle
251 
252  !> @brief Save a particle's state to the particle store
253  subroutine save_particle(this, particle, ip)
254  class(particlestoretype), intent(inout) :: this !< particle storage
255  type(particletype), intent(in) :: particle !< particle
256  integer(I4B), intent(in) :: ip !< particle index
257 
258  this%imdl(ip) = particle%imdl
259  this%iprp(ip) = particle%iprp
260  this%irpt(ip) = particle%irpt
261  this%name(ip) = particle%name
262  this%istopweaksink(ip) = particle%istopweaksink
263  this%istopzone(ip) = particle%istopzone
264  this%icu(ip) = particle%icu
265  this%ilay(ip) = particle%ilay
266  this%izone(ip) = particle%izone
267  this%istatus(ip) = particle%istatus
268  this%x(ip) = particle%x
269  this%y(ip) = particle%y
270  this%z(ip) = particle%z
271  this%trelease(ip) = particle%trelease
272  this%tstop(ip) = particle%tstop
273  this%ttrack(ip) = particle%ttrack
274  this%idomain( &
275  ip, &
276  1:levelmax) = &
277  particle%idomain(1:levelmax)
278  this%iboundary( &
279  ip, &
280  1:levelmax) = &
281  particle%iboundary(1:levelmax)
282  this%ifrctrn(ip) = particle%ifrctrn
283  this%iexmeth(ip) = particle%iexmeth
284  this%extol(ip) = particle%extol
285  this%extend(ip) = particle%iextend
286  end subroutine save_particle
287 
288  !> @brief Apply the given global-to-local transformation to the particle.
289  subroutine transform_coords(this, xorigin, yorigin, zorigin, &
290  sinrot, cosrot, invert, reset)
291  use geomutilmodule, only: transform, compose
292  class(particletype), intent(inout) :: this !< particle
293  real(DP), intent(in), optional :: xorigin !< x coordinate of origin
294  real(DP), intent(in), optional :: yorigin !< y coordinate of origin
295  real(DP), intent(in), optional :: zorigin !< z coordinate of origin
296  real(DP), intent(in), optional :: sinrot !< sine of rotation angle
297  real(DP), intent(in), optional :: cosrot !< cosine of rotation angle
298  logical(LGP), intent(in), optional :: invert !< whether to invert
299  logical(LGP), intent(in), optional :: reset !< whether to reset
300 
301  ! Reset if requested
302  if (present(reset)) then
303  if (reset) then
304  this%xorigin = dzero
305  this%yorigin = dzero
306  this%zorigin = dzero
307  this%sinrot = dzero
308  this%cosrot = done
309  this%cosrot = done
310  this%transformed = .false.
311  return
312  end if
313  end if
314 
315  ! Otherwise, transform coordinates
316  call transform(this%x, this%y, this%z, &
317  this%x, this%y, this%z, &
318  xorigin, yorigin, zorigin, &
319  sinrot, cosrot, invert)
320 
321  ! Modify transformation from model coordinates to particle's new
322  ! local coordinates by incorporating this latest transformation
323  call compose(this%xorigin, this%yorigin, this%zorigin, &
324  this%sinrot, this%cosrot, &
325  xorigin, yorigin, zorigin, &
326  sinrot, cosrot, invert)
327 
328  ! Set isTransformed flag to true. Note that there is no check
329  ! to see whether the modification brings the coordinates back
330  ! to model coordinates (in which case the origin would be very
331  ! close to zero and sinrot and cosrot would be very close to 0.
332  ! and 1., respectively, allowing for roundoff error).
333  this%transformed = .true.
334  end subroutine transform_coords
335 
336  !> @brief Return the particle's model (global) coordinates.
337  subroutine get_model_coords(this, x, y, z)
338  use geomutilmodule, only: transform, compose
339  class(particletype), intent(inout) :: this !< particle
340  real(DP), intent(out) :: x !< x coordinate
341  real(DP), intent(out) :: y !< y coordinate
342  real(DP), intent(out) :: z !< z coordinate
343 
344  if (this%transformed) then
345  ! Transform back from local to model coordinates
346  call transform(this%x, this%y, this%z, x, y, z, &
347  this%xorigin, this%yorigin, this%zorigin, &
348  this%sinrot, this%cosrot, .true.)
349  else
350  ! Already in model coordinates
351  x = this%x
352  y = this%y
353  z = this%z
354  end if
355  end subroutine get_model_coords
356 
357  integer function num_stored(this) result(n)
358  class(particlestoretype) :: this
359  n = size(this%imdl)
360  end function num_stored
361 
362 end module particlemodule
This module contains simulation constants.
Definition: Constants.f90:9
integer(i4b), parameter lenboundname
maximum length of a bound name
Definition: Constants.f90:36
real(dp), parameter dzero
real constant zero
Definition: Constants.f90:65
integer(i4b), parameter lenmempath
maximum length of the memory path
Definition: Constants.f90:27
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
subroutine, public compose(xorigin, yorigin, zorigin, sinrot, cosrot, xorigin_new, yorigin_new, zorigin_new, sinrot_new, cosrot_new, invert)
Apply a 3D translation and 2D rotation to an existing transformation.
Definition: GeomUtil.f90:243
This module defines variable data types.
Definition: kind.f90:8
subroutine load_particle(this, store, imdl, iprp, ip)
Load a particle from the particle store.
Definition: Particle.f90:216
subroutine, public allocate_particle_store(this, np, mempath)
Create a new particle store.
Definition: Particle.f90:120
subroutine resize(this, np, mempath)
Reallocate particle arrays.
Definition: Particle.f90:180
subroutine transform_coords(this, xorigin, yorigin, zorigin, sinrot, cosrot, invert, reset)
Apply the given global-to-local transformation to the particle.
Definition: Particle.f90:291
subroutine get_model_coords(this, x, y, z)
Return the particle's model (global) coordinates.
Definition: Particle.f90:338
integer function num_stored(this)
Definition: Particle.f90:358
subroutine save_particle(this, particle, ip)
Save a particle's state to the particle store.
Definition: Particle.f90:254
integer, parameter, public levelmax
Definition: Particle.f90:14
subroutine deallocate(this, mempath)
Deallocate particle arrays.
Definition: Particle.f90:151
subroutine, public create_particle(particle)
Create a new particle.
Definition: Particle.f90:112
Structure of arrays to store particles.
Definition: Particle.f90:74
Particle tracked by the PRT model.
Definition: Particle.f90:32