MODFLOW 6  version 6.7.0.dev3
USGS Modular Hydrologic Model
LeastSquaresGradient.f90
Go to the documentation of this file.
2  use kindmodule, only: dp, i4b
3  use constantsmodule, only: done
4 
5  Use igradient
6  use basedismodule, only: disbasetype
7  use pseudoinversemodule, only: pinv
9 
10  implicit none
11  private
12 
13  public :: leastsquaresgradienttype
14 
15  type array2d
16  real(dp), dimension(:, :), allocatable :: data
17  end type array2d
18 
19  !> @brief Weighted least-squares gradient method for structured and unstructured grids.
20  !!
21  !! This class implements a least-squares gradient reconstruction for use on both structured and unstructured grids.
22  !! For each cell, it precomputes and caches a gradient reconstruction matrix using the Moore-Penrose pseudoinverse,
23  !! based on the geometry and connectivity of the mesh. The operator is created once during initialization
24  !! and can then be efficiently applied to any scalar field to compute the gradient in each cell.
25  !! The gradient can then be computed by multiplying the reconstruction matrix with the difference vector.
26  !! ∇ɸ = R * ∑(ɸ_i - ɸ_up), where i are the neighboring cells.
27  !!
28  !! Usage:
29  !! 1. Create the gradient object with the discretization
30  !! 2. Set the scalar field using `set_field(phi)` where phi is the field for which gradients are computed
31  !! 3. Retrieve gradients for any cell using the `get(n)` method
32  !!
33  !! - The gradient operator is constructed using normalized direction vectors between cell centers,
34  !! scaled by the inverse of the distance.
35  !! - The least-squares approach ensures robust gradients even for irregular or rank-deficient stencils.
36  !! - The operator is cached for each cell, so gradient computation is efficient for repeated queries.
37  !! - The `set_field` method establishes a pointer to the scalar field data.
38  !! - The `get` method computes the gradient for any cell using the current scalar field.
39  !!
40  !! @note Boundary cells are not handled in a special manner. This may impact the quality of the gradient
41  !! near boundaries, especially if a cell does not have enough neighbors (fewer than three in 3D).
42  !<
44  class(disbasetype), pointer :: dis
45  real(dp), dimension(:), pointer :: phi
46  type(array2d), allocatable, dimension(:) :: r ! Gradient reconstruction matrix
47  contains
48  procedure :: get
49  procedure :: set_field
50 
51  procedure, private :: compute_cell_gradient
54 
55  interface leastsquaresgradienttype
56  module procedure constructor
57  end interface leastsquaresgradienttype
58 
59 contains
60  function constructor(dis) Result(gradient)
61  ! --dummy
62  class(disbasetype), pointer, intent(in) :: dis
63  !-- return
64  type(leastsquaresgradienttype) :: gradient
65  ! -- local
66  integer(I4B) :: n, nodes
67 
68  gradient%dis => dis
69  nodes = dis%nodes
70 
71  ! -- Compute the gradient rec
72  nodes = dis%nodes
73  allocate (gradient%R(dis%nodes))
74  do n = 1, nodes
75  gradient%R(n)%data = gradient%create_gradient_reconstruction_matrix(n)
76  end do
77  end function constructor
78 
79  function create_gradient_reconstruction_matrix(this, n) result(R)
80  ! -- dummy
81  class(leastsquaresgradienttype) :: this
82  integer(I4B), intent(in) :: n ! Cell index for which to create the operator
83  real(dp), dimension(:, :), allocatable :: r ! The resulting gradient reconstruction matrix (3 x number_connections)
84  ! -- local
85  integer(I4B) :: number_connections ! Number of connected neighboring cells
86  integer(I4B) :: ipos, local_pos, m ! Loop indices and neighbor cell index
87  real(dp) :: length ! Distance between cell centers
88  real(dp), dimension(3) :: dnm ! Vector from cell n to neighbor m
89  real(dp), dimension(:, :), allocatable :: d ! Matrix of normalized direction vectors (number_connections x 3)
90  real(dp), dimension(:, :), allocatable :: inverse_distance ! Diagonal scaling matrix (number_connections x number_connections),
91  ! where each diagonal entry is the inverse of the distance between
92 
93  number_connections = number_connected_faces(this%dis, n)
94 
95  allocate (d(number_connections, 3))
96  allocate (r(3, number_connections))
97  allocate (inverse_distance(number_connections, number_connections))
98 
99  inverse_distance = 0
100  d = 0
101 
102  ! Assemble the distance matrix
103  ! Handle the internal connections
104  local_pos = 1
105  do ipos = this%dis%con%ia(n) + 1, this%dis%con%ia(n + 1) - 1
106  m = this%dis%con%ja(ipos)
107 
108  dnm = node_distance(this%dis, n, m)
109  length = norm2(dnm)
110 
111  d(local_pos, :) = dnm / length
112  inverse_distance(local_pos, local_pos) = 1.0_dp / length
113 
114  local_pos = local_pos + 1
115  end do
116 
117  ! Compute the gradient reconstructions matrix
118  r = matmul(pinv(d), inverse_distance)
119 
121 
122  function get(this, n) result(grad_c)
123  ! -- dummy
124  class(leastsquaresgradienttype), target :: this
125  integer(I4B), intent(in) :: n
126  !-- return
127  real(dp), dimension(3) :: grad_c
128 
129  grad_c = this%compute_cell_gradient(n)
130  end function get
131 
132  subroutine set_field(this, phi)
133  ! -- dummy
134  class(leastsquaresgradienttype), target :: this
135  real(DP), dimension(:), pointer, intent(in) :: phi
136 
137  this%phi => phi
138  end subroutine set_field
139 
140  function compute_cell_gradient(this, n) result(grad_c)
141  ! -- return
142  real(dp), dimension(3) :: grad_c
143  ! -- dummy
144  class(leastsquaresgradienttype), target :: this
145  integer(I4B), intent(in) :: n
146  ! -- local
147  real(dp), dimension(:, :), pointer :: r
148  integer(I4B) :: ipos, local_pos
149  integer(I4B) :: number_connections
150 
151  integer(I4B) :: m
152  real(dp), dimension(:), allocatable :: dc
153 
154  ! Assemble the concentration difference vector
155  number_connections = number_connected_faces(this%dis, n)
156  allocate (dc(number_connections))
157  local_pos = 1
158  do ipos = this%dis%con%ia(n) + 1, this%dis%con%ia(n + 1) - 1
159  m = this%dis%con%ja(ipos)
160  dc(local_pos) = this%phi(m) - this%phi(n)
161  local_pos = local_pos + 1
162  end do
163 
164  ! Compute the cells gradient
165  r => this%R(n)%data
166  grad_c = matmul(r, dc)
167 
168  end function compute_cell_gradient
169 
This module contains simulation constants.
Definition: Constants.f90:9
real(dp), parameter done
real constant 1
Definition: Constants.f90:76
integer(i4b) function, public number_connected_faces(dis, n)
Returns the number of connected faces for a given cell.
Definition: DisUtils.f90:22
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
real(dp) function, dimension(3) get(this, n)
real(dp) function, dimension(:, :), allocatable create_gradient_reconstruction_matrix(this, n)
real(dp) function, dimension(3) compute_cell_gradient(this, n)
type(leastsquaresgradienttype) function constructor(dis)
real(dp) function, dimension(size(a, dim=2), size(a, dim=1)), public pinv(A)
Abstract interface for cell-based gradient computation.
Definition: IGradient.f90:15
Weighted least-squares gradient method for structured and unstructured grids.