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  real(DP) :: cellsize, tol
570 
571  call this%fmi%dis%get_polyverts(ic, polyverts)
572  ! Check that the point is within the cell. The point-in-polygon
573  ! check is exact by default, which is unreliable when coords are
574  ! are rotated or very large relative to the cell's size: release
575  ! points on or very close to the cell edge could be rejected as
576  ! outside the cell. Give the check a tolerance scaled to the
577  ! cell extent to reduce sensitivity to coordinate inexactness.
578  cellsize = max(maxval(polyverts(1, :)) - minval(polyverts(1, :)), &
579  maxval(polyverts(2, :)) - minval(polyverts(2, :)))
580  tol = cellsize * cellsize * dem7
581  if (.not. point_in_polygon(x, y, polyverts, tol)) then
582  write (errmsg, '(a,g0,a,g0,a,i0)') &
583  'Error: release point (x=', x, ', y=', y, ') is not in cell ', &
584  this%dis%get_nodeuser(ic)
585  call store_error(errmsg, terminate=.false.)
586  call store_error_filename(this%input_fname)
587  end if
588  if (z > maxval(this%dis%top)) then
589  write (errmsg, '(a,g0,a,g0,a,i0)') &
590  'Error: release point (z=', z, ') is above grid top ', &
591  maxval(this%dis%top)
592  call store_error(errmsg, terminate=.false.)
593  call store_error_filename(this%input_fname)
594  else if (z < minval(this%dis%bot)) then
595  write (errmsg, '(a,g0,a,g0,a,i0)') &
596  'Error: release point (z=', z, ') is below grid bottom ', &
597  minval(this%dis%bot)
598  call store_error(errmsg, terminate=.false.)
599  call store_error_filename(this%input_fname)
600  end if
601  deallocate (polyverts)
602  end subroutine validate_release_point
603 
604  !> Release a particle at the specified time.
605  !!
606  !! Releasing a particle entails validating the particle's
607  !! coordinates and settings, transforming its coordinates
608  !! if needed, initializing the particle's initial tracking
609  !! time to the given release time, storing the particle in
610  !! the particle store (from which the PRT model will later
611  !! retrieve it, apply the tracking method, and check it in
612  !! again), and accumulating the particle's mass (the total
613  !! mass released from each release point is calculated for
614  !! budget reporting).
615  !<
616  subroutine release(this, ip, trelease)
617  ! dummy
618  class(prtprptype), intent(inout) :: this !< this instance
619  integer(I4B), intent(in) :: ip !< particle index
620  real(DP), intent(in) :: trelease !< release time
621  ! local
622  integer(I4B) :: np
623  type(particletype), pointer :: particle
624 
625  call this%initialize_particle(particle, ip, trelease)
626  np = this%nparticles + 1
627  this%nparticles = np
628  call this%particles_staging%put(particle, np)
629  deallocate (particle)
630  this%rptm(ip) = this%rptm(ip) + done ! TODO configurable mass
631 
632  end subroutine release
633 
634  subroutine initialize_particle(this, particle, ip, trelease)
636  class(prtprptype), intent(inout) :: this !< this instance
637  type(particletype), pointer, intent(inout) :: particle !< the particle
638  integer(I4B), intent(in) :: ip !< particle index
639  real(DP), intent(in) :: trelease !< release time
640  ! local
641  logical(LGP) :: draped
642  integer(I4B) :: irow, icol, ilay, icpl
643  integer(I4B) :: ic, icu, ic_old
644  real(DP) :: x, y, z
645  ! formats
646  character(len=*), parameter :: fmticterr = &
647  "('Error in ',a,': Flow model interface does not contain ICELLTYPE. &
648  &ICELLTYPE is required for PRT to distinguish convertible cells &
649  &from confined cells if LOCAL_Z release coordinates are provided. &
650  &Make sure a GWFGRID entry is configured in the PRT FMI package.')"
651 
652  ic = this%rptnode(ip)
653 
654  call create_particle(particle)
655 
656  if (size(this%boundname) /= 0) then
657  particle%name = this%boundname(ip)
658  else
659  particle%name = ''
660  end if
661 
662  particle%irpt = ip
663  particle%istopweaksink = this%istopweaksink
664  particle%istopzone = this%istopzone
665  particle%idrymeth = this%idrymeth
666  particle%istatus = 0 ! status 0 until tracking starts
667 
668  ! If the cell is inactive, either drape the particle
669  ! to the top-most active cell beneath it if drape is
670  ! enabled, or else terminate permanently unreleased.
671  draped = .false.
672  if (this%ibound(ic) == 0) then
673  ic_old = ic
674  if (this%drape) then
675  call this%dis%highest_active(ic, this%ibound)
676  draped = ic /= ic_old
677  if (.not. draped .and. this%ibound(ic) == 0) then
678  ! negative unreleased status signals to the
679  ! tracking method that we haven't yet saved
680  ! a termination record, it needs to do so.
681  particle%istatus = -1 * term_unreleased
682  end if
683  else
684  particle%istatus = -1 * term_unreleased
685  end if
686  end if
687 
688  icu = this%dis%get_nodeuser(ic)
689  particle%icu = icu
690  select type (dis => this%dis)
691  type is (distype)
692  call get_ijk(icu, dis%nrow, dis%ncol, dis%nlay, irow, icol, ilay)
693  type is (disvtype)
694  call get_jk(icu, dis%ncpl, dis%nlay, icpl, ilay)
695  end select
696  particle%ilay = ilay
697  particle%izone = this%rptzone(ic)
698 
699  ! if the particle was draped to this cell, set the z coord to
700  ! the effective top of the cell. if it was not draped, and is
701  ! a local z coord, calculate the corresponding model z coord.
702  if (draped) then
703  z = this%fmi%dis%bot(ic) + &
704  this%fmi%gwfsat(ic) * &
705  (this%fmi%dis%top(ic) - this%fmi%dis%bot(ic))
706  else if (this%localz) then
707  z = this%fmi%dis%bot(ic) + &
708  this%rptz(ip) * &
709  this%fmi%gwfsat(ic) * &
710  (this%fmi%dis%top(ic) - this%fmi%dis%bot(ic))
711  else
712  z = this%rptz(ip)
713  end if
714 
715  x = this%rptx(ip)
716  y = this%rpty(ip)
717 
718  if (this%ichkmeth > 0) &
719  call this%validate_release_point(ic, x, y, z)
720 
721  particle%x = x
722  particle%y = y
723  particle%z = z
724  particle%trelease = trelease
725 
726  ! Set stop time to earlier of STOPTIME and STOPTRAVELTIME
727  if (this%stoptraveltime == huge(1d0)) then
728  particle%tstop = this%stoptime
729  else
730  particle%tstop = particle%trelease + this%stoptraveltime
731  if (this%stoptime < particle%tstop) particle%tstop = this%stoptime
732  end if
733 
734  particle%ttrack = particle%trelease
735  particle%itrdomain(level_model) = 0
736  particle%iboundary(level_model) = 0
737  particle%itrdomain(level_feature) = ic
738  particle%iboundary(level_feature) = 0
739  particle%itrdomain(level_subfeature) = 0
740  particle%iboundary(level_subfeature) = 0
741  particle%frctrn = this%frctrn
742  particle%iexmeth = this%iexmeth
743  particle%extend = this%extend
744  particle%icycwin = this%icycwin
745  particle%extol = this%extol
746  end subroutine initialize_particle
747 
748  !> @ brief Read and prepare period data for particle input
749  subroutine prp_rp(this)
750  ! modules
751  use tdismodule, only: kper, nper
754  ! dummy variables
755  class(prtprptype), intent(inout) :: this
756  ! local variables
757  type(characterstringtype), dimension(:), contiguous, &
758  pointer :: settings
759  integer(I4B), pointer :: iper, ionper, nlist
760  integer(I4B) :: n
761 
762  ! set pointer to last and next period loaded
763  call mem_setptr(iper, 'IPER', this%input_mempath)
764  call mem_setptr(ionper, 'IONPER', this%input_mempath)
765 
766  if (kper == 1 .and. &
767  (iper == 0) .and. &
768  (ionper > nper) .and. &
769  size(this%schedule%time_select%times) == 0) then
770  ! If the user hasn't provided any release settings (neither
771  ! explicit release times, release time frequency, nor period
772  ! block release settings), default to a single release at the
773  ! start of the simulation (t=0). Add t=0 directly to the time
774  ! selection rather than time step selection because the latter
775  ! would fill forward, releasing at the start of every period.
776  call this%schedule%time_select%extend([dzero])
777  return
778  else if (iper /= kper) then
779  return
780  end if
781 
782  ! set input context pointers
783  call mem_setptr(nlist, 'NBOUND', this%input_mempath)
784  call mem_setptr(settings, 'SETTING', this%input_mempath)
785 
786  ! Store period block configuration for fill-forward.
787  if (allocated(this%period_block_lines)) deallocate (this%period_block_lines)
788  allocate (this%period_block_lines(nlist))
789  do n = 1, nlist
790  this%period_block_lines(n) = settings(n)
791  end do
792  end subroutine prp_rp
793 
794  !> @ brief No-op RP method override for exchange PRP.
795  subroutine exg_prp_rp(this)
796  class(exgprtprptype), intent(inout) :: this
797  end subroutine exg_prp_rp
798 
799  !> @ brief Calculate flow between package and model.
800  subroutine prp_cq_simrate(this, hnew, flowja, imover)
801  ! modules
802  use tdismodule, only: delt
803  ! dummy variables
804  class(prtprptype) :: this
805  real(DP), dimension(:), intent(in) :: hnew
806  real(DP), dimension(:), intent(inout) :: flowja !< flow between package and model
807  integer(I4B), intent(in) :: imover !< flag indicating if the mover package is active
808  ! local variables
809  integer(I4B) :: i
810  integer(I4B) :: node
811  integer(I4B) :: idiag
812  real(DP) :: rrate
813 
814  ! If no boundaries, skip flow calculations.
815  if (this%nbound <= 0) return
816 
817  ! Loop through each boundary calculating flow.
818  do i = 1, this%nbound
819  node = this%nodelist(i)
820  rrate = dzero
821  ! If cell is no-flow or constant-head, then ignore it.
822  if (node > 0) then
823  ! Calculate the flow rate into the cell.
824  idiag = this%dis%con%ia(node)
825  rrate = this%rptm(i) * (done / delt) ! reciprocal of tstp length
826  flowja(idiag) = flowja(idiag) + rrate
827  end if
828 
829  ! Save simulated value to simvals array.
830  this%simvals(i) = rrate
831  end do
832  end subroutine prp_cq_simrate
833 
834  subroutine define_listlabel(this)
835  class(prtprptype), intent(inout) :: this
836  ! not implemented, not used
837  end subroutine define_listlabel
838 
839  !> @brief Indicates whether observations are supported.
840  logical function prp_obs_supported(this)
841  class(prtprptype) :: this
842  prp_obs_supported = .true.
843  end function prp_obs_supported
844 
845  !> @brief Store supported observations
846  subroutine prp_df_obs(this)
847  ! dummy
848  class(prtprptype) :: this
849  ! local
850  integer(I4B) :: indx
851  call this%obs%StoreObsType('prp', .true., indx)
852  this%obs%obsData(indx)%ProcessIdPtr => defaultobsidprocessor
853 
854  ! Store obs type and assign procedure pointer
855  ! for to-mvr observation type.
856  call this%obs%StoreObsType('to-mvr', .true., indx)
857  this%obs%obsData(indx)%ProcessIdPtr => defaultobsidprocessor
858  end subroutine prp_df_obs
859 
860  !> @ brief Set options specific to PrtPrpType
861  subroutine prp_options(this)
862  ! -- modules
865  use openspecmodule, only: access, form
868  ! -- dummy variables
869  class(prtprptype), intent(inout) :: this
870  ! -- local variables
871  character(len=LENVARNAME), dimension(3) :: drytrack_method = &
872  &[character(len=LENVARNAME) :: 'DROP', 'STOP', 'STAY']
873  character(len=lenvarname), dimension(2) :: coorcheck_method = &
874  &[character(len=LENVARNAME) :: 'NONE', 'EAGER']
875  character(len=LINELENGTH) :: trackfile, trackcsvfile, fname
876  type(prtprpparamfoundtype) :: found
877  character(len=*), parameter :: fmtextolwrn = &
878  "('WARNING: EXIT_SOLVE_TOLERANCE is set to ',g10.3,' &
879  &which is much greater than the default value of ',g10.3,'. &
880  &The tolerance that strikes the best balance between accuracy &
881  &and runtime is problem-dependent. Since the variable being &
882  &solved varies from 0 to 1, tolerance values much less than 1 &
883  &typically give the best results.')"
884 
885  ! source base class options
886  call this%BndExtType%source_options()
887 
888  ! update defaults from input context
889  call mem_set_value(this%stoptime, 'STOPTIME', this%input_mempath, &
890  found%stoptime)
891  call mem_set_value(this%stoptraveltime, 'STOPTRAVELTIME', &
892  this%input_mempath, found%stoptraveltime)
893  call mem_set_value(this%istopweaksink, 'ISTOPWEAKSINK', this%input_mempath, &
894  found%istopweaksink)
895  call mem_set_value(this%istopzone, 'ISTOPZONE', this%input_mempath, &
896  found%istopzone)
897  call mem_set_value(this%drape, 'DRAPE', this%input_mempath, &
898  found%drape)
899  call mem_set_value(this%idrymeth, 'IDRYMETH', this%input_mempath, &
900  drytrack_method, found%idrymeth)
901  call mem_set_value(trackfile, 'TRACKFILE', this%input_mempath, &
902  found%trackfile)
903  call mem_set_value(trackcsvfile, 'TRACKCSVFILE', this%input_mempath, &
904  found%trackcsvfile)
905  call mem_set_value(this%localz, 'LOCALZ', this%input_mempath, &
906  found%localz)
907  call mem_set_value(this%extend, 'EXTEND', this%input_mempath, &
908  found%extend)
909  call mem_set_value(this%extol, 'EXTOL', this%input_mempath, &
910  found%extol)
911  call mem_set_value(this%rttol, 'RTTOL', this%input_mempath, &
912  found%rttol)
913  call mem_set_value(this%rtfreq, 'RTFREQ', this%input_mempath, &
914  found%rtfreq)
915  call mem_set_value(this%frctrn, 'FRCTRN', this%input_mempath, &
916  found%frctrn)
917  call mem_set_value(this%iexmeth, 'IEXMETH', this%input_mempath, &
918  found%iexmeth)
919  call mem_set_value(this%ichkmeth, 'ICHKMETH', this%input_mempath, &
920  coorcheck_method, found%ichkmeth)
921  call mem_set_value(this%icycwin, 'ICYCWIN', this%input_mempath, found%icycwin)
922 
923  ! update internal state and validate input
924  if (found%idrymeth) then
925  if (this%idrymeth == 0) then
926  write (errmsg, '(a)') 'Unsupported dry tracking method. &
927  &DRY_TRACKING_METHOD must be "DROP", "STOP", or "STAY"'
928  call store_error(errmsg)
929  else
930  ! adjust for method zero indexing
931  this%idrymeth = this%idrymeth - 1
932  end if
933  end if
934 
935  if (found%extol) then
936  if (this%extol <= dzero) &
937  call store_error('EXIT_SOLVE_TOLERANCE MUST BE POSITIVE')
938  if (this%extol > dem2) then
939  write (warnmsg, fmt=fmtextolwrn) &
940  this%extol, default_exit_solve_tolerance
941  call store_warning(warnmsg)
942  end if
943  end if
944 
945  if (found%rttol) then
946  if (this%rttol <= dzero) &
947  call store_error('RELEASE_TIME_TOLERANCE MUST BE POSITIVE')
948  end if
949 
950  if (found%rtfreq) then
951  if (this%rtfreq <= dzero) &
952  call store_error('RELEASE_TIME_FREQUENCY MUST BE POSITIVE')
953  end if
954 
955  if (found%iexmeth) then
956  if (.not. (this%iexmeth /= 1 .or. this%iexmeth /= 2)) &
957  call store_error('DEV_EXIT_SOLVE_METHOD MUST BE &
958  &1 (BRENT) OR 2 (CHANDRUPATLA)')
959  end if
960 
961  if (found%ichkmeth) then
962  if (this%ichkmeth == 0) then
963  write (errmsg, '(a)') 'Unsupported coordinate check method. &
964  &COORDINATE_CHECK_METHOD must be "NONE" or "EAGER"'
965  call store_error(errmsg)
966  else
967  ! adjust for method zero based indexing
968  this%ichkmeth = this%ichkmeth - 1
969  end if
970  end if
971 
972  if (found%icycwin) then
973  if (this%icycwin < 0) &
974  call store_error('CYCLE_DETECTION_WINDOW MUST BE NON-NEGATIVE')
975  end if
976 
977  ! fileout options
978  if (found%trackfile) then
979  this%itrkout = getunit()
980  call openfile(this%itrkout, this%iout, trackfile, 'DATA(BINARY)', &
981  form, access, filstat_opt='REPLACE', &
982  mode_opt=mnormal)
983  ! open and write ascii header spec file
984  this%itrkhdr = getunit()
985  fname = trim(trackfile)//'.hdr'
986  call openfile(this%itrkhdr, this%iout, fname, 'CSV', &
987  filstat_opt='REPLACE', mode_opt=mnormal)
988  write (this%itrkhdr, '(a,/,a)') trackheader, trackdtypes
989  end if
990 
991  if (found%trackcsvfile) then
992  this%itrkcsv = getunit()
993  call openfile(this%itrkcsv, this%iout, trackcsvfile, 'CSV', &
994  filstat_opt='REPLACE')
995  write (this%itrkcsv, '(a)') trackheader
996  end if
997 
998  ! terminate if any errors were detected
999  if (count_errors() > 0) then
1000  call store_error_filename(this%input_fname)
1001  end if
1002 
1003  ! log found options
1004  call this%prp_log_options(found, trackfile, trackcsvfile)
1005 
1006  ! Create release schedule now that we know
1007  ! the coincident release time tolerance
1008  this%schedule => create_release_schedule(tolerance=this%rttol)
1009  end subroutine prp_options
1010 
1011  !> @ brief No-op options method override for exchange PRP.
1012  !! Just creates an empty release schedule.
1013  subroutine exg_prp_options(this)
1014  class(exgprtprptype), intent(inout) :: this
1015  this%schedule => create_release_schedule(tolerance=this%rttol)
1016  end subroutine exg_prp_options
1017 
1018  !> @ brief Log options specific to PrtPrpType
1019  subroutine prp_log_options(this, found, trackfile, trackcsvfile)
1020  ! -- modules
1022  ! -- dummy variables
1023  class(prtprptype), intent(inout) :: this
1024  type(prtprpparamfoundtype), intent(in) :: found
1025  character(len=*), intent(in) :: trackfile
1026  character(len=*), intent(in) :: trackcsvfile
1027  ! -- local variables
1028  ! formats
1029  character(len=*), parameter :: fmttrkbin = &
1030  "(4x, 'PARTICLE TRACKS WILL BE SAVED TO BINARY FILE: ', a, /4x, &
1031  &'OPENED ON UNIT: ', I0)"
1032  character(len=*), parameter :: fmttrkcsv = &
1033  "(4x, 'PARTICLE TRACKS WILL BE SAVED TO CSV FILE: ', a, /4x, &
1034  &'OPENED ON UNIT: ', I0)"
1035 
1036  write (this%iout, '(1x,a)') 'PROCESSING PARTICLE INPUT DIMENSIONS'
1037 
1038  if (found%frctrn) then
1039  write (this%iout, '(4x,a)') &
1040  'IF DISV, TRACKING WILL USE THE TERNARY METHOD REGARDLESS OF CELL TYPE'
1041  end if
1042 
1043  if (found%trackfile) then
1044  write (this%iout, fmttrkbin) trim(adjustl(trackfile)), this%itrkout
1045  end if
1046 
1047  if (found%trackcsvfile) then
1048  write (this%iout, fmttrkcsv) trim(adjustl(trackcsvfile)), this%itrkcsv
1049  end if
1050 
1051  write (this%iout, '(1x,a)') 'END OF PARTICLE INPUT DIMENSIONS'
1052  end subroutine prp_log_options
1053 
1054  !> @ brief Set dimensions specific to PrtPrpType
1055  subroutine prp_dimensions(this)
1056  ! modules
1059  ! dummy variables
1060  class(prtprptype), intent(inout) :: this
1061  ! local variables
1062  type(prtprpparamfoundtype) :: found
1063 
1064  call mem_set_value(this%nreleasepoints, 'NRELEASEPTS', this%input_mempath, &
1065  found%nreleasepts)
1066  call mem_set_value(this%nreleasetimes, 'NRELEASETIMES', this%input_mempath, &
1067  found%nreleasetimes)
1068 
1069  write (this%iout, '(1x,a)') 'PROCESSING PARTICLE INPUT DIMENSIONS'
1070  write (this%iout, '(4x,a,i0)') 'NRELEASEPTS = ', this%nreleasepoints
1071  write (this%iout, '(4x,a,i0)') 'NRELEASETIMES = ', this%nreleasetimes
1072  write (this%iout, '(1x,a)') 'END OF PARTICLE INPUT DIMENSIONS'
1073 
1074  ! set maxbound and nbound to nreleasepts
1075  this%maxbound = this%nreleasepoints
1076  this%nbound = this%nreleasepoints
1077 
1078  call this%prp_allocate_arrays()
1079  call this%prp_packagedata()
1080  call this%prp_releasetimes()
1081  call this%prp_load_releasetimefrequency()
1082  end subroutine prp_dimensions
1083 
1084  !> @ brief Dimensions method override for exchange PRP.
1085  !! Just set all dimensions to zero and allocate arrays.
1086  subroutine exg_prp_dimensions(this)
1087  class(exgprtprptype), intent(inout) :: this
1088 
1089  this%nreleasepoints = 0
1090  this%nreleasetimes = 0
1091  this%maxbound = 0
1092  this%nbound = 0
1093 
1094  call this%prp_allocate_arrays()
1095  end subroutine exg_prp_dimensions
1096 
1097  !> @brief Load package data (release points).
1098  subroutine prp_packagedata(this)
1099  use memorymanagermodule, only: mem_setptr
1101  use geomutilmodule, only: get_node
1103  ! dummy
1104  class(prtprptype), intent(inout) :: this
1105  ! local
1106  integer(I4B), dimension(:), pointer, contiguous :: irptno
1107  integer(I4B), dimension(:, :), pointer, contiguous :: cellids
1108  real(DP), dimension(:), pointer, contiguous :: xrpts, yrpts, zrpts
1109  type(characterstringtype), dimension(:), pointer, &
1110  contiguous :: boundnames
1111  character(len=LENBOUNDNAME) :: bndName, bndNameTemp
1112  character(len=9) :: cno
1113  character(len=20) :: cellidstr
1114  integer(I4B), dimension(:), allocatable :: nboundchk
1115  integer(I4B), dimension(:), pointer :: cellid
1116  integer(I4B) :: n, noder, nodeu, rptno
1117 
1118  ! set input context pointers
1119  call mem_setptr(irptno, 'IRPTNO', this%input_mempath)
1120  call mem_setptr(cellids, 'CELLID', this%input_mempath)
1121  call mem_setptr(xrpts, 'XRPT', this%input_mempath)
1122  call mem_setptr(yrpts, 'YRPT', this%input_mempath)
1123  call mem_setptr(zrpts, 'ZRPT', this%input_mempath)
1124  call mem_setptr(boundnames, 'BOUNDNAME', this%input_mempath)
1125 
1126  ! allocate and initialize temporary variables
1127  allocate (nboundchk(this%nreleasepoints))
1128  do n = 1, this%nreleasepoints
1129  nboundchk(n) = 0
1130  end do
1131 
1132  write (this%iout, '(/1x,a)') 'PROCESSING '//trim(adjustl(this%packName)) &
1133  //' PACKAGEDATA'
1134 
1135  do n = 1, size(irptno)
1136 
1137  rptno = irptno(n)
1138 
1139  if (rptno < 1 .or. rptno > this%nreleasepoints) then
1140  write (errmsg, '(a,i0,a,i0,a)') &
1141  'Expected ', this%nreleasepoints, ' release points. &
1142  &Points must be numbered from 1 to ', this%nreleasepoints, '.'
1143  call store_error(errmsg)
1144  cycle
1145  end if
1146 
1147  ! increment nboundchk
1148  nboundchk(rptno) = nboundchk(rptno) + 1
1149 
1150  ! set cellid
1151  cellid => cellids(:, n)
1152 
1153  ! set node user
1154  if (this%dis%ndim == 1) then
1155  nodeu = cellid(1)
1156  elseif (this%dis%ndim == 2) then
1157  nodeu = get_node(cellid(1), 1, cellid(2), &
1158  this%dis%mshape(1), 1, &
1159  this%dis%mshape(2))
1160  else
1161  nodeu = get_node(cellid(1), cellid(2), cellid(3), &
1162  this%dis%mshape(1), &
1163  this%dis%mshape(2), &
1164  this%dis%mshape(3))
1165  end if
1166 
1167  ! set noder
1168  noder = this%dis%get_nodenumber(nodeu, 1)
1169  if (noder <= 0) then
1170  call this%dis%nodeu_to_string(nodeu, cellidstr)
1171  write (errmsg, '(a)') &
1172  'Particle release point configured for nonexistent cell: '// &
1173  trim(adjustl(cellidstr))//'. This cell has IDOMAIN <= 0 and '&
1174  &'therefore does not exist in the model grid.'
1175  call store_error(errmsg)
1176  cycle
1177  else
1178  this%rptnode(rptno) = noder
1179  end if
1180 
1181  if (this%localz .and. (zrpts(n) < 0 .or. zrpts(n) > 1)) then
1182  call store_error('Local z coordinate must fall in the interval [0, 1]')
1183  cycle
1184  end if
1185 
1186  ! set coordinates
1187  this%rptx(rptno) = xrpts(n)
1188  this%rpty(rptno) = yrpts(n)
1189  this%rptz(rptno) = zrpts(n)
1190 
1191  ! set default boundname
1192  write (cno, '(i9.9)') rptno
1193  bndname = 'PRP'//cno
1194 
1195  ! read boundnames from file, if provided
1196  if (this%inamedbound /= 0) then
1197  bndnametemp = boundnames(n)
1198  if (bndnametemp /= '') bndname = bndnametemp
1199  else
1200  bndname = ''
1201  end if
1202 
1203  ! set boundname
1204  this%rptname(rptno) = bndname
1205  end do
1206 
1207  write (this%iout, '(1x,a)') &
1208  'END OF '//trim(adjustl(this%packName))//' PACKAGEDATA'
1209 
1210  ! check for duplicate or missing particle release points
1211  do n = 1, this%nreleasepoints
1212  if (nboundchk(n) == 0) then
1213  write (errmsg, '(a,a,1x,i0,a)') 'No data specified for particle ', &
1214  'release point', n, '.'
1215  call store_error(errmsg)
1216  else if (nboundchk(n) > 1) then
1217  write (errmsg, '(a,1x,i0,1x,a,1x,i0,1x,a)') &
1218  'Data for particle release point', n, 'specified', nboundchk(n), &
1219  'times.'
1220  call store_error(errmsg)
1221  end if
1222  end do
1223 
1224  ! terminate if any errors were detected
1225  if (count_errors() > 0) then
1226  call store_error_filename(this%input_fname)
1227  end if
1228 
1229  ! cleanup
1230  deallocate (nboundchk)
1231 
1232  call memorystore_release('IRPTNO', this%input_mempath)
1233  call memorystore_release('CELLID', this%input_mempath)
1234  call memorystore_release('XRPT', this%input_mempath)
1235  call memorystore_release('YRPT', this%input_mempath)
1236  call memorystore_release('ZRPT', this%input_mempath)
1237  call memorystore_release('BOUNDNAME', this%input_mempath)
1238  end subroutine prp_packagedata
1239 
1240  !> @brief Load explicitly specified release times.
1241  subroutine prp_releasetimes(this)
1243  ! dummy
1244  class(prtprptype), intent(inout) :: this
1245  ! local
1246  real(DP), dimension(:), pointer, contiguous :: time
1247  integer(I4B) :: n, isize
1248  real(DP), allocatable :: times(:)
1249 
1250  if (this%nreleasetimes <= 0) return
1251 
1252  ! allocate times array
1253  allocate (times(this%nreleasetimes))
1254 
1255  ! check if input array was read
1256  call get_isize('TIME', this%input_mempath, isize)
1257 
1258  if (isize <= 0) then
1259  errmsg = "RELEASTIMES block expected when &
1260  &NRELEASETIMES dimension is non-zero."
1261  call store_error(errmsg)
1262  call store_error_filename(this%input_fname)
1263  end if
1264 
1265  ! set input context pointer
1266  call mem_setptr(time, 'TIME', this%input_mempath)
1267 
1268  ! set input data
1269  do n = 1, size(time)
1270  times(n) = time(n)
1271  end do
1272 
1273  ! register times with the release schedule
1274  call this%schedule%time_select%extend(times)
1275 
1276  ! make sure times strictly increase
1277  if (.not. this%schedule%time_select%increasing()) then
1278  errmsg = "RELEASTIMES block entries must strictly increase."
1279  call store_error(errmsg)
1280  call store_error_filename(this%input_fname)
1281  end if
1282 
1283  ! deallocate
1284  deallocate (times)
1285  end subroutine prp_releasetimes
1286 
1287  !> @brief Load regularly spaced release times if configured.
1289  ! modules
1290  use tdismodule, only: totalsimtime
1291  ! dummy
1292  class(prtprptype), intent(inout) :: this
1293  ! local
1294  real(DP), allocatable :: times(:)
1295 
1296  ! check if a release time frequency is configured
1297  if (this%rtfreq <= dzero) return
1298 
1299  ! create array of regularly-spaced release times
1300  times = arange( &
1301  start=dzero, &
1302  stop=totalsimtime, &
1303  step=this%rtfreq)
1304 
1305  ! register times with release schedule
1306  call this%schedule%time_select%extend(times)
1307 
1308  ! make sure times strictly increase
1309  if (.not. this%schedule%time_select%increasing()) then
1310  errmsg = "Release times must strictly increase"
1311  call store_error(errmsg)
1312  call store_error_filename(this%input_fname)
1313  end if
1314 
1315  ! deallocate
1316  deallocate (times)
1317 
1318  end subroutine prp_load_releasetimefrequency
1319 
1320 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
real(dp), parameter dem7
real constant 1e-7
Definition: Constants.f90:110
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:92
logical function, public point_in_polygon(x, y, poly, tol)
Check if a point is within a polygon.
Definition: GeomUtil.f90:31
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:109
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:137
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:40
@, public level_subfeature
Definition: Method.f90:41
@, public level_model
Definition: Method.f90:39
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:750
subroutine exg_prp_rp(this)
@ brief No-op RP method override for exchange PRP.
Definition: prt-prp.f90:796
subroutine prp_load_releasetimefrequency(this)
Load regularly spaced release times if configured.
Definition: prt-prp.f90:1289
subroutine prp_cq_simrate(this, hnew, flowja, imover)
@ brief Calculate flow between package and model.
Definition: prt-prp.f90:801
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:847
real(dp), parameter default_exit_solve_tolerance
Definition: prt-prp.f90:36
subroutine define_listlabel(this)
Definition: prt-prp.f90:835
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:1014
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:1056
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:1087
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:635
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:1242
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:862
subroutine prp_log_options(this, found, trackfile, trackcsvfile)
@ brief Log options specific to PrtPrpType
Definition: prt-prp.f90:1020
logical function prp_obs_supported(this)
Indicates whether observations are supported.
Definition: prt-prp.f90:841
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:617
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:1099
This module contains simulation methods.
Definition: Sim.f90:10
subroutine, public store_warning(msg, substring)
Store warning message.
Definition: Sim.f90:237
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:204
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