From 2e37ae76535375f157053bdeab1e9e34143b5b59 Mon Sep 17 00:00:00 2001 From: Eric Neiva Date: Tue, 5 Apr 2022 18:30:31 +0200 Subject: [PATCH 1/8] Added ModalC0 Bases --- Project.toml | 1 + src/Polynomials/AgFEMModalC0Bases.jl | 488 ++++++++++++++++++ src/Polynomials/Polynomials.jl | 5 + .../LinearCombinationDofVectors.jl | 46 ++ src/ReferenceFEs/ReferenceFEs.jl | 3 + .../AgFEMModalC0BasesTests.jl | 65 +++ test/PolynomialsTests/runtests.jl | 2 + test/ReferenceFEsTests/runtests.jl | 2 + 8 files changed, 612 insertions(+) create mode 100644 src/Polynomials/AgFEMModalC0Bases.jl create mode 100644 src/ReferenceFEs/LinearCombinationDofVectors.jl create mode 100644 test/PolynomialsTests/AgFEMModalC0BasesTests.jl diff --git a/Project.toml b/Project.toml index 01cef6535..a61c6f4a2 100644 --- a/Project.toml +++ b/Project.toml @@ -19,6 +19,7 @@ LineSearches = "d3d80556-e9d4-5f37-9878-2ab0fcc64255" LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e" NLsolve = "2774e3e8-f4cf-5e23-947b-6d7e65073b56" NearestNeighbors = "b8a86587-4115-5ab1-83bc-aa920d37bbce" +PolynomialBases = "c74db56a-226d-5e98-8bb0-a6049094aeea" QuadGK = "1fd47b50-473d-5c70-9696-f719f8f3bcdc" Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c" SparseArrays = "2f01184e-e22b-5df5-ae63-d93ebab69eaf" diff --git a/src/Polynomials/AgFEMModalC0Bases.jl b/src/Polynomials/AgFEMModalC0Bases.jl new file mode 100644 index 000000000..3b75c2c43 --- /dev/null +++ b/src/Polynomials/AgFEMModalC0Bases.jl @@ -0,0 +1,488 @@ +struct AgFEMModalC0 <: Field end + +struct AgFEMModalC0Basis{D,T,V} <: AbstractVector{AgFEMModalC0} + orders::NTuple{D,Int} + terms::Vector{CartesianIndex{D}} + a::Vector{Point{D,V}} + b::Vector{Point{D,V}} + function AgFEMModalC0Basis{D}( + ::Type{T}, + orders::NTuple{D,Int}, + terms::Vector{CartesianIndex{D}}, + a::Vector{Point{D,V}}, + b::Vector{Point{D,V}}) where {D,T,V} + new{D,T,V}(orders,terms,a,b) + end +end + +@inline Base.size(a::AgFEMModalC0Basis{D,T,V}) where {D,T,V} = (length(a.terms)*num_components(T),) +@inline Base.getindex(a::AgFEMModalC0Basis,i::Integer) = AgFEMModalC0() +@inline Base.IndexStyle(::AgFEMModalC0Basis) = IndexLinear() + +function AgFEMModalC0Basis{D}( + ::Type{T}, + orders::NTuple{D,Int}, + a::Vector{Point{D,V}}, + b::Vector{Point{D,V}}; + filter::Function=_q_filter, + sort!::Function=_sort_by_nfaces!) where {D,T,V} + + terms = _define_terms_mc0(filter, sort!, orders) + AgFEMModalC0Basis{D}(T,orders,terms,a,b) +end + +function AgFEMModalC0Basis{D}( + ::Type{T}, + orders::NTuple{D,Int}, + sa::Point{D,V}, + sb::Point{D,V}; + filter::Function=_q_filter, + sort!::Function=_sort_by_nfaces!) where {D,T,V} + + terms = _define_terms_mc0(filter, sort!, orders) + a = fill(sa,length(terms)) + b = fill(sb,length(terms)) + AgFEMModalC0Basis{D}(T,orders,terms,a,b) +end + +function AgFEMModalC0Basis{D}( + ::Type{T}, + orders::NTuple{D,Int}; + filter::Function=_q_filter, + sort!::Function=_sort_by_nfaces!) where {D,T} + + sa = Point{D,eltype(T)}(tfill(zero(eltype(T)),Val{D}())) + sb = Point{D,eltype(T)}(tfill(one(eltype(T)),Val{D}())) + AgFEMModalC0Basis{D}(T,orders,sa,sb,filter=filter,sort! = sort!) +end + +function AgFEMModalC0Basis{D}( + ::Type{T}, + order::Int, + a::Vector{Point{D,V}}, + b::Vector{Point{D,V}}; + filter::Function=_q_filter, + sort!::Function=_sort_by_nfaces!) where {D,T,V} + + orders = tfill(order,Val{D}()) + AgFEMModalC0Basis{D}(T,orders,a,b,filter=filter,sort! = sort!) +end + +function AgFEMModalC0Basis{D}( + ::Type{T}, + order::Int; + filter::Function=_q_filter, + sort!::Function=_sort_by_nfaces!) where {D,T} + + orders = tfill(order,Val{D}()) + AgFEMModalC0Basis{D}(T,orders,filter=filter,sort! = sort!) +end + +# API + +""" + get_order(b::AgFEMModalC0Basis) +""" +function get_order(b::AgFEMModalC0Basis) + maximum(b.orders) +end + +""" + get_orders(b::AgFEMModalC0Basis) +""" +function get_orders(b::AgFEMModalC0Basis) + b.orders +end + +return_type(::AgFEMModalC0Basis{D,T,V}) where {D,T,V} = T + +# Field implementation + +function return_cache(f::AgFEMModalC0Basis{D,T,V},x::AbstractVector{<:Point}) where {D,T,V} + @assert D == length(eltype(x)) "Incorrect number of point components" + np = length(x) + ndof = length(f.terms)*num_components(T) + n = 1 + _maximum(f.orders) + r = CachedArray(zeros(T,(np,ndof))) + v = CachedArray(zeros(T,(ndof,))) + c = CachedArray(zeros(eltype(T),(D,n))) + (r, v, c) +end + +function evaluate!(cache,f::AgFEMModalC0Basis{D,T,V},x::AbstractVector{<:Point}) where {D,T,V} + r, v, c = cache + np = length(x) + ndof = length(f.terms)*num_components(T) + n = 1 + _maximum(f.orders) + setsize!(r,(np,ndof)) + setsize!(v,(ndof,)) + setsize!(c,(D,n)) + for i in 1:np + @inbounds xi = x[i] + _evaluate_nd_amc0!(v,xi,f.a,f.b,f.orders,f.terms,c) + for j in 1:ndof + @inbounds r[i,j] = v[j] + end + end + r.array +end + +function return_cache( + fg::FieldGradientArray{1,AgFEMModalC0Basis{D,V,W}}, + x::AbstractVector{<:Point}) where {D,V,W} + + f = fg.fa + @assert D == length(eltype(x)) "Incorrect number of point components" + np = length(x) + ndof = length(f.terms)*num_components(V) + xi = testitem(x) + T = gradient_type(V,xi) + n = 1 + _maximum(f.orders) + r = CachedArray(zeros(T,(np,ndof))) + v = CachedArray(zeros(T,(ndof,))) + c = CachedArray(zeros(eltype(T),(D,n))) + g = CachedArray(zeros(eltype(T),(D,n))) + (r, v, c, g) +end + +function evaluate!( + cache, + fg::FieldGradientArray{1,AgFEMModalC0Basis{D,T,V}}, + x::AbstractVector{<:Point}) where {D,T,V} + + f = fg.fa + r, v, c, g = cache + np = length(x) + ndof = length(f.terms) * num_components(T) + n = 1 + _maximum(f.orders) + setsize!(r,(np,ndof)) + setsize!(v,(ndof,)) + setsize!(c,(D,n)) + setsize!(g,(D,n)) + for i in 1:np + @inbounds xi = x[i] + _gradient_nd_amc0!(v,xi,f.a,f.b,f.orders,f.terms,c,g,T) + for j in 1:ndof + @inbounds r[i,j] = v[j] + end + end + r.array +end + +function return_cache( + fg::FieldGradientArray{2,AgFEMModalC0Basis{D,V,W}}, + x::AbstractVector{<:Point}) where {D,V,W} + + f = fg.fa + @assert D == length(eltype(x)) "Incorrect number of point components" + np = length(x) + ndof = length(f.terms)*num_components(V) + xi = testitem(x) + T = gradient_type(gradient_type(V,xi),xi) + n = 1 + _maximum(f.orders) + r = CachedArray(zeros(T,(np,ndof))) + v = CachedArray(zeros(T,(ndof,))) + c = CachedArray(zeros(eltype(T),(D,n))) + g = CachedArray(zeros(eltype(T),(D,n))) + h = CachedArray(zeros(eltype(T),(D,n))) + (r, v, c, g, h) +end + +function evaluate!( + cache, + fg::FieldGradientArray{2,AgFEMModalC0Basis{D,T,V}}, + x::AbstractVector{<:Point}) where {D,T,V} + + f = fg.fa + r, v, c, g, h = cache + np = length(x) + ndof = length(f.terms) * num_components(T) + n = 1 + _maximum(f.orders) + setsize!(r,(np,ndof)) + setsize!(v,(ndof,)) + setsize!(c,(D,n)) + setsize!(g,(D,n)) + setsize!(h,(D,n)) + for i in 1:np + @inbounds xi = x[i] + _hessian_nd_amc0!(v,xi,f.a,f.b,f.orders,f.terms,c,g,h,T) + for j in 1:ndof + @inbounds r[i,j] = v[j] + end + end + r.array +end + +# Helpers + +_sort_by_tensor_prod!(terms,orders) = terms + +function _sort_by_nfaces!(terms::Vector{CartesianIndex{D}},orders) where D + + # Generate indices of n-faces and order s.t. + # (1) dimension-increasing (2) lexicographic + bin_rang_nfaces = tfill(0:1,Val{D}()) + bin_ids_nfaces = collect(Iterators.product(bin_rang_nfaces...)) + sum_bin_ids_nfaces = [sum(bin_ids_nfaces[i]) for i in eachindex(bin_ids_nfaces)] + bin_ids_nfaces = permute!(bin_ids_nfaces,sortperm(sum_bin_ids_nfaces)) + + # Generate LIs of basis funs s.t. order by n-faces + lids_b = LinearIndices(Tuple([orders[i]+1 for i=1:D])) + + eet = eltype(eltype(bin_ids_nfaces)) + f(x) = Tuple( x[i] == one(eet) ? (0:0) : (1:2) for i in 1:length(x) ) + g(x) = Tuple( x[i] == one(eet) ? (3:orders[i]+1) : (0:0) for i in 1:length(x) ) + rang_nfaces = map(f,bin_ids_nfaces) + rang_own_dofs = map(g,bin_ids_nfaces) + + P = Int64[] + for i = 1:length(bin_ids_nfaces) + cis_nfaces = CartesianIndices(rang_nfaces[i]) + cis_own_dofs = CartesianIndices(rang_own_dofs[i]) + for ci in cis_nfaces + ci = ci .+ cis_own_dofs + P = vcat(P,reshape(lids_b[ci],length(ci))) + end + end + + permute!(terms,P) +end + +function _compute_filter_mask(terms,filter,orders) + g = (0 .* orders) .+ 1 + to = CartesianIndex(g) + maxorder = _maximum(orders) + term_to_is_fterm = lazy_map(t->filter(Int[Tuple(t-to)...],maxorder),terms) + findall(term_to_is_fterm) +end + +function _define_terms_mc0(filter,sort!,orders) + terms = _define_terms(_q_filter,orders) + sort!(terms,orders) + mask = _compute_filter_mask(terms,filter,orders) + collect(lazy_map(Reindex(terms),mask)) +end + +function _evaluate_1d_amc0!(v::AbstractMatrix{T},x,a,b,order,d) where T + @assert order > 0 + n = order + 1 + z = one(T) + @inbounds v[d,1] = z - x[d] + @inbounds v[d,2] = x[d] + if n > 2 + ξ = ( 2*x[d] - ( a[d] + b[d] ) ) / ( b[d] - a[d] ) + for i in 3:n + @inbounds v[d,i] = -sqrt(2*i-3)*v[d,1]*v[d,2]*jacobi(ξ,i-3,1,1)/(i-2) + end + end +end + +function _gradient_1d_amc0!(v::AbstractMatrix{T},x,a,b,order,d) where T + @assert order > 0 + n = order + 1 + z = one(T) + @inbounds v[d,1] = -z + @inbounds v[d,2] = z + if n > 2 + ξ = ( 2*x[d] - ( a[d] + b[d] ) ) / ( b[d] - a[d] ) + v1 = z - x[d] + v2 = x[d] + for i in 3:n + j, dj = jacobi_and_derivative(ξ,i-3,1,1) + @inbounds v[d,i] = -sqrt(2*i-3)*(v[d,1]*v2*j+v1*v[d,2]*j+v1*v2*(2/(b[d]-a[d]))*dj)/(i-2) + end + end +end + +function _hessian_1d_amc0!(v::AbstractMatrix{T},x,a,b,order,d) where T + @assert order > 0 + n = order + 1 + y = zero(T) + z = one(T) + @inbounds v[d,1] = y + @inbounds v[d,2] = y + if n > 2 + ξ = ( 2*x[d] - ( a[d] + b[d] ) ) / ( b[d] - a[d] ) + v1 = z - x[d] + v2 = x[d] + dv1 = -z + dv2 = z + for i in 3:n + j, dj = jacobi_and_derivative(ξ,i-3,1,1) + _, d2j = jacobi_and_derivative(ξ,i-4,2,2) + @inbounds v[d,i] = -sqrt(2*i-3)*(2*dv1*dv2*j+2*(dv1*v2+v1*dv2)*(2/(b[d]-a[d]))*dj+v1*v2*d2j*2*i*((b[d]-a[d])^2))/(i-2) + end + end +end + +function _evaluate_nd_amc0!( + v::AbstractVector{V}, + x, + a::Vector{Point{D,T}}, + b::Vector{Point{D,T}}, + orders, + terms::AbstractVector{CartesianIndex{D}}, + c::AbstractMatrix{T}) where {V,T,D} + + dim = D + o = one(T) + k = 1 + l = length(terms) + + for (i,ci) in enumerate(terms) + + for d in 1:dim + _evaluate_1d_amc0!(c,x,a[i],b[i],orders[d],d) + end + + s = o + for d in 1:dim + @inbounds s *= c[d,ci[d]] + end + + k = _set_value_mc0!(v,s,k,l) + + end + +end + +@inline function _set_value_mc0!(v::AbstractVector{V},s::T,k,l) where {V,T} + m = zero(Mutable(V)) + z = zero(T) + js = eachindex(m) + for j in js + for i in js + @inbounds m[i] = z + end + @inbounds m[j] = s + i = k+l*(j-1) + @inbounds v[i] = m + end + k+1 +end + +@inline function _set_value_mc0!(v::AbstractVector{<:Real},s,k,l) + @inbounds v[k] = s + k+1 +end + +function _gradient_nd_amc0!( + v::AbstractVector{G}, + x, + a::Vector{Point{D,T}}, + b::Vector{Point{D,T}}, + orders, + terms::AbstractVector{CartesianIndex{D}}, + c::AbstractMatrix{T}, + g::AbstractMatrix{T}, + ::Type{V}) where {G,T,D,V} + + dim = D + z = zero(Mutable(VectorValue{D,T})) + o = one(T) + k = 1 + l = length(terms) + + for (i,ci) in enumerate(terms) + + for d in 1:dim + _evaluate_1d_amc0!(c,x,a[i],b[i],orders[d],d) + _gradient_1d_amc0!(g,x,a[i],b[i],orders[d],d) + end + + s = z + for i in eachindex(s) + @inbounds s[i] = o + end + for q in 1:dim + for d in 1:dim + if d != q + @inbounds s[q] *= c[d,ci[d]] + else + @inbounds s[q] *= g[d,ci[d]] + end + end + end + + k = _set_gradient_mc0!(v,s,k,l,V) + + end + +end + +@inline function _set_gradient_mc0!( + v::AbstractVector{G},s,k,l,::Type{<:Real}) where G + + @inbounds v[k] = s + k+1 +end + +@inline function _set_gradient_mc0!( + v::AbstractVector{G},s,k,l,::Type{V}) where {V,G} + + T = eltype(s) + m = zero(Mutable(G)) + w = zero(V) + z = zero(T) + for (ij,j) in enumerate(CartesianIndices(w)) + for i in CartesianIndices(m) + @inbounds m[i] = z + end + for i in CartesianIndices(s) + @inbounds m[i,j] = s[i] + end + i = k+l*(ij-1) + @inbounds v[i] = m + end + k+1 +end + +function _hessian_nd_amc0!( + v::AbstractVector{G}, + x, + a::Vector{Point{D,T}}, + b::Vector{Point{D,T}}, + orders, + terms::AbstractVector{CartesianIndex{D}}, + c::AbstractMatrix{T}, + g::AbstractMatrix{T}, + h::AbstractMatrix{T}, + ::Type{V}) where {G,T,D,V} + + dim = D + z = zero(Mutable(TensorValue{D,D,T})) + o = one(T) + k = 1 + l = length(terms) + + for (i,ci) in enumerate(terms) + + for d in 1:dim + _evaluate_1d_amc0!(c,x,a[i],b[i],orders[d],d) + _gradient_1d_amc0!(g,x,a[i],b[i],orders[d],d) + _hessian_1d_amc0!(h,x,a[i],b[i],orders[d],d) + end + + s = z + for i in eachindex(s) + @inbounds s[i] = o + end + for r in 1:dim + for q in 1:dim + for d in 1:dim + if d != q && d != r + @inbounds s[r,q] *= c[d,ci[d]] + elseif d == q && d ==r + @inbounds s[r,q] *= h[d,ci[d]] + else + @inbounds s[r,q] *= g[d,ci[d]] + end + end + end + end + + k = _set_gradient_mc0!(v,s,k,l,V) + + end + +end \ No newline at end of file diff --git a/src/Polynomials/Polynomials.jl b/src/Polynomials/Polynomials.jl index 20a51068d..4fa5c228d 100644 --- a/src/Polynomials/Polynomials.jl +++ b/src/Polynomials/Polynomials.jl @@ -14,6 +14,8 @@ using Gridap.Arrays using Gridap.TensorValues using Gridap.Fields +using PolynomialBases: jacobi, jacobi_and_derivative + import Gridap.Fields: evaluate! import Gridap.Fields: return_cache import Gridap.Arrays: return_type @@ -22,6 +24,7 @@ export MonomialBasis export QGradMonomialBasis export QCurlGradMonomialBasis export PCurlGradMonomialBasis +export AgFEMModalC0Basis export get_exponents export get_order @@ -36,4 +39,6 @@ include("QCurlGradMonomialBases.jl") include("PCurlGradMonomialBases.jl") +include("AgFEMModalC0Bases.jl") + end # module diff --git a/src/ReferenceFEs/LinearCombinationDofVectors.jl b/src/ReferenceFEs/LinearCombinationDofVectors.jl new file mode 100644 index 000000000..507a34724 --- /dev/null +++ b/src/ReferenceFEs/LinearCombinationDofVectors.jl @@ -0,0 +1,46 @@ +""" + struct LinearCombinationDofVector{T} <: AbstractVector{Dof} + change_of_basis::Matrix{T} + dof_basis::AbstractVector{<:Dof} + end + +Type that implements a dof basis (a) as the linear combination of a dof basis +(b). The dofs are first evaluated at dof basis (b) (field `dof_basis`) and the +dof values are next mapped to dof basis (a) applying a change of basis (field +`change_of_basis`). + +Fields: + +- `change_of_basis::Matrix{T}` the matrix of the change from dof basis (b) to (a) +- `dof_basis::AbstractVector{<:Dof}` A type representing dof basis (b) +""" +struct LinearCombinationDofVector{T} <: AbstractVector{Dof} + change_of_basis::Matrix{T} + dof_basis::AbstractVector{<:Dof} +end + +@inline Base.size(a::LinearCombinationDofVector) = size(a.dof_basis) +@inline Base.axes(a::LinearCombinationDofVector) = axes(a.dof_basis) +@inline Base.getindex(a::LinearCombinationDofVector,i::Integer) = getindex(a.dof_basis,i) +@inline Base.IndexStyle(::LinearCombinationDofVector) = IndexLinear() + +function linear_combination(a::AbstractMatrix{<:Number}, + b::AbstractVector{<:Dof}) + LinearCombinationDofVector(a,b) +end + +function linear_combination(a::LinearCombinationDofVector{T}, + b::AbstractVector{<:Dof}) where T + linear_combination(a.change_of_basis,b) +end + +function return_cache(b::LinearCombinationDofVector,field) + c, cf = return_cache(b.dof_basis,field) + c, cf, return_cache(*,b.change_of_basis,c) +end + +@inline function evaluate!(cache,b::LinearCombinationDofVector,field) + c, cf, cc = cache + vals = evaluate!(cache,b.dof_basis,field) + evaluate!(cc,*,b.change_of_basis,vals) +end \ No newline at end of file diff --git a/src/ReferenceFEs/ReferenceFEs.jl b/src/ReferenceFEs/ReferenceFEs.jl index 9eab471b5..26ddf4677 100644 --- a/src/ReferenceFEs/ReferenceFEs.jl +++ b/src/ReferenceFEs/ReferenceFEs.jl @@ -26,6 +26,7 @@ import Gridap.Arrays: evaluate! import Gridap.Arrays: return_type import Gridap.Fields: evaluate import Gridap.Fields: lazy_map +import Gridap.Fields: linear_combination import Gridap.Polynomials: MonomialBasis import Gridap.Polynomials: get_order @@ -228,4 +229,6 @@ include("MockDofs.jl") include("BezierRefFEs.jl") +include("LinearCombinationDofVectors.jl") + end # module diff --git a/test/PolynomialsTests/AgFEMModalC0BasesTests.jl b/test/PolynomialsTests/AgFEMModalC0BasesTests.jl new file mode 100644 index 000000000..6d7f92c8a --- /dev/null +++ b/test/PolynomialsTests/AgFEMModalC0BasesTests.jl @@ -0,0 +1,65 @@ +module AgFEMModalC0BasesTests + +using Test +using Gridap.TensorValues +using Gridap.Fields +using Gridap.Polynomials +# using BenchmarkTools + +import Gridap.Fields: Broadcasting + +# Real-valued Q space with isotropic order + +x1 = Point(0.0) +x2 = Point(0.5) +x3 = Point(1.0) + +V = Float64 +G = gradient_type(V,x1) +H = gradient_type(G,x1) +order = 12 + +order = 3 +a = fill(Point(-0.5),order+1) +b = fill(Point(2.5),order+1) +b1 = AgFEMModalC0Basis{1}(V,order,a,b) +∇b1 = Broadcasting(∇)(b1) +∇∇b1 = Broadcasting(∇)(∇b1) + +@test evaluate(b1,[x1,x2,x3,]) ≈ [1.0 0.0 -0.0 0.0; + 0.5 0.5 -0.4330127018922193 0.18633899812498247; + 0.0 1.0 -0.0 -0.0] +@test evaluate(∇b1,[x1,x2,x3,]) ≈ G[(-1.0,) (1.0,) (-1.7320508075688772,) (1.4907119849998598,); + (-1.0,) (1.0,) (-0.0,) (-0.37267799624996495,); + (-1.0,) (1.0,) (1.7320508075688772,) (-0.0,)] +@test evaluate(∇∇b1,[x1,x2,x3,]) ≈ H[(0.0,) (0.0,) (3.4641016151377544,) (-5.962847939999439,); + (0.0,) (0.0,) (3.4641016151377544,) (-1.4907119849998598,); + (0.0,) (0.0,) (3.4641016151377544,) (2.9814239699997196,)] + +x1 = Point(0.0,0.0) +x2 = Point(0.5,0.5) +x3 = Point(1.0,1.0) +a = [ Point(0.0,0.0),Point(0.0,0.0),Point(0.0,0.0),Point(0.0,0.0), + Point(-0.5,2.5),Point(-0.5,2.5),Point(0.0,1.5),Point(0.0,1.5), + Point(-1.0,-1.0),Point(-1.0,-1.0),Point(-1.0,-1.0),Point(-1.0,-1.0), + Point(0.0,0.0),Point(0.0,0.0),Point(0.0,0.0),Point(0.0,0.0) ] +b = [ Point(1.0,1.0),Point(1.0,1.0),Point(1.0,1.0),Point(1.0,1.0), + Point(2.5,2.5),Point(2.5,2.5),Point(1.5,1.5),Point(1.5,1.5), + Point(-1.0,1.0),Point(-1.0,1.0),Point(-1.0,1.25),Point(-1.0,1.25), + Point(1.0,1.0),Point(1.0,1.0),Point(1.0,1.0),Point(1.0,1.0) ] +b2 = AgFEMModalC0Basis{2}(V,order,a,b) +∇b2 = Broadcasting(∇)(b2) +∇∇b2 = Broadcasting(∇)(∇b2) + +G = gradient_type(V,x1) +H = gradient_type(G,x1) + +@test evaluate(b2,[x1,x2,x3,]) ≈ [ 1.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0; + 0.25 0.25 0.25 0.25 -0.21650635094610965 0.09316949906249124 -0.21650635094610965 0.09316949906249124 -0.21650635094610965 -0.13975424859373686 -0.21650635094610965 -0.09316949906249122 0.18749999999999997 0.0 0.0 0.0; + 0.0 0.0 0.0 1.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 ] +@test evaluate(∇b2,[x1,x2,x3,])[:,10] ≈ G[ (0.0, 0.0,); (0.2795084971874737, -0.2795084971874737,); (0.0, 0.0,) ] +@test evaluate(∇∇b2,[x1,x2,x3,])[:,10] ≈ H[ (0.0, 0.0, 0.0, -4.47213595499958); + (0.0, 0.5590169943749475, 0.5590169943749475, 1.118033988749895); + (0.0, -2.23606797749979, -2.23606797749979, 0.0) ] + +end # module \ No newline at end of file diff --git a/test/PolynomialsTests/runtests.jl b/test/PolynomialsTests/runtests.jl index 0f9feb15d..90b0eef01 100644 --- a/test/PolynomialsTests/runtests.jl +++ b/test/PolynomialsTests/runtests.jl @@ -10,6 +10,8 @@ using Test @testset "PCurlGradMonomialBases" begin include("PCurlGradMonomialBasesTests.jl") end +@testset "AgFEMModalC0Bases" begin include("AgFEMModalC0BasesTests.jl") end + #@testset "ChangeBasis" begin include("ChangeBasisTests.jl") end end # module diff --git a/test/ReferenceFEsTests/runtests.jl b/test/ReferenceFEsTests/runtests.jl index 5f71aff71..066e51d21 100644 --- a/test/ReferenceFEsTests/runtests.jl +++ b/test/ReferenceFEsTests/runtests.jl @@ -38,4 +38,6 @@ using Test @testset "BezierRefFEs" begin include("BezierRefFEsTests.jl") end +@testset "LinearCombinationDofVectors" begin include("LinearCombinationDofVectorsTests.jl") end + end # module From 2febe3f94fb994a8fe0d6f5a9833ed1c1375a36b Mon Sep 17 00:00:00 2001 From: Eric Neiva Date: Tue, 5 Apr 2022 19:14:16 +0200 Subject: [PATCH 2/8] Added ModalC0 RefFEs --- src/Exports.jl | 2 + src/Geometry/DiscreteModels.jl | 6 + .../{AgFEMModalC0Bases.jl => ModalC0Bases.jl} | 86 ++++---- src/Polynomials/Polynomials.jl | 4 +- src/ReferenceFEs/ModalC0RefFEs.jl | 207 ++++++++++++++++++ src/ReferenceFEs/ReferenceFEInterfaces.jl | 50 +++++ src/ReferenceFEs/ReferenceFEs.jl | 12 + ...alC0BasesTests.jl => ModalC0BasesTests.jl} | 6 +- test/PolynomialsTests/runtests.jl | 2 +- test/ReferenceFEsTests/ModalC0RefFEsTests.jl | 151 +++++++++++++ test/ReferenceFEsTests/runtests.jl | 2 +- 11 files changed, 479 insertions(+), 49 deletions(-) rename src/Polynomials/{AgFEMModalC0Bases.jl => ModalC0Bases.jl} (81%) create mode 100644 src/ReferenceFEs/ModalC0RefFEs.jl rename test/PolynomialsTests/{AgFEMModalC0BasesTests.jl => ModalC0BasesTests.jl} (96%) create mode 100644 test/ReferenceFEsTests/ModalC0RefFEsTests.jl diff --git a/src/Exports.jl b/src/Exports.jl index 9226e683c..292ce2fdc 100644 --- a/src/Exports.jl +++ b/src/Exports.jl @@ -88,9 +88,11 @@ using Gridap.TensorValues: ⊗; export ⊗ @publish ReferenceFEs Lagrangian @publish ReferenceFEs RaviartThomas @publish ReferenceFEs Nedelec +@publish ReferenceFEs ModalC0 @publish ReferenceFEs lagrangian @publish ReferenceFEs raviart_thomas @publish ReferenceFEs nedelec +@publish ReferenceFEs modalC0 @publish Geometry get_triangulation @publish Geometry num_cells diff --git a/src/Geometry/DiscreteModels.jl b/src/Geometry/DiscreteModels.jl index 9c2854e5c..ad8752d31 100644 --- a/src/Geometry/DiscreteModels.jl +++ b/src/Geometry/DiscreteModels.jl @@ -388,6 +388,12 @@ function ReferenceFE(model::DiscreteModel,args...;kwargs...) cell_to_reffe end +function ReferenceFE(model::DiscreteModel,basis::ModalC0,args...;kwargs...) + ctype_to_polytope = get_polytopes(model) + @assert length(ctype_to_polytope) == 1 "Only one polytope expected" + compute_cell_to_modalC0_reffe(ctype_to_polytope[1],num_cells(model),args...;kwargs...) +end + # IO function to_dict(model::DiscreteModel) diff --git a/src/Polynomials/AgFEMModalC0Bases.jl b/src/Polynomials/ModalC0Bases.jl similarity index 81% rename from src/Polynomials/AgFEMModalC0Bases.jl rename to src/Polynomials/ModalC0Bases.jl index 3b75c2c43..78b6e1fa0 100644 --- a/src/Polynomials/AgFEMModalC0Bases.jl +++ b/src/Polynomials/ModalC0Bases.jl @@ -1,11 +1,11 @@ -struct AgFEMModalC0 <: Field end +struct ModalC0BasisFunction <: Field end -struct AgFEMModalC0Basis{D,T,V} <: AbstractVector{AgFEMModalC0} +struct ModalC0Basis{D,T,V} <: AbstractVector{ModalC0BasisFunction} orders::NTuple{D,Int} terms::Vector{CartesianIndex{D}} a::Vector{Point{D,V}} b::Vector{Point{D,V}} - function AgFEMModalC0Basis{D}( + function ModalC0Basis{D}( ::Type{T}, orders::NTuple{D,Int}, terms::Vector{CartesianIndex{D}}, @@ -15,11 +15,11 @@ struct AgFEMModalC0Basis{D,T,V} <: AbstractVector{AgFEMModalC0} end end -@inline Base.size(a::AgFEMModalC0Basis{D,T,V}) where {D,T,V} = (length(a.terms)*num_components(T),) -@inline Base.getindex(a::AgFEMModalC0Basis,i::Integer) = AgFEMModalC0() -@inline Base.IndexStyle(::AgFEMModalC0Basis) = IndexLinear() +@inline Base.size(a::ModalC0Basis{D,T,V}) where {D,T,V} = (length(a.terms)*num_components(T),) +@inline Base.getindex(a::ModalC0Basis,i::Integer) = ModalC0BasisFunction() +@inline Base.IndexStyle(::ModalC0Basis) = IndexLinear() -function AgFEMModalC0Basis{D}( +function ModalC0Basis{D}( ::Type{T}, orders::NTuple{D,Int}, a::Vector{Point{D,V}}, @@ -28,10 +28,10 @@ function AgFEMModalC0Basis{D}( sort!::Function=_sort_by_nfaces!) where {D,T,V} terms = _define_terms_mc0(filter, sort!, orders) - AgFEMModalC0Basis{D}(T,orders,terms,a,b) + ModalC0Basis{D}(T,orders,terms,a,b) end -function AgFEMModalC0Basis{D}( +function ModalC0Basis{D}( ::Type{T}, orders::NTuple{D,Int}, sa::Point{D,V}, @@ -42,10 +42,10 @@ function AgFEMModalC0Basis{D}( terms = _define_terms_mc0(filter, sort!, orders) a = fill(sa,length(terms)) b = fill(sb,length(terms)) - AgFEMModalC0Basis{D}(T,orders,terms,a,b) + ModalC0Basis{D}(T,orders,terms,a,b) end -function AgFEMModalC0Basis{D}( +function ModalC0Basis{D}( ::Type{T}, orders::NTuple{D,Int}; filter::Function=_q_filter, @@ -53,10 +53,10 @@ function AgFEMModalC0Basis{D}( sa = Point{D,eltype(T)}(tfill(zero(eltype(T)),Val{D}())) sb = Point{D,eltype(T)}(tfill(one(eltype(T)),Val{D}())) - AgFEMModalC0Basis{D}(T,orders,sa,sb,filter=filter,sort! = sort!) + ModalC0Basis{D}(T,orders,sa,sb,filter=filter,sort! = sort!) end -function AgFEMModalC0Basis{D}( +function ModalC0Basis{D}( ::Type{T}, order::Int, a::Vector{Point{D,V}}, @@ -65,40 +65,40 @@ function AgFEMModalC0Basis{D}( sort!::Function=_sort_by_nfaces!) where {D,T,V} orders = tfill(order,Val{D}()) - AgFEMModalC0Basis{D}(T,orders,a,b,filter=filter,sort! = sort!) + ModalC0Basis{D}(T,orders,a,b,filter=filter,sort! = sort!) end -function AgFEMModalC0Basis{D}( +function ModalC0Basis{D}( ::Type{T}, order::Int; filter::Function=_q_filter, sort!::Function=_sort_by_nfaces!) where {D,T} orders = tfill(order,Val{D}()) - AgFEMModalC0Basis{D}(T,orders,filter=filter,sort! = sort!) + ModalC0Basis{D}(T,orders,filter=filter,sort! = sort!) end # API """ - get_order(b::AgFEMModalC0Basis) + get_order(b::ModalC0Basis) """ -function get_order(b::AgFEMModalC0Basis) +function get_order(b::ModalC0Basis) maximum(b.orders) end """ - get_orders(b::AgFEMModalC0Basis) + get_orders(b::ModalC0Basis) """ -function get_orders(b::AgFEMModalC0Basis) +function get_orders(b::ModalC0Basis) b.orders end -return_type(::AgFEMModalC0Basis{D,T,V}) where {D,T,V} = T +return_type(::ModalC0Basis{D,T,V}) where {D,T,V} = T # Field implementation -function return_cache(f::AgFEMModalC0Basis{D,T,V},x::AbstractVector{<:Point}) where {D,T,V} +function return_cache(f::ModalC0Basis{D,T,V},x::AbstractVector{<:Point}) where {D,T,V} @assert D == length(eltype(x)) "Incorrect number of point components" np = length(x) ndof = length(f.terms)*num_components(T) @@ -109,7 +109,7 @@ function return_cache(f::AgFEMModalC0Basis{D,T,V},x::AbstractVector{<:Point}) wh (r, v, c) end -function evaluate!(cache,f::AgFEMModalC0Basis{D,T,V},x::AbstractVector{<:Point}) where {D,T,V} +function evaluate!(cache,f::ModalC0Basis{D,T,V},x::AbstractVector{<:Point}) where {D,T,V} r, v, c = cache np = length(x) ndof = length(f.terms)*num_components(T) @@ -119,7 +119,7 @@ function evaluate!(cache,f::AgFEMModalC0Basis{D,T,V},x::AbstractVector{<:Point}) setsize!(c,(D,n)) for i in 1:np @inbounds xi = x[i] - _evaluate_nd_amc0!(v,xi,f.a,f.b,f.orders,f.terms,c) + _evaluate_nd_mc0!(v,xi,f.a,f.b,f.orders,f.terms,c) for j in 1:ndof @inbounds r[i,j] = v[j] end @@ -128,7 +128,7 @@ function evaluate!(cache,f::AgFEMModalC0Basis{D,T,V},x::AbstractVector{<:Point}) end function return_cache( - fg::FieldGradientArray{1,AgFEMModalC0Basis{D,V,W}}, + fg::FieldGradientArray{1,ModalC0Basis{D,V,W}}, x::AbstractVector{<:Point}) where {D,V,W} f = fg.fa @@ -147,7 +147,7 @@ end function evaluate!( cache, - fg::FieldGradientArray{1,AgFEMModalC0Basis{D,T,V}}, + fg::FieldGradientArray{1,ModalC0Basis{D,T,V}}, x::AbstractVector{<:Point}) where {D,T,V} f = fg.fa @@ -161,7 +161,7 @@ function evaluate!( setsize!(g,(D,n)) for i in 1:np @inbounds xi = x[i] - _gradient_nd_amc0!(v,xi,f.a,f.b,f.orders,f.terms,c,g,T) + _gradient_nd_mc0!(v,xi,f.a,f.b,f.orders,f.terms,c,g,T) for j in 1:ndof @inbounds r[i,j] = v[j] end @@ -170,7 +170,7 @@ function evaluate!( end function return_cache( - fg::FieldGradientArray{2,AgFEMModalC0Basis{D,V,W}}, + fg::FieldGradientArray{2,ModalC0Basis{D,V,W}}, x::AbstractVector{<:Point}) where {D,V,W} f = fg.fa @@ -190,7 +190,7 @@ end function evaluate!( cache, - fg::FieldGradientArray{2,AgFEMModalC0Basis{D,T,V}}, + fg::FieldGradientArray{2,ModalC0Basis{D,T,V}}, x::AbstractVector{<:Point}) where {D,T,V} f = fg.fa @@ -205,7 +205,7 @@ function evaluate!( setsize!(h,(D,n)) for i in 1:np @inbounds xi = x[i] - _hessian_nd_amc0!(v,xi,f.a,f.b,f.orders,f.terms,c,g,h,T) + _hessian_nd_mc0!(v,xi,f.a,f.b,f.orders,f.terms,c,g,h,T) for j in 1:ndof @inbounds r[i,j] = v[j] end @@ -215,6 +215,8 @@ end # Helpers +_s_filter_mc0(e,o) = ( sum( [ i for i in e if i>1 ] ) <= o ) + _sort_by_tensor_prod!(terms,orders) = terms function _sort_by_nfaces!(terms::Vector{CartesianIndex{D}},orders) where D @@ -263,7 +265,7 @@ function _define_terms_mc0(filter,sort!,orders) collect(lazy_map(Reindex(terms),mask)) end -function _evaluate_1d_amc0!(v::AbstractMatrix{T},x,a,b,order,d) where T +function _evaluate_1d_mc0!(v::AbstractMatrix{T},x,a,b,order,d) where T @assert order > 0 n = order + 1 z = one(T) @@ -277,7 +279,7 @@ function _evaluate_1d_amc0!(v::AbstractMatrix{T},x,a,b,order,d) where T end end -function _gradient_1d_amc0!(v::AbstractMatrix{T},x,a,b,order,d) where T +function _gradient_1d_mc0!(v::AbstractMatrix{T},x,a,b,order,d) where T @assert order > 0 n = order + 1 z = one(T) @@ -294,7 +296,7 @@ function _gradient_1d_amc0!(v::AbstractMatrix{T},x,a,b,order,d) where T end end -function _hessian_1d_amc0!(v::AbstractMatrix{T},x,a,b,order,d) where T +function _hessian_1d_mc0!(v::AbstractMatrix{T},x,a,b,order,d) where T @assert order > 0 n = order + 1 y = zero(T) @@ -315,7 +317,7 @@ function _hessian_1d_amc0!(v::AbstractMatrix{T},x,a,b,order,d) where T end end -function _evaluate_nd_amc0!( +function _evaluate_nd_mc0!( v::AbstractVector{V}, x, a::Vector{Point{D,T}}, @@ -332,7 +334,7 @@ function _evaluate_nd_amc0!( for (i,ci) in enumerate(terms) for d in 1:dim - _evaluate_1d_amc0!(c,x,a[i],b[i],orders[d],d) + _evaluate_1d_mc0!(c,x,a[i],b[i],orders[d],d) end s = o @@ -366,7 +368,7 @@ end k+1 end -function _gradient_nd_amc0!( +function _gradient_nd_mc0!( v::AbstractVector{G}, x, a::Vector{Point{D,T}}, @@ -386,8 +388,8 @@ function _gradient_nd_amc0!( for (i,ci) in enumerate(terms) for d in 1:dim - _evaluate_1d_amc0!(c,x,a[i],b[i],orders[d],d) - _gradient_1d_amc0!(g,x,a[i],b[i],orders[d],d) + _evaluate_1d_mc0!(c,x,a[i],b[i],orders[d],d) + _gradient_1d_mc0!(g,x,a[i],b[i],orders[d],d) end s = z @@ -437,7 +439,7 @@ end k+1 end -function _hessian_nd_amc0!( +function _hessian_nd_mc0!( v::AbstractVector{G}, x, a::Vector{Point{D,T}}, @@ -458,9 +460,9 @@ function _hessian_nd_amc0!( for (i,ci) in enumerate(terms) for d in 1:dim - _evaluate_1d_amc0!(c,x,a[i],b[i],orders[d],d) - _gradient_1d_amc0!(g,x,a[i],b[i],orders[d],d) - _hessian_1d_amc0!(h,x,a[i],b[i],orders[d],d) + _evaluate_1d_mc0!(c,x,a[i],b[i],orders[d],d) + _gradient_1d_mc0!(g,x,a[i],b[i],orders[d],d) + _hessian_1d_mc0!(h,x,a[i],b[i],orders[d],d) end s = z diff --git a/src/Polynomials/Polynomials.jl b/src/Polynomials/Polynomials.jl index 4fa5c228d..9b955a1eb 100644 --- a/src/Polynomials/Polynomials.jl +++ b/src/Polynomials/Polynomials.jl @@ -24,7 +24,7 @@ export MonomialBasis export QGradMonomialBasis export QCurlGradMonomialBasis export PCurlGradMonomialBasis -export AgFEMModalC0Basis +export ModalC0Basis export get_exponents export get_order @@ -39,6 +39,6 @@ include("QCurlGradMonomialBases.jl") include("PCurlGradMonomialBases.jl") -include("AgFEMModalC0Bases.jl") +include("ModalC0Bases.jl") end # module diff --git a/src/ReferenceFEs/ModalC0RefFEs.jl b/src/ReferenceFEs/ModalC0RefFEs.jl new file mode 100644 index 000000000..0e6e2e01e --- /dev/null +++ b/src/ReferenceFEs/ModalC0RefFEs.jl @@ -0,0 +1,207 @@ +struct ModalC0 <: ReferenceFEName end + +const modalC0 = ModalC0() + +struct SerendipityLagrangian <: ReferenceFEName end + +const serendipitylagrangian = SerendipityLagrangian() + +""" + ModalC0RefFE(::Type{T},p::Polytope{D},orders) where {T,D} + +Returns an instance of `GenericRefFE{ModalC0}` representing a ReferenceFE with +Modal C0-continuous shape functions (multivariate scalar-valued, vector-valued, +or tensor-valued, iso- or aniso-tropic). + +For more details about the shape functions, see Section 1.1.5 in + +Ern, A., & Guermond, J. L. (2013). Theory and practice of finite elements +(Vol. 159). Springer Science & Business Media. + +and references therein. + +The constructor is only implemented for for n-cubes and the minimum order in +any dimension must be greater than one. The DoFs are numbered by n-faces in the +same way as with CLagrangianRefFEs. +""" +function ModalC0RefFE( + ::Type{T}, + p::Polytope{D}, + orders, + a::Vector{Point{D,V}}, + b::Vector{Point{D,V}} ) where {T,D,V} + + @notimplementedif ! is_n_cube(p) + @notimplementedif minimum(orders) < one(eltype(orders)) + + shapefuns = ModalC0Basis{D}(T,orders,a,b) + + ndofs, predofs, lag_reffe, face_dofs = compute_reffe_data(T,p,orders,lagrangian) + + GenericRefFE{ModalC0}( + ndofs, + p, + predofs, + GradConformity(), + lag_reffe, + face_dofs, + shapefuns) +end + +function ModalC0RefFE( + ::Type{T}, + p::Polytope{D}, + orders ) where {T,D,V} + + @notimplementedif ! is_n_cube(p) + @notimplementedif minimum(orders) < one(eltype(orders)) + + shapefuns = ModalC0Basis{D}(T,orders) + + ndofs, predofs, lag_reffe, face_dofs = compute_reffe_data(T,p,orders,lagrangian) + + GenericRefFE{ModalC0}( + ndofs, + p, + predofs, + GradConformity(), + lag_reffe, + face_dofs, + shapefuns) +end + +function get_orders(reffe::GenericRefFE{ModalC0,D}) where{D} + get_orders(get_shapefuns(reffe)) +end + +function compute_reffe_data(::Type{T}, + p::Polytope{D}, + order::Int, + name::ReferenceFEName) where {T,D} + orders = tfill(order,Val{D}()) + compute_reffe_data(T,p,orders,name) +end + +function compute_reffe_data(::Type{T}, + p::Polytope{D}, + orders::NTuple{D,Int}, + ::Lagrangian) where {T,D} + lag_reffe = LagrangianRefFE(T,p,orders) + reffe = lag_reffe.reffe + reffe.ndofs, reffe.dofs, lag_reffe, reffe.face_dofs +end + +function compute_reffe_data(::Type{T}, + p::Polytope{D}, + orders::NTuple{D,Int}, + ::SerendipityLagrangian) where {T,D} + lag_reffe = SerendipityRefFE(T,p,orders) + reffe = lag_reffe.reffe + reffe.ndofs, reffe.dofs, lag_reffe, reffe.face_dofs +end + +function ReferenceFE( + polytope::Polytope{D}, + ::ModalC0, + ::Type{T}, + orders::Union{Integer,NTuple{D,Int}}; + kwargs...) where {T,D} + ModalC0RefFE(T,polytope,orders;kwargs...) +end + +function Conformity(::GenericRefFE{ModalC0},sym::Symbol) + h1 = (:H1,:C0,:Hgrad) + if sym == :L2 + L2Conformity() + elseif sym in h1 + H1Conformity() + else + @unreachable """\n + It is not possible to use conformity = $sym on a ModalC0RefFE with H1 conformity. + Possible values of conformity for this reference fe are $((:L2, h1...)). + """ + end +end + +function get_face_own_dofs( + reffe::GenericRefFE{ModalC0},conf::GradConformity) + lagrangian_reffe = reffe.metadata + get_face_own_dofs(lagrangian_reffe,conf) +end + +function get_face_own_dofs_permutations( + reffe::GenericRefFE{ModalC0},conf::GradConformity) + lagrangian_reffe = reffe.metadata + get_face_own_dofs_permutations(lagrangian_reffe,conf) +end + +function compute_shapefun_bboxes!( + a::Vector{Point{D,V}}, + b::Vector{Point{D,V}}, + bboxes::Vector{Point{D,V}}, + face_own_dofs) where {D,V} + for i in 1:length(face_own_dofs) + a[face_own_dofs[i]] .= bboxes[2*i-1] + b[face_own_dofs[i]] .= bboxes[2*i] + end +end + +function compute_cell_to_modalC0_reffe( + p::Polytope{D}, + ncells::Int, + ::Type{T}, + orders::Union{Integer,NTuple{D,Int}}, + bboxes; + reffe_type=lagrangian) where {T,D} # type-stability? + + @notimplementedif ! is_n_cube(p) + @notimplementedif minimum(orders) < one(eltype(orders)) + @assert ncells == length(bboxes) + + ndofs, predofs, lag_reffe, face_dofs = compute_reffe_data(T,p,orders,reffe_type) + face_own_dofs = get_face_own_dofs(lag_reffe,GradConformity()) + + filter = reffe_type == lagrangian ? _q_filter : _s_filter_mc0 + + sh(bbs) = begin + a = fill(Point{D,eltype(T)}(tfill(zero(eltype(T)),Val{D}())),ndofs) + b = fill(Point{D,eltype(T)}(tfill(zero(eltype(T)),Val{D}())),ndofs) + compute_shapefun_bboxes!(a,b,bbs,face_own_dofs) + ModalC0Basis{D}(T,orders,a,b,filter=filter) + end + + reffe(sh) = GenericRefFE{ModalC0}(ndofs, + p, + predofs, + GradConformity(), + lag_reffe, + face_dofs, + sh) + + reffes = [ reffe(sh(bbs)) for bbs in bboxes ] + CompressedArray(reffes,1:ncells) +end + +function compute_cell_to_modalC0_reffe( + p::Polytope{D}, + ncells::Int, + ::Type{T}, + orders::Union{Integer,NTuple{D,Int}}; + reffe_type=lagrangian) where {T,D} # type-stability? + + @notimplementedif ! is_n_cube(p) + @notimplementedif minimum(orders) < one(eltype(orders)) + + filter = reffe_type == lagrangian ? _q_filter : _s_filter_mc0 + + ndofs, predofs, lag_reffe, face_dofs = compute_reffe_data(T,p,orders,reffe_type) + reffe = GenericRefFE{ModalC0}(ndofs, + p, + predofs, + GradConformity(), + lag_reffe, + face_dofs, + ModalC0Basis{D}(T,orders,filter=filter)) + + Fill(reffe,ncells) +end \ No newline at end of file diff --git a/src/ReferenceFEs/ReferenceFEInterfaces.jl b/src/ReferenceFEs/ReferenceFEInterfaces.jl index db48d8744..e4fe4f5cd 100644 --- a/src/ReferenceFEs/ReferenceFEInterfaces.jl +++ b/src/ReferenceFEs/ReferenceFEInterfaces.jl @@ -382,6 +382,22 @@ function compute_shapefuns(dofs,prebasis) linear_combination(change,prebasis) end +""" + compute_dofs(predofs,shapefuns) + +Helper function used to compute the dof basis +associated with the dof basis `predofs` and the basis `shapefuns`. + +It is equivalent to + + change = inv(evaluate(predofs,shapefuns)) + linear_combination(change,predofs) # i.e. transpose(change)*predofs +""" +function compute_dofs(predofs,shapefuns) + change = inv(evaluate(predofs,shapefuns)) + linear_combination(change,predofs) +end + # Concrete implementation """ @@ -440,6 +456,40 @@ struct GenericRefFE{T,D} <: ReferenceFE{D} face_dofs, shapefuns) end + @doc """ + GenericRefFE{T}( + ndofs::Int, + polytope::Polytope{D}, + prebasis::AbstractVector{<:Field}, + predofs::AbstractVector{<:Dof}, + conformity::Conformity, + metadata, + face_dofs::Vector{Vector{Int}}, + shapefuns::AbstractVector{<:Field}, + dofs::AbstractVector{<:Dof}=compute_dofs(predofs,shapefuns)) where {T,D} + + Constructs a `GenericRefFE` object with the provided data. + """ + function GenericRefFE{T}( + ndofs::Int, + polytope::Polytope{D}, + predofs::AbstractVector{<:Dof}, + conformity::Conformity, + metadata, + face_dofs::Vector{Vector{Int}}, + shapefuns::AbstractVector{<:Field}, + dofs::AbstractVector{<:Dof}=compute_dofs(predofs,shapefuns)) where {T,D} + + new{T,D}( + ndofs, + polytope, + shapefuns, + dofs, + conformity, + metadata, + face_dofs, + shapefuns) + end end num_dofs(reffe::GenericRefFE) = reffe.ndofs diff --git a/src/ReferenceFEs/ReferenceFEs.jl b/src/ReferenceFEs/ReferenceFEs.jl index 26ddf4677..c9356e2b9 100644 --- a/src/ReferenceFEs/ReferenceFEs.jl +++ b/src/ReferenceFEs/ReferenceFEs.jl @@ -17,6 +17,10 @@ using Gridap.TensorValues using Gridap.Fields using Gridap.Polynomials +using Gridap.Polynomials: _q_filter, _s_filter_mc0 +using Gridap.Polynomials: _compute_filter_mask +using Gridap.Polynomials: _define_terms, _sort_by_nfaces! + using QuadGK: gauss using FastGaussQuadrature: gaussjacobi using FastGaussQuadrature: gausslegendre @@ -146,6 +150,9 @@ export MomentBasedDofBasis export get_face_own_nodes export get_face_nodes +export linear_combination +export compute_cell_to_modalC0_reffe + export VERTEX1 export SEG2 export TRI3 @@ -168,16 +175,19 @@ export SerendipityRefFE export RaviartThomasRefFE export NedelecRefFE export BezierRefFE +export ModalC0RefFE export Lagrangian export RaviartThomas export Nedelec export Bezier +export ModalC0 export lagrangian export raviart_thomas export nedelec export bezier +export modalC0 export Quadrature export QuadratureName @@ -229,6 +239,8 @@ include("MockDofs.jl") include("BezierRefFEs.jl") +include("ModalC0RefFEs.jl") + include("LinearCombinationDofVectors.jl") end # module diff --git a/test/PolynomialsTests/AgFEMModalC0BasesTests.jl b/test/PolynomialsTests/ModalC0BasesTests.jl similarity index 96% rename from test/PolynomialsTests/AgFEMModalC0BasesTests.jl rename to test/PolynomialsTests/ModalC0BasesTests.jl index 6d7f92c8a..577ba9737 100644 --- a/test/PolynomialsTests/AgFEMModalC0BasesTests.jl +++ b/test/PolynomialsTests/ModalC0BasesTests.jl @@ -1,4 +1,4 @@ -module AgFEMModalC0BasesTests +module ModalC0BasesTests using Test using Gridap.TensorValues @@ -22,7 +22,7 @@ order = 12 order = 3 a = fill(Point(-0.5),order+1) b = fill(Point(2.5),order+1) -b1 = AgFEMModalC0Basis{1}(V,order,a,b) +b1 = ModalC0Basis{1}(V,order,a,b) ∇b1 = Broadcasting(∇)(b1) ∇∇b1 = Broadcasting(∇)(∇b1) @@ -47,7 +47,7 @@ b = [ Point(1.0,1.0),Point(1.0,1.0),Point(1.0,1.0),Point(1.0,1.0), Point(2.5,2.5),Point(2.5,2.5),Point(1.5,1.5),Point(1.5,1.5), Point(-1.0,1.0),Point(-1.0,1.0),Point(-1.0,1.25),Point(-1.0,1.25), Point(1.0,1.0),Point(1.0,1.0),Point(1.0,1.0),Point(1.0,1.0) ] -b2 = AgFEMModalC0Basis{2}(V,order,a,b) +b2 = ModalC0Basis{2}(V,order,a,b) ∇b2 = Broadcasting(∇)(b2) ∇∇b2 = Broadcasting(∇)(∇b2) diff --git a/test/PolynomialsTests/runtests.jl b/test/PolynomialsTests/runtests.jl index 90b0eef01..eb14c265f 100644 --- a/test/PolynomialsTests/runtests.jl +++ b/test/PolynomialsTests/runtests.jl @@ -10,7 +10,7 @@ using Test @testset "PCurlGradMonomialBases" begin include("PCurlGradMonomialBasesTests.jl") end -@testset "AgFEMModalC0Bases" begin include("AgFEMModalC0BasesTests.jl") end +@testset "ModalC0Bases" begin include("ModalC0BasesTests.jl") end #@testset "ChangeBasis" begin include("ChangeBasisTests.jl") end diff --git a/test/ReferenceFEsTests/ModalC0RefFEsTests.jl b/test/ReferenceFEsTests/ModalC0RefFEsTests.jl new file mode 100644 index 000000000..ca01e8c47 --- /dev/null +++ b/test/ReferenceFEsTests/ModalC0RefFEsTests.jl @@ -0,0 +1,151 @@ +module ModalC0RefFEsTests + +using Test +using Gridap +using Gridap.ReferenceFEs +using Gridap.FESpaces +using Gridap.Fields +using Gridap.CellData + +# using BenchmarkTools + +# # Degenerated case +# order = 0 +# reffe = ReferenceFE(QUAD,modalC0,Float64,order) + +# # Error if create on simplices +# order = 1 +# reffe = ReferenceFE(TRI,modalC0,Float64,order) + +order = 1 +p = QUAD +T = VectorValue{2,Float64} + +m = ReferenceFE(p,modalC0,T,order) +l = ReferenceFE(p,lagrangian,T,order) + +test_reference_fe(m) + +@test num_dofs(m) == num_dofs(l) +@test Conformity(m) === Conformity(l) +@test get_face_own_dofs(m,Conformity(m)) == get_face_own_dofs(l,Conformity(l)) +@test get_face_own_dofs_permutations(m,Conformity(m)) == get_face_own_dofs_permutations(l,Conformity(l)) + +# domain = (0,1,0,1) +# partition = (2,2) +# model = CartesianDiscreteModel(domain,partition) + +function test_function_interpolation(::Type{T},order,C,u,bboxes,R) where T + reffe = ReferenceFE(modalC0,T,order,bboxes,reffe_type=R) + V = FESpace(model,reffe,conformity=C) + test_single_field_fe_space(V) + uh = interpolate(u,V) + Ω = Triangulation(model) + degree = 2*order + dΩ = Measure(Ω,degree) + l2(u) = sqrt(sum( ∫( u⊙u )*dΩ )) + e = u - uh + el2 = l2(e) + @test el2 < 1.0e-9 + # writevtk(Ω,"results",nsubcells=20,cellfields=["uh"=>uh]) + # free_vals = zeros(num_free_dofs(V)); free_vals[7] = 1 + # uh = FEFunction(V,free_vals) + # writevtk(Ω,"shape_7",nsubcells=20,cellfields=["s7"=>uh]) + # free_vals = zeros(num_free_dofs(V)); free_vals[9] = 1 + # uh = FEFunction(V,free_vals) + # writevtk(Ω,"shape_9",nsubcells=20,cellfields=["s9"=>uh]) + # free_vals = zeros(num_free_dofs(V)); free_vals[11] = 1 + # uh = FEFunction(V,free_vals) + # writevtk(Ω,"shape_11",nsubcells=20,cellfields=["s11"=>uh]) + # free_vals = zeros(num_free_dofs(V)); free_vals[13] = 1 + # uh = FEFunction(V,free_vals) + # writevtk(Ω,"shape_13",nsubcells=20,cellfields=["s13"=>uh]) +end + +domain = (0,1) +partition = (1,) +model = CartesianDiscreteModel(domain,partition) +trian = Triangulation(model) + +T = Float64; order = 3; C = :H1; u(x) = x[1]^3 +bboxes = [Point(0.0),Point(1.0),Point(0.0),Point(1.0),Point(-3.0),Point(1.0)] +bboxes = CellPoint(fill(bboxes,1),trian,PhysicalDomain()) +test_function_interpolation(T,order,C,u,bboxes.cell_ref_point,lagrangian) + +domain = (0,1) +partition = (4,) +model = CartesianDiscreteModel(domain,partition) + +T = Float64; order = 3; C = :H1; u(x) = x[1]^3 +bboxes = [ [Point(0.0),Point(1.0),Point(0.0),Point(1.0),Point(-1.0),Point(3.0)], + [Point(0.0),Point(1.0),Point(0.0),Point(1.0),Point(-1.5),Point(3.2)], + [Point(0.0),Point(1.0),Point(0.0),Point(1.0),Point(-0.2),Point(1.0)], + [Point(0.0),Point(1.0),Point(0.0),Point(1.0),Point(-1.2),Point(2.0)] ] +test_function_interpolation(T,order,C,u,bboxes,lagrangian) + +domain = (0,4) +partition = (4,) +model = CartesianDiscreteModel(domain,partition) + +T = Float64; order = 3; C = :H1; u(x) = x[1]^3 +bboxes = [ [Point(0.0),Point(1.0),Point(0.0),Point(1.0),Point(-1.0),Point(3.0)], + [Point(0.0),Point(1.0),Point(0.0),Point(1.0),Point(-1.5),Point(3.2)], + [Point(0.0),Point(1.0),Point(0.0),Point(1.0),Point(-0.2),Point(1.0)], + [Point(0.0),Point(1.0),Point(0.0),Point(1.0),Point(-1.2),Point(2.0)] ] +test_function_interpolation(T,order,C,u,bboxes,lagrangian) + +domain = (0,1,0,1) +partition = (2,2,) +model = CartesianDiscreteModel(domain,partition) +trian = Triangulation(model) + +T = Float64; order = 3; C = :H1; u(x) = (x[1]+x[2])^3 +bboxes = reshape( [Point(0.0,0.0),Point(1.0,1.0),Point(0.0,0.0), + Point(1.0,1.0),Point(0.0,0.0),Point(1.0,1.0), + Point(0.0,0.0),Point(1.0,1.0),Point(-0.5,2.5), + Point(2.5,2.5),Point(-0.5,2.5),Point(2.5,2.5), + Point(-1.0,-1.0),Point(-1.0,1.25),Point(-1.0,-1.0), + Point(-1.0,1.25),Point(0.0,0.0),Point(1.0,1.0)], 18 ) +bboxes = CellPoint(fill(bboxes,4),trian,PhysicalDomain()) +test_function_interpolation(T,order,C,u,bboxes.cell_ref_point,lagrangian) + +# T = Float64; order = 5; C = :H1; u(x) = (x[1]+x[2])^5 +# bboxes = reshape( [Point(0.0,0.0),Point(1.0,1.0),Point(0.0,0.0), +# Point(1.0,1.0),Point(0.0,0.0),Point(1.0,1.0), +# Point(0.0,0.0),Point(1.0,1.0),Point(-0.5,2.5), +# Point(2.5,2.5),Point(-0.5,2.5),Point(2.5,2.5), +# Point(-1.0,-1.0),Point(-1.0,1.25),Point(-1.0,-1.0), +# Point(-1.0,1.25),Point(0.0,0.0),Point(1.0,1.0)], 18 ) +# bboxes = CellPoint(fill(bboxes,4),trian,PhysicalDomain()) +# test_function_interpolation(T,order,C,u,bboxes.cell_ref_point,serendipitylagrangian) + +# order = 1; T = Float64; C = :L2; u(x) = x[1]+x[2] +# test_function_interpolation(T,order,C,u) + +# order = 1; T = VectorValue{2,Float64}; C = :H1 +# u(x) = VectorValue(x[1]+x[2],x[2]) +# test_function_interpolation(T,order,C,u) + +# domain = (0,1,0,1,0,1) +# partition = (2,2,2) +# model = CartesianDiscreteModel(domain,partition) + +# order = 1; T = Float64; C = :H1; u(x) = x[1]+x[2]+x[3] +# test_function_interpolation(T,order,C,u) + +# Inspect operator matrix to check if L2-scalar product of +# gradients of bubble functions satisfy Kronecker's delta +# domain = (0,1) +# partition = (1) +# model = CartesianDiscreteModel(domain,partition) +# order = 6; T = Float64; C = :H1; +# reffe = ReferenceFE(modalC0,T,order) +# V = FESpace(model,reffe,conformity=C) +# Ω = Triangulation(model) +# degree = 2*order +# dΩ = LebesgueMeasure(Ω,degree) +# a(u,v) = ∫( ∇(v)⊙∇(u) )*dΩ +# b(v) = 0.0 +# op = AffineFEOperator(bboxes,V,V) + +end # module diff --git a/test/ReferenceFEsTests/runtests.jl b/test/ReferenceFEsTests/runtests.jl index 066e51d21..45faf6f44 100644 --- a/test/ReferenceFEsTests/runtests.jl +++ b/test/ReferenceFEsTests/runtests.jl @@ -38,6 +38,6 @@ using Test @testset "BezierRefFEs" begin include("BezierRefFEsTests.jl") end -@testset "LinearCombinationDofVectors" begin include("LinearCombinationDofVectorsTests.jl") end +@testset "ModalC0RefFEs" begin include("ModalC0RefFEsTests.jl") end end # module From 1550b4a9acab22934a985bbe50e8fb233c06b37c Mon Sep 17 00:00:00 2001 From: Eric Neiva Date: Wed, 13 Apr 2022 09:31:36 +0200 Subject: [PATCH 3/8] Added Modal C0 RefFEs for Serendipity Lagrangian Spaces --- src/ReferenceFEs/ModalC0RefFEs.jl | 47 +++++------- src/ReferenceFEs/ReferenceFEs.jl | 2 + src/ReferenceFEs/SerendipityRefFEs.jl | 23 +++--- test/ReferenceFEsTests/ModalC0RefFEsTests.jl | 78 ++++++++++---------- 4 files changed, 69 insertions(+), 81 deletions(-) diff --git a/src/ReferenceFEs/ModalC0RefFEs.jl b/src/ReferenceFEs/ModalC0RefFEs.jl index 0e6e2e01e..77f9dcb75 100644 --- a/src/ReferenceFEs/ModalC0RefFEs.jl +++ b/src/ReferenceFEs/ModalC0RefFEs.jl @@ -2,10 +2,6 @@ struct ModalC0 <: ReferenceFEName end const modalC0 = ModalC0() -struct SerendipityLagrangian <: ReferenceFEName end - -const serendipitylagrangian = SerendipityLagrangian() - """ ModalC0RefFE(::Type{T},p::Polytope{D},orders) where {T,D} @@ -29,14 +25,15 @@ function ModalC0RefFE( p::Polytope{D}, orders, a::Vector{Point{D,V}}, - b::Vector{Point{D,V}} ) where {T,D,V} + b::Vector{Point{D,V}}; + space::Symbol=_default_space(p) ) where {T,D,V} @notimplementedif ! is_n_cube(p) @notimplementedif minimum(orders) < one(eltype(orders)) shapefuns = ModalC0Basis{D}(T,orders,a,b) - ndofs, predofs, lag_reffe, face_dofs = compute_reffe_data(T,p,orders,lagrangian) + ndofs, predofs, lag_reffe, face_dofs = compute_reffe_data(T,p,orders,space=space) GenericRefFE{ModalC0}( ndofs, @@ -51,14 +48,15 @@ end function ModalC0RefFE( ::Type{T}, p::Polytope{D}, - orders ) where {T,D,V} + orders; + space::Symbol=_default_space(p) ) where {T,D,V} @notimplementedif ! is_n_cube(p) @notimplementedif minimum(orders) < one(eltype(orders)) shapefuns = ModalC0Basis{D}(T,orders) - ndofs, predofs, lag_reffe, face_dofs = compute_reffe_data(T,p,orders,lagrangian) + ndofs, predofs, lag_reffe, face_dofs = compute_reffe_data(T,p,orders,space=space) GenericRefFE{ModalC0}( ndofs, @@ -76,26 +74,17 @@ end function compute_reffe_data(::Type{T}, p::Polytope{D}, - order::Int, - name::ReferenceFEName) where {T,D} + order::Int; + space::Symbol=_default_space(p)) where {T,D} orders = tfill(order,Val{D}()) - compute_reffe_data(T,p,orders,name) -end - -function compute_reffe_data(::Type{T}, - p::Polytope{D}, - orders::NTuple{D,Int}, - ::Lagrangian) where {T,D} - lag_reffe = LagrangianRefFE(T,p,orders) - reffe = lag_reffe.reffe - reffe.ndofs, reffe.dofs, lag_reffe, reffe.face_dofs + compute_reffe_data(T,p,orders,space=space) end function compute_reffe_data(::Type{T}, p::Polytope{D}, - orders::NTuple{D,Int}, - ::SerendipityLagrangian) where {T,D} - lag_reffe = SerendipityRefFE(T,p,orders) + orders::NTuple{D,Int}; + space::Symbol=_default_space(p)) where {T,D} + lag_reffe = LagrangianRefFE(T,p,orders,space=space) reffe = lag_reffe.reffe reffe.ndofs, reffe.dofs, lag_reffe, reffe.face_dofs end @@ -152,16 +141,16 @@ function compute_cell_to_modalC0_reffe( ::Type{T}, orders::Union{Integer,NTuple{D,Int}}, bboxes; - reffe_type=lagrangian) where {T,D} # type-stability? + space::Symbol=_default_space(p)) where {T,D} # type-stability? @notimplementedif ! is_n_cube(p) @notimplementedif minimum(orders) < one(eltype(orders)) @assert ncells == length(bboxes) - ndofs, predofs, lag_reffe, face_dofs = compute_reffe_data(T,p,orders,reffe_type) + ndofs, predofs, lag_reffe, face_dofs = compute_reffe_data(T,p,orders,space=space) face_own_dofs = get_face_own_dofs(lag_reffe,GradConformity()) - filter = reffe_type == lagrangian ? _q_filter : _s_filter_mc0 + filter = space == :Q ? _q_filter : _s_filter_mc0 sh(bbs) = begin a = fill(Point{D,eltype(T)}(tfill(zero(eltype(T)),Val{D}())),ndofs) @@ -187,14 +176,14 @@ function compute_cell_to_modalC0_reffe( ncells::Int, ::Type{T}, orders::Union{Integer,NTuple{D,Int}}; - reffe_type=lagrangian) where {T,D} # type-stability? + space::Symbol=_default_space(p)) where {T,D} # type-stability? @notimplementedif ! is_n_cube(p) @notimplementedif minimum(orders) < one(eltype(orders)) - filter = reffe_type == lagrangian ? _q_filter : _s_filter_mc0 + filter = space == :Q ? _q_filter : _s_filter_mc0 - ndofs, predofs, lag_reffe, face_dofs = compute_reffe_data(T,p,orders,reffe_type) + ndofs, predofs, lag_reffe, face_dofs = compute_reffe_data(T,p,orders,space=space) reffe = GenericRefFE{ModalC0}(ndofs, p, predofs, diff --git a/src/ReferenceFEs/ReferenceFEs.jl b/src/ReferenceFEs/ReferenceFEs.jl index c9356e2b9..502b395ad 100644 --- a/src/ReferenceFEs/ReferenceFEs.jl +++ b/src/ReferenceFEs/ReferenceFEs.jl @@ -35,6 +35,8 @@ import Gridap.Polynomials: MonomialBasis import Gridap.Polynomials: get_order import Gridap.Polynomials: get_orders +import Gridap.Polynomials: _compute_filter_mask +import Gridap.Polynomials: _define_terms, _sort_by_nfaces! import Gridap.Io: to_dict import Gridap.Io: from_dict diff --git a/src/ReferenceFEs/SerendipityRefFEs.jl b/src/ReferenceFEs/SerendipityRefFEs.jl index 258b9fe64..935a72c0e 100644 --- a/src/ReferenceFEs/SerendipityRefFEs.jl +++ b/src/ReferenceFEs/SerendipityRefFEs.jl @@ -103,24 +103,19 @@ function compute_own_nodes(p::SerendipityPolytope{1},orders) compute_own_nodes(p.hex,orders) end -function compute_own_nodes(p::SerendipityPolytope{2},orders) - order, = orders - if order == 4 - o = (2,2) - elseif order in (0,1,2,3) - o=(1,1) - else - @unreachable "Serendipity elements only up to order 4" - end - compute_own_nodes(p.hex,o) -end +_own_s_filter(e,o) = ( sum( [ i for i in e ] ) <= o && all( [ i > 1 for i in e ] ) ) -function compute_own_nodes(p::SerendipityPolytope{3},orders) - Point{3,Float64}[] +function _compute_own_s_nodes(orders) + _terms = _define_terms(_q_filter,orders) + _sort_by_nfaces!(_terms,orders) + mask = _compute_filter_mask(_terms,_own_s_filter,orders) + _terms = lazy_map(Reindex(_terms),mask) + terms = map(t->CartesianIndex(Tuple(t)),_terms) + _terms_to_coords(terms,orders) end function compute_own_nodes(p::SerendipityPolytope,orders) - @unreachable "Serendipity elements only up to 3d" + _compute_own_s_nodes(orders) end function compute_face_orders( diff --git a/test/ReferenceFEsTests/ModalC0RefFEsTests.jl b/test/ReferenceFEsTests/ModalC0RefFEsTests.jl index ca01e8c47..9f2a6983e 100644 --- a/test/ReferenceFEsTests/ModalC0RefFEsTests.jl +++ b/test/ReferenceFEsTests/ModalC0RefFEsTests.jl @@ -9,14 +9,6 @@ using Gridap.CellData # using BenchmarkTools -# # Degenerated case -# order = 0 -# reffe = ReferenceFE(QUAD,modalC0,Float64,order) - -# # Error if create on simplices -# order = 1 -# reffe = ReferenceFE(TRI,modalC0,Float64,order) - order = 1 p = QUAD T = VectorValue{2,Float64} @@ -35,9 +27,7 @@ test_reference_fe(m) # partition = (2,2) # model = CartesianDiscreteModel(domain,partition) -function test_function_interpolation(::Type{T},order,C,u,bboxes,R) where T - reffe = ReferenceFE(modalC0,T,order,bboxes,reffe_type=R) - V = FESpace(model,reffe,conformity=C) +function _test_function_interpolation(order,u,V) test_single_field_fe_space(V) uh = interpolate(u,V) Ω = Triangulation(model) @@ -62,6 +52,18 @@ function test_function_interpolation(::Type{T},order,C,u,bboxes,R) where T # writevtk(Ω,"shape_13",nsubcells=20,cellfields=["s13"=>uh]) end +function test_function_interpolation(::Type{T},order,C,u) where T + reffe = ReferenceFE(modalC0,T,order) + V = FESpace(model,reffe,conformity=C) + _test_function_interpolation(order,u,V) +end + +function test_function_interpolation(::Type{T},order,C,u,bboxes,space) where T + reffe = ReferenceFE(modalC0,T,order,bboxes,space=space) + V = FESpace(model,reffe,conformity=C) + _test_function_interpolation(order,u,V) +end + domain = (0,1) partition = (1,) model = CartesianDiscreteModel(domain,partition) @@ -70,7 +72,7 @@ trian = Triangulation(model) T = Float64; order = 3; C = :H1; u(x) = x[1]^3 bboxes = [Point(0.0),Point(1.0),Point(0.0),Point(1.0),Point(-3.0),Point(1.0)] bboxes = CellPoint(fill(bboxes,1),trian,PhysicalDomain()) -test_function_interpolation(T,order,C,u,bboxes.cell_ref_point,lagrangian) +test_function_interpolation(T,order,C,u,bboxes.cell_ref_point,:Q) domain = (0,1) partition = (4,) @@ -81,7 +83,7 @@ bboxes = [ [Point(0.0),Point(1.0),Point(0.0),Point(1.0),Point(-1.0),Point(3.0)], [Point(0.0),Point(1.0),Point(0.0),Point(1.0),Point(-1.5),Point(3.2)], [Point(0.0),Point(1.0),Point(0.0),Point(1.0),Point(-0.2),Point(1.0)], [Point(0.0),Point(1.0),Point(0.0),Point(1.0),Point(-1.2),Point(2.0)] ] -test_function_interpolation(T,order,C,u,bboxes,lagrangian) +test_function_interpolation(T,order,C,u,bboxes,:Q) domain = (0,4) partition = (4,) @@ -92,7 +94,7 @@ bboxes = [ [Point(0.0),Point(1.0),Point(0.0),Point(1.0),Point(-1.0),Point(3.0)], [Point(0.0),Point(1.0),Point(0.0),Point(1.0),Point(-1.5),Point(3.2)], [Point(0.0),Point(1.0),Point(0.0),Point(1.0),Point(-0.2),Point(1.0)], [Point(0.0),Point(1.0),Point(0.0),Point(1.0),Point(-1.2),Point(2.0)] ] -test_function_interpolation(T,order,C,u,bboxes,lagrangian) +test_function_interpolation(T,order,C,u,bboxes,:Q) domain = (0,1,0,1) partition = (2,2,) @@ -107,31 +109,31 @@ bboxes = reshape( [Point(0.0,0.0),Point(1.0,1.0),Point(0.0,0.0), Point(-1.0,-1.0),Point(-1.0,1.25),Point(-1.0,-1.0), Point(-1.0,1.25),Point(0.0,0.0),Point(1.0,1.0)], 18 ) bboxes = CellPoint(fill(bboxes,4),trian,PhysicalDomain()) -test_function_interpolation(T,order,C,u,bboxes.cell_ref_point,lagrangian) - -# T = Float64; order = 5; C = :H1; u(x) = (x[1]+x[2])^5 -# bboxes = reshape( [Point(0.0,0.0),Point(1.0,1.0),Point(0.0,0.0), -# Point(1.0,1.0),Point(0.0,0.0),Point(1.0,1.0), -# Point(0.0,0.0),Point(1.0,1.0),Point(-0.5,2.5), -# Point(2.5,2.5),Point(-0.5,2.5),Point(2.5,2.5), -# Point(-1.0,-1.0),Point(-1.0,1.25),Point(-1.0,-1.0), -# Point(-1.0,1.25),Point(0.0,0.0),Point(1.0,1.0)], 18 ) -# bboxes = CellPoint(fill(bboxes,4),trian,PhysicalDomain()) -# test_function_interpolation(T,order,C,u,bboxes.cell_ref_point,serendipitylagrangian) - -# order = 1; T = Float64; C = :L2; u(x) = x[1]+x[2] -# test_function_interpolation(T,order,C,u) - -# order = 1; T = VectorValue{2,Float64}; C = :H1 -# u(x) = VectorValue(x[1]+x[2],x[2]) -# test_function_interpolation(T,order,C,u) - -# domain = (0,1,0,1,0,1) -# partition = (2,2,2) -# model = CartesianDiscreteModel(domain,partition) +test_function_interpolation(T,order,C,u,bboxes.cell_ref_point,:Q) + +T = Float64; order = 5; C = :H1; u(x) = (x[1]+x[2])^5 +bboxes = reshape( [Point(0.0,0.0),Point(1.0,1.0),Point(0.0,0.0), + Point(1.0,1.0),Point(0.0,0.0),Point(1.0,1.0), + Point(0.0,0.0),Point(1.0,1.0),Point(-0.5,2.5), + Point(2.5,2.5),Point(-0.5,2.5),Point(2.5,2.5), + Point(-1.0,-1.0),Point(-1.0,1.25),Point(-1.0,-1.0), + Point(-1.0,1.25),Point(0.0,0.0),Point(1.0,1.0)], 18 ) +bboxes = CellPoint(fill(bboxes,4),trian,PhysicalDomain()) +test_function_interpolation(T,order,C,u,bboxes.cell_ref_point,:S) + +order = 1; T = Float64; C = :L2; u(x) = x[1]+x[2] +test_function_interpolation(T,order,C,u) + +order = 1; T = VectorValue{2,Float64}; C = :H1 +u(x) = VectorValue(x[1]+x[2],x[2]) +test_function_interpolation(T,order,C,u) + +domain = (0,1,0,1,0,1) +partition = (2,2,2) +model = CartesianDiscreteModel(domain,partition) -# order = 1; T = Float64; C = :H1; u(x) = x[1]+x[2]+x[3] -# test_function_interpolation(T,order,C,u) +order = 1; T = Float64; C = :H1; u(x) = x[1]+x[2]+x[3] +test_function_interpolation(T,order,C,u) # Inspect operator matrix to check if L2-scalar product of # gradients of bubble functions satisfy Kronecker's delta From 4ba046e6550032d74057b1b214487ddd5350096e Mon Sep 17 00:00:00 2001 From: Eric Neiva Date: Wed, 13 Apr 2022 11:36:23 +0200 Subject: [PATCH 4/8] Letting eval ModalC0Bases at single point --- src/Polynomials/ModalC0Bases.jl | 42 +++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/src/Polynomials/ModalC0Bases.jl b/src/Polynomials/ModalC0Bases.jl index 78b6e1fa0..f8f112c1c 100644 --- a/src/Polynomials/ModalC0Bases.jl +++ b/src/Polynomials/ModalC0Bases.jl @@ -213,6 +213,48 @@ function evaluate!( r.array end +# Optimizing evaluation at a single point + +function return_cache(f::AbstractVector{ModalC0},x::Point) + xs = [x] + cf = return_cache(f,xs) + v = evaluate!(cf,f,xs) + r = CachedArray(zeros(eltype(v),(size(v,2),))) + r, cf, xs +end + +function evaluate!(cache,f::AbstractVector{ModalC0},x::Point) + r, cf, xs = cache + xs[1] = x + v = evaluate!(cf,f,xs) + ndof = size(v,2) + setsize!(r,(ndof,)) + a = r.array + copyto!(a,v) + a +end + +function return_cache( + f::FieldGradientArray{N,<:AbstractVector{ModalC0}}, x::Point) where {N} + xs = [x] + cf = return_cache(f,xs) + v = evaluate!(cf,f,xs) + r = CachedArray(zeros(eltype(v),(size(v,2),))) + r, cf, xs +end + +function evaluate!( + cache, f::FieldGradientArray{N,<:AbstractVector{ModalC0}}, x::Point) where {N} + r, cf, xs = cache + xs[1] = x + v = evaluate!(cf,f,xs) + ndof = size(v,2) + setsize!(r,(ndof,)) + a = r.array + copyto!(a,v) + a +end + # Helpers _s_filter_mc0(e,o) = ( sum( [ i for i in e if i>1 ] ) <= o ) From 1bf6f6c6200bab2e0e6b43cf58069d283b6186b8 Mon Sep 17 00:00:00 2001 From: Eric Neiva Date: Wed, 13 Apr 2022 11:41:07 +0200 Subject: [PATCH 5/8] Fixed name of modalc0basisfunction --- src/Polynomials/ModalC0Bases.jl | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Polynomials/ModalC0Bases.jl b/src/Polynomials/ModalC0Bases.jl index f8f112c1c..1a0df5eb8 100644 --- a/src/Polynomials/ModalC0Bases.jl +++ b/src/Polynomials/ModalC0Bases.jl @@ -215,7 +215,7 @@ end # Optimizing evaluation at a single point -function return_cache(f::AbstractVector{ModalC0},x::Point) +function return_cache(f::AbstractVector{ModalC0BasisFunction},x::Point) xs = [x] cf = return_cache(f,xs) v = evaluate!(cf,f,xs) @@ -223,7 +223,7 @@ function return_cache(f::AbstractVector{ModalC0},x::Point) r, cf, xs end -function evaluate!(cache,f::AbstractVector{ModalC0},x::Point) +function evaluate!(cache,f::AbstractVector{ModalC0BasisFunction},x::Point) r, cf, xs = cache xs[1] = x v = evaluate!(cf,f,xs) @@ -235,7 +235,7 @@ function evaluate!(cache,f::AbstractVector{ModalC0},x::Point) end function return_cache( - f::FieldGradientArray{N,<:AbstractVector{ModalC0}}, x::Point) where {N} + f::FieldGradientArray{N,<:AbstractVector{ModalC0BasisFunction}}, x::Point) where {N} xs = [x] cf = return_cache(f,xs) v = evaluate!(cf,f,xs) @@ -244,7 +244,7 @@ function return_cache( end function evaluate!( - cache, f::FieldGradientArray{N,<:AbstractVector{ModalC0}}, x::Point) where {N} + cache, f::FieldGradientArray{N,<:AbstractVector{ModalC0BasisFunction}}, x::Point) where {N} r, cf, xs = cache xs[1] = x v = evaluate!(cf,f,xs) From eadad319241a0163b31381153d9b0c877d03a127 Mon Sep 17 00:00:00 2001 From: Eric Neiva Date: Wed, 13 Apr 2022 17:03:53 +0200 Subject: [PATCH 6/8] Expressing Modal C0 shapefuns as linear combination --- src/ReferenceFEs/ModalC0RefFEs.jl | 2 +- src/ReferenceFEs/ReferenceFEInterfaces.jl | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/ReferenceFEs/ModalC0RefFEs.jl b/src/ReferenceFEs/ModalC0RefFEs.jl index 77f9dcb75..c02d03c02 100644 --- a/src/ReferenceFEs/ModalC0RefFEs.jl +++ b/src/ReferenceFEs/ModalC0RefFEs.jl @@ -69,7 +69,7 @@ function ModalC0RefFE( end function get_orders(reffe::GenericRefFE{ModalC0,D}) where{D} - get_orders(get_shapefuns(reffe)) + get_orders(reffe.prebasis) end function compute_reffe_data(::Type{T}, diff --git a/src/ReferenceFEs/ReferenceFEInterfaces.jl b/src/ReferenceFEs/ReferenceFEInterfaces.jl index e4fe4f5cd..f26381605 100644 --- a/src/ReferenceFEs/ReferenceFEInterfaces.jl +++ b/src/ReferenceFEs/ReferenceFEInterfaces.jl @@ -488,7 +488,7 @@ struct GenericRefFE{T,D} <: ReferenceFE{D} conformity, metadata, face_dofs, - shapefuns) + linear_combination(Eye{Int}(ndofs),shapefuns)) # Trick to be able to eval dofs af shapefuns end end From a0fbc898011bb9087eb9b8953db1bf5a000cc145 Mon Sep 17 00:00:00 2001 From: Eric Neiva Date: Wed, 27 Apr 2022 17:27:39 +0200 Subject: [PATCH 7/8] Add precision to comment --- src/ReferenceFEs/ReferenceFEInterfaces.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ReferenceFEs/ReferenceFEInterfaces.jl b/src/ReferenceFEs/ReferenceFEInterfaces.jl index f26381605..1542801b0 100644 --- a/src/ReferenceFEs/ReferenceFEInterfaces.jl +++ b/src/ReferenceFEs/ReferenceFEInterfaces.jl @@ -488,7 +488,7 @@ struct GenericRefFE{T,D} <: ReferenceFE{D} conformity, metadata, face_dofs, - linear_combination(Eye{Int}(ndofs),shapefuns)) # Trick to be able to eval dofs af shapefuns + linear_combination(Eye{Int}(ndofs),shapefuns)) # Trick to be able to eval dofs af shapefuns in physical space end end From 5a2a156f9908996110d180415397ed02880d91fe Mon Sep 17 00:00:00 2001 From: Eric Neiva Date: Wed, 27 Apr 2022 17:30:57 +0200 Subject: [PATCH 8/8] Update NEWS.md following PR #777 --- NEWS.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/NEWS.md b/NEWS.md index c05813665..bdbfe165c 100644 --- a/NEWS.md +++ b/NEWS.md @@ -4,6 +4,12 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## Unreleased + +### Added +- Implemented (generalised) ModalC0 Polynomial bases and reference FEs. Since PR [#777](https://github.com/gridap/Gridap.jl/pull/777) +- Serendipity reference FEs for any dimension and order. Since PR [#777](https://github.com/gridap/Gridap.jl/pull/777) + ## [0.17.12] - 2022-03-24 ### Fixed