MODFLOW 6  version 6.8.0.dev0
USGS Modular Hydrologic Model
prt-prp.f90
Go to the documentation of this file.
2  use kindmodule, only: dp, i4b, lgp
6  use bndmodule, only: bndtype
7  use bndextmodule, only: bndexttype
9  use prtfmimodule, only: prtfmitype
22  use dismodule, only: distype
23  use disvmodule, only: disvtype
24  use errorutilmodule, only: pstop
25  use mathutilmodule, only: arange, is_close
27 
28  implicit none
29 
30  private
31  public :: prtprptype, exgprtprptype
32  public :: prp_create
33 
34  character(len=LENFTYPE) :: ftype = 'PRP'
35  character(len=16) :: text = ' PRP'
36  real(dp), parameter :: default_exit_solve_tolerance = dem5
37 
38  !> @brief Particle release point (PRP) package
39  type, extends(bndexttype) :: prtprptype
40  ! options
41  logical(LGP), pointer :: extend => null() !< extend tracking beyond simulation's end
42  logical(LGP), pointer :: frctrn => null() !< force ternary solution for quad grids
43  logical(LGP), pointer :: drape => null() !< whether to drape particle to topmost active cell
44  logical(LGP), pointer :: localz => null() !< compute z coordinates local to the release cell
45  integer(I4B), pointer :: istopweaksink => null() !< weak sink option: 0 = no stop, 1 = stop
46  integer(I4B), pointer :: istopzone => null() !< optional stop zone number: 0 = no stop zone
47  integer(I4B), pointer :: idrymeth => null() !< dry tracking method: 0 = drop, 1 = stop, 2 = stay
48  integer(I4B), pointer :: itrkout => null() !< binary track file
49  integer(I4B), pointer :: itrkhdr => null() !< track header file
50  integer(I4B), pointer :: itrkcsv => null() !< CSV track file
51  integer(I4B), pointer :: irlstls => null() !< release time file
52  integer(I4B), pointer :: iexmeth => null() !< method for iterative solution of particle exit location and time in generalized Pollock's method
53  integer(I4B), pointer :: ichkmeth => null() !< method for checking particle release coordinates are in the specified cells, 0 = none, 1 = eager
54  integer(I4B), pointer :: icycwin => null() !< cycle detection window size
55  real(dp), pointer :: extol => null() !< tolerance for iterative solution of particle exit location and time in generalized Pollock's method
56  real(dp), pointer :: rttol => null() !< tolerance for coincident particle release times
57  real(dp), pointer :: rtfreq => null() !< frequency for regularly spaced release times
58  real(dp), pointer :: offset => null() !< release time offset
59  real(dp), pointer :: stoptime => null() !< stop time for all release points
60  real(dp), pointer :: stoptraveltime => null() !< stop travel time for all points
61  ! members
62  type(prtfmitype), pointer :: fmi => null() !< flow model interface
63  type(particlestoretype), pointer :: particles => null() !< particle store
64  type(particlereleasescheduletype), pointer :: schedule => null() !< particle release schedule
65  integer(I4B), pointer :: nreleasepoints => null() !< number of release points
66  integer(I4B), pointer :: nreleasetimes => null() !< number of user-specified particle release times
67  integer(I4B), pointer :: nparticles => null() !< number of particles released
68  integer(I4B), pointer, contiguous :: rptnode(:) => null() !< release point reduced nns
69  integer(I4B), pointer, contiguous :: rptzone(:) => null() !< release point zone numbers
70  real(dp), pointer, contiguous :: rptx(:) => null() !< release point x coordinates
71  real(dp), pointer, contiguous :: rpty(:) => null() !< release point y coordinates
72  real(dp), pointer, contiguous :: rptz(:) => null() !< release point z coordinates
73  real(dp), pointer, contiguous :: rptm(:) => null() !< total mass released from point
74  character(len=LENBOUNDNAME), pointer, contiguous :: rptname(:) => null() !< release point names
75  character(len=LINELENGTH), allocatable :: period_block_lines(:) !< last period block configuration for fill-forward
76  integer(I4B) :: applied_kper !< period for which configuration was last applied
77  contains
78  procedure :: prp_allocate_arrays
79  procedure :: prp_allocate_scalars
80  procedure :: bnd_ar => prp_ar
81  procedure :: bnd_ad => prp_ad
82  procedure :: bnd_rp => prp_rp
83  procedure :: bnd_cq_simrate => prp_cq_simrate
84  procedure :: bnd_da => prp_da
85  procedure :: define_listlabel
86  procedure :: prp_set_pointers
87  procedure :: source_options => prp_options
88  procedure :: source_dimensions => prp_dimensions
89  procedure :: prp_log_options
90  procedure :: prp_packagedata
91  procedure :: prp_releasetimes
93  procedure :: release
94  procedure :: log_release
96  procedure :: initialize_particle
97  procedure, public :: bnd_obs_supported => prp_obs_supported
98  procedure, public :: bnd_df_obs => prp_df_obs
99  end type prtprptype
100 
101  !> @brief Exchange PRP package. A variant of the normal PRP package
102  !! that doesn't read from input files but instead receives particle
103  !! transfers from coupled models while preserving the pattern where
104  !! PRP packages own particles. Call it "Particle Registry Package"?
105  type, extends(prtprptype) :: exgprtprptype
106  contains
109  procedure :: source_dimensions => exg_prp_dimensions
110  procedure :: source_options => exg_prp_options
111  procedure :: bnd_ar => exg_prp_ar
112  procedure :: bnd_rp => exg_prp_rp
113  procedure :: bnd_ad => exg_prp_ad
114  procedure :: bnd_cq_simrate => exg_prp_cq_simrate
115  procedure :: bnd_bd => exg_prp_bd
116  procedure :: bnd_ot_model_flows => exg_prp_ot_model_flows
117  end type exgprtprptype
118 contains
119 
120  !> @brief Create a new particle release point package.
121  !!
122  !! Creates either a standard PRP (reads from input file) or an exchange
123  !! PRP (programmatically populated). The type is determined by whether
124  !! input_mempath is provided: if present, standard; if absent, exchange.
125  subroutine prp_create(packobj, id, ibcnum, inunit, iout, namemodel, &
126  pakname, fmi, input_mempath)
127  ! dummy
128  class(bndtype), pointer :: packobj
129  integer(I4B), intent(in) :: id
130  integer(I4B), intent(in) :: ibcnum
131  integer(I4B), intent(in) :: inunit
132  integer(I4B), intent(in) :: iout
133  character(len=*), intent(in) :: namemodel
134  character(len=*), intent(in) :: pakname
135  character(len=*), intent(in), optional :: input_mempath
136  type(prtfmitype), pointer :: fmi
137  ! local
138  type(prtprptype), pointer :: prpobj
139  type(exgprtprptype), pointer :: exgprpobj
140  ! formats
141  character(len=*), parameter :: fmtheader = &
142  "(1x, /1x, 'PRP PARTICLE RELEASE POINT PACKAGE', &
143  &' INPUT READ FROM MEMPATH: ', a, /)"
144  character(len=*), parameter :: fmtexgheader = &
145  "(1x, /1x, 'PRP-EXG EXCHANGE PARTICLE RELEASE POINT PACKAGE', &
146  &' (PROGRAMMATIC INPUT)', /)"
147 
148  if (present(input_mempath)) then
149  ! standard PRP
150  allocate (prpobj)
151  packobj => prpobj
152 
153  call packobj%set_names(ibcnum, namemodel, pakname, ftype, input_mempath)
154  prpobj%text = text
155 
156  call prpobj%prp_allocate_scalars()
157  call packobj%pack_initialize()
158 
159  packobj%inunit = inunit
160  packobj%iout = iout
161  packobj%id = id
162  packobj%ibcnum = ibcnum
163  packobj%ncolbnd = 4
164  packobj%iscloc = 1
165  prpobj%fmi => fmi
166 
167  if (inunit > 0) write (iout, fmtheader) input_mempath
168  else
169  ! exchange PRP
170  allocate (exgprpobj)
171  packobj => exgprpobj
172 
173  call packobj%set_names(ibcnum, namemodel, pakname, ftype)
174  exgprpobj%text = text
175 
176  call exgprpobj%prp_allocate_scalars()
177  call packobj%pack_initialize()
178 
179  packobj%inunit = inunit
180  packobj%iout = iout
181  packobj%id = id
182  packobj%ibcnum = ibcnum
183  packobj%ncolbnd = 4
184  packobj%iscloc = 1
185  exgprpobj%fmi => fmi
186 
187  if (iout > 0) write (iout, fmtexgheader)
188  end if
189  end subroutine prp_create
190 
191  !> @brief Deallocate memory
192  subroutine prp_da(this)
193  class(prtprptype) :: this
194 
195  ! Deallocate parent
196  call this%BndExtType%bnd_da()
197 
198  ! Deallocate scalars
199  call mem_deallocate(this%localz)
200  call mem_deallocate(this%extend)
201  call mem_deallocate(this%offset)
202  call mem_deallocate(this%stoptime)
203  call mem_deallocate(this%stoptraveltime)
204  call mem_deallocate(this%istopweaksink)
205  call mem_deallocate(this%istopzone)
206  call mem_deallocate(this%drape)
207  call mem_deallocate(this%idrymeth)
208  call mem_deallocate(this%nreleasepoints)
209  call mem_deallocate(this%nreleasetimes)
210  call mem_deallocate(this%nparticles)
211  call mem_deallocate(this%itrkout)
212  call mem_deallocate(this%itrkhdr)
213  call mem_deallocate(this%itrkcsv)
214  call mem_deallocate(this%irlstls)
215  call mem_deallocate(this%frctrn)
216  call mem_deallocate(this%iexmeth)
217  call mem_deallocate(this%ichkmeth)
218  call mem_deallocate(this%icycwin)
219  call mem_deallocate(this%extol)
220  call mem_deallocate(this%rttol)
221  call mem_deallocate(this%rtfreq)
222 
223  ! Deallocate arrays
224  call mem_deallocate(this%rptx)
225  call mem_deallocate(this%rpty)
226  call mem_deallocate(this%rptz)
227  call mem_deallocate(this%rptnode)
228  call mem_deallocate(this%rptm)
229  call mem_deallocate(this%rptname, 'RPTNAME', this%memoryPath)
230 
231  ! Deallocate period block storage
232  if (allocated(this%period_block_lines)) deallocate (this%period_block_lines)
233 
234  ! Deallocate objects
235  call this%particles%destroy(this%memoryPath)
236  call this%schedule%destroy()
237  deallocate (this%particles)
238  deallocate (this%schedule)
239  end subroutine prp_da
240 
241  !> @ brief Set pointers to model variables
242  subroutine prp_set_pointers(this, ibound, izone)
243  class(prtprptype) :: this
244  integer(I4B), dimension(:), pointer, contiguous :: ibound
245  integer(I4B), dimension(:), pointer, contiguous :: izone
246 
247  this%ibound => ibound
248  this%rptzone => izone
249  end subroutine prp_set_pointers
250 
251  !> @brief Allocate arrays
252  subroutine prp_allocate_arrays(this, nodelist, auxvar)
253  ! dummy
254  class(prtprptype) :: this
255  integer(I4B), dimension(:), pointer, contiguous, optional :: nodelist
256  real(DP), dimension(:, :), pointer, contiguous, optional :: auxvar
257  ! local
258  integer(I4B) :: nps
259 
260  call this%BndExtType%allocate_arrays()
261 
262  ! Allocate particle store, starting with the number
263  ! of release points (arrays resized if/when needed)
264  call create_particle_store( &
265  this%particles, &
266  this%nreleasepoints, &
267  this%memoryPath)
268 
269  ! Allocate arrays
270  call mem_allocate(this%rptx, this%nreleasepoints, 'RPTX', this%memoryPath)
271  call mem_allocate(this%rpty, this%nreleasepoints, 'RPTY', this%memoryPath)
272  call mem_allocate(this%rptz, this%nreleasepoints, 'RPTZ', this%memoryPath)
273  call mem_allocate(this%rptm, this%nreleasepoints, 'RPTMASS', &
274  this%memoryPath)
275  call mem_allocate(this%rptnode, this%nreleasepoints, 'RPTNODER', &
276  this%memoryPath)
277  call mem_allocate(this%rptname, lenboundname, this%nreleasepoints, &
278  'RPTNAME', this%memoryPath)
279 
280  ! Initialize arrays
281  do nps = 1, this%nreleasepoints
282  this%rptm(nps) = dzero
283  end do
284  end subroutine prp_allocate_arrays
285 
286  !> @brief Allocate scalars
287  subroutine prp_allocate_scalars(this)
288  class(prtprptype) :: this
289 
290  ! Allocate parent's scalars
291  call this%BndExtType%allocate_scalars()
292 
293  ! Allocate scalars for this type
294  call mem_allocate(this%localz, 'LOCALZ', this%memoryPath)
295  call mem_allocate(this%extend, 'EXTEND', this%memoryPath)
296  call mem_allocate(this%offset, 'OFFSET', this%memoryPath)
297  call mem_allocate(this%stoptime, 'STOPTIME', this%memoryPath)
298  call mem_allocate(this%stoptraveltime, 'STOPTRAVELTIME', this%memoryPath)
299  call mem_allocate(this%istopweaksink, 'ISTOPWEAKSINK', this%memoryPath)
300  call mem_allocate(this%istopzone, 'ISTOPZONE', this%memoryPath)
301  call mem_allocate(this%drape, 'DRAPE', this%memoryPath)
302  call mem_allocate(this%idrymeth, 'IDRYMETH', this%memoryPath)
303  call mem_allocate(this%nreleasepoints, 'NRELEASEPOINTS', this%memoryPath)
304  call mem_allocate(this%nreleasetimes, 'NRELEASETIMES', this%memoryPath)
305  call mem_allocate(this%nparticles, 'NPARTICLES', this%memoryPath)
306  call mem_allocate(this%itrkout, 'ITRKOUT', this%memoryPath)
307  call mem_allocate(this%itrkhdr, 'ITRKHDR', this%memoryPath)
308  call mem_allocate(this%itrkcsv, 'ITRKCSV', this%memoryPath)
309  call mem_allocate(this%irlstls, 'IRLSTLS', this%memoryPath)
310  call mem_allocate(this%frctrn, 'FRCTRN', this%memoryPath)
311  call mem_allocate(this%iexmeth, 'IEXMETH', this%memoryPath)
312  call mem_allocate(this%ichkmeth, 'ICHKMETH', this%memoryPath)
313  call mem_allocate(this%icycwin, 'ICYCWIN', this%memoryPath)
314  call mem_allocate(this%extol, 'EXTOL', this%memoryPath)
315  call mem_allocate(this%rttol, 'RTTOL', this%memoryPath)
316  call mem_allocate(this%rtfreq, 'RTFREQ', this%memoryPath)
317 
318  ! Set values
319  this%localz = .false.
320  this%extend = .false.
321  this%offset = dzero
322  this%stoptime = huge(1d0)
323  this%stoptraveltime = huge(1d0)
324  this%istopweaksink = 0
325  this%istopzone = 0
326  this%drape = .false.
327  this%idrymeth = 0
328  this%nreleasepoints = 0
329  this%nreleasetimes = 0
330  this%nparticles = 0
331  this%itrkout = 0
332  this%itrkhdr = 0
333  this%itrkcsv = 0
334  this%irlstls = 0
335  this%frctrn = .false.
336  this%iexmeth = 0
337  this%ichkmeth = 1
338  this%icycwin = 0
339  this%extol = default_exit_solve_tolerance
340  this%rttol = dsame * dep9
341  this%rtfreq = dzero
342  this%applied_kper = 0
343 
344  end subroutine prp_allocate_scalars
345 
346  !> @ brief Allocate and read period data
347  subroutine prp_ar(this)
348  class(prtprptype), intent(inout) :: this
349  integer(I4B) :: n
350 
351  call this%obs%obs_ar()
352 
353  if (this%inamedbound /= 0) then
354  do n = 1, this%nreleasepoints
355  this%boundname(n) = this%rptname(n)
356  end do
357  end if
358  do n = 1, this%nreleasepoints
359  this%nodelist(n) = this%rptnode(n)
360  end do
361  end subroutine prp_ar
362 
363  !> @brief Allocate scalars for exchange PRP.
364  !!
365  !! The exchange PRP is a headless package (no input file) but BndExtType
366  !! expects certain variables to exist in the input context (IPER, IONPER)
367  !! so we need to manually create them before calling the parent procedure.
368  subroutine exg_prp_allocate_scalars(this)
370  class(exgprtprptype) :: this
371  integer(I4B), pointer :: iper, ionper
372 
373  this%input_mempath = trim(this%memoryPath)//'-INPUT'
374 
375  call mem_allocate(iper, 'IPER', this%input_mempath)
376  call mem_allocate(ionper, 'IONPER', this%input_mempath)
377 
378  ! set iper = 0. this forces BndExtType%bnd_rp to
379  ! return early, since iper will never match kper.
380  iper = 0
381  ionper = 0
382 
383  call this%PrtPrpType%prp_allocate_scalars()
384  end subroutine exg_prp_allocate_scalars
385 
386  !> @brief Allocate arrays for exchange PRP.
387  !!
388  !! BndExtType expects certain array variables to exist in the input context
389  !! (CELLID, NODEULIST, BOUNDNAME, AUXVAR). This method manually creates
390  !! zero-sized arrays before calling the parent's allocate_arrays.
391  subroutine exg_prp_allocate_arrays(this, nodelist, auxvar)
394  class(exgprtprptype) :: this
395  integer(I4B), dimension(:), pointer, contiguous, optional :: nodelist
396  real(DP), dimension(:, :), pointer, contiguous, optional :: auxvar
397  ! local
398  integer(I4B), dimension(:, :), pointer, contiguous :: cellid
399  integer(I4B), dimension(:), pointer, contiguous :: nodeulist
400  type(characterstringtype), dimension(:), pointer, contiguous :: boundname
401  real(DP), dimension(:, :), pointer, contiguous :: auxvar_input
402 
403  call mem_allocate(cellid, this%dis%ndim, 0, 'CELLID', this%input_mempath)
404  call mem_allocate(nodeulist, 0, 'NODEULIST', this%input_mempath)
405  call mem_allocate(boundname, lenboundname, 0, 'BOUNDNAME', &
406  this%input_mempath)
407  call mem_allocate(auxvar_input, 0, 0, 'AUXVAR', this%input_mempath)
408 
409  call this%PrtPrpType%prp_allocate_arrays(nodelist, auxvar)
410  end subroutine exg_prp_allocate_arrays
411 
412  !> @ brief No-op AR method override for exchange PRP.
413  subroutine exg_prp_ar(this)
414  class(exgprtprptype), intent(inout) :: this
415  end subroutine exg_prp_ar
416 
417  !> @brief Advance a time step and release particles if scheduled.
418  subroutine prp_ad(this)
419  use tdismodule, only: totalsimtime, kstp, kper
420  class(prtprptype) :: this
421  integer(I4B) :: ip, it
422  real(DP) :: t
423 
424  ! Notes
425  ! -----
426  ! Each release point can be thought of as
427  ! a gumball machine with infinite supply:
428  ! a point can release an arbitrary number
429  ! of particles, but only one at any time.
430  ! Coincident release times are merged to
431  ! a single time by the release scheduler.
432 
433  ! Reset mass accumulators for this time step.
434  do ip = 1, this%nreleasepoints
435  this%rptm(ip) = dzero
436  end do
437 
438  ! Advance the release schedule. At the start of each period (kstp==1),
439  ! apply period block configuration if available and not yet applied.
440  ! This handles both new configuration and fill-forward periods.
441  ! For subsequent time steps, just advance without arguments to
442  ! advance the time selection object to the current time step.
443  if (kstp == 1 .and. &
444  kper /= this%applied_kper .and. &
445  allocated(this%period_block_lines)) then
446  call this%schedule%advance(lines=this%period_block_lines)
447  this%applied_kper = kper
448  else
449  call this%schedule%advance()
450  end if
451 
452  ! Check if any releases will be made this time step.
453  if (.not. this%schedule%any()) return
454 
455  ! Log the schedule to the list file.
456  call this%log_release()
457 
458  ! Expand the particle store. We know from the
459  ! schedule how many particles will be released.
460  call this%particles%resize( &
461  this%particles%num_stored() + &
462  (this%nreleasepoints * this%schedule%count()), &
463  this%memoryPath)
464 
465  ! Release a particle from each point for
466  ! each release time in the current step.
467  do ip = 1, this%nreleasepoints
468  do it = 1, this%schedule%count()
469  t = this%schedule%times(it)
470  ! Skip the release time if it's before the simulation
471  ! starts, or if no `extend_tracking`, after it ends.
472  if (t < dzero) then
473  write (warnmsg, '(a,g0,a)') &
474  'Skipping negative release time (t=', t, ').'
475  call store_warning(warnmsg)
476  cycle
477  else if (t > totalsimtime .and. .not. this%extend) then
478  write (warnmsg, '(a,g0,a)') &
479  'Skipping release time falling after the end of the &
480  &simulation (t=', t, '). Enable EXTEND_TRACKING to &
481  &release particles after the simulation end time.'
482  call store_warning(warnmsg)
483  cycle
484  end if
485  call this%release(ip, t)
486  end do
487  end do
488  end subroutine prp_ad
489 
490  !> @brief No-op AD method override for exchange PRP.
491  subroutine exg_prp_ad(this)
492  class(exgprtprptype) :: this
493  end subroutine exg_prp_ad
494 
495  !> @brief No-op flow calculation for exchange PRP.
496  !!
497  !! Exchange PRP particles are transferred from other models and already
498  !! active, so their mass is already accounted for in the STORAGE term.
499  subroutine exg_prp_cq_simrate(this, hnew, flowja, imover)
500  class(exgprtprptype) :: this
501  real(DP), dimension(:), intent(in) :: hnew
502  real(DP), dimension(:), intent(inout) :: flowja
503  integer(I4B), intent(in) :: imover
504  end subroutine exg_prp_cq_simrate
505 
506  !> @brief No-op budget method for exchange PRP.
507  !! Likewise about the STORAGE term accounting.
508  subroutine exg_prp_bd(this, model_budget)
509  use budgetmodule, only: budgettype
510  class(exgprtprptype) :: this
511  type(budgettype), intent(inout) :: model_budget
512  end subroutine exg_prp_bd
513 
514  !> @brief No-op flow output method for exchange PRP.
515  !! No contribution to budget, no need to write output.
516  subroutine exg_prp_ot_model_flows(this, icbcfl, ibudfl, icbcun, imap)
517  class(exgprtprptype) :: this
518  integer(I4B), intent(in) :: icbcfl
519  integer(I4B), intent(in) :: ibudfl
520  integer(I4B), intent(in) :: icbcun
521  integer(I4B), dimension(:), optional, intent(in) :: imap
522  end subroutine exg_prp_ot_model_flows
523 
524  !> @brief Log the release scheduled for this time step.
525  subroutine log_release(this)
526  class(prtprptype), intent(inout) :: this !< prp
527  if (this%iprpak > 0) then
528  write (this%iout, "(1x,/1x,a,1x,i0)") &
529  'PARTICLE RELEASE FOR PRP', this%ibcnum
530  call this%schedule%log(this%iout)
531  end if
532  end subroutine log_release
533 
534  !> @brief Verify that the release point is in the cell.
535  !!
536  !! Terminate with an error if the release point lies outside the
537  !! given cell, or if the point is above or below the grid top or
538  !! bottom, respectively.
539  !<
540  subroutine validate_release_point(this, ic, x, y, z)
541  class(prtprptype), intent(inout) :: this !< this instance
542  integer(I4B), intent(in) :: ic !< cell index
543  real(DP), intent(in) :: x, y, z !< release point
544  ! local
545  real(DP), allocatable :: polyverts(:, :)
546 
547  call this%fmi%dis%get_polyverts(ic, polyverts)
548  if (.not. point_in_polygon(x, y, polyverts)) then
549  write (errmsg, '(a,g0,a,g0,a,i0)') &
550  'Error: release point (x=', x, ', y=', y, ') is not in cell ', &
551  this%dis%get_nodeuser(ic)
552  call store_error(errmsg, terminate=.false.)
553  call store_error_filename(this%input_fname)
554  end if
555  if (z > maxval(this%dis%top)) then
556  write (errmsg, '(a,g0,a,g0,a,i0)') &
557  'Error: release point (z=', z, ') is above grid top ', &
558  maxval(this%dis%top)
559  call store_error(errmsg, terminate=.false.)
560  call store_error_filename(this%input_fname)
561  else if (z < minval(this%dis%bot)) then
562  write (errmsg, '(a,g0,a,g0,a,i0)') &
563  'Error: release point (z=', z, ') is below grid bottom ', &
564  minval(this%dis%bot)
565  call store_error(errmsg, terminate=.false.)
566  call store_error_filename(this%input_fname)
567  end if
568  deallocate (polyverts)
569  end subroutine validate_release_point
570 
571  !> Release a particle at the specified time.
572  !!
573  !! Releasing a particle entails validating the particle's
574  !! coordinates and settings, transforming its coordinates
575  !! if needed, initializing the particle's initial tracking
576  !! time to the given release time, storing the particle in
577  !! the particle store (from which the PRT model will later
578  !! retrieve it, apply the tracking method, and check it in
579  !! again), and accumulating the particle's mass (the total
580  !! mass released from each release point is calculated for
581  !! budget reporting).
582  !<
583  subroutine release(this, ip, trelease)
584  ! dummy
585  class(prtprptype), intent(inout) :: this !< this instance
586  integer(I4B), intent(in) :: ip !< particle index
587  real(DP), intent(in) :: trelease !< release time
588  ! local
589  integer(I4B) :: np
590  type(particletype), pointer :: particle
591 
592  call this%initialize_particle(particle, ip, trelease)
593  np = this%nparticles + 1
594  this%nparticles = np
595  call this%particles%put(particle, np)
596  deallocate (particle)
597  this%rptm(ip) = this%rptm(ip) + done ! TODO configurable mass
598 
599  end subroutine release
600 
601  subroutine initialize_particle(this, particle, ip, trelease)
603  class(prtprptype), intent(inout) :: this !< this instance
604  type(particletype), pointer, intent(inout) :: particle !< the particle
605  integer(I4B), intent(in) :: ip !< particle index
606  real(DP), intent(in) :: trelease !< release time
607  ! local
608  logical(LGP) :: draped
609  integer(I4B) :: irow, icol, ilay, icpl
610  integer(I4B) :: ic, icu, ic_old
611  real(DP) :: x, y, z
612  real(DP) :: top, bot, hds
613  ! formats
614  character(len=*), parameter :: fmticterr = &
615  "('Error in ',a,': Flow model interface does not contain ICELLTYPE. &
616  &ICELLTYPE is required for PRT to distinguish convertible cells &
617  &from confined cells if LOCAL_Z release coordinates are provided. &
618  &Make sure a GWFGRID entry is configured in the PRT FMI package.')"
619 
620  ic = this%rptnode(ip)
621 
622  call create_particle(particle)
623 
624  if (size(this%boundname) /= 0) then
625  particle%name = this%boundname(ip)
626  else
627  particle%name = ''
628  end if
629 
630  particle%irpt = ip
631  particle%istopweaksink = this%istopweaksink
632  particle%istopzone = this%istopzone
633  particle%idrymeth = this%idrymeth
634  particle%istatus = 0 ! status 0 until tracking starts
635 
636  ! If the cell is inactive, either drape the particle
637  ! to the top-most active cell beneath it if drape is
638  ! enabled, or else terminate permanently unreleased.
639  draped = .false.
640  if (this%ibound(ic) == 0) then
641  ic_old = ic
642  if (this%drape) then
643  call this%dis%highest_active(ic, this%ibound)
644  draped = ic /= ic_old
645  if (.not. draped .and. this%ibound(ic) == 0) then
646  ! negative unreleased status signals to the
647  ! tracking method that we haven't yet saved
648  ! a termination record, it needs to do so.
649  particle%istatus = -1 * term_unreleased
650  end if
651  else
652  particle%istatus = -1 * term_unreleased
653  end if
654  end if
655 
656  icu = this%dis%get_nodeuser(ic)
657  particle%icu = icu
658  select type (dis => this%dis)
659  type is (distype)
660  call get_ijk(icu, dis%nrow, dis%ncol, dis%nlay, irow, icol, ilay)
661  type is (disvtype)
662  call get_jk(icu, dis%ncpl, dis%nlay, icpl, ilay)
663  end select
664  particle%ilay = ilay
665  particle%izone = this%rptzone(ic)
666 
667  ! if the particle was draped, override the release z coord and
668  ! set it to the saturated top of the cell. this puts a draped
669  ! a draped particle at the water table for a convertible cell
670  ! or at the geometric cell top for a confined cell. if it was
671  ! not draped and localz is enabled, calculate a model z coord
672  ! using the geometric cell top if the cell is confined or the
673  ! water table as the effective top if the cell is convertible.
674  if (draped) then
675  z = this%fmi%dis%bot(ic) + &
676  this%fmi%gwfsat(ic) * &
677  (this%fmi%dis%top(ic) - this%fmi%dis%bot(ic))
678  else if (this%localz) then
679  ! TODO: is this sufficient instead of the below??
680  ! z = this%fmi%dis%bot(ic) + &
681  ! this%rptz(ip) * &
682  ! this%fmi%gwfsat(ic) * &
683  ! (this%fmi%dis%top(ic) - this%fmi%dis%bot(ic))
684 
685  top = this%fmi%dis%top(ic)
686  bot = this%fmi%dis%bot(ic)
687  if (this%fmi%gwfceltyp(icu) /= 0) then
688  hds = this%fmi%gwfhead(ic)
689  top = min(top, hds)
690  top = max(top, bot)
691  end if
692  z = bot + this%rptz(ip) * (top - bot)
693  else
694  z = this%rptz(ip)
695  end if
696 
697  x = this%rptx(ip)
698  y = this%rpty(ip)
699 
700  if (this%ichkmeth > 0) &
701  call this%validate_release_point(ic, x, y, z)
702 
703  particle%x = x
704  particle%y = y
705  particle%z = z
706  particle%trelease = trelease
707 
708  ! Set stop time to earlier of STOPTIME and STOPTRAVELTIME
709  if (this%stoptraveltime == huge(1d0)) then
710  particle%tstop = this%stoptime
711  else
712  particle%tstop = particle%trelease + this%stoptraveltime
713  if (this%stoptime < particle%tstop) particle%tstop = this%stoptime
714  end if
715 
716  particle%ttrack = particle%trelease
717  particle%itrdomain(level_model) = 0
718  particle%iboundary(level_model) = 0
719  particle%itrdomain(level_feature) = ic
720  particle%iboundary(level_feature) = 0
721  particle%itrdomain(level_subfeature) = 0
722  particle%iboundary(level_subfeature) = 0
723  particle%frctrn = this%frctrn
724  particle%iexmeth = this%iexmeth
725  particle%extend = this%extend
726  particle%icycwin = this%icycwin
727  particle%extol = this%extol
728  end subroutine initialize_particle
729 
730  !> @ brief Read and prepare period data for particle input
731  subroutine prp_rp(this)
732  ! modules
733  use tdismodule, only: kper, nper
736  ! dummy variables
737  class(prtprptype), intent(inout) :: this
738  ! local variables
739  type(characterstringtype), dimension(:), contiguous, &
740  pointer :: settings
741  integer(I4B), pointer :: iper, ionper, nlist
742  integer(I4B) :: n
743 
744  ! set pointer to last and next period loaded
745  call mem_setptr(iper, 'IPER', this%input_mempath)
746  call mem_setptr(ionper, 'IONPER', this%input_mempath)
747 
748  if (kper == 1 .and. &
749  (iper == 0) .and. &
750  (ionper > nper) .and. &
751  size(this%schedule%time_select%times) == 0) then
752  ! If the user hasn't provided any release settings (neither
753  ! explicit release times, release time frequency, or period
754  ! block release settings), default to a single release at the
755  ! start of the first period's first time step.
756  ! Store default configuration; advance() will be called in prp_ad().
757  if (allocated(this%period_block_lines)) deallocate (this%period_block_lines)
758  allocate (this%period_block_lines(1))
759  this%period_block_lines(1) = "FIRST"
760  return
761  else if (iper /= kper) then
762  return
763  end if
764 
765  ! set input context pointers
766  call mem_setptr(nlist, 'NBOUND', this%input_mempath)
767  call mem_setptr(settings, 'SETTING', this%input_mempath)
768 
769  ! Store period block configuration for fill-forward.
770  if (allocated(this%period_block_lines)) deallocate (this%period_block_lines)
771  allocate (this%period_block_lines(nlist))
772  do n = 1, nlist
773  this%period_block_lines(n) = settings(n)
774  end do
775  end subroutine prp_rp
776 
777  !> @ brief No-op RP method override for exchange PRP.
778  subroutine exg_prp_rp(this)
779  class(exgprtprptype), intent(inout) :: this
780  end subroutine exg_prp_rp
781 
782  !> @ brief Calculate flow between package and model.
783  subroutine prp_cq_simrate(this, hnew, flowja, imover)
784  ! modules
785  use tdismodule, only: delt
786  ! dummy variables
787  class(prtprptype) :: this
788  real(DP), dimension(:), intent(in) :: hnew
789  real(DP), dimension(:), intent(inout) :: flowja !< flow between package and model
790  integer(I4B), intent(in) :: imover !< flag indicating if the mover package is active
791  ! local variables
792  integer(I4B) :: i
793  integer(I4B) :: node
794  integer(I4B) :: idiag
795  real(DP) :: rrate
796 
797  ! If no boundaries, skip flow calculations.
798  if (this%nbound <= 0) return
799 
800  ! Loop through each boundary calculating flow.
801  do i = 1, this%nbound
802  node = this%nodelist(i)
803  rrate = dzero
804  ! If cell is no-flow or constant-head, then ignore it.
805  if (node > 0) then
806  ! Calculate the flow rate into the cell.
807  idiag = this%dis%con%ia(node)
808  rrate = this%rptm(i) * (done / delt) ! reciprocal of tstp length
809  flowja(idiag) = flowja(idiag) + rrate
810  end if
811 
812  ! Save simulated value to simvals array.
813  this%simvals(i) = rrate
814  end do
815  end subroutine prp_cq_simrate
816 
817  subroutine define_listlabel(this)
818  class(prtprptype), intent(inout) :: this
819  ! not implemented, not used
820  end subroutine define_listlabel
821 
822  !> @brief Indicates whether observations are supported.
823  logical function prp_obs_supported(this)
824  class(prtprptype) :: this
825  prp_obs_supported = .true.
826  end function prp_obs_supported
827 
828  !> @brief Store supported observations
829  subroutine prp_df_obs(this)
830  ! dummy
831  class(prtprptype) :: this
832  ! local
833  integer(I4B) :: indx
834  call this%obs%StoreObsType('prp', .true., indx)
835  this%obs%obsData(indx)%ProcessIdPtr => defaultobsidprocessor
836 
837  ! Store obs type and assign procedure pointer
838  ! for to-mvr observation type.
839  call this%obs%StoreObsType('to-mvr', .true., indx)
840  this%obs%obsData(indx)%ProcessIdPtr => defaultobsidprocessor
841  end subroutine prp_df_obs
842 
843  !> @ brief Set options specific to PrtPrpType
844  subroutine prp_options(this)
845  ! -- modules
848  use openspecmodule, only: access, form
851  ! -- dummy variables
852  class(prtprptype), intent(inout) :: this
853  ! -- local variables
854  character(len=LENVARNAME), dimension(3) :: drytrack_method = &
855  &[character(len=LENVARNAME) :: 'DROP', 'STOP', 'STAY']
856  character(len=lenvarname), dimension(2) :: coorcheck_method = &
857  &[character(len=LENVARNAME) :: 'NONE', 'EAGER']
858  character(len=LINELENGTH) :: trackfile, trackcsvfile, fname
859  type(prtprpparamfoundtype) :: found
860  character(len=*), parameter :: fmtextolwrn = &
861  "('WARNING: EXIT_SOLVE_TOLERANCE is set to ',g10.3,' &
862  &which is much greater than the default value of ',g10.3,'. &
863  &The tolerance that strikes the best balance between accuracy &
864  &and runtime is problem-dependent. Since the variable being &
865  &solved varies from 0 to 1, tolerance values much less than 1 &
866  &typically give the best results.')"
867 
868  ! source base class options
869  call this%BndExtType%source_options()
870 
871  ! update defaults from input context
872  call mem_set_value(this%stoptime, 'STOPTIME', this%input_mempath, &
873  found%stoptime)
874  call mem_set_value(this%stoptraveltime, 'STOPTRAVELTIME', &
875  this%input_mempath, found%stoptraveltime)
876  call mem_set_value(this%istopweaksink, 'ISTOPWEAKSINK', this%input_mempath, &
877  found%istopweaksink)
878  call mem_set_value(this%istopzone, 'ISTOPZONE', this%input_mempath, &
879  found%istopzone)
880  call mem_set_value(this%drape, 'DRAPE', this%input_mempath, &
881  found%drape)
882  call mem_set_value(this%idrymeth, 'IDRYMETH', this%input_mempath, &
883  drytrack_method, found%idrymeth)
884  call mem_set_value(trackfile, 'TRACKFILE', this%input_mempath, &
885  found%trackfile)
886  call mem_set_value(trackcsvfile, 'TRACKCSVFILE', this%input_mempath, &
887  found%trackcsvfile)
888  call mem_set_value(this%localz, 'LOCALZ', this%input_mempath, &
889  found%localz)
890  call mem_set_value(this%extend, 'EXTEND', this%input_mempath, &
891  found%extend)
892  call mem_set_value(this%extol, 'EXTOL', this%input_mempath, &
893  found%extol)
894  call mem_set_value(this%rttol, 'RTTOL', this%input_mempath, &
895  found%rttol)
896  call mem_set_value(this%rtfreq, 'RTFREQ', this%input_mempath, &
897  found%rtfreq)
898  call mem_set_value(this%frctrn, 'FRCTRN', this%input_mempath, &
899  found%frctrn)
900  call mem_set_value(this%iexmeth, 'IEXMETH', this%input_mempath, &
901  found%iexmeth)
902  call mem_set_value(this%ichkmeth, 'ICHKMETH', this%input_mempath, &
903  coorcheck_method, found%ichkmeth)
904  call mem_set_value(this%icycwin, 'ICYCWIN', this%input_mempath, found%icycwin)
905 
906  ! update internal state and validate input
907  if (found%idrymeth) then
908  if (this%idrymeth == 0) then
909  write (errmsg, '(a)') 'Unsupported dry tracking method. &
910  &DRY_TRACKING_METHOD must be "DROP", "STOP", or "STAY"'
911  call store_error(errmsg)
912  else
913  ! adjust for method zero indexing
914  this%idrymeth = this%idrymeth - 1
915  end if
916  end if
917 
918  if (found%extol) then
919  if (this%extol <= dzero) &
920  call store_error('EXIT_SOLVE_TOLERANCE MUST BE POSITIVE')
921  if (this%extol > dem2) then
922  write (warnmsg, fmt=fmtextolwrn) &
923  this%extol, default_exit_solve_tolerance
924  call store_warning(warnmsg)
925  end if
926  end if
927 
928  if (found%rttol) then
929  if (this%rttol <= dzero) &
930  call store_error('RELEASE_TIME_TOLERANCE MUST BE POSITIVE')
931  end if
932 
933  if (found%rtfreq) then
934  if (this%rtfreq <= dzero) &
935  call store_error('RELEASE_TIME_FREQUENCY MUST BE POSITIVE')
936  end if
937 
938  if (found%iexmeth) then
939  if (.not. (this%iexmeth /= 1 .or. this%iexmeth /= 2)) &
940  call store_error('DEV_EXIT_SOLVE_METHOD MUST BE &
941  &1 (BRENT) OR 2 (CHANDRUPATLA)')
942  end if
943 
944  if (found%ichkmeth) then
945  if (this%ichkmeth == 0) then
946  write (errmsg, '(a)') 'Unsupported coordinate check method. &
947  &COORDINATE_CHECK_METHOD must be "NONE" or "EAGER"'
948  call store_error(errmsg)
949  else
950  ! adjust for method zero based indexing
951  this%ichkmeth = this%ichkmeth - 1
952  end if
953  end if
954 
955  if (found%icycwin) then
956  if (this%icycwin < 0) &
957  call store_error('CYCLE_DETECTION_WINDOW MUST BE NON-NEGATIVE')
958  end if
959 
960  ! fileout options
961  if (found%trackfile) then
962  this%itrkout = getunit()
963  call openfile(this%itrkout, this%iout, trackfile, 'DATA(BINARY)', &
964  form, access, filstat_opt='REPLACE', &
965  mode_opt=mnormal)
966  ! open and write ascii header spec file
967  this%itrkhdr = getunit()
968  fname = trim(trackfile)//'.hdr'
969  call openfile(this%itrkhdr, this%iout, fname, 'CSV', &
970  filstat_opt='REPLACE', mode_opt=mnormal)
971  write (this%itrkhdr, '(a,/,a)') trackheader, trackdtypes
972  end if
973 
974  if (found%trackcsvfile) then
975  this%itrkcsv = getunit()
976  call openfile(this%itrkcsv, this%iout, trackcsvfile, 'CSV', &
977  filstat_opt='REPLACE')
978  write (this%itrkcsv, '(a)') trackheader
979  end if
980 
981  ! terminate if any errors were detected
982  if (count_errors() > 0) then
983  call store_error_filename(this%input_fname)
984  end if
985 
986  ! log found options
987  call this%prp_log_options(found, trackfile, trackcsvfile)
988 
989  ! Create release schedule now that we know
990  ! the coincident release time tolerance
991  this%schedule => create_release_schedule(tolerance=this%rttol)
992  end subroutine prp_options
993 
994  !> @ brief No-op options method override for exchange PRP.
995  !! Just creates an empty release schedule.
996  subroutine exg_prp_options(this)
997  class(exgprtprptype), intent(inout) :: this
998  this%schedule => create_release_schedule(tolerance=this%rttol)
999  end subroutine exg_prp_options
1000 
1001  !> @ brief Log options specific to PrtPrpType
1002  subroutine prp_log_options(this, found, trackfile, trackcsvfile)
1003  ! -- modules
1005  ! -- dummy variables
1006  class(prtprptype), intent(inout) :: this
1007  type(prtprpparamfoundtype), intent(in) :: found
1008  character(len=*), intent(in) :: trackfile
1009  character(len=*), intent(in) :: trackcsvfile
1010  ! -- local variables
1011  ! formats
1012  character(len=*), parameter :: fmttrkbin = &
1013  "(4x, 'PARTICLE TRACKS WILL BE SAVED TO BINARY FILE: ', a, /4x, &
1014  &'OPENED ON UNIT: ', I0)"
1015  character(len=*), parameter :: fmttrkcsv = &
1016  "(4x, 'PARTICLE TRACKS WILL BE SAVED TO CSV FILE: ', a, /4x, &
1017  &'OPENED ON UNIT: ', I0)"
1018 
1019  write (this%iout, '(1x,a)') 'PROCESSING PARTICLE INPUT DIMENSIONS'
1020 
1021  if (found%frctrn) then
1022  write (this%iout, '(4x,a)') &
1023  'IF DISV, TRACKING WILL USE THE TERNARY METHOD REGARDLESS OF CELL TYPE'
1024  end if
1025 
1026  if (found%trackfile) then
1027  write (this%iout, fmttrkbin) trim(adjustl(trackfile)), this%itrkout
1028  end if
1029 
1030  if (found%trackcsvfile) then
1031  write (this%iout, fmttrkcsv) trim(adjustl(trackcsvfile)), this%itrkcsv
1032  end if
1033 
1034  write (this%iout, '(1x,a)') 'END OF PARTICLE INPUT DIMENSIONS'
1035  end subroutine prp_log_options
1036 
1037  !> @ brief Set dimensions specific to PrtPrpType
1038  subroutine prp_dimensions(this)
1039  ! modules
1042  ! dummy variables
1043  class(prtprptype), intent(inout) :: this
1044  ! local variables
1045  type(prtprpparamfoundtype) :: found
1046 
1047  call mem_set_value(this%nreleasepoints, 'NRELEASEPTS', this%input_mempath, &
1048  found%nreleasepts)
1049  call mem_set_value(this%nreleasetimes, 'NRELEASETIMES', this%input_mempath, &
1050  found%nreleasetimes)
1051 
1052  write (this%iout, '(1x,a)') 'PROCESSING PARTICLE INPUT DIMENSIONS'
1053  write (this%iout, '(4x,a,i0)') 'NRELEASEPTS = ', this%nreleasepoints
1054  write (this%iout, '(4x,a,i0)') 'NRELEASETIMES = ', this%nreleasetimes
1055  write (this%iout, '(1x,a)') 'END OF PARTICLE INPUT DIMENSIONS'
1056 
1057  ! set maxbound and nbound to nreleasepts
1058  this%maxbound = this%nreleasepoints
1059  this%nbound = this%nreleasepoints
1060 
1061  call this%prp_allocate_arrays()
1062  call this%prp_packagedata()
1063  call this%prp_releasetimes()
1064  call this%prp_load_releasetimefrequency()
1065  end subroutine prp_dimensions
1066 
1067  !> @ brief Dimensions method override for exchange PRP.
1068  !! Just set all dimensions to zero and allocate arrays.
1069  subroutine exg_prp_dimensions(this)
1070  class(exgprtprptype), intent(inout) :: this
1071 
1072  this%nreleasepoints = 0
1073  this%nreleasetimes = 0
1074  this%maxbound = 0
1075  this%nbound = 0
1076 
1077  call this%prp_allocate_arrays()
1078  end subroutine exg_prp_dimensions
1079 
1080  !> @brief Load package data (release points).
1081  subroutine prp_packagedata(this)
1082  use memorymanagermodule, only: mem_setptr
1084  use geomutilmodule, only: get_node
1086  ! dummy
1087  class(prtprptype), intent(inout) :: this
1088  ! local
1089  integer(I4B), dimension(:), pointer, contiguous :: irptno
1090  integer(I4B), dimension(:, :), pointer, contiguous :: cellids
1091  real(DP), dimension(:), pointer, contiguous :: xrpts, yrpts, zrpts
1092  type(characterstringtype), dimension(:), pointer, &
1093  contiguous :: boundnames
1094  character(len=LENBOUNDNAME) :: bndName, bndNameTemp
1095  character(len=9) :: cno
1096  character(len=20) :: cellidstr
1097  integer(I4B), dimension(:), allocatable :: nboundchk
1098  integer(I4B), dimension(:), pointer :: cellid
1099  integer(I4B) :: n, noder, nodeu, rptno
1100 
1101  ! set input context pointers
1102  call mem_setptr(irptno, 'IRPTNO', this%input_mempath)
1103  call mem_setptr(cellids, 'CELLID', this%input_mempath)
1104  call mem_setptr(xrpts, 'XRPT', this%input_mempath)
1105  call mem_setptr(yrpts, 'YRPT', this%input_mempath)
1106  call mem_setptr(zrpts, 'ZRPT', this%input_mempath)
1107  call mem_setptr(boundnames, 'BOUNDNAME', this%input_mempath)
1108 
1109  ! allocate and initialize temporary variables
1110  allocate (nboundchk(this%nreleasepoints))
1111  do n = 1, this%nreleasepoints
1112  nboundchk(n) = 0
1113  end do
1114 
1115  write (this%iout, '(/1x,a)') 'PROCESSING '//trim(adjustl(this%packName)) &
1116  //' PACKAGEDATA'
1117 
1118  do n = 1, size(irptno)
1119 
1120  rptno = irptno(n)
1121 
1122  if (rptno < 1 .or. rptno > this%nreleasepoints) then
1123  write (errmsg, '(a,i0,a,i0,a)') &
1124  'Expected ', this%nreleasepoints, ' release points. &
1125  &Points must be numbered from 1 to ', this%nreleasepoints, '.'
1126  call store_error(errmsg)
1127  cycle
1128  end if
1129 
1130  ! increment nboundchk
1131  nboundchk(rptno) = nboundchk(rptno) + 1
1132 
1133  ! set cellid
1134  cellid => cellids(:, n)
1135 
1136  ! set node user
1137  if (this%dis%ndim == 1) then
1138  nodeu = cellid(1)
1139  elseif (this%dis%ndim == 2) then
1140  nodeu = get_node(cellid(1), 1, cellid(2), &
1141  this%dis%mshape(1), 1, &
1142  this%dis%mshape(2))
1143  else
1144  nodeu = get_node(cellid(1), cellid(2), cellid(3), &
1145  this%dis%mshape(1), &
1146  this%dis%mshape(2), &
1147  this%dis%mshape(3))
1148  end if
1149 
1150  ! set noder
1151  noder = this%dis%get_nodenumber(nodeu, 1)
1152  if (noder <= 0) then
1153  call this%dis%nodeu_to_string(nodeu, cellidstr)
1154  write (errmsg, '(a)') &
1155  'Particle release point configured for nonexistent cell: '// &
1156  trim(adjustl(cellidstr))//'. This cell has IDOMAIN <= 0 and '&
1157  &'therefore does not exist in the model grid.'
1158  call store_error(errmsg)
1159  cycle
1160  else
1161  this%rptnode(rptno) = noder
1162  end if
1163 
1164  if (this%localz .and. (zrpts(n) < 0 .or. zrpts(n) > 1)) then
1165  call store_error('Local z coordinate must fall in the interval [0, 1]')
1166  cycle
1167  end if
1168 
1169  ! set coordinates
1170  this%rptx(rptno) = xrpts(n)
1171  this%rpty(rptno) = yrpts(n)
1172  this%rptz(rptno) = zrpts(n)
1173 
1174  ! set default boundname
1175  write (cno, '(i9.9)') rptno
1176  bndname = 'PRP'//cno
1177 
1178  ! read boundnames from file, if provided
1179  if (this%inamedbound /= 0) then
1180  bndnametemp = boundnames(n)
1181  if (bndnametemp /= '') bndname = bndnametemp
1182  else
1183  bndname = ''
1184  end if
1185 
1186  ! set boundname
1187  this%rptname(rptno) = bndname
1188  end do
1189 
1190  write (this%iout, '(1x,a)') &
1191  'END OF '//trim(adjustl(this%packName))//' PACKAGEDATA'
1192 
1193  ! check for duplicate or missing particle release points
1194  do n = 1, this%nreleasepoints
1195  if (nboundchk(n) == 0) then
1196  write (errmsg, '(a,a,1x,i0,a)') 'No data specified for particle ', &
1197  'release point', n, '.'
1198  call store_error(errmsg)
1199  else if (nboundchk(n) > 1) then
1200  write (errmsg, '(a,1x,i0,1x,a,1x,i0,1x,a)') &
1201  'Data for particle release point', n, 'specified', nboundchk(n), &
1202  'times.'
1203  call store_error(errmsg)
1204  end if
1205  end do
1206 
1207  ! terminate if any errors were detected
1208  if (count_errors() > 0) then
1209  call store_error_filename(this%input_fname)
1210  end if
1211 
1212  ! cleanup
1213  deallocate (nboundchk)
1214 
1215  call memorystore_release('IRPTNO', this%input_mempath)
1216  call memorystore_release('CELLID', this%input_mempath)
1217  call memorystore_release('XRPT', this%input_mempath)
1218  call memorystore_release('YRPT', this%input_mempath)
1219  call memorystore_release('ZRPT', this%input_mempath)
1220  call memorystore_release('BOUNDNAME', this%input_mempath)
1221  end subroutine prp_packagedata
1222 
1223  !> @brief Load explicitly specified release times.
1224  subroutine prp_releasetimes(this)
1226  ! dummy
1227  class(prtprptype), intent(inout) :: this
1228  ! local
1229  real(DP), dimension(:), pointer, contiguous :: time
1230  integer(I4B) :: n, isize
1231  real(DP), allocatable :: times(:)
1232 
1233  if (this%nreleasetimes <= 0) return
1234 
1235  ! allocate times array
1236  allocate (times(this%nreleasetimes))
1237 
1238  ! check if input array was read
1239  call get_isize('TIME', this%input_mempath, isize)
1240 
1241  if (isize <= 0) then
1242  errmsg = "RELEASTIMES block expected when &
1243  &NRELEASETIMES dimension is non-zero."
1244  call store_error(errmsg)
1245  call store_error_filename(this%input_fname)
1246  end if
1247 
1248  ! set input context pointer
1249  call mem_setptr(time, 'TIME', this%input_mempath)
1250 
1251  ! set input data
1252  do n = 1, size(time)
1253  times(n) = time(n)
1254  end do
1255 
1256  ! register times with the release schedule
1257  call this%schedule%time_select%extend(times)
1258 
1259  ! make sure times strictly increase
1260  if (.not. this%schedule%time_select%increasing()) then
1261  errmsg = "RELEASTIMES block entries must strictly increase."
1262  call store_error(errmsg)
1263  call store_error_filename(this%input_fname)
1264  end if
1265 
1266  ! deallocate
1267  deallocate (times)
1268  end subroutine prp_releasetimes
1269 
1270  !> @brief Load regularly spaced release times if configured.
1272  ! modules
1273  use tdismodule, only: totalsimtime
1274  ! dummy
1275  class(prtprptype), intent(inout) :: this
1276  ! local
1277  real(DP), allocatable :: times(:)
1278 
1279  ! check if a release time frequency is configured
1280  if (this%rtfreq <= dzero) return
1281 
1282  ! create array of regularly-spaced release times
1283  times = arange( &
1284  start=dzero, &
1285  stop=totalsimtime, &
1286  step=this%rtfreq)
1287 
1288  ! register times with release schedule
1289  call this%schedule%time_select%extend(times)
1290 
1291  ! make sure times strictly increase
1292  if (.not. this%schedule%time_select%increasing()) then
1293  errmsg = "Release times must strictly increase"
1294  call store_error(errmsg)
1295  call store_error_filename(this%input_fname)
1296  end if
1297 
1298  ! deallocate
1299  deallocate (times)
1300 
1301  end subroutine prp_load_releasetimefrequency
1302 
1303 end module prtprpmodule
This module contains the extended boundary package.
This module contains the base boundary package.
This module contains the BudgetModule.
Definition: Budget.f90:20
This module contains simulation constants.
Definition: Constants.f90:9
real(dp), parameter dsame
real constant for values that are considered the same based on machine precision
Definition: Constants.f90:122
integer(i4b), parameter linelength
maximum length of a standard line
Definition: Constants.f90:45
@ tabcenter
centered table column
Definition: Constants.f90:172
@ tableft
left justified table column
Definition: Constants.f90:171
@ mnormal
normal output mode
Definition: Constants.f90:206
real(dp), parameter dep3
real constant 1000
Definition: Constants.f90:88
integer(i4b), parameter lenpakloc
maximum length of a package location
Definition: Constants.f90:50
real(dp), parameter dem1
real constant 1e-1
Definition: Constants.f90:103
real(dp), parameter dep9
real constant 1e9
Definition: Constants.f90:90
integer(i4b), parameter lenvarname
maximum length of a variable name
Definition: Constants.f90:17
integer(i4b), parameter lenftype
maximum length of a package type (DIS, WEL, OC, etc.)
Definition: Constants.f90:39
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
real(dp), parameter dem5
real constant 1e-5
Definition: Constants.f90:108
real(dp), parameter dem2
real constant 1e-2
Definition: Constants.f90:105
real(dp), parameter done
real constant 1
Definition: Constants.f90:76
Definition: Dis.f90:1
subroutine pstop(status, message)
Stop the program, optionally specifying an error status code.
Definition: ErrorUtil.f90:24
integer(i4b) function, public get_node(ilay, irow, icol, nlay, nrow, ncol)
Get node number, given layer, row, and column indices for a structured grid. If any argument is inval...
Definition: GeomUtil.f90:83
logical function, public point_in_polygon(x, y, poly)
Check if a point is within a polygon.
Definition: GeomUtil.f90:27
subroutine, public get_ijk(nodenumber, nrow, ncol, nlay, irow, icol, ilay)
Get row, column and layer indices from node number and grid dimensions. If nodenumber is invalid,...
Definition: GeomUtil.f90:100
subroutine, public get_jk(nodenumber, ncpl, nlay, icpl, ilay)
Get layer index and within-layer node index from node number and grid dimensions. If nodenumber is in...
Definition: GeomUtil.f90:128
integer(i4b) function, public getunit()
Get a free unit number.
subroutine, public openfile(iu, iout, fname, ftype, fmtarg_opt, accarg_opt, filstat_opt, mode_opt)
Open a file.
Definition: InputOutput.f90:30
This module defines variable data types.
Definition: kind.f90:8
pure real(dp) function, dimension(:), allocatable, public arange(start, stop, step)
Return reals separated by the given step over the given interval.
Definition: MathUtil.f90:384
pure logical function, public is_close(a, b, rtol, atol, symmetric)
Check if a real value is approximately equal to another.
Definition: MathUtil.f90:46
subroutine, public memorystore_release(varname, memory_path)
Release a single variable from the memory store.
subroutine, public get_isize(name, mem_path, isize)
@ brief Get the number of elements for this variable
Particle tracking strategies.
Definition: Method.f90:2
@, public level_feature
Definition: Method.f90:41
@, public level_subfeature
Definition: Method.f90:42
@, public level_model
Definition: Method.f90:40
This module contains the derived type ObsType.
Definition: Obs.f90:127
subroutine, public defaultobsidprocessor(obsrv, dis, inunitobs, iout)
@ brief Process IDstring provided for each observation
Definition: Obs.f90:246
character(len=20) access
Definition: OpenSpec.f90:7
character(len=20) form
Definition: OpenSpec.f90:7
subroutine create_particle_store(store, np, mempath)
Allocate particle store.
Definition: Particle.f90:157
@ term_unreleased
terminated permanently unreleased
Definition: Particle.f90:36
subroutine create_particle(particle)
Create a new particle.
Definition: Particle.f90:150
Particle release scheduling.
type(particlereleasescheduletype) function, pointer, public create_release_schedule(tolerance)
Create a new release schedule.
Particle track output module.
character(len= *), parameter, public trackheader
character(len= *), parameter, public trackdtypes
subroutine exg_prp_allocate_arrays(this, nodelist, auxvar)
Allocate arrays for exchange PRP.
Definition: prt-prp.f90:392
subroutine exg_prp_ar(this)
@ brief No-op AR method override for exchange PRP.
Definition: prt-prp.f90:414
subroutine prp_allocate_arrays(this, nodelist, auxvar)
Allocate arrays.
Definition: prt-prp.f90:253
subroutine exg_prp_cq_simrate(this, hnew, flowja, imover)
No-op flow calculation for exchange PRP.
Definition: prt-prp.f90:500
subroutine prp_rp(this)
@ brief Read and prepare period data for particle input
Definition: prt-prp.f90:732
subroutine exg_prp_rp(this)
@ brief No-op RP method override for exchange PRP.
Definition: prt-prp.f90:779
subroutine prp_load_releasetimefrequency(this)
Load regularly spaced release times if configured.
Definition: prt-prp.f90:1272
subroutine prp_cq_simrate(this, hnew, flowja, imover)
@ brief Calculate flow between package and model.
Definition: prt-prp.f90:784
subroutine exg_prp_allocate_scalars(this)
Allocate scalars for exchange PRP.
Definition: prt-prp.f90:369
subroutine exg_prp_ad(this)
No-op AD method override for exchange PRP.
Definition: prt-prp.f90:492
character(len=lenftype) ftype
Definition: prt-prp.f90:34
subroutine exg_prp_bd(this, model_budget)
No-op budget method for exchange PRP. Likewise about the STORAGE term accounting.
Definition: prt-prp.f90:509
subroutine prp_df_obs(this)
Store supported observations.
Definition: prt-prp.f90:830
real(dp), parameter default_exit_solve_tolerance
Definition: prt-prp.f90:36
subroutine define_listlabel(this)
Definition: prt-prp.f90:818
subroutine log_release(this)
Log the release scheduled for this time step.
Definition: prt-prp.f90:526
subroutine exg_prp_options(this)
@ brief No-op options method override for exchange PRP. Just creates an empty release schedule.
Definition: prt-prp.f90:997
subroutine prp_ad(this)
Advance a time step and release particles if scheduled.
Definition: prt-prp.f90:419
subroutine prp_allocate_scalars(this)
Allocate scalars.
Definition: prt-prp.f90:288
subroutine prp_dimensions(this)
@ brief Set dimensions specific to PrtPrpType
Definition: prt-prp.f90:1039
subroutine exg_prp_dimensions(this)
@ brief Dimensions method override for exchange PRP. Just set all dimensions to zero and allocate arr...
Definition: prt-prp.f90:1070
subroutine prp_set_pointers(this, ibound, izone)
@ brief Set pointers to model variables
Definition: prt-prp.f90:243
subroutine exg_prp_ot_model_flows(this, icbcfl, ibudfl, icbcun, imap)
No-op flow output method for exchange PRP. No contribution to budget, no need to write output.
Definition: prt-prp.f90:517
subroutine initialize_particle(this, particle, ip, trelease)
Definition: prt-prp.f90:602
subroutine prp_da(this)
Deallocate memory.
Definition: prt-prp.f90:193
character(len=16) text
Definition: prt-prp.f90:35
subroutine prp_releasetimes(this)
Load explicitly specified release times.
Definition: prt-prp.f90:1225
subroutine prp_options(this)
@ brief Set options specific to PrtPrpType
Definition: prt-prp.f90:845
subroutine prp_log_options(this, found, trackfile, trackcsvfile)
@ brief Log options specific to PrtPrpType
Definition: prt-prp.f90:1003
logical function prp_obs_supported(this)
Indicates whether observations are supported.
Definition: prt-prp.f90:824
subroutine prp_ar(this)
@ brief Allocate and read period data
Definition: prt-prp.f90:348
subroutine release(this, ip, trelease)
Release a particle at the specified time.
Definition: prt-prp.f90:584
subroutine, public prp_create(packobj, id, ibcnum, inunit, iout, namemodel, pakname, fmi, input_mempath)
Create a new particle release point package.
Definition: prt-prp.f90:127
subroutine validate_release_point(this, ic, x, y, z)
Verify that the release point is in the cell.
Definition: prt-prp.f90:541
subroutine prp_packagedata(this)
Load package data (release points).
Definition: prt-prp.f90:1082
This module contains simulation methods.
Definition: Sim.f90:10
subroutine, public store_warning(msg, substring)
Store warning message.
Definition: Sim.f90:236
subroutine, public store_error(msg, terminate)
Store an error message.
Definition: Sim.f90:92
integer(i4b) function, public count_errors()
Return number of errors.
Definition: Sim.f90:59
subroutine, public store_error_filename(filename, terminate)
Store the erroring file name.
Definition: Sim.f90:203
This module contains simulation variables.
Definition: SimVariables.f90:9
character(len=maxcharlen) errmsg
error message string
character(len=maxcharlen) warnmsg
warning message string
real(dp), pointer, public totalsimtime
time at end of simulation
Definition: tdis.f90:40
integer(i4b), pointer, public kstp
current time step number
Definition: tdis.f90:27
integer(i4b), pointer, public kper
current stress period number
Definition: tdis.f90:26
real(dp), pointer, public delt
length of the current time step
Definition: tdis.f90:32
integer(i4b), pointer, public nper
number of stress period
Definition: tdis.f90:24
@ brief BndType
Derived type for the Budget object.
Definition: Budget.f90:39
This class is used to store a single deferred-length character string. It was designed to work in an ...
Definition: CharString.f90:23
Structured grid discretization.
Definition: Dis.f90:23
Vertex grid discretization.
Definition: Disv.f90:25
Structure of arrays to store particles.
Definition: Particle.f90:109
Particle tracked by the PRT model.
Definition: Particle.f90:62
Particle track output manager. Handles printing as well as writing to files. One output unit can be c...
Exchange PRP package. A variant of the normal PRP package that doesn't read from input files but inst...
Definition: prt-prp.f90:105
Particle release point (PRP) package.
Definition: prt-prp.f90:39