MODFLOW 6  version 6.7.0.dev0
USGS Modular Hydrologic Model
tsp-adv.f90
Go to the documentation of this file.
2 
3  use kindmodule, only: dp, i4b
4  use constantsmodule, only: done, dzero, dhalf, dtwo, dnodata, dprec, &
7  use basedismodule, only: disbasetype
8  use tspfmimodule, only: tspfmitype
11 
12  implicit none
13  private
14  public :: tspadvtype
15  public :: adv_cr
16 
17  type, extends(numericalpackagetype) :: tspadvtype
18 
19  integer(I4B), pointer :: iadvwt => null() !< advection scheme (0 up, 1 central, 2 tvd)
20  real(dp), pointer :: ats_percel => null() !< user-specified fractional number of cells advection can move a particle during one time step
21  integer(I4B), dimension(:), pointer, contiguous :: ibound => null() !< pointer to model ibound
22  type(tspfmitype), pointer :: fmi => null() !< pointer to fmi object
23  real(dp), pointer :: eqnsclfac => null() !< governing equation scale factor; =1. for solute; =rhow*cpw for energy
24 
25  contains
26 
27  procedure :: adv_df
28  procedure :: adv_ar
29  procedure :: adv_dt
30  procedure :: adv_fc
31  procedure :: adv_cq
32  procedure :: adv_da
33 
34  procedure :: allocate_scalars
35  procedure, private :: read_options
36  procedure, private :: advqtvd
37  procedure, private :: advtvd_bd
38  procedure :: adv_weight
39  procedure :: advtvd
40 
41  end type tspadvtype
42 
43 contains
44 
45  !> @ brief Create a new ADV object
46  !!
47  !! Create a new ADV package
48  !<
49  subroutine adv_cr(advobj, name_model, inunit, iout, fmi, eqnsclfac)
50  ! -- dummy
51  type(tspadvtype), pointer :: advobj
52  character(len=*), intent(in) :: name_model
53  integer(I4B), intent(in) :: inunit
54  integer(I4B), intent(in) :: iout
55  type(tspfmitype), intent(in), target :: fmi
56  real(dp), intent(in), pointer :: eqnsclfac !< governing equation scale factor
57  !
58  ! -- Create the object
59  allocate (advobj)
60  !
61  ! -- create name and memory path
62  call advobj%set_names(1, name_model, 'ADV', 'ADV')
63  !
64  ! -- Allocate scalars
65  call advobj%allocate_scalars()
66  !
67  ! -- Set variables
68  advobj%inunit = inunit
69  advobj%iout = iout
70  advobj%fmi => fmi
71  advobj%eqnsclfac => eqnsclfac
72  end subroutine adv_cr
73 
74  !> @brief Define ADV object
75  !!
76  !! Define the ADV package
77  !<
78  subroutine adv_df(this, adv_options)
79  ! -- dummy
80  class(tspadvtype) :: this
81  type(tspadvoptionstype), optional, intent(in) :: adv_options !< the optional options, for when not constructing from file
82  ! -- local
83  character(len=*), parameter :: fmtadv = &
84  "(1x,/1x,'ADV-- ADVECTION PACKAGE, VERSION 1, 8/25/2017', &
85  &' INPUT READ FROM UNIT ', i0, //)"
86  !
87  ! -- Read or set advection options
88  if (.not. present(adv_options)) then
89  !
90  ! -- Initialize block parser (adv has no define, so it's
91  ! not done until here)
92  call this%parser%Initialize(this%inunit, this%iout)
93  !
94  ! --print a message identifying the advection package.
95  write (this%iout, fmtadv) this%inunit
96  !
97  ! --read options from file
98  call this%read_options()
99  else
100  !
101  ! --set options from input arg
102  this%iadvwt = adv_options%iAdvScheme
103  end if
104  end subroutine adv_df
105 
106  !> @brief Allocate and read method for package
107  !!
108  !! Method to allocate and read static data for the ADV package.
109  !<
110  subroutine adv_ar(this, dis, ibound)
111  ! -- modules
112  ! -- dummy
113  class(tspadvtype) :: this
114  class(disbasetype), pointer, intent(in) :: dis
115  integer(I4B), dimension(:), pointer, contiguous, intent(in) :: ibound
116  ! -- local
117  ! -- formats
118  !
119  ! -- adv pointers to arguments that were passed in
120  this%dis => dis
121  this%ibound => ibound
122  end subroutine adv_ar
123 
124  !> @brief Calculate maximum time step length
125  !!
126  !! Return the largest time step that meets stability constraints
127  !<
128  subroutine adv_dt(this, dtmax, msg, thetam)
129  ! dummy
130  class(tspadvtype) :: this !< this instance
131  real(DP), intent(out) :: dtmax !< maximum allowable dt subject to stability constraint
132  character(len=*), intent(inout) :: msg !< package/cell dt constraint message
133  real(DP), dimension(:), intent(in) :: thetam !< porosity
134  ! local
135  integer(I4B) :: n
136  integer(I4B) :: m
137  integer(I4B) :: ipos
138  integer(I4B) :: nrmax
139  character(len=LINELENGTH) :: cellstr
140  real(DP) :: dt
141  real(DP) :: flowmax
142  real(DP) :: flowsumpos
143  real(DP) :: flowsumneg
144  real(DP) :: flownm
145  real(DP) :: cell_volume
146  dtmax = dnodata
147  nrmax = 0
148  msg = ''
149 
150  ! If ats_percel not specified by user, then return without making
151  ! the courant time step calculation
152  if (this%ats_percel == dnodata) then
153  return
154  end if
155 
156  ! Calculate time step lengths based on stability constraint for each cell
157  ! and store the smallest one
158  do n = 1, this%dis%nodes
159  if (this%ibound(n) == 0) cycle
160  flowsumneg = dzero
161  flowsumpos = dzero
162  do ipos = this%dis%con%ia(n) + 1, this%dis%con%ia(n + 1) - 1
163  if (this%dis%con%mask(ipos) == 0) cycle
164  m = this%dis%con%ja(ipos)
165  if (this%ibound(m) == 0) cycle
166  flownm = this%fmi%gwfflowja(ipos)
167  if (flownm < dzero) then
168  flowsumneg = flowsumneg - flownm
169  else
170  flowsumpos = flowsumpos + flownm
171  end if
172  end do
173  flowmax = max(flowsumneg, flowsumpos)
174  if (flowmax < dprec) cycle
175  cell_volume = this%dis%get_cell_volume(n, this%dis%top(n))
176  dt = cell_volume * this%fmi%gwfsat(n) * thetam(n) / flowmax
177  dt = dt * this%ats_percel
178  if (dt < dtmax) then
179  dtmax = dt
180  nrmax = n
181  end if
182  end do
183  if (nrmax > 0) then
184  call this%dis%noder_to_string(nrmax, cellstr)
185  write (msg, *) adjustl(trim(this%memoryPath))//'-'//trim(cellstr)
186  end if
187  end subroutine adv_dt
188 
189  !> @brief Fill coefficient method for ADV package
190  !!
191  !! Method to calculate coefficients and fill amat and rhs.
192  !<
193  subroutine adv_fc(this, nodes, matrix_sln, idxglo, cnew, rhs)
194  ! -- modules
195  ! -- dummy
196  class(tspadvtype) :: this
197  integer, intent(in) :: nodes
198  class(matrixbasetype), pointer :: matrix_sln
199  integer(I4B), intent(in), dimension(:) :: idxglo
200  real(DP), intent(in), dimension(:) :: cnew
201  real(DP), dimension(:), intent(inout) :: rhs
202  ! -- local
203  integer(I4B) :: n, m, idiag, ipos
204  real(DP) :: omega, qnm
205  !
206  ! -- Calculate advection terms and add to solution rhs and hcof. qnm
207  ! is the volumetric flow rate and has dimensions of L^/T.
208  do n = 1, nodes
209  if (this%ibound(n) == 0) cycle
210  idiag = this%dis%con%ia(n)
211  do ipos = this%dis%con%ia(n) + 1, this%dis%con%ia(n + 1) - 1
212  if (this%dis%con%mask(ipos) == 0) cycle
213  m = this%dis%con%ja(ipos)
214  if (this%ibound(m) == 0) cycle
215  qnm = this%fmi%gwfflowja(ipos) * this%eqnsclfac
216  omega = this%adv_weight(this%iadvwt, ipos, n, m, qnm)
217  call matrix_sln%add_value_pos(idxglo(ipos), qnm * (done - omega))
218  call matrix_sln%add_value_pos(idxglo(idiag), qnm * omega)
219  end do
220  end do
221  !
222  ! -- TVD
223  if (this%iadvwt == 2) then
224  do n = 1, nodes
225  if (this%ibound(n) == 0) cycle
226  call this%advtvd(n, cnew, rhs)
227  end do
228  end if
229  end subroutine adv_fc
230 
231  !> @brief Calculate TVD
232  !!
233  !! Use explicit scheme to calculate the advective component of transport.
234  !! TVD is an acronym for Total-Variation Diminishing
235  !<
236  subroutine advtvd(this, n, cnew, rhs)
237  ! -- modules
238  ! -- dummy
239  class(tspadvtype) :: this
240  integer(I4B), intent(in) :: n
241  real(DP), dimension(:), intent(in) :: cnew
242  real(DP), dimension(:), intent(inout) :: rhs
243  ! -- local
244  real(DP) :: qtvd
245  integer(I4B) :: m, ipos
246  !
247  ! -- Loop through each n connection. This will
248  do ipos = this%dis%con%ia(n) + 1, this%dis%con%ia(n + 1) - 1
249  if (this%dis%con%mask(ipos) == 0) cycle
250  m = this%dis%con%ja(ipos)
251  if (m > n .and. this%ibound(m) /= 0) then
252  qtvd = this%advqtvd(n, m, ipos, cnew)
253  rhs(n) = rhs(n) - qtvd
254  rhs(m) = rhs(m) + qtvd
255  end if
256  end do
257  end subroutine advtvd
258 
259  !> @brief Calculate TVD
260  !!
261  !! Use explicit scheme to calculate the advective component of transport.
262  !! TVD is an acronym for Total-Variation Diminishing
263  !<
264  function advqtvd(this, n, m, iposnm, cnew) result(qtvd)
265  ! -- modules
266  use constantsmodule, only: dprec
267  ! -- return
268  real(dp) :: qtvd
269  ! -- dummy
270  class(tspadvtype) :: this
271  integer(I4B), intent(in) :: n
272  integer(I4B), intent(in) :: m
273  integer(I4B), intent(in) :: iposnm
274  real(dp), dimension(:), intent(in) :: cnew
275  ! -- local
276  integer(I4B) :: ipos, isympos, iup, idn, i2up, j
277  real(dp) :: qnm, qmax, qupj, elupdn, elup2up
278  real(dp) :: smooth, cdiff, alimiter
279  !
280  ! -- initialize
281  qtvd = dzero
282  !
283  ! -- Find upstream node
284  isympos = this%dis%con%jas(iposnm)
285  qnm = this%fmi%gwfflowja(iposnm)
286  if (qnm > dzero) then
287  ! -- positive flow into n means m is upstream
288  iup = m
289  idn = n
290  else
291  iup = n
292  idn = m
293  end if
294  elupdn = this%dis%con%cl1(isympos) + this%dis%con%cl2(isympos)
295  !
296  ! -- Find second node upstream to iup
297  i2up = 0
298  qmax = dzero
299  do ipos = this%dis%con%ia(iup) + 1, this%dis%con%ia(iup + 1) - 1
300  j = this%dis%con%ja(ipos)
301  if (this%ibound(j) == 0) cycle
302  qupj = this%fmi%gwfflowja(ipos)
303  isympos = this%dis%con%jas(ipos)
304  if (qupj > qmax) then
305  qmax = qupj
306  i2up = j
307  elup2up = this%dis%con%cl1(isympos) + this%dis%con%cl2(isympos)
308  end if
309  end do
310  !
311  ! -- Calculate flux limiting term
312  if (i2up > 0) then
313  smooth = dzero
314  cdiff = abs(cnew(idn) - cnew(iup))
315  if (cdiff > dprec) then
316  smooth = (cnew(iup) - cnew(i2up)) / elup2up * &
317  elupdn / (cnew(idn) - cnew(iup))
318  end if
319  if (smooth > dzero) then
320  alimiter = dtwo * smooth / (done + smooth)
321  qtvd = dhalf * alimiter * qnm * (cnew(idn) - cnew(iup))
322  qtvd = qtvd * this%eqnsclfac
323  end if
324  end if
325  end function advqtvd
326 
327  !> @brief Calculate advection contribution to flowja
328  !<
329  subroutine adv_cq(this, cnew, flowja)
330  ! -- modules
331  ! -- dummy
332  class(tspadvtype) :: this
333  real(DP), intent(in), dimension(:) :: cnew
334  real(DP), intent(inout), dimension(:) :: flowja
335  ! -- local
336  integer(I4B) :: nodes
337  integer(I4B) :: n, m, idiag, ipos
338  real(DP) :: omega, qnm
339  !
340  ! -- Calculate advection and add to flowja. qnm is the volumetric flow
341  ! rate and has dimensions of L^/T.
342  nodes = this%dis%nodes
343  do n = 1, nodes
344  if (this%ibound(n) == 0) cycle
345  idiag = this%dis%con%ia(n)
346  do ipos = this%dis%con%ia(n) + 1, this%dis%con%ia(n + 1) - 1
347  m = this%dis%con%ja(ipos)
348  if (this%ibound(m) == 0) cycle
349  qnm = this%fmi%gwfflowja(ipos) * this%eqnsclfac
350  omega = this%adv_weight(this%iadvwt, ipos, n, m, qnm)
351  flowja(ipos) = flowja(ipos) + qnm * omega * cnew(n) + &
352  qnm * (done - omega) * cnew(m)
353  end do
354  end do
355  !
356  ! -- TVD
357  if (this%iadvwt == 2) call this%advtvd_bd(cnew, flowja)
358  end subroutine adv_cq
359 
360  !> @brief Add TVD contribution to flowja
361  subroutine advtvd_bd(this, cnew, flowja)
362  ! -- modules
363  ! -- dummy
364  class(tspadvtype) :: this
365  real(DP), dimension(:), intent(in) :: cnew
366  real(DP), dimension(:), intent(inout) :: flowja
367  ! -- local
368  real(DP) :: qtvd, qnm
369  integer(I4B) :: nodes, n, m, ipos
370  !
371  nodes = this%dis%nodes
372  do n = 1, nodes
373  if (this%ibound(n) == 0) cycle
374  do ipos = this%dis%con%ia(n) + 1, this%dis%con%ia(n + 1) - 1
375  m = this%dis%con%ja(ipos)
376  if (this%ibound(m) /= 0) then
377  qnm = this%fmi%gwfflowja(ipos)
378  qtvd = this%advqtvd(n, m, ipos, cnew)
379  flowja(ipos) = flowja(ipos) + qtvd
380  end if
381  end do
382  end do
383  end subroutine advtvd_bd
384 
385  !> @brief Deallocate memory
386  !<
387  subroutine adv_da(this)
388  ! -- modules
390  ! -- dummy
391  class(tspadvtype) :: this
392  !
393  ! -- Deallocate arrays if package was active
394  if (this%inunit > 0) then
395  end if
396  !
397  ! -- nullify pointers
398  this%ibound => null()
399  !
400  ! -- Scalars
401  call mem_deallocate(this%iadvwt)
402  call mem_deallocate(this%ats_percel)
403  !
404  ! -- deallocate parent
405  call this%NumericalPackageType%da()
406  end subroutine adv_da
407 
408  !> @brief Allocate scalars specific to the streamflow energy transport (SFE)
409  !! package.
410  !<
411  subroutine allocate_scalars(this)
412  ! -- modules
414  ! -- dummy
415  class(tspadvtype) :: this
416  ! -- local
417  !
418  ! -- allocate scalars in NumericalPackageType
419  call this%NumericalPackageType%allocate_scalars()
420  !
421  ! -- Allocate
422  call mem_allocate(this%iadvwt, 'IADVWT', this%memoryPath)
423  call mem_allocate(this%ats_percel, 'ATS_PERCEL', this%memoryPath)
424  !
425  ! -- Initialize
426  this%iadvwt = 0
427  this%ats_percel = dnodata
428  !
429  ! -- Advection creates an asymmetric coefficient matrix
430  this%iasym = 1
431  end subroutine allocate_scalars
432 
433  !> @brief Read options
434  !!
435  !! Read the options block
436  !<
437  subroutine read_options(this)
438  ! -- modules
439  use constantsmodule, only: linelength
440  use simmodule, only: store_error
441  ! -- dummy
442  class(tspadvtype) :: this
443  ! -- local
444  character(len=LINELENGTH) :: errmsg, keyword
445  integer(I4B) :: ierr
446  logical :: isfound, endOfBlock
447  ! -- formats
448  character(len=*), parameter :: fmtiadvwt = &
449  &"(4x,'ADVECTION WEIGHTING SCHEME HAS BEEN SET TO: ', a)"
450  !
451  ! -- get options block
452  call this%parser%GetBlock('OPTIONS', isfound, ierr, blockrequired=.false., &
453  supportopenclose=.true.)
454  !
455  ! -- parse options block if detected
456  if (isfound) then
457  write (this%iout, '(1x,a)') 'PROCESSING ADVECTION OPTIONS'
458  do
459  call this%parser%GetNextLine(endofblock)
460  if (endofblock) exit
461  call this%parser%GetStringCaps(keyword)
462  select case (keyword)
463  case ('SCHEME')
464  call this%parser%GetStringCaps(keyword)
465  select case (keyword)
466  case ('UPSTREAM')
467  this%iadvwt = 0
468  write (this%iout, fmtiadvwt) 'UPSTREAM'
469  case ('CENTRAL')
470  this%iadvwt = 1
471  write (this%iout, fmtiadvwt) 'CENTRAL'
472  case ('TVD')
473  this%iadvwt = 2
474  write (this%iout, fmtiadvwt) 'TVD'
475  case default
476  write (errmsg, '(a, a)') &
477  'Unknown scheme: ', trim(keyword)
478  call store_error(errmsg)
479  write (errmsg, '(a, a)') &
480  'Scheme must be "UPSTREAM", "CENTRAL" or "TVD"'
481  call store_error(errmsg)
482  call this%parser%StoreErrorUnit()
483  end select
484  case ('ATS_PERCEL')
485  this%ats_percel = this%parser%GetDouble()
486  if (this%ats_percel == dzero) this%ats_percel = dnodata
487  write (this%iout, '(4x,a,1pg15.6)') &
488  'User-specified fractional cell distance for adaptive time &
489  &steps: ', this%ats_percel
490  case default
491  write (errmsg, '(a,a)') 'Unknown ADVECTION option: ', &
492  trim(keyword)
493  call store_error(errmsg, terminate=.true.)
494  end select
495  end do
496  write (this%iout, '(1x,a)') 'END OF ADVECTION OPTIONS'
497  end if
498  end subroutine read_options
499 
500  !> @ brief Advection weight
501  !!
502  !! Calculate the advection weight
503  !<
504  function adv_weight(this, iadvwt, ipos, n, m, qnm) result(omega)
505  ! -- return
506  real(dp) :: omega
507  ! -- dummy
508  class(tspadvtype) :: this
509  integer, intent(in) :: iadvwt
510  integer, intent(in) :: ipos
511  integer, intent(in) :: n
512  integer, intent(in) :: m
513  real(dp), intent(in) :: qnm
514  ! -- local
515  real(dp) :: lnm, lmn
516 
517  select case (iadvwt)
518  case (1)
519  ! -- calculate weight based on distances between nodes and the shared
520  ! face of the connection
521  if (this%dis%con%ihc(this%dis%con%jas(ipos)) == 0) then
522  ! -- vertical connection; assume cell is fully saturated
523  lnm = dhalf * (this%dis%top(n) - this%dis%bot(n))
524  lmn = dhalf * (this%dis%top(m) - this%dis%bot(m))
525  else
526  ! -- horizontal connection
527  lnm = this%dis%con%cl1(this%dis%con%jas(ipos))
528  lmn = this%dis%con%cl2(this%dis%con%jas(ipos))
529  end if
530  omega = lmn / (lnm + lmn)
531  case (0, 2)
532  ! -- use upstream weighting for upstream and tvd schemes
533  if (qnm > dzero) then
534  omega = dzero
535  else
536  omega = done
537  end if
538  end select
539  end function adv_weight
540 
541 end module tspadvmodule
This module contains simulation constants.
Definition: Constants.f90:9
integer(i4b), parameter linelength
maximum length of a standard line
Definition: Constants.f90:45
real(dp), parameter dnodata
real no data constant
Definition: Constants.f90:95
real(dp), parameter dhalf
real constant 1/2
Definition: Constants.f90:68
real(dp), parameter dzero
real constant zero
Definition: Constants.f90:65
real(dp), parameter dprec
real constant machine precision
Definition: Constants.f90:120
real(dp), parameter dtwo
real constant 2
Definition: Constants.f90:79
real(dp), parameter done
real constant 1
Definition: Constants.f90:76
This module defines variable data types.
Definition: kind.f90:8
This module contains the base numerical package type.
This module contains simulation methods.
Definition: Sim.f90:10
subroutine, public store_error(msg, terminate)
Store an error message.
Definition: Sim.f90:92
subroutine advtvd_bd(this, cnew, flowja)
Add TVD contribution to flowja.
Definition: tsp-adv.f90:362
real(dp) function advqtvd(this, n, m, iposnm, cnew)
Calculate TVD.
Definition: tsp-adv.f90:265
subroutine adv_df(this, adv_options)
Define ADV object.
Definition: tsp-adv.f90:79
subroutine adv_fc(this, nodes, matrix_sln, idxglo, cnew, rhs)
Fill coefficient method for ADV package.
Definition: tsp-adv.f90:194
subroutine adv_cq(this, cnew, flowja)
Calculate advection contribution to flowja.
Definition: tsp-adv.f90:330
subroutine adv_da(this)
Deallocate memory.
Definition: tsp-adv.f90:388
subroutine advtvd(this, n, cnew, rhs)
Calculate TVD.
Definition: tsp-adv.f90:237
subroutine adv_ar(this, dis, ibound)
Allocate and read method for package.
Definition: tsp-adv.f90:111
real(dp) function adv_weight(this, iadvwt, ipos, n, m, qnm)
@ brief Advection weight
Definition: tsp-adv.f90:505
subroutine read_options(this)
Read options.
Definition: tsp-adv.f90:438
subroutine, public adv_cr(advobj, name_model, inunit, iout, fmi, eqnsclfac)
@ brief Create a new ADV object
Definition: tsp-adv.f90:50
subroutine adv_dt(this, dtmax, msg, thetam)
Calculate maximum time step length.
Definition: tsp-adv.f90:129
subroutine allocate_scalars(this)
Allocate scalars specific to the streamflow energy transport (SFE) package.
Definition: tsp-adv.f90:412