Skip to content

Commit 9959bfd

Browse files
authored
(feat) Utvd optimization (#2427)
* Refactor tsp-adv. Move different interpolation scheme to separate files * Add the SVD-algorithm and the pseudoinverse method * Add the new unstructured TVD limiter * Fix merge * Add UTVD tests to existing unit tests. Fix correct stencil size on exchanges * Apply review comments * Fix lint error * Apply review comments * Apply review comment. Use pinv directly on the distance matrix omstead of the G matrix * Optimize the limiter by caching several values * Clean up * Fix deconstructor call * Move some function around * Rename variable to better match documentation nomenclature * Apply review comments. Make the flow more explicit
1 parent 19295a9 commit 9959bfd

File tree

13 files changed

+432
-63
lines changed

13 files changed

+432
-63
lines changed

make/makefile

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -357,6 +357,7 @@ $(OBJDIR)/VectorInterpolation.o \
357357
$(OBJDIR)/swf-cxs.o \
358358
$(OBJDIR)/OutputControlData.o \
359359
$(OBJDIR)/gwf-ic.o \
360+
$(OBJDIR)/LocalCellExtrema.o \
360361
$(OBJDIR)/InterpolationSchemeInterface.o \
361362
$(OBJDIR)/IGradient.o \
362363
$(OBJDIR)/DisUtils.o \
@@ -388,6 +389,7 @@ $(OBJDIR)/TVDScheme.o \
388389
$(OBJDIR)/TspAdvOptions.o \
389390
$(OBJDIR)/LeastSquaresGradient.o \
390391
$(OBJDIR)/CentralDifferenceScheme.o \
392+
$(OBJDIR)/CachedGradient.o \
391393
$(OBJDIR)/AdvSchemeEnum.o \
392394
$(OBJDIR)/UzfCellGroup.o \
393395
$(OBJDIR)/Xt3dInterface.o \

msvs/mf6core.vfproj

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -390,6 +390,7 @@
390390
<File RelativePath="..\src\Model\SurfaceWaterFlow\swf.f90"/></Filter>
391391
<Filter Name="TransportModel">
392392
<Filter Name="Gradient">
393+
<File RelativePath="..\src\Model\TransportModel\Gradient\CachedGradient.f90"/>
393394
<File RelativePath="..\src\Model\TransportModel\Gradient\IGradient.f90"/>
394395
<File RelativePath="..\src\Model\TransportModel\Gradient\LeastSquaresGradient.f90"/></Filter>
395396
<Filter Name="InterpolationScheme">
@@ -700,6 +701,7 @@
700701
<File RelativePath="..\src\Utilities\ListIterator.f90"/>
701702
<File RelativePath="..\src\Utilities\ListNode.f90"/>
702703
<File RelativePath="..\src\Utilities\ListReader.f90"/>
704+
<File RelativePath="..\src\Utilities\LocalCellExtrema.f90"/>
703705
<File RelativePath="..\src\Utilities\LongLineReader.f90"/>
704706
<File RelativePath="..\src\Utilities\MathUtil.f90"/>
705707
<File RelativePath="..\src\Utilities\Message.f90"/>
Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
module CachedGradientModule
2+
use KindModule, only: DP, I4B
3+
use ConstantsModule, only: DONE
4+
5+
Use IGradient
6+
use BaseDisModule, only: DisBaseType
7+
8+
implicit none
9+
private
10+
11+
public :: CachedGradientType
12+
13+
!> @brief Decorator that adds caching to any gradient computation implementation
14+
!!
15+
!! This class wraps any IGradientType implementation and provides caching functionality
16+
!! using the Decorator pattern. When set_field is called, it pre-computes gradients
17+
!! for all nodes and stores them in memory for fast O(1) retrieval. This trades memory
18+
!! for speed when gradients are accessed multiple times for the same scalar field.
19+
!!
20+
!! The class takes ownership of the wrapped gradient object via move semantics and
21+
!! provides the same interface as any other gradient implementation. This allows it
22+
!! to be used transparently in place of the original gradient object.
23+
!!
24+
!! Usage pattern:
25+
!! 1. Create a base gradient implementation (e.g., LeastSquaresGradientType)
26+
!! 2. Wrap it with CachedGradientType using the constructor
27+
!! 3. Call set_field() once to pre-compute all gradients
28+
!! 4. Call get() multiple times for fast cached lookups
29+
!!
30+
!! @note The wrapped gradient object is moved (not copied) during construction
31+
!! for efficient memory management.
32+
!<
33+
type, extends(IGradientType) :: CachedGradientType
34+
private
35+
class(DisBaseType), pointer :: dis
36+
class(IGradientType), allocatable :: gradient
37+
real(DP), dimension(:, :), allocatable :: cached_gradients ! gradients at nodes
38+
contains
39+
procedure :: get
40+
procedure :: set_field
41+
final :: destructor
42+
end type CachedGradientType
43+
44+
interface CachedGradientType
45+
module procedure Constructor
46+
end interface CachedGradientType
47+
48+
contains
49+
50+
function constructor(gradient, dis) result(cached_gradient)
51+
! --dummy
52+
class(IGradientType), allocatable, intent(inout) :: gradient
53+
class(DisBaseType), pointer, intent(in) :: dis
54+
!-- return
55+
type(CachedGradientType) :: cached_gradient
56+
57+
cached_gradient%dis => dis
58+
59+
call move_alloc(gradient, cached_gradient%gradient) ! Take ownership
60+
allocate (cached_gradient%cached_gradients(dis%nodes, 3))
61+
62+
end function constructor
63+
64+
subroutine destructor(this)
65+
! -- dummy
66+
type(CachedGradientType), intent(inout) :: this
67+
68+
deallocate (this%cached_gradients)
69+
end subroutine destructor
70+
71+
function get(this, n) result(grad_c)
72+
! -- dummy
73+
class(CachedGradientType), target :: this
74+
integer(I4B), intent(in) :: n
75+
!-- return
76+
real(DP), dimension(3) :: grad_c
77+
78+
grad_c = this%cached_gradients(n, :)
79+
end function get
80+
81+
subroutine set_field(this, phi)
82+
! -- dummy
83+
class(CachedGradientType), target :: this
84+
real(DP), dimension(:), pointer, intent(in) :: phi
85+
! -- local
86+
integer(I4B) :: n
87+
88+
call this%gradient%set_field(phi)
89+
do n = 1, this%dis%nodes
90+
this%cached_gradients(n, :) = this%gradient%get(n)
91+
end do
92+
93+
end subroutine set_field
94+
95+
end module CachedGradientModule

src/Model/TransportModel/Gradient/IGradient.f90

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,22 +15,31 @@ module IGradient
1515
type, abstract :: IGradientType
1616
contains
1717
procedure(get_if), deferred :: get
18+
procedure(set_field_if), deferred :: set_field
1819
end type IGradientType
1920

2021
abstract interface
2122

22-
function get_if(this, n, c) result(grad_c)
23+
function get_if(this, n) result(grad_c)
2324
! -- import
2425
import IGradientType
2526
import DP, I4B
2627
! -- dummy
2728
class(IGradientType), target :: this
2829
integer(I4B), intent(in) :: n
29-
real(DP), dimension(:), intent(in) :: c
3030
!-- return
3131
real(DP), dimension(3) :: grad_c
3232
end function
3333

34+
subroutine set_field_if(this, phi)
35+
! -- import
36+
import IGradientType
37+
import DP, I4B
38+
! -- dummy
39+
class(IGradientType), target :: this
40+
real(DP), dimension(:), pointer, intent(in) :: phi
41+
end subroutine
42+
3443
end interface
3544

3645
contains

src/Model/TransportModel/Gradient/LeastSquaresGradient.f90

Lines changed: 26 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -25,20 +25,28 @@ module LeastSquaresGradientModule
2525
!! The gradient can then be computed by multiplying the reconstruction matrix with the difference vector.
2626
!! ∇ɸ = R * ∑(ɸ_i - ɸ_up), where i are the neighboring cells.
2727
!!
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+
!!
2833
!! - The gradient operator is constructed using normalized direction vectors between cell centers,
2934
!! scaled by the inverse of the distance.
3035
!! - The least-squares approach ensures robust gradients even for irregular or rank-deficient stencils.
3136
!! - The operator is cached for each cell, so gradient computation is efficient for repeated queries.
32-
!! - The class provides a `get` method to compute the gradient for any cell and scalar field.
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.
3339
!!
3440
!! @note Boundary cells are not handled in a special manner. This may impact the quality of the gradient
3541
!! near boundaries, especially if a cell does not have enough neighbors (fewer than three in 3D).
3642
!<
3743
type, extends(IGradientType) :: LeastSquaresGradientType
3844
class(DisBaseType), pointer :: dis
45+
real(DP), dimension(:), pointer :: phi
3946
type(Array2D), allocatable, dimension(:) :: R ! Gradient reconstruction matrix
4047
contains
4148
procedure :: get
49+
procedure :: set_field
4250

4351
procedure, private :: compute_cell_gradient
4452
procedure, private :: create_gradient_reconstruction_matrix
@@ -79,16 +87,16 @@ function create_gradient_reconstruction_matrix(this, n) result(R)
7987
real(DP) :: length ! Distance between cell centers
8088
real(DP), dimension(3) :: dnm ! Vector from cell n to neighbor m
8189
real(DP), dimension(:, :), allocatable :: d ! Matrix of normalized direction vectors (number_connections x 3)
82-
real(DP), dimension(:, :), allocatable :: grad_scale ! Diagonal scaling matrix (number_connections x number_connections),
90+
real(DP), dimension(:, :), allocatable :: inverse_distance ! Diagonal scaling matrix (number_connections x number_connections),
8391
! where each diagonal entry is the inverse of the distance between
8492

8593
number_connections = number_connected_faces(this%dis, n)
8694

8795
allocate (d(number_connections, 3))
8896
allocate (R(3, number_connections))
89-
allocate (grad_scale(number_connections, number_connections))
97+
allocate (inverse_distance(number_connections, number_connections))
9098

91-
grad_scale = 0
99+
inverse_distance = 0
92100
d = 0
93101

94102
! Assemble the distance matrix
@@ -101,34 +109,40 @@ function create_gradient_reconstruction_matrix(this, n) result(R)
101109
length = norm2(dnm)
102110

103111
d(local_pos, :) = dnm / length
104-
grad_scale(local_pos, local_pos) = 1.0_dp / length
112+
inverse_distance(local_pos, local_pos) = 1.0_dp / length
105113

106114
local_pos = local_pos + 1
107115
end do
108116

109117
! Compute the gradient reconstructions matrix
110-
R = matmul(pinv(d), grad_scale)
118+
R = matmul(pinv(d), inverse_distance)
111119

112120
end function create_gradient_reconstruction_matrix
113121

114-
function get(this, n, c) result(grad_c)
122+
function get(this, n) result(grad_c)
115123
! -- dummy
116124
class(LeastSquaresGradientType), target :: this
117125
integer(I4B), intent(in) :: n
118-
real(DP), dimension(:), intent(in) :: c
119126
!-- return
120127
real(DP), dimension(3) :: grad_c
121128

122-
grad_c = this%compute_cell_gradient(n, c)
129+
grad_c = this%compute_cell_gradient(n)
123130
end function get
124131

125-
function compute_cell_gradient(this, n, phi_new) result(grad_c)
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)
126141
! -- return
127142
real(DP), dimension(3) :: grad_c
128143
! -- dummy
129144
class(LeastSquaresGradientType), target :: this
130145
integer(I4B), intent(in) :: n
131-
real(DP), dimension(:), intent(in) :: phi_new
132146
! -- local
133147
real(DP), dimension(:, :), pointer :: R
134148
integer(I4B) :: ipos, local_pos
@@ -143,7 +157,7 @@ function compute_cell_gradient(this, n, phi_new) result(grad_c)
143157
local_pos = 1
144158
do ipos = this%dis%con%ia(n) + 1, this%dis%con%ia(n + 1) - 1
145159
m = this%dis%con%ja(ipos)
146-
dc(local_pos) = phi_new(m) - phi_new(n)
160+
dc(local_pos) = this%phi(m) - this%phi(n)
147161
local_pos = local_pos + 1
148162
end do
149163

src/Model/TransportModel/InterpolationScheme/CentralDifferenceScheme.f90

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,10 @@ module CentralDifferenceSchemeModule
1515
private
1616
class(DisBaseType), pointer :: dis
1717
type(TspFmiType), pointer :: fmi
18+
real(DP), dimension(:), pointer :: phi
1819
contains
1920
procedure :: compute
21+
procedure :: set_field
2022
end type CentralDifferenceSchemeType
2123

2224
interface CentralDifferenceSchemeType
@@ -36,15 +38,27 @@ function constructor(dis, fmi) result(interpolation_scheme)
3638

3739
end function constructor
3840

39-
function compute(this, n, m, iposnm, phi) result(phi_face)
41+
!> @brief Set the scalar field for which interpolation will be computed
42+
!!
43+
!! This method establishes a pointer to the scalar field data for
44+
!! subsequent central difference interpolation computations.
45+
!<
46+
subroutine set_field(this, phi)
47+
! -- dummy
48+
class(CentralDifferenceSchemeType), target :: this
49+
real(DP), intent(in), dimension(:), pointer :: phi
50+
51+
this%phi => phi
52+
end subroutine set_field
53+
54+
function compute(this, n, m, iposnm) result(phi_face)
4055
!-- return
4156
type(CoefficientsType) :: phi_face
4257
! -- dummy
4358
class(CentralDifferenceSchemeType), target :: this
4459
integer(I4B), intent(in) :: n
4560
integer(I4B), intent(in) :: m
4661
integer(I4B), intent(in) :: iposnm
47-
real(DP), intent(in), dimension(:) :: phi
4862
! -- local
4963
real(DP) :: lnm, lmn, omega
5064

@@ -66,4 +80,5 @@ function compute(this, n, m, iposnm, phi) result(phi_face)
6680
phi_face%c_m = DONE - omega
6781

6882
end function compute
83+
6984
end module CentralDifferenceSchemeModule

src/Model/TransportModel/InterpolationScheme/InterpolationSchemeInterface.f90

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,11 +16,18 @@ module InterpolationSchemeInterfaceModule
1616
type, abstract :: InterpolationSchemeInterface
1717
contains
1818
procedure(compute_if), deferred :: compute
19+
procedure(set_field_if), deferred :: set_field
1920
end type InterpolationSchemeInterface
2021

2122
abstract interface
2223

23-
function compute_if(this, n, m, iposnm, phi) result(phi_face)
24+
!> @brief Compute interpolation coefficients for a face value
25+
!!
26+
!! This method computes the coefficients needed to interpolate a scalar field
27+
!! value at the face between two connected cells. The method returns coefficients
28+
!! that define how the face value depends on the cell-centered values.
29+
!<
30+
function compute_if(this, n, m, iposnm) result(phi_face)
2431
! -- import
2532
import DP, I4B
2633
import InterpolationSchemeInterface
@@ -32,9 +39,23 @@ function compute_if(this, n, m, iposnm, phi) result(phi_face)
3239
integer(I4B), intent(in) :: n
3340
integer(I4B), intent(in) :: m
3441
integer(I4B), intent(in) :: iposnm
35-
real(DP), intent(in), dimension(:) :: phi
3642
end function
3743

44+
!> @brief Set the scalar field for which interpolation will be computed
45+
!!
46+
!! This method establishes a pointer to the scalar field data that will be
47+
!! used for subsequent interpolation computations. Implementations may also
48+
!! update any dependent cached data to ensure consistent results.
49+
!<
50+
subroutine set_field_if(this, phi)
51+
! -- import
52+
import DP
53+
import InterpolationSchemeInterface
54+
! -- dummy
55+
class(InterpolationSchemeInterface), target :: this
56+
real(DP), intent(in), dimension(:), pointer :: phi
57+
end subroutine
58+
3859
end interface
3960

4061
end module InterpolationSchemeInterfaceModule

0 commit comments

Comments
 (0)