Skip to content

Commit

Permalink
Fix gradient interpolation+regression test.
Browse files Browse the repository at this point in the history
  • Loading branch information
termi-official committed Nov 7, 2023
1 parent 0db747c commit 71b543b
Show file tree
Hide file tree
Showing 2 changed files with 64 additions and 137 deletions.
22 changes: 3 additions & 19 deletions src/utils.jl
Original file line number Diff line number Diff line change
Expand Up @@ -7,24 +7,8 @@ const TetrahedralCell = Ferrite.AbstractCell{<:Ferrite.RefTetrahedron}
getdim(::Ferrite.AbstractRefShape{rdim}) where rdim = rdim
getdim(::Type{<:Ferrite.AbstractRefShape{rdim}}) where rdim = rdim

get_gradient_interpolation(::Lagrange{shape,order}) where {sdim,shape<:Ferrite.AbstractRefShape{sdim},order} = VectorizedInterpolation{shape,DiscontinuousLagrange{shape,order-1}()}
get_gradient_interpolation_type(::Type{Lagrange{shape,order}}) where {sdim,shape<:Ferrite.AbstractRefShape{sdim},order} = DiscontinuousLagrange{shape,order-1}

function Ferrite.shape_value(ipv::VectorizedInterpolation{4, shape}, ξ::Tensors.Vec{2, T}, I::Int) where {shape <: Ferrite.AbstractRefShape{2}, T}
i0, c0 = divrem(I - 1, 4)
i = i0 + 1
c = c0 + 1
v = Ferrite.shape_value(ipv.ip, ξ, i)
return SVector(ntuple(j -> j == c ? v : zero(v), 4))
end

function Ferrite.shape_value(ipv::VectorizedInterpolation{9, shape}, ξ::Tensors.Vec{3, T}, I::Int) where {shape <: Ferrite.AbstractRefShape{3}, T}
i0, c0 = divrem(I - 1, 9)
i = i0 + 1
c = c0 + 1
v = Ferrite.shape_value(ipv.ip, ξ, i)
return SVector(ntuple(j -> j == c ? v : zero(v), 9))
end
get_gradient_interpolation(::Lagrange{shape,order}) where {sdim,shape<:Ferrite.AbstractRefShape{sdim},order} = VectorizedInterpolation{sdim}(DiscontinuousLagrange{shape,order-1}())
get_gradient_interpolation_type(::Type{Lagrange{shape,order}}) where {sdim,shape<:Ferrite.AbstractRefShape{sdim},order} = VectorizedInterpolation{sdim,shape,order-1,DiscontinuousLagrange{shape,order-1}}

# Note: This extracts the face spanned by the vertices, not the actual face!
linear_face_cell(cell::TetrahedralCell, local_face_idx::Int) = Triangle(Ferrite.faces(cell)[local_face_idx])
Expand Down Expand Up @@ -494,7 +478,7 @@ function interpolate_gradient_field(dh::DofHandler{spatial_dim}, u::AbstractVect
for (cell_num, cell) in enumerate(Ferrite.CellIterator(dh))
# Get element dofs on parent field
Ferrite.celldofs!(cell_dofs, dh, cell_num)
uᵉ .= u[cell_dofs[Ferrite.dof_range(dh, field_name)]]
uᵉ = @views u[cell_dofs[Ferrite.dof_range(dh, field_name)]]

# And initialize cellvalues for the cell to evaluate the gradient at the basis functions
# of the gradient field
Expand Down
179 changes: 61 additions & 118 deletions test/runtests.jl
Original file line number Diff line number Diff line change
@@ -1,144 +1,87 @@
using FerriteViz
using FerriteViz, Ferrite
using Test

include("../docs/src/ferrite-examples/heat-equation.jl")
include("../docs/src/ferrite-examples/linear-elasticity.jl")
_test_tolerance(ip::Interpolation{<:Any,1}) = 5e-1
_test_tolerance(ip::Interpolation) = 1e-8

# TODO move this into Ferrite core

@testset "gradient fields (scalar)" begin
@testset "gradient fields" begin
# Check scalar problems
for (size, geo, ip) [(21, Triangle, Lagrange{RefTriangle,1}()),
(7,Triangle, Lagrange{RefTriangle,2}()),
(5,Triangle, Lagrange{RefTriangle,3}()),
#(21,Tetrahedron, Lagrange{3,RefTetrahedron,1}()), #converges rather slowly in the gradient
(7,Tetrahedron, Lagrange{3,RefTetrahedron,2}()),
(21,Quadrilateral, Lagrange{RefQuadrilateral,1}()),
(7,Quadrilateral, Lagrange{RefQuadrilateral,2}()),
#(21,Hexahedron, Lagrange{3,RefHexahedron,1}()), # slows down the pipeline quite a bit, so left out
(7,Hexahedron, Lagrange{3,RefHexahedron,2}())]
@testset "($size, $geo, $ip)" begin
# Compute solution
dh, u = manufactured_heat_problem(geo, ip, size)
ip_geo = Ferrite.default_interpolation(typeof(Ferrite.getcells(FerriteViz.get_grid(dh), 1)))
for (num_elements_per_dim, geo, ip) [
(4, Triangle, Lagrange{RefTriangle,1}()),
(2,Triangle, Lagrange{RefTriangle,2}()),
(2,Triangle, Lagrange{RefTriangle,3}()),
(5,Tetrahedron, Lagrange{RefTetrahedron,1}()),
(3,Tetrahedron, Lagrange{RefTetrahedron,2}()),
(4,Quadrilateral, Lagrange{RefQuadrilateral,1}()),
(2,Quadrilateral, Lagrange{RefQuadrilateral,2}()),
(4,Hexahedron, Lagrange{RefHexahedron,1}()),
(2,Hexahedron, Lagrange{RefHexahedron,2}())
]
@testset failfast=true "scalar($num_elements_per_dim, $geo, $ip)" begin
# Get solution
dim = Ferrite.getdim(ip)
qr = QuadratureRule{Ferrite.getrefshape(ip)}(1)
grid = generate_grid(geo, ntuple(x->num_elements_per_dim, dim));

# Check solution
cellvalues = CellValues(qr, ip, ip_geo);
for cell in CellIterator(dh)
reinit!(cellvalues, cell)
n_basefuncs = getnbasefunctions(cellvalues)
coords = getcoordinates(cell)
uₑ = u[celldofs(cell)]
for q_point in 1:getnquadpoints(cellvalues)
x = spatial_coordinate(cellvalues, q_point, coords)
for i in 1:n_basefuncs
uₐₙₐ = prod(cos, x*π/2)
uₐₚₚᵣₒₓ = function_value(cellvalues, q_point, uₑ)
@test isapprox(uₐₙₐ, uₐₚₚᵣₒₓ; atol=1e-2)
end
end
end
dh = DofHandler(grid)
add!(dh, :u, ip)
close!(dh);

u = Vector{Float64}(undef, ndofs(dh))
f_ana(x) = sum(0.5 * x.^2)
Ferrite.apply_analytical!(u, dh, :u, f_ana)

# Compute gradient/flux field
(dh_grad, u_grad) = FerriteViz.interpolate_gradient_field(dh, u, :u)

# Check gradient of solution
cellvalues_grad = CellValues(qr, Ferrite.getfieldinterpolation(dh_grad, :gradient), ip_geo);
qr = QuadratureRule{Ferrite.getrefshape(ip)}(2)
ip_geo = Ferrite.default_interpolation(geo)
ip_grad = Ferrite.getfieldinterpolation(dh_grad, Ferrite.find_field(dh_grad, :gradient))
cellvalues_grad = Ferrite.PointValuesInternal(qr.points[1], ip_grad)
cellvalues_geo = Ferrite.PointValuesInternal(qr.points[1], ip_geo)
for cell in CellIterator(dh_grad)
reinit!(cellvalues_grad, cell)
n_basefuncs = getnbasefunctions(cellvalues_grad)
coords = getcoordinates(cell)
uₑ = u_grad[celldofs(cell)]
for q_point in 1:getnquadpoints(cellvalues_grad)
x = spatial_coordinate(cellvalues_grad, q_point, coords)
for i 1:n_basefuncs
uₐₚₚᵣₒₓ = function_value(cellvalues_grad, q_point, uₑ)
for d 1:dim
uₐₙₐ = π/2
for j 1:(d-1)
uₐₙₐ *= cos(x[j]*π/2)
end
uₐₙₐ *= -sin(x[d]*π/2)
for j (d+1):dim
uₐₙₐ *= cos(x[j]*π/2)
end
@test isapprox(uₐₙₐ, uₐₚₚᵣₒₓ[d]; atol=1e-1)
end
end
x = function_value(cellvalues_geo, q_point, coords)
uₐₚₚᵣₒₓ = function_value(cellvalues_grad, q_point, uₑ)
uₐₙₐ = Tensors.gradient(f_ana, x)
@test isapprox(uₐₙₐ, uₐₚₚᵣₒₓ;atol=_test_tolerance(ip))
end
end
end
end
end

# Masterpiece approaching....
@testset "gradient fields (vectorial)" begin
# Check scalar problems
@testset "($size, $geo, $ip)" for (size, geo, ip) [
(21, Triangle, Lagrange{RefTriangle,1}()^2),
(7,Triangle, Lagrange{RefTriangle,2}()^2),
(5,Triangle, Lagrange{RefTriangle,3}()^2),
#(21,Tetrahedron, Lagrange{3,RefTetrahedron,1}()^3), #converges rather slowly in the gradient
#(7,Tetrahedron, Lagrange{3,RefTetrahedron,2}()^3),
(21,Quadrilateral, Lagrange{RefQuadrilateral,1}()^2),
(7,Quadrilateral, Lagrange{RefQuadrilateral,2}()^2),
#(21,Hexahedron, Lagrange{3,RefHexahedron,1}()^3), # slows down the pipeline quite a bit, so left out
#(7,Hexahedron, Lagrange{3,RefHexahedron,2}()^3)
]
@testset "vector($num_elements_per_dim, $geo, $ip)" begin
# Get solution
dim = Ferrite.getdim(ip)
@testset "$component" for component 1:dim
# Compute solution
dh, u = manufactured_linear_elastic_problem(geo, ip, size, component)
qr = QuadratureRule{Ferrite.getrefshape(ip)}(1)
ip_geo = Ferrite.default_interpolation(typeof(Ferrite.getcells(FerriteViz.get_grid(dh), 1)))
grid = generate_grid(geo, ntuple(x->num_elements_per_dim, dim));

# Check solution
cellvalues = CellValues(qr, ip, ip_geo);
for cell in CellIterator(dh)
reinit!(cellvalues, cell)
n_basefuncs = getnbasefunctions(cellvalues)
coords = getcoordinates(cell)
uₑ = u[celldofs(cell)]
for q_point in 1:getnquadpoints(cellvalues)
x = spatial_coordinate(cellvalues, q_point, coords)
for i in 1:n_basefuncs
uₐₙₐ = prod(cos, x*π/2)
uₐₚₚᵣₒₓ = function_value(cellvalues, q_point, uₑ)[component]
@test isapprox(uₐₙₐ, uₐₚₚᵣₒₓ; atol=1e-2)
end
end
end
dh = DofHandler(grid)
add!(dh, :u, ip^dim)
close!(dh);

# Compute gradient/flux field
(dh_grad, u_grad) = FerriteViz.interpolate_gradient_field(dh, u, :u)
f_ana(x) = Vec{dim}(i->(sum(x.^(i-1) /i)))
u = Vector{Float64}(undef, ndofs(dh))
Ferrite.apply_analytical!(u, dh, :u, f_ana)

# Check gradient of solution
# cellvalues_grad = CellValues(qr, Ferrite.getfieldinterpolation(dh_grad, :gradient), ip_geo);
# for cell in CellIterator(dh_grad)
# reinit!(cellvalues_grad, cell)
# n_basefuncs = getnbasefunctions(cellvalues_grad)
# coords = getcoordinates(cell)
# uₑ = u_grad[celldofs(cell)]
# for q_point in 1:getnquadpoints(cellvalues_grad)
# x = spatial_coordinate(cellvalues_grad, q_point, coords)
# for i ∈ 1:n_basefuncs
# uₐₚₚᵣₒₓ = function_value(cellvalues_grad, q_point, uₑ)
# for d ∈ 1:dim
# uₐₙₐ = π/2
# for j ∈ 1:(d-1)
# uₐₙₐ *= cos(x[j]*π/2)
# end
# uₐₙₐ *= -sin(x[d]*π/2)
# for j ∈ (d+1):dim
# uₐₙₐ *= cos(x[j]*π/2)
# end
# @test isapprox(uₐₙₐ, uₐₚₚᵣₒₓ[d]; atol=1e-1)
# end
# end
# end
# end
# Compute gradient/flux field
(dh_grad, u_grad) = FerriteViz.interpolate_gradient_field(dh, u, :u)

# Check gradient of solution
qr = QuadratureRule{Ferrite.getrefshape(ip)}(2)
ip_geo = Ferrite.default_interpolation(geo)
ip_grad = Ferrite.getfieldinterpolation(dh_grad, Ferrite.find_field(dh_grad, :gradient))
cellvalues_grad = Ferrite.PointValuesInternal(qr.points[1], ip_grad)
cellvalues_geo = Ferrite.PointValuesInternal(qr.points[1], ip_geo)
for cell in CellIterator(dh_grad)
coords = getcoordinates(cell)
uₑ = u_grad[celldofs(cell)]
for q_point in 1:getnquadpoints(cellvalues_grad)
x = function_value(cellvalues_geo, q_point, coords)
uₐₚₚᵣₒₓ = function_value(cellvalues_grad, q_point, uₑ)
uₐₙₐ = Tensors.gradient(f_ana, x)
@test isapprox(uₐₙₐ, uₐₚₚᵣₒₓ;atol=_test_tolerance(ip))
end
end
end
end
Expand Down

0 comments on commit 71b543b

Please sign in to comment.