Skip to content

Commit a2b8d6a

Browse files
committed
fixed hcurln1 interpolation and rearranged matrix interpolation code
1 parent 2e92ba7 commit a2b8d6a

File tree

6 files changed

+115
-120
lines changed

6 files changed

+115
-120
lines changed

src/ExtendableFEMBase.jl

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -117,7 +117,6 @@ export interpolate! # must be defined separately by each FEdefinition
117117
export nodevalues, continuify
118118
export nodevalues!, nodevalues_subset!
119119
export nodevalues_view
120-
export compute_interpolation_jacobian
121120

122121
export interpolator_matrix
123122

@@ -175,6 +174,9 @@ export PointEvaluator, evaluate!, evaluate_bary!, eval_func, eval_func_bary
175174
include("lazy_interpolate.jl")
176175
export lazy_interpolate!
177176

177+
include("interpolation_matrix_representations.jl")
178+
export compute_lazy_interpolation_jacobian
179+
export H1Pk_to_HDIVRT0_interpolator
178180

179181
# ExtendableFEMBaseUnicodePlotsExt extension
180182

src/fedefs/hcurl_n1.jl

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,8 @@ get_dofmap_pattern(FEType::Type{<:HCURLN1{2}}, ::Union{Type{FaceDofs}, Type{BFac
2727

2828
isdefined(FEType::Type{<:HCURLN1}, ::Type{<:Triangle2D}) = true
2929

30+
interior_dofs_offset(::Type{<:ON_CELLS}, ::Type{<:HCURLN1{2}}, ::Type{<:Triangle2D}) = 6
31+
3032
function N1_tangentflux_eval_2d!(result, f, qpinfo)
3133
result[1] = -f[1] * qpinfo.normal[2] # rotated normal = tangent
3234
result[1] += f[2] * qpinfo.normal[1]
@@ -57,7 +59,7 @@ end
5759

5860
function ExtendableGrids.interpolate!(Target, FE::FESpace{Tv, Ti, FEType, APT}, ::Type{ON_CELLS}, data; items = [], kwargs...) where {Tv, Ti, FEType <: HCURLN1, APT}
5961
edim = get_ncomponents(FEType)
60-
return if edim == 2
62+
if edim == 2
6163
# delegate cell faces to face interpolation
6264
subitems = slice(FE.dofgrid[CellFaces], items)
6365
interpolate!(Target, FE, ON_FACES, data; items = subitems, kwargs...)
@@ -68,8 +70,7 @@ function ExtendableGrids.interpolate!(Target, FE::FESpace{Tv, Ti, FEType, APT},
6870
end
6971

7072
# set values of interior N1 functions such that P0 moments are preserved
71-
get_interpolator(FE, ON_CELLS).evaluate!(Target, data, items; kwargs...)
72-
73+
return get_interpolator(FE, ON_CELLS).evaluate!(Target, data, items; kwargs...)
7374
end
7475

7576
# on faces dofs are only tangential fluxes
Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
"""
2+
````
3+
function compute_lazy_interpolation_jacobian(
4+
target_space::FESpace,
5+
source_space::FESpace;
6+
use_cellparents::Bool = false,
7+
kwargs...
8+
)
9+
````
10+
11+
Compute the Jacobian of the [`lazy_interpolate!`](@ref) call with respect to the
12+
`source_space` degrees of freedom, i.e. for functions ``v = \\sum_j \\alpha_j \\, \\varphi_j`` of the
13+
`source_space` and the interpolation operator ``I(v) = \\sum_k L_k(v)\\,\\phi_k = \\sum_k L_k\\left(\\sum_j \\alpha_j \\varphi_j\\right) \\, \\phi_k``
14+
into the `target_space`, this function computes the jacobian ``\\left[\\frac{\\partial L_k}{\\partial \\alpha_j}\\right]_{k,\\,j}``
15+
and returns its sparse matrix representation.
16+
17+
# Arguments
18+
- `target_space::FESpace`: Finite element space into which the interpolation ``I(v)`` is directed.
19+
- `source_space::FESpace`: Finite element space from which ``v`` is taken.
20+
21+
# Keyword Arguments
22+
- `use_cellparents`: Use parent cell information if available (can speed up the calculation if the `target_space` is defined on a subgrid of `source_space`).
23+
- `kwargs...`: Additional keyword arguments passed to lower-level `lazy_interpolate!` call.
24+
25+
# Notes
26+
- Since [`lazy_interpolate!`](@ref) is based on evaluating functions from the `source_space`
27+
using a [`ExtendableFEMBase.PointEvaluator`](@ref), this should be used carefully on finer
28+
grids as this is not the most efficient method, but will work out of the box for any
29+
`source` and `target` spaces.
30+
- This function can be used for computing prolongation or restriction operators if the `FESpace`s are defined on coarser/finer grids, respectively.
31+
32+
"""
33+
function compute_lazy_interpolation_jacobian(target_space::FESpace, source_space::FESpace; use_cellparents::Bool = false, kwargs...)
34+
# DifferentiationInterface.jacobian needs a function of signature
35+
# AbstractVector -> AbstractVector
36+
function do_interpolation(source_vector::AbstractVector; use_cellparents = use_cellparents)
37+
T = valtype(source_vector)
38+
target_vector = FEVector{T}(target_space)[1]
39+
source_FE_Vector = FEVector{T}(source_space)
40+
source_FE_Vector.entries .= source_vector
41+
42+
lazy_interpolate!(target_vector, source_FE_Vector, [(1, Identity)]; use_cellparents, kwargs...)
43+
44+
return target_vector.entries
45+
end
46+
47+
n = ndofs(source_space)
48+
49+
dense_forward_backend = AutoForwardDiff()
50+
sparse_forward_backend = AutoSparse(
51+
dense_forward_backend;
52+
sparsity_detector = TracerSparsityDetector(),
53+
coloring_algorithm = GreedyColoringAlgorithm(),
54+
)
55+
56+
M = jacobian(x -> do_interpolation(x; use_cellparents), sparse_forward_backend, ones(n))
57+
58+
return M
59+
end
60+
61+
"""
62+
````
63+
function H1Pk_to_HDIVRT0_interpolator(
64+
VRT0::FESpace,
65+
V1::FESpace
66+
)
67+
````
68+
69+
Computes the interpolation matrix from the [`H1Pk`](@ref)-conforming source space `V1`
70+
into the [`HDIVRT0`](@ref)-conforming target space `VRT0` *defined on the same grid* and
71+
returns its sparse matrix representation.
72+
73+
# Arguments
74+
- `VRT0::FESpace`: [`HDIVRT0`](@ref) target space into which the interpolation is directed.
75+
- `V1::FESpace`: [`H1Pk`](@ref) source space from which the interpolant is taken.
76+
77+
"""
78+
function H1Pk_to_HDIVRT0_interpolator(VRT0::FESpace, V1::FESpace)
79+
FEType = get_FEType(V1)
80+
facedofs_V1::VariableTargetAdjacency{Int32} = V1[FaceDofs]
81+
dim = get_ncomponents(V1)
82+
EGF = dim == 2 ? Edge1D : Triangle2D
83+
ndofs = get_ndofs(ON_FACES, FEType, EGF)
84+
order = get_polynomialorder(FEType, EGF)
85+
face_basis = get_basis(ON_FACES, FEType, EGF)
86+
result = zeros(ndofs, dim)
87+
ref_integrate!(result, EGF, order, face_basis)
88+
89+
F0 = FEMatrix(V1, VRT0)
90+
FE::ExtendableSparseMatrix{Float64, Int64} = F0.entries
91+
xgrid = V1.xgrid
92+
@assert xgrid == VRT0.xgrid
93+
xFaceVolumes = xgrid[FaceVolumes]
94+
xFaceNormals = xgrid[FaceNormals]
95+
nfaces = num_sources(xFaceNormals)
96+
for face in 1:nfaces
97+
for dof1 in 1:ndofs
98+
FE[facedofs_V1[dof1, face], face] = dot(view(result, dof1, :), view(xFaceNormals, :, face)) * xFaceVolumes[face]
99+
end
100+
end
101+
flush!(FE)
102+
return F0
103+
end

src/interpolations.jl

Lines changed: 0 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -91,63 +91,6 @@ function ExtendableGrids.interpolate!(target::FEVectorBlock, source; kwargs...)
9191
return interpolate!(target, ON_CELLS, source; kwargs...)
9292
end
9393

94-
"""
95-
````
96-
function compute_interpolation_jacobian(
97-
target_space::FESpace,
98-
source_space::FESpace;
99-
use_cellparents::Bool = false,
100-
kwargs...
101-
)
102-
````
103-
104-
Compute the Jacobian of the [`lazy_interpolate!`](@ref) call with respect to the
105-
`source_space` degrees of freedom, i.e. for functions ``v = \\sum_j \\alpha_j \\, \\varphi_j`` of the
106-
`source_space` and the interpolation operator ``I(v) = \\sum_k L_k(v)\\,\\phi_k = \\sum_k L_k\\left(\\sum_j \\alpha_j \\varphi_j\\right) \\, \\phi_k``
107-
into the `target_space`, this function computes the jacobian ``\\left[\\frac{\\partial L_k}{\\partial \\alpha_j}\\right]_{k,\\,j}``
108-
and returns its sparse matrix representation.
109-
110-
# Arguments
111-
- `target_space::FESpace`: Finite element space into which the interpolation ``I(v)`` is directed.
112-
- `source_space::FESpace`: Finite element space from which ``v`` is taken.
113-
114-
# Keyword Arguments
115-
- `use_cellparents`: Use parent cell information if available (can speed up the calculation if the `target_space` is defined on a subgrid of `source_space`).
116-
- `kwargs...`: Additional keyword arguments passed to lower-level `lazy_interpolate!` call.
117-
118-
# Notes
119-
- This function can be used for computing prolongation or restriction operators if the `FESpace`s are defined on coarser/finer grids, respectively.
120-
121-
"""
122-
function compute_interpolation_jacobian(target_space::FESpace, source_space::FESpace; use_cellparents::Bool = false, kwargs...)
123-
# DifferentiationInterface.jacobian needs a function of signature
124-
# AbstractVector -> AbstractVector
125-
function do_interpolation(source_vector::AbstractVector; use_cellparents = use_cellparents)
126-
T = valtype(source_vector)
127-
target_vector = FEVector{T}(target_space)[1]
128-
source_FE_Vector = FEVector{T}(source_space)
129-
source_FE_Vector.entries .= source_vector
130-
131-
lazy_interpolate!(target_vector, source_FE_Vector, [(1, Identity)]; use_cellparents, kwargs...)
132-
133-
return target_vector.entries
134-
end
135-
136-
n = ndofs(source_space)
137-
138-
dense_forward_backend = AutoForwardDiff()
139-
sparse_forward_backend = AutoSparse(
140-
dense_forward_backend;
141-
sparsity_detector = TracerSparsityDetector(),
142-
coloring_algorithm = GreedyColoringAlgorithm(),
143-
)
144-
145-
M = jacobian(x -> do_interpolation(x; use_cellparents), sparse_forward_backend, ones(n))
146-
147-
return M
148-
end
149-
150-
15194
"""
15295
````
15396
function nodevalues_subset!(

test/test_interpolation_matrix.jl

Lines changed: 4 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -10,13 +10,6 @@ function run_grid_interpolation_matrix_tests()
1010
H1Pk{1, 1, 5} => 5,
1111
]
1212

13-
PairTestCatalog1D = [
14-
(L2P0{1}, H1P1{1}) => 0,
15-
(H1P1{1}, H1P2{1, 1}) => 1,
16-
(H1P2{1, 1}, H1P3{1, 1}) => 2,
17-
(H1Pk{1, 1, 3}, H1Pk{1, 1, 5}) => 3,
18-
]
19-
2013
TestCatalog2D = [
2114
HCURLN0{2} => 0,
2215
HCURLN1{2} => 1,
@@ -46,16 +39,6 @@ function run_grid_interpolation_matrix_tests()
4639
H1Pk{2, 2, 5} => 5,
4740
]
4841

49-
PairTestCatalog2D = [
50-
(H1P1{2}, HDIVRT0{2}) => 0,
51-
(H1P2{2, 2}, HDIVRT0{2}) => 0,
52-
(H1P2{2, 2}, HDIVRT1{2}) => 1,
53-
(H1P2{2, 2}, HDIVRTk{2, 2}) => 2,
54-
(HDIVRT1{2}, HDIVBDM1{2}) => 1,
55-
(L2P0{2}, L2P1{2}) => 0,
56-
(H1P2B{2, 2}, H1BR{2}) => 1,
57-
]
58-
5942
TestCatalog3D = [
6043
HCURLN0{3} => 0,
6144
HDIVRT0{3} => 0,
@@ -72,15 +55,6 @@ function run_grid_interpolation_matrix_tests()
7255
H1P3{3, 3} => 3,
7356
]
7457

75-
PairTestCatalog3D = [
76-
(H1P1{3}, HDIVRT0{3}) => 0,
77-
(H1P2{3, 3}, HDIVRT0{3}) => 0,
78-
(H1P2{3, 3}, HDIVRT1{3}) => 1,
79-
(HDIVRT1{3}, HDIVBDM1{3}) => 1,
80-
(L2P0{3}, L2P1{3}) => 0,
81-
(H1P2B{3, 3}, H1BR{3}) => 1,
82-
]
83-
8458
# test interpolation of same space between refined grids
8559
function test_grid_matrix_computation(xgrid, FEType, order; broken::Bool = false, use_cellparents::Bool = false)
8660
u, ~ = exact_function(Val(dim_grid(xgrid)), order)
@@ -94,7 +68,7 @@ function run_grid_interpolation_matrix_tests()
9468
target_vector = FEVector(target_FES)
9569
interpolate!(target_vector[1], u; bonus_quadorder = order)
9670

97-
interpolation_matrix = compute_interpolation_jacobian(target_FES, source_FES; use_cellparents)
71+
interpolation_matrix = compute_lazy_interpolation_jacobian(target_FES, source_FES; use_cellparents)
9872
matrix_interpolated_entries = interpolation_matrix * source_vector.entries
9973

10074
return @test norm(target_vector.entries - matrix_interpolated_entries) < tolerance
@@ -177,34 +151,6 @@ function run_space_interpolation_matrix_tests()
177151
(H1P2B{3, 3}, H1BR{3}) => 1,
178152
]
179153

180-
181-
# V1: H1-conforming space, computes matrix into RT0 space
182-
function RT0_interpolator(V1::FESpace, VRT0::FESpace)
183-
FEType = get_FEType(V1)
184-
facedofs_V1::VariableTargetAdjacency{Int32} = V1[FaceDofs]
185-
dim = get_ncomponents(V1)
186-
EGF = dim == 2 ? Edge1D : Triangle2D
187-
ndofs = get_ndofs(ON_FACES, FEType, EGF)
188-
order = get_polynomialorder(FEType, EGF)
189-
face_basis = get_basis(ON_FACES, FEType, EGF)
190-
result = zeros(ndofs, dim)
191-
ref_integrate!(result, EGF, order, face_basis)
192-
193-
F0 = FEMatrix(V1, VRT0)
194-
FE::ExtendableSparseMatrix{Float64, Int64} = F0.entries
195-
xgrid = V1.xgrid
196-
xFaceVolumes = xgrid[FaceVolumes]
197-
xFaceNormals = xgrid[FaceNormals]
198-
nfaces = num_sources(xFaceNormals)
199-
for face in 1:nfaces
200-
for dof1 in 1:ndofs
201-
FE[facedofs_V1[dof1, face], face] = dot(view(result, dof1, :), view(xFaceNormals, :, face)) * xFaceVolumes[face]
202-
end
203-
end
204-
flush!(FE)
205-
return F0
206-
end
207-
208154
# test interpolation for different elements on same grid
209155
function test_space_matrix_computation(xgrid, source_FEType, target_FEType, order; broken::Bool = false, use_cellparents::Bool = false)
210156
u, ~ = exact_function(Val(dim_grid(xgrid)), order)
@@ -218,7 +164,7 @@ function run_space_interpolation_matrix_tests()
218164
target_vector = FEVector(target_FES)
219165
interpolate!(target_vector[1], u; bonus_quadorder = order)
220166

221-
interpolation_matrix = compute_interpolation_jacobian(target_FES, source_FES; use_cellparents)
167+
interpolation_matrix = compute_lazy_interpolation_jacobian(target_FES, source_FES; use_cellparents)
222168
matrix_interpolated_entries = interpolation_matrix * source_vector.entries
223169

224170
return @test norm(target_vector.entries - matrix_interpolated_entries) < tolerance
@@ -250,8 +196,8 @@ function run_space_interpolation_matrix_tests()
250196
if (source_element, target_element) == (H1P1{2}, HDIVRT0{2}) && !broken
251197
source_FES = FESpace{source_element}(xgrid; broken)
252198
target_FES = FESpace{target_element}(xgrid; broken)
253-
autodiff_matrix = compute_interpolation_jacobian(target_FES, source_FES; use_cellparents = false)
254-
RT0_matrix = RT0_interpolator(source_FES, target_FES)
199+
autodiff_matrix = compute_lazy_interpolation_jacobian(target_FES, source_FES; use_cellparents = false)
200+
RT0_matrix = H1Pk_to_HDIVRT0_interpolator(target_FES, source_FES)
255201

256202
@test norm(autodiff_matrix' - RT0_matrix[1]) < tolerance
257203
end

test/test_interpolators.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,7 @@ function run_interpolator_tests()
109109
show(devnull, Solution)
110110

111111
# compute error
112-
error = compute_error(Solution[1], u, order)
112+
error = compute_error(Solution[1], u, order + 1)
113113
println("FEType = $FEType $(broken ? "broken" : "") $AT | ndofs = $(FES.ndofs) | order = $order | error = $(norm(error, Inf))")
114114
return @test norm(error) < tolerance
115115
end

0 commit comments

Comments
 (0)