Skip to content

Commit

Permalink
Merge pull request #758 from gridap/nedelex_on_simplex
Browse files Browse the repository at this point in the history
Nedelec elements on triangles and tetrahedra
  • Loading branch information
fverdugo authored Mar 9, 2022
2 parents 92b58a1 + 8a31b6c commit 2c6cd8b
Show file tree
Hide file tree
Showing 11 changed files with 425 additions and 4 deletions.
1 change: 1 addition & 0 deletions NEWS.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

### Added
- Implemented addition/subtraction between a `TensorValue` and a `SymTensorValue`. Since PR [#755](https://github.com/gridap/Gridap.jl/pull/755).
- Nédélec elements on simplices (lowest order interpolation for the moment). Since PR [#758](https://github.com/gridap/Gridap.jl/pull/758).

### Fixed
- Restrict to the active model the evaluation of a FE function at arbitrary points. Since PR [#752](https://github.com/gridap/Gridap.jl/pull/752).
Expand Down
51 changes: 51 additions & 0 deletions src/FESpaces/CurlConformingFESpaces.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@

function get_cell_dof_basis(
model::DiscreteModel,
cell_reffe::AbstractArray{<:GenericRefFE{Nedelec}},
::CurlConformity)

cell_dofs = lazy_map(get_dof_basis,cell_reffe)
cell_ownids = lazy_map(get_face_own_dofs,cell_reffe)
cell_map = get_cell_map(Triangulation(model))
cell_Jt = lazy_map(Broadcasting(∇),cell_map)
cell_x = lazy_map(get_nodes,cell_dofs)
cell_Jtx = lazy_map(evaluate,cell_Jt,cell_x)
k = TransformNedelecDofBasis()
lazy_map(k,cell_dofs,cell_Jtx,cell_ownids)
end

struct TransformNedelecDofBasis <: Map end

function return_cache(::TransformNedelecDofBasis,dofs,Jtx,ownids)
# Assumes the same element type in all the mesh
deepcopy(dofs)
end

function evaluate!(cache,::TransformNedelecDofBasis,dofs,Jtx,ownids)
basis = cache # Assumes the same element type in all the mesh
for face in 1:length(ownids)
face_dofs_ids = ownids[face]
face_point_ids = dofs.face_nodes[face]
face_moments_out = basis.face_moments[face]
face_moments_in = dofs.face_moments[face]
for p in 1:length(face_point_ids)
F = transpose(Jtx[p])
for i in 1:length(face_dofs_ids)
face_moments_out[p,i] = Fface_moments_in[p,i]
end
end
end
basis
end

function get_cell_shapefuns(
model::DiscreteModel,
cell_reffe::AbstractArray{<:GenericRefFE{Nedelec}},
::CurlConformity)

cell_reffe_shapefuns = lazy_map(get_shapefuns,cell_reffe)
cell_map = get_cell_map(Triangulation(model))
k = ReferenceFEs.CoVariantPiolaMap()
lazy_map(k,cell_reffe_shapefuns,cell_map)
end

2 changes: 2 additions & 0 deletions src/FESpaces/FESpaces.jl
Original file line number Diff line number Diff line change
Expand Up @@ -194,6 +194,8 @@ include("ConformingFESpaces.jl")

include("DivConformingFESpaces.jl")

include("CurlConformingFESpaces.jl")

include("FESpaceFactories.jl")

include("PhysicalFEs.jl")
Expand Down
134 changes: 134 additions & 0 deletions src/Polynomials/QGradMonomialBases.jl
Original file line number Diff line number Diff line change
Expand Up @@ -238,3 +238,137 @@ function _gradient_nd_qgrad!(
end

end


struct NedelecPrebasisOnSimplex{D} <: AbstractVector{Monomial}
order::Int
function NedelecPrebasisOnSimplex{D}(order::Integer) where D
new{D}(Int(order))
end
end

function Base.size(a::NedelecPrebasisOnSimplex{3})
@notimplementedif a.order != 0
(6,)
end

function Base.size(a::NedelecPrebasisOnSimplex{2})
@notimplementedif a.order != 0
(3,)
end

Base.getindex(a::NedelecPrebasisOnSimplex,i::Integer) = Monomial()
Base.IndexStyle(::Type{<:NedelecPrebasisOnSimplex}) = IndexLinear()

num_terms(a::NedelecPrebasisOnSimplex) = length(a)
get_order(f::NedelecPrebasisOnSimplex) = f.order

function return_cache(
f::NedelecPrebasisOnSimplex{D},x::AbstractVector{<:Point}) where D
@notimplementedif f.order != 0
np = length(x)
ndofs = num_terms(f)
V = eltype(x)
a = zeros(V,(np,ndofs))
CachedArray(a)
end

function evaluate!(
cache,f::NedelecPrebasisOnSimplex{3},x::AbstractVector{<:Point})
@notimplementedif f.order != 0
np = length(x)
ndofs = num_terms(f)
setsize!(cache,(np,ndofs))
a = cache.array
V = eltype(x)
T = eltype(V)
z = zero(T)
u = one(T)
for (ip,p) in enumerate(x)
a[ip,1] = VectorValue((u,z,z))
a[ip,2] = VectorValue((z,u,z))
a[ip,3] = VectorValue((z,z,u))
a[ip,4] = VectorValue((-p[2],p[1],z))
a[ip,5] = VectorValue((-p[3],z,p[1]))
a[ip,6] = VectorValue((z,-p[3],p[2]))
end
a
end

function evaluate!(
cache,f::NedelecPrebasisOnSimplex{2},x::AbstractVector{<:Point})
@notimplementedif f.order != 0
np = length(x)
ndofs = num_terms(f)
setsize!(cache,(np,ndofs))
a = cache.array
V = eltype(x)
T = eltype(V)
z = zero(T)
u = one(T)
for (ip,p) in enumerate(x)
a[ip,1] = VectorValue((u,z))
a[ip,2] = VectorValue((z,u))
a[ip,3] = VectorValue((-p[2],p[1]))
end
a
end

function return_cache(
g::FieldGradientArray{1,<:NedelecPrebasisOnSimplex{D}},
x::AbstractVector{<:Point}) where D
f = g.fa
@notimplementedif f.order != 0
np = length(x)
ndofs = num_terms(f)
xi = testitem(x)
V = eltype(x)
G = gradient_type(V,xi)
a = zeros(G,(np,ndofs))
CachedArray(a)
end

function evaluate!(
cache,
g::FieldGradientArray{1,<:NedelecPrebasisOnSimplex{3}},
x::AbstractVector{<:Point})
f = g.fa
@notimplementedif f.order != 0
np = length(x)
ndofs = num_terms(f)
setsize!(cache,(np,ndofs))
a = cache.array
fill!(a,zero(eltype(a)))
V = eltype(x)
T = eltype(V)
z = zero(T)
u = one(T)
for (ip,p) in enumerate(x)
a[ip,4] = TensorValue((z,-u,z, u,z,z, z,z,z))
a[ip,5] = TensorValue((z,z,-u, z,z,z, u,z,z))
a[ip,6] = TensorValue((z,z,z, z,z,-u, z,u,z))
end
a
end

function evaluate!(
cache,
g::FieldGradientArray{1,<:NedelecPrebasisOnSimplex{2}},
x::AbstractVector{<:Point})
f = g.fa
@notimplementedif f.order != 0
np = length(x)
ndofs = num_terms(f)
setsize!(cache,(np,ndofs))
a = cache.array
fill!(a,zero(eltype(a)))
V = eltype(x)
T = eltype(V)
z = zero(T)
u = one(T)
for (ip,p) in enumerate(x)
a[ip,3] = TensorValue((z,-u, u,z))
end
a
end

50 changes: 48 additions & 2 deletions src/ReferenceFEs/NedelecRefFEs.jl
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,13 @@ function NedelecRefFE(::Type{et},p::Polytope,order::Integer) where et
# @santiagobadia : Project, go to complex numbers
D = num_dims(p)

prebasis = QGradMonomialBasis{D}(et,order)
if is_n_cube(p)
prebasis = QGradMonomialBasis{D}(et,order)
elseif is_simplex(p)
prebasis = Polynomials.NedelecPrebasisOnSimplex{D}(order)
else
@unreachable "Only implemented for n-cubes and simplices"
end

nf_nodes, nf_moments = _Nedelec_nodes_and_moments(et,p,order)

Expand Down Expand Up @@ -70,7 +76,7 @@ end

function _Nedelec_nodes_and_moments(::Type{et}, p::Polytope, order::Integer) where et

@notimplementedif ! is_n_cube(p)
@notimplementedif !( is_n_cube(p) || (is_simplex(p) && order==0) )

D = num_dims(p)
ft = VectorValue{D,et}
Expand Down Expand Up @@ -222,3 +228,43 @@ function _broadcast_extend(::Type{T},Tm,b) where T
end
return c
end

struct CoVariantPiolaMap <: Map end

function evaluate!(
cache,
::Broadcasting{typeof(∇)},
a::Fields.BroadcastOpFieldArray{CoVariantPiolaMap})
v, Jt = a.args
# Assuming J comes from an affine map
∇v = Broadcasting(∇)(v)
k = CoVariantPiolaMap()
Broadcasting(Operation(k))(∇v,Jt)
end

function lazy_map(
::Broadcasting{typeof(gradient)},
a::LazyArray{<:Fill{Broadcasting{Operation{CoVariantPiolaMap}}}})
v, Jt = a.args
∇v = lazy_map(Broadcasting(∇),v)
k = CoVariantPiolaMap()
lazy_map(Broadcasting(Operation(k)),∇v,Jt)
end

function evaluate!(cache,::CoVariantPiolaMap,v::Number,Jt::Number)
vtranspose(inv(Jt)) # we multiply by the right side to compute the gradient correctly
end

function evaluate!(cache,k::CoVariantPiolaMap,v::AbstractVector{<:Field},phi::Field)
Jt = (phi)
Broadcasting(Operation(k))(v,Jt)
end

function lazy_map(
k::CoVariantPiolaMap,
cell_ref_shapefuns::AbstractArray{<:AbstractArray{<:Field}},
cell_map::AbstractArray{<:Field})

cell_Jt = lazy_map(∇,cell_map)
lazy_map(Broadcasting(Operation(k)),cell_ref_shapefuns,cell_Jt)
end
4 changes: 2 additions & 2 deletions src/ReferenceFEs/RaviartThomasRefFEs.jl
Original file line number Diff line number Diff line change
Expand Up @@ -310,13 +310,13 @@ end

function _moment_dof_basis_cache(vals::AbstractVector,ndofs)
T = eltype(vals)
r = zeros(eltype(T),ndofs)
r = fill(zero(eltype(T))*0.,ndofs)
end

function _moment_dof_basis_cache(vals::AbstractMatrix,ndofs)
_, npdofs = size(vals)
T = eltype(vals)
r = zeros(eltype(T),ndofs,npdofs)
r = fill(zero(eltype(T))*0.,ndofs,npdofs)
end

function evaluate!(cache,b::MomentBasedDofBasis,field)
Expand Down
51 changes: 51 additions & 0 deletions test/FESpacesTests/CurlConformingFESpacesTests.jl
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,64 @@ using Gridap.CellData
using Gridap.Fields
using Gridap.ReferenceFEs

domain =(0,1,0,1)
partition = (2,2)
model = CartesianDiscreteModel(domain,partition)
order = 1
u((x,y)) = 2*VectorValue(2,3)
reffe = ReferenceFE(nedelec,order)
V = TestFESpace(model,reffe,dirichlet_tags = "boundary")
test_single_field_fe_space(V)
U = TrialFESpace(V,u)
uh = interpolate(u,U)
e = u - uh
Ω = Triangulation(model)
= Measure(Ω,order)
el2 = sqrt(sum( ( ee )*dΩ ))
@test el2 < 1.0e-10
#using Gridap.Visualization
#writevtk(Ω,"nedel",nsubcells=10,cellfields=["err"=>e,"u"=>u,"uh"=>uh])

domain =(0,1,0,1)
partition = (3,3)
model = CartesianDiscreteModel(domain,partition) |> simplexify
order = 0
u((x,y)) = 2*VectorValue(-y,x)
reffe = ReferenceFE(nedelec,order)
V = TestFESpace(model,reffe,dirichlet_tags = "boundary")
test_single_field_fe_space(V)
U = TrialFESpace(V,u)
uh = interpolate(u,U)
e = u - uh
Ω = Triangulation(model)
= Measure(Ω,order)
el2 = sqrt(sum( ( ee )*dΩ ))
@test el2 < 1.0e-10

domain =(0,1,0,1,0,1)
partition = (3,3,3)
model = CartesianDiscreteModel(domain,partition) |> simplexify
order = 0
u((x,y,z)) = 2*VectorValue(-y,x,0.) - VectorValue(0.,-z,y)
reffe = ReferenceFE(nedelec,order)
V = TestFESpace(model,reffe,dirichlet_tags = "boundary")
test_single_field_fe_space(V)
U = TrialFESpace(V,u)
uh = interpolate(u,U)
e = u - uh
Ω = Triangulation(model)
= Measure(Ω,order)
el2 = sqrt(sum( ( ee )*dΩ ))
@test el2 < 1.0e-10

domain =(0,1,0,1,0,1)
partition = (3,3,3)
model = CartesianDiscreteModel(domain,partition)

order = 2

u(x) = VectorValue(x[1]*x[1],x[1]*x[1]*x[1],0.0)
#u(x) = VectorValue(2,3,5)
# u(x) = x

reffe = ReferenceFE(nedelec,order)
Expand Down
Loading

0 comments on commit 2c6cd8b

Please sign in to comment.