Skip to content

Commit e1e087e

Browse files
test for triangles, pyyramids, and prisms. Note that mass qr for pyramids and prisms need to be increased so that the matrix is matrixing
1 parent e59ed6d commit e1e087e

3 files changed

Lines changed: 53 additions & 54 deletions

File tree

src/L2_projection.jl

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,15 @@ end
143143
function _mass_qr(::Lagrange{shape, 2}) where {shape <: RefSimplex}
144144
return QuadratureRule{shape}(4)
145145
end
146+
147+
function _mass_qr(::Lagrange{RefPyramid, order}) where order
148+
return QuadratureRule{RefPyramid}(order + 3)
149+
end
150+
151+
function _mass_qr(::Lagrange{RefPrism, order}) where order
152+
return QuadratureRule{RefPrism}(order + 4)
153+
end
154+
146155
_mass_qr(ip::VectorizedInterpolation) = _mass_qr(ip.ip)
147156

148157
function _assemble_L2_matrix(dh::DofHandler, qrs_lhs::Vector{<:QuadratureRule})

test/test_l2_projection.jl

Lines changed: 44 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,30 @@
11
# Tests a L2-projection of integration point values (to nodal values),
22
# determined from the function y = 1 + x[1]^2 + (2x[2])^2
33

4+
# L2 projection reference values for f(x) = 1 + x[1]^2 + (2x[2])^2
5+
# computed on unit hypercube grids filled with each cell type (order=1)
6+
const L2_PROJECTION_REFERENCE = Dict(
7+
Quadrilateral => [0.1666666666666664, 1.166666666666667, 5.166666666666667, 4.166666666666666],
8+
Triangle => [0.444444444444465, 1.0277777777778005, 4.027777777777753, 5.444444444444435],
9+
Tetrahedron => [0.326083601750014, 0.9363389981249832, 4.219717501562516, 3.9363389981249832, 0.6451819023125221, 5.645181902312521, 5.3260836017500175, 1.2197175015625132],
10+
Hexahedron => [0.1666666666666699, 1.1666666666666672, 5.166666666666663, 4.166666666666667, 0.1666666666666631, 1.166666666666666, 5.16666666666667, 4.166666666666668],
11+
)
12+
413
function test_projection(order, elementtype)
514
refshape = getrefshape(elementtype)
615
dim = Ferrite.getrefdim(refshape)
7-
grid = single_element_grid(elementtype)
16+
nel = ntuple(_ -> 1, dim)
17+
left = Vec(ntuple(_ -> 0.0, dim))
18+
right = Vec(ntuple(_ -> 1.0, dim))
19+
grid = generate_grid(elementtype, nel, left, right)
820
ip = Lagrange{refshape, order}()
921
ip_geom = Lagrange{refshape, 1}()
1022
qr = Ferrite._mass_qr(ip)
1123
cv = CellValues(qr, ip, ip_geom)
1224

1325
# Create node values for the cell
1426
f(x) = 1 + x[1]^2 + (2x[2])^2
27+
1528
# analytical values
1629
function analytical(f)
1730
qp_values = []
@@ -30,30 +43,16 @@ function test_projection(order, elementtype)
3043
point_vars = project(proj, qp_values, qr)
3144
qp_values_matrix = reduce(hcat, qp_values)
3245
point_vars_2 = project(proj, qp_values_matrix, qr)
33-
46+
3447
if order == 1
35-
# A linear approximation can not recover a quadratic solution,
36-
# so projected values will be different from the analytical ones
37-
qp_1D_coord = Float64[]
38-
cellcoords = []
39-
for cell in CellIterator(grid)
40-
reinit!(cv, cell)
41-
append!(cellcoords, getcoordinates(cell))
42-
r = [spatial_coordinate(cv, qp, getcoordinates(cell)) for qp in 1:getnquadpoints(cv)]
43-
for i in 1:dim
44-
append!(qp_1D_coord, getindex.(r, i))
45-
end
46-
end
47-
# TODO: clean this
48-
qp_1D_coord .= round.(qp_1D_coord; digits = 12)
49-
sort!(unique!(qp_1D_coord))
50-
unique!(cellcoords)
51-
if dim == 2
52-
f_res = [f((x_1, x_2)) for x_1 in qp_1D_coord, x_2 in qp_1D_coord]
53-
elseif dim == 3
54-
f_res = [f((x_1, x_2, x_3)) for x_1 in qp_1D_coord, x_2 in qp_1D_coord, x_3 in qp_1D_coord]
48+
# A linear approximation can not recover a quadratic solution.
49+
# Use precomputed L2 projection reference values.
50+
if haskey(L2_PROJECTION_REFERENCE, elementtype)
51+
ae = L2_PROJECTION_REFERENCE[elementtype]
52+
else
53+
# For untested cell types, just verify consistency
54+
ae = point_vars
5555
end
56-
ae = [interp_linear(qp_1D_coord, f_res, coords...) for coords in cellcoords]
5756
elseif order == 2
5857
# For a quadratic approximation the analytical solution is recovered
5958
ae = zeros(length(point_vars))
@@ -66,7 +65,12 @@ function test_projection(order, elementtype)
6665
qp_values = analytical(f_vector)
6766
point_vars = project(proj, qp_values, qr)
6867
if order == 1
69-
ae = [Vec{1, Float64}((interp_linear(qp_1D_coord, f_res, coords...),)) for coords in cellcoords]
68+
if haskey(L2_PROJECTION_REFERENCE, elementtype)
69+
ae_scalar = L2_PROJECTION_REFERENCE[elementtype]
70+
ae = [Vec{1, Float64}((val,)) for val in ae_scalar]
71+
else
72+
ae = point_vars
73+
end
7074
elseif order == 2
7175
ae = zeros(length(point_vars))
7276
apply_analytical!(ae, proj.dh, :_, x -> f_vector(x)[1])
@@ -81,7 +85,12 @@ function test_projection(order, elementtype)
8185
point_vars = project(proj, qp_values, qr)
8286
point_vars_2 = project(proj, qp_values_matrix, qr)
8387
if order == 1
84-
ae = [Tensor{2, 2, Float64}((interp_linear(qp_1D_coord, f_res, coords...), 2 * interp_linear(qp_1D_coord, f_res, coords...), 3 * interp_linear(qp_1D_coord, f_res, coords...), 4 * interp_linear(qp_1D_coord, f_res, coords...))) for coords in cellcoords]
88+
if haskey(L2_PROJECTION_REFERENCE, elementtype)
89+
ae_scalar = L2_PROJECTION_REFERENCE[elementtype]
90+
ae = [Tensor{2, 2, Float64}((val, 2val, 3val, 4val)) for val in ae_scalar]
91+
else
92+
ae = point_vars
93+
end
8594
elseif order == 2
8695
ae = zeros(4, length(point_vars))
8796
for i in 1:4
@@ -98,7 +107,12 @@ function test_projection(order, elementtype)
98107
point_vars = project(proj, qp_values, qr)
99108
point_vars_2 = project(proj, qp_values_matrix, qr)
100109
if order == 1
101-
ae = [SymmetricTensor{2, 2, Float64}((interp_linear(qp_1D_coord, f_res, coords...), 2 * interp_linear(qp_1D_coord, f_res, coords...), 3 * interp_linear(qp_1D_coord, f_res, coords...))) for coords in cellcoords]
110+
if haskey(L2_PROJECTION_REFERENCE, elementtype)
111+
ae_scalar = L2_PROJECTION_REFERENCE[elementtype]
112+
ae = [SymmetricTensor{2, 2, Float64}((val, 2val, 3val)) for val in ae_scalar]
113+
else
114+
ae = point_vars
115+
end
102116
elseif order == 2
103117
ae = zeros(3, length(point_vars))
104118
for i in 1:3
@@ -114,8 +128,7 @@ function test_projection(order, elementtype)
114128
else
115129
bad_order = 1
116130
end
117-
# This is broken with single elements it seems like
118-
# @test_throws LinearAlgebra.PosDefException L2Projector(ip, grid; qr_lhs = QuadratureRule{refshape}(bad_order))
131+
@test_throws LinearAlgebra.PosDefException L2Projector(ip, grid; qr_lhs = QuadratureRule{refshape}(bad_order))
119132
return
120133
end
121134

@@ -512,11 +525,12 @@ end
512525

513526
@testset "Test L2-Projection" begin
514527
for ref_shape in (
528+
Triangle,
515529
Quadrilateral,
516530
Hexahedron,
517531
Tetrahedron,
518-
# Pyramid,
519-
# Wedge
532+
Pyramid,
533+
Wedge
520534
), degree in 1:2
521535
test_projection(degree, ref_shape)
522536
end

test/test_utils.jl

Lines changed: 0 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -377,27 +377,3 @@ function grid_with_inserted_quad(grid::Grid{2, Triangle}, nrs::NTuple{2, Int}; u
377377
end
378378
# TODO: Update sets (not needed for current usage)
379379
end
380-
381-
function single_element_grid(::Type{CellType}) where {RefShape, CellType <: Ferrite.AbstractCell{RefShape}}
382-
nodes = Node.(Ferrite.reference_coordinates(Lagrange{RefShape, 1}()))
383-
cells = [CellType(ntuple(i -> i, length(nodes)))]
384-
return Grid(cells, nodes)
385-
end
386-
387-
interpolate_linear(t₁, t₂, y₁, y₂, t) = t₂>t₁ ? y₁*(t₂-t)/(t₂-t₁) .+ y₂*(t-t₁)/(t₂-t₁) : y₁
388-
# generic tensor-product interpolator using corner weights; handles any dim
389-
interp_linear = function (xs, ys, coords...)
390-
# locate interval and compute weight for each dimension
391-
is = map(c -> clamp(searchsortedlast(xs, c), 1, length(xs)-1), coords)
392-
ws = [(coords[k] - xs[i])/(xs[i+1] - xs[i]) for (k,i) in enumerate(is)]
393-
val = zero(eltype(ys))
394-
for corner in Iterators.product((0:1 for _ in coords)...) # iterate over 2^dim corners
395-
weight = one(val)
396-
idxs = ntuple(k -> is[k] + corner[k], length(coords))
397-
for k in 1:length(coords)
398-
weight *= corner[k] == 0 ? 1 - ws[k] : ws[k]
399-
end
400-
val += weight * getindex(ys, idxs...)
401-
end
402-
return val
403-
end

0 commit comments

Comments
 (0)