MODFLOW 6  version 6.7.0.dev3
USGS Modular Hydrologic Model
gwt-mst.f90
Go to the documentation of this file.
1 !> -- @ brief Mobile Storage and Transfer (MST) Module
2 !!
3 !! The GwtMstModule contains the GwtMstType, which is the
4 !! derived type responsible for adding the effects of
5 !! 1. Changes in dissolved solute mass
6 !! 2. Decay of dissolved solute mass
7 !! 3. Sorption
8 !! 4. Decay of sorbed solute mass
9 !<
11 
12  use kindmodule, only: dp, i4b
16  use simmodule, only: store_error, count_errors, &
20  use basedismodule, only: disbasetype
21  use tspfmimodule, only: tspfmitype
25 
26  implicit none
27  public :: gwtmsttype
28  public :: mst_cr
29  !
30  integer(I4B), parameter :: nbditems = 4
31  character(len=LENBUDTXT), dimension(NBDITEMS) :: budtxt
32  data budtxt/' STORAGE-AQUEOUS', ' DECAY-AQUEOUS', &
33  ' STORAGE-SORBED', ' DECAY-SORBED'/
34 
35  !> @brief Enumerator that defines the decay options
36  !<
37  ENUM, BIND(C)
38  ENUMERATOR :: decay_off = 0 !< Decay (or production) of mass inactive (default)
39  ENUMERATOR :: decay_first_order = 1 !< First-order decay
40  ENUMERATOR :: decay_zero_order = 2 !< Zeroth-order decay
41  END ENUM
42 
43  !> @ brief Mobile storage and transfer
44  !!
45  !! Data and methods for handling changes in solute storage,
46  !! decay of dissolved solute mass, sorption, and decay of
47  !! sorbed mass.
48  !<
49  type, extends(numericalpackagetype) :: gwtmsttype
50  !
51  ! -- storage
52  real(dp), dimension(:), pointer, contiguous :: porosity => null() !< mobile porosity defined as volume mobile voids per volume of mobile domain
53  real(dp), dimension(:), pointer, contiguous :: thetam => null() !< mobile porosity defined as volume mobile voids per volume of aquifer
54  real(dp), dimension(:), pointer, contiguous :: volfracim => null() !< sum of all immobile domain volume fractions
55  real(dp), dimension(:), pointer, contiguous :: ratesto => null() !< rate of mobile storage
56  !
57  ! -- decay
58  integer(I4B), pointer :: idcy => null() !< order of decay rate (0:none, 1:first, 2:zero)
59  real(dp), dimension(:), pointer, contiguous :: decay => null() !< first or zero order decay rate (aqueous)
60  real(dp), dimension(:), pointer, contiguous :: decay_sorbed => null() !< first or zero order decay rate (sorbed)
61  real(dp), dimension(:), pointer, contiguous :: ratedcy => null() !< rate of decay
62  real(dp), dimension(:), pointer, contiguous :: decaylast => null() !< decay rate used for last iteration (needed for zero order decay)
63  real(dp), dimension(:), pointer, contiguous :: decayslast => null() !< sorbed decay rate used for last iteration (needed for zero order decay)
64  !
65  ! -- sorption
66  integer(I4B), pointer :: isrb => null() !< sorption active flag (0:off, 1:linear, 2:freundlich, 3:langmuir)
67  integer(I4B), pointer :: ioutsorbate => null() !< unit number for sorbate concentration output
68  real(dp), dimension(:), pointer, contiguous :: bulk_density => null() !< bulk density of mobile domain; mass of mobile domain solid per aquifer volume
69  real(dp), dimension(:), pointer, contiguous :: distcoef => null() !< kd distribution coefficient
70  real(dp), dimension(:), pointer, contiguous :: sp2 => null() !< second sorption parameter
71  real(dp), dimension(:), pointer, contiguous :: ratesrb => null() !< rate of sorption
72  real(dp), dimension(:), pointer, contiguous :: ratedcys => null() !< rate of sorbed mass decay
73  real(dp), dimension(:), pointer, contiguous :: csrb => null() !< sorbate concentration
74  class(isothermtype), pointer :: isotherm => null() !< pointer to isotherm object
75  ! -- misc
76  integer(I4B), dimension(:), pointer, contiguous :: ibound => null() !< pointer to model ibound
77  type(tspfmitype), pointer :: fmi => null() !< pointer to fmi object
78 
79  contains
80 
81  procedure :: mst_ar
82  procedure :: mst_fc
83  procedure :: mst_fc_sto
84  procedure :: mst_fc_dcy
85  procedure :: mst_fc_srb
86  procedure :: mst_fc_dcy_srb
87  procedure :: mst_cq
88  procedure :: mst_cq_sto
89  procedure :: mst_cq_dcy
90  procedure :: mst_cq_srb
91  procedure :: mst_cq_dcy_srb
92  procedure :: mst_calc_csrb
93  procedure :: mst_bd
94  procedure :: mst_ot_flow
95  procedure :: mst_ot_dv
96  procedure :: mst_da
97  procedure :: allocate_scalars
98  procedure :: addto_volfracim
99  procedure :: get_volfracm
100  procedure, private :: allocate_arrays
101  procedure, private :: log_options
102  procedure, private :: log_data
103  procedure, private :: source_options
104  procedure, private :: source_data
105 
106  end type gwtmsttype
107 
108 contains
109 
110  !> @ brief Create a new package object
111  !!
112  !! Create a new MST object
113  !<
114  subroutine mst_cr(mstobj, name_model, input_mempath, inunit, iout, fmi)
115  ! -- dummy
116  type(gwtmsttype), pointer :: mstobj !< unallocated new mst object to create
117  character(len=*), intent(in) :: name_model !< name of the model
118  character(len=*), intent(in) :: input_mempath !< memory path of input
119  integer(I4B), intent(in) :: inunit !< unit number of WEL package input file
120  integer(I4B), intent(in) :: iout !< unit number of model listing file
121  type(tspfmitype), intent(in), target :: fmi !< fmi package for this GWT model
122  !
123  ! -- Create the object
124  allocate (mstobj)
125  !
126  ! -- create name and memory path
127  call mstobj%set_names(1, name_model, 'MST', 'MST', input_mempath)
128  !
129  ! -- Allocate scalars
130  call mstobj%allocate_scalars()
131  !
132  ! -- Set variables
133  mstobj%inunit = inunit
134  mstobj%iout = iout
135  mstobj%fmi => fmi
136  end subroutine mst_cr
137 
138  !> @ brief Allocate and read method for package
139  !!
140  !! Method to allocate and read static data for the package.
141  !<
142  subroutine mst_ar(this, dis, ibound)
143  ! -- modules
144  ! -- dummy
145  class(gwtmsttype), intent(inout) :: this !< GwtMstType object
146  class(disbasetype), pointer, intent(in) :: dis !< pointer to dis package
147  integer(I4B), dimension(:), pointer, contiguous :: ibound !< pointer to GWT ibound array
148  ! -- local
149  ! -- formats
150  character(len=*), parameter :: fmtmst = &
151  "(1x,/1x,'MST -- MOBILE STORAGE AND TRANSFER PACKAGE, VERSION 1, &
152  &7/29/2020 INPUT READ FROM MEMPATH: ', A, //)"
153  !
154  ! --print a message identifying the mobile storage and transfer package.
155  write (this%iout, fmtmst) this%input_mempath
156  !
157  ! -- Source options
158  call this%source_options()
159  !
160  ! -- store pointers to arguments that were passed in
161  this%dis => dis
162  this%ibound => ibound
163  !
164  ! -- Allocate arrays
165  call this%allocate_arrays(dis%nodes)
166  !
167  ! -- source the data block
168  call this%source_data()
169  !
170  ! -- Create isotherm object if sorption is active
171  this%isotherm => create_isotherm(this%isrb, this%distcoef, this%sp2)
172  end subroutine mst_ar
173 
174  !> @ brief Fill coefficient method for package
175  !!
176  !! Method to calculate and fill coefficients for the package.
177  !<
178  subroutine mst_fc(this, nodes, cold, nja, matrix_sln, idxglo, cnew, &
179  rhs, kiter)
180  ! -- modules
181  ! -- dummy
182  class(gwtmsttype) :: this !< GwtMstType object
183  integer, intent(in) :: nodes !< number of nodes
184  real(DP), intent(in), dimension(nodes) :: cold !< concentration at end of last time step
185  integer(I4B), intent(in) :: nja !< number of GWT connections
186  class(matrixbasetype), pointer :: matrix_sln !< solution matrix
187  integer(I4B), intent(in), dimension(nja) :: idxglo !< mapping vector for model (local) to solution (global)
188  real(DP), intent(inout), dimension(nodes) :: rhs !< right-hand side vector for model
189  real(DP), intent(in), dimension(nodes) :: cnew !< concentration at end of this time step
190  integer(I4B), intent(in) :: kiter !< solution outer iteration number
191  !
192  ! -- storage contribution
193  call this%mst_fc_sto(nodes, cold, nja, matrix_sln, idxglo, rhs)
194  !
195  ! -- decay contribution
196  if (this%idcy /= decay_off) then
197  call this%mst_fc_dcy(nodes, cold, cnew, nja, matrix_sln, idxglo, &
198  rhs, kiter)
199  end if
200  !
201  ! -- sorption contribution
202  if (this%isrb /= sorption_off) then
203  call this%mst_fc_srb(nodes, cold, nja, matrix_sln, idxglo, rhs, cnew)
204  end if
205  !
206  ! -- decay sorbed contribution
207  if (this%isrb /= sorption_off .and. this%idcy /= decay_off) then
208  call this%mst_fc_dcy_srb(nodes, cold, nja, matrix_sln, idxglo, rhs, &
209  cnew, kiter)
210  end if
211  end subroutine mst_fc
212 
213  !> @ brief Fill storage coefficient method for package
214  !!
215  !! Method to calculate and fill storage coefficients for the package.
216  !<
217  subroutine mst_fc_sto(this, nodes, cold, nja, matrix_sln, idxglo, rhs)
218  ! -- modules
219  use tdismodule, only: delt
220  ! -- dummy
221  class(gwtmsttype) :: this !< GwtMstType object
222  integer, intent(in) :: nodes !< number of nodes
223  real(DP), intent(in), dimension(nodes) :: cold !< concentration at end of last time step
224  integer(I4B), intent(in) :: nja !< number of GWT connections
225  class(matrixbasetype), pointer :: matrix_sln !< solution coefficient matrix
226  integer(I4B), intent(in), dimension(nja) :: idxglo !< mapping vector for model (local) to solution (global)
227  real(DP), intent(inout), dimension(nodes) :: rhs !< right-hand side vector for model
228  ! -- local
229  integer(I4B) :: n, idiag
230  real(DP) :: tled
231  real(DP) :: hhcof, rrhs
232  real(DP) :: vnew, vold
233  !
234  ! -- set variables
235  tled = done / delt
236  !
237  ! -- loop through and calculate storage contribution to hcof and rhs
238  do n = 1, this%dis%nodes
239  !
240  ! -- skip if transport inactive
241  if (this%ibound(n) <= 0) cycle
242  !
243  ! -- calculate new and old water volumes
244  vnew = this%dis%area(n) * (this%dis%top(n) - this%dis%bot(n)) * &
245  this%fmi%gwfsat(n) * this%thetam(n)
246  vold = vnew
247  if (this%fmi%igwfstrgss /= 0) vold = vold + this%fmi%gwfstrgss(n) * delt
248  if (this%fmi%igwfstrgsy /= 0) vold = vold + this%fmi%gwfstrgsy(n) * delt
249  !
250  ! -- add terms to diagonal and rhs accumulators
251  hhcof = -vnew * tled
252  rrhs = -vold * tled * cold(n)
253  idiag = this%dis%con%ia(n)
254  call matrix_sln%add_value_pos(idxglo(idiag), hhcof)
255  rhs(n) = rhs(n) + rrhs
256  end do
257  end subroutine mst_fc_sto
258 
259  !> @ brief Fill decay coefficient method for package
260  !!
261  !! Method to calculate and fill decay coefficients for the package.
262  !<
263  subroutine mst_fc_dcy(this, nodes, cold, cnew, nja, matrix_sln, &
264  idxglo, rhs, kiter)
265  ! -- modules
266  use tdismodule, only: delt
267  ! -- dummy
268  class(gwtmsttype) :: this !< GwtMstType object
269  integer, intent(in) :: nodes !< number of nodes
270  real(DP), intent(in), dimension(nodes) :: cold !< concentration at end of last time step
271  real(DP), intent(in), dimension(nodes) :: cnew !< concentration at end of this time step
272  integer(I4B), intent(in) :: nja !< number of GWT connections
273  class(matrixbasetype), pointer :: matrix_sln !< solution coefficient matrix
274  integer(I4B), intent(in), dimension(nja) :: idxglo !< mapping vector for model (local) to solution (global)
275  real(DP), intent(inout), dimension(nodes) :: rhs !< right-hand side vector for model
276  integer(I4B), intent(in) :: kiter !< solution outer iteration number
277  ! -- local
278  integer(I4B) :: n, idiag
279  real(DP) :: hhcof, rrhs
280  real(DP) :: swtpdt
281  real(DP) :: vcell
282  real(DP) :: decay_rate
283  !
284  ! -- loop through and calculate decay contribution to hcof and rhs
285  do n = 1, this%dis%nodes
286  !
287  ! -- skip if transport inactive
288  if (this%ibound(n) <= 0) cycle
289  !
290  ! -- calculate new and old water volumes
291  vcell = this%dis%area(n) * (this%dis%top(n) - this%dis%bot(n))
292  swtpdt = this%fmi%gwfsat(n)
293  !
294  ! -- add decay rate terms to accumulators
295  idiag = this%dis%con%ia(n)
296  select case (this%idcy)
297  case (decay_first_order)
298  !
299  ! -- first order decay rate is a function of concentration, so add
300  ! to left hand side
301  if (cnew(n) > dzero) then
302  hhcof = -this%decay(n) * vcell * swtpdt * this%thetam(n)
303  call matrix_sln%add_value_pos(idxglo(idiag), hhcof)
304  end if
305  case (decay_zero_order)
306  !
307  ! -- Call function to get zero-order decay rate, which may be changed
308  ! from the user-specified rate to prevent negative concentrations
309  decay_rate = get_zero_order_decay(this%decay(n), this%decaylast(n), &
310  kiter, cold(n), cnew(n), delt)
311  this%decaylast(n) = decay_rate
312  rrhs = decay_rate * vcell * swtpdt * this%thetam(n)
313  rhs(n) = rhs(n) + rrhs
314  end select
315  !
316  end do
317  end subroutine mst_fc_dcy
318 
319  !> @ brief Fill sorption coefficient method for package
320  !!
321  !! Method to calculate and fill sorption coefficients for the package.
322  !<
323  subroutine mst_fc_srb(this, nodes, cold, nja, matrix_sln, idxglo, rhs, &
324  cnew)
325  ! -- modules
326  use tdismodule, only: delt
327  ! -- dummy
328  class(gwtmsttype) :: this !< GwtMstType object
329  integer, intent(in) :: nodes !< number of nodes
330  real(DP), intent(in), dimension(nodes) :: cold !< concentration at end of last time step
331  integer(I4B), intent(in) :: nja !< number of GWT connections
332  class(matrixbasetype), pointer :: matrix_sln !< solution coefficient matrix
333  integer(I4B), intent(in), dimension(nja) :: idxglo !< mapping vector for model (local) to solution (global)
334  real(DP), intent(inout), dimension(nodes) :: rhs !< right-hand side vector for model
335  real(DP), intent(in), dimension(nodes) :: cnew !< concentration at end of this time step
336  ! -- local
337  integer(I4B) :: n, idiag
338  real(DP) :: tled
339  real(DP) :: hhcof, rrhs
340  real(DP) :: vcell
341  real(DP) :: volfracm
342  real(DP) :: rhobm
343  real(DP), dimension(nodes) :: c_half
344  real(DP) :: cbar_derv_half
345  real(DP) :: cbar_new, cbar_half, cbar_old
346  real(DP) :: sat_new, sat_half, sat_old
347  !
348  ! -- set variables
349  tled = done / delt
350  c_half = 0.5_dp * (cold + cnew)
351  !
352  ! -- loop through and calculate sorption contribution to hcof and rhs
353  do n = 1, this%dis%nodes
354  !
355  ! -- skip if transport inactive
356  if (this%ibound(n) <= 0) cycle
357  !
358  ! -- assign variables
359  vcell = this%dis%area(n) * (this%dis%top(n) - this%dis%bot(n))
360  volfracm = this%get_volfracm(n)
361  rhobm = this%bulk_density(n)
362  sat_new = this%fmi%gwfsat(n)
363  sat_old = this%fmi%gwfsatold(n, delt)
364 
365  ! -- Midpoint formulation using average values
366  cbar_new = this%isotherm%value(cnew, n)
367  cbar_old = this%isotherm%value(cold, n)
368  cbar_half = 0.5 * (cbar_new + cbar_old)
369 
370  sat_half = 0.5 * (sat_new + sat_old)
371  cbar_derv_half = this%isotherm%derivative(c_half, n)
372 
373  hhcof = -volfracm * rhobm * cbar_derv_half * sat_half * vcell * tled
374  idiag = this%dis%con%ia(n)
375  call matrix_sln%add_value_pos(idxglo(idiag), hhcof)
376 
377  rrhs = -volfracm * rhobm * cbar_derv_half * sat_half * cold(n) &
378  * vcell * tled
379  rhs(n) = rhs(n) + rrhs
380 
381  rrhs = volfracm * rhobm * cbar_half * (sat_new - sat_old) * vcell * tled
382  rhs(n) = rhs(n) + rrhs
383 
384  end do
385  end subroutine mst_fc_srb
386 
387  !> @ brief Fill sorption-decay coefficient method for package
388  !!
389  !! Method to calculate and fill sorption-decay coefficients for the package.
390  !<
391  subroutine mst_fc_dcy_srb(this, nodes, cold, nja, matrix_sln, idxglo, &
392  rhs, cnew, kiter)
393  ! -- modules
394  use tdismodule, only: delt
395  ! -- dummy
396  class(gwtmsttype) :: this !< GwtMstType object
397  integer, intent(in) :: nodes !< number of nodes
398  real(DP), intent(in), dimension(nodes) :: cold !< concentration at end of last time step
399  integer(I4B), intent(in) :: nja !< number of GWT connections
400  class(matrixbasetype), pointer :: matrix_sln !< solution coefficient matrix
401  integer(I4B), intent(in), dimension(nja) :: idxglo !< mapping vector for model (local) to solution (global)
402  real(DP), intent(inout), dimension(nodes) :: rhs !< right-hand side vector for model
403  real(DP), intent(in), dimension(nodes) :: cnew !< concentration at end of this time step
404  integer(I4B), intent(in) :: kiter !< solution outer iteration number
405  ! -- local
406  integer(I4B) :: n, idiag
407  real(DP) :: hhcof, rrhs
408  real(DP) :: vcell
409  real(DP) :: swnew
410  real(DP) :: distcoef
411  real(DP) :: volfracm
412  real(DP) :: rhobm
413  real(DP) :: term
414  real(DP) :: csrb
415  real(DP) :: decay_rate
416  real(DP) :: csrbold
417  real(DP) :: csrbnew
418  !
419  ! -- loop through and calculate sorption contribution to hcof and rhs
420  do n = 1, this%dis%nodes
421  !
422  ! -- skip if transport inactive
423  if (this%ibound(n) <= 0) cycle
424  !
425  ! -- set variables
426  hhcof = dzero
427  rrhs = dzero
428  vcell = this%dis%area(n) * (this%dis%top(n) - this%dis%bot(n))
429  swnew = this%fmi%gwfsat(n)
430  distcoef = this%distcoef(n)
431  idiag = this%dis%con%ia(n)
432  volfracm = this%get_volfracm(n)
433  rhobm = this%bulk_density(n)
434  term = this%decay_sorbed(n) * volfracm * rhobm * swnew * vcell
435  !
436  ! -- add sorbed mass decay rate terms to accumulators
437  select case (this%idcy)
438  case (decay_first_order)
439  select case (this%isrb)
440  case (sorption_linear)
441  !
442  ! -- first order decay rate is a function of concentration, so add
443  ! to left hand side
444  if (cnew(n) > dzero) then
445  hhcof = -term * distcoef
446  end if
447  case (sorption_freund)
448  !
449  ! -- nonlinear Freundlich sorption, so add to RHS
450  csrb = this%isotherm%value(cnew, n)
451  rrhs = term * csrb
452  case (sorption_lang)
453  !
454  ! -- nonlinear Lanmuir sorption, so add to RHS
455  csrb = this%isotherm%value(cnew, n)
456  rrhs = term * csrb
457  end select
458  case (decay_zero_order)
459  !
460  ! -- call function to get zero-order decay rate, which may be changed
461  ! from the user-specified rate to prevent negative concentrations
462  if (distcoef > dzero) then
463  csrbold = this%isotherm%value(cold, n)
464  csrbnew = this%isotherm%value(cnew, n)
465  !
466  decay_rate = get_zero_order_decay(this%decay_sorbed(n), &
467  this%decayslast(n), &
468  kiter, csrbold, csrbnew, delt)
469  this%decayslast(n) = decay_rate
470  rrhs = decay_rate * volfracm * rhobm * swnew * vcell
471  end if
472  end select
473  !
474  ! -- Add hhcof to diagonal and rrhs to right-hand side
475  call matrix_sln%add_value_pos(idxglo(idiag), hhcof)
476  rhs(n) = rhs(n) + rrhs
477  !
478  end do
479  end subroutine mst_fc_dcy_srb
480 
481  !> @ brief Calculate flows for package
482  !!
483  !! Method to calculate flows for the package.
484  !<
485  subroutine mst_cq(this, nodes, cnew, cold, flowja)
486  ! -- dummy
487  class(gwtmsttype) :: this !< GwtMstType object
488  integer(I4B), intent(in) :: nodes !< number of nodes
489  real(DP), intent(in), dimension(nodes) :: cnew !< concentration at end of this time step
490  real(DP), intent(in), dimension(nodes) :: cold !< concentration at end of last time step
491  real(DP), dimension(:), contiguous, intent(inout) :: flowja !< flow between two connected control volumes
492  !
493  ! - storage
494  call this%mst_cq_sto(nodes, cnew, cold, flowja)
495  !
496  ! -- decay
497  if (this%idcy /= decay_off) then
498  call this%mst_cq_dcy(nodes, cnew, cold, flowja)
499  end if
500  !
501  ! -- sorption
502  if (this%isrb /= sorption_off) then
503  call this%mst_cq_srb(nodes, cnew, cold, flowja)
504  end if
505  !
506  ! -- decay sorbed
507  if (this%isrb /= sorption_off .and. this%idcy /= decay_off) then
508  call this%mst_cq_dcy_srb(nodes, cnew, cold, flowja)
509  end if
510  !
511  ! -- calculate csrb
512  if (this%isrb /= sorption_off) then
513  call this%mst_calc_csrb(cnew)
514  end if
515  end subroutine mst_cq
516 
517  !> @ brief Calculate storage terms for package
518  !!
519  !! Method to calculate storage terms for the package.
520  !<
521  subroutine mst_cq_sto(this, nodes, cnew, cold, flowja)
522  ! -- modules
523  use tdismodule, only: delt
524  ! -- dummy
525  class(gwtmsttype) :: this !< GwtMstType object
526  integer(I4B), intent(in) :: nodes !< number of nodes
527  real(DP), intent(in), dimension(nodes) :: cnew !< concentration at end of this time step
528  real(DP), intent(in), dimension(nodes) :: cold !< concentration at end of last time step
529  real(DP), dimension(:), contiguous, intent(inout) :: flowja !< flow between two connected control volumes
530  ! -- local
531  integer(I4B) :: n
532  integer(I4B) :: idiag
533  real(DP) :: rate
534  real(DP) :: tled
535  real(DP) :: vnew, vold
536  real(DP) :: hhcof, rrhs
537  !
538  ! -- initialize
539  tled = done / delt
540  !
541  ! -- Calculate storage change
542  do n = 1, nodes
543  this%ratesto(n) = dzero
544  !
545  ! -- skip if transport inactive
546  if (this%ibound(n) <= 0) cycle
547  !
548  ! -- calculate new and old water volumes
549  vnew = this%dis%area(n) * (this%dis%top(n) - this%dis%bot(n)) * &
550  this%fmi%gwfsat(n) * this%thetam(n)
551  vold = vnew
552  if (this%fmi%igwfstrgss /= 0) vold = vold + this%fmi%gwfstrgss(n) * delt
553  if (this%fmi%igwfstrgsy /= 0) vold = vold + this%fmi%gwfstrgsy(n) * delt
554  !
555  ! -- calculate rate
556  hhcof = -vnew * tled
557  rrhs = -vold * tled * cold(n)
558  rate = hhcof * cnew(n) - rrhs
559  this%ratesto(n) = rate
560  idiag = this%dis%con%ia(n)
561  flowja(idiag) = flowja(idiag) + rate
562  end do
563  end subroutine mst_cq_sto
564 
565  !> @ brief Calculate decay terms for package
566  !!
567  !! Method to calculate decay terms for the package.
568  !<
569  subroutine mst_cq_dcy(this, nodes, cnew, cold, flowja)
570  ! -- modules
571  use tdismodule, only: delt
572  ! -- dummy
573  class(gwtmsttype) :: this !< GwtMstType object
574  integer(I4B), intent(in) :: nodes !< number of nodes
575  real(DP), intent(in), dimension(nodes) :: cnew !< concentration at end of this time step
576  real(DP), intent(in), dimension(nodes) :: cold !< concentration at end of last time step
577  real(DP), dimension(:), contiguous, intent(inout) :: flowja !< flow between two connected control volumes
578  ! -- local
579  integer(I4B) :: n
580  integer(I4B) :: idiag
581  real(DP) :: rate
582  real(DP) :: swtpdt
583  real(DP) :: hhcof, rrhs
584  real(DP) :: vcell
585  real(DP) :: decay_rate
586  !
587  ! -- initialize
588  !
589  ! -- Calculate decay change
590  do n = 1, nodes
591  !
592  ! -- skip if transport inactive
593  this%ratedcy(n) = dzero
594  if (this%ibound(n) <= 0) cycle
595  !
596  ! -- calculate new and old water volumes
597  vcell = this%dis%area(n) * (this%dis%top(n) - this%dis%bot(n))
598  swtpdt = this%fmi%gwfsat(n)
599  !
600  ! -- calculate decay gains and losses
601  rate = dzero
602  hhcof = dzero
603  rrhs = dzero
604  if (this%idcy == decay_first_order) then
605  if (cnew(n) > dzero) then
606  hhcof = -this%decay(n) * vcell * swtpdt * this%thetam(n)
607  end if
608  elseif (this%idcy == decay_zero_order) then
609  decay_rate = get_zero_order_decay(this%decay(n), this%decaylast(n), &
610  0, cold(n), cnew(n), delt)
611  rrhs = decay_rate * vcell * swtpdt * this%thetam(n)
612  end if
613  rate = hhcof * cnew(n) - rrhs
614  this%ratedcy(n) = rate
615  idiag = this%dis%con%ia(n)
616  flowja(idiag) = flowja(idiag) + rate
617  !
618  end do
619  end subroutine mst_cq_dcy
620 
621  !> @ brief Calculate sorption terms for package
622  !!
623  !! Method to calculate sorption terms for the package.
624  !<
625  subroutine mst_cq_srb(this, nodes, cnew, cold, flowja)
626  ! -- modules
627  use tdismodule, only: delt
628  ! -- dummy
629  class(gwtmsttype) :: this !< GwtMstType object
630  integer(I4B), intent(in) :: nodes !< number of nodes
631  real(DP), intent(in), dimension(nodes) :: cnew !< concentration at end of this time step
632  real(DP), intent(in), dimension(nodes) :: cold !< concentration at end of last time step
633  real(DP), dimension(:), contiguous, intent(inout) :: flowja !< flow between two connected control volumes
634  ! -- local
635  integer(I4B) :: n
636  integer(I4B) :: idiag
637  real(DP) :: rate
638  real(DP) :: tled
639  real(DP) :: vcell
640  real(DP) :: volfracm
641  real(DP) :: rhobm
642  real(DP), dimension(nodes) :: c_half
643  real(DP) :: cbar_derv_half
644  real(DP) :: cbar_new, cbar_half, cbar_old
645  real(DP) :: sat_new, sat_half, sat_old
646  !
647  ! -- initialize
648  tled = done / delt
649  c_half = 0.5_dp * (cold + cnew)
650  !
651  ! -- Calculate sorption change
652  do n = 1, nodes
653  !
654  ! -- initialize rates
655  this%ratesrb(n) = dzero
656  rate = 0.0_dp
657  !
658  ! -- skip if transport inactive
659  if (this%ibound(n) <= 0) cycle
660  !
661  ! -- assign variables
662  vcell = this%dis%area(n) * (this%dis%top(n) - this%dis%bot(n))
663 
664  rhobm = this%bulk_density(n)
665  volfracm = this%get_volfracm(n)
666  sat_new = this%fmi%gwfsat(n)
667  sat_old = this%fmi%gwfsatold(n, delt)
668 
669  ! -- Midpoint formulation using average values
670  cbar_new = this%isotherm%value(cnew, n)
671  cbar_old = this%isotherm%value(cold, n)
672  cbar_half = 0.5 * (cbar_new + cbar_old)
673 
674  sat_half = 0.5 * (sat_new + sat_old)
675  cbar_derv_half = this%isotherm%derivative(c_half, n)
676 
677  rate = -volfracm * rhobm * cbar_derv_half * sat_half * cnew(n) &
678  * vcell * tled
679  rate = rate + volfracm * rhobm * cbar_derv_half * sat_half * cold(n) &
680  * vcell * tled
681  rate = rate - volfracm * rhobm * cbar_half * (sat_new - sat_old) &
682  * vcell * tled
683 
684  this%ratesrb(n) = rate
685  idiag = this%dis%con%ia(n)
686  flowja(idiag) = flowja(idiag) + rate
687  !
688  end do
689  end subroutine mst_cq_srb
690 
691  !> @ brief Calculate decay-sorption terms for package
692  !!
693  !! Method to calculate decay-sorption terms for the package.
694  !<
695  subroutine mst_cq_dcy_srb(this, nodes, cnew, cold, flowja)
696  ! -- modules
697  use tdismodule, only: delt
698  ! -- dummy
699  class(gwtmsttype) :: this !< GwtMstType object
700  integer(I4B), intent(in) :: nodes !< number of nodes
701  real(DP), intent(in), dimension(nodes) :: cnew !< concentration at end of this time step
702  real(DP), intent(in), dimension(nodes) :: cold !< concentration at end of last time step
703  real(DP), dimension(:), contiguous, intent(inout) :: flowja !< flow between two connected control volumes
704  ! -- local
705  integer(I4B) :: n
706  integer(I4B) :: idiag
707  real(DP) :: rate
708  real(DP) :: hhcof, rrhs
709  real(DP) :: vcell
710  real(DP) :: swnew
711  real(DP) :: distcoef
712  real(DP) :: volfracm
713  real(DP) :: rhobm
714  real(DP) :: term
715  real(DP) :: csrb
716  real(DP) :: csrbnew
717  real(DP) :: csrbold
718  real(DP) :: decay_rate
719  !
720  ! -- Calculate sorbed decay change
721  ! This routine will only be called if sorption and decay are active
722  do n = 1, nodes
723  !
724  ! -- initialize rates
725  this%ratedcys(n) = dzero
726  !
727  ! -- skip if transport inactive
728  if (this%ibound(n) <= 0) cycle
729  !
730  ! -- set variables
731  hhcof = dzero
732  rrhs = dzero
733  vcell = this%dis%area(n) * (this%dis%top(n) - this%dis%bot(n))
734  swnew = this%fmi%gwfsat(n)
735  distcoef = this%distcoef(n)
736  volfracm = this%get_volfracm(n)
737  rhobm = this%bulk_density(n)
738  term = this%decay_sorbed(n) * volfracm * rhobm * swnew * vcell
739  !
740  ! -- add sorbed mass decay rate terms to accumulators
741  select case (this%idcy)
742  case (decay_first_order)
743 
744  !
745  select case (this%isrb)
746  case (sorption_linear)
747  !
748  ! -- first order decay rate is a function of concentration, so add
749  ! to left hand side
750  if (cnew(n) > dzero) then
751  hhcof = -term * distcoef
752  end if
753  case (sorption_freund)
754  !
755  ! -- nonlinear Freundlich sorption, so add to RHS
756  csrb = this%isotherm%value(cnew, n)
757  rrhs = term * csrb
758  case (sorption_lang)
759  !
760  ! -- nonlinear Lanmuir sorption, so add to RHS
761  csrb = this%isotherm%value(cnew, n)
762  rrhs = term * csrb
763  end select
764  case (decay_zero_order)
765  !
766  ! -- Call function to get zero-order decay rate, which may be changed
767  ! from the user-specified rate to prevent negative concentrations
768  if (distcoef > dzero) then
769  csrbold = this%isotherm%value(cold, n)
770  csrbnew = this%isotherm%value(cnew, n)
771 
772  decay_rate = get_zero_order_decay(this%decay_sorbed(n), &
773  this%decayslast(n), &
774  0, csrbold, csrbnew, delt)
775  rrhs = decay_rate * volfracm * rhobm * swnew * vcell
776  end if
777  end select
778  !
779  ! -- calculate rate
780  rate = hhcof * cnew(n) - rrhs
781  this%ratedcys(n) = rate
782  idiag = this%dis%con%ia(n)
783  flowja(idiag) = flowja(idiag) + rate
784  !
785  end do
786  end subroutine mst_cq_dcy_srb
787 
788  !> @ brief Calculate sorbed concentration
789  !<
790  subroutine mst_calc_csrb(this, cnew)
791  ! -- dummy
792  class(gwtmsttype) :: this !< GwtMstType object
793  real(DP), intent(in), dimension(:) :: cnew !< concentration at end of this time step
794  ! -- local
795  integer(I4B) :: n
796  real(DP) :: csrb
797 
798  ! Calculate sorbed concentration
799  do n = 1, size(cnew)
800  csrb = dzero
801  if (this%ibound(n) > 0 .and. this%isrb /= sorption_off) then
802  csrb = this%isotherm%value(cnew, n)
803  end if
804  this%csrb(n) = csrb
805  end do
806 
807  end subroutine mst_calc_csrb
808 
809  !> @ brief Calculate budget terms for package
810  !<
811  subroutine mst_bd(this, isuppress_output, model_budget)
812  ! -- modules
813  use tdismodule, only: delt
815  ! -- dummy
816  class(gwtmsttype) :: this !< GwtMstType object
817  integer(I4B), intent(in) :: isuppress_output !< flag to suppress output
818  type(budgettype), intent(inout) :: model_budget !< model budget object
819  ! -- local
820  real(DP) :: rin
821  real(DP) :: rout
822  !
823  ! -- sto
824  call rate_accumulator(this%ratesto, rin, rout)
825  call model_budget%addentry(rin, rout, delt, budtxt(1), &
826  isuppress_output, rowlabel=this%packName)
827  !
828  ! -- dcy
829  if (this%idcy /= decay_off) then
830  call rate_accumulator(this%ratedcy, rin, rout)
831  call model_budget%addentry(rin, rout, delt, budtxt(2), &
832  isuppress_output, rowlabel=this%packName)
833  end if
834  !
835  ! -- srb
836  if (this%isrb /= sorption_off) then
837  call rate_accumulator(this%ratesrb, rin, rout)
838  call model_budget%addentry(rin, rout, delt, budtxt(3), &
839  isuppress_output, rowlabel=this%packName)
840  end if
841  !
842  ! -- srb dcy
843  if (this%isrb /= sorption_off .and. this%idcy /= decay_off) then
844  call rate_accumulator(this%ratedcys, rin, rout)
845  call model_budget%addentry(rin, rout, delt, budtxt(4), &
846  isuppress_output, rowlabel=this%packName)
847  end if
848  end subroutine mst_bd
849 
850  !> @ brief Output flow terms for package
851  !!
852  !! Method to output terms for the package.
853  !<
854  subroutine mst_ot_flow(this, icbcfl, icbcun)
855  ! -- dummy
856  class(gwtmsttype) :: this !< GwtMstType object
857  integer(I4B), intent(in) :: icbcfl !< flag and unit number for cell-by-cell output
858  integer(I4B), intent(in) :: icbcun !< flag indication if cell-by-cell data should be saved
859  ! -- local
860  integer(I4B) :: ibinun
861  integer(I4B) :: iprint, nvaluesp, nwidthp
862  character(len=1) :: cdatafmp = ' ', editdesc = ' '
863  real(DP) :: dinact
864  !
865  ! -- Set unit number for binary output
866  if (this%ipakcb < 0) then
867  ibinun = icbcun
868  elseif (this%ipakcb == 0) then
869  ibinun = 0
870  else
871  ibinun = this%ipakcb
872  end if
873  if (icbcfl == 0) ibinun = 0
874  !
875  ! -- Record the storage rate if requested
876  if (ibinun /= 0) then
877  iprint = 0
878  dinact = dzero
879  !
880  ! -- sto
881  call this%dis%record_array(this%ratesto, this%iout, iprint, -ibinun, &
882  budtxt(1), cdatafmp, nvaluesp, &
883  nwidthp, editdesc, dinact)
884  !
885  ! -- dcy
886  if (this%idcy /= decay_off) &
887  call this%dis%record_array(this%ratedcy, this%iout, iprint, -ibinun, &
888  budtxt(2), cdatafmp, nvaluesp, &
889  nwidthp, editdesc, dinact)
890  !
891  ! -- srb
892  if (this%isrb /= sorption_off) &
893  call this%dis%record_array(this%ratesrb, this%iout, iprint, -ibinun, &
894  budtxt(3), cdatafmp, nvaluesp, &
895  nwidthp, editdesc, dinact)
896  !
897  ! -- dcy srb
898  if (this%isrb /= sorption_off .and. this%idcy /= decay_off) &
899  call this%dis%record_array(this%ratedcys, this%iout, iprint, -ibinun, &
900  budtxt(4), cdatafmp, nvaluesp, &
901  nwidthp, editdesc, dinact)
902  end if
903  end subroutine mst_ot_flow
904 
905  !> @brief Save sorbate concentration array to binary file
906  !<
907  subroutine mst_ot_dv(this, idvsave)
908  ! -- dummy
909  class(gwtmsttype) :: this
910  integer(I4B), intent(in) :: idvsave
911  ! -- local
912  character(len=1) :: cdatafmp = ' ', editdesc = ' '
913  integer(I4B) :: ibinun
914  integer(I4B) :: iprint
915  integer(I4B) :: nvaluesp
916  integer(I4B) :: nwidthp
917  real(DP) :: dinact
918 
919  ! Set unit number for sorbate output
920  if (this%ioutsorbate /= 0) then
921  ibinun = 1
922  else
923  ibinun = 0
924  end if
925  if (idvsave == 0) ibinun = 0
926 
927  ! save sorbate concentration array
928  if (ibinun /= 0) then
929  iprint = 0
930  dinact = dhnoflo
931  if (this%ioutsorbate /= 0) then
932  ibinun = this%ioutsorbate
933  call this%dis%record_array(this%csrb, this%iout, iprint, ibinun, &
934  ' SORBATE', cdatafmp, nvaluesp, &
935  nwidthp, editdesc, dinact)
936  end if
937  end if
938 
939  end subroutine mst_ot_dv
940 
941  !> @ brief Deallocate
942  !!
943  !! Method to deallocate memory for the package.
944  !<
945  subroutine mst_da(this)
946  ! -- modules
948  ! -- dummy
949  class(gwtmsttype) :: this !< GwtMstType object
950  !
951  ! -- Deallocate arrays if package was active
952  if (this%inunit > 0) then
953  call mem_deallocate(this%porosity)
954  call mem_deallocate(this%thetam)
955  call mem_deallocate(this%volfracim)
956  call mem_deallocate(this%ratesto)
957  call mem_deallocate(this%idcy)
958  call mem_deallocate(this%decay)
959  call mem_deallocate(this%decay_sorbed)
960  call mem_deallocate(this%ratedcy)
961  call mem_deallocate(this%decaylast)
962  call mem_deallocate(this%decayslast)
963  call mem_deallocate(this%isrb)
964  call mem_deallocate(this%ioutsorbate)
965  call mem_deallocate(this%bulk_density)
966  call mem_deallocate(this%distcoef)
967  call mem_deallocate(this%sp2)
968  call mem_deallocate(this%ratesrb)
969  call mem_deallocate(this%csrb)
970  call mem_deallocate(this%ratedcys)
971  this%ibound => null()
972  this%fmi => null()
973  end if
974  !
975  ! -- Scalars
976  !
977  ! -- Objects
978  if (associated(this%isotherm)) then
979  deallocate (this%isotherm)
980  nullify (this%isotherm)
981  end if
982  !
983  ! -- deallocate parent
984  call this%NumericalPackageType%da()
985  end subroutine mst_da
986 
987  !> @ brief Allocate scalar variables for package
988  !!
989  !! Method to allocate scalar variables for the package.
990  !<
991  subroutine allocate_scalars(this)
992  ! -- modules
994  ! -- dummy
995  class(gwtmsttype) :: this !< GwtMstType object
996  !
997  ! -- Allocate scalars in NumericalPackageType
998  call this%NumericalPackageType%allocate_scalars()
999  !
1000  ! -- Allocate
1001  call mem_allocate(this%isrb, 'ISRB', this%memoryPath)
1002  call mem_allocate(this%ioutsorbate, 'IOUTSORBATE', this%memoryPath)
1003  call mem_allocate(this%idcy, 'IDCY', this%memoryPath)
1004  !
1005  ! -- Initialize
1006  this%isrb = izero
1007  this%ioutsorbate = 0
1008  this%idcy = izero
1009  end subroutine allocate_scalars
1010 
1011  !> @ brief Allocate arrays for package
1012  !!
1013  !! Method to allocate arrays for the package.
1014  !<
1015  subroutine allocate_arrays(this, nodes)
1016  ! -- modules
1018  use constantsmodule, only: dzero
1019  ! -- dummy
1020  class(gwtmsttype) :: this !< GwtMstType object
1021  integer(I4B), intent(in) :: nodes !< number of nodes
1022  ! -- local
1023  integer(I4B) :: n
1024  !
1025  ! -- Allocate
1026  ! -- sto
1027  call mem_allocate(this%porosity, nodes, 'POROSITY', this%memoryPath)
1028  call mem_allocate(this%thetam, nodes, 'THETAM', this%memoryPath)
1029  call mem_allocate(this%volfracim, nodes, 'VOLFRACIM', this%memoryPath)
1030  call mem_allocate(this%ratesto, nodes, 'RATESTO', this%memoryPath)
1031  !
1032  ! -- dcy
1033  if (this%idcy == decay_off) then
1034  call mem_allocate(this%ratedcy, 1, 'RATEDCY', this%memoryPath)
1035  call mem_allocate(this%decay, 1, 'DECAY', this%memoryPath)
1036  call mem_allocate(this%decaylast, 1, 'DECAYLAST', this%memoryPath)
1037  else
1038  call mem_allocate(this%ratedcy, this%dis%nodes, 'RATEDCY', this%memoryPath)
1039  call mem_allocate(this%decay, nodes, 'DECAY', this%memoryPath)
1040  call mem_allocate(this%decaylast, nodes, 'DECAYLAST', this%memoryPath)
1041  end if
1042  if (this%idcy /= decay_off .and. this%isrb /= sorption_off) then
1043  call mem_allocate(this%ratedcys, this%dis%nodes, 'RATEDCYS', &
1044  this%memoryPath)
1045  call mem_allocate(this%decayslast, this%dis%nodes, 'DECAYSLAST', &
1046  this%memoryPath)
1047  else
1048  call mem_allocate(this%ratedcys, 1, 'RATEDCYS', this%memoryPath)
1049  call mem_allocate(this%decayslast, 1, 'DECAYSLAST', this%memoryPath)
1050  end if
1051  call mem_allocate(this%decay_sorbed, 1, 'DECAY_SORBED', &
1052  this%memoryPath)
1053  !
1054  ! -- srb
1055  if (this%isrb == sorption_off) then
1056  call mem_allocate(this%bulk_density, 1, 'BULK_DENSITY', this%memoryPath)
1057  call mem_allocate(this%sp2, 1, 'SP2', this%memoryPath)
1058  call mem_allocate(this%distcoef, 1, 'DISTCOEF', this%memoryPath)
1059  call mem_allocate(this%ratesrb, 1, 'RATESRB', this%memoryPath)
1060  call mem_allocate(this%csrb, 1, 'CSRB', this%memoryPath)
1061  else
1062  call mem_allocate(this%bulk_density, nodes, 'BULK_DENSITY', this%memoryPath)
1063  call mem_allocate(this%distcoef, nodes, 'DISTCOEF', this%memoryPath)
1064  call mem_allocate(this%ratesrb, nodes, 'RATESRB', this%memoryPath)
1065  call mem_allocate(this%csrb, nodes, 'CSRB', this%memoryPath)
1066  if (this%isrb == sorption_linear) then
1067  call mem_allocate(this%sp2, 1, 'SP2', this%memoryPath)
1068  else
1069  call mem_allocate(this%sp2, nodes, 'SP2', this%memoryPath)
1070  end if
1071  end if
1072  !
1073  ! -- Initialize
1074  do n = 1, nodes
1075  this%porosity(n) = dzero
1076  this%thetam(n) = dzero
1077  this%volfracim(n) = dzero
1078  this%ratesto(n) = dzero
1079  end do
1080  do n = 1, size(this%decay)
1081  this%decay(n) = dzero
1082  this%ratedcy(n) = dzero
1083  this%decaylast(n) = dzero
1084  end do
1085  do n = 1, size(this%bulk_density)
1086  this%bulk_density(n) = dzero
1087  this%distcoef(n) = dzero
1088  this%ratesrb(n) = dzero
1089  this%csrb(n) = dzero
1090  end do
1091  do n = 1, size(this%sp2)
1092  this%sp2(n) = dzero
1093  end do
1094  do n = 1, size(this%ratedcys)
1095  this%ratedcys(n) = dzero
1096  this%decayslast(n) = dzero
1097  end do
1098  end subroutine allocate_arrays
1099 
1100  !> @ brief Source options for package
1101  !!
1102  !! Method to source options for the package.
1103  !<
1104  subroutine source_options(this)
1105  ! -- modules
1106  use constantsmodule, only: lenvarname
1107  use openspecmodule, only: access, form
1108  use inputoutputmodule, only: getunit, openfile
1111  ! -- dummy
1112  class(gwtmsttype) :: this
1113  ! -- locals
1114  type(gwtmstparamfoundtype) :: found
1115  character(len=LENVARNAME), dimension(3) :: sorption_method = &
1116  &[character(len=LENVARNAME) :: 'LINEAR', 'FREUNDLICH', 'LANGMUIR']
1117  character(len=linelength) :: fname
1118  !
1119  ! -- update defaults with memory sourced values
1120  call mem_set_value(this%ipakcb, 'SAVE_FLOWS', this%input_mempath, &
1121  found%save_flows)
1122  call mem_set_value(this%idcy, 'ORDER1_DECAY', this%input_mempath, &
1123  found%order1_decay)
1124  call mem_set_value(this%idcy, 'ORDER0_DECAY', this%input_mempath, &
1125  found%order0_decay)
1126  call mem_set_value(this%isrb, 'SORPTION', this%input_mempath, &
1127  sorption_method, found%sorption)
1128  call mem_set_value(fname, 'SORBATEFILE', this%input_mempath, &
1129  found%sorbatefile)
1130 
1131  ! -- found side effects
1132  if (found%save_flows) this%ipakcb = -1
1133  if (found%order1_decay) this%idcy = decay_first_order
1134  if (found%order0_decay) this%idcy = decay_zero_order
1135  if (found%sorption) then
1136  if (this%isrb == izero) then
1137  call store_error('Unknown sorption type was specified. &
1138  &Sorption must be specified as LINEAR, &
1139  &FREUNDLICH, or LANGMUIR.')
1140  call store_error_filename(this%input_fname)
1141  end if
1142  end if
1143  if (found%sorbatefile) then
1144  this%ioutsorbate = getunit()
1145  call openfile(this%ioutsorbate, this%iout, fname, 'DATA(BINARY)', &
1146  form, access, 'REPLACE', mode_opt=mnormal)
1147  end if
1148  !
1149  ! -- log options
1150  if (this%iout > 0) then
1151  call this%log_options(found, fname)
1152  end if
1153  end subroutine source_options
1154 
1155  !> @brief Log user options to list file
1156  !<
1157  subroutine log_options(this, found, sorbate_fname)
1159  class(gwtmsttype) :: this
1160  type(gwtmstparamfoundtype), intent(in) :: found
1161  character(len=*), intent(in) :: sorbate_fname
1162  ! -- formats
1163  character(len=*), parameter :: fmtisvflow = &
1164  "(4x,'CELL-BY-CELL FLOW INFORMATION WILL BE SAVED TO BINARY FILE &
1165  &WHENEVER ICBCFL IS NOT ZERO.')"
1166  character(len=*), parameter :: fmtlinear = &
1167  &"(4x,'LINEAR SORPTION IS ACTIVE. ')"
1168  character(len=*), parameter :: fmtfreundlich = &
1169  &"(4x,'FREUNDLICH SORPTION IS ACTIVE. ')"
1170  character(len=*), parameter :: fmtlangmuir = &
1171  &"(4x,'LANGMUIR SORPTION IS ACTIVE. ')"
1172  character(len=*), parameter :: fmtidcy1 = &
1173  &"(4x,'FIRST-ORDER DECAY IS ACTIVE. ')"
1174  character(len=*), parameter :: fmtidcy2 = &
1175  &"(4x,'ZERO-ORDER DECAY IS ACTIVE. ')"
1176  character(len=*), parameter :: fmtfileout = &
1177  "(4x,'MST ',1x,a,1x,' WILL BE SAVED TO FILE: ',a,/4x,&
1178  &'OPENED ON UNIT: ',I7)"
1179 
1180  write (this%iout, '(1x,a)') 'PROCESSING MOBILE STORAGE AND TRANSFER OPTIONS'
1181  if (found%save_flows) then
1182  write (this%iout, fmtisvflow)
1183  end if
1184  if (found%order1_decay) then
1185  write (this%iout, fmtidcy1)
1186  end if
1187  if (found%order0_decay) then
1188  write (this%iout, fmtidcy2)
1189  end if
1190  if (found%sorption) then
1191  select case (this%isrb)
1192  case (sorption_linear)
1193  write (this%iout, fmtlinear)
1194  case (sorption_freund)
1195  write (this%iout, fmtfreundlich)
1196  case (sorption_lang)
1197  write (this%iout, fmtlangmuir)
1198  end select
1199  end if
1200  if (found%sorbatefile) then
1201  write (this%iout, fmtfileout) &
1202  'SORBATE', sorbate_fname, this%ioutsorbate
1203  end if
1204  write (this%iout, '(1x,a)') 'END OF MOBILE STORAGE AND TRANSFER OPTIONS'
1205  end subroutine log_options
1206 
1207  !> @ brief Source data for package
1208  !!
1209  !! Method to source data for the package.
1210  !<
1211  subroutine source_data(this)
1212  ! -- modules
1216  ! -- dummy
1217  class(gwtmsttype) :: this
1218  ! -- locals
1219  character(len=LINELENGTH) :: errmsg
1220  type(gwtmstparamfoundtype) :: found
1221  integer(I4B) :: n, asize
1222  integer(I4B), dimension(:), pointer, contiguous :: map
1223  !
1224  ! -- set map to convert user input data into reduced data
1225  map => null()
1226  if (this%dis%nodes < this%dis%nodesuser) map => this%dis%nodeuser
1227  !
1228  ! -- reallocate
1229  if (this%isrb == sorption_off) then
1230  call get_isize('BULK_DENSITY', this%input_mempath, asize)
1231  if (asize > 0) &
1232  call mem_reallocate(this%bulk_density, this%dis%nodes, &
1233  'BULK_DENSITY', this%memoryPath)
1234  call get_isize('DISTCOEF', this%input_mempath, asize)
1235  if (asize > 0) &
1236  call mem_reallocate(this%distcoef, this%dis%nodes, 'DISTCOEF', &
1237  this%memoryPath)
1238  end if
1239  if (this%idcy == decay_off) then
1240  call get_isize('DECAY', this%input_mempath, asize)
1241  if (asize > 0) &
1242  call mem_reallocate(this%decay, this%dis%nodes, 'DECAY', this%memoryPath)
1243  end if
1244  call get_isize('DECAY_SORBED', this%input_mempath, asize)
1245  if (asize > 0) then
1246  call mem_reallocate(this%decay_sorbed, this%dis%nodes, &
1247  'DECAY_SORBED', this%memoryPath)
1248  end if
1249  if (this%isrb == sorption_off .or. this%isrb == sorption_linear) then
1250  call get_isize('SP2', this%input_mempath, asize)
1251  if (asize > 0) &
1252  call mem_reallocate(this%sp2, this%dis%nodes, 'SP2', this%memoryPath)
1253  end if
1254  !
1255  ! -- update defaults with memory sourced values
1256  call mem_set_value(this%porosity, 'POROSITY', this%input_mempath, map, &
1257  found%porosity)
1258  call mem_set_value(this%decay, 'DECAY', this%input_mempath, map, &
1259  found%decay)
1260  call mem_set_value(this%decay_sorbed, 'DECAY_SORBED', this%input_mempath, &
1261  map, found%decay_sorbed)
1262  call mem_set_value(this%bulk_density, 'BULK_DENSITY', this%input_mempath, &
1263  map, found%bulk_density)
1264  call mem_set_value(this%distcoef, 'DISTCOEF', this%input_mempath, map, &
1265  found%distcoef)
1266  call mem_set_value(this%sp2, 'SP2', this%input_mempath, map, &
1267  found%sp2)
1268 
1269  ! -- log options
1270  if (this%iout > 0) then
1271  call this%log_data(found)
1272  end if
1273 
1274  ! -- Check for required porosity
1275  if (.not. found%porosity) then
1276  write (errmsg, '(a)') 'POROSITY not specified in GRIDDATA block.'
1277  call store_error(errmsg)
1278  end if
1279 
1280  ! -- Check for required sorption variables
1281  if (this%isrb == sorption_linear .or. this%isrb == sorption_freund .or. &
1282  this%isrb == sorption_lang) then
1283  if (.not. found%bulk_density) then
1284  write (errmsg, '(a)') 'Sorption is active but BULK_DENSITY &
1285  &not specified. BULK_DENSITY must be specified in GRIDDATA block.'
1286  call store_error(errmsg)
1287  end if
1288  if (.not. found%distcoef) then
1289  write (errmsg, '(a)') 'Sorption is active but distribution &
1290  &coefficient not specified. DISTCOEF must be specified in &
1291  &GRIDDATA block.'
1292  call store_error(errmsg)
1293  end if
1294  if (this%isrb == sorption_freund .or. this%isrb == sorption_lang) then
1295  if (.not. found%sp2) then
1296  write (errmsg, '(a)') 'Freundlich or langmuir sorption is active &
1297  &but SP2 not specified. SP2 must be specified in &
1298  &GRIDDATA block.'
1299  call store_error(errmsg)
1300  end if
1301  end if
1302  else
1303  if (found%bulk_density) then
1304  write (warnmsg, '(a)') 'Sorption is not active but &
1305  &BULK_DENSITY was specified. BULK_DENSITY will have no affect on &
1306  &simulation results.'
1307  call store_warning(warnmsg)
1308  write (this%iout, '(1x,a)') 'WARNING. '//warnmsg
1309  end if
1310  if (found%distcoef) then
1311  write (warnmsg, '(a)') 'Sorption is not active but &
1312  &distribution coefficient was specified. DISTCOEF will have &
1313  &no affect on simulation results.'
1314  call store_warning(warnmsg)
1315  write (this%iout, '(1x,a)') 'WARNING. '//warnmsg
1316  end if
1317  if (found%sp2) then
1318  write (warnmsg, '(a)') 'Sorption is not active but &
1319  &SP2 was specified. SP2 will have &
1320  &no affect on simulation results.'
1321  call store_warning(warnmsg)
1322  write (this%iout, '(1x,a)') 'WARNING. '//warnmsg
1323  end if
1324  end if
1325 
1326  ! -- Check for required decay/production rate coefficients
1327  if (this%idcy /= decay_off) then
1328  if (.not. found%decay) then
1329  write (errmsg, '(a)') 'First or zero order decay is &
1330  &active but the first rate coefficient is not specified. DECAY &
1331  &must be specified in GRIDDATA block.'
1332  call store_error(errmsg)
1333  end if
1334  if (.not. found%decay_sorbed) then
1335  !
1336  ! -- If DECAY_SORBED not specified and sorption is active, then
1337  ! terminate with an error
1338  if (this%isrb == sorption_linear .or. this%isrb == sorption_freund .or. &
1339  this%isrb == sorption_lang) then
1340  write (errmsg, '(a)') 'DECAY_SORBED not provided in GRIDDATA &
1341  &block but decay and sorption are active. Specify DECAY_SORBED &
1342  &in GRIDDATA block.'
1343  call store_error(errmsg)
1344  end if
1345  end if
1346  else
1347  if (found%decay) then
1348  write (warnmsg, '(a)') 'First- or zero-order decay &
1349  &is not active but decay was specified. DECAY will &
1350  &have no affect on simulation results.'
1351  call store_warning(warnmsg)
1352  write (this%iout, '(1x,a)') 'WARNING. '//warnmsg
1353  end if
1354  if (found%decay_sorbed) then
1355  write (warnmsg, '(a)') 'First- or zero-order decay &
1356  &is not active but DECAY_SORBED was specified. &
1357  &DECAY_SORBED will have no affect on simulation results.'
1358  call store_warning(warnmsg)
1359  write (this%iout, '(1x,a)') 'WARNING. '//warnmsg
1360  end if
1361  end if
1362 
1363  ! -- terminate if errors
1364  if (count_errors() > 0) then
1365  call store_error_filename(this%input_fname)
1366  end if
1367 
1368  ! -- initialize thetam from porosity
1369  do n = 1, size(this%porosity)
1370  this%thetam(n) = this%porosity(n)
1371  end do
1372  end subroutine source_data
1373 
1374  !> @brief Log user data to list file
1375  !<
1376  subroutine log_data(this, found)
1378  class(gwtmsttype) :: this
1379  type(gwtmstparamfoundtype), intent(in) :: found
1380 
1381  write (this%iout, '(1x,a)') 'PROCESSING GRIDDATA'
1382  if (found%porosity) then
1383  write (this%iout, '(4x,a)') 'MOBILE DOMAIN POROSITY set from input file'
1384  end if
1385  if (found%decay) then
1386  write (this%iout, '(4x,a)') 'DECAY RATE set from input file'
1387  end if
1388  if (found%decay_sorbed) then
1389  write (this%iout, '(4x,a)') 'DECAY SORBED RATE set from input file'
1390  end if
1391  if (found%bulk_density) then
1392  write (this%iout, '(4x,a)') 'BULK DENSITY set from input file'
1393  end if
1394  if (found%distcoef) then
1395  write (this%iout, '(4x,a)') 'DISTRIBUTION COEFFICIENT set from input file'
1396  end if
1397  if (found%sp2) then
1398  write (this%iout, '(4x,a)') 'SECOND SORPTION PARAM set from input file'
1399  end if
1400  write (this%iout, '(1x,a)') 'END PROCESSING GRIDDATA'
1401  end subroutine log_data
1402 
1403  !> @ brief Add volfrac values to volfracim
1404  !!
1405  !! Method to add immobile domain volume fracions, which are stored as a
1406  !! cumulative value in volfracim.
1407  !<
1408  subroutine addto_volfracim(this, volfracim)
1409  ! -- dummy
1410  class(gwtmsttype) :: this !< GwtMstType object
1411  real(DP), dimension(:), intent(in) :: volfracim !< immobile domain volume fraction that contributes to total immobile volume fraction
1412  ! -- local
1413  integer(I4B) :: n
1414  !
1415  ! -- Add to volfracim
1416  do n = 1, this%dis%nodes
1417  this%volfracim(n) = this%volfracim(n) + volfracim(n)
1418  end do
1419  !
1420  ! -- An immobile domain is adding a volume fraction, so update thetam
1421  ! accordingly.
1422  do n = 1, this%dis%nodes
1423  this%thetam(n) = this%get_volfracm(n) * this%porosity(n)
1424  end do
1425  end subroutine addto_volfracim
1426 
1427  !> @ brief Return mobile domain volume fraction
1428  !!
1429  !! Calculate and return the volume fraction of the aquifer that is mobile
1430  !<
1431  function get_volfracm(this, node) result(volfracm)
1432  ! -- dummy
1433  class(gwtmsttype) :: this !< GwtMstType object
1434  integer(I4B), intent(in) :: node !< node number
1435  ! -- return
1436  real(dp) :: volfracm
1437  !
1438  volfracm = done - this%volfracim(node)
1439  end function get_volfracm
1440 
1441  !> @ brief Calculate zero-order decay rate and constrain if necessary
1442  !!
1443  !! Function to calculate the zero-order decay rate from the user specified
1444  !! decay rate. If the decay rate is positive, then the decay rate must
1445  !! be constrained so that more mass is not removed than is available.
1446  !! Without this constraint, negative concentrations could result from
1447  !! zero-order decay.
1448  !<
1449  function get_zero_order_decay(decay_rate_usr, decay_rate_last, kiter, &
1450  cold, cnew, delt) result(decay_rate)
1451  ! -- dummy
1452  real(dp), intent(in) :: decay_rate_usr !< user-entered decay rate
1453  real(dp), intent(in) :: decay_rate_last !< decay rate used for last iteration
1454  integer(I4B), intent(in) :: kiter !< Picard iteration counter
1455  real(dp), intent(in) :: cold !< concentration at end of last time step
1456  real(dp), intent(in) :: cnew !< concentration at end of this time step
1457  real(dp), intent(in) :: delt !< length of time step
1458  ! -- return
1459  real(dp) :: decay_rate !< returned value for decay rate
1460  !
1461  ! -- Return user rate if production, otherwise constrain, if necessary
1462  if (decay_rate_usr < dzero) then
1463  !
1464  ! -- Production, no need to limit rate
1465  decay_rate = decay_rate_usr
1466  else
1467  !
1468  ! -- Need to ensure decay does not result in negative
1469  ! concentration, so reduce the rate if it would result in
1470  ! removing more mass than is in the cell.
1471  if (kiter == 1) then
1472  decay_rate = min(decay_rate_usr, cold / delt)
1473  else
1474  decay_rate = decay_rate_last
1475  if (cnew < dzero) then
1476  decay_rate = decay_rate_last + cnew / delt
1477  else if (cnew > cold) then
1478  decay_rate = decay_rate_last + cnew / delt
1479  end if
1480  decay_rate = min(decay_rate_usr, decay_rate)
1481  end if
1482  decay_rate = max(decay_rate, dzero)
1483  end if
1484  end function get_zero_order_decay
1485 
1486 end module gwtmstmodule
This module contains the BudgetModule.
Definition: Budget.f90:20
subroutine, public rate_accumulator(flow, rin, rout)
@ brief Rate accumulator subroutine
Definition: Budget.f90:632
This module contains simulation constants.
Definition: Constants.f90:9
integer(i4b), parameter linelength
maximum length of a standard line
Definition: Constants.f90:45
@ mnormal
normal output mode
Definition: Constants.f90:206
real(dp), parameter dhnoflo
real no flow constant
Definition: Constants.f90:93
integer(i4b), parameter lenvarname
maximum length of a variable name
Definition: Constants.f90:17
real(dp), parameter dhalf
real constant 1/2
Definition: Constants.f90:68
integer(i4b), parameter izero
integer constant zero
Definition: Constants.f90:51
real(dp), parameter dzero
real constant zero
Definition: Constants.f90:65
integer(i4b), parameter maxcharlen
maximum length of char string
Definition: Constants.f90:47
real(dp), parameter dtwo
real constant 2
Definition: Constants.f90:79
integer(i4b), parameter lenbudtxt
maximum length of a budget component names
Definition: Constants.f90:37
real(dp), parameter done
real constant 1
Definition: Constants.f90:76
– @ brief Mobile Storage and Transfer (MST) Module
Definition: gwt-mst.f90:10
subroutine mst_cq_srb(this, nodes, cnew, cold, flowja)
@ brief Calculate sorption terms for package
Definition: gwt-mst.f90:626
subroutine mst_calc_csrb(this, cnew)
@ brief Calculate sorbed concentration
Definition: gwt-mst.f90:791
subroutine addto_volfracim(this, volfracim)
@ brief Add volfrac values to volfracim
Definition: gwt-mst.f90:1409
real(dp) function get_zero_order_decay(decay_rate_usr, decay_rate_last, kiter, cold, cnew, delt)
@ brief Calculate zero-order decay rate and constrain if necessary
Definition: gwt-mst.f90:1451
character(len=lenbudtxt), dimension(nbditems) budtxt
Definition: gwt-mst.f90:31
subroutine mst_cq(this, nodes, cnew, cold, flowja)
@ brief Calculate flows for package
Definition: gwt-mst.f90:486
@ decay_zero_order
Zeroth-order decay.
Definition: gwt-mst.f90:40
@ decay_first_order
First-order decay.
Definition: gwt-mst.f90:39
@ decay_off
Decay (or production) of mass inactive (default)
Definition: gwt-mst.f90:38
subroutine mst_bd(this, isuppress_output, model_budget)
@ brief Calculate budget terms for package
Definition: gwt-mst.f90:812
subroutine allocate_arrays(this, nodes)
@ brief Allocate arrays for package
Definition: gwt-mst.f90:1016
subroutine mst_cq_dcy_srb(this, nodes, cnew, cold, flowja)
@ brief Calculate decay-sorption terms for package
Definition: gwt-mst.f90:696
subroutine, public mst_cr(mstobj, name_model, input_mempath, inunit, iout, fmi)
@ brief Create a new package object
Definition: gwt-mst.f90:115
real(dp) function get_volfracm(this, node)
@ brief Return mobile domain volume fraction
Definition: gwt-mst.f90:1432
subroutine mst_ot_dv(this, idvsave)
Save sorbate concentration array to binary file.
Definition: gwt-mst.f90:908
subroutine mst_ot_flow(this, icbcfl, icbcun)
@ brief Output flow terms for package
Definition: gwt-mst.f90:855
subroutine mst_ar(this, dis, ibound)
@ brief Allocate and read method for package
Definition: gwt-mst.f90:143
subroutine mst_fc_srb(this, nodes, cold, nja, matrix_sln, idxglo, rhs, cnew)
@ brief Fill sorption coefficient method for package
Definition: gwt-mst.f90:325
subroutine mst_cq_dcy(this, nodes, cnew, cold, flowja)
@ brief Calculate decay terms for package
Definition: gwt-mst.f90:570
subroutine allocate_scalars(this)
@ brief Allocate scalar variables for package
Definition: gwt-mst.f90:992
subroutine log_options(this, found, sorbate_fname)
Log user options to list file.
Definition: gwt-mst.f90:1158
subroutine source_options(this)
@ brief Source options for package
Definition: gwt-mst.f90:1105
subroutine mst_fc_dcy_srb(this, nodes, cold, nja, matrix_sln, idxglo, rhs, cnew, kiter)
@ brief Fill sorption-decay coefficient method for package
Definition: gwt-mst.f90:393
subroutine source_data(this)
@ brief Source data for package
Definition: gwt-mst.f90:1212
subroutine log_data(this, found)
Log user data to list file.
Definition: gwt-mst.f90:1377
subroutine mst_da(this)
@ brief Deallocate
Definition: gwt-mst.f90:946
subroutine mst_fc_dcy(this, nodes, cold, cnew, nja, matrix_sln, idxglo, rhs, kiter)
@ brief Fill decay coefficient method for package
Definition: gwt-mst.f90:265
integer(i4b), parameter nbditems
Definition: gwt-mst.f90:30
subroutine mst_fc(this, nodes, cold, nja, matrix_sln, idxglo, cnew, rhs, kiter)
@ brief Fill coefficient method for package
Definition: gwt-mst.f90:180
subroutine mst_fc_sto(this, nodes, cold, nja, matrix_sln, idxglo, rhs)
@ brief Fill storage coefficient method for package
Definition: gwt-mst.f90:218
subroutine mst_cq_sto(this, nodes, cnew, cold, flowja)
@ brief Calculate storage terms for package
Definition: gwt-mst.f90:522
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
integer(i4b), parameter sorption_off
Sorption is inactive (default)
Definition: IsothermEnum.f90:6
integer(i4b), parameter sorption_freund
Freundlich sorption between aqueous and solid phases.
Definition: IsothermEnum.f90:8
integer(i4b), parameter sorption_lang
Langmuir sorption between aqueous and solid phases.
Definition: IsothermEnum.f90:9
integer(i4b), parameter sorption_linear
Linear sorption between aqueous and solid phases.
Definition: IsothermEnum.f90:7
class(isothermtype) function, pointer, public create_isotherm(isotherm_type, distcoef, sp2)
Create an isotherm object based on type and parameters.
This module defines variable data types.
Definition: kind.f90:8
subroutine, public get_isize(name, mem_path, isize)
@ brief Get the number of elements for this variable
This module contains the base numerical package type.
character(len=20) access
Definition: OpenSpec.f90:7
character(len=20) form
Definition: OpenSpec.f90:7
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 delt
length of the current time step
Definition: tdis.f90:29
Derived type for the Budget object.
Definition: Budget.f90:39
@ brief Mobile storage and transfer
Definition: gwt-mst.f90:49