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