MODFLOW 6  version 6.7.0.dev2
USGS Modular Hydrologic Model
UTVDScheme.f90
Go to the documentation of this file.
2  use kindmodule, only: dp, i4b
3  use constantsmodule, only: done, dzero, dsame, dhalf
6  use basedismodule, only: disbasetype
7  use tspfmimodule, only: tspfmitype
8  use igradient, only: igradienttype
10 
11  implicit none
12  private
13 
14  public :: utvdschemetype
15 
16  !> @brief Total Variation Diminishing (TVD) interpolation scheme.
17  !!
18  !! This class implements a high-resolution, TVD interpolation scheme for use in transport modeling.
19  !! It extends a generic interpolation scheme interface and supports multiple TVD limiters (van Leer,
20  !! Koren, Superbee, van Albada, etc.) for controlling numerical diffusion and oscillations.
21  !! The default limiter is van Leer, but others can be selected by changing the `limiter_id` member.
22  !!
23  !! The scheme uses a combination of low-order upwind and high-order limited terms to compute
24  !! face concentrations. The high-order term is constructed using a gradient-based virtual node
25  !! value, following the approach described by Darwish et al. An additional TVD clamp is applied
26  !! to the virtual node value to enforce TVD compliance, especially on grids where the original
27  !! method may not guarantee monotonicity.
28  !!
29  !! - Supports both structured and unstructured grids via polymorphic discretization and gradient objects.
30  !! - The limiter can be selected via the `limiter_id` member (default is van Leer).
31  !! - The method `find_local_extrema` finds the minimum and maximum values among the current cell and its neighbors,
32  !! which is used to enforce the TVD condition.
33  !! - The `compute` method calculates the face coefficients for the transport equation.
34  !<
36  private
37  class(disbasetype), pointer :: dis
38  type(tspfmitype), pointer :: fmi
39  class(igradienttype), pointer :: gradient
40  integer(I4B) :: limiter_id = 2 ! default to van Leer limiter
41  contains
42  procedure :: compute
43 
44  procedure, private :: find_local_extrema
45  procedure, private :: limiter
46  end type utvdschemetype
47 
48  interface utvdschemetype
49  module procedure constructor
50  end interface utvdschemetype
51 
52 contains
53  function constructor(dis, fmi, gradient) &
54  result(interpolation_scheme)
55  ! -- return
56  type(utvdschemetype) :: interpolation_scheme
57  ! --dummy
58  class(disbasetype), pointer, intent(in) :: dis
59  type(tspfmitype), pointer, intent(in) :: fmi
60  class(igradienttype), allocatable, target, intent(in) :: gradient
61 
62  interpolation_scheme%dis => dis
63  interpolation_scheme%fmi => fmi
64  interpolation_scheme%gradient => gradient
65 
66  end function constructor
67 
68  function compute(this, n, m, iposnm, phi) result(phi_face)
69  !-- return
70  type(coefficientstype), target :: phi_face ! Output: coefficients for the face between cells n and m
71  ! -- dummy
72  class(utvdschemetype), target :: this
73  integer(I4B), intent(in) :: n ! Index of the first cell
74  integer(I4B), intent(in) :: m ! Index of the second cell
75  integer(I4B), intent(in) :: iposnm ! Position in the connectivity array for the n-m connection
76  real(dp), intent(in), dimension(:) :: phi ! Array of scalar values (e.g., concentrations) at all cells
77  ! -- local
78  integer(I4B) :: iup, idn, isympos ! Indices for upwind, downwind, and symmetric position in connectivity
79  real(dp) :: qnm ! Flow rate across the face between n and m
80  real(dp), pointer :: coef_up, coef_dn ! Pointers to upwind and downwind coefficients in phi_face
81  real(dp), dimension(3) :: grad_c ! gradient at upwind cell
82  real(dp), dimension(3) :: dnm ! vector from upwind to downwind cell
83  real(dp) :: smooth ! Smoothness indicator for limiter i.e. ratio of gradients
84  real(dp) :: alimiter ! Value of the TVD limiter
85  real(dp) :: cl1, cl2 ! Connection lengths from upwind and downwind cells to the face
86  real(dp) :: relative_distance ! Relative distance factor for high-order term
87  real(dp) :: c_virtual ! Virtual node concentration (Darwish method)
88  real(dp) :: min_phi, max_phi ! Local minimum and maximum among cell and neighbors
89 
90  isympos = this%dis%con%jas(iposnm)
91  qnm = this%fmi%gwfflowja(iposnm)
92  !
93  ! -- Find upstream node
94  if (qnm > dzero) then
95  ! -- positive flow into n means m is upstream
96  iup = m
97  idn = n
98 
99  cl1 = this%dis%con%cl2(isympos)
100  cl2 = this%dis%con%cl1(isympos)
101 
102  coef_up => phi_face%c_m
103  coef_dn => phi_face%c_n
104  else
105  iup = n
106  idn = m
107 
108  cl1 = this%dis%con%cl1(isympos)
109  cl2 = this%dis%con%cl2(isympos)
110 
111  coef_up => phi_face%c_n
112  coef_dn => phi_face%c_m
113  end if
114  !
115  ! -- Add low order terms
116  coef_up = done
117  !
118  ! -- Add high order terms
119  !
120  ! -- Return if straddled cells have same value
121  if (abs(phi(idn) - phi(iup)) < dsame) return
122  !
123  ! -- Compute cell concentration gradient
124  grad_c = this%gradient%get(iup, phi)
125  !
126  ! Darwish's method to compute virtual node concentration
127  dnm = node_distance(this%dis, iup, idn)
128  c_virtual = phi(idn) - 2.0_dp * (dot_product(grad_c, dnm))
129  !
130  ! Enforce local TVD condition.
131  ! This is done by limiting the virtual concentration to the range of
132  ! the max and min concentration of the neighbouring cells.
133  call find_local_extrema(this, iup, phi, min_phi, max_phi)
134 
135  if (c_virtual > max_phi) then
136  c_virtual = max_phi
137  end if
138 
139  if (c_virtual < max(min_phi, dzero)) then
140  c_virtual = max(min_phi, dzero)
141  end if
142  !
143  ! -- Compute smoothness factor
144  smooth = (phi(iup) - c_virtual) / (phi(idn) - phi(iup))
145  !
146  ! -- Compute limiter
147  alimiter = this%limiter(smooth)
148 
149  ! High order term is:
150  relative_distance = cl1 / (cl1 + cl2)
151  phi_face%rhs = -relative_distance * alimiter * (phi(idn) - phi(iup))
152 
153  ! Alternative way of writing the high order term by adding it to the
154  ! coefficients matrix. The equation to be added is:
155  ! high_order = cl1 / (cl1 + cl2) * alimiter * qnm * (phi(idn) - phi(iup))
156  ! This is split into two parts:
157  ! coef_up = coef_up - relative_distance * alimiter
158  ! coef_dn = coef_dn + relative_distance * alimiter
159 
160  end function compute
161 
162  subroutine find_local_extrema(this, n, phi, min_phi, max_phi)
163  ! -- dummy
164  class(utvdschemetype) :: this
165  integer(I4B), intent(in) :: n
166  real(DP), intent(in), dimension(:) :: phi
167  real(DP), intent(out) :: min_phi, max_phi
168  ! -- local
169  integer(I4B) :: ipos, m
170 
171  min_phi = phi(n)
172  max_phi = phi(n)
173  do ipos = this%dis%con%ia(n) + 1, this%dis%con%ia(n + 1) - 1
174  m = this%dis%con%ja(ipos)
175  min_phi = min(min_phi, phi(m))
176  max_phi = max(max_phi, phi(m))
177  end do
178  end subroutine
179 
180  function limiter(this, r) result(theta)
181  ! -- return
182  real(dp) :: theta ! limited slope
183  ! -- dummy
184  class(utvdschemetype) :: this
185  real(dp) :: r ! ratio of successive gradients
186 
187  select case (this%limiter_id)
188  case (2) ! van Leer
189  theta = max(0.0_dp, min((r + dabs(r)) / (1.0_dp + dabs(r)), 2.0_dp))
190  case (3) ! Koren
191  theta = max(0.0_dp, min(2.0_dp * r, &
192  1.0_dp / 3.0_dp + 2.0_dp / 3.0_dp * r, 2.0_dp))
193  case (4) ! Superbee
194  theta = max(0.0_dp, min(2.0_dp * r, 1.0_dp), min(r, 2.0_dp))
195  case (5) ! van Albada
196  theta = max(0.0_dp, (r * r + r) / (r * r + 1.0_dp))
197  case (6) ! Koren modified
198  theta = max(0.0_dp, min(4.0_dp * r * r + r, &
199  1.0_dp / 3.0_dp + 2.0_dp / 3.0_dp * r, 2.0_dp))
200  case default
201  theta = dzero
202  end select
203  end function
204 
205 end module utvdschememodule
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
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 done
real constant 1
Definition: Constants.f90:76
real(dp) function, dimension(3), public node_distance(dis, n, m)
Returns the vector distance from cell n to cell m.
Definition: DisUtils.f90:39
This module defines variable data types.
Definition: kind.f90:8
type(coefficientstype) function, target compute(this, n, m, iposnm, phi)
Definition: UTVDScheme.f90:69
subroutine find_local_extrema(this, n, phi, min_phi, max_phi)
Definition: UTVDScheme.f90:163
type(utvdschemetype) function constructor(dis, fmi, gradient)
Definition: UTVDScheme.f90:55
real(dp) function limiter(this, r)
Definition: UTVDScheme.f90:181
Abstract interface for cell-based gradient computation.
Definition: IGradient.f90:15
Total Variation Diminishing (TVD) interpolation scheme.
Definition: UTVDScheme.f90:35