From d93abbe68ffe984c39946f84771358f8c8bd45ca Mon Sep 17 00:00:00 2001 From: JordiManyer Date: Sat, 12 Oct 2024 15:44:36 +1100 Subject: [PATCH 001/105] Optimise sign-flip map --- src/FESpaces/CurlConformingFESpaces.jl | 4 +- src/FESpaces/DivConformingFESpaces.jl | 184 ++++++++++++------------- 2 files changed, 93 insertions(+), 95 deletions(-) diff --git a/src/FESpaces/CurlConformingFESpaces.jl b/src/FESpaces/CurlConformingFESpaces.jl index a16c2cc5e..0bf9e45cc 100644 --- a/src/FESpaces/CurlConformingFESpaces.jl +++ b/src/FESpaces/CurlConformingFESpaces.jl @@ -2,8 +2,8 @@ function get_cell_dof_basis( model::DiscreteModel, cell_reffe::AbstractArray{<:GenericRefFE{Nedelec}}, - ::CurlConformity) - + ::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)) diff --git a/src/FESpaces/DivConformingFESpaces.jl b/src/FESpaces/DivConformingFESpaces.jl index 5decd79ab..708b8f6ce 100644 --- a/src/FESpaces/DivConformingFESpaces.jl +++ b/src/FESpaces/DivConformingFESpaces.jl @@ -24,118 +24,114 @@ # shape function corresponding to the global DoF. # * We do NOT have to use the signed determinant, but its absolute value, in the Piola Map. -struct TransformRTDofBasis{Dc,Dp} <: Map end ; - -function get_cell_dof_basis(model::DiscreteModel, - cell_reffe::AbstractArray{<:GenericRefFE{<:DivConforming}}, - ::DivConformity, - sign_flip=get_sign_flip(model, cell_reffe)) - cell_map = get_cell_map(Triangulation(model)) - phi = cell_map[1] - Jt = lazy_map(Broadcasting(∇),cell_map) - x = lazy_map(get_nodes,lazy_map(get_dof_basis,cell_reffe)) - Jtx = lazy_map(evaluate,Jt,x) - reffe = cell_reffe[1] - Dc = num_dims(reffe) - # @santiagobadia: A hack here, for RT returns Float64 and for BDM VectorValue{Float64} - et = eltype(return_type(get_prebasis(reffe))) - pt = Point{Dc,et} - Dp = first(size(return_type(phi,zero(pt)))) - k = TransformRTDofBasis{Dc,Dp}() - lazy_map(k,cell_reffe,Jtx,sign_flip) +struct TransformRTDofBasis{Dc,Dp} <: Map end + +function get_cell_dof_basis( + model::DiscreteModel{Dc,Dp}, + cell_reffe::AbstractArray{<:GenericRefFE{<:DivConforming}}, + ::DivConformity, + sign_flip = get_sign_flip(model, cell_reffe) +) where {Dc,Dp} + cell_map = get_cell_map(get_grid(model)) + Jt = lazy_map(Broadcasting(∇),cell_map) + x = lazy_map(get_nodes,lazy_map(get_dof_basis,cell_reffe)) + Jtx = lazy_map(evaluate,Jt,x) + k = TransformRTDofBasis{Dc,Dp}() + lazy_map(k,cell_reffe,Jtx,sign_flip) end -function get_cell_shapefuns(model::DiscreteModel, - cell_reffe::AbstractArray{<:GenericRefFE{<:DivConforming}}, - ::DivConformity, - sign_flip=get_sign_flip(model, cell_reffe)) - cell_reffe_shapefuns=lazy_map(get_shapefuns,cell_reffe) - k=ContraVariantPiolaMap() - lazy_map(k, - cell_reffe_shapefuns, - get_cell_map(Triangulation(model)), - lazy_map(Broadcasting(constant_field), sign_flip)) +function get_cell_shapefuns( + model::DiscreteModel, + cell_reffe::AbstractArray{<:GenericRefFE{<:DivConforming}}, + ::DivConformity, + sign_flip = get_sign_flip(model, cell_reffe) +) + cell_map = get_cell_map(get_grid(model)) + cell_shapefuns = lazy_map(get_shapefuns,cell_reffe) + k = ContraVariantPiolaMap() + lazy_map(k,cell_shapefuns,cell_map,lazy_map(Broadcasting(constant_field),sign_flip)) end struct SignFlipMap{T} <: Map model::T + facet_owners::Vector{Int32} end -function return_cache(k::SignFlipMap,reffe,cell_id) - model = k.model - D = num_cell_dims(model) - gtopo = get_grid_topology(model) - - # Extract composition among cells and facets - cell_wise_facets_ids = get_faces(gtopo, D, D - 1) - cache_cell_wise_facets_ids = array_cache(cell_wise_facets_ids) +function SignFlipMap(model) + facet_owners = compute_facet_owners(model) + SignFlipMap(model,facet_owners) +end - # Extract cells around facets - cells_around_facets = get_faces(gtopo, D - 1, D) - cache_cells_around_facets = array_cache(cells_around_facets) +function return_cache(k::SignFlipMap,reffe,facet_own_dofs,cell) + model = k.model + Dc = num_cell_dims(model) + topo = get_grid_topology(model) - (cell_wise_facets_ids, - cache_cell_wise_facets_ids, - cells_around_facets, - cache_cells_around_facets, - CachedVector(Bool)) + cell_facets = get_faces(topo, Dc, Dc-1) + cell_facets_cache = array_cache(cell_facets) + return cell_facets,cell_facets_cache,CachedVector(Bool) end -function evaluate!(cache,k::SignFlipMap,reffe,cell_id) - model = k.model - - cell_wise_facets_ids, - cache_cell_wise_facets_ids, - cells_around_facets, - cache_cells_around_facets, - sign_flip_cached = cache +function evaluate!(cache,k::SignFlipMap,reffe,facet_own_dofs,cell) + cell_facets,cell_facets_cache,sign_flip_cache = cache + facet_owners = k.facet_owners - setsize!(sign_flip_cached, (num_dofs(reffe),)) - sign_flip = sign_flip_cached.array + setsize!(sign_flip_cache, (num_dofs(reffe),)) + sign_flip = sign_flip_cache.array sign_flip .= false - D = num_dims(reffe) - face_own_dofs = get_face_own_dofs(reffe) - facet_lid = get_offsets(get_polytope(reffe))[D] + 1 - cell_facets_ids = getindex!(cache_cell_wise_facets_ids, - cell_wise_facets_ids, - cell_id) - for facet_gid in cell_facets_ids - facet_cells_around = getindex!(cache_cells_around_facets, - cells_around_facets, - facet_gid) - is_slave = (findfirst((x) -> (x == cell_id), facet_cells_around) == 2) - if is_slave - for dof in face_own_dofs[facet_lid] - sign_flip[dof] = true - end + facets = getindex!(cell_facets_cache,cell_facets,cell) + for (lfacet,facet) in enumerate(facets) + owner = facet_owners[facet] + if owner != cell + for dof in facet_own_dofs[lfacet] + sign_flip[dof] = true end - facet_lid = facet_lid + 1 + end end - sign_flip + + return sign_flip end -function get_sign_flip(model::DiscreteModel, - cell_reffe::AbstractArray{<:GenericRefFE{<:DivConforming}}) - lazy_map(SignFlipMap(model), - cell_reffe, - IdentityVector(Int32(num_cells(model)))) +function get_sign_flip( + model::DiscreteModel{Dc}, + cell_reffe::AbstractArray{<:GenericRefFE{<:DivConforming}} +) where Dc + # Comment: lazy_maps on cell_reffes are very optimised, since they are CompressedArray/FillArray + get_facet_own_dofs(reffe) = view(get_face_own_dofs(reffe),get_dimrange(get_polytope(reffe),Dc-1)) + cell_facet_own_dofs = lazy_map(get_facet_own_dofs,cell_reffe) + cell_ids = IdentityVector(Int32(num_cells(model))) + lazy_map(SignFlipMap(model),cell_reffe,cell_facet_own_dofs,cell_ids) end -function return_cache(::TransformRTDofBasis{Dc,Dp}, - reffe::GenericRefFE{<:DivConforming}, - Jtx, - ::AbstractVector{Bool}) where {Dc,Dp} - p = get_polytope(reffe) - prebasis = get_prebasis(reffe) - order = get_order(prebasis) +function compute_facet_owners(model::DiscreteModel{Dc,Dp}) where {Dc,Dp} + topo = get_grid_topology(model) + facet_to_cell = get_faces(topo, Dc-1, Dc) + + nfacets = num_faces(topo, Dc-1) + owners = Vector{Int32}(undef, nfacets) + for facet in 1:nfacets + facet_cells = view(facet_to_cell, facet) + owners[facet] = first(facet_cells) + end + + return owners +end + +function return_cache( + ::TransformRTDofBasis{Dc,Dp}, + reffe::GenericRefFE{<:DivConforming}, + Jtx, + ::AbstractVector{Bool} +) where {Dc,Dp} # @santiagobadia: Hack as above - et = eltype(return_type(prebasis)) + et = eltype(return_type(get_prebasis(reffe))) dofs = get_dof_basis(reffe) - nodes, nf_nodes, nf_moments = get_nodes(dofs), - get_face_nodes_dofs(dofs), - get_face_moments(dofs) + + nodes = get_nodes(dofs) + nf_nodes = get_face_nodes_dofs(dofs) + nf_moments = get_face_moments(dofs) db = MomentBasedDofBasis(nodes,nf_moments,nf_nodes) face_moments = [ similar(i,VectorValue{Dp,et}) for i in nf_moments ] @@ -143,11 +139,13 @@ function return_cache(::TransformRTDofBasis{Dc,Dp}, cache end -function evaluate!(cache, - ::TransformRTDofBasis, - reffe::GenericRefFE{<:DivConforming}, - Jt_q, - sign_flip::AbstractVector{Bool}) +function evaluate!( + cache, + ::TransformRTDofBasis, + reffe::GenericRefFE{<:DivConforming}, + Jt_q, + sign_flip::AbstractVector{Bool} +) nodes, nf_nodes, nf_moments, face_moments = cache face_own_dofs=get_face_own_dofs(reffe) for face in 1:length(face_moments) From 9bb709340c681a9df156d86476bf61d24f92715f Mon Sep 17 00:00:00 2001 From: JordiManyer Date: Sat, 12 Oct 2024 18:05:49 +1100 Subject: [PATCH 002/105] Massive optimisations to RT --- src/FESpaces/DivConformingFESpaces.jl | 15 ++++--- src/Fields/FieldsInterfaces.jl | 29 +++++++----- src/ReferenceFEs/RaviartThomasRefFEs.jl | 60 +++++++++++++------------ 3 files changed, 57 insertions(+), 47 deletions(-) diff --git a/src/FESpaces/DivConformingFESpaces.jl b/src/FESpaces/DivConformingFESpaces.jl index 708b8f6ce..32d032abc 100644 --- a/src/FESpaces/DivConformingFESpaces.jl +++ b/src/FESpaces/DivConformingFESpaces.jl @@ -168,20 +168,23 @@ end # Support for DIV operator -function DIV(f::LazyArray{<:Fill{T}}) where T - df=DIV(f.args[1]) - k=f.maps.value + +function DIV(f::LazyArray{<:Fill}) + df = DIV(f.args[1]) + k = f.maps.value lazy_map(k,df) end + function DIV(f::LazyArray{<:Fill{Broadcasting{Operation{ContraVariantPiolaMap}}}}) ϕrgₖ = f.args[1] fsign_flip = f.args[4] - div_ϕrgₖ = lazy_map(Broadcasting(divergence),ϕrgₖ) - fsign_flip=lazy_map(Broadcasting(Operation(x->(-1)^x)), fsign_flip) + div_ϕrgₖ = lazy_map(Broadcasting(divergence),ϕrgₖ) + fsign_flip = lazy_map(Broadcasting(Operation(x->(-1)^x)), fsign_flip) lazy_map(Broadcasting(Operation(*)),fsign_flip,div_ϕrgₖ) end + function DIV(a::LazyArray{<:Fill{typeof(linear_combination)}}) - i_to_basis = DIV(a.args[2]) + i_to_basis = DIV(a.args[2]) i_to_values = a.args[1] lazy_map(linear_combination,i_to_values,i_to_basis) end diff --git a/src/Fields/FieldsInterfaces.jl b/src/Fields/FieldsInterfaces.jl index bdad6a111..7b948d3ff 100644 --- a/src/Fields/FieldsInterfaces.jl +++ b/src/Fields/FieldsInterfaces.jl @@ -246,21 +246,26 @@ function evaluate!(c,f::ConstantField,x::Point) f.value end -function return_cache(f::ConstantField,x::AbstractArray{<:Point}) - nx = size(x) - c = fill(f.value,nx) - CachedArray(c) -end +#function return_cache(f::ConstantField,x::AbstractArray{<:Point}) +# nx = size(x) +# c = fill(f.value,nx) +# CachedArray(c) +#end +# +#function evaluate!(c,f::ConstantField,x::AbstractArray{<:Point}) +# nx = size(x) +# # This optimization is a bug if we include several ConstantField with different states +# # in the same array and we try to reuse cache between them. +# #if size(c) != nx +# setsize!(c,nx) +# fill!(c.array,f.value) +# #end +# c.array +#end function evaluate!(c,f::ConstantField,x::AbstractArray{<:Point}) nx = size(x) - # This optimization is a bug if we include several ConstantField with different states - # in the same array and we try to reuse cache between them. - #if size(c) != nx - setsize!(c,nx) - fill!(c.array,f.value) - #end - c.array + return Fill(f.value,nx) end function return_cache(f::FieldGradient{N,<:ConstantField},x::Point) where N diff --git a/src/ReferenceFEs/RaviartThomasRefFEs.jl b/src/ReferenceFEs/RaviartThomasRefFEs.jl index d4388ea94..f6990f47f 100644 --- a/src/ReferenceFEs/RaviartThomasRefFEs.jl +++ b/src/ReferenceFEs/RaviartThomasRefFEs.jl @@ -379,49 +379,51 @@ struct ContraVariantPiolaMap <: Map end function evaluate!( cache, ::Broadcasting{typeof(∇)}, - a::Fields.BroadcastOpFieldArray{ContraVariantPiolaMap}) - v, Jt, detJ,sign_flip = a.args - # Assuming J comes from an affine map + a::Fields.BroadcastOpFieldArray{ContraVariantPiolaMap} +) + v, Jt, sign_flip = a.args ∇v = Broadcasting(∇)(v) k = ContraVariantPiolaMap() - Broadcasting(Operation(k))(∇v,Jt,detJ,sign_flip) + Broadcasting(Operation(k))(∇v,Jt,sign_flip) end function lazy_map( ::Broadcasting{typeof(gradient)}, - a::LazyArray{<:Fill{Broadcasting{Operation{ContraVariantPiolaMap}}}}) - v, Jt, detJ,sign_flip = a.args + a::LazyArray{<:Fill{Broadcasting{Operation{ContraVariantPiolaMap}}}} +) + v, Jt, sign_flip = a.args ∇v = lazy_map(Broadcasting(∇),v) k = ContraVariantPiolaMap() - lazy_map(Broadcasting(Operation(k)),∇v,Jt,detJ,sign_flip) -end - -function evaluate!(cache,::ContraVariantPiolaMap, - v::Number, - Jt::Number, - detJ::Number, - sign_flip::Bool) - ((-1)^sign_flip*v)⋅((1/detJ)*Jt) -end - -function evaluate!(cache, - k::ContraVariantPiolaMap, - v::AbstractVector{<:Field}, - phi::Field, - sign_flip::AbstractVector{<:Field}) - Jt = ∇(phi) - detJ = Operation(meas)(Jt) - Broadcasting(Operation(k))(v,Jt,detJ,sign_flip) + lazy_map(Broadcasting(Operation(k)),∇v,Jt,sign_flip) end function lazy_map( k::ContraVariantPiolaMap, cell_ref_shapefuns::AbstractArray{<:AbstractArray{<:Field}}, cell_map::AbstractArray{<:Field}, - sign_flip::AbstractArray{<:AbstractArray{<:Field}}) - + sign_flip::AbstractArray{<:AbstractArray{<:Field}} +) cell_Jt = lazy_map(∇,cell_map) - cell_detJ = lazy_map(Operation(meas),cell_Jt) + lazy_map(Broadcasting(Operation(k)),cell_ref_shapefuns,cell_Jt,sign_flip) +end - lazy_map(Broadcasting(Operation(k)),cell_ref_shapefuns,cell_Jt,cell_detJ,sign_flip) +function evaluate!( + cache,::ContraVariantPiolaMap, + v::Number, + Jt::Number, + sign_flip::Bool +) + idetJ = 1/meas(Jt) + ((-1)^sign_flip*v)⋅(idetJ*Jt) +end + +function evaluate!( + cache, + k::ContraVariantPiolaMap, + v::AbstractVector{<:Field}, + phi::Field, + sign_flip::AbstractVector{<:Field} +) + Jt = ∇(phi) + Broadcasting(Operation(k))(v,Jt,sign_flip) end From 4ab8e0d95752217928b36fbba22a689fae68642a Mon Sep 17 00:00:00 2001 From: JordiManyer Date: Sat, 12 Oct 2024 18:59:19 +1100 Subject: [PATCH 003/105] Added ConstantFieldArrays --- src/Fields/FieldArrays.jl | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/src/Fields/FieldArrays.jl b/src/Fields/FieldArrays.jl index 12379b639..f8a2eee1a 100644 --- a/src/Fields/FieldArrays.jl +++ b/src/Fields/FieldArrays.jl @@ -694,3 +694,39 @@ for op in (:*,:⋅,:⊙,:⊗) end end end + +# Optimisations to +# lazy_map(Broadcasting(constant_field),a::AbstractArray{<:AbstractArray{<:Number}}) + +struct ConstantFieldArray{T,N} <: AbstractArray{ConstantField{T},N} + values::Array{T,N} +end + +Base.size(a::ConstantFieldArray) = size(a.values) +Base.axes(a::ConstantFieldArray) = axes(a.values) +Base.getindex(a::ConstantFieldArray,i::Integer) = ConstantField(a.values[i]) + +function return_value(::Broadcasting{typeof(constant_field)},values::AbstractArray{<:Number}) + ConstantFieldArray(values) +end + +function evaluate!(cache,::Broadcasting{typeof(constant_field)},values::AbstractArray{<:Number}) + ConstantFieldArray(values) +end + +function evaluate!(c,f::ConstantFieldArray,x::Point) + return f.values +end + +function return_cache(f::ConstantFieldArray{T},x::AbstractArray{<:Point}) where T + return CachedArray(zeros(T,(size(x)...,size(f)...))) +end + +function evaluate!(c,f::ConstantFieldArray{T},x::AbstractArray{<:Point}) where T + setsize!(c,(size(x)...,size(f)...)) + r = c.array + for i in eachindex(x) + r[i,:] .= f.values + end + return r +end From c192534091df7b39d2904ce62cda61d4b6f49a19 Mon Sep 17 00:00:00 2001 From: JordiManyer Date: Mon, 14 Oct 2024 10:51:22 +1100 Subject: [PATCH 004/105] Added gitignore for test folder --- test/.gitignore | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 test/.gitignore diff --git a/test/.gitignore b/test/.gitignore new file mode 100644 index 000000000..5c07398da --- /dev/null +++ b/test/.gitignore @@ -0,0 +1,2 @@ +*.jl +!runtests.jl \ No newline at end of file From a553631e24970f58518c194534e68500f6bc656b Mon Sep 17 00:00:00 2001 From: JordiManyer Date: Mon, 14 Oct 2024 11:27:41 +1100 Subject: [PATCH 005/105] Minor modification to constantFieldArrays --- src/Fields/FieldArrays.jl | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/Fields/FieldArrays.jl b/src/Fields/FieldArrays.jl index f8a2eee1a..e7e2c2c42 100644 --- a/src/Fields/FieldArrays.jl +++ b/src/Fields/FieldArrays.jl @@ -698,8 +698,12 @@ end # Optimisations to # lazy_map(Broadcasting(constant_field),a::AbstractArray{<:AbstractArray{<:Number}}) -struct ConstantFieldArray{T,N} <: AbstractArray{ConstantField{T},N} - values::Array{T,N} +struct ConstantFieldArray{T,N,A} <: AbstractArray{ConstantField{T},N} + values::A + function ConstantFieldArray(values::AbstractArray{T,N}) where {T,N} + A = typeof(values) + new{T,N,A}(values) + end end Base.size(a::ConstantFieldArray) = size(a.values) From 13a3861c985dfe65a902bc53e4c8524648146121 Mon Sep 17 00:00:00 2001 From: JordiManyer Date: Mon, 14 Oct 2024 21:15:23 +1100 Subject: [PATCH 006/105] Minor bugfix --- src/Adaptivity/MacroFEs.jl | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/Adaptivity/MacroFEs.jl b/src/Adaptivity/MacroFEs.jl index 4fd09c1a2..031a73b04 100644 --- a/src/Adaptivity/MacroFEs.jl +++ b/src/Adaptivity/MacroFEs.jl @@ -247,6 +247,9 @@ function Arrays.evaluate!(caches, a::MacroFEBasis,xc::AbstractArray{<:Point}) for fcell in 1:num_subcells(a.rrule) r = xf.ptrs[fcell]:xf.ptrs[fcell+1]-1 + if isempty(r) + continue + end vals = evaluate!(eval_caches[fcell],a.fine_data[fcell],view(xf.data,r)) I = view(ids.data,r) J = a.ids.fcell_to_cids[fcell] From 2dbda6a81a2e344be4db00230b79fa2e9ac1baa7 Mon Sep 17 00:00:00 2001 From: JordiManyer Date: Mon, 14 Oct 2024 21:38:54 +1100 Subject: [PATCH 007/105] Minor --- src/Adaptivity/MacroFEs.jl | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/src/Adaptivity/MacroFEs.jl b/src/Adaptivity/MacroFEs.jl index 031a73b04..f96a1d181 100644 --- a/src/Adaptivity/MacroFEs.jl +++ b/src/Adaptivity/MacroFEs.jl @@ -230,28 +230,26 @@ function Arrays.return_cache(a::MacroFEBasis,xc::AbstractArray{<:Point}) geo_cache = return_cache(k,rr,xc) xf, ids = evaluate!(geo_cache,k,rr,xc) + xf_cache = array_cache(xf) eval_caches = map(return_cache,a.fine_data,xf) T = eltype(evaluate!(first(eval_caches),first(a.fine_data),first(xf))) res_cache = CachedArray(zeros(T,length(xc),length(a))) - return res_cache, k, geo_cache, eval_caches + return res_cache, k, geo_cache, eval_caches, xf_cache end function Arrays.evaluate!(caches, a::MacroFEBasis,xc::AbstractArray{<:Point}) - res_cache, k, geo_cache, eval_caches = caches + res_cache, k, geo_cache, eval_caches, xf_cache = caches setsize!(res_cache,(length(xc),length(a))) res = res_cache.array fill!(res,zero(eltype(res))) xf, ids = evaluate!(geo_cache,k,a.rrule,xc) - for fcell in 1:num_subcells(a.rrule) - r = xf.ptrs[fcell]:xf.ptrs[fcell+1]-1 - if isempty(r) - continue - end - vals = evaluate!(eval_caches[fcell],a.fine_data[fcell],view(xf.data,r)) - I = view(ids.data,r) + for fcell in 1:num_subcells(a.rrule) + xf_k = getindex!(xf_cache,xf,fcell) + vals = evaluate!(eval_caches[fcell],a.fine_data[fcell],xf_k) + I = view(ids,fcell) J = a.ids.fcell_to_cids[fcell] res[I,J] .= vals end From d22809fd70e8c35cc35201252bd4492b60ea504c Mon Sep 17 00:00:00 2001 From: JordiManyer Date: Mon, 14 Oct 2024 21:56:05 +1100 Subject: [PATCH 008/105] Minor --- src/Adaptivity/MacroFEs.jl | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/Adaptivity/MacroFEs.jl b/src/Adaptivity/MacroFEs.jl index f96a1d181..af1974f2a 100644 --- a/src/Adaptivity/MacroFEs.jl +++ b/src/Adaptivity/MacroFEs.jl @@ -229,9 +229,11 @@ function Arrays.return_cache(a::MacroFEBasis,xc::AbstractArray{<:Point}) k = CoarseToFinePointMap() geo_cache = return_cache(k,rr,xc) xf, ids = evaluate!(geo_cache,k,rr,xc) - xf_cache = array_cache(xf) - eval_caches = map(return_cache,a.fine_data,xf) + + # NOTE: xf may be empty for some subcells, so it's safer to use testvalue + xt = testvalue(xc) + eval_caches = map(ffields -> return_cache(ffields,xt),a.fine_data) T = eltype(evaluate!(first(eval_caches),first(a.fine_data),first(xf))) res_cache = CachedArray(zeros(T,length(xc),length(a))) From ace1df474e4a046fc84e3aea021d1a9683f36d91 Mon Sep 17 00:00:00 2001 From: JordiManyer Date: Mon, 14 Oct 2024 22:04:05 +1100 Subject: [PATCH 009/105] Minor --- src/Adaptivity/MacroFEs.jl | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/Adaptivity/MacroFEs.jl b/src/Adaptivity/MacroFEs.jl index af1974f2a..293746de8 100644 --- a/src/Adaptivity/MacroFEs.jl +++ b/src/Adaptivity/MacroFEs.jl @@ -235,7 +235,7 @@ function Arrays.return_cache(a::MacroFEBasis,xc::AbstractArray{<:Point}) xt = testvalue(xc) eval_caches = map(ffields -> return_cache(ffields,xt),a.fine_data) - T = eltype(evaluate!(first(eval_caches),first(a.fine_data),first(xf))) + T = eltype(evaluate!(first(eval_caches),first(a.fine_data),xt)) res_cache = CachedArray(zeros(T,length(xc),length(a))) return res_cache, k, geo_cache, eval_caches, xf_cache end @@ -250,6 +250,9 @@ function Arrays.evaluate!(caches, a::MacroFEBasis,xc::AbstractArray{<:Point}) for fcell in 1:num_subcells(a.rrule) xf_k = getindex!(xf_cache,xf,fcell) + if isempty(xf_k) + continue + end vals = evaluate!(eval_caches[fcell],a.fine_data[fcell],xf_k) I = view(ids,fcell) J = a.ids.fcell_to_cids[fcell] From 513e78a9befb91f86f4257f5d8f57c3fc7333e20 Mon Sep 17 00:00:00 2001 From: JordiManyer Date: Tue, 15 Oct 2024 10:20:26 +1100 Subject: [PATCH 010/105] Bugfixes --- src/FESpaces/DivConformingFESpaces.jl | 2 +- src/Fields/FieldsInterfaces.jl | 29 +++++++++++---------------- 2 files changed, 13 insertions(+), 18 deletions(-) diff --git a/src/FESpaces/DivConformingFESpaces.jl b/src/FESpaces/DivConformingFESpaces.jl index 32d032abc..bcc6c3a42 100644 --- a/src/FESpaces/DivConformingFESpaces.jl +++ b/src/FESpaces/DivConformingFESpaces.jl @@ -177,7 +177,7 @@ end function DIV(f::LazyArray{<:Fill{Broadcasting{Operation{ContraVariantPiolaMap}}}}) ϕrgₖ = f.args[1] - fsign_flip = f.args[4] + fsign_flip = f.args[3] div_ϕrgₖ = lazy_map(Broadcasting(divergence),ϕrgₖ) fsign_flip = lazy_map(Broadcasting(Operation(x->(-1)^x)), fsign_flip) lazy_map(Broadcasting(Operation(*)),fsign_flip,div_ϕrgₖ) diff --git a/src/Fields/FieldsInterfaces.jl b/src/Fields/FieldsInterfaces.jl index 7b948d3ff..bdad6a111 100644 --- a/src/Fields/FieldsInterfaces.jl +++ b/src/Fields/FieldsInterfaces.jl @@ -246,26 +246,21 @@ function evaluate!(c,f::ConstantField,x::Point) f.value end -#function return_cache(f::ConstantField,x::AbstractArray{<:Point}) -# nx = size(x) -# c = fill(f.value,nx) -# CachedArray(c) -#end -# -#function evaluate!(c,f::ConstantField,x::AbstractArray{<:Point}) -# nx = size(x) -# # This optimization is a bug if we include several ConstantField with different states -# # in the same array and we try to reuse cache between them. -# #if size(c) != nx -# setsize!(c,nx) -# fill!(c.array,f.value) -# #end -# c.array -#end +function return_cache(f::ConstantField,x::AbstractArray{<:Point}) + nx = size(x) + c = fill(f.value,nx) + CachedArray(c) +end function evaluate!(c,f::ConstantField,x::AbstractArray{<:Point}) nx = size(x) - return Fill(f.value,nx) + # This optimization is a bug if we include several ConstantField with different states + # in the same array and we try to reuse cache between them. + #if size(c) != nx + setsize!(c,nx) + fill!(c.array,f.value) + #end + c.array end function return_cache(f::FieldGradient{N,<:ConstantField},x::Point) where N From 37cb07685219995b97dc6d2cf0e48d44b3fe6eb7 Mon Sep 17 00:00:00 2001 From: JordiManyer Date: Mon, 21 Oct 2024 16:38:08 +1100 Subject: [PATCH 011/105] Expanded Jacobi and added Chebyshev --- src/Polynomials/ChebyshevPolynomialBases.jl | 581 ++++++++++++++++++ src/Polynomials/Polynomials.jl | 9 + src/Polynomials/QGradJacobiPolynomialBases.jl | 303 +++++++++ src/ReferenceFEs/RaviartThomasRefFEs.jl | 55 +- test/AdaptivityTests/RefinementRulesTests.jl | 9 + 5 files changed, 948 insertions(+), 9 deletions(-) create mode 100644 src/Polynomials/ChebyshevPolynomialBases.jl create mode 100644 src/Polynomials/QGradJacobiPolynomialBases.jl diff --git a/src/Polynomials/ChebyshevPolynomialBases.jl b/src/Polynomials/ChebyshevPolynomialBases.jl new file mode 100644 index 000000000..7e6ff5b40 --- /dev/null +++ b/src/Polynomials/ChebyshevPolynomialBases.jl @@ -0,0 +1,581 @@ +struct ChebyshevPolynomial <: Field end + +struct ChebyshevPolynomialBasis{D,T} <: AbstractVector{ChebyshevPolynomial} + orders::NTuple{D,Int} + terms::Vector{CartesianIndex{D}} + function ChebyshevPolynomialBasis{D}( + ::Type{T}, orders::NTuple{D,Int}, terms::Vector{CartesianIndex{D}}) where {D,T} + new{D,T}(orders,terms) + end +end + +@inline Base.size(a::ChebyshevPolynomialBasis{D,T}) where {D,T} = (length(a.terms)*num_components(T),) +@inline Base.getindex(a::ChebyshevPolynomialBasis,i::Integer) = ChebyshevPolynomial() +@inline Base.IndexStyle(::ChebyshevPolynomialBasis) = IndexLinear() + +function ChebyshevPolynomialBasis{D}( + ::Type{T}, orders::NTuple{D,Int}, filter::Function=_q_filter) where {D,T} + + terms = _define_terms(filter, orders) + ChebyshevPolynomialBasis{D}(T,orders,terms) +end + +function ChebyshevPolynomialBasis{D}( + ::Type{T}, order::Int, filter::Function=_q_filter) where {D,T} + + orders = tfill(order,Val{D}()) + ChebyshevPolynomialBasis{D}(T,orders,filter) +end + +# API + +function get_exponents(b::ChebyshevPolynomialBasis) + indexbase = 1 + [Tuple(t) .- indexbase for t in b.terms] +end + +function get_order(b::ChebyshevPolynomialBasis) + maximum(b.orders) +end + +function get_orders(b::ChebyshevPolynomialBasis) + b.orders +end + +return_type(::ChebyshevPolynomialBasis{D,T}) where {D,T} = T + +# Field implementation + +function return_cache(f::ChebyshevPolynomialBasis{D,T},x::AbstractVector{<:Point}) where {D,T} + @check 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::ChebyshevPolynomialBasis{D,T},x::AbstractVector{<:Point}) where {D,T} + 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_ch!(v,xi,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,ChebyshevPolynomialBasis{D,V}}, + x::AbstractVector{<:Point}) where {D,V} + + 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,ChebyshevPolynomialBasis{D,T}}, + x::AbstractVector{<:Point}) where {D,T} + + 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_ch!(v,xi,f.orders,f.terms,c,g,T) + for j in 1:ndof + @inbounds r[i,j] = v[j] + end + end + r.array +end + +# Optimizing evaluation at a single point + +function return_cache(f::ChebyshevPolynomialBasis{D,T},x::Point) where {D,T} + ndof = length(f.terms)*num_components(T) + r = CachedArray(zeros(T,(ndof,))) + xs = [x] + cf = return_cache(f,xs) + r, cf, xs +end + +function evaluate!(cache,f::ChebyshevPolynomialBasis{D,T},x::Point) where {D,T} + 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,ChebyshevPolynomialBasis{D,V}}, x::Point) where {N,D,V} + 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,ChebyshevPolynomialBasis{D,V}}, x::Point) where {N,D,V} + 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 + +function _evaluate_1d_ch_T!(v::AbstractMatrix{T},x,order,d) where T + n = order + 1 + o = one(T) + @inbounds v[d,1] = o + if n > 1 + ξ = (2*x[d] - 1) # ξ ∈ [-1,1] + ξ2 = 2*ξ + v[d,2] = ξ + for i in 3:n + @inbounds v[d,i] = v[d,i-1]*ξ2 - v[d,i-2] + end + end +end + +function _evaluate_1d_ch_U!(v::AbstractMatrix{T},x,order,d) where T + n = order + 1 + o = one(T) + @inbounds v[d,1] = o + if n > 1 + ξ = (2*x[d] - 1) # ξ ∈ [-1,1] + ξ2 = 2*ξ + v[d,2] = ξ2 + for i in 3:n + @inbounds v[d,i] = v[d,i-1]*ξ2 - v[d,i-2] + end + end +end + +function _gradient_1d_ch_T!(v::AbstractMatrix{T},x,order,d) where T + n = order + 1 + z = zero(T) + o = one(T) + dξdx = T(2.0) + @inbounds v[d,1] = z # dT_0 = 0 + if n > 1 + ξ = T(2*x[d] - 1) + @inbounds v[d,2] = dξdx*o # dT_1 = 1*U_0 = 1 + unm1 = o + un = 2*ξ + for i in 3:n + @inbounds v[d,i] = dξdx*(i-1)*un # dT_i = i*U_{i-1} + un, unm1 = 2*ξ*un - unm1, un + end + end +end + +function _evaluate_nd_ch!( + v::AbstractVector{V}, + x, + orders, + terms::AbstractVector{CartesianIndex{D}}, + c::AbstractMatrix{T}) where {V,T,D} + + dim = D + for d in 1:dim + _evaluate_1d_ch_T!(c,x,orders[d],d) + end + + o = one(T) + k = 1 + + for ci in terms + + s = o + for d in 1:dim + @inbounds s *= c[d,ci[d]] + end + + k = _set_value!(v,s,k) + + end + +end + +function _gradient_nd_ch!( + v::AbstractVector{G}, + x, + orders, + terms::AbstractVector{CartesianIndex{D}}, + c::AbstractMatrix{T}, + g::AbstractMatrix{T}, + ::Type{V}) where {G,T,D,V} + + dim = D + for d in 1:dim + _evaluate_1d_ch_T!(c,x,orders[d],d) + _gradient_1d_ch_T!(g,x,orders[d],d) + end + + z = zero(Mutable(VectorValue{D,T})) + o = one(T) + k = 1 + + for ci in terms + + 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!(v,s,k,V) + + end + +end + +############################################################################################ + +""" + struct QGradChebyshevPolynomialBasis{...} <: AbstractVector{Monomial} + +This type implements a multivariate vector-valued polynomial basis +spanning the space needed for Nedelec reference elements on n-cubes. +The type parameters and fields of this `struct` are not public. +This type fully implements the [`Field`](@ref) interface, with up to first order +derivatives. +""" +struct QGradChebyshevPolynomialBasis{D,T} <: AbstractVector{ChebyshevPolynomial} + order::Int + terms::CartesianIndices{D} + perms::Matrix{Int} + function QGradChebyshevPolynomialBasis(::Type{T},order::Int,terms::CartesianIndices{D},perms::Matrix{Int}) where {D,T} + new{D,T}(order,terms,perms) + end +end + +Base.size(a::QGradChebyshevPolynomialBasis) = (_ndofs_qgrad_ch(a),) +Base.getindex(a::QGradChebyshevPolynomialBasis,i::Integer) = ChebyshevPolynomial() +Base.IndexStyle(::QGradChebyshevPolynomialBasis) = IndexLinear() + +""" + QGradChebyshevPolynomialBasis{D}(::Type{T},order::Int) where {D,T} + +Returns a `QGradChebyshevPolynomialBasis` object. `D` is the dimension +of the coordinate space and `T` is the type of the components in the vector-value. +The `order` argument has the following meaning: the curl of the functions in this basis +is in the Q space of degree `order`. +""" +function QGradChebyshevPolynomialBasis{D}(::Type{T},order::Int) where {D,T} + @check T<:Real "T needs to be <:Real since represents the type of the components of the vector value" + _order = order + 1 + _t = tfill(_order+1,Val{D-1}()) + t = (_order,_t...) + terms = CartesianIndices(t) + perms = _prepare_perms(D) + QGradChebyshevPolynomialBasis(T,order,terms,perms) +end + +""" + num_terms(f::QGradChebyshevPolynomialBasis{D,T}) where {D,T} +""" +num_terms(f::QGradChebyshevPolynomialBasis{D,T}) where {D,T} = length(f.terms)*D + +get_order(f::QGradChebyshevPolynomialBasis) = f.order + +function return_cache(f::QGradChebyshevPolynomialBasis{D,T},x::AbstractVector{<:Point}) where {D,T} + @check D == length(eltype(x)) "Incorrect number of point components" + np = length(x) + ndof = _ndofs_qgrad_ch(f) + n = 1 + f.order+1 + V = VectorValue{D,T} + r = CachedArray(zeros(V,(np,ndof))) + v = CachedArray(zeros(V,(ndof,))) + c = CachedArray(zeros(T,(D,n))) + (r, v, c) +end + +function evaluate!(cache,f::QGradChebyshevPolynomialBasis{D,T},x::AbstractVector{<:Point}) where {D,T} + r, v, c = cache + np = length(x) + ndof = _ndofs_qgrad_ch(f) + n = 1 + f.order+1 + setsize!(r,(np,ndof)) + setsize!(v,(ndof,)) + setsize!(c,(D,n)) + for i in 1:np + @inbounds xi = x[i] + _evaluate_nd_qgrad_ch!(v,xi,f.order+1,f.terms,f.perms,c) + for j in 1:ndof + @inbounds r[i,j] = v[j] + end + end + r.array +end + +function return_cache( + fg::FieldGradientArray{1,QGradChebyshevPolynomialBasis{D,T}}, + x::AbstractVector{<:Point}) where {D,T} + + f = fg.fa + @check D == length(eltype(x)) "Incorrect number of point components" + np = length(x) + ndof = _ndofs_qgrad_ch(f) + n = 1 + f.order+1 + xi = testitem(x) + V = VectorValue{D,T} + G = gradient_type(V,xi) + r = CachedArray(zeros(G,(np,ndof))) + v = CachedArray(zeros(G,(ndof,))) + c = CachedArray(zeros(T,(D,n))) + g = CachedArray(zeros(T,(D,n))) + (r, v, c, g) +end + +function evaluate!( + cache, + fg::FieldGradientArray{1,QGradChebyshevPolynomialBasis{D,T}}, + x::AbstractVector{<:Point}) where {D,T} + + f = fg.fa + r, v, c, g = cache + np = length(x) + ndof = _ndofs_qgrad_ch(f) + n = 1 + f.order+1 + setsize!(r,(np,ndof)) + setsize!(v,(ndof,)) + setsize!(c,(D,n)) + setsize!(g,(D,n)) + V = VectorValue{D,T} + for i in 1:np + @inbounds xi = x[i] + _gradient_nd_qgrad_ch!(v,xi,f.order+1,f.terms,f.perms,c,g,V) + for j in 1:ndof + @inbounds r[i,j] = v[j] + end + end + r.array +end + +# Helpers + +_ndofs_qgrad_ch(f::QGradChebyshevPolynomialBasis{D}) where D = D*(length(f.terms)) + +function _evaluate_nd_qgrad_ch!( + v::AbstractVector{V}, + x, + order, + terms::CartesianIndices{D}, + perms::Matrix{Int}, + c::AbstractMatrix{T}) where {V,T,D} + + dim = D + for d in 1:dim + _evaluate_1d_ch_T!(c,x,order,d) + end + + o = one(T) + k = 1 + m = zero(Mutable(V)) + js = eachindex(m) + z = zero(T) + + for ci in terms + + for j in js + + @inbounds for i in js + m[i] = z + end + + s = o + @inbounds for d in 1:dim + s *= c[d,ci[perms[d,j]]] + end + + m[j] = s + v[k] = m + k += 1 + + end + + end + +end + +function _gradient_nd_qgrad_ch!( + v::AbstractVector{G}, + x, + order, + terms::CartesianIndices{D}, + perms::Matrix{Int}, + c::AbstractMatrix{T}, + g::AbstractMatrix{T}, + ::Type{V}) where {G,T,D,V} + + dim = D + for d in 1:dim + _evaluate_1d_ch_T!(c,x,order,d) + _gradient_1d_ch_T!(g,x,order,d) + end + + z = zero(Mutable(V)) + m = zero(Mutable(G)) + js = eachindex(z) + mjs = eachindex(m) + o = one(T) + zi = zero(T) + k = 1 + + for ci in terms + + for j in js + + s = z + for i in js + s[i] = o + end + + for q in 1:dim + for d in 1:dim + if d != q + @inbounds s[q] *= c[d,ci[perms[d,j]]] + else + @inbounds s[q] *= g[d,ci[perms[d,j]]] + end + end + end + + @inbounds for i in mjs + m[i] = zi + end + + for i in js + @inbounds m[i,j] = s[i] + end + @inbounds v[k] = m + k += 1 + + end + + end + +end + +############################################################################################ + +""" + struct QCurlGradChebyshevPolynomialBasis{...} <: AbstractArray{Monomial} + +This type implements a multivariate vector-valued polynomial basis +spanning the space needed for Raviart-Thomas reference elements on n-cubes. +The type parameters and fields of this `struct` are not public. +This type fully implements the [`Field`](@ref) interface, with up to first order +derivatives. +""" +struct QCurlGradChebyshevPolynomialBasis{D,T} <: AbstractVector{ChebyshevPolynomial} + qgrad::QGradChebyshevPolynomialBasis{D,T} + function QCurlGradChebyshevPolynomialBasis(::Type{T},order::Int,terms::CartesianIndices{D},perms::Matrix{Int}) where {D,T} + qgrad = QGradChebyshevPolynomialBasis(T,order,terms,perms) + new{D,T}(qgrad) + end +end + +Base.size(a::QCurlGradChebyshevPolynomialBasis) = (length(a.qgrad),) +Base.getindex(a::QCurlGradChebyshevPolynomialBasis,i::Integer) = ChebyshevPolynomial() +Base.IndexStyle(::QCurlGradChebyshevPolynomialBasis) = IndexLinear() + +""" + QCurlGradChebyshevPolynomialBasis{D}(::Type{T},order::Int) where {D,T} + +Returns a `QCurlGradChebyshevPolynomialBasis` object. `D` is the dimension +of the coordinate space and `T` is the type of the components in the vector-value. +The `order` argument has the following meaning: the divergence of the functions in this basis +is in the Q space of degree `order`. +""" +function QCurlGradChebyshevPolynomialBasis{D}(::Type{T},order::Int) where {D,T} + @check T<:Real "T needs to be <:Real since represents the type of the components of the vector value" + _order = order+1 + _t = tfill(_order,Val{D-1}()) + t = (_order+1,_t...) + terms = CartesianIndices(t) + perms = _prepare_perms(D) + QCurlGradChebyshevPolynomialBasis(T,order,terms,perms) +end + +return_type(::QCurlGradChebyshevPolynomialBasis{D,T}) where {D,T} = T + +function return_cache(f::QCurlGradChebyshevPolynomialBasis,x::AbstractVector{<:Point}) + return_cache(f.qgrad,x) +end + +function evaluate!(cache,f::QCurlGradChebyshevPolynomialBasis,x::AbstractVector{<:Point}) + evaluate!(cache,f.qgrad,x) +end + +function return_cache( + fg::FieldGradientArray{N,<:QCurlGradChebyshevPolynomialBasis}, + x::AbstractVector{<:Point}) where N + + f = fg.fa + return_cache(FieldGradientArray{N}(f.qgrad),x) +end + +function evaluate!( + cache, + fg::FieldGradientArray{N,<:QCurlGradChebyshevPolynomialBasis}, + x::AbstractVector{<:Point}) where N + + f = fg.fa + evaluate!(cache,FieldGradientArray{N}(f.qgrad),x) +end + +""" + num_terms(f::QCurlGradChebyshevPolynomialBasis{D,T}) where {D,T} +""" +num_terms(f::QCurlGradChebyshevPolynomialBasis{D,T}) where {D,T} = length(f.qgrad.terms)*D + +get_order(f::QCurlGradChebyshevPolynomialBasis{D,T}) where {D,T} = get_order(f.qgrad) + diff --git a/src/Polynomials/Polynomials.jl b/src/Polynomials/Polynomials.jl index 826e6f07f..a9a35bc1e 100644 --- a/src/Polynomials/Polynomials.jl +++ b/src/Polynomials/Polynomials.jl @@ -26,6 +26,11 @@ export QCurlGradMonomialBasis export PCurlGradMonomialBasis export ModalC0Basis export JacobiPolynomialBasis +export QGradJacobiPolynomialBasis +export QCurlGradJacobiPolynomialBasis +export ChebyshevPolynomialBasis +export QGradChebyshevPolynomialBasis +export QCurlGradChebyshevPolynomialBasis export get_exponents export get_order @@ -44,4 +49,8 @@ include("ModalC0Bases.jl") include("JacobiPolynomialBases.jl") +include("QGradJacobiPolynomialBases.jl") + +include("ChebyshevPolynomialBases.jl") + end # module diff --git a/src/Polynomials/QGradJacobiPolynomialBases.jl b/src/Polynomials/QGradJacobiPolynomialBases.jl new file mode 100644 index 000000000..eef6aad49 --- /dev/null +++ b/src/Polynomials/QGradJacobiPolynomialBases.jl @@ -0,0 +1,303 @@ + +""" + struct QGradJacobiPolynomialBasis{...} <: AbstractVector{Monomial} + +This type implements a multivariate vector-valued polynomial basis +spanning the space needed for Nedelec reference elements on n-cubes. +The type parameters and fields of this `struct` are not public. +This type fully implements the [`Field`](@ref) interface, with up to first order +derivatives. +""" +struct QGradJacobiPolynomialBasis{D,T} <: AbstractVector{JacobiPolynomial} + order::Int + terms::CartesianIndices{D} + perms::Matrix{Int} + function QGradJacobiPolynomialBasis(::Type{T},order::Int,terms::CartesianIndices{D},perms::Matrix{Int}) where {D,T} + new{D,T}(order,terms,perms) + end +end + +Base.size(a::QGradJacobiPolynomialBasis) = (_ndofs_qgrad_jp(a),) +Base.getindex(a::QGradJacobiPolynomialBasis,i::Integer) = JacobiPolynomial() +Base.IndexStyle(::QGradJacobiPolynomialBasis) = IndexLinear() + +""" + QGradJacobiPolynomialBasis{D}(::Type{T},order::Int) where {D,T} + +Returns a `QGradJacobiPolynomialBasis` object. `D` is the dimension +of the coordinate space and `T` is the type of the components in the vector-value. +The `order` argument has the following meaning: the curl of the functions in this basis +is in the Q space of degree `order`. +""" +function QGradJacobiPolynomialBasis{D}(::Type{T},order::Int) where {D,T} + @check T<:Real "T needs to be <:Real since represents the type of the components of the vector value" + _order = order + 1 + _t = tfill(_order+1,Val{D-1}()) + t = (_order,_t...) + terms = CartesianIndices(t) + perms = _prepare_perms(D) + QGradJacobiPolynomialBasis(T,order,terms,perms) +end + +""" + num_terms(f::QGradJacobiPolynomialBasis{D,T}) where {D,T} +""" +num_terms(f::QGradJacobiPolynomialBasis{D,T}) where {D,T} = length(f.terms)*D + +get_order(f::QGradJacobiPolynomialBasis) = f.order + +function return_cache(f::QGradJacobiPolynomialBasis{D,T},x::AbstractVector{<:Point}) where {D,T} + @check D == length(eltype(x)) "Incorrect number of point components" + np = length(x) + ndof = _ndofs_qgrad_jp(f) + n = 1 + f.order+1 + V = VectorValue{D,T} + r = CachedArray(zeros(V,(np,ndof))) + v = CachedArray(zeros(V,(ndof,))) + c = CachedArray(zeros(T,(D,n))) + (r, v, c) +end + +function evaluate!(cache,f::QGradJacobiPolynomialBasis{D,T},x::AbstractVector{<:Point}) where {D,T} + r, v, c = cache + np = length(x) + ndof = _ndofs_qgrad_jp(f) + n = 1 + f.order+1 + setsize!(r,(np,ndof)) + setsize!(v,(ndof,)) + setsize!(c,(D,n)) + for i in 1:np + @inbounds xi = x[i] + _evaluate_nd_qgrad_jp!(v,xi,f.order+1,f.terms,f.perms,c) + for j in 1:ndof + @inbounds r[i,j] = v[j] + end + end + r.array +end + +function return_cache( + fg::FieldGradientArray{1,QGradJacobiPolynomialBasis{D,T}}, + x::AbstractVector{<:Point}) where {D,T} + + f = fg.fa + @check D == length(eltype(x)) "Incorrect number of point components" + np = length(x) + ndof = _ndofs_qgrad_jp(f) + n = 1 + f.order+1 + xi = testitem(x) + V = VectorValue{D,T} + G = gradient_type(V,xi) + r = CachedArray(zeros(G,(np,ndof))) + v = CachedArray(zeros(G,(ndof,))) + c = CachedArray(zeros(T,(D,n))) + g = CachedArray(zeros(T,(D,n))) + (r, v, c, g) +end + +function evaluate!( + cache, + fg::FieldGradientArray{1,QGradJacobiPolynomialBasis{D,T}}, + x::AbstractVector{<:Point}) where {D,T} + + f = fg.fa + r, v, c, g = cache + np = length(x) + ndof = _ndofs_qgrad_jp(f) + n = 1 + f.order+1 + setsize!(r,(np,ndof)) + setsize!(v,(ndof,)) + setsize!(c,(D,n)) + setsize!(g,(D,n)) + V = VectorValue{D,T} + for i in 1:np + @inbounds xi = x[i] + _gradient_nd_qgrad_jp!(v,xi,f.order+1,f.terms,f.perms,c,g,V) + for j in 1:ndof + @inbounds r[i,j] = v[j] + end + end + r.array +end + +# Helpers + +_ndofs_qgrad_jp(f::QGradJacobiPolynomialBasis{D}) where D = D*(length(f.terms)) + +function _evaluate_nd_qgrad_jp!( + v::AbstractVector{V}, + x, + order, + terms::CartesianIndices{D}, + perms::Matrix{Int}, + c::AbstractMatrix{T}) where {V,T,D} + + dim = D + for d in 1:dim + _evaluate_1d_jp!(c,x,order,d) + end + + o = one(T) + k = 1 + m = zero(Mutable(V)) + js = eachindex(m) + z = zero(T) + + for ci in terms + + for j in js + + @inbounds for i in js + m[i] = z + end + + s = o + @inbounds for d in 1:dim + s *= c[d,ci[perms[d,j]]] + end + + m[j] = s + v[k] = m + k += 1 + + end + + end + +end + +function _gradient_nd_qgrad_jp!( + v::AbstractVector{G}, + x, + order, + terms::CartesianIndices{D}, + perms::Matrix{Int}, + c::AbstractMatrix{T}, + g::AbstractMatrix{T}, + ::Type{V}) where {G,T,D,V} + + dim = D + for d in 1:dim + _evaluate_1d_jp!(c,x,order,d) + _gradient_1d_jp!(g,x,order,d) + end + + z = zero(Mutable(V)) + m = zero(Mutable(G)) + js = eachindex(z) + mjs = eachindex(m) + o = one(T) + zi = zero(T) + k = 1 + + for ci in terms + + for j in js + + s = z + for i in js + s[i] = o + end + + for q in 1:dim + for d in 1:dim + if d != q + @inbounds s[q] *= c[d,ci[perms[d,j]]] + else + @inbounds s[q] *= g[d,ci[perms[d,j]]] + end + end + end + + @inbounds for i in mjs + m[i] = zi + end + + for i in js + @inbounds m[i,j] = s[i] + end + @inbounds v[k] = m + k += 1 + + end + + end + +end + +############################################################################################ + +""" + struct QCurlGradJacobiPolynomialBasis{...} <: AbstractArray{Monomial} + +This type implements a multivariate vector-valued polynomial basis +spanning the space needed for Raviart-Thomas reference elements on n-cubes. +The type parameters and fields of this `struct` are not public. +This type fully implements the [`Field`](@ref) interface, with up to first order +derivatives. +""" +struct QCurlGradJacobiPolynomialBasis{D,T} <: AbstractVector{JacobiPolynomial} + qgrad::QGradJacobiPolynomialBasis{D,T} + function QCurlGradJacobiPolynomialBasis(::Type{T},order::Int,terms::CartesianIndices{D},perms::Matrix{Int}) where {D,T} + qgrad = QGradJacobiPolynomialBasis(T,order,terms,perms) + new{D,T}(qgrad) + end +end + +Base.size(a::QCurlGradJacobiPolynomialBasis) = (length(a.qgrad),) +Base.getindex(a::QCurlGradJacobiPolynomialBasis,i::Integer) = JacobiPolynomial() +Base.IndexStyle(::QCurlGradJacobiPolynomialBasis) = IndexLinear() + +""" + QCurlGradJacobiPolynomialBasis{D}(::Type{T},order::Int) where {D,T} + +Returns a `QCurlGradJacobiPolynomialBasis` object. `D` is the dimension +of the coordinate space and `T` is the type of the components in the vector-value. +The `order` argument has the following meaning: the divergence of the functions in this basis +is in the Q space of degree `order`. +""" +function QCurlGradJacobiPolynomialBasis{D}(::Type{T},order::Int) where {D,T} + @check T<:Real "T needs to be <:Real since represents the type of the components of the vector value" + _order = order+1 + _t = tfill(_order,Val{D-1}()) + t = (_order+1,_t...) + terms = CartesianIndices(t) + perms = _prepare_perms(D) + QCurlGradJacobiPolynomialBasis(T,order,terms,perms) +end + +return_type(::QCurlGradJacobiPolynomialBasis{D,T}) where {D,T} = T + +function return_cache(f::QCurlGradJacobiPolynomialBasis,x::AbstractVector{<:Point}) + return_cache(f.qgrad,x) +end + +function evaluate!(cache,f::QCurlGradJacobiPolynomialBasis,x::AbstractVector{<:Point}) + evaluate!(cache,f.qgrad,x) +end + +function return_cache( + fg::FieldGradientArray{N,<:QCurlGradJacobiPolynomialBasis}, + x::AbstractVector{<:Point}) where N + + f = fg.fa + return_cache(FieldGradientArray{N}(f.qgrad),x) +end + +function evaluate!( + cache, + fg::FieldGradientArray{N,<:QCurlGradJacobiPolynomialBasis}, + x::AbstractVector{<:Point}) where N + + f = fg.fa + evaluate!(cache,FieldGradientArray{N}(f.qgrad),x) +end + +""" + num_terms(f::QCurlGradJacobiPolynomialBasis{D,T}) where {D,T} +""" +num_terms(f::QCurlGradJacobiPolynomialBasis{D,T}) where {D,T} = length(f.qgrad.terms)*D + +get_order(f::QCurlGradJacobiPolynomialBasis{D,T}) where {D,T} = get_order(f.qgrad) + +############################################################################################ diff --git a/src/ReferenceFEs/RaviartThomasRefFEs.jl b/src/ReferenceFEs/RaviartThomasRefFEs.jl index f6990f47f..954544911 100644 --- a/src/ReferenceFEs/RaviartThomasRefFEs.jl +++ b/src/ReferenceFEs/RaviartThomasRefFEs.jl @@ -13,14 +13,21 @@ The `order` argument has the following meaning: the divergence of the functions is in the Q space of degree `order`. """ -function RaviartThomasRefFE(::Type{et},p::Polytope,order::Integer) where et +function RaviartThomasRefFE( + ::Type{et},p::Polytope,order::Integer;basis_type=:monomial +) where et + @assert basis_type ∈ (:monomial, :jacobi, :chebyshev) D = num_dims(p) - if is_n_cube(p) + if is_n_cube(p) && basis_type == :monomial prebasis = QCurlGradMonomialBasis{D}(et,order) - elseif is_simplex(p) + elseif is_simplex(p) && basis_type == :monomial prebasis = PCurlGradMonomialBasis{D}(et,order) + elseif is_n_cube(p) && basis_type == :jacobi + prebasis = QCurlGradJacobiPolynomialBasis{D}(et,order) + elseif is_n_cube(p) && basis_type == :chebyshev + prebasis = QCurlGradChebyshevPolynomialBasis{D}(et,order) else @notimplemented "H(div) Reference FE only available for cubes and simplices" end @@ -49,12 +56,12 @@ function RaviartThomasRefFE(::Type{et},p::Polytope,order::Integer) where et reffe end -function ReferenceFE(p::Polytope,::RaviartThomas, order) - RaviartThomasRefFE(Float64,p,order) +function ReferenceFE(p::Polytope,::RaviartThomas,order;basis_type=:monomial) + RaviartThomasRefFE(Float64,p,order;basis_type) end -function ReferenceFE(p::Polytope,::RaviartThomas,::Type{T}, order) where T - RaviartThomasRefFE(T,p,order) +function ReferenceFE(p::Polytope,::RaviartThomas,::Type{T},order;basis_type=:monomial) where T + RaviartThomasRefFE(T,p,order;basis_type) end function Conformity(reffe::GenericRefFE{RaviartThomas},sym::Symbol) @@ -187,13 +194,41 @@ function _RT_face_values(p,et,order,phi) # Moments (fmoments) # The RT prebasis is expressed in terms of shape function - fshfs = MonomialBasis(et,fp,order) + #fshfs = MonomialBasis(et,fp,order) + fshfs = JacobiBasis(et,fp,order) + #fshfs = ChebyshevBasis(et,fp,order) + #fshfs = get_shapefuns(LagrangianRefFE(et,fp,order)) # Face moments, i.e., M(Fi)_{ab} = q_RF^a(xgp_RFi^b) w_Fi^b n_Fi ⋅ () fmoments = _RT_face_moments(p, fshfs, c_fips, fcips, fwips, phi) return fcips, fmoments +end +function JacobiBasis(::Type{T},p::Polytope,orders) where T + compute_jacobi_basis(T,p,orders) +end +function JacobiBasis(::Type{T},p::Polytope{D},order::Int) where {D,T} + orders = tfill(order,Val{D}()) + JacobiBasis(T,p,orders) +end +function compute_jacobi_basis(::Type{T},p::ExtrusionPolytope{D},orders) where {D,T} + extrusion = Tuple(p.extrusion) + terms = _monomial_terms(extrusion,orders) + JacobiPolynomialBasis{D}(T,orders,terms) +end + +function ChebyshevBasis(::Type{T},p::Polytope,orders) where T + compute_chebyshev_basis(T,p,orders) +end +function ChebyshevBasis(::Type{T},p::Polytope{D},order::Int) where {D,T} + orders = tfill(order,Val{D}()) + ChebyshevBasis(T,p,orders) +end +function compute_chebyshev_basis(::Type{T},p::ExtrusionPolytope{D},orders) where {D,T} + extrusion = Tuple(p.extrusion) + terms = _monomial_terms(extrusion,orders) + ChebyshevPolynomialBasis{D}(T,orders,terms) end function _RT_cell_moments(p, cbasis, ccips, cwips) @@ -214,7 +249,9 @@ function _RT_cell_values(p,et,order,phi) # Cell moments, i.e., M(C)_{ab} = q_C^a(xgp_C^b) w_C^b ⋅ () if is_n_cube(p) - cbasis = QGradMonomialBasis{num_dims(p)}(et,order-1) + #cbasis = QGradMonomialBasis{num_dims(p)}(et,order-1) + cbasis = QGradJacobiPolynomialBasis{num_dims(p)}(et,order-1) + #cbasis = QGradChebyshevPolynomialBasis{num_dims(p)}(et,order-1) elseif is_simplex(p) T = VectorValue{num_dims(p),et} cbasis = MonomialBasis{num_dims(p)}(T,order-1, _p_filter) diff --git a/test/AdaptivityTests/RefinementRulesTests.jl b/test/AdaptivityTests/RefinementRulesTests.jl index 4cc8f63a5..cb7b6cc92 100644 --- a/test/AdaptivityTests/RefinementRulesTests.jl +++ b/test/AdaptivityTests/RefinementRulesTests.jl @@ -33,4 +33,13 @@ Adaptivity.test_refinement_rule(rr_bc3) rr_ps2 = Adaptivity.PowellSabinRefinementRule(TRI) rr_ps3 = Adaptivity.PowellSabinRefinementRule(TET) +rr_bc2 = Adaptivity.BarycentricRefinementRule(QUAD) +Adaptivity.test_refinement_rule(rr_bc2) + +rr_bc3 = Adaptivity.BarycentricRefinementRule(HEX) +Adaptivity.test_refinement_rule(rr_bc3) + +writevtk(rr_bc3.ref_grid,"ref_barycentric";append=false) + + end \ No newline at end of file From 6d8e04e8b2fda6962a94fa98a44e853400d81525 Mon Sep 17 00:00:00 2001 From: JordiManyer Date: Mon, 21 Oct 2024 16:53:22 +1100 Subject: [PATCH 012/105] Minor --- test/AdaptivityTests/RefinementRulesTests.jl | 9 --------- 1 file changed, 9 deletions(-) diff --git a/test/AdaptivityTests/RefinementRulesTests.jl b/test/AdaptivityTests/RefinementRulesTests.jl index cb7b6cc92..4cc8f63a5 100644 --- a/test/AdaptivityTests/RefinementRulesTests.jl +++ b/test/AdaptivityTests/RefinementRulesTests.jl @@ -33,13 +33,4 @@ Adaptivity.test_refinement_rule(rr_bc3) rr_ps2 = Adaptivity.PowellSabinRefinementRule(TRI) rr_ps3 = Adaptivity.PowellSabinRefinementRule(TET) -rr_bc2 = Adaptivity.BarycentricRefinementRule(QUAD) -Adaptivity.test_refinement_rule(rr_bc2) - -rr_bc3 = Adaptivity.BarycentricRefinementRule(HEX) -Adaptivity.test_refinement_rule(rr_bc3) - -writevtk(rr_bc3.ref_grid,"ref_barycentric";append=false) - - end \ No newline at end of file From 47861317134fb498f77b2b95cc3beef5fe22600c Mon Sep 17 00:00:00 2001 From: JordiManyer Date: Tue, 22 Oct 2024 15:58:33 +1100 Subject: [PATCH 013/105] Added new phi kwarg for RT reffe --- src/ReferenceFEs/RaviartThomasRefFEs.jl | 28 ++++++++++++++++++++++--- 1 file changed, 25 insertions(+), 3 deletions(-) diff --git a/src/ReferenceFEs/RaviartThomasRefFEs.jl b/src/ReferenceFEs/RaviartThomasRefFEs.jl index 954544911..1e42b5fa5 100644 --- a/src/ReferenceFEs/RaviartThomasRefFEs.jl +++ b/src/ReferenceFEs/RaviartThomasRefFEs.jl @@ -14,7 +14,7 @@ is in the Q space of degree `order`. """ function RaviartThomasRefFE( - ::Type{et},p::Polytope,order::Integer;basis_type=:monomial + ::Type{et},p::Polytope,order::Integer;basis_type=:monomial,phi=GenericField(identity) ) where et @assert basis_type ∈ (:monomial, :jacobi, :chebyshev) @@ -32,7 +32,7 @@ function RaviartThomasRefFE( @notimplemented "H(div) Reference FE only available for cubes and simplices" end - nf_nodes, nf_moments = _RT_nodes_and_moments(et,p,order,GenericField(identity)) + nf_nodes, nf_moments = _RT_nodes_and_moments(et,p,order,phi) face_own_dofs = _face_own_dofs_from_moments(nf_moments) @@ -146,7 +146,7 @@ function _broadcast(::Type{T},n,b) where T return c end -function _RT_face_moments(p, fshfs, c_fips, fcips, fwips,phi) +function _RT_face_moments(p, fshfs, c_fips, fcips, fwips, phi) nc = length(c_fips) cfshfs = fill(fshfs, nc) cvals = lazy_map(evaluate,cfshfs,c_fips) @@ -231,6 +231,27 @@ function compute_chebyshev_basis(::Type{T},p::ExtrusionPolytope{D},orders) where ChebyshevPolynomialBasis{D}(T,orders,terms) end +function compute_lagrangian_rt_basis(D,et,order) + P = get_dof_basis(LagrangianRefFE(et,SEGMENT,order)).nodes + DP = get_dof_basis(LagrangianRefFE(et,SEGMENT,order-1)).nodes + nodes1d = map(I -> (I[1] == I[2]) ? P : DP, CartesianIndices((D,D))) + + S = Tuple(map(k -> Tuple(map(length,nodes1d[k,:])),1:D)) + nnodes = sum(map(prod,S)) + nodes = Vector{VectorValue{D,et}}(undef,nnodes) + k = 1 + for d in 1:D + Sk = S[d] + for I in CartesianIndices(Sk) + nodes[k] = VectorValue(map(l -> nodes1d[d,l][I[l]].data[1], 1:D)) + k += 1 + end + end + + dof_to_comp = vcat(map(k -> fill(k,prod(S[k])), 1:D)...) + dof_to_node = Base.OneTo(nnodes) +end + function _RT_cell_moments(p, cbasis, ccips, cwips) # Interior DOFs-related basis evaluated at interior integration points ishfs_iips = evaluate(cbasis,ccips) @@ -252,6 +273,7 @@ function _RT_cell_values(p,et,order,phi) #cbasis = QGradMonomialBasis{num_dims(p)}(et,order-1) cbasis = QGradJacobiPolynomialBasis{num_dims(p)}(et,order-1) #cbasis = QGradChebyshevPolynomialBasis{num_dims(p)}(et,order-1) + #cbasis = get_shapefuns(RaviartThomasRefFE(et,p,order-1)) elseif is_simplex(p) T = VectorValue{num_dims(p),et} cbasis = MonomialBasis{num_dims(p)}(T,order-1, _p_filter) From 99f49194df07cf6e7bd4c27ceee5267e1234541f Mon Sep 17 00:00:00 2001 From: JordiManyer Date: Wed, 23 Oct 2024 14:40:18 +1100 Subject: [PATCH 014/105] Minor --- src/ReferenceFEs/RaviartThomasRefFEs.jl | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/ReferenceFEs/RaviartThomasRefFEs.jl b/src/ReferenceFEs/RaviartThomasRefFEs.jl index 1e42b5fa5..45b2d6728 100644 --- a/src/ReferenceFEs/RaviartThomasRefFEs.jl +++ b/src/ReferenceFEs/RaviartThomasRefFEs.jl @@ -56,12 +56,12 @@ function RaviartThomasRefFE( reffe end -function ReferenceFE(p::Polytope,::RaviartThomas,order;basis_type=:monomial) - RaviartThomasRefFE(Float64,p,order;basis_type) +function ReferenceFE(p::Polytope,::RaviartThomas,order;kwargs...) + RaviartThomasRefFE(Float64,p,order;kwargs...) end -function ReferenceFE(p::Polytope,::RaviartThomas,::Type{T},order;basis_type=:monomial) where T - RaviartThomasRefFE(T,p,order;basis_type) +function ReferenceFE(p::Polytope,::RaviartThomas,::Type{T},order;kwargs...) where T + RaviartThomasRefFE(T,p,order;kwargs...) end function Conformity(reffe::GenericRefFE{RaviartThomas},sym::Symbol) From b970d36998b764a0b82c41970f4f894f7c3ad022 Mon Sep 17 00:00:00 2001 From: JordiManyer Date: Mon, 28 Oct 2024 15:35:26 +1100 Subject: [PATCH 015/105] Minor --- src/FESpaces/DivConformingFESpaces.jl | 5 ++--- src/ReferenceFEs/RaviartThomasRefFEs.jl | 22 ---------------------- 2 files changed, 2 insertions(+), 25 deletions(-) diff --git a/src/FESpaces/DivConformingFESpaces.jl b/src/FESpaces/DivConformingFESpaces.jl index bcc6c3a42..db08c8ba2 100644 --- a/src/FESpaces/DivConformingFESpaces.jl +++ b/src/FESpaces/DivConformingFESpaces.jl @@ -135,8 +135,7 @@ function return_cache( db = MomentBasedDofBasis(nodes,nf_moments,nf_nodes) face_moments = [ similar(i,VectorValue{Dp,et}) for i in nf_moments ] - cache = (db.nodes, db.face_nodes, nf_moments, face_moments) - cache + return db.nodes, db.face_nodes, nf_moments, face_moments end function evaluate!( @@ -147,7 +146,7 @@ function evaluate!( sign_flip::AbstractVector{Bool} ) nodes, nf_nodes, nf_moments, face_moments = cache - face_own_dofs=get_face_own_dofs(reffe) + face_own_dofs = get_face_own_dofs(reffe) for face in 1:length(face_moments) nf_moments_face = nf_moments[face] face_moments_face = face_moments[face] diff --git a/src/ReferenceFEs/RaviartThomasRefFEs.jl b/src/ReferenceFEs/RaviartThomasRefFEs.jl index 45b2d6728..9443480e6 100644 --- a/src/ReferenceFEs/RaviartThomasRefFEs.jl +++ b/src/ReferenceFEs/RaviartThomasRefFEs.jl @@ -164,7 +164,6 @@ function _RT_face_moments(p, fshfs, c_fips, fcips, fwips, phi) change_ips = lazy_map(evaluate,change,fcips) cvals = [ _broadcast(typeof(n),n,J.*b) for (n,b,J) in zip(fns,cvals,change_ips)] - return cvals end @@ -231,27 +230,6 @@ function compute_chebyshev_basis(::Type{T},p::ExtrusionPolytope{D},orders) where ChebyshevPolynomialBasis{D}(T,orders,terms) end -function compute_lagrangian_rt_basis(D,et,order) - P = get_dof_basis(LagrangianRefFE(et,SEGMENT,order)).nodes - DP = get_dof_basis(LagrangianRefFE(et,SEGMENT,order-1)).nodes - nodes1d = map(I -> (I[1] == I[2]) ? P : DP, CartesianIndices((D,D))) - - S = Tuple(map(k -> Tuple(map(length,nodes1d[k,:])),1:D)) - nnodes = sum(map(prod,S)) - nodes = Vector{VectorValue{D,et}}(undef,nnodes) - k = 1 - for d in 1:D - Sk = S[d] - for I in CartesianIndices(Sk) - nodes[k] = VectorValue(map(l -> nodes1d[d,l][I[l]].data[1], 1:D)) - k += 1 - end - end - - dof_to_comp = vcat(map(k -> fill(k,prod(S[k])), 1:D)...) - dof_to_node = Base.OneTo(nnodes) -end - function _RT_cell_moments(p, cbasis, ccips, cwips) # Interior DOFs-related basis evaluated at interior integration points ishfs_iips = evaluate(cbasis,ccips) From 79edf5b6583020ad27e9e0d9b6a1be949ea4c36c Mon Sep 17 00:00:00 2001 From: JordiManyer Date: Wed, 30 Oct 2024 16:48:57 +1100 Subject: [PATCH 016/105] Minor fix in JacobiPolymials that did now allow interpolation --- src/Polynomials/JacobiPolynomialBases.jl | 12 +++++------ src/ReferenceFEs/MomentBasedReferenceFEs.jl | 23 +++++++++++++++++++++ 2 files changed, 29 insertions(+), 6 deletions(-) create mode 100644 src/ReferenceFEs/MomentBasedReferenceFEs.jl diff --git a/src/Polynomials/JacobiPolynomialBases.jl b/src/Polynomials/JacobiPolynomialBases.jl index 2b615d9c3..ad35f8154 100644 --- a/src/Polynomials/JacobiPolynomialBases.jl +++ b/src/Polynomials/JacobiPolynomialBases.jl @@ -163,15 +163,15 @@ end # Optimizing evaluation at a single point -function return_cache(f::JacobiPolynomialBasis{D,T},x::Point) where {D,T} - ndof = length(f.terms)*num_components(T) - r = CachedArray(zeros(T,(ndof,))) +function return_cache(f::AbstractVector{JacobiPolynomial},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::JacobiPolynomialBasis{D,T},x::Point) where {D,T} +function evaluate!(cache,f::AbstractVector{JacobiPolynomial},x::Point) r, cf, xs = cache xs[1] = x v = evaluate!(cf,f,xs) @@ -183,7 +183,7 @@ function evaluate!(cache,f::JacobiPolynomialBasis{D,T},x::Point) where {D,T} end function return_cache( - f::FieldGradientArray{N,JacobiPolynomialBasis{D,V}}, x::Point) where {N,D,V} + f::FieldGradientArray{N,<:AbstractVector{JacobiPolynomial}}, x::Point) where {N} xs = [x] cf = return_cache(f,xs) v = evaluate!(cf,f,xs) @@ -192,7 +192,7 @@ function return_cache( end function evaluate!( - cache, f::FieldGradientArray{N,JacobiPolynomialBasis{D,V}}, x::Point) where {N,D,V} + cache, f::FieldGradientArray{N,<:AbstractVector{JacobiPolynomial}}, x::Point) where {N} r, cf, xs = cache xs[1] = x v = evaluate!(cf,f,xs) diff --git a/src/ReferenceFEs/MomentBasedReferenceFEs.jl b/src/ReferenceFEs/MomentBasedReferenceFEs.jl new file mode 100644 index 000000000..56aac999e --- /dev/null +++ b/src/ReferenceFEs/MomentBasedReferenceFEs.jl @@ -0,0 +1,23 @@ + +""" +For each moment, I need: + - its dimension + - the basis we are integrating against + - a function (φ,ϕ) -> the functional to integrate + - a face filter/mask +""" +function MomentBasedReferenceFE( + p::Polytope{D}, + face_moments::AbstractVector{<:AbstractVector{<:Function}}, + face_basis::AbstractVector{<:AbstractVector{<:AbstractVector{<:Field}}}; + face_mask::AbstractVector{<:AbstractVector{Bool}} = [fill(true,num_faces(p, d)) for d in 0:D], + phi = GenericField(identity) +) where D + @assert length(face_moments) == length(face_mask) == length(face_basis) == D+1 + @assert all(length(face_moments[d+1]) == length(face_basis[d+1]) for d in 0:D) + @assert all(length(face_mask[d+1]) == num_faces(p, d) for d in 0:D) + +end + + + From 57c9897a2978227705c5bc0f0228f2afe3e3859d Mon Sep 17 00:00:00 2001 From: JordiManyer Date: Mon, 4 Nov 2024 23:44:28 +1100 Subject: [PATCH 017/105] Started MWE of moment-based reffes --- test/moment_based_reffes.jl | 305 ++++++++++++++++++++++++++++++++++++ 1 file changed, 305 insertions(+) create mode 100644 test/moment_based_reffes.jl diff --git a/test/moment_based_reffes.jl b/test/moment_based_reffes.jl new file mode 100644 index 000000000..540ae2467 --- /dev/null +++ b/test/moment_based_reffes.jl @@ -0,0 +1,305 @@ + +using Gridap +using Gridap.ReferenceFEs +using Gridap.Fields +using Gridap.Arrays +using Gridap.Geometry + +using FillArrays + +using Gridap.ReferenceFEs: MonomialBasis, JacobiBasis +using Gridap.Polynomials, Gridap.TensorValues + +############################################################################################ +# Fixes + +function Arrays.return_cache(k::Fields.BroadcastOpFieldArray{typeof(∘)},x::AbstractArray{<:Point}) + f, g = k.args + cg = return_cache(g,x) + gx = evaluate!(cg,g,x) + cf = return_cache(f,gx) + return cg, cf +end + +function Arrays.evaluate!(cache, k::Fields.BroadcastOpFieldArray{typeof(∘)}, x::AbstractArray{<:Point}) + cg, cf = cache + f, g = k.args + gx = evaluate!(cg,g,x) + fgx = evaluate!(cf,f,gx) + return fgx +end + +function Arrays.return_value(k::Fields.OperationField{typeof(∘)},x::AbstractArray{<:Point}) + f, g = k.fields + gx = return_value(g,x) + fgx = return_value(f,gx) + return fgx +end + +function Arrays.return_cache(k::Fields.OperationField{typeof(∘)},x::AbstractArray{<:Point}) + f, g = k.fields + cg = return_cache(g,x) + gx = evaluate!(cg,g,x) + cf = return_cache(f,gx) + return cg, cf +end + +function Arrays.evaluate!(cache, k::Fields.OperationField{typeof(∘)}, x::AbstractArray{<:Point}) + cg, cf = cache + f, g = k.fields + gx = evaluate!(cg,g,x) + fgx = evaluate!(cf,f,gx) + return fgx +end + +############################################################################################ +# FaceMeasure + +struct FaceMeasure{Df,Dc} + poly::Polytope{Dc} + face::Int + quad::Quadrature + function FaceMeasure{Df}(poly::Polytope{Dc},face::Int,order::Int) where {Df,Dc} + fpoly = get_face_polytope(poly,Df,face) + quad = Quadrature(fpoly,order) + new{Df,Dc}(poly,face,quad) + end +end + +function get_face_polytope(p::Polytope{Dc},Df::Int,face::Int) where Dc + return get_reffaces(p)[get_face_type(p)[get_offset(p,Df) + face]] +end + +function get_normal(m::FaceMeasure{Df,Dc}) where {Df,Dc} + @assert Df == Dc - 1 + n = get_facet_normal(m.poly) + return ConstantField(n[m.face]) +end + +function get_tangent(m::FaceMeasure{1,Dc}) where {Dc} + t = get_edge_tangent(m.poly) + return ConstantField(t[m.face]) +end + +function Geometry.get_cell_map(m::FaceMeasure{Df,Dc}) where {Df,Dc} + if Df == Dc + return GenericField(identity) + end + fp = get_face_polytope(m.poly,Df,m.face) + coords = get_face_coordinates(m.poly,Df)[m.face] + fields = get_shapefuns(LagrangianRefFE(Float64,fp,1)) + return linear_combination(coords,fields) +end + +function Arrays.evaluate(f,ds::FaceMeasure) + x = get_coordinates(ds.quad) + w = get_weights(ds.quad) + fx = evaluate(f,x) + return w .* fx, x +end + +constant_basis(T::Type{<:Real}) = [one(T)] +function constant_basis(V::Type{<:MultiValue}) + T = eltype(V) + n = num_components(V) + z, o = zero(T), one(T) + return [V(ntuple(i -> ifelse(i == j, o, z),Val(n))) for j in 1:n] +end + +############################################################################################ + +""" +A moment is given by a triplet (f,σ,μ) where + - f is id of a face Fk + - σ is a function σ(φ,μ,ds) that returns a Field-like object to be integrated over Fk + - μ is a polynomials basis on Fk +""" +function MomentBasedReferenceFE( + p::Polytope{D}, + prebasis::AbstractVector{<:Field}, + moments::AbstractVector{<:Tuple{<:Integer,<:Function,<:AbstractArray{<:Field}}} +) where D + + # TODO: Basis of the constants for the tensor-type we have + T = return_type(prebasis) + order = get_order(prebasis) + φ = constant_basis(VectorValue{D,T}) + + # TODO: This has to be something that can fully contract with the prebasis φ + V = VectorValue{D,Float64} + + n_moments = length(moments) + face_moments = Vector{Array{V}}(undef, n_moments) + face_nodes = Vector{UnitRange{Int}}(undef, n_moments) + nodes = Point{D}[] + + k = 1 + n_nodes = 1 + for (face,σ,μ) in moments + d = get_facedims(p)[face] + lface = face - get_offset(p,d) + + qdegree = order + get_order(μ) + 1 + ds = FaceMeasure{d}(p, lface, qdegree) + + fmap = get_cell_map(ds) + φf = transpose(Broadcasting(Operation(∘))(map(constant_field,φ),fmap)) + vals, f_coords = evaluate(σ(φf,μ,ds),ds) + coords = evaluate(fmap,f_coords) + + face_moments[k] = map(v -> v⋅φ, eachslice(vals, dims=(1,2))) # (nN, nμ, nφ) ⋅ nφ -> (nN, nμ) + face_nodes[k] = n_nodes:(n_nodes+length(coords)-1) + append!(nodes, coords) + + k += 1 + n_nodes += length(coords) + end + + return MomentBasedDofBasis(nodes, face_moments, face_nodes) +end + +function cmom(φ,μ,ds) + Broadcasting(Operation(⋅))(φ,μ) +end + +function fmom(φ,μ,ds) + t = get_normal(ds) + φt = Broadcasting(Operation(⋅))(φ,t) + Broadcasting(Operation(*))(φt,μ) +end + +# RT implementation + +p = QUAD +order = 1 +prebasis = QCurlGradMonomialBasis{2}(Float64,order) + +cb = QGradJacobiPolynomialBasis{2}(Float64,order-1) +fb = JacobiBasis(Float64,SEGMENT,order) +moments = [ + [(f+4,fmom,fb) for f in 1:num_faces(p,1)]...,(9,cmom,cb) +] + +dof_basis = MomentBasedReferenceFE(p,prebasis,moments) + +rt_reffe = RaviartThomasRefFE(Float64,p,order) +rt_dofs = get_dof_basis(rt_reffe) + +Mrt = evaluate(rt_dofs,prebasis) +M = evaluate(dof_basis,prebasis) +M == Mrt + +############################################################################################ + +#φ = MonomialBasis(VectorValue{2,Float64},QUAD,1) +φ = ConstantField(VectorValue(ntuple(i -> 1.0, 2))) + +dc = FaceMeasure{2}(QUAD,1,2) +mc = get_cell_map(dc) +φc = transpose(Broadcasting(Operation(∘))(φ,mc)) +μc = MonomialBasis(VectorValue{2,Float64},QUAD,1) + +df = FaceMeasure{1}(QUAD,1,2) +mf = get_cell_map(df) +φf = transpose(Broadcasting(Operation(∘))(φ,mf)) +μf = MonomialBasis(Float64,SEGMENT,1) + +σc = cmom(φc,μc,dc) +σf = fmom(φf,μf,df) + +vc = evaluate(σc,dc) +vf = evaluate(σf,df) + +sum(vc, dims=1) + + + +""" +List of issues: + +- ReferenceFEs is loaded before CellData, i.e we do NOT have access to the + CellField machinery to compute the moments. + +- Most operations that are defined for CellFields are not 100% working for arrays of Fields, + where we tend to use the Broadcasting + Operation machinery. + For example, ∇(φ) is explicitly deactivated in favor of Broadcasting(∇)(φ). +""" + +p = QUAD + +pts = [Point(0.0,0.0), Point(1.0,0.0), Point(1.0,1.0), Point(0.0,1.0)] + +φ = ReferenceFEs.MonomialBasis(Float64,p,1) +Broadcasting(∇)(φ) + + +evaluate(*,φ,φ) +evaluate(+,φ,φ) +evaluate(-,φ,φ) + +evaluate(evaluate(Broadcasting(+),φ,φ),pts) +evaluate(Broadcasting(Operation(+))(φ,φ),pts) + +evaluate(evaluate(⋅,φ,φ),pts) +evaluate(evaluate(Broadcasting(Operation(⋅)),φ,φ),pts) + + +############################################################################################ +# Deprecated code + +function MomentBasedReferenceFE( + p::Polytope{D}, + order::Int, + face_moments::AbstractVector{<:Union{Function,Nothing}}, + face_basis::AbstractVector{<:Union{AbstractVector{<:Field},Nothing}} +) where D + _face_moments = Vector{AbstractVector{<:Union{Function,Nothing}}}(undef, D+1) + _face_basis = Vector{AbstractVector{<:Union{AbstractVector{<:Field},Nothing}}}(undef, D+1) + for d in 0:D + _face_moments[d+1] = Fill(face_moments[d+1], num_faces(p,d)) + _face_basis[d+1] = Fill(face_basis[d+1], num_faces(p,d)) + end + MomentBasedReferenceFE( + p, + order, + _face_moments, + _face_basis + #[Fill(m,num_faces(p,d-1)) for (d,m) in enumerate(face_moments)], + #[Fill(b,num_faces(p,d-1)) for (d,b) in enumerate(face_basis)], + ) +end + +function MomentBasedReferenceFE( + p::Polytope{D}, + order::Int, + face_moments::AbstractVector{<:AbstractVector{<:Union{Function,Nothing}}}, + face_basis::AbstractVector{<:AbstractVector{<:Union{AbstractVector{<:Field},Nothing}}} +) where D + @assert length(face_moments) == length(face_basis) == D+1 + @assert all(length(face_moments[d+1]) == length(face_basis[d+1]) for d in 0:D) + + d_to_face_to_coords = [Vector{Vector{Point{D}}}(undef, num_faces(p,d)) for d in 0:D] + d_to_face_to_vals = [Vector{Matrix{Float64}}(undef, num_faces(p,d)) for d in 0:D] + for d in 0:D + for face in 1:num_faces(p,d) + σ = face_moments[d+1][face] + μ = face_basis[d+1][face] + + if !isnothing(σ) + @assert !isnothing(μ) + qdegree = order*get_order(μ) + ds = FaceMeasure{d}(p, face, qdegree) + + fmap = get_cell_map(ds) + φ = Operation(∘)(ConstantField(VectorValue(ntuple(i -> 1.0, D))),fmap) + vals, coords = evaluate(σ(φ,μ,ds),ds) + + d_to_face_to_coords[d+1][face] = evaluate(fmap,coords) + d_to_face_to_vals[d+1][face] = vals + end + end + end + + return d_to_face_to_coords, d_to_face_to_vals +end + From cee7072a2f6b74d58f99babd0205b35bbf314b2d Mon Sep 17 00:00:00 2001 From: JordiManyer Date: Tue, 5 Nov 2024 10:57:51 +1100 Subject: [PATCH 018/105] Repreoduced RT and ND reffes in 2D/3D --- src/ReferenceFEs/MomentBasedReferenceFEs.jl | 1 - src/ReferenceFEs/NedelecRefFEs.jl | 2 + test/moment_based_reffes.jl | 126 ++++++++++++++++++-- 3 files changed, 117 insertions(+), 12 deletions(-) diff --git a/src/ReferenceFEs/MomentBasedReferenceFEs.jl b/src/ReferenceFEs/MomentBasedReferenceFEs.jl index 56aac999e..6e76f8b7d 100644 --- a/src/ReferenceFEs/MomentBasedReferenceFEs.jl +++ b/src/ReferenceFEs/MomentBasedReferenceFEs.jl @@ -11,7 +11,6 @@ function MomentBasedReferenceFE( face_moments::AbstractVector{<:AbstractVector{<:Function}}, face_basis::AbstractVector{<:AbstractVector{<:AbstractVector{<:Field}}}; face_mask::AbstractVector{<:AbstractVector{Bool}} = [fill(true,num_faces(p, d)) for d in 0:D], - phi = GenericField(identity) ) where D @assert length(face_moments) == length(face_mask) == length(face_basis) == D+1 @assert all(length(face_moments[d+1]) == length(face_basis[d+1]) for d in 0:D) diff --git a/src/ReferenceFEs/NedelecRefFEs.jl b/src/ReferenceFEs/NedelecRefFEs.jl index c6c731ac9..81924f856 100644 --- a/src/ReferenceFEs/NedelecRefFEs.jl +++ b/src/ReferenceFEs/NedelecRefFEs.jl @@ -317,6 +317,8 @@ function _broadcast_cross(::Type{T},n,b) where T return c end +# Moves bi values from 2D to 3D, by multiplying by a 3x2 matrix Tm +# Tm = [1.0 0.0; 0.0 1.0; 0.0 0.0] for instance function _broadcast_extend(::Type{T},Tm,b) where T c = Array{T}(undef,size(b)) for (ii,i) in enumerate(b) diff --git a/test/moment_based_reffes.jl b/test/moment_based_reffes.jl index 540ae2467..b68cdc3e0 100644 --- a/test/moment_based_reffes.jl +++ b/test/moment_based_reffes.jl @@ -52,6 +52,8 @@ function Arrays.evaluate!(cache, k::Fields.OperationField{typeof(∘)}, x::Abstr return fgx end +Arrays.return_type(::Polynomials.QGradMonomialBasis{D,T}) where {D,T} = T + ############################################################################################ # FaceMeasure @@ -91,6 +93,12 @@ function Geometry.get_cell_map(m::FaceMeasure{Df,Dc}) where {Df,Dc} return linear_combination(coords,fields) end +function get_extension(m::FaceMeasure{Df,Dc}) where {Df,Dc} + @assert Df == Dc - 1 + vs = ReferenceFEs._nfaces_vertices(Float64,m.poly,Df)[m.face] + return ConstantField(TensorValue(hcat([vs[2]-vs[1]...],[vs[3]-vs[1]...]))) +end + function Arrays.evaluate(f,ds::FaceMeasure) x = get_coordinates(ds.quad) w = get_weights(ds.quad) @@ -98,6 +106,9 @@ function Arrays.evaluate(f,ds::FaceMeasure) return w .* fx, x end +############################################################################################ +# Constant basis: Basis for a tensor type + constant_basis(T::Type{<:Real}) = [one(T)] function constant_basis(V::Type{<:MultiValue}) T = eltype(V) @@ -107,17 +118,28 @@ function constant_basis(V::Type{<:MultiValue}) end ############################################################################################ +using Gridap.ReferenceFEs: ReferenceFEName, Conformity +struct MomentBasedReffe{T<:ReferenceFEName} <: ReferenceFEName + name :: T +end """ A moment is given by a triplet (f,σ,μ) where - f is id of a face Fk - σ is a function σ(φ,μ,ds) that returns a Field-like object to be integrated over Fk - μ is a polynomials basis on Fk + +Open questions: + - Do we want to keep having structures face -> data? I guess if we had more than a single + moment per face, we would aggregate them. + - Can we always determine the minimum integration order for each moment? """ function MomentBasedReferenceFE( + name::ReferenceFEName, p::Polytope{D}, prebasis::AbstractVector{<:Field}, - moments::AbstractVector{<:Tuple{<:Integer,<:Function,<:AbstractArray{<:Field}}} + moments::AbstractVector{<:Tuple{<:Integer,<:Function,<:AbstractArray{<:Field}}}, + conformity::Conformity; ) where D # TODO: Basis of the constants for the tensor-type we have @@ -128,13 +150,17 @@ function MomentBasedReferenceFE( # TODO: This has to be something that can fully contract with the prebasis φ V = VectorValue{D,Float64} + # TODO: Do we want these of length n_moments or n_faces? + n_faces = num_faces(p) n_moments = length(moments) face_moments = Vector{Array{V}}(undef, n_moments) face_nodes = Vector{UnitRange{Int}}(undef, n_moments) + face_own_dofs = [Int[] for _ in 1:n_faces] nodes = Point{D}[] k = 1 n_nodes = 1 + n_dofs = 1 for (face,σ,μ) in moments d = get_facedims(p)[face] lface = face - get_offset(p,d) @@ -150,45 +176,123 @@ function MomentBasedReferenceFE( face_moments[k] = map(v -> v⋅φ, eachslice(vals, dims=(1,2))) # (nN, nμ, nφ) ⋅ nφ -> (nN, nμ) face_nodes[k] = n_nodes:(n_nodes+length(coords)-1) append!(nodes, coords) + append!(face_own_dofs[face], n_dofs:(n_dofs+size(vals,2)-1)) k += 1 n_nodes += length(coords) + n_dofs += size(vals,2) end - return MomentBasedDofBasis(nodes, face_moments, face_nodes) + dof_basis = MomentBasedDofBasis(nodes, face_moments, face_nodes) + metadata = nothing + + return GenericRefFE{typeof(MomentBasedReffe(name))}( + n_dofs, + p, + prebasis, + dof_basis, + conformity, + metadata, + face_own_dofs + ) end function cmom(φ,μ,ds) Broadcasting(Operation(⋅))(φ,μ) end -function fmom(φ,μ,ds) - t = get_normal(ds) +function fmom_dot(φ,μ,ds) + n = get_normal(ds) + φn = Broadcasting(Operation(⋅))(φ,n) + Broadcasting(Operation(*))(φn,μ) +end + +function fmom_cross(φ,μ,ds) + n = get_normal(ds) + E = get_extension(ds) + Eμ = Broadcasting(Operation(⋅))(E,μ) # We have to extend the basis to 3D (see Nedelec) + φn = Broadcasting(Operation(×))(φ,n) + Broadcasting(Operation(⋅))(φn,Eμ) +end + +function emom(φ,μ,ds) + t = get_tangent(ds) φt = Broadcasting(Operation(⋅))(φ,t) Broadcasting(Operation(*))(φt,μ) end # RT implementation -p = QUAD +D = 2 +p = (D==2) ? QUAD : HEX order = 1 -prebasis = QCurlGradMonomialBasis{2}(Float64,order) -cb = QGradJacobiPolynomialBasis{2}(Float64,order-1) +prebasis = QCurlGradMonomialBasis{D}(Float64,order) +cb = QGradJacobiPolynomialBasis{D}(Float64,order-1) fb = JacobiBasis(Float64,SEGMENT,order) moments = [ - [(f+4,fmom,fb) for f in 1:num_faces(p,1)]...,(9,cmom,cb) + [(f+get_offset(p,1),fmom_dot,fb) for f in 1:num_faces(p,1)]..., # Face moments + (num_faces(p),cmom,cb) # Cell moments ] - -dof_basis = MomentBasedReferenceFE(p,prebasis,moments) +reffe = MomentBasedReferenceFE(RaviartThomas(),p,prebasis,moments,DivConformity()) +dofs = get_dof_basis(reffe) rt_reffe = RaviartThomasRefFE(Float64,p,order) rt_dofs = get_dof_basis(rt_reffe) Mrt = evaluate(rt_dofs,prebasis) -M = evaluate(dof_basis,prebasis) +M = evaluate(dofs,prebasis) M == Mrt +# ND implementation + +D = 2 +p = (D==2) ? QUAD : HEX +order = 1 + +prebasis = QGradMonomialBasis{D}(Float64,order) +cb = QCurlGradMonomialBasis{D}(Float64,order-1) +fb = QGradMonomialBasis{D-1}(Float64,order-1) +eb = MonomialBasis(Float64,SEGMENT,order) +moments = [ + [(f+get_offset(p,1),emom,eb) for f in 1:num_faces(p,1)]..., # Edge moments + (num_faces(p),cmom,cb) # Cell moments +] +reffe = MomentBasedReferenceFE(Nedelec(),p,prebasis,moments,CurlConformity()) +dofs = get_dof_basis(reffe) + +nd_reffe = NedelecRefFE(Float64,p,order) +nd_dofs = get_dof_basis(nd_reffe) + +Mnd = evaluate(nd_dofs,prebasis) +M = evaluate(dofs,prebasis) +M == Mnd + +# 3D ND implementation + +D = 3 +p = (D==2) ? QUAD : HEX +order = 1 + +prebasis = QGradMonomialBasis{D}(Float64,order) +cb = QCurlGradMonomialBasis{D}(Float64,order-1) +fb = QGradMonomialBasis{D-1}(Float64,order-1) +eb = MonomialBasis(Float64,SEGMENT,order) +moments = [ + [(f+get_offset(p,1),emom,eb) for f in 1:num_faces(p,1)]..., # Edge moments + [(f+get_offset(p,2),fmom_cross,fb) for f in 1:num_faces(p,2)]..., # Face moments + (num_faces(p),cmom,cb) # Cell moments +] +reffe = MomentBasedReferenceFE(Nedelec(),p,prebasis,moments,CurlConformity()) +dofs = get_dof_basis(reffe) + +nd_reffe = NedelecRefFE(Float64,p,order) +nd_dofs = get_dof_basis(nd_reffe) + +Mnd = evaluate(nd_dofs,prebasis) +M = evaluate(dofs,prebasis) +M == Mnd # This is because the get_facet_orientations hack in Nedelec... Why is it necessary? + ############################################################################################ #φ = MonomialBasis(VectorValue{2,Float64},QUAD,1) From 62ad25989b5aaf5155cda144986e934ec6b91d03 Mon Sep 17 00:00:00 2001 From: JordiManyer Date: Tue, 5 Nov 2024 11:20:54 +1100 Subject: [PATCH 019/105] Minor --- test/moment_based_reffes.jl | 138 ++++-------------------------------- 1 file changed, 13 insertions(+), 125 deletions(-) diff --git a/test/moment_based_reffes.jl b/test/moment_based_reffes.jl index b68cdc3e0..ce5dec6aa 100644 --- a/test/moment_based_reffes.jl +++ b/test/moment_based_reffes.jl @@ -108,6 +108,7 @@ end ############################################################################################ # Constant basis: Basis for a tensor type +# Another possible name would be "component basis constant_basis(T::Type{<:Real}) = [one(T)] function constant_basis(V::Type{<:MultiValue}) @@ -133,6 +134,13 @@ Open questions: - Do we want to keep having structures face -> data? I guess if we had more than a single moment per face, we would aggregate them. - Can we always determine the minimum integration order for each moment? + +Current pains: +- ReferenceFEs is loaded before CellData, i.e we do NOT have access to the + CellField machinery to compute the moments. +- Most operations that are defined for CellFields are not 100% working for arrays of Fields, + where we tend to use the Broadcasting + Operation machinery. + For example, ∇(φ) is explicitly deactivated in favor of Broadcasting(∇)(φ). """ function MomentBasedReferenceFE( name::ReferenceFEName, @@ -187,13 +195,7 @@ function MomentBasedReferenceFE( metadata = nothing return GenericRefFE{typeof(MomentBasedReffe(name))}( - n_dofs, - p, - prebasis, - dof_basis, - conformity, - metadata, - face_own_dofs + n_dofs, p, prebasis, dof_basis, conformity, metadata, face_own_dofs ) end @@ -208,10 +210,11 @@ function fmom_dot(φ,μ,ds) end function fmom_cross(φ,μ,ds) - n = get_normal(ds) + o = get_facet_orientations(ds.poly)[ds.face] # Why do we need this? Is this to avoid a sign map? + n = o*get_normal(ds) E = get_extension(ds) Eμ = Broadcasting(Operation(⋅))(E,μ) # We have to extend the basis to 3D (see Nedelec) - φn = Broadcasting(Operation(×))(φ,n) + φn = Broadcasting(Operation(×))(n,φ) Broadcasting(Operation(⋅))(φn,Eμ) end @@ -291,119 +294,4 @@ nd_dofs = get_dof_basis(nd_reffe) Mnd = evaluate(nd_dofs,prebasis) M = evaluate(dofs,prebasis) -M == Mnd # This is because the get_facet_orientations hack in Nedelec... Why is it necessary? - -############################################################################################ - -#φ = MonomialBasis(VectorValue{2,Float64},QUAD,1) -φ = ConstantField(VectorValue(ntuple(i -> 1.0, 2))) - -dc = FaceMeasure{2}(QUAD,1,2) -mc = get_cell_map(dc) -φc = transpose(Broadcasting(Operation(∘))(φ,mc)) -μc = MonomialBasis(VectorValue{2,Float64},QUAD,1) - -df = FaceMeasure{1}(QUAD,1,2) -mf = get_cell_map(df) -φf = transpose(Broadcasting(Operation(∘))(φ,mf)) -μf = MonomialBasis(Float64,SEGMENT,1) - -σc = cmom(φc,μc,dc) -σf = fmom(φf,μf,df) - -vc = evaluate(σc,dc) -vf = evaluate(σf,df) - -sum(vc, dims=1) - - - -""" -List of issues: - -- ReferenceFEs is loaded before CellData, i.e we do NOT have access to the - CellField machinery to compute the moments. - -- Most operations that are defined for CellFields are not 100% working for arrays of Fields, - where we tend to use the Broadcasting + Operation machinery. - For example, ∇(φ) is explicitly deactivated in favor of Broadcasting(∇)(φ). -""" - -p = QUAD - -pts = [Point(0.0,0.0), Point(1.0,0.0), Point(1.0,1.0), Point(0.0,1.0)] - -φ = ReferenceFEs.MonomialBasis(Float64,p,1) -Broadcasting(∇)(φ) - - -evaluate(*,φ,φ) -evaluate(+,φ,φ) -evaluate(-,φ,φ) - -evaluate(evaluate(Broadcasting(+),φ,φ),pts) -evaluate(Broadcasting(Operation(+))(φ,φ),pts) - -evaluate(evaluate(⋅,φ,φ),pts) -evaluate(evaluate(Broadcasting(Operation(⋅)),φ,φ),pts) - - -############################################################################################ -# Deprecated code - -function MomentBasedReferenceFE( - p::Polytope{D}, - order::Int, - face_moments::AbstractVector{<:Union{Function,Nothing}}, - face_basis::AbstractVector{<:Union{AbstractVector{<:Field},Nothing}} -) where D - _face_moments = Vector{AbstractVector{<:Union{Function,Nothing}}}(undef, D+1) - _face_basis = Vector{AbstractVector{<:Union{AbstractVector{<:Field},Nothing}}}(undef, D+1) - for d in 0:D - _face_moments[d+1] = Fill(face_moments[d+1], num_faces(p,d)) - _face_basis[d+1] = Fill(face_basis[d+1], num_faces(p,d)) - end - MomentBasedReferenceFE( - p, - order, - _face_moments, - _face_basis - #[Fill(m,num_faces(p,d-1)) for (d,m) in enumerate(face_moments)], - #[Fill(b,num_faces(p,d-1)) for (d,b) in enumerate(face_basis)], - ) -end - -function MomentBasedReferenceFE( - p::Polytope{D}, - order::Int, - face_moments::AbstractVector{<:AbstractVector{<:Union{Function,Nothing}}}, - face_basis::AbstractVector{<:AbstractVector{<:Union{AbstractVector{<:Field},Nothing}}} -) where D - @assert length(face_moments) == length(face_basis) == D+1 - @assert all(length(face_moments[d+1]) == length(face_basis[d+1]) for d in 0:D) - - d_to_face_to_coords = [Vector{Vector{Point{D}}}(undef, num_faces(p,d)) for d in 0:D] - d_to_face_to_vals = [Vector{Matrix{Float64}}(undef, num_faces(p,d)) for d in 0:D] - for d in 0:D - for face in 1:num_faces(p,d) - σ = face_moments[d+1][face] - μ = face_basis[d+1][face] - - if !isnothing(σ) - @assert !isnothing(μ) - qdegree = order*get_order(μ) - ds = FaceMeasure{d}(p, face, qdegree) - - fmap = get_cell_map(ds) - φ = Operation(∘)(ConstantField(VectorValue(ntuple(i -> 1.0, D))),fmap) - vals, coords = evaluate(σ(φ,μ,ds),ds) - - d_to_face_to_coords[d+1][face] = evaluate(fmap,coords) - d_to_face_to_vals[d+1][face] = vals - end - end - end - - return d_to_face_to_coords, d_to_face_to_vals -end - +M == Mnd From 107adf14ff5be3a4a1cb66d4710dd576f902bcf9 Mon Sep 17 00:00:00 2001 From: JordiManyer Date: Sat, 9 Nov 2024 11:25:10 +1100 Subject: [PATCH 020/105] Added PCurlGradJacobiPolynomialBases --- .../PCurlGradJacobiPolynomialBases.jl | 281 ++++++++++++++++++ src/Polynomials/Polynomials.jl | 2 + src/ReferenceFEs/RaviartThomasRefFEs.jl | 2 + 3 files changed, 285 insertions(+) create mode 100644 src/Polynomials/PCurlGradJacobiPolynomialBases.jl diff --git a/src/Polynomials/PCurlGradJacobiPolynomialBases.jl b/src/Polynomials/PCurlGradJacobiPolynomialBases.jl new file mode 100644 index 000000000..3e12da56f --- /dev/null +++ b/src/Polynomials/PCurlGradJacobiPolynomialBases.jl @@ -0,0 +1,281 @@ +""" +struct PCurlGradJacobiPolynomialBasis{...} <: AbstractArray{JacobiPolynomial} + +This type implements a multivariate vector-valued polynomial basis +spanning the space needed for Raviart-Thomas reference elements on simplices. +The type parameters and fields of this `struct` are not public. +This type fully implements the [`Field`](@ref) interface, with up to first order +derivatives. +""" +struct PCurlGradJacobiPolynomialBasis{D,T} <: AbstractVector{JacobiPolynomial} + order::Int + pterms::Array{CartesianIndex{D},1} + sterms::Array{CartesianIndex{D},1} + perms::Matrix{Int} + function PCurlGradJacobiPolynomialBasis(::Type{T},order::Int, + pterms::Array{CartesianIndex{D},1},sterms::Array{CartesianIndex{D},1}, + perms::Matrix{Int}) where {D,T} + new{D,T}(order,pterms,sterms,perms) + end +end + +Base.size(a::PCurlGradJacobiPolynomialBasis) = (_ndofs_pgrad(a),) +# @santiagobadia : Not sure we want to create the monomial machinery +Base.getindex(a::PCurlGradJacobiPolynomialBasis,i::Integer) = JacobiPolynomial() +Base.IndexStyle(::PCurlGradJacobiPolynomialBasis) = IndexLinear() + +""" +PCurlGradJacobiPolynomialBasis{D}(::Type{T},order::Int) where {D,T} + +Returns a `PCurlGradJacobiPolynomialBasis` object. `D` is the dimension +of the coordinate space and `T` is the type of the components in the vector-value. +The `order` argument has the following meaning: the divergence of the functions +in this basis is in the P space of degree `order`. +""" +function PCurlGradJacobiPolynomialBasis{D}(::Type{T},order::Int) where {D,T} + @check T<:Real "T needs to be <:Real since represents the type of the components of the vector value" + P_k = MonomialBasis{D}(T, order, _p_filter) + S_k = MonomialBasis{D}(T, order, _s_filter) + pterms = P_k.terms + sterms = S_k.terms + perms = _prepare_perms(D) + PCurlGradJacobiPolynomialBasis(T,order,pterms,sterms,perms) +end + +""" + num_terms(f::PCurlGradJacobiPolynomialBasis{D,T}) where {D,T} +""" +function num_terms(f::PCurlGradJacobiPolynomialBasis{D,T}) where {D,T} + Int(_p_dim(f.order,D)*D + _p_dim(f.order,D-1)) +end + +get_order(f::PCurlGradJacobiPolynomialBasis{D,T}) where {D,T} = f.order + +return_type(::PCurlGradJacobiPolynomialBasis{D,T}) where {D,T} = T + +function return_cache(f::PCurlGradJacobiPolynomialBasis{D,T},x::AbstractVector{<:Point}) where {D,T} + @check D == length(eltype(x)) "Incorrect number of point components" + np = length(x) + ndof = _ndofs_pgrad(f) + n = 1 + f.order+1 + V = VectorValue{D,T} + r = CachedArray(zeros(V,(np,ndof))) + v = CachedArray(zeros(V,(ndof,))) + c = CachedArray(zeros(T,(D,n))) + (r, v, c) +end + +function evaluate!(cache,f::PCurlGradJacobiPolynomialBasis{D,T},x::AbstractVector{<:Point}) where {D,T} + r, v, c = cache + np = length(x) + ndof = _ndofs_pgrad(f) + n = 1 + f.order+1 + setsize!(r,(np,ndof)) + setsize!(v,(ndof,)) + setsize!(c,(D,n)) + for i in 1:np + @inbounds xi = x[i] + _evaluate_nd_pcurlgrad_jp!(v,xi,f.order+1,f.pterms,f.sterms,f.perms,c) + for j in 1:ndof + @inbounds r[i,j] = v[j] + end + end + r.array +end + +function return_cache( + fg::FieldGradientArray{1,PCurlGradJacobiPolynomialBasis{D,T}}, + x::AbstractVector{<:Point}) where {D,T} + + f = fg.fa + @check D == length(eltype(x)) "Incorrect number of point components" + np = length(x) + ndof = _ndofs_pgrad(f) + n = 1 + f.order+1 + xi = testitem(x) + V = VectorValue{D,T} + G = gradient_type(V,xi) + r = CachedArray(zeros(G,(np,ndof))) + v = CachedArray(zeros(G,(ndof,))) + c = CachedArray(zeros(T,(D,n))) + g = CachedArray(zeros(T,(D,n))) + (r, v, c, g) +end + +function evaluate!(cache, + fg::FieldGradientArray{1,PCurlGradJacobiPolynomialBasis{D,T}}, + x::AbstractVector{<:Point}) where {D,T} + + f = fg.fa + r, v, c, g = cache + np = length(x) + ndof = _ndofs_pgrad(f) + n = 1 + f.order+1 + setsize!(r,(np,ndof)) + setsize!(v,(ndof,)) + setsize!(c,(D,n)) + setsize!(g,(D,n)) + V = VectorValue{D,T} + for i in 1:np + @inbounds xi = x[i] + _gradient_nd_pcurlgrad_jp!(v,xi,f.order+1,f.pterms,f.sterms,f.perms,c,g,V) + for j in 1:ndof + @inbounds r[i,j] = v[j] + end + end + r.array +end + + +# Helpers + +_ndofs_pgrad(f::PCurlGradJacobiPolynomialBasis{D}) where D = num_terms(f) + +function _evaluate_nd_pcurlgrad_jp!( + v::AbstractVector{V}, + x, + order, + pterms::Array{CartesianIndex{D},1}, + sterms::Array{CartesianIndex{D},1}, + perms::Matrix{Int}, + c::AbstractMatrix{T}) where {V,T,D} + + dim = D + for d in 1:dim + _evaluate_1d_jp!(c,x,order,d) + end + + o = one(T) + k = 1 + m = zero(Mutable(V)) + js = eachindex(m) + z = zero(T) + + for ci in pterms + for j in js + + @inbounds for i in js + m[i] = z + end + + s = o + @inbounds for d in 1:dim + s *= c[d,ci[perms[d,j]]] + end + + m[j] = s + v[k] = m + k += 1 + end + end + + for ci in sterms + @inbounds for i in js + m[i] = z + end + for j in js + + s = c[j,2] + @inbounds for d in 1:dim + s *= c[d,ci[d]] + end + + m[j] = s + + end + v[k] = m + k += 1 + end +end + +function _gradient_nd_pcurlgrad_jp!( + v::AbstractVector{G}, + x, + order, + pterms::Array{CartesianIndex{D},1}, + sterms::Array{CartesianIndex{D},1}, + perms::Matrix{Int}, + c::AbstractMatrix{T}, + g::AbstractMatrix{T}, + ::Type{V}) where {G,T,D,V} + + dim = D + for d in 1:dim + _evaluate_1d_jp!(c,x,order,d) + _gradient_1d_jp!(g,x,order,d) + end + + z = zero(Mutable(V)) + m = zero(Mutable(G)) + js = eachindex(z) + mjs = eachindex(m) + o = one(T) + zi = zero(T) + k = 1 + + for ci in pterms + for j in js + + s = z + for i in js + s[i] = o + end + + for q in 1:dim + for d in 1:dim + if d != q + @inbounds s[q] *= c[d,ci[perms[d,j]]] + else + @inbounds s[q] *= g[d,ci[perms[d,j]]] + end + end + end + + @inbounds for i in mjs + m[i] = zi + end + + for i in js + @inbounds m[i,j] = s[i] + end + @inbounds v[k] = m + k += 1 + end + end + + for ci in sterms + + @inbounds for i in mjs + m[i] = zi + end + + for j in js + + s = z + for i in js + s[i] = c[j,2] + 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 + aux = o + @inbounds for d in 1:dim + aux *= c[d,ci[d]] + end + s[j] += aux + + for i in js + @inbounds m[i,j] = s[i] + end + end + @inbounds v[k] = m + k += 1 + end +end diff --git a/src/Polynomials/Polynomials.jl b/src/Polynomials/Polynomials.jl index 85d34e3ac..66da8fc51 100644 --- a/src/Polynomials/Polynomials.jl +++ b/src/Polynomials/Polynomials.jl @@ -52,6 +52,8 @@ include("JacobiPolynomialBases.jl") include("QGradJacobiPolynomialBases.jl") +include("PCurlGradJacobiPolynomialBases.jl") + include("ChebyshevPolynomialBases.jl") end # module diff --git a/src/ReferenceFEs/RaviartThomasRefFEs.jl b/src/ReferenceFEs/RaviartThomasRefFEs.jl index 9443480e6..11d7f9b8a 100644 --- a/src/ReferenceFEs/RaviartThomasRefFEs.jl +++ b/src/ReferenceFEs/RaviartThomasRefFEs.jl @@ -26,6 +26,8 @@ function RaviartThomasRefFE( prebasis = PCurlGradMonomialBasis{D}(et,order) elseif is_n_cube(p) && basis_type == :jacobi prebasis = QCurlGradJacobiPolynomialBasis{D}(et,order) + elseif is_simplex(p) && basis_type == :jacobi + prebasis = PCurlGradJacobiPolynomialBasis{D}(et,order) elseif is_n_cube(p) && basis_type == :chebyshev prebasis = QCurlGradChebyshevPolynomialBasis{D}(et,order) else From f2d896dbaad6c0cbce1bab52475761b76e3eef80 Mon Sep 17 00:00:00 2001 From: JordiManyer Date: Sat, 9 Nov 2024 11:50:17 +1100 Subject: [PATCH 021/105] Minor --- src/Polynomials/Polynomials.jl | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Polynomials/Polynomials.jl b/src/Polynomials/Polynomials.jl index 66da8fc51..81a4b04c2 100644 --- a/src/Polynomials/Polynomials.jl +++ b/src/Polynomials/Polynomials.jl @@ -29,6 +29,7 @@ export ModalC0Basis export JacobiPolynomialBasis export QGradJacobiPolynomialBasis export QCurlGradJacobiPolynomialBasis +export PCurlGradJacobiPolynomialBasis export ChebyshevPolynomialBasis export QGradChebyshevPolynomialBasis export QCurlGradChebyshevPolynomialBasis From 06fe0d46b84ccdc2ca05d62ed46a3f7a362cf413 Mon Sep 17 00:00:00 2001 From: JordiManyer Date: Sun, 10 Nov 2024 01:02:12 +1100 Subject: [PATCH 022/105] Replicated Raviart-Thomas refes with new machinery --- src/Fields/FieldArrays.jl | 16 + .../PCurlGradJacobiPolynomialBases.jl | 2 +- src/Polynomials/PCurlGradMonomialBases.jl | 2 +- src/Polynomials/QCurlGradMonomialBases.jl | 3 +- src/Polynomials/QGradJacobiPolynomialBases.jl | 4 +- src/Polynomials/QGradMonomialBases.jl | 2 + src/ReferenceFEs/DivConformingReferenceFEs.jl | 143 ++++++++ src/ReferenceFEs/MomentBasedReferenceFEs.jl | 310 +++++++++++++++++- src/ReferenceFEs/ReferenceFEs.jl | 4 +- .../{ => deprecated}/RaviartThomasRefFEs.jl | 0 test/moment_based_reffes.jl | 15 + 11 files changed, 483 insertions(+), 18 deletions(-) create mode 100644 src/ReferenceFEs/DivConformingReferenceFEs.jl rename src/ReferenceFEs/{ => deprecated}/RaviartThomasRefFEs.jl (100%) diff --git a/src/Fields/FieldArrays.jl b/src/Fields/FieldArrays.jl index e7e2c2c42..4eba60280 100644 --- a/src/Fields/FieldArrays.jl +++ b/src/Fields/FieldArrays.jl @@ -509,6 +509,22 @@ for T in (:(Point),:(AbstractArray{<:Point})) evaluate!(r,bm,rs...) end + function return_cache(k::BroadcastOpFieldArray{typeof(∘)},x::$T) + f, g = k.args + cg = return_cache(g,x) + gx = evaluate!(cg,g,x) + cf = return_cache(f,gx) + return cg, cf + end + + function evaluate!(cache, k::BroadcastOpFieldArray{typeof(∘)},x::$T) + cg, cf = cache + f, g = k.args + gx = evaluate!(cg,g,x) + fgx = evaluate!(cf,f,gx) + return fgx + end + end end diff --git a/src/Polynomials/PCurlGradJacobiPolynomialBases.jl b/src/Polynomials/PCurlGradJacobiPolynomialBases.jl index 3e12da56f..1b4cf9923 100644 --- a/src/Polynomials/PCurlGradJacobiPolynomialBases.jl +++ b/src/Polynomials/PCurlGradJacobiPolynomialBases.jl @@ -51,7 +51,7 @@ end get_order(f::PCurlGradJacobiPolynomialBasis{D,T}) where {D,T} = f.order -return_type(::PCurlGradJacobiPolynomialBasis{D,T}) where {D,T} = T +return_type(::PCurlGradJacobiPolynomialBasis{D,T}) where {D,T} = VectorValue{D,T} function return_cache(f::PCurlGradJacobiPolynomialBasis{D,T},x::AbstractVector{<:Point}) where {D,T} @check D == length(eltype(x)) "Incorrect number of point components" diff --git a/src/Polynomials/PCurlGradMonomialBases.jl b/src/Polynomials/PCurlGradMonomialBases.jl index 23c6d0f21..359886786 100644 --- a/src/Polynomials/PCurlGradMonomialBases.jl +++ b/src/Polynomials/PCurlGradMonomialBases.jl @@ -52,7 +52,7 @@ end get_order(f::PCurlGradMonomialBasis{D,T}) where {D,T} = f.order -return_type(::PCurlGradMonomialBasis{D,T}) where {D,T} = T +return_type(::PCurlGradMonomialBasis{D,T}) where {D,T} = VectorValue{D,T} function return_cache(f::PCurlGradMonomialBasis{D,T},x::AbstractVector{<:Point}) where {D,T} @check D == length(eltype(x)) "Incorrect number of point components" diff --git a/src/Polynomials/QCurlGradMonomialBases.jl b/src/Polynomials/QCurlGradMonomialBases.jl index b0ad934dc..e4b519dda 100644 --- a/src/Polynomials/QCurlGradMonomialBases.jl +++ b/src/Polynomials/QCurlGradMonomialBases.jl @@ -39,8 +39,7 @@ function QCurlGradMonomialBasis{D}(::Type{T},order::Int) where {D,T} QCurlGradMonomialBasis(T,order,terms,perms) end -# @santiagobadia: This is dirty, I would put here VectorValue{D,T} -return_type(::QCurlGradMonomialBasis{D,T}) where {D,T} = T +return_type(::QCurlGradMonomialBasis{D,T}) where {D,T} = VectorValue{D,T} function return_cache(f::QCurlGradMonomialBasis,x::AbstractVector{<:Point}) return_cache(f.qgrad,x) diff --git a/src/Polynomials/QGradJacobiPolynomialBases.jl b/src/Polynomials/QGradJacobiPolynomialBases.jl index eef6aad49..d160363ef 100644 --- a/src/Polynomials/QGradJacobiPolynomialBases.jl +++ b/src/Polynomials/QGradJacobiPolynomialBases.jl @@ -46,6 +46,8 @@ num_terms(f::QGradJacobiPolynomialBasis{D,T}) where {D,T} = length(f.terms)*D get_order(f::QGradJacobiPolynomialBasis) = f.order +return_type(::QGradJacobiPolynomialBasis{D,T}) where {D,T} = VectorValue{D,T} + function return_cache(f::QGradJacobiPolynomialBasis{D,T},x::AbstractVector{<:Point}) where {D,T} @check D == length(eltype(x)) "Incorrect number of point components" np = length(x) @@ -266,7 +268,7 @@ function QCurlGradJacobiPolynomialBasis{D}(::Type{T},order::Int) where {D,T} QCurlGradJacobiPolynomialBasis(T,order,terms,perms) end -return_type(::QCurlGradJacobiPolynomialBasis{D,T}) where {D,T} = T +return_type(::QCurlGradJacobiPolynomialBasis{D,T}) where {D,T} = VectorValue{D,T} function return_cache(f::QCurlGradJacobiPolynomialBasis,x::AbstractVector{<:Point}) return_cache(f.qgrad,x) diff --git a/src/Polynomials/QGradMonomialBases.jl b/src/Polynomials/QGradMonomialBases.jl index 2e69283ab..0b9dcd9b7 100644 --- a/src/Polynomials/QGradMonomialBases.jl +++ b/src/Polynomials/QGradMonomialBases.jl @@ -47,6 +47,8 @@ num_terms(f::QGradMonomialBasis{D,T}) where {D,T} = length(f.terms)*D get_order(f::QGradMonomialBasis) = f.order +return_type(::QGradMonomialBasis{D,T}) where {D,T} = VectorValue{D,T} + function return_cache(f::QGradMonomialBasis{D,T},x::AbstractVector{<:Point}) where {D,T} @check D == length(eltype(x)) "Incorrect number of point components" np = length(x) diff --git a/src/ReferenceFEs/DivConformingReferenceFEs.jl b/src/ReferenceFEs/DivConformingReferenceFEs.jl new file mode 100644 index 000000000..cd0434374 --- /dev/null +++ b/src/ReferenceFEs/DivConformingReferenceFEs.jl @@ -0,0 +1,143 @@ + +struct DivConformity <: Conformity end +abstract type DivConforming <: ReferenceFEName end + +# RaviartThomas + +struct RaviartThomas <: DivConforming end +const raviart_thomas = RaviartThomas() + +""" + RaviartThomasRefFE(::Type{et},p::Polytope,order::Integer) where et + +The `order` argument has the following meaning: the divergence of the functions in this basis +is in the Q space of degree `order`. +""" +function RaviartThomasRefFE( + ::Type{T},p::Polytope{D},order::Integer +) where {T,D} + + if is_n_cube(p) + prebasis = QCurlGradJacobiPolynomialBasis{D}(T,order) # Prebasis + cb = QGradJacobiPolynomialBasis{D}(T,order-1) # Cell basis + fb = JacobiPolynomialBasis{D}(T,order,Polynomials._q_filter) # Face basis + elseif is_simplex(p) + prebasis = PCurlGradMonomialBasis{D}(et,order) # Prebasis + cb = MonomialBasis{D}(T,order-1,Polynomials._p_filter) # Cell basis + fb = JacobiPolynomialBasis{D}(T,order,Polynomials._p_filter) # Face basis + else + @notimplemented "H(div) Reference FE only available for cubes and simplices" + end + + function cmom(φ,μ,ds) # Cell moment function + Broadcasting(Operation(⋅))(φ,μ) + end + function fmom(φ,μ,ds) # Face moment function + n = get_normal(ds) + φn = Broadcasting(Operation(⋅))(φ,n) + Broadcasting(Operation(*))(φn,μ) + end + moments = [ + (get_dimrange(p,D-1),fmom,fb), # Face moments + (get_dimrange(p,D),cmom,cb) # Cell moments + ] + + return MomentBasedReferenceFE(RaviartThomas(),p,prebasis,moments,DivConformity()) +end + +function ReferenceFE(p::Polytope,::RaviartThomas,order;kwargs...) + RaviartThomasRefFE(Float64,p,order;kwargs...) +end + +function ReferenceFE(p::Polytope,::RaviartThomas,::Type{T},order;kwargs...) where T + RaviartThomasRefFE(T,p,order;kwargs...) +end + +function Conformity(reffe::GenericRefFE{RaviartThomas},sym::Symbol) + hdiv = (:Hdiv,:HDiv) + if sym == :L2 + L2Conformity() + elseif sym in hdiv + DivConformity() + else + @unreachable """\n + It is not possible to use conformity = $sym on a Raviart Thomas reference FE. + + Possible values of conformity for this reference fe are $((:L2, hdiv...)). + """ + end +end + +function get_face_own_dofs(reffe::GenericRefFE{RaviartThomas}, conf::DivConformity) + get_face_dofs(reffe) +end + +# TODO: Please remove me +function JacobiBasis(::Type{T},p::Polytope,orders) where T + compute_jacobi_basis(T,p,orders) +end +function JacobiBasis(::Type{T},p::Polytope{D},order::Int) where {D,T} + orders = tfill(order,Val{D}()) + JacobiBasis(T,p,orders) +end +function compute_jacobi_basis(::Type{T},p::ExtrusionPolytope{D},orders) where {D,T} + extrusion = Tuple(p.extrusion) + terms = _monomial_terms(extrusion,orders) + JacobiPolynomialBasis{D}(T,orders,terms) +end + +# ContraVariantPiolaMap + +struct ContraVariantPiolaMap <: Map end + +function evaluate!( + cache, + ::Broadcasting{typeof(∇)}, + a::Fields.BroadcastOpFieldArray{ContraVariantPiolaMap} +) + v, Jt, sign_flip = a.args + ∇v = Broadcasting(∇)(v) + k = ContraVariantPiolaMap() + Broadcasting(Operation(k))(∇v,Jt,sign_flip) +end + +function lazy_map( + ::Broadcasting{typeof(gradient)}, + a::LazyArray{<:Fill{Broadcasting{Operation{ContraVariantPiolaMap}}}} +) + v, Jt, sign_flip = a.args + ∇v = lazy_map(Broadcasting(∇),v) + k = ContraVariantPiolaMap() + lazy_map(Broadcasting(Operation(k)),∇v,Jt,sign_flip) +end + +function lazy_map( + k::ContraVariantPiolaMap, + cell_ref_shapefuns::AbstractArray{<:AbstractArray{<:Field}}, + cell_map::AbstractArray{<:Field}, + sign_flip::AbstractArray{<:AbstractArray{<:Field}} +) + cell_Jt = lazy_map(∇,cell_map) + lazy_map(Broadcasting(Operation(k)),cell_ref_shapefuns,cell_Jt,sign_flip) +end + +function evaluate!( + cache,::ContraVariantPiolaMap, + v::Number, + Jt::Number, + sign_flip::Bool +) + idetJ = 1/meas(Jt) + ((-1)^sign_flip*v)⋅(idetJ*Jt) +end + +function evaluate!( + cache, + k::ContraVariantPiolaMap, + v::AbstractVector{<:Field}, + phi::Field, + sign_flip::AbstractVector{<:Field} +) + Jt = ∇(phi) + Broadcasting(Operation(k))(v,Jt,sign_flip) +end diff --git a/src/ReferenceFEs/MomentBasedReferenceFEs.jl b/src/ReferenceFEs/MomentBasedReferenceFEs.jl index 6e76f8b7d..fb3a16066 100644 --- a/src/ReferenceFEs/MomentBasedReferenceFEs.jl +++ b/src/ReferenceFEs/MomentBasedReferenceFEs.jl @@ -1,22 +1,308 @@ +# MomentBasedDofBasis + +struct Moment <: Dof end + +struct MomentBasedDofBasis{P,V} <: AbstractVector{Moment} + nodes::Vector{P} + face_moments::Vector{Array{V}} + face_nodes::Vector{UnitRange{Int}} + + function MomentBasedDofBasis(nodes,f_moments,f_nodes) + P = eltype(nodes) + V = eltype(eltype(f_moments)) + new{P,V}(nodes,f_moments,f_nodes) + end + + function MomentBasedDofBasis(f_nodes,f_moments) + P = eltype(eltype(f_nodes)) + V = eltype(eltype(f_moments)) + nodes = P[] + face_nodes = UnitRange{Int}[] + nfaces = length(f_nodes) + n = 1 + for fi in 1:nfaces + nodes_fi = f_nodes[fi] + nini = n + for node_fi in nodes_fi + push!(nodes,node_fi) + n += 1 + end + nend = n-1 + push!(face_nodes,nini:nend) + end + new{P,V}(nodes,f_moments,face_nodes) + end +end + +Base.size(a::MomentBasedDofBasis) = (length(a.nodes),) +Base.axes(a::MomentBasedDofBasis) = (axes(a.nodes,1),) +Base.getindex(a::MomentBasedDofBasis,i::Integer) = Moment() +Base.IndexStyle(::MomentBasedDofBasis) = IndexLinear() + +get_nodes(b::MomentBasedDofBasis) = b.nodes +get_face_moments(b::MomentBasedDofBasis) = b.face_moments +get_face_nodes_dofs(b::MomentBasedDofBasis) = b.face_nodes + +function num_dofs(b::MomentBasedDofBasis) + n = 0 + for m in b.face_moments + n += size(m,2) + end + n +end + +function return_cache(b::MomentBasedDofBasis{P,V},field) where {P,V} + alloc_cache(vals::AbstractVector,T,ndofs) = zeros(T,ndofs) + alloc_cache(vals::AbstractMatrix,T,ndofs) = zeros(T,ndofs,size(vals,2)) + cf = return_cache(field,b.nodes) + vals = evaluate!(cf,field,b.nodes) + T = typeof(dot(zero(V),zero(eltype(vals)))) + r = alloc_cache(vals,T,num_dofs(b)) + c = CachedArray(r) + (c, cf) +end + +function evaluate!(cache,b::MomentBasedDofBasis,field) + c, cf = cache + vals = evaluate!(cf,field,b.nodes) + dofs = c.array + _eval_moment_dof_basis!(dofs,vals,b) + dofs +end + +function _eval_moment_dof_basis!(dofs,vals::AbstractVector,b) + o = 1 + z = zero(eltype(dofs)) + face_nodes = b.face_nodes + face_moments = b.face_moments + for face in eachindex(face_moments) + moments = face_moments[face] + if !iszero(length(moments)) + nodes = face_nodes[face] + ni,nj = size(moments) + for j in 1:nj + dofs[o] = z + for i in 1:ni + dofs[o] += moments[i,j]⋅vals[nodes[i]] + end + o += 1 + end + end + end +end + +function _eval_moment_dof_basis!(dofs,vals::AbstractMatrix,b) + o = 1 + na = size(vals,2) + z = zero(eltype(dofs)) + face_nodes = b.face_nodes + face_moments = b.face_moments + for face in eachindex(face_moments) + moments = face_moments[face] + if !iszero(length(moments)) + nodes = face_nodes[face] + ni,nj = size(moments) + for j in 1:nj + for a in 1:na + dofs[o,a] = z + for i in 1:ni + dofs[o,a] += moments[i,j]⋅vals[nodes[i],a] + end + end + o += 1 + end + end + end +end + +# MomentBasedReferenceFE + +mutable struct FaceMeasure{Df,Dc} + face ::Int + cpoly::Polytope{Dc} + fpoly::Polytope{Df} + quad ::Quadrature + fmaps::Vector{<:Field} + function FaceMeasure( + cpoly::Polytope{Dc},fpoly::Polytope{Df},order::Int + ) where {Df,Dc} + # Quadrature on the face + quad = Quadrature(fpoly,order) + # Face to cell coordinate map + if Df == Dc + fmaps = [GenericField(identity)] + else # TODO: Could this be an AffineMap? + fcoords = get_face_coordinates(cpoly,Df) + basis = get_shapefuns(LagrangianRefFE(Float64,fpoly,1)) + fmaps = map(c -> linear_combination(c,basis),fcoords) + end + new{Df,Dc}(1,cpoly,fpoly,quad,fmaps) + end +end + +function set_face!(m::FaceMeasure{Df},face::Int) where {Df} + @assert 0 < face <= num_faces(m.cpoly, Df) + m.face = face + return m +end + +# TODO: Normals are accesed, but tangent are computed on demand. This means +# that we will be repeating work unless we cache them. +function get_facet_normal(m::FaceMeasure{Df,Dc}) where {Df,Dc} + @assert Df == Dc - 1 + n = get_facet_normal(m.cpoly) + return ConstantField(n[m.face]) +end + +function get_edge_tangent(m::FaceMeasure{1,Dc}) where {Dc} + t = get_edge_tangent(m.cpoly) + return ConstantField(t[m.face]) +end + +function get_extension(m::FaceMeasure{Df,Dc}) where {Df,Dc} + @assert Df == Dc - 1 + vs = ReferenceFEs._nfaces_vertices(Float64,m.cpoly,Df)[m.face] + return ConstantField(TensorValue(hcat([vs[2]-vs[1]...],[vs[3]-vs[1]...]))) +end + +function Arrays.return_cache( + σ::Function, # σ(φ,μ,ds) -> Field/Array{Field} + φ::AbstractArray{<:Field}, # φ: prebasis (defined on the cell) + μ::AbstractArray{<:Field}, # μ: polynomial basis (defined on the face) + ds::FaceMeasure # ds: face measure +) + fmap = ds.fmaps[ds.face] + φf = transpose(Broadcasting(Operation(∘))(φ,fmap)) + f = σ(φf,μ,ds) + + xf = get_coordinates(ds.quad) + w = get_weights(ds.quad) + fmap_cache = return_cache(fmap,xf) + + f_cache = return_cache(f,xf) + return fmap_cache, f_cache, xf, w +end + +function Arrays.evaluate!( + cache, + σ::Function, # σ(φ,μ,ds) -> Field/Array{Field} + φ::AbstractArray{<:Field}, # φ: prebasis (defined on the cell) + μ::AbstractArray{<:Field}, # μ: polynomial basis (defined on the face) + ds::FaceMeasure # ds: face measure +) + fmap_cache, f_cache, xf, w = cache + + fmap = ds.fmaps[ds.face] + φf = transpose(Broadcasting(Operation(∘))(φ,fmap)) + f = σ(φf,μ,ds) + + xc = evaluate!(fmap_cache,fmap,xf) # quad pts on the cell + fx = evaluate!(f_cache,f,xf) # f evaluated on the quad pts + fx .= w .* fx + return fx, xc +end + +component_basis(T::Type{<:Real}) = [one(T)] +function component_basis(V::Type{<:MultiValue}) + T = eltype(V) + n = num_components(V) + z, o = zero(T), one(T) + return [V(ntuple(i -> ifelse(i == j, o, z),Val(n))) for j in 1:n] +end + """ -For each moment, I need: - - its dimension - - the basis we are integrating against - - a function (φ,ϕ) -> the functional to integrate - - a face filter/mask +A moment is given by a triplet (f,σ,μ) where + - f is vector of ids if faces Fk + - σ is a function σ(φ,μ,ds) that returns a Field-like object to be integrated over each Fk + - μ is a polynomials basis on Fk + +We are assuming that all the faces in a moment are of the same type. """ function MomentBasedReferenceFE( + name::ReferenceFEName, p::Polytope{D}, - face_moments::AbstractVector{<:AbstractVector{<:Function}}, - face_basis::AbstractVector{<:AbstractVector{<:AbstractVector{<:Field}}}; - face_mask::AbstractVector{<:AbstractVector{Bool}} = [fill(true,num_faces(p, d)) for d in 0:D], + prebasis::AbstractVector{<:Field}, + moments::AbstractVector{<:Tuple{Vector{Int},<:Function,<:AbstractArray{<:Field}}}, + conformity::Conformity; ) where D - @assert length(face_moments) == length(face_mask) == length(face_basis) == D+1 - @assert all(length(face_moments[d+1]) == length(face_basis[d+1]) for d in 0:D) - @assert all(length(face_mask[d+1]) == num_faces(p, d) for d in 0:D) -end + n_faces = num_faces(p) + n_moments = length(moments) + face_dims = get_facedims(p) + face_offsets = get_offsets(p) + reffaces, face_types = _compute_reffaces_and_face_types(p) + + T = return_type(prebasis) + order = get_order(prebasis) + φ_vec = component_basis(T) + φ = map(constant_field,φ_vec) + # Create face measures for each moment + measures = Vector{FaceMeasure}(undef,n_moments) + for (k,(faces,σ,μ)) in enumerate(moments) + ftype = face_types[first(faces)] + @assert all(face_types[faces] .== ftype) + qdegree = order + get_order(μ) + 1 + fp = reffaces[ftype] + measures[k] = FaceMeasure(p,fp,qdegree) + end + # Count number of dofs and quad pts per face + face_n_dofs = zeros(Int,n_faces) + face_n_nodes = zeros(Int,n_faces) + for ((faces,σ,μ),ds) in zip(moments,measures) + face_n_dofs[faces] += size(μ,2) + face_n_nodes[faces] += num_points(ds.quad) + end + + # Compute face moment and node indices + n_dofs = 1 + n_nodes = 1 + face_own_dofs = Vector{Vector{Int}}(undef,n_faces) + face_nodes = Vector{UnitRange{Int}}(undef, n_faces) + face_moments = Vector{Array{T}}(undef, n_faces) + for face in 1:n_faces + n_dofs_i = face_n_dofs[face] + n_nodes_i = face_n_nodes[face] + face_own_dofs[face] = collect(n_dofs:(n_dofs+n_dofs_i-1)) + face_nodes[face] = n_nodes:(n_nodes+n_nodes_i-1) + face_moments[face] = zeros(T,n_nodes_i,n_dofs_i) + n_dofs += n_dofs_i + n_nodes += n_nodes_i + end + nodes = Vector{Point{D}}(undef,n_nodes) + # Compute face moments and nodes + fill!(face_n_dofs,0) + fill!(face_n_nodes,0) + for ((faces,σ,μ),ds) in zip(moments,measures) + cache = return_cache(σ,φ,μ,ds) + for face in faces + d = face_dims[face] + lface = face - face_offsets[d] + set_face!(ds,lface) + + # vals : (nN, nμ, nφ), coords : (nN) + vals, coords = evaluate!(cache,σ,φ,μ,ds) + + dof_offset = face_n_dofs[face] + node_offset = first(face_nodes[face]) + face_n_nodes[face] + for i in axes(vals,1) + for j in axes(vals,2) + face_moments[face][i,j+dof_offset] = dot(vals[i,j,:],φ_vec) + end + nodes[i+node_offset] = coords[i] + end + + face_n_nodes[face] += size(vals,1) + face_n_dofs[face] += size(vals,2) + end + end + + dof_basis = MomentBasedDofBasis(nodes, face_moments, face_nodes) + metadata = nothing + return GenericRefFE{typeof(name)}( + n_dofs, p, prebasis, dof_basis, conformity, metadata, face_own_dofs + ) +end diff --git a/src/ReferenceFEs/ReferenceFEs.jl b/src/ReferenceFEs/ReferenceFEs.jl index 24cf3d310..2ffb605f4 100644 --- a/src/ReferenceFEs/ReferenceFEs.jl +++ b/src/ReferenceFEs/ReferenceFEs.jl @@ -242,7 +242,9 @@ include("DuffyQuadratures.jl") include("StrangQuadratures.jl") -include("RaviartThomasRefFEs.jl") +include("MomentBasedReferenceFEs.jl") + +include("DivConformingReferenceFEs.jl") include("BDMRefFEs.jl") diff --git a/src/ReferenceFEs/RaviartThomasRefFEs.jl b/src/ReferenceFEs/deprecated/RaviartThomasRefFEs.jl similarity index 100% rename from src/ReferenceFEs/RaviartThomasRefFEs.jl rename to src/ReferenceFEs/deprecated/RaviartThomasRefFEs.jl diff --git a/test/moment_based_reffes.jl b/test/moment_based_reffes.jl index ce5dec6aa..dcfc20462 100644 --- a/test/moment_based_reffes.jl +++ b/test/moment_based_reffes.jl @@ -54,6 +54,19 @@ end Arrays.return_type(::Polynomials.QGradMonomialBasis{D,T}) where {D,T} = T +############################################################################################ + +# Doesnt work... +# function Arrays.return_value(k::Broadcasting{<:typeof(∘)},args::Union{Field,AbstractArray{<:Field}}...) +# f, g = args +# Fields.BroadcastOpFieldArray(f,g) +# end +# +# function Arrays.evaluate!(cache,k::Broadcasting{<:typeof(∘)},args::Union{Field,AbstractArray{<:Field}}...) +# f, g = args +# Fields.BroadcastOpFieldArray(f,g) +# end + ############################################################################################ # FaceMeasure @@ -295,3 +308,5 @@ nd_dofs = get_dof_basis(nd_reffe) Mnd = evaluate(nd_dofs,prebasis) M = evaluate(dofs,prebasis) M == Mnd + +############################################################################################ From dfdb7e4c6ee240240b4eb0e4ca19b39a8941e1ef Mon Sep 17 00:00:00 2001 From: JordiManyer Date: Sat, 16 Nov 2024 21:02:03 +1100 Subject: [PATCH 023/105] Ported Nedelec and BDM --- src/ReferenceFEs/BDMRefFEs.jl | 167 +------- src/ReferenceFEs/Dofs.jl | 83 ---- src/ReferenceFEs/MomentBasedReferenceFEs.jl | 2 +- src/ReferenceFEs/NedelecRefFEs.jl | 307 +++------------ ...ReferenceFEs.jl => RaviartThomasRefFEs.jl} | 6 +- src/ReferenceFEs/deprecated/BDMRefFEs.jl | 192 +++++++++ src/ReferenceFEs/deprecated/NedelecRefFEs.jl | 368 ++++++++++++++++++ 7 files changed, 631 insertions(+), 494 deletions(-) rename src/ReferenceFEs/{DivConformingReferenceFEs.jl => RaviartThomasRefFEs.jl} (93%) create mode 100644 src/ReferenceFEs/deprecated/BDMRefFEs.jl create mode 100644 src/ReferenceFEs/deprecated/NedelecRefFEs.jl diff --git a/src/ReferenceFEs/BDMRefFEs.jl b/src/ReferenceFEs/BDMRefFEs.jl index 81d3bd9af..65df8d8c4 100644 --- a/src/ReferenceFEs/BDMRefFEs.jl +++ b/src/ReferenceFEs/BDMRefFEs.jl @@ -10,39 +10,31 @@ is in the P space of degree `order-1`. """ function BDMRefFE(::Type{et},p::Polytope,order::Integer) where et - D = num_dims(p) - - vet = VectorValue{num_dims(p),et} + vet = VectorValue{D,et} if is_simplex(p) prebasis = MonomialBasis(vet,p,order) + fb = MonomialBasis{D-1}(et,order,Polynomials._p_filter) + cb = Polynomials.NedelecPrebasisOnSimplex{D}(order-2) else @notimplemented "BDM Reference FE only available for simplices" end - nf_nodes, nf_moments = _BDM_nodes_and_moments(et,p,order,GenericField(identity)) - - face_own_dofs = _face_own_dofs_from_moments(nf_moments) - - face_dofs = face_own_dofs - - dof_basis = MomentBasedDofBasis(nf_nodes, nf_moments) - - ndofs = num_dofs(dof_basis) - - metadata = nothing - - reffe = GenericRefFE{BDM}( - ndofs, - p, - prebasis, - dof_basis, - DivConformity(), - metadata, - face_dofs) + function cmom(φ,μ,ds) # Cell moment function: σ_K(φ,μ) = ∫(φ·μ)dK + Broadcasting(Operation(⋅))(φ,μ) + end + function fmom(φ,μ,ds) # Face moment function : σ_F(φ,μ) = ∫((φ·n)*μ)dF + n = get_normal(ds) + φn = Broadcasting(Operation(⋅))(φ,n) + Broadcasting(Operation(*))(φn,μ) + end + moments = [ + (get_dimrange(p,D-1),fmom,fb), # Face moments + (get_dimrange(p,D),cmom,cb) # Cell moments + ] - reffe + return MomentBasedReferenceFE(BDM(),p,prebasis,moments,DivConformity()) end function ReferenceFE(p::Polytope,::BDM, order) @@ -65,128 +57,9 @@ function Conformity(reffe::GenericRefFE{BDM},sym::Symbol) Possible values of conformity for this reference fe are $((:L2, hdiv...)). """ - end - end - - function get_face_own_dofs(reffe::GenericRefFE{BDM}, conf::DivConformity) - get_face_dofs(reffe) end +end - function _BDM_nodes_and_moments(::Type{et}, p::Polytope, order::Integer, phi::Field) where et - - D = num_dims(p) - ft = VectorValue{D,et} - pt = Point{D,et} - - nf_nodes = [ zeros(pt,0) for face in 1:num_faces(p)] - nf_moments = [ zeros(ft,0,0) for face in 1:num_faces(p)] - - fcips, fmoments = _BDM_face_values(p,et,order,phi) - frange = get_dimrange(p,D-1) - nf_nodes[frange] = fcips - nf_moments[frange] = fmoments - - if (order > 1) - ccips, cmoments = _BDM_cell_values(p,et,order,phi) - crange = get_dimrange(p,D) - nf_nodes[crange] = ccips - nf_moments[crange] = cmoments - end - - nf_nodes, nf_moments - end - - function _BDM_face_moments(p, fshfs, c_fips, fcips, fwips,phi) - nc = length(c_fips) - cfshfs = fill(fshfs, nc) - cvals = lazy_map(evaluate,cfshfs,c_fips) - cvals = [fwips[i].*cvals[i] for i in 1:nc] - fns = get_facet_normal(p) - - # Must express the normal in terms of the real/reference system of - # coordinates (depending if phi≡I or phi is a mapping, resp.) - # Hence, J = transpose(grad(phi)) - - Jt = fill(∇(phi),nc) - Jt_inv = lazy_map(Operation(pinvJt),Jt) - det_Jt = lazy_map(Operation(meas),Jt) - change = lazy_map(*,det_Jt,Jt_inv) - change_ips = lazy_map(evaluate,change,fcips) - - cvals = [ _broadcast(typeof(n),n,J.*b) for (n,b,J) in zip(fns,cvals,change_ips)] - - return cvals - end - - # It provides for every face the nodes and the moments arrays - function _BDM_face_values(p,et,order,phi) - - # Reference facet - @check is_simplex(p) "We are assuming that all n-faces of the same n-dim are the same." - fp = Polytope{num_dims(p)-1}(p,1) - - # geomap from ref face to polytope faces - fgeomap = _ref_face_to_faces_geomap(p,fp) - - # Nodes are integration points (for exact integration) - # Thus, we define the integration points in the reference - # face polytope (fips and wips). Next, we consider the - # n-face-wise arrays of nodes in fp (constant cell array c_fips) - # the one of the points in the polytope after applying the geopmap - # (fcips), and the weights for these nodes (fwips, a constant cell array) - # Nodes (fcips) - degree = (order)*2 - fquad = Quadrature(fp,degree) - fips = get_coordinates(fquad) - wips = get_weights(fquad) - - c_fips, fcips, fwips = _nfaces_evaluation_points_weights(p, fgeomap, fips, wips) - - # Moments (fmoments) - # The BDM prebasis is expressed in terms of shape function - fshfs = MonomialBasis(et,fp,order) - - # Face moments, i.e., M(Fi)_{ab} = q_RF^a(xgp_RFi^b) w_Fi^b n_Fi ⋅ () - fmoments = _BDM_face_moments(p, fshfs, c_fips, fcips, fwips, phi) - - return fcips, fmoments - - end - - function _BDM_cell_moments(p, cbasis, ccips, cwips) - # Interior DOFs-related basis evaluated at interior integration points - ishfs_iips = evaluate(cbasis,ccips) - return cwips.⋅ishfs_iips - end - - # It provides for every cell the nodes and the moments arrays - function _BDM_cell_values(p,et,order,phi) - # Compute integration points at interior - degree = 2*(order) - iquad = Quadrature(p,degree) - ccips = get_coordinates(iquad) - cwips = get_weights(iquad) - - # Cell moments, i.e., M(C)_{ab} = q_C^a(xgp_C^b) w_C^b ⋅ () - if is_simplex(p) - T = VectorValue{num_dims(p),et} - # cbasis = GradMonomialBasis{num_dims(p)}(T,order-1) - cbasis = Polynomials.NedelecPrebasisOnSimplex{num_dims(p)}(order-2) - else - @notimplemented - end - cell_moments = _BDM_cell_moments(p, cbasis, ccips, cwips ) - - # Must scale weights using phi map to get the correct integrals - # scaling = meas(grad(phi)) - Jt = ∇(phi) - Jt_inv = pinvJt(Jt) - det_Jt = meas(Jt) - change = det_Jt*Jt_inv - change_ips = evaluate(change,ccips) - - cmoments = change_ips.⋅cell_moments - - return [ccips], [cmoments] - - end +function get_face_own_dofs(reffe::GenericRefFE{BDM}, conf::DivConformity) + get_face_dofs(reffe) +end diff --git a/src/ReferenceFEs/Dofs.jl b/src/ReferenceFEs/Dofs.jl index 75201f9b0..cee46b14e 100644 --- a/src/ReferenceFEs/Dofs.jl +++ b/src/ReferenceFEs/Dofs.jl @@ -1,74 +1,5 @@ abstract type Dof <: Map end -# """ -# abstract type Dof <: Map - -# Abstract type representing a degree of freedom (DOF), a basis of DOFs, and related objects. -# These different cases are distinguished by the return type obtained when evaluating the `Dof` -# object on a `Field` object. See function [`evaluate_dof!`](@ref) for more details. - -# The following functions needs to be overloaded - -# - [`dof_cache`](@ref) -# - [`evaluate_dof!`](@ref) - -# The following functions can be overloaded optionally - -# - [`dof_return_type`](@ref) - -# The interface is tested with - -# - [`test_dof`](@ref) - -# In most of the cases it is not strictly needed that types that implement this interface -# inherit from `Dof`. However, we recommend to inherit from `Dof`, when possible. - - -# """ -# abstract type Dof <: Map end - -# """ -# return_cache(dof,field) - -# Returns the cache needed to call `evaluate_dof!(cache,dof,field)` -# """ -# function return_cache(dof::Dof,field) -# @abstractmethod -# end - -# """ -# evaluate_dof!(cache,dof,field) - -# Evaluates the dof `dof` with the field `field`. It can return either an scalar value or -# an array of scalar values depending the case. The `cache` object is computed with function -# [`dof_cache`](@ref). - -# When a mathematical dof is evaluated on a physical field, a scalar number is returned. If either -# the `Dof` object is a basis of DOFs, or the `Field` object is a basis of fields, -# or both objects are bases, then the returned object is an array of scalar numbers. The first -# dimensions in the resulting array are for the `Dof` object and the last ones for the `Field` -# object. E.g, a basis of `nd` DOFs evaluated at physical field returns a vector of `nd` entries. -# A basis of `nd` DOFs evaluated at a basis of `nf` fields returns a matrix of size `(nd,nf)`. -# """ -# function evaluate!(cache,dof::Dof,field) -# @abstractmethod -# end - -# """ -# dof_return_type(dof,field) - -# Returns the type for the value obtained with evaluating `dof` with `field`. - -# It defaults to - -# typeof(evaluate_dof(dof,field)) -# """ -# function return_type(dof::Dof,field) -# typeof(evaluate(dof,field)) -# end - -# Testers - """ test_dof(dof,field,v;cmp::Function=(==)) @@ -93,17 +24,3 @@ function _test_dof(dof,field,v,cmp) @test cmp(r,v) @test typeof(r) == return_type(dof,field) end - -#struct DofEval <: Map end -# -#function return_cache(k::DofEval,dof,field) -# return_cache(dof,field) -#end -# -#function evaluate!(cache,k::DofEval,dof,field) -# evaluate!(cache,dof,field) -#end -# -#function return_type(k::DofEval,dof,field) -# return_type(dof,field) -#end diff --git a/src/ReferenceFEs/MomentBasedReferenceFEs.jl b/src/ReferenceFEs/MomentBasedReferenceFEs.jl index fb3a16066..ab407a003 100644 --- a/src/ReferenceFEs/MomentBasedReferenceFEs.jl +++ b/src/ReferenceFEs/MomentBasedReferenceFEs.jl @@ -213,7 +213,7 @@ end """ A moment is given by a triplet (f,σ,μ) where - - f is vector of ids if faces Fk + - f is vector of ids of faces Fk - σ is a function σ(φ,μ,ds) that returns a Field-like object to be integrated over each Fk - μ is a polynomials basis on Fk diff --git a/src/ReferenceFEs/NedelecRefFEs.jl b/src/ReferenceFEs/NedelecRefFEs.jl index 81924f856..2b5120fcc 100644 --- a/src/ReferenceFEs/NedelecRefFEs.jl +++ b/src/ReferenceFEs/NedelecRefFEs.jl @@ -1,7 +1,7 @@ + struct CurlConformity <: Conformity end struct Nedelec <: ReferenceFEName end - const nedelec = Nedelec() """ @@ -11,40 +11,54 @@ The `order` argument has the following meaning: the curl of the functions in th is in the Q space of degree `order`. """ function NedelecRefFE(::Type{et},p::Polytope,order::Integer) where et - - # @santiagobadia : Project, go to complex numbers D = num_dims(p) if is_n_cube(p) - prebasis = QGradMonomialBasis{D}(et,order) + prebasis = QGradMonomialBasis{D}(et,order) # Prebasis + eb = MonomialBasis{1}(et,order) # Edge basis + fb = QGradMonomialBasis{D-1}(et,order-1) # Face basis + cb = QCurlGradMonomialBasis{D}(et,order-1) # Cell basis elseif is_simplex(p) - prebasis = Polynomials.NedelecPrebasisOnSimplex{D}(order) + prebasis = Polynomials.NedelecPrebasisOnSimplex{D}(order) # Prebasis + eb = MonomialBasis{1}(et,order) # Edge basis + fb = MonomialBasis{D-1}(VectorValue{D-1,et},order-1,Polynomials._p_filter) # Face basis + cb = MonomialBasis{D}(VectorValue{D,et},order-D+1,Polynomials._p_filter) # Cell basis else - @unreachable "Only implemented for n-cubes and simplices" + @unreachable "Nedelec Reference FE only implemented for n-cubes and simplices" end - nf_nodes, nf_moments = _Nedelec_nodes_and_moments(et,p,order) - - face_own_dofs = _face_own_dofs_from_moments(nf_moments) - - face_dofs = face_own_dofs - - dof_basis = MomentBasedDofBasis(nf_nodes, nf_moments) - - ndofs = num_dofs(dof_basis) - - metadata = nothing + function cmom(φ,μ,ds) # Cell moment function: σ_K(φ,μ) = ∫(φ⋅μ)dK + Broadcasting(Operation(⋅))(φ,μ) + end + function fmom(φ,μ,ds) # Face moment function: σ_F(φ,μ) = ∫((φ×n)⋅μ)dF + o = get_facet_orientations(ds.poly)[ds.face] # This is a hack to avoid a sign map + n = o*get_normal(ds) + E = get_extension(ds) + Eμ = Broadcasting(Operation(⋅))(E,μ) # We have to extend the basis to 3D + φn = Broadcasting(Operation(×))(n,φ) + Broadcasting(Operation(⋅))(φn,Eμ) + end + function emom(φ,μ,ds) # Edge moment function: σ_E(φ,μ) = ∫((φ⋅t)*μ)dE + t = get_tangent(ds) + φt = Broadcasting(Operation(⋅))(φ,t) + Broadcasting(Operation(*))(φt,μ) + end - reffe = GenericRefFE{Nedelec}( - ndofs, - p, - prebasis, - dof_basis, - CurlConformity(), - metadata, - face_dofs) + if D == 2 + moments = [ # In 2D we do not have face moments + (get_dimrange(p,1),emom,eb), # Edge moments + (get_dimrange(p,D),cmom,cb) # Cell moments + ] + else + @assert D == 3 + moments = [ + (get_dimrange(p,1),emom,eb), # Edge moments + (get_dimrange(p,D-1),fmom,fb), # Face moments + (get_dimrange(p,D),cmom,cb) # Cell moments + ] + end - reffe + return MomentBasedReferenceFE(Nedelec(),p,prebasis,moments,CurlConformity()) end function ReferenceFE(p::Polytope,::Nedelec, order) @@ -70,31 +84,25 @@ function Conformity(reffe::GenericRefFE{Nedelec},sym::Symbol) end end -function get_face_own_dofs(reffe::GenericRefFE{Nedelec}, conf::CurlConformity) +function get_face_own_dofs(reffe::GenericRefFE{Nedelec}, ::CurlConformity) reffe.face_dofs # For Nedelec, this member variable holds the face owned dofs end -function get_face_own_dofs(reffe::GenericRefFE{Nedelec}, conf::L2Conformity) - face_own_dofs=[Int[] for i in 1:num_faces(reffe)] - face_own_dofs[end]=collect(1:num_dofs(reffe)) - face_own_dofs -end - function get_face_dofs(reffe::GenericRefFE{Nedelec,Dc}) where Dc - face_dofs=[Int[] for i in 1:num_faces(reffe)] - face_own_dofs=get_face_own_dofs(reffe) + face_dofs = [Int[] for i in 1:num_faces(reffe)] + face_own_dofs = get_face_own_dofs(reffe) p = get_polytope(reffe) - for d=1:Dc # Starting from edges, vertices do not own DoFs for Nedelec + for d = 1:Dc # Starting from edges, vertices do not own DoFs for Nedelec first_face = get_offset(p,d) nfaces = num_faces(reffe,d) - for face=first_face+1:first_face+nfaces - for df=1:d-1 + for face = first_face+1:first_face+nfaces + for df = 1:d-1 face_faces = get_faces(p,d,df) first_cface = get_offset(p,df) for cface in face_faces[face-first_face] cface_own_dofs = face_own_dofs[first_cface+cface] for dof in cface_own_dofs - push!(face_dofs[face],dof) + push!(face_dofs[face],dof) end end end @@ -106,227 +114,6 @@ function get_face_dofs(reffe::GenericRefFE{Nedelec,Dc}) where Dc face_dofs end - -function _Nedelec_nodes_and_moments(::Type{et}, p::Polytope, order::Integer) where et - - @notimplementedif !( is_n_cube(p) || (is_simplex(p) ) ) - - D = num_dims(p) - ft = VectorValue{D,et} - pt = Point{D,et} - - nf_nodes = [ zeros(pt,0) for face in 1:num_faces(p)] - nf_moments = [ zeros(ft,0,0) for face in 1:num_faces(p)] - - ecips, emoments = _Nedelec_edge_values(p,et,order) - erange = get_dimrange(p,1) - nf_nodes[erange] = ecips - nf_moments[erange] = emoments - - if ( num_dims(p) == 3 && order > 0) - - if is_n_cube(p) - fcips, fmoments = _Nedelec_face_values(p,et,order) - else - fcips, fmoments = _Nedelec_face_values_simplex(p,et,order) - end - - frange = get_dimrange(p,D-1) - nf_nodes[frange] = fcips - nf_moments[frange] = fmoments - - end - - if ( is_n_cube(p) && order > 0) || ( is_simplex(p) && order > D-2) - - ccips, cmoments = _Nedelec_cell_values(p,et,order) - crange = get_dimrange(p,D) - nf_nodes[crange] = ccips - nf_moments[crange] = cmoments - - end - - nf_nodes, nf_moments -end - -function _Nedelec_edge_values(p,et,order) - - # Reference facet - dim1 = 1 - ep = Polytope{dim1}(p,1) - - # geomap from ref face to polytope faces - egeomap = _ref_face_to_faces_geomap(p,ep) - - # Compute integration points at all polynomial edges - degree = (order)*2 - equad = Quadrature(ep,degree) - cips = get_coordinates(equad) - wips = get_weights(equad) - - - c_eips, ecips, ewips = _nfaces_evaluation_points_weights(p, egeomap, cips, wips) - - # Edge moments, i.e., M(Ei)_{ab} = q_RE^a(xgp_REi^b) w_Fi^b t_Ei ⋅ () - eshfs = MonomialBasis(et,ep,order) - emoments = _Nedelec_edge_moments(p, eshfs, c_eips, ecips, ewips) - - return ecips, emoments - -end - -function _Nedelec_edge_moments(p, fshfs, c_fips, fcips, fwips) - ts = get_edge_tangent(p) - nc = length(c_fips) - cfshfs = fill(fshfs, nc) - cvals = lazy_map(evaluate,cfshfs,c_fips) - cvals = [fwips[i].*cvals[i] for i in 1:nc] - # @santiagobadia : Only working for oriented meshes now - cvals = [ _broadcast(typeof(t),t,b) for (t,b) in zip(ts,cvals)] - return cvals -end - -function _Nedelec_face_values(p,et,order) - - # Reference facet - @assert is_n_cube(p) "We are assuming that all n-faces of the same n-dim are the same." - fp = Polytope{num_dims(p)-1}(p,1) - - # geomap from ref face to polytope faces - fgeomap = _ref_face_to_faces_geomap(p,fp) - - # Compute integration points at all polynomial edges - degree = (order)*2 - fquad = Quadrature(fp,degree) - fips = get_coordinates(fquad) - wips = get_weights(fquad) - - c_fips, fcips, fwips = _nfaces_evaluation_points_weights(p, fgeomap, fips, wips) - - # Face moments, i.e., M(Fi)_{ab} = w_Fi^b q_RF^a(xgp_RFi^b) (n_Fi × ()) - fshfs = QGradMonomialBasis{num_dims(fp)}(et,order-1) - - fmoments = _Nedelec_face_moments(p, fshfs, c_fips, fcips, fwips) - - return fcips, fmoments - -end - -function _Nedelec_face_moments(p, fshfs, c_fips, fcips, fwips) - nc = length(c_fips) - cfshfs = fill(fshfs, nc) - cvals = lazy_map(evaluate,cfshfs,c_fips) - - fvs = _nfaces_vertices(Float64,p,num_dims(p)-1) - fts = [hcat([vs[2]-vs[1]...],[vs[3]-vs[1]...]) for vs in fvs] - - # Ref facet FE functions evaluated at the facet integration points (in ref facet) - cvals = [fwips[i].*cvals[i] for i in 1:nc] - - fns = get_facet_normal(p) - os = get_facet_orientations(p) - # @santiagobadia : Temporary hack for making it work for structured hex meshes - ft = eltype(fns) - cvals = [ _broadcast_extend(ft,Tm,b) for (Tm,b) in zip(fts,cvals)] - cvals = [ _broadcast_cross(ft,n*o,b) for (n,o,b) in zip(fns,os,cvals)] - return cvals -end - -function _Nedelec_face_values_simplex(p,et,order) - - # Reference facet - @assert is_simplex(p) "We are assuming that all n-faces of the same n-dim are the same." - fp = Polytope{num_dims(p)-1}(p,1) - - # geomap from ref face to polytope faces - fgeomap = _ref_face_to_faces_geomap(p,fp) - - # Compute integration points at all polynomial edges - degree = (order)*2 - fquad = Quadrature(fp,degree) - fips = get_coordinates(fquad) - wips = get_weights(fquad) - - c_fips, fcips, fwips, fJtips = _nfaces_evaluation_points_weights_with_jac(p, fgeomap, fips, wips) - - Df = num_dims(fp) - fshfs = MonomialBasis{Df}(VectorValue{Df,et},order-1,(e,k)->sum(e)<=k) - - fmoments = _Nedelec_face_moments_simplex(p, fshfs, c_fips, fcips, fwips, fJtips) - - return fcips, fmoments - -end - -function _nfaces_evaluation_points_weights_with_jac(p, fgeomap, fips, wips) - nc = length(fgeomap) - c_fips = fill(fips,nc) - c_wips = fill(wips,nc) - pquad = lazy_map(evaluate,fgeomap,c_fips) - ## Must account for diagonals in simplex discretizations to get the correct - ## scaling - Jt1 = lazy_map(∇,fgeomap) - Jt1_ips = lazy_map(evaluate,Jt1,c_fips) - #det_J = lazy_map(Broadcasting(meas),Jt1_ips) - #c_detwips = collect(lazy_map(Broadcasting(*),c_wips,det_J)) - c_detwips = c_wips - c_fips, pquad, c_detwips, Jt1_ips -end - -function _Nedelec_face_moments_simplex(p, fshfs, c_fips, fcips, fwips, fJtips) - nc = length(c_fips) - cfshfs = fill(fshfs, nc) - cfshfs_fips = lazy_map(evaluate,cfshfs,c_fips) - function weigth(qij,Jti,wi) - Ji = transpose(Jti) - Ji⋅qij*wi - end - cvals = map(Broadcasting(weigth),cfshfs_fips,fJtips,fwips) - return cvals -end - -# It provides for every cell the nodes and the moments arrays -function _Nedelec_cell_values(p,et,order) - - # Compute integration points at interior - degree = 2*(order) - iquad = Quadrature(p,degree) - ccips = get_coordinates(iquad) - cwips = get_weights(iquad) - - # Cell moments, i.e., M(C)_{ab} = q_C^a(xgp_C^b) w_C^b ⋅ () - if is_n_cube(p) - cbasis = QCurlGradMonomialBasis{num_dims(p)}(et,order-1) - else - D = num_dims(p) - cbasis = MonomialBasis{D}(VectorValue{D,et},order-D+1,(e,k)->sum(e)<=k) - end - cmoments = _Nedelec_cell_moments(p, cbasis, ccips, cwips ) - - return [ccips], [cmoments] - -end - -const _Nedelec_cell_moments = _RT_cell_moments - -function _broadcast_cross(::Type{T},n,b) where T - c = Array{T}(undef,size(b)) - for (ii, i) in enumerate(b) - c[ii] = T(cross(get_array(i),get_array(n)))# cross product - end - return c -end - -# Moves bi values from 2D to 3D, by multiplying by a 3x2 matrix Tm -# Tm = [1.0 0.0; 0.0 1.0; 0.0 0.0] for instance -function _broadcast_extend(::Type{T},Tm,b) where T - c = Array{T}(undef,size(b)) - for (ii,i) in enumerate(b) - c[ii] = T(Tm*[i...]) - end - return c -end - struct CoVariantPiolaMap <: Map end function evaluate!( diff --git a/src/ReferenceFEs/DivConformingReferenceFEs.jl b/src/ReferenceFEs/RaviartThomasRefFEs.jl similarity index 93% rename from src/ReferenceFEs/DivConformingReferenceFEs.jl rename to src/ReferenceFEs/RaviartThomasRefFEs.jl index cd0434374..7437cae22 100644 --- a/src/ReferenceFEs/DivConformingReferenceFEs.jl +++ b/src/ReferenceFEs/RaviartThomasRefFEs.jl @@ -26,13 +26,13 @@ function RaviartThomasRefFE( cb = MonomialBasis{D}(T,order-1,Polynomials._p_filter) # Cell basis fb = JacobiPolynomialBasis{D}(T,order,Polynomials._p_filter) # Face basis else - @notimplemented "H(div) Reference FE only available for cubes and simplices" + @notimplemented "Raviart-Thomas Reference FE only available for cubes and simplices" end - function cmom(φ,μ,ds) # Cell moment function + function cmom(φ,μ,ds) # Cell moment function: σ_K(φ,μ) = ∫(φ·μ)dK Broadcasting(Operation(⋅))(φ,μ) end - function fmom(φ,μ,ds) # Face moment function + function fmom(φ,μ,ds) # Face moment function : σ_F(φ,μ) = ∫((φ·n)*μ)dF n = get_normal(ds) φn = Broadcasting(Operation(⋅))(φ,n) Broadcasting(Operation(*))(φn,μ) diff --git a/src/ReferenceFEs/deprecated/BDMRefFEs.jl b/src/ReferenceFEs/deprecated/BDMRefFEs.jl new file mode 100644 index 000000000..81d3bd9af --- /dev/null +++ b/src/ReferenceFEs/deprecated/BDMRefFEs.jl @@ -0,0 +1,192 @@ +struct BDM <: DivConforming end + +const bdm = BDM() + +""" +BDMRefFE(::Type{et},p::Polytope,order::Integer) where et + +The `order` argument has the following meaning: the divergence of the functions in this basis +is in the P space of degree `order-1`. + +""" +function BDMRefFE(::Type{et},p::Polytope,order::Integer) where et + + D = num_dims(p) + + vet = VectorValue{num_dims(p),et} + + if is_simplex(p) + prebasis = MonomialBasis(vet,p,order) + else + @notimplemented "BDM Reference FE only available for simplices" + end + + nf_nodes, nf_moments = _BDM_nodes_and_moments(et,p,order,GenericField(identity)) + + face_own_dofs = _face_own_dofs_from_moments(nf_moments) + + face_dofs = face_own_dofs + + dof_basis = MomentBasedDofBasis(nf_nodes, nf_moments) + + ndofs = num_dofs(dof_basis) + + metadata = nothing + + reffe = GenericRefFE{BDM}( + ndofs, + p, + prebasis, + dof_basis, + DivConformity(), + metadata, + face_dofs) + + reffe +end + +function ReferenceFE(p::Polytope,::BDM, order) + BDMRefFE(Float64,p,order) +end + +function ReferenceFE(p::Polytope,::BDM,::Type{T}, order) where T + BDMRefFE(T,p,order) +end + +function Conformity(reffe::GenericRefFE{BDM},sym::Symbol) + hdiv = (:Hdiv,:HDiv) + if sym == :L2 + L2Conformity() + elseif sym in hdiv + DivConformity() + else + @unreachable """\n + It is not possible to use conformity = $sym on a BDM reference FE. + + Possible values of conformity for this reference fe are $((:L2, hdiv...)). + """ + end + end + + function get_face_own_dofs(reffe::GenericRefFE{BDM}, conf::DivConformity) + get_face_dofs(reffe) + end + + function _BDM_nodes_and_moments(::Type{et}, p::Polytope, order::Integer, phi::Field) where et + + D = num_dims(p) + ft = VectorValue{D,et} + pt = Point{D,et} + + nf_nodes = [ zeros(pt,0) for face in 1:num_faces(p)] + nf_moments = [ zeros(ft,0,0) for face in 1:num_faces(p)] + + fcips, fmoments = _BDM_face_values(p,et,order,phi) + frange = get_dimrange(p,D-1) + nf_nodes[frange] = fcips + nf_moments[frange] = fmoments + + if (order > 1) + ccips, cmoments = _BDM_cell_values(p,et,order,phi) + crange = get_dimrange(p,D) + nf_nodes[crange] = ccips + nf_moments[crange] = cmoments + end + + nf_nodes, nf_moments + end + + function _BDM_face_moments(p, fshfs, c_fips, fcips, fwips,phi) + nc = length(c_fips) + cfshfs = fill(fshfs, nc) + cvals = lazy_map(evaluate,cfshfs,c_fips) + cvals = [fwips[i].*cvals[i] for i in 1:nc] + fns = get_facet_normal(p) + + # Must express the normal in terms of the real/reference system of + # coordinates (depending if phi≡I or phi is a mapping, resp.) + # Hence, J = transpose(grad(phi)) + + Jt = fill(∇(phi),nc) + Jt_inv = lazy_map(Operation(pinvJt),Jt) + det_Jt = lazy_map(Operation(meas),Jt) + change = lazy_map(*,det_Jt,Jt_inv) + change_ips = lazy_map(evaluate,change,fcips) + + cvals = [ _broadcast(typeof(n),n,J.*b) for (n,b,J) in zip(fns,cvals,change_ips)] + + return cvals + end + + # It provides for every face the nodes and the moments arrays + function _BDM_face_values(p,et,order,phi) + + # Reference facet + @check is_simplex(p) "We are assuming that all n-faces of the same n-dim are the same." + fp = Polytope{num_dims(p)-1}(p,1) + + # geomap from ref face to polytope faces + fgeomap = _ref_face_to_faces_geomap(p,fp) + + # Nodes are integration points (for exact integration) + # Thus, we define the integration points in the reference + # face polytope (fips and wips). Next, we consider the + # n-face-wise arrays of nodes in fp (constant cell array c_fips) + # the one of the points in the polytope after applying the geopmap + # (fcips), and the weights for these nodes (fwips, a constant cell array) + # Nodes (fcips) + degree = (order)*2 + fquad = Quadrature(fp,degree) + fips = get_coordinates(fquad) + wips = get_weights(fquad) + + c_fips, fcips, fwips = _nfaces_evaluation_points_weights(p, fgeomap, fips, wips) + + # Moments (fmoments) + # The BDM prebasis is expressed in terms of shape function + fshfs = MonomialBasis(et,fp,order) + + # Face moments, i.e., M(Fi)_{ab} = q_RF^a(xgp_RFi^b) w_Fi^b n_Fi ⋅ () + fmoments = _BDM_face_moments(p, fshfs, c_fips, fcips, fwips, phi) + + return fcips, fmoments + + end + + function _BDM_cell_moments(p, cbasis, ccips, cwips) + # Interior DOFs-related basis evaluated at interior integration points + ishfs_iips = evaluate(cbasis,ccips) + return cwips.⋅ishfs_iips + end + + # It provides for every cell the nodes and the moments arrays + function _BDM_cell_values(p,et,order,phi) + # Compute integration points at interior + degree = 2*(order) + iquad = Quadrature(p,degree) + ccips = get_coordinates(iquad) + cwips = get_weights(iquad) + + # Cell moments, i.e., M(C)_{ab} = q_C^a(xgp_C^b) w_C^b ⋅ () + if is_simplex(p) + T = VectorValue{num_dims(p),et} + # cbasis = GradMonomialBasis{num_dims(p)}(T,order-1) + cbasis = Polynomials.NedelecPrebasisOnSimplex{num_dims(p)}(order-2) + else + @notimplemented + end + cell_moments = _BDM_cell_moments(p, cbasis, ccips, cwips ) + + # Must scale weights using phi map to get the correct integrals + # scaling = meas(grad(phi)) + Jt = ∇(phi) + Jt_inv = pinvJt(Jt) + det_Jt = meas(Jt) + change = det_Jt*Jt_inv + change_ips = evaluate(change,ccips) + + cmoments = change_ips.⋅cell_moments + + return [ccips], [cmoments] + + end diff --git a/src/ReferenceFEs/deprecated/NedelecRefFEs.jl b/src/ReferenceFEs/deprecated/NedelecRefFEs.jl new file mode 100644 index 000000000..81924f856 --- /dev/null +++ b/src/ReferenceFEs/deprecated/NedelecRefFEs.jl @@ -0,0 +1,368 @@ +struct CurlConformity <: Conformity end + +struct Nedelec <: ReferenceFEName end + +const nedelec = Nedelec() + +""" + NedelecRefFE(::Type{et},p::Polytope,order::Integer) where et + +The `order` argument has the following meaning: the curl of the functions in this basis +is in the Q space of degree `order`. +""" +function NedelecRefFE(::Type{et},p::Polytope,order::Integer) where et + + # @santiagobadia : Project, go to complex numbers + D = num_dims(p) + + 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) + + face_own_dofs = _face_own_dofs_from_moments(nf_moments) + + face_dofs = face_own_dofs + + dof_basis = MomentBasedDofBasis(nf_nodes, nf_moments) + + ndofs = num_dofs(dof_basis) + + metadata = nothing + + reffe = GenericRefFE{Nedelec}( + ndofs, + p, + prebasis, + dof_basis, + CurlConformity(), + metadata, + face_dofs) + + reffe +end + +function ReferenceFE(p::Polytope,::Nedelec, order) + NedelecRefFE(Float64,p,order) +end + +function ReferenceFE(p::Polytope,::Nedelec,::Type{T}, order) where T + NedelecRefFE(T,p,order) +end + +function Conformity(reffe::GenericRefFE{Nedelec},sym::Symbol) + hcurl = (:Hcurl,:HCurl) + if sym == :L2 + L2Conformity() + elseif sym in hcurl + CurlConformity() + else + @unreachable """\n + It is not possible to use conformity = $sym on a Nedelec reference FE. + + Possible values of conformity for this reference fe are $((:L2, hcurl...)). + """ + end +end + +function get_face_own_dofs(reffe::GenericRefFE{Nedelec}, conf::CurlConformity) + reffe.face_dofs # For Nedelec, this member variable holds the face owned dofs +end + +function get_face_own_dofs(reffe::GenericRefFE{Nedelec}, conf::L2Conformity) + face_own_dofs=[Int[] for i in 1:num_faces(reffe)] + face_own_dofs[end]=collect(1:num_dofs(reffe)) + face_own_dofs +end + +function get_face_dofs(reffe::GenericRefFE{Nedelec,Dc}) where Dc + face_dofs=[Int[] for i in 1:num_faces(reffe)] + face_own_dofs=get_face_own_dofs(reffe) + p = get_polytope(reffe) + for d=1:Dc # Starting from edges, vertices do not own DoFs for Nedelec + first_face = get_offset(p,d) + nfaces = num_faces(reffe,d) + for face=first_face+1:first_face+nfaces + for df=1:d-1 + face_faces = get_faces(p,d,df) + first_cface = get_offset(p,df) + for cface in face_faces[face-first_face] + cface_own_dofs = face_own_dofs[first_cface+cface] + for dof in cface_own_dofs + push!(face_dofs[face],dof) + end + end + end + for dof in face_own_dofs[face] + push!(face_dofs[face],dof) + end + end + end + face_dofs +end + + +function _Nedelec_nodes_and_moments(::Type{et}, p::Polytope, order::Integer) where et + + @notimplementedif !( is_n_cube(p) || (is_simplex(p) ) ) + + D = num_dims(p) + ft = VectorValue{D,et} + pt = Point{D,et} + + nf_nodes = [ zeros(pt,0) for face in 1:num_faces(p)] + nf_moments = [ zeros(ft,0,0) for face in 1:num_faces(p)] + + ecips, emoments = _Nedelec_edge_values(p,et,order) + erange = get_dimrange(p,1) + nf_nodes[erange] = ecips + nf_moments[erange] = emoments + + if ( num_dims(p) == 3 && order > 0) + + if is_n_cube(p) + fcips, fmoments = _Nedelec_face_values(p,et,order) + else + fcips, fmoments = _Nedelec_face_values_simplex(p,et,order) + end + + frange = get_dimrange(p,D-1) + nf_nodes[frange] = fcips + nf_moments[frange] = fmoments + + end + + if ( is_n_cube(p) && order > 0) || ( is_simplex(p) && order > D-2) + + ccips, cmoments = _Nedelec_cell_values(p,et,order) + crange = get_dimrange(p,D) + nf_nodes[crange] = ccips + nf_moments[crange] = cmoments + + end + + nf_nodes, nf_moments +end + +function _Nedelec_edge_values(p,et,order) + + # Reference facet + dim1 = 1 + ep = Polytope{dim1}(p,1) + + # geomap from ref face to polytope faces + egeomap = _ref_face_to_faces_geomap(p,ep) + + # Compute integration points at all polynomial edges + degree = (order)*2 + equad = Quadrature(ep,degree) + cips = get_coordinates(equad) + wips = get_weights(equad) + + + c_eips, ecips, ewips = _nfaces_evaluation_points_weights(p, egeomap, cips, wips) + + # Edge moments, i.e., M(Ei)_{ab} = q_RE^a(xgp_REi^b) w_Fi^b t_Ei ⋅ () + eshfs = MonomialBasis(et,ep,order) + emoments = _Nedelec_edge_moments(p, eshfs, c_eips, ecips, ewips) + + return ecips, emoments + +end + +function _Nedelec_edge_moments(p, fshfs, c_fips, fcips, fwips) + ts = get_edge_tangent(p) + nc = length(c_fips) + cfshfs = fill(fshfs, nc) + cvals = lazy_map(evaluate,cfshfs,c_fips) + cvals = [fwips[i].*cvals[i] for i in 1:nc] + # @santiagobadia : Only working for oriented meshes now + cvals = [ _broadcast(typeof(t),t,b) for (t,b) in zip(ts,cvals)] + return cvals +end + +function _Nedelec_face_values(p,et,order) + + # Reference facet + @assert is_n_cube(p) "We are assuming that all n-faces of the same n-dim are the same." + fp = Polytope{num_dims(p)-1}(p,1) + + # geomap from ref face to polytope faces + fgeomap = _ref_face_to_faces_geomap(p,fp) + + # Compute integration points at all polynomial edges + degree = (order)*2 + fquad = Quadrature(fp,degree) + fips = get_coordinates(fquad) + wips = get_weights(fquad) + + c_fips, fcips, fwips = _nfaces_evaluation_points_weights(p, fgeomap, fips, wips) + + # Face moments, i.e., M(Fi)_{ab} = w_Fi^b q_RF^a(xgp_RFi^b) (n_Fi × ()) + fshfs = QGradMonomialBasis{num_dims(fp)}(et,order-1) + + fmoments = _Nedelec_face_moments(p, fshfs, c_fips, fcips, fwips) + + return fcips, fmoments + +end + +function _Nedelec_face_moments(p, fshfs, c_fips, fcips, fwips) + nc = length(c_fips) + cfshfs = fill(fshfs, nc) + cvals = lazy_map(evaluate,cfshfs,c_fips) + + fvs = _nfaces_vertices(Float64,p,num_dims(p)-1) + fts = [hcat([vs[2]-vs[1]...],[vs[3]-vs[1]...]) for vs in fvs] + + # Ref facet FE functions evaluated at the facet integration points (in ref facet) + cvals = [fwips[i].*cvals[i] for i in 1:nc] + + fns = get_facet_normal(p) + os = get_facet_orientations(p) + # @santiagobadia : Temporary hack for making it work for structured hex meshes + ft = eltype(fns) + cvals = [ _broadcast_extend(ft,Tm,b) for (Tm,b) in zip(fts,cvals)] + cvals = [ _broadcast_cross(ft,n*o,b) for (n,o,b) in zip(fns,os,cvals)] + return cvals +end + +function _Nedelec_face_values_simplex(p,et,order) + + # Reference facet + @assert is_simplex(p) "We are assuming that all n-faces of the same n-dim are the same." + fp = Polytope{num_dims(p)-1}(p,1) + + # geomap from ref face to polytope faces + fgeomap = _ref_face_to_faces_geomap(p,fp) + + # Compute integration points at all polynomial edges + degree = (order)*2 + fquad = Quadrature(fp,degree) + fips = get_coordinates(fquad) + wips = get_weights(fquad) + + c_fips, fcips, fwips, fJtips = _nfaces_evaluation_points_weights_with_jac(p, fgeomap, fips, wips) + + Df = num_dims(fp) + fshfs = MonomialBasis{Df}(VectorValue{Df,et},order-1,(e,k)->sum(e)<=k) + + fmoments = _Nedelec_face_moments_simplex(p, fshfs, c_fips, fcips, fwips, fJtips) + + return fcips, fmoments + +end + +function _nfaces_evaluation_points_weights_with_jac(p, fgeomap, fips, wips) + nc = length(fgeomap) + c_fips = fill(fips,nc) + c_wips = fill(wips,nc) + pquad = lazy_map(evaluate,fgeomap,c_fips) + ## Must account for diagonals in simplex discretizations to get the correct + ## scaling + Jt1 = lazy_map(∇,fgeomap) + Jt1_ips = lazy_map(evaluate,Jt1,c_fips) + #det_J = lazy_map(Broadcasting(meas),Jt1_ips) + #c_detwips = collect(lazy_map(Broadcasting(*),c_wips,det_J)) + c_detwips = c_wips + c_fips, pquad, c_detwips, Jt1_ips +end + +function _Nedelec_face_moments_simplex(p, fshfs, c_fips, fcips, fwips, fJtips) + nc = length(c_fips) + cfshfs = fill(fshfs, nc) + cfshfs_fips = lazy_map(evaluate,cfshfs,c_fips) + function weigth(qij,Jti,wi) + Ji = transpose(Jti) + Ji⋅qij*wi + end + cvals = map(Broadcasting(weigth),cfshfs_fips,fJtips,fwips) + return cvals +end + +# It provides for every cell the nodes and the moments arrays +function _Nedelec_cell_values(p,et,order) + + # Compute integration points at interior + degree = 2*(order) + iquad = Quadrature(p,degree) + ccips = get_coordinates(iquad) + cwips = get_weights(iquad) + + # Cell moments, i.e., M(C)_{ab} = q_C^a(xgp_C^b) w_C^b ⋅ () + if is_n_cube(p) + cbasis = QCurlGradMonomialBasis{num_dims(p)}(et,order-1) + else + D = num_dims(p) + cbasis = MonomialBasis{D}(VectorValue{D,et},order-D+1,(e,k)->sum(e)<=k) + end + cmoments = _Nedelec_cell_moments(p, cbasis, ccips, cwips ) + + return [ccips], [cmoments] + +end + +const _Nedelec_cell_moments = _RT_cell_moments + +function _broadcast_cross(::Type{T},n,b) where T + c = Array{T}(undef,size(b)) + for (ii, i) in enumerate(b) + c[ii] = T(cross(get_array(i),get_array(n)))# cross product + end + return c +end + +# Moves bi values from 2D to 3D, by multiplying by a 3x2 matrix Tm +# Tm = [1.0 0.0; 0.0 1.0; 0.0 0.0] for instance +function _broadcast_extend(::Type{T},Tm,b) where T + c = Array{T}(undef,size(b)) + for (ii,i) in enumerate(b) + c[ii] = T(Tm*[i...]) + 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) + v⋅transpose(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 From c967869e8fc801ab7c66f56ace220162d446d089 Mon Sep 17 00:00:00 2001 From: JordiManyer Date: Mon, 18 Nov 2024 23:08:56 +1100 Subject: [PATCH 024/105] Many fixes --- src/ReferenceFEs/BDMRefFEs.jl | 16 +++++++------- src/ReferenceFEs/MomentBasedReferenceFEs.jl | 14 +++++++------ src/ReferenceFEs/NedelecRefFEs.jl | 4 ++-- src/ReferenceFEs/RaviartThomasRefFEs.jl | 21 +++++++++++-------- src/ReferenceFEs/ReferenceFEs.jl | 2 +- .../DivConformingFESpacesTests.jl | 4 ++-- 6 files changed, 34 insertions(+), 27 deletions(-) diff --git a/src/ReferenceFEs/BDMRefFEs.jl b/src/ReferenceFEs/BDMRefFEs.jl index 65df8d8c4..984bb0bba 100644 --- a/src/ReferenceFEs/BDMRefFEs.jl +++ b/src/ReferenceFEs/BDMRefFEs.jl @@ -9,13 +9,12 @@ The `order` argument has the following meaning: the divergence of the functions is in the P space of degree `order-1`. """ -function BDMRefFE(::Type{et},p::Polytope,order::Integer) where et +function BDMRefFE(::Type{T},p::Polytope,order::Integer) where T D = num_dims(p) - vet = VectorValue{D,et} if is_simplex(p) - prebasis = MonomialBasis(vet,p,order) - fb = MonomialBasis{D-1}(et,order,Polynomials._p_filter) + prebasis = MonomialBasis{D}(VectorValue{D,T},order,Polynomials._p_filter) + fb = MonomialBasis{D-1}(T,order,Polynomials._p_filter) cb = Polynomials.NedelecPrebasisOnSimplex{D}(order-2) else @notimplemented "BDM Reference FE only available for simplices" @@ -25,14 +24,17 @@ function BDMRefFE(::Type{et},p::Polytope,order::Integer) where et Broadcasting(Operation(⋅))(φ,μ) end function fmom(φ,μ,ds) # Face moment function : σ_F(φ,μ) = ∫((φ·n)*μ)dF - n = get_normal(ds) + n = get_facet_normal(ds) φn = Broadcasting(Operation(⋅))(φ,n) Broadcasting(Operation(*))(φn,μ) end - moments = [ + + moments = Tuple[ (get_dimrange(p,D-1),fmom,fb), # Face moments - (get_dimrange(p,D),cmom,cb) # Cell moments ] + if order > 1 + push!(moments,(get_dimrange(p,D),cmom,cb)) # Cell moments + end return MomentBasedReferenceFE(BDM(),p,prebasis,moments,DivConformity()) end diff --git a/src/ReferenceFEs/MomentBasedReferenceFEs.jl b/src/ReferenceFEs/MomentBasedReferenceFEs.jl index ab407a003..5832ff26a 100644 --- a/src/ReferenceFEs/MomentBasedReferenceFEs.jl +++ b/src/ReferenceFEs/MomentBasedReferenceFEs.jl @@ -223,7 +223,7 @@ function MomentBasedReferenceFE( name::ReferenceFEName, p::Polytope{D}, prebasis::AbstractVector{<:Field}, - moments::AbstractVector{<:Tuple{Vector{Int},<:Function,<:AbstractArray{<:Field}}}, + moments::AbstractVector{<:Tuple},#{<:AbstractVector{Int},<:Function,<:AbstractVector{<:Field}}}, conformity::Conformity; ) where D @@ -252,8 +252,8 @@ function MomentBasedReferenceFE( face_n_dofs = zeros(Int,n_faces) face_n_nodes = zeros(Int,n_faces) for ((faces,σ,μ),ds) in zip(moments,measures) - face_n_dofs[faces] += size(μ,2) - face_n_nodes[faces] += num_points(ds.quad) + face_n_dofs[faces] .+= length(μ) + face_n_nodes[faces] .+= num_points(ds.quad) end # Compute face moment and node indices @@ -271,23 +271,25 @@ function MomentBasedReferenceFE( n_dofs += n_dofs_i n_nodes += n_nodes_i end - nodes = Vector{Point{D}}(undef,n_nodes) + n_dofs = n_dofs - 1 + n_nodes = n_nodes - 1 # Compute face moments and nodes fill!(face_n_dofs,0) fill!(face_n_nodes,0) + nodes = Vector{Point{D,Float64}}(undef,n_nodes) for ((faces,σ,μ),ds) in zip(moments,measures) cache = return_cache(σ,φ,μ,ds) for face in faces d = face_dims[face] - lface = face - face_offsets[d] + lface = face - face_offsets[d+1] set_face!(ds,lface) # vals : (nN, nμ, nφ), coords : (nN) vals, coords = evaluate!(cache,σ,φ,μ,ds) dof_offset = face_n_dofs[face] - node_offset = first(face_nodes[face]) + face_n_nodes[face] + node_offset = first(face_nodes[face]) + face_n_nodes[face] - 1 for i in axes(vals,1) for j in axes(vals,2) face_moments[face][i,j+dof_offset] = dot(vals[i,j,:],φ_vec) diff --git a/src/ReferenceFEs/NedelecRefFEs.jl b/src/ReferenceFEs/NedelecRefFEs.jl index 2b5120fcc..e3dfb76b1 100644 --- a/src/ReferenceFEs/NedelecRefFEs.jl +++ b/src/ReferenceFEs/NedelecRefFEs.jl @@ -32,14 +32,14 @@ function NedelecRefFE(::Type{et},p::Polytope,order::Integer) where et end function fmom(φ,μ,ds) # Face moment function: σ_F(φ,μ) = ∫((φ×n)⋅μ)dF o = get_facet_orientations(ds.poly)[ds.face] # This is a hack to avoid a sign map - n = o*get_normal(ds) + n = o*get_facet_normal(ds) E = get_extension(ds) Eμ = Broadcasting(Operation(⋅))(E,μ) # We have to extend the basis to 3D φn = Broadcasting(Operation(×))(n,φ) Broadcasting(Operation(⋅))(φn,Eμ) end function emom(φ,μ,ds) # Edge moment function: σ_E(φ,μ) = ∫((φ⋅t)*μ)dE - t = get_tangent(ds) + t = get_edge_tangent(ds) φt = Broadcasting(Operation(⋅))(φ,t) Broadcasting(Operation(*))(φt,μ) end diff --git a/src/ReferenceFEs/RaviartThomasRefFEs.jl b/src/ReferenceFEs/RaviartThomasRefFEs.jl index 7437cae22..29844461b 100644 --- a/src/ReferenceFEs/RaviartThomasRefFEs.jl +++ b/src/ReferenceFEs/RaviartThomasRefFEs.jl @@ -18,13 +18,13 @@ function RaviartThomasRefFE( ) where {T,D} if is_n_cube(p) - prebasis = QCurlGradJacobiPolynomialBasis{D}(T,order) # Prebasis - cb = QGradJacobiPolynomialBasis{D}(T,order-1) # Cell basis - fb = JacobiPolynomialBasis{D}(T,order,Polynomials._q_filter) # Face basis + prebasis = QCurlGradJacobiPolynomialBasis{D}(T,order) # Prebasis + cb = QGradJacobiPolynomialBasis{D}(T,order-1) # Cell basis + fb = JacobiPolynomialBasis{D-1}(T,order,Polynomials._q_filter) # Face basis elseif is_simplex(p) - prebasis = PCurlGradMonomialBasis{D}(et,order) # Prebasis - cb = MonomialBasis{D}(T,order-1,Polynomials._p_filter) # Cell basis - fb = JacobiPolynomialBasis{D}(T,order,Polynomials._p_filter) # Face basis + prebasis = PCurlGradMonomialBasis{D}(T,order) # Prebasis + cb = JacobiPolynomialBasis{D}(VectorValue{D,T},order-1,Polynomials._p_filter) # Cell basis + fb = JacobiPolynomialBasis{D-1}(T,order,Polynomials._p_filter) # Face basis else @notimplemented "Raviart-Thomas Reference FE only available for cubes and simplices" end @@ -33,14 +33,17 @@ function RaviartThomasRefFE( Broadcasting(Operation(⋅))(φ,μ) end function fmom(φ,μ,ds) # Face moment function : σ_F(φ,μ) = ∫((φ·n)*μ)dF - n = get_normal(ds) + n = get_facet_normal(ds) φn = Broadcasting(Operation(⋅))(φ,n) Broadcasting(Operation(*))(φn,μ) end - moments = [ + + moments = Tuple[ (get_dimrange(p,D-1),fmom,fb), # Face moments - (get_dimrange(p,D),cmom,cb) # Cell moments ] + if (order > 0) + push!(moments,(get_dimrange(p,D),cmom,cb)) # Cell moments + end return MomentBasedReferenceFE(RaviartThomas(),p,prebasis,moments,DivConformity()) end diff --git a/src/ReferenceFEs/ReferenceFEs.jl b/src/ReferenceFEs/ReferenceFEs.jl index 2ffb605f4..44cbc6bee 100644 --- a/src/ReferenceFEs/ReferenceFEs.jl +++ b/src/ReferenceFEs/ReferenceFEs.jl @@ -244,7 +244,7 @@ include("StrangQuadratures.jl") include("MomentBasedReferenceFEs.jl") -include("DivConformingReferenceFEs.jl") +include("RaviartThomasRefFEs.jl") include("BDMRefFEs.jl") diff --git a/test/FESpacesTests/DivConformingFESpacesTests.jl b/test/FESpacesTests/DivConformingFESpacesTests.jl index 8bda23e06..39ac66419 100644 --- a/test/FESpacesTests/DivConformingFESpacesTests.jl +++ b/test/FESpacesTests/DivConformingFESpacesTests.jl @@ -87,7 +87,7 @@ end reffe = ReferenceFE(TET,raviart_thomas,order) - domain =(0,1,0,1,0,1) + domain = (0,1,0,1,0,1) partition = (3,3,3) model = simplexify(CartesianDiscreteModel(domain,partition)) @@ -135,7 +135,7 @@ end degree = 1 reffe_rt = ReferenceFE(raviart_thomas,Float64,order) - V = FESpace(Dc2Dp3model, reffe_rt ; conformity=:HDiv) + V = FESpace(Dc2Dp3model, reffe_rt; conformity=:HDiv) U = TrialFESpace(V,u) reffe = ReferenceFE(lagrangian,Float64,order) Q = TestFESpace(Dc2Dp3model,reffe,conformity=:L2) From 2837bbb1c91ea60abb94afbe4830722c4f4d1dd1 Mon Sep 17 00:00:00 2001 From: JordiManyer Date: Sat, 23 Nov 2024 23:48:44 +1100 Subject: [PATCH 025/105] Bugfixes in Nedelec --- src/Polynomials/QGradMonomialBases.jl | 2 ++ src/ReferenceFEs/MomentBasedReferenceFEs.jl | 10 ++++------ src/ReferenceFEs/NedelecRefFEs.jl | 22 +++++++++------------ 3 files changed, 15 insertions(+), 19 deletions(-) diff --git a/src/Polynomials/QGradMonomialBases.jl b/src/Polynomials/QGradMonomialBases.jl index 0b9dcd9b7..f31319429 100644 --- a/src/Polynomials/QGradMonomialBases.jl +++ b/src/Polynomials/QGradMonomialBases.jl @@ -260,6 +260,8 @@ Base.IndexStyle(::Type{<:NedelecPrebasisOnSimplex}) = IndexLinear() num_terms(a::NedelecPrebasisOnSimplex) = length(a) get_order(f::NedelecPrebasisOnSimplex) = f.order +return_type(::NedelecPrebasisOnSimplex{D}) where {D} = VectorValue{D,Float64} + function return_cache( f::NedelecPrebasisOnSimplex{d},x::AbstractVector{<:Point}) where d np = length(x) diff --git a/src/ReferenceFEs/MomentBasedReferenceFEs.jl b/src/ReferenceFEs/MomentBasedReferenceFEs.jl index 5832ff26a..db138c255 100644 --- a/src/ReferenceFEs/MomentBasedReferenceFEs.jl +++ b/src/ReferenceFEs/MomentBasedReferenceFEs.jl @@ -257,22 +257,20 @@ function MomentBasedReferenceFE( end # Compute face moment and node indices - n_dofs = 1 - n_nodes = 1 + n_dofs = 0 + n_nodes = 0 face_own_dofs = Vector{Vector{Int}}(undef,n_faces) face_nodes = Vector{UnitRange{Int}}(undef, n_faces) face_moments = Vector{Array{T}}(undef, n_faces) for face in 1:n_faces n_dofs_i = face_n_dofs[face] n_nodes_i = face_n_nodes[face] - face_own_dofs[face] = collect(n_dofs:(n_dofs+n_dofs_i-1)) - face_nodes[face] = n_nodes:(n_nodes+n_nodes_i-1) + face_own_dofs[face] = collect((n_dofs+1):(n_dofs+n_dofs_i)) + face_nodes[face] = (n_nodes+1):(n_nodes+n_nodes_i) face_moments[face] = zeros(T,n_nodes_i,n_dofs_i) n_dofs += n_dofs_i n_nodes += n_nodes_i end - n_dofs = n_dofs - 1 - n_nodes = n_nodes - 1 # Compute face moments and nodes fill!(face_n_dofs,0) diff --git a/src/ReferenceFEs/NedelecRefFEs.jl b/src/ReferenceFEs/NedelecRefFEs.jl index e3dfb76b1..7cdbb98e5 100644 --- a/src/ReferenceFEs/NedelecRefFEs.jl +++ b/src/ReferenceFEs/NedelecRefFEs.jl @@ -31,7 +31,7 @@ function NedelecRefFE(::Type{et},p::Polytope,order::Integer) where et Broadcasting(Operation(⋅))(φ,μ) end function fmom(φ,μ,ds) # Face moment function: σ_F(φ,μ) = ∫((φ×n)⋅μ)dF - o = get_facet_orientations(ds.poly)[ds.face] # This is a hack to avoid a sign map + o = get_facet_orientations(ds.cpoly)[ds.face] # This is a hack to avoid a sign map n = o*get_facet_normal(ds) E = get_extension(ds) Eμ = Broadcasting(Operation(⋅))(E,μ) # We have to extend the basis to 3D @@ -44,18 +44,14 @@ function NedelecRefFE(::Type{et},p::Polytope,order::Integer) where et Broadcasting(Operation(*))(φt,μ) end - if D == 2 - moments = [ # In 2D we do not have face moments - (get_dimrange(p,1),emom,eb), # Edge moments - (get_dimrange(p,D),cmom,cb) # Cell moments - ] - else - @assert D == 3 - moments = [ - (get_dimrange(p,1),emom,eb), # Edge moments - (get_dimrange(p,D-1),fmom,fb), # Face moments - (get_dimrange(p,D),cmom,cb) # Cell moments - ] + moments = Tuple[ + (get_dimrange(p,1),emom,eb), # Edge moments + ] + if D == 3 && order > 0 + push!(moments,(get_dimrange(p,D-1),fmom,fb)) # Face moments + end + if (is_n_cube(p) && order > 0) || (is_simplex(p) && order > D-2) + push!(moments,(get_dimrange(p,D),cmom,cb)) # Cell moments end return MomentBasedReferenceFE(Nedelec(),p,prebasis,moments,CurlConformity()) From 9b38ee10a20f6b0c709962e658e61e77f33cc772 Mon Sep 17 00:00:00 2001 From: JordiManyer Date: Sun, 24 Nov 2024 00:10:39 +1100 Subject: [PATCH 026/105] Accounted for detrminant of the face --- src/ReferenceFEs/MomentBasedReferenceFEs.jl | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/src/ReferenceFEs/MomentBasedReferenceFEs.jl b/src/ReferenceFEs/MomentBasedReferenceFEs.jl index db138c255..c032e4c89 100644 --- a/src/ReferenceFEs/MomentBasedReferenceFEs.jl +++ b/src/ReferenceFEs/MomentBasedReferenceFEs.jl @@ -179,9 +179,12 @@ function Arrays.return_cache( xf = get_coordinates(ds.quad) w = get_weights(ds.quad) fmap_cache = return_cache(fmap,xf) + + detJ = Broadcasting(Operation(meas))(Broadcasting(∇)(fmap)) + detJ_cache = return_cache(detJ,xf) f_cache = return_cache(f,xf) - return fmap_cache, f_cache, xf, w + return fmap_cache, detJ_cache, f_cache, xf, w end function Arrays.evaluate!( @@ -191,15 +194,18 @@ function Arrays.evaluate!( μ::AbstractArray{<:Field}, # μ: polynomial basis (defined on the face) ds::FaceMeasure # ds: face measure ) - fmap_cache, f_cache, xf, w = cache + fmap_cache, detJ_cache, f_cache, xf, w = cache fmap = ds.fmaps[ds.face] φf = transpose(Broadcasting(Operation(∘))(φ,fmap)) f = σ(φf,μ,ds) + detJ = Broadcasting(Operation(meas))(Broadcasting(∇)(fmap)) + dF = evaluate!(detJ_cache,detJ,xf) + xc = evaluate!(fmap_cache,fmap,xf) # quad pts on the cell fx = evaluate!(f_cache,f,xf) # f evaluated on the quad pts - fx .= w .* fx + fx .= (w .* dF) .* fx return fx, xc end From 2e78db9001ee9ef90988fc289e9f23ac2f3814f0 Mon Sep 17 00:00:00 2001 From: JordiManyer Date: Mon, 25 Nov 2024 09:11:07 +1100 Subject: [PATCH 027/105] Started implementing AW and MTW reffes --- src/ReferenceFEs/AWRefFEs.jl | 82 +++++++++++++++++++ src/ReferenceFEs/MTWRefFEs.jl | 70 ++++++++++++++++ .../CurlConformingFESpacesTests.jl | 4 +- 3 files changed, 154 insertions(+), 2 deletions(-) create mode 100644 src/ReferenceFEs/AWRefFEs.jl create mode 100644 src/ReferenceFEs/MTWRefFEs.jl diff --git a/src/ReferenceFEs/AWRefFEs.jl b/src/ReferenceFEs/AWRefFEs.jl new file mode 100644 index 000000000..2f6850afb --- /dev/null +++ b/src/ReferenceFEs/AWRefFEs.jl @@ -0,0 +1,82 @@ +struct ArnoldWinther <: ReferenceFEName end + +const arnoldwinther = ArnoldWinther() + +""" +ArnoldWintherRefFE(::Type{T},p::Polytope,order::Integer) where T + +Arnold-Winther reference finite element. + +References: + +- `Mixed Finite Elements for Elasticity`, Arnold and Winther (2002) +- `Nonconforming Mixed Finite Elements for Elasticity`, Arnold and Winther (2003) +- `Transformations for Piola-mapped elements`, Aznaran, Farrell and Kirby (2022) + +""" +function ArnoldWintherRefFE(::Type{T},p::Polytope,order::Integer) where T + @assert p == TRI "ArnoldWinther Reference FE only defined for TRIangles" + conforming = true # TODO: Make this an argument + + VT = SymTensorValue{2,T} + prebasis = MonomialBasis{2}(VT,3,Polynomials._p_filter) + fb = MonomialBasis{D-1}(T,0,Polynomials._p_filter) + cb = map(constant_field,component_basis(VT)) + + function cmom(φ,μ,ds) # Cell and Node moment function: σ_K(φ,μ) = ∫(φ:μ)dK + Broadcasting(Operation(⊙))(φ,μ) + end + function fmom_n(φ,μ,ds) # Face moment function (normal) : σ_F(φ,μ) = ∫((n·φ·n)*μ)dF + n = get_facet_normal(ds) + φn = Broadcasting(Operation(⋅))(φ,n) + nφn = Broadcasting(Operation(⋅))(n,φn) + Broadcasting(Operation(*))(nφn,μ) + end + function fmom_t(φ,μ,ds) # Face moment function (tangent) : σ_F(φ,μ) = ∫((n·φ·t)*μ)dF + n = get_facet_normal(ds) + t = get_edge_tangent(ds) + φn = Broadcasting(Operation(⋅))(φ,t) + nφn = Broadcasting(Operation(⋅))(n,φn) + Broadcasting(Operation(*))(nφn,μ) + end + + moments = Tuple[ + (get_dimrange(p,1),fmom_n,fb), # Face moments (normal-normal) + (get_dimrange(p,1),fmom_t,fb), # Face moments (normal-tangent) + (get_dimrange(p,2),cmom,cb) # Cell moments + ] + + if conforming + node_moments = Tuple[(get_dimrange(p,0),cmom,cb)] # Node moments + moments = vcat(node_moments,moments) + end + + return MomentBasedReferenceFE(ArnoldWinther(),p,prebasis,moments,DivConformity()) +end + +function ReferenceFE(p::Polytope,::ArnoldWinther, order) + BDMRefFE(Float64,p,order) +end + +function ReferenceFE(p::Polytope,::ArnoldWinther,::Type{T}, order) where T + BDMRefFE(T,p,order) +end + +function Conformity(reffe::GenericRefFE{ArnoldWinther},sym::Symbol) + hdiv = (:Hdiv,:HDiv) + if sym == :L2 + L2Conformity() + elseif sym in hdiv + DivConformity() + else + @unreachable """\n + It is not possible to use conformity = $sym on a ArnoldWinther reference FE. + + Possible values of conformity for this reference fe are $((:L2, hdiv...)). + """ + end +end + +function get_face_own_dofs(reffe::GenericRefFE{ArnoldWinther}, conf::DivConformity) + get_face_dofs(reffe) +end diff --git a/src/ReferenceFEs/MTWRefFEs.jl b/src/ReferenceFEs/MTWRefFEs.jl new file mode 100644 index 000000000..c334c10a1 --- /dev/null +++ b/src/ReferenceFEs/MTWRefFEs.jl @@ -0,0 +1,70 @@ +struct MardalTaiWinther <: ReferenceFEName end + +const mtw = MardalTaiWinther() + +""" +MardalTaiWintherRefFE(::Type{et},p::Polytope,order::Integer) where et + +Mardal-Tai-Winther reference finite element. + +References: + +- `A Robust Finite Element Method for Darcy-Stokes Flow`, Mardal, Tai and Winther (2002) +- `Transformations for Piola-mapped elements`, Aznaran, Farrell and Kirby (2022) + +""" +function MardalTaiWintherRefFE(::Type{T},p::Polytope,order::Integer) where T + D = num_dims(p) + @assert is_simplex(p) "MardalTaiWinther Reference FE only defined simplices" + @asset order == 3 "MardalTaiWinther Reference FE is by definition of order 3" + # TODO: We should just not allow this to be an argument + + prebasis = MonomialBasis{D}(VectorValue{D,T},3,Polynomials._p_filter) + eb = MonomialBasis{1}(T,0,Polynomials._p_filter) + fb = MonomialBasis{D-1}(T,1,Polynomials._p_filter) + + function emom(φ,μ,ds) # Edge moment function: σ_K(φ,μ) = ∫((φ⋅t)*μ)dK + t = get_edge_tangent(ds) + φt = Broadcasting(Operation(⋅))(φ,t) + Broadcasting(Operation(*))(φt,μ) + end + function fmom(φ,μ,ds) # Face moment function : σ_F(φ,μ) = ∫((φ·n)*μ)dF + n = get_facet_normal(ds) + φn = Broadcasting(Operation(⋅))(φ,n) + Broadcasting(Operation(*))(φn,μ) + end + + moments = [ + (get_dimrange(p,1),emom,eb), # Edge moments + (get_dimrange(p,D-1),fmom,fb), # Face moments + ] + + return MomentBasedReferenceFE(MardalTaiWinther(),p,prebasis,moments,DivConformity()) +end + +function ReferenceFE(p::Polytope,::MardalTaiWinther, order) + MardalTaiWintherRefFE(Float64,p,order) +end + +function ReferenceFE(p::Polytope,::MardalTaiWinther,::Type{T}, order) where T + MardalTaiWintherRefFE(T,p,order) +end + +function Conformity(reffe::GenericRefFE{MardalTaiWinther},sym::Symbol) + hdiv = (:Hdiv,:HDiv) + if sym == :L2 + L2Conformity() + elseif sym in hdiv + DivConformity() + else + @unreachable """\n + It is not possible to use conformity = $sym on a MardalTaiWinther reference FE. + + Possible values of conformity for this reference fe are $((:L2, hdiv...)). + """ + end +end + +function get_face_own_dofs(reffe::GenericRefFE{MardalTaiWinther}, conf::DivConformity) + get_face_dofs(reffe) +end diff --git a/test/FESpacesTests/CurlConformingFESpacesTests.jl b/test/FESpacesTests/CurlConformingFESpacesTests.jl index bac081dd3..bd46c3383 100644 --- a/test/FESpacesTests/CurlConformingFESpacesTests.jl +++ b/test/FESpacesTests/CurlConformingFESpacesTests.jl @@ -9,7 +9,7 @@ using Gridap.CellData using Gridap.Fields using Gridap.ReferenceFEs -domain =(0,1,0,1) +domain = (0,1,0,1) partition = (2,2) model = CartesianDiscreteModel(domain,partition) order = 1 @@ -27,7 +27,7 @@ el2 = sqrt(sum( ∫( e⋅e )*dΩ )) #using Gridap.Visualization #writevtk(Ω,"nedel",nsubcells=10,cellfields=["err"=>e,"u"=>u,"uh"=>uh]) -domain =(0,1,0,1) +domain = (0,1,0,1) partition = (3,3) model = CartesianDiscreteModel(domain,partition) |> simplexify order = 0 From 4b3a82d062fc13642813fd5ff617d7e83521d0f7 Mon Sep 17 00:00:00 2001 From: JordiManyer Date: Tue, 26 Nov 2024 08:37:53 +1100 Subject: [PATCH 028/105] Started looking at the pullback machinery --- src/ReferenceFEs/Pullbacks.jl | 128 ++++++++++++++++++++++++++++++++++ 1 file changed, 128 insertions(+) create mode 100644 src/ReferenceFEs/Pullbacks.jl diff --git a/src/ReferenceFEs/Pullbacks.jl b/src/ReferenceFEs/Pullbacks.jl new file mode 100644 index 000000000..36e2d7a03 --- /dev/null +++ b/src/ReferenceFEs/Pullbacks.jl @@ -0,0 +1,128 @@ + +""" + abstract type Pullback <: Map end + +Represents a pullback map F*, defined as + F* : V̂ -> V +where + - V̂ is a function space on the reference cell K̂ and + - V is a function space on the physical cell K. +""" +abstract type Pullback <: Map end + +function lazy_map( + ::Broadcasting{typeof(gradient)}, a::LazyArray{<:Fill{Broadcasting{Operation{<:Pullback}}}} +) + cell_ref_basis, args = a.args + cell_ref_gradient = lazy_map(Broadcasting(∇),cell_ref_basis) + return lazy_map(a.maps.value,cell_ref_gradient,args...) +end + +# InversePullback + +""" + struct InversePullback{PB <: Pullback} <: Map end + +Represents the inverse of a pullback map F*, defined as + (F*)^-1 : V -> V̂ +where + - V̂ is a function space on the reference cell K̂ and + - V is a function space on the physical cell K. +""" +struct InversePullback{PB} <: Map + pullback::PB + function InversePullback(pullback::Pullback) + PB = typeof(pullback) + new{PB}(pullback) + end +end + +Arrays.inverse_map(pb::Pullback) = InversePullback(pb) +Arrays.inverse_map(ipb::InversePullback) = ipb.pullback + +# Pushforward + +""" + struct Pushforward{PB <: Pullback} <: Map end + +Represents a pushforward map F**, defined as + F** : V* -> V̂* +where + - V̂* is a dof space on the reference cell K̂ and + - V* is a dof space on the physical cell K. +Its action on physical dofs σ : V -> R is defined in terms of the pullback map F* as + F**(σ) := σ∘F* : V̂ -> R +""" +struct Pushforward{PB} <: Map + pullback::PB + function Pushforward(pullback::Pullback) + PB = typeof(pullback) + new{PB}(pullback) + end +end + +function Arrays.lazy_map( + ::typeof{evaluate},k::LazyArray{<:Fill{<:Pushforward}},ref_cell_basis +) + pf = k.maps.value + phys_cell_dofs, cell_map, pb_args = k.args + phys_cell_basis = lazy_map(pf.pullback,ref_cell_basis,cell_map,pb_args...) + return lazy_map(evaluate,phys_cell_dofs,phys_cell_basis) +end + +# InversePushforward + +""" + struct InversePushforward{PB <: Pullback} <: Map end + +Represents the inverse of the pushforward map F**, defined as + (F**)^-1 : V̂* -> V* +where + - V̂* is a dof space on the reference cell K̂ and + - V* is a dof space on the physical cell K. +Its action on reference dofs ̂σ : V -> R is defined in terms of the pullback map F* as + F**(̂σ) := ̂σ∘(F*)^-1 : V -> R +""" +struct InversePushforward{PB} <: Map + pullback::PB + function InversePushforward(pullback::Pullback) + PB = typeof(pullback) + new{PB}(pullback) + end +end + +Arrays.inverse_map(pf::Pushforward) = InversePushforward(pf.pullback) +Arrays.inverse_map(ipf::InversePushforward) = Pushforward(ipf.pullback) + +function Arrays.lazy_map( + ::typeof{evaluate},k::LazyArray{<:Fill{<:InversePushforward}},phys_cell_basis +) + pf = inverse_map(k.maps.value) + ref_cell_dofs, cell_map, pb_args = k.args + ref_cell_basis = lazy_map(inverse_map(pf.pullback),phys_cell_basis,cell_map,pb_args...) + return lazy_map(evaluate,ref_cell_dofs,ref_cell_basis) +end + +# ContraVariantPiolaMap + +struct ContraVariantPiolaMap <: Pullback end + +function lazy_map( + k::ContraVariantPiolaMap, + cell_ref_shapefuns::AbstractArray{<:AbstractArray{<:Field}}, + cell_map::AbstractArray{<:Field}, + sign_flip::AbstractArray{<:AbstractArray{<:Field}} +) + cell_Jt = lazy_map(∇,cell_map) + lazy_map(Broadcasting(Operation(k)),cell_ref_shapefuns,cell_Jt,sign_flip) +end + +function evaluate!( + cache,::ContraVariantPiolaMap, + v::Number, + Jt::Number, + sign_flip::Bool +) + idetJ = 1/meas(Jt) + ((-1)^sign_flip*v)⋅(idetJ*Jt) +end From 02a1648076b62fc6fff792c2378a1c88b6949718 Mon Sep 17 00:00:00 2001 From: JordiManyer Date: Mon, 2 Dec 2024 12:32:40 +1100 Subject: [PATCH 029/105] Minor --- src/ReferenceFEs/MomentBasedReferenceFEs.jl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/ReferenceFEs/MomentBasedReferenceFEs.jl b/src/ReferenceFEs/MomentBasedReferenceFEs.jl index c032e4c89..7dc76378b 100644 --- a/src/ReferenceFEs/MomentBasedReferenceFEs.jl +++ b/src/ReferenceFEs/MomentBasedReferenceFEs.jl @@ -35,8 +35,8 @@ struct MomentBasedDofBasis{P,V} <: AbstractVector{Moment} end end -Base.size(a::MomentBasedDofBasis) = (length(a.nodes),) -Base.axes(a::MomentBasedDofBasis) = (axes(a.nodes,1),) +Base.size(a::MomentBasedDofBasis) = (num_dofs(a),) +Base.axes(a::MomentBasedDofBasis) = (Base.OneTo(num_dofs),) Base.getindex(a::MomentBasedDofBasis,i::Integer) = Moment() Base.IndexStyle(::MomentBasedDofBasis) = IndexLinear() From 38954cb4d75194bfc20e623649261b70cb0fdfea Mon Sep 17 00:00:00 2001 From: Antoine Marteau Date: Tue, 3 Dec 2024 09:50:53 +1100 Subject: [PATCH 030/105] remove duplicate --- src/Polynomials/ChebyshevPolynomialBases.jl | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/src/Polynomials/ChebyshevPolynomialBases.jl b/src/Polynomials/ChebyshevPolynomialBases.jl index 7e6ff5b40..78520122a 100644 --- a/src/Polynomials/ChebyshevPolynomialBases.jl +++ b/src/Polynomials/ChebyshevPolynomialBases.jl @@ -175,20 +175,6 @@ function _evaluate_1d_ch_T!(v::AbstractMatrix{T},x,order,d) where T end end -function _evaluate_1d_ch_U!(v::AbstractMatrix{T},x,order,d) where T - n = order + 1 - o = one(T) - @inbounds v[d,1] = o - if n > 1 - ξ = (2*x[d] - 1) # ξ ∈ [-1,1] - ξ2 = 2*ξ - v[d,2] = ξ2 - for i in 3:n - @inbounds v[d,i] = v[d,i-1]*ξ2 - v[d,i-2] - end - end -end - function _gradient_1d_ch_T!(v::AbstractMatrix{T},x,order,d) where T n = order + 1 z = zero(T) From bb7e2528dbe917709bacbfba06f9d3b37c32c784 Mon Sep 17 00:00:00 2001 From: MU00232207 Date: Thu, 5 Dec 2024 13:59:04 +1100 Subject: [PATCH 031/105] CR working in 2D --- src/ReferenceFEs/CRRefFEs.jl | 59 +++++++++++++++++++++ src/ReferenceFEs/MomentBasedReferenceFEs.jl | 43 +++++++++++++++ src/ReferenceFEs/ReferenceFEs.jl | 5 ++ test/ReferenceFEsTests/BDMRefFEsTests.jl | 6 +-- 4 files changed, 110 insertions(+), 3 deletions(-) create mode 100644 src/ReferenceFEs/CRRefFEs.jl diff --git a/src/ReferenceFEs/CRRefFEs.jl b/src/ReferenceFEs/CRRefFEs.jl new file mode 100644 index 000000000..261fcd64b --- /dev/null +++ b/src/ReferenceFEs/CRRefFEs.jl @@ -0,0 +1,59 @@ +struct CR <: ReferenceFEName end +const cr = CR() + +""" +CRRefFE(::Type{et},p::Polytope,order::Integer) where et + +The `order` argument has the following meaning: the divergence of the functions in this basis +is in the P space of degree `order-1`. + +""" +function CRRefFE(::Type{T},p::Polytope,order::Integer) where T + D = num_dims(p) + + if is_simplex(p) && order == 1 + prebasis = MonomialBasis{D}(T,order,Polynomials._p_filter) + fb = MonomialBasis{D-1}(T,0,Polynomials._p_filter) + else + @notimplemented "CR Reference FE only available for simplices and lowest order" + end + + function fmom(φ,μ,ds) # Face moment function : σ_F(φ,μ) = 1/|F| ( ∫((φ)*μ)dF ) + D = num_dims(ds.cpoly) + facet_measure = get_facet_measure(ds.cpoly, D-1) + facet_measure_1 = Gridap.Fields.ConstantField(1 / facet_measure[ds.face]) + φμ = Broadcasting(Operation(⋅))(φ,μ) + Broadcasting(Operation(*))(φμ,facet_measure_1) + end + + moments = Tuple[ + (get_dimrange(p,D-1),fmom,fb), # Face moments + ] + + return Gridap.ReferenceFEs.MomentBasedReferenceFE(CR(),p,prebasis,moments,L2Conformity()) +end + + +function ReferenceFE(p::Polytope,::CR, order) + CRRefFE(Float64,p,order) +end + +function ReferenceFE(p::Polytope,::CR,::Type{T}, order) where T + CRRefFE(T,p,order) +end + +function Conformity(reffe::GenericRefFE{CR},sym::Symbol) + if sym == :L2 + L2Conformity() + else + @unreachable """\n + It is not possible to use conformity = $sym on a CR reference FE. + + Possible values of conformity for this reference fe are $((:L2, hdiv...)). + """ + end +end + +function get_face_own_dofs(reffe::GenericRefFE{CR}, conf::L2Conformity) + get_face_dofs(reffe) +end diff --git a/src/ReferenceFEs/MomentBasedReferenceFEs.jl b/src/ReferenceFEs/MomentBasedReferenceFEs.jl index c032e4c89..7974016d2 100644 --- a/src/ReferenceFEs/MomentBasedReferenceFEs.jl +++ b/src/ReferenceFEs/MomentBasedReferenceFEs.jl @@ -166,6 +166,49 @@ function get_extension(m::FaceMeasure{Df,Dc}) where {Df,Dc} return ConstantField(TensorValue(hcat([vs[2]-vs[1]...],[vs[3]-vs[1]...]))) end +# TO DO: Bug in 3D, when n==2; n==4 and D==3 +function get_facet_measure(p::Polytope{D}, face::Int) where D + measures = Float64[] + facet_entities = get_face_coordinates(p) + for entity in facet_entities + n = length(entity) + if n == 1 + push!(measures, 0.0) # A point has zero measure + elseif n == 2 + # Length of an edge + p1, p2 = entity + push!(measures, norm(p2-p1)) + elseif n == 3 && D == 2 + # Perimeter of the closed polygon + n = length(entity) + perimeter = 0.0 + for i in 1:n + p1, p2 = entity[i], entity[mod1(i+1, n)] # cyclic indices + perimeter += norm([p2[1] - p1[1], p2[2] - p1[2]]) + end + push!(measures, perimeter) + elseif n == 3 && D == 3 + # Area of a simplex + p1, p2, p3 = entity + v1 = [p2[i] - p1[i] for i in 1:D] + v2 = [p3[i] - p1[i] for i in 1:D] + area = 0.5 * norm(cross(v1, v2)) + push!(measures, area) + elseif n == 4 && D == 3 + # Volume of a tetrahedron ( To do: Should be perimeter of the tetrahedron.) + p1, p2, p3, p4 = entity + v1 = [p2[i] - p1[i] for i in 1:D] + v2 = [p3[i] - p1[i] for i in 1:D] + v3 = [p4[i] - p1[i] for i in 1:D] + volume = abs(dot(v1, cross(v2, v3))) / 6 + push!(measures, volume) + end + + end + dim = get_dimranges(p)[face+1] + return measures[dim] +end + function Arrays.return_cache( σ::Function, # σ(φ,μ,ds) -> Field/Array{Field} φ::AbstractArray{<:Field}, # φ: prebasis (defined on the cell) diff --git a/src/ReferenceFEs/ReferenceFEs.jl b/src/ReferenceFEs/ReferenceFEs.jl index 44cbc6bee..4799cb396 100644 --- a/src/ReferenceFEs/ReferenceFEs.jl +++ b/src/ReferenceFEs/ReferenceFEs.jl @@ -56,6 +56,7 @@ export get_dimranges export get_dimrange export get_vertex_coordinates export get_facet_normal +export get_facet_measure export get_facet_orientations export get_edge_tangent export get_vertex_permutations @@ -182,6 +183,7 @@ export BDMRefFE export NedelecRefFE export BezierRefFE export ModalC0RefFE +export CRRefFE export Lagrangian export DivConforming @@ -197,6 +199,7 @@ export bdm export nedelec export bezier export modalC0 +export cr export Quadrature export QuadratureName @@ -250,6 +253,8 @@ include("BDMRefFEs.jl") include("NedelecRefFEs.jl") +include("CRRefFEs.jl") + include("MockDofs.jl") include("BezierRefFEs.jl") diff --git a/test/ReferenceFEsTests/BDMRefFEsTests.jl b/test/ReferenceFEsTests/BDMRefFEsTests.jl index f695d2c19..7b124ae18 100644 --- a/test/ReferenceFEsTests/BDMRefFEsTests.jl +++ b/test/ReferenceFEsTests/BDMRefFEsTests.jl @@ -1,4 +1,4 @@ -module BDMRefFEsTest +# module BDMRefFEsTest using Test using Gridap.Polynomials @@ -34,7 +34,7 @@ field = GenericField(x->v*x[1]) cache = return_cache(dof_basis,field) r = evaluate!(cache, dof_basis, field) -test_dof_array(dof_basis,field,r) +@enter test_dof_array(dof_basis,field,r) cache = return_cache(dof_basis,prebasis) r = evaluate!(cache, dof_basis, prebasis) @@ -98,4 +98,4 @@ reffe = ReferenceFE(TET,bdm,Float64,1) @test BDM() == bdm -end # module +# end # module From 289476aaee50609f7434f93fc779075eaad6029e Mon Sep 17 00:00:00 2001 From: MU00232207 Date: Mon, 9 Dec 2024 15:14:00 +1100 Subject: [PATCH 032/105] CRRefFEs working in 2D --- src/ReferenceFEs/MomentBasedReferenceFEs.jl | 2 +- test/ReferenceFEsTests/CRRefFEsTests.jl | 33 ++++ .../ReferenceFEsTests/CrouziexRaviartTests.jl | 148 ++++++++++++++++++ 3 files changed, 182 insertions(+), 1 deletion(-) create mode 100644 test/ReferenceFEsTests/CRRefFEsTests.jl create mode 100644 test/ReferenceFEsTests/CrouziexRaviartTests.jl diff --git a/src/ReferenceFEs/MomentBasedReferenceFEs.jl b/src/ReferenceFEs/MomentBasedReferenceFEs.jl index 7974016d2..1d6a94e02 100644 --- a/src/ReferenceFEs/MomentBasedReferenceFEs.jl +++ b/src/ReferenceFEs/MomentBasedReferenceFEs.jl @@ -166,7 +166,7 @@ function get_extension(m::FaceMeasure{Df,Dc}) where {Df,Dc} return ConstantField(TensorValue(hcat([vs[2]-vs[1]...],[vs[3]-vs[1]...]))) end -# TO DO: Bug in 3D, when n==2; n==4 and D==3 +# TO DO: Bug in 3D, when n==2; n==4 and D==3. Also, working on this to make better and more general. function get_facet_measure(p::Polytope{D}, face::Int) where D measures = Float64[] facet_entities = get_face_coordinates(p) diff --git a/test/ReferenceFEsTests/CRRefFEsTests.jl b/test/ReferenceFEsTests/CRRefFEsTests.jl new file mode 100644 index 000000000..24869c0d8 --- /dev/null +++ b/test/ReferenceFEsTests/CRRefFEsTests.jl @@ -0,0 +1,33 @@ +using Gridap +using Gridap.ReferenceFEs +using Gridap.Geometry +using Gridap.Fields +using Gridap.Arrays +using Gridap.ReferenceFEs +using Gridap.Polynomials +using Gridap.Helpers + +using FillArrays + + +p = TRI +D = num_dims(p) + +# T = Float64 +T = VectorValue{D,Float64} + + +cr_reffe = CRRefFE(T,p,1) + +cr_dofs = get_dof_basis(cr_reffe) +cr_prebasis = get_prebasis(cr_reffe) +cr_shapefuns = get_shapefuns(cr_reffe) + +M = evaluate(cr_dofs, cr_shapefuns) + +partition = (0,1,0,1) +cells = (2,2) +model = simplexify(CartesianDiscreteModel(partition, cells)) + +V = FESpace(model,cr_reffe) +get_cell_dof_ids(V) \ No newline at end of file diff --git a/test/ReferenceFEsTests/CrouziexRaviartTests.jl b/test/ReferenceFEsTests/CrouziexRaviartTests.jl new file mode 100644 index 000000000..ed867bc3a --- /dev/null +++ b/test/ReferenceFEsTests/CrouziexRaviartTests.jl @@ -0,0 +1,148 @@ +module CrouziexRaviartTests + + using Gridap + using Gridap.ReferenceFEs, Gridap.Geometry, Gridap.FESpaces, Gridap.Arrays, Gridap.TensorValues + using Gridap.Helpers + + + function solve_crScalarPoisson(partition, cells, u_exact) + + f(x) = - Δ(u_exact)(x) + + model = simplexify(CartesianDiscreteModel(partition, cells)) + + # reffe = CRRefFE(VectorValue{2,Float64},TRI,1) + reffe = CRRefFE(Float64,TRI,1) + V = FESpace(model,reffe,dirichlet_tags="boundary") + U = TrialFESpace(V,u_exact) + + Ω = Triangulation(model) + dΩ = Measure(Ω,3) + + a(u,v) = ∫( ∇(u)⋅∇(v) )*dΩ + l(v) = ∫( f*v )*dΩ + + op = AffineFEOperator(a,l,U,V) + uh = solve(op) + e = uh-u_exact + + return sqrt(sum( ∫( e⋅e )*dΩ )) + end + + + function solve_crVectorPoisson(partition, cells, u_exact) + + f(x) = - Δ(u_exact)(x) + + model = simplexify(CartesianDiscreteModel(partition, cells)) + + reffe = CRRefFE(VectorValue{2,Float64},TRI,1) + V = FESpace(model,reffe,dirichlet_tags="boundary") + U = TrialFESpace(V, u_exact) + + Ω = Triangulation(model) + dΩ = Measure(Ω,3) + + a(u,v) = ∫( ∇(u) ⊙ ∇(v) )*dΩ + l(v) = ∫( f ⋅ v )*dΩ + + op = AffineFEOperator(a,l,U,V) + uh = solve(op) + e = uh-u_exact + + return sqrt(sum( ∫( e⋅e )*dΩ )) + end + + function solve_crStokes(partition, cells, u_exact, p_exact) + f(x) = - Δ(u_exact)(x) + ∇(p_exact)(x) + model = simplexify(CartesianDiscreteModel(partition, cells)) + + reffe_u = CRRefFE(VectorValue{2,Float64},TRI,1) + reffe_p = ReferenceFE(lagrangian, Float64, 0; space=:P) + V = FESpace(model,reffe_u,dirichlet_tags="boundary") + Q = FESpace(model,reffe_p,conformity=:L2,constraint=:zeromean) + Y = MultiFieldFESpace([V,Q]) + + U = TrialFESpace(V, u_exact) + P = TrialFESpace(Q) + X = MultiFieldFESpace([U,P]) + + Ω = Triangulation(model) + dΩ = Measure(Ω,3) + + a((u,p),(v,q)) = ∫( ∇(v)⊙∇(u) - (∇⋅v)*p + q*(∇⋅u) )dΩ + l((v,q)) = ∫( v⋅f )dΩ + + op = AffineFEOperator(a,l,X,Y) + uh, ph = solve(op) + eu = uh-u_exact + ep = ph-p_exact + + return sqrt(sum( ∫( eu⋅eu )*dΩ )), sqrt(sum( ∫( ep⋅ep )*dΩ )) + end + + function conv_test_Stokes(partition,ns,u,p) + el2u = Float64[] + el2p = Float64[] + hs = Float64[] + for n in ns + l2u, l2p = solve_crStokes(partition,(n,n),u,p) + h = 1.0/n + push!(el2u,l2u) + push!(el2p,l2p) + push!(hs,h) + end + println(el2u) + println(el2p) + el2u, el2p, hs + end + + function conv_test_Poisson(partition,ns,u) + el2 = Float64[] + hs = Float64[] + for n in ns + l2 = solve_crScalarPoisson(partition,(n,n),u) + println(l2) + h = 1.0/n + push!(el2,l2) + push!(hs,h) + end + println(el2) + el2, hs + end + + function slope(hs,errors) + x = log10.(hs) + y = log10.(errors) + linreg = hcat(fill!(similar(x), 1), x) \ y + linreg[2] + end + + + partition = (0,1,0,1) + + # Stokes + u_exact(x) = VectorValue( [sin(pi*x[1])^2*sin(pi*x[2])*cos(pi*x[2]), -sin(pi*x[2])^2*sin(pi*x[1])*cos(pi*x[1])] ) + p_exact(x) = sin(2*pi*x[1])*sin(2*pi*x[2]) + + # Scalar Poisson + u_exact_p(x) = sin(2*π*x[1])*sin(2*π*x[2]) + + # Vector Poisson + # u_exact(x) = VectorValue( [sin(2*π*x[1])*sin(2*π*x[2]), x[1]*(x[1]-1)*x[2]*(x[2]-1)] ) + + ns = [4,8,16,32,64] + + el, hs = conv_test_Poisson(partition,ns,u_exact_p) + # println("Slope L2-norm u_Poisson: $(slope(hs,el))") + + elu, elp, hs = conv_test_Stokes(partition,ns,u_exact,p_exact) + println("Slope L2-norm u_Stokes: $(slope(hs,elu))") + println("Slope L2-norm p_Stokes: $(slope(hs,elp))") + + println("Slope L2-norm u_Poisson: $(slope(hs,el))") + +end # module + + + From adeeee2bb55ce8bbff6d74e7a9b0a8951af47fa2 Mon Sep 17 00:00:00 2001 From: Antoine Marteau Date: Tue, 10 Dec 2024 16:02:44 +1100 Subject: [PATCH 033/105] Implemented a generic 1d polynomial evaluation API --- NEWS.md | 3 + src/Polynomials/ChebyshevPolynomialBases.jl | 37 ++++-- ...ialBases.jl => LegendrePolynomialBases.jl} | 112 ++++++++++-------- src/Polynomials/ModalC0Bases.jl | 16 ++- src/Polynomials/MonomialBases.jl | 57 +++++---- ...jl => PCurlGradLegendrePolynomialBases.jl} | 51 ++++---- src/Polynomials/PCurlGradMonomialBases.jl | 5 +- src/Polynomials/PolynomialInterfaces.jl | 104 ++++++++++++++++ src/Polynomials/Polynomials.jl | 18 +-- ...ses.jl => QGradLegendrePolynomialBases.jl} | 99 ++++++++-------- src/Polynomials/QGradMonomialBases.jl | 5 +- src/ReferenceFEs/RaviartThomasRefFEs.jl | 22 ++-- .../deprecated/RaviartThomasRefFEs.jl | 26 ++-- ...sts.jl => LegendrePolynomialBasesTests.jl} | 26 ++-- test/PolynomialsTests/runtests.jl | 2 +- 15 files changed, 366 insertions(+), 217 deletions(-) rename src/Polynomials/{JacobiPolynomialBases.jl => LegendrePolynomialBases.jl} (64%) rename src/Polynomials/{PCurlGradJacobiPolynomialBases.jl => PCurlGradLegendrePolynomialBases.jl} (72%) create mode 100644 src/Polynomials/PolynomialInterfaces.jl rename src/Polynomials/{QGradJacobiPolynomialBases.jl => QGradLegendrePolynomialBases.jl} (58%) rename test/PolynomialsTests/{JacobiPolynomialBasesTests.jl => LegendrePolynomialBasesTests.jl} (85%) diff --git a/NEWS.md b/NEWS.md index 524a03951..8072d90f5 100644 --- a/NEWS.md +++ b/NEWS.md @@ -11,6 +11,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Added AMR-related methods `mark` and `estimate` to `Adaptivity` module. Implemented Dorfler marking strategy. Since PR[#1063](https://github.com/gridap/Gridap.jl/pull/1063). +### Changed +- Existing Jacobi polynomial bases/spaces were renamed to Legendre (which they are). TODO + ## [0.18.8] - 2024-12-2 ### Added diff --git a/src/Polynomials/ChebyshevPolynomialBases.jl b/src/Polynomials/ChebyshevPolynomialBases.jl index 78520122a..9337a981d 100644 --- a/src/Polynomials/ChebyshevPolynomialBases.jl +++ b/src/Polynomials/ChebyshevPolynomialBases.jl @@ -1,3 +1,12 @@ +""" + ChebyshevPType{kind,K} <: PolynomialType{K} + +Type representing Chebyshev polynomials of order up to `K`, +where `kind` is either `:T` or `:U` for first and second kind Chebyshev polynomials respectively. +""" +struct ChebyshevPType{kind, K} <: PolynomialType{K} end +isHierarchical(::ChebyshevPType) = true + struct ChebyshevPolynomial <: Field end struct ChebyshevPolynomialBasis{D,T} <: AbstractVector{ChebyshevPolynomial} @@ -161,8 +170,10 @@ end # Helpers -function _evaluate_1d_ch_T!(v::AbstractMatrix{T},x,order,d) where T - n = order + 1 +function _evaluate_1d!( + ::Type{ChebyshevPType{:T,K}}, v::AbstractMatrix{T},x,d) where {K,T<:Number} + + n = K + 1 o = one(T) @inbounds v[d,1] = o if n > 1 @@ -175,19 +186,21 @@ function _evaluate_1d_ch_T!(v::AbstractMatrix{T},x,order,d) where T end end -function _gradient_1d_ch_T!(v::AbstractMatrix{T},x,order,d) where T - n = order + 1 +function _gradient_1d!( + ::Type{ChebyshevPType{:T,K}}, g::AbstractMatrix{T},x,d) where {K,T<:Number} + + n = K + 1 z = zero(T) o = one(T) dξdx = T(2.0) - @inbounds v[d,1] = z # dT_0 = 0 + @inbounds g[d,1] = z # dT_0 = 0 if n > 1 ξ = T(2*x[d] - 1) - @inbounds v[d,2] = dξdx*o # dT_1 = 1*U_0 = 1 + @inbounds g[d,2] = dξdx*o # dT_1 = 1*U_0 = 1 unm1 = o un = 2*ξ for i in 3:n - @inbounds v[d,i] = dξdx*(i-1)*un # dT_i = i*U_{i-1} + @inbounds g[d,i] = dξdx*(i-1)*un # dT_i = i*U_{i-1} un, unm1 = 2*ξ*un - unm1, un end end @@ -202,7 +215,7 @@ function _evaluate_nd_ch!( dim = D for d in 1:dim - _evaluate_1d_ch_T!(c,x,orders[d],d) + _evaluate_1d!(ChebyshevPType{:T,orders[d]},c,x,d) end o = one(T) @@ -232,8 +245,7 @@ function _gradient_nd_ch!( dim = D for d in 1:dim - _evaluate_1d_ch_T!(c,x,orders[d],d) - _gradient_1d_ch_T!(g,x,orders[d],d) + _derivatives_1d!(ChebyshevPType{:T,orders[d]},(c,g),x,d) end z = zero(Mutable(VectorValue{D,T})) @@ -399,7 +411,7 @@ function _evaluate_nd_qgrad_ch!( dim = D for d in 1:dim - _evaluate_1d_ch_T!(c,x,order,d) + _evaluate_1d!(ChebyshevPType{:T,order},c,x,d) end o = one(T) @@ -443,8 +455,7 @@ function _gradient_nd_qgrad_ch!( dim = D for d in 1:dim - _evaluate_1d_ch_T!(c,x,order,d) - _gradient_1d_ch_T!(g,x,order,d) + _derivatives_1d!(ChebyshevPType{:T,order},(c,g),x,d) end z = zero(Mutable(V)) diff --git a/src/Polynomials/JacobiPolynomialBases.jl b/src/Polynomials/LegendrePolynomialBases.jl similarity index 64% rename from src/Polynomials/JacobiPolynomialBases.jl rename to src/Polynomials/LegendrePolynomialBases.jl index a9ef86417..1ee26003c 100644 --- a/src/Polynomials/JacobiPolynomialBases.jl +++ b/src/Polynomials/LegendrePolynomialBases.jl @@ -1,52 +1,60 @@ -struct JacobiPolynomial <: Field end +""" + LegendrePType{K} <: PolynomialType{K} -struct JacobiPolynomialBasis{D,T} <: AbstractVector{JacobiPolynomial} +Type representing Legendre polynomials of order up to `K` +""" +struct LegendrePType{K} <: PolynomialType{K} end +isHierarchical(::LegendrePType) = true + +struct LegendrePolynomial <: Field end + +struct LegendrePolynomialBasis{D,T} <: AbstractVector{LegendrePolynomial} orders::NTuple{D,Int} terms::Vector{CartesianIndex{D}} - function JacobiPolynomialBasis{D}( + function LegendrePolynomialBasis{D}( ::Type{T}, orders::NTuple{D,Int}, terms::Vector{CartesianIndex{D}}) where {D,T} new{D,T}(orders,terms) end end -@inline Base.size(a::JacobiPolynomialBasis{D,T}) where {D,T} = (length(a.terms)*num_indep_components(T),) -@inline Base.getindex(a::JacobiPolynomialBasis,i::Integer) = JacobiPolynomial() -@inline Base.IndexStyle(::JacobiPolynomialBasis) = IndexLinear() +@inline Base.size(a::LegendrePolynomialBasis{D,T}) where {D,T} = (length(a.terms)*num_indep_components(T),) +@inline Base.getindex(a::LegendrePolynomialBasis,i::Integer) = LegendrePolynomial() +@inline Base.IndexStyle(::LegendrePolynomialBasis) = IndexLinear() -function JacobiPolynomialBasis{D}( +function LegendrePolynomialBasis{D}( ::Type{T}, orders::NTuple{D,Int}, filter::Function=_q_filter) where {D,T} terms = _define_terms(filter, orders) - JacobiPolynomialBasis{D}(T,orders,terms) + LegendrePolynomialBasis{D}(T,orders,terms) end -function JacobiPolynomialBasis{D}( +function LegendrePolynomialBasis{D}( ::Type{T}, order::Int, filter::Function=_q_filter) where {D,T} orders = tfill(order,Val{D}()) - JacobiPolynomialBasis{D}(T,orders,filter) + LegendrePolynomialBasis{D}(T,orders,filter) end # API -function get_exponents(b::JacobiPolynomialBasis) +function get_exponents(b::LegendrePolynomialBasis) indexbase = 1 [Tuple(t) .- indexbase for t in b.terms] end -function get_order(b::JacobiPolynomialBasis) +function get_order(b::LegendrePolynomialBasis) maximum(b.orders) end -function get_orders(b::JacobiPolynomialBasis) +function get_orders(b::LegendrePolynomialBasis) b.orders end -return_type(::JacobiPolynomialBasis{D,T}) where {D,T} = T +return_type(::LegendrePolynomialBasis{D,T}) where {D,T} = T # Field implementation -function return_cache(f::JacobiPolynomialBasis{D,T},x::AbstractVector{<:Point}) where {D,T} +function return_cache(f::LegendrePolynomialBasis{D,T},x::AbstractVector{<:Point}) where {D,T} @check D == length(eltype(x)) "Incorrect number of point components" np = length(x) ndof = length(f) @@ -57,7 +65,7 @@ function return_cache(f::JacobiPolynomialBasis{D,T},x::AbstractVector{<:Point}) (r, v, c) end -function evaluate!(cache,f::JacobiPolynomialBasis{D,T},x::AbstractVector{<:Point}) where {D,T} +function evaluate!(cache,f::LegendrePolynomialBasis{D,T},x::AbstractVector{<:Point}) where {D,T} r, v, c = cache np = length(x) ndof = length(f) @@ -67,7 +75,7 @@ function evaluate!(cache,f::JacobiPolynomialBasis{D,T},x::AbstractVector{<:Point setsize!(c,(D,n)) for i in 1:np @inbounds xi = x[i] - _evaluate_nd_jp!(v,xi,f.orders,f.terms,c) + _evaluate_nd_leg!(v,xi,f.orders,f.terms,c) for j in 1:ndof @inbounds r[i,j] = v[j] end @@ -76,7 +84,7 @@ function evaluate!(cache,f::JacobiPolynomialBasis{D,T},x::AbstractVector{<:Point end function return_cache( - fg::FieldGradientArray{1,JacobiPolynomialBasis{D,V}}, + fg::FieldGradientArray{1,LegendrePolynomialBasis{D,V}}, x::AbstractVector{<:Point}) where {D,V} f = fg.fa @@ -95,7 +103,7 @@ end function evaluate!( cache, - fg::FieldGradientArray{1,JacobiPolynomialBasis{D,T}}, + fg::FieldGradientArray{1,LegendrePolynomialBasis{D,T}}, x::AbstractVector{<:Point}) where {D,T} f = fg.fa @@ -109,7 +117,7 @@ function evaluate!( setsize!(g,(D,n)) for i in 1:np @inbounds xi = x[i] - _gradient_nd_jp!(v,xi,f.orders,f.terms,c,g,T) + _gradient_nd_leg!(v,xi,f.orders,f.terms,c,g,T) for j in 1:ndof @inbounds r[i,j] = v[j] end @@ -118,7 +126,7 @@ function evaluate!( end function return_cache( - fg::FieldGradientArray{2,JacobiPolynomialBasis{D,V}}, + fg::FieldGradientArray{2,LegendrePolynomialBasis{D,V}}, x::AbstractVector{<:Point}) where {D,V} f = fg.fa @@ -138,7 +146,7 @@ end function evaluate!( cache, - fg::FieldGradientArray{2,JacobiPolynomialBasis{D,T}}, + fg::FieldGradientArray{2,LegendrePolynomialBasis{D,T}}, x::AbstractVector{<:Point}) where {D,T} f = fg.fa @@ -153,7 +161,7 @@ function evaluate!( setsize!(h,(D,n)) for i in 1:np @inbounds xi = x[i] - _hessian_nd_jp!(v,xi,f.orders,f.terms,c,g,h,T) + _hessian_nd_leg!(v,xi,f.orders,f.terms,c,g,h,T) for j in 1:ndof @inbounds r[i,j] = v[j] end @@ -163,7 +171,7 @@ end # Optimizing evaluation at a single point -function return_cache(f::AbstractVector{JacobiPolynomial},x::Point) +function return_cache(f::AbstractVector{LegendrePolynomial},x::Point) xs = [x] cf = return_cache(f,xs) v = evaluate!(cf,f,xs) @@ -171,7 +179,7 @@ function return_cache(f::AbstractVector{JacobiPolynomial},x::Point) r, cf, xs end -function evaluate!(cache,f::AbstractVector{JacobiPolynomial},x::Point) +function evaluate!(cache,f::AbstractVector{LegendrePolynomial},x::Point) r, cf, xs = cache xs[1] = x v = evaluate!(cf,f,xs) @@ -183,7 +191,7 @@ function evaluate!(cache,f::AbstractVector{JacobiPolynomial},x::Point) end function return_cache( - f::FieldGradientArray{N,<:AbstractVector{JacobiPolynomial}}, x::Point) where {N} + f::FieldGradientArray{N,<:AbstractVector{LegendrePolynomial}}, x::Point) where {N} xs = [x] cf = return_cache(f,xs) v = evaluate!(cf,f,xs) @@ -192,7 +200,7 @@ function return_cache( end function evaluate!( - cache, f::FieldGradientArray{N,<:AbstractVector{JacobiPolynomial}}, x::Point) where {N} + cache, f::FieldGradientArray{N,<:AbstractVector{LegendrePolynomial}}, x::Point) where {N} r, cf, xs = cache xs[1] = x v = evaluate!(cf,f,xs) @@ -205,44 +213,53 @@ end # Helpers -function _evaluate_1d_jp!(v::AbstractMatrix{T},x,order,d) where T - n = order + 1 - z = one(T) - @inbounds v[d,1] = z +function _evaluate_1d!( + ::Type{LegendrePType{K}}, v::AbstractMatrix{T},x,d) where {K,T<:Number} + + n = K + 1 + o = one(T) + @inbounds v[d,1] = o if n > 1 ξ = ( 2*x[d] - 1 ) for i in 2:n + # The sqrt(2i-1) factor normalizes the basis polynomial for L2 scalar product on ξ∈[0,1], indeed: + # ∫[0,1] Pn(2ξ-1)^2 dξ = 1/2 ∫[-1,1] Pn(t)^2 dt = 1/(2n+1) + # C.f. Eq. (1.25) in Section 1.1.5 in Ern & Guermond book (2013). @inbounds v[d,i] = sqrt(2*i-1)*jacobi(ξ,i-1,0,0) end end end -function _gradient_1d_jp!(v::AbstractMatrix{T},x,order,d) where T - n = order + 1 +function _gradient_1d!( + ::Type{LegendrePType{K}}, g::AbstractMatrix{T},x,d) where {K,T<:Number} + + n = K + 1 z = zero(T) - @inbounds v[d,1] = z + @inbounds g[d,1] = z if n > 1 ξ = ( 2*x[d] - 1 ) for i in 2:n - @inbounds v[d,i] = sqrt(2*i-1)*i*jacobi(ξ,i-2,1,1) + @inbounds g[d,i] = sqrt(2*i-1)*i*jacobi(ξ,i-2,1,1) end end end -function _hessian_1d_jp!(v::AbstractMatrix{T},x,order,d) where T - n = order + 1 +function _hessian_1d!( + ::Type{LegendrePType{K}}, h::AbstractMatrix{T},x,d) where {K,T<:Number} + + n = K + 1 z = zero(T) - @inbounds v[d,1] = z + @inbounds h[d,1] = z if n > 1 - @inbounds v[d,2] = z + @inbounds h[d,2] = z ξ = ( 2*x[d] - 1 ) for i in 3:n - @inbounds v[d,i] = sqrt(2*i-1)*(i*(i+1)/2)*jacobi(ξ,i-3,2,2) + @inbounds h[d,i] = sqrt(2*i-1)*(i*(i+1)/2)*jacobi(ξ,i-3,2,2) end end end -function _evaluate_nd_jp!( +function _evaluate_nd_leg!( v::AbstractVector{V}, x, orders, @@ -251,7 +268,7 @@ function _evaluate_nd_jp!( dim = D for d in 1:dim - _evaluate_1d_jp!(c,x,orders[d],d) + _evaluate_1d!(LegendrePType{orders[d]},c,x,d) end o = one(T) @@ -270,7 +287,7 @@ function _evaluate_nd_jp!( end -function _gradient_nd_jp!( +function _gradient_nd_leg!( v::AbstractVector{G}, x, orders, @@ -281,8 +298,7 @@ function _gradient_nd_jp!( dim = D for d in 1:dim - _evaluate_1d_jp!(c,x,orders[d],d) - _gradient_1d_jp!(g,x,orders[d],d) + _derivatives_1d!(LegendrePType{orders[d]},(c,g),x,d) end z = zero(Mutable(VectorValue{D,T})) @@ -311,7 +327,7 @@ function _gradient_nd_jp!( end -function _hessian_nd_jp!( +function _hessian_nd_leg!( v::AbstractVector{G}, x, orders, @@ -323,9 +339,7 @@ function _hessian_nd_jp!( dim = D for d in 1:dim - _evaluate_1d_jp!(c,x,orders[d],d) - _gradient_1d_jp!(g,x,orders[d],d) - _hessian_1d_jp!(h,x,orders[d],d) + _derivatives_1d!(LegendrePType{orders[d]},(c,g,h),x,d) end z = zero(Mutable(TensorValue{D,D,T})) diff --git a/src/Polynomials/ModalC0Bases.jl b/src/Polynomials/ModalC0Bases.jl index 3fbe918d2..e5a02cc53 100644 --- a/src/Polynomials/ModalC0Bases.jl +++ b/src/Polynomials/ModalC0Bases.jl @@ -308,11 +308,19 @@ function _define_terms_mc0(filter,sort!,orders) collect(lazy_map(Reindex(terms),mask)) end +""" +Reference: equation (16) in + +Badia, S.; Neiva, E. & Verdugo, F.; (2022); +Robust high-order unfitted finite elements by interpolation-based discrete extension, +Computers & Mathematics with Applications, +https://doi.org/10.1016/j.camwa.2022.09.027 +""" function _evaluate_1d_mc0!(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] + o = one(T) + @inbounds v[d,1] = o - x[d] @inbounds v[d,2] = x[d] if n > 2 ξ = ( 2*x[d] - ( a[d] + b[d] ) ) / ( b[d] - a[d] ) @@ -464,7 +472,7 @@ end v::AbstractVector{G},s,k,l,::Type{V}) where {V,G} # Git blame me for readable non-generated version @notimplementedif num_indep_components(G) != num_components(G) "Not implemented for symmetric Jacobian or Hessian" - + m = Array{String}(undef, size(G)) N_val_dims = length(size(V)) s_size = size(G)[1:end-N_val_dims] @@ -474,7 +482,7 @@ end id = join(Tuple(ci)) body *= "@inbounds s$id = s[$ci];" end - + V_size = size(V) for (ij,j) in enumerate(CartesianIndices(V_size)) for i in CartesianIndices(m) diff --git a/src/Polynomials/MonomialBases.jl b/src/Polynomials/MonomialBases.jl index f1452f34b..957906056 100644 --- a/src/Polynomials/MonomialBases.jl +++ b/src/Polynomials/MonomialBases.jl @@ -1,3 +1,11 @@ +""" + MonomialPType{K} <: PolynomialType{K} + +Type representing the monomial polynomials of order up to `K` +""" +struct MonomialPType{K} <: PolynomialType{K} end +isHierarchical(::MonomialPType) = true + struct Monomial <: Field end """ @@ -347,41 +355,45 @@ function _define_terms(filter,orders) [ ci for ci in cis if filter(Int[Tuple(ci-co)...],maxorder) ] end -function _evaluate_1d!(v::AbstractMatrix{T},x,order,d) where T - n = order + 1 - z = one(T) - @inbounds v[d,1] = z +function _evaluate_1d!( + ::Type{MonomialPType{K}}, v::AbstractMatrix{T},x,d) where {K,T<:Number} + + n = K + 1 @inbounds xd = x[d] - xn = xd - for i in 2:n + xn = one(T) + for i in 1:n @inbounds v[d,i] = xn xn *= xd end end -function _gradient_1d!(v::AbstractMatrix{T},x,order,d) where T - n = order + 1 +function _gradient_1d!( + ::Type{MonomialPType{K}}, g::AbstractMatrix{T},x,d) where {K,T<:Number} + + n = K + 1 z = zero(T) - @inbounds v[d,1] = z + @inbounds g[d,1] = z @inbounds xd = x[d] xn = one(T) for i in 2:n - @inbounds v[d,i] = (i-1)*xn + @inbounds g[d,i] = (i-1)*xn xn *= xd end end -function _hessian_1d!(v::AbstractMatrix{T},x,order,d) where T - n = order + 1 +function _hessian_1d!( + ::Type{MonomialPType{K}}, h::AbstractMatrix{T},x,d) where {K,T<:Number} + + n = K + 1 z = zero(T) - @inbounds v[d,1] = z + @inbounds h[d,1] = z if n>1 - @inbounds v[d,2] = z + @inbounds h[d,2] = z end @inbounds xd = x[d] xn = one(T) for i in 3:n - @inbounds v[d,i] = (i-1)*(i-2)*xn + @inbounds h[d,i] = (i-1)*(i-2)*xn xn *= xd end end @@ -395,7 +407,7 @@ function _evaluate_nd!( dim = D for d in 1:dim - _evaluate_1d!(c,x,orders[d],d) + _evaluate_1d!(MonomialPType{orders[d]},c,x,d) end o = one(T) @@ -441,8 +453,7 @@ function _gradient_nd!( dim = D for d in 1:dim - _evaluate_1d!(c,x,orders[d],d) - _gradient_1d!(g,x,orders[d],d) + _derivatives_1d!(MonomialPType{orders[d]},(c,g),x,d) end o = one(T) @@ -515,7 +526,7 @@ end @generated function _set_gradient!( v::AbstractVector{G},s,k,::Type{V}) where {V<:AbstractSymTensorValue{D},G} where D # Git blame me for readable non-generated version - + T = eltype(s) m = Array{String}(undef, size(G)) s_length = size(G)[1] @@ -524,10 +535,10 @@ end skip_last_diagval = is_traceless ? 1 : 0 # Skid V_DD if traceless body = "z = $(zero(T));" - for i in 1:s_length + for i in 1:s_length body *= "@inbounds s$i = s[$i];" end - + for c in 1:(D-skip_last_diagval) # Go over cols for r in c:D # Go over lower triangle, current col for i in eachindex(m) @@ -562,9 +573,7 @@ function _hessian_nd!( dim = D for d in 1:dim - _evaluate_1d!(c,x,orders[d],d) - _gradient_1d!(g,x,orders[d],d) - _hessian_1d!(h,x,orders[d],d) + _derivatives_1d!(MonomialPType{orders[d]},(c,g,h),x,d) end z = zero(Mutable(TensorValue{D,D,T})) diff --git a/src/Polynomials/PCurlGradJacobiPolynomialBases.jl b/src/Polynomials/PCurlGradLegendrePolynomialBases.jl similarity index 72% rename from src/Polynomials/PCurlGradJacobiPolynomialBases.jl rename to src/Polynomials/PCurlGradLegendrePolynomialBases.jl index 1b4cf9923..b3ed05f76 100644 --- a/src/Polynomials/PCurlGradJacobiPolynomialBases.jl +++ b/src/Polynomials/PCurlGradLegendrePolynomialBases.jl @@ -1,5 +1,5 @@ """ -struct PCurlGradJacobiPolynomialBasis{...} <: AbstractArray{JacobiPolynomial} +struct PCurlGradLegendrePolynomialBasis{...} <: AbstractArray{LegendrePolynomial} This type implements a multivariate vector-valued polynomial basis spanning the space needed for Raviart-Thomas reference elements on simplices. @@ -7,53 +7,53 @@ The type parameters and fields of this `struct` are not public. This type fully implements the [`Field`](@ref) interface, with up to first order derivatives. """ -struct PCurlGradJacobiPolynomialBasis{D,T} <: AbstractVector{JacobiPolynomial} +struct PCurlGradLegendrePolynomialBasis{D,T} <: AbstractVector{LegendrePolynomial} order::Int pterms::Array{CartesianIndex{D},1} sterms::Array{CartesianIndex{D},1} perms::Matrix{Int} - function PCurlGradJacobiPolynomialBasis(::Type{T},order::Int, + function PCurlGradLegendrePolynomialBasis(::Type{T},order::Int, pterms::Array{CartesianIndex{D},1},sterms::Array{CartesianIndex{D},1}, perms::Matrix{Int}) where {D,T} new{D,T}(order,pterms,sterms,perms) end end -Base.size(a::PCurlGradJacobiPolynomialBasis) = (_ndofs_pgrad(a),) +Base.size(a::PCurlGradLegendrePolynomialBasis) = (_ndofs_pgrad(a),) # @santiagobadia : Not sure we want to create the monomial machinery -Base.getindex(a::PCurlGradJacobiPolynomialBasis,i::Integer) = JacobiPolynomial() -Base.IndexStyle(::PCurlGradJacobiPolynomialBasis) = IndexLinear() +Base.getindex(a::PCurlGradLegendrePolynomialBasis,i::Integer) = LegendrePolynomial() +Base.IndexStyle(::PCurlGradLegendrePolynomialBasis) = IndexLinear() """ -PCurlGradJacobiPolynomialBasis{D}(::Type{T},order::Int) where {D,T} +PCurlGradLegendrePolynomialBasis{D}(::Type{T},order::Int) where {D,T} -Returns a `PCurlGradJacobiPolynomialBasis` object. `D` is the dimension +Returns a `PCurlGradLegendrePolynomialBasis` object. `D` is the dimension of the coordinate space and `T` is the type of the components in the vector-value. The `order` argument has the following meaning: the divergence of the functions in this basis is in the P space of degree `order`. """ -function PCurlGradJacobiPolynomialBasis{D}(::Type{T},order::Int) where {D,T} +function PCurlGradLegendrePolynomialBasis{D}(::Type{T},order::Int) where {D,T} @check T<:Real "T needs to be <:Real since represents the type of the components of the vector value" P_k = MonomialBasis{D}(T, order, _p_filter) S_k = MonomialBasis{D}(T, order, _s_filter) pterms = P_k.terms sterms = S_k.terms perms = _prepare_perms(D) - PCurlGradJacobiPolynomialBasis(T,order,pterms,sterms,perms) + PCurlGradLegendrePolynomialBasis(T,order,pterms,sterms,perms) end """ - num_terms(f::PCurlGradJacobiPolynomialBasis{D,T}) where {D,T} + num_terms(f::PCurlGradLegendrePolynomialBasis{D,T}) where {D,T} """ -function num_terms(f::PCurlGradJacobiPolynomialBasis{D,T}) where {D,T} +function num_terms(f::PCurlGradLegendrePolynomialBasis{D,T}) where {D,T} Int(_p_dim(f.order,D)*D + _p_dim(f.order,D-1)) end -get_order(f::PCurlGradJacobiPolynomialBasis{D,T}) where {D,T} = f.order +get_order(f::PCurlGradLegendrePolynomialBasis{D,T}) where {D,T} = f.order -return_type(::PCurlGradJacobiPolynomialBasis{D,T}) where {D,T} = VectorValue{D,T} +return_type(::PCurlGradLegendrePolynomialBasis{D,T}) where {D,T} = VectorValue{D,T} -function return_cache(f::PCurlGradJacobiPolynomialBasis{D,T},x::AbstractVector{<:Point}) where {D,T} +function return_cache(f::PCurlGradLegendrePolynomialBasis{D,T},x::AbstractVector{<:Point}) where {D,T} @check D == length(eltype(x)) "Incorrect number of point components" np = length(x) ndof = _ndofs_pgrad(f) @@ -65,7 +65,7 @@ function return_cache(f::PCurlGradJacobiPolynomialBasis{D,T},x::AbstractVector{< (r, v, c) end -function evaluate!(cache,f::PCurlGradJacobiPolynomialBasis{D,T},x::AbstractVector{<:Point}) where {D,T} +function evaluate!(cache,f::PCurlGradLegendrePolynomialBasis{D,T},x::AbstractVector{<:Point}) where {D,T} r, v, c = cache np = length(x) ndof = _ndofs_pgrad(f) @@ -75,7 +75,7 @@ function evaluate!(cache,f::PCurlGradJacobiPolynomialBasis{D,T},x::AbstractVecto setsize!(c,(D,n)) for i in 1:np @inbounds xi = x[i] - _evaluate_nd_pcurlgrad_jp!(v,xi,f.order+1,f.pterms,f.sterms,f.perms,c) + _evaluate_nd_pcurlgrad_leg!(v,xi,f.order+1,f.pterms,f.sterms,f.perms,c) for j in 1:ndof @inbounds r[i,j] = v[j] end @@ -84,7 +84,7 @@ function evaluate!(cache,f::PCurlGradJacobiPolynomialBasis{D,T},x::AbstractVecto end function return_cache( - fg::FieldGradientArray{1,PCurlGradJacobiPolynomialBasis{D,T}}, + fg::FieldGradientArray{1,PCurlGradLegendrePolynomialBasis{D,T}}, x::AbstractVector{<:Point}) where {D,T} f = fg.fa @@ -103,7 +103,7 @@ function return_cache( end function evaluate!(cache, - fg::FieldGradientArray{1,PCurlGradJacobiPolynomialBasis{D,T}}, + fg::FieldGradientArray{1,PCurlGradLegendrePolynomialBasis{D,T}}, x::AbstractVector{<:Point}) where {D,T} f = fg.fa @@ -118,7 +118,7 @@ function evaluate!(cache, V = VectorValue{D,T} for i in 1:np @inbounds xi = x[i] - _gradient_nd_pcurlgrad_jp!(v,xi,f.order+1,f.pterms,f.sterms,f.perms,c,g,V) + _gradient_nd_pcurlgrad_leg!(v,xi,f.order+1,f.pterms,f.sterms,f.perms,c,g,V) for j in 1:ndof @inbounds r[i,j] = v[j] end @@ -129,9 +129,9 @@ end # Helpers -_ndofs_pgrad(f::PCurlGradJacobiPolynomialBasis{D}) where D = num_terms(f) +_ndofs_pgrad(f::PCurlGradLegendrePolynomialBasis{D}) where D = num_terms(f) -function _evaluate_nd_pcurlgrad_jp!( +function _evaluate_nd_pcurlgrad_leg!( v::AbstractVector{V}, x, order, @@ -142,7 +142,7 @@ function _evaluate_nd_pcurlgrad_jp!( dim = D for d in 1:dim - _evaluate_1d_jp!(c,x,order,d) + _evaluate_1d!(LegendrePType{order},c,x,d) end o = one(T) @@ -188,7 +188,7 @@ function _evaluate_nd_pcurlgrad_jp!( end end -function _gradient_nd_pcurlgrad_jp!( +function _gradient_nd_pcurlgrad_leg!( v::AbstractVector{G}, x, order, @@ -201,8 +201,7 @@ function _gradient_nd_pcurlgrad_jp!( dim = D for d in 1:dim - _evaluate_1d_jp!(c,x,order,d) - _gradient_1d_jp!(g,x,order,d) + _derivatives_1d!(LegendrePType{order},(c,g),x,d) end z = zero(Mutable(V)) diff --git a/src/Polynomials/PCurlGradMonomialBases.jl b/src/Polynomials/PCurlGradMonomialBases.jl index 359886786..5f5358c55 100644 --- a/src/Polynomials/PCurlGradMonomialBases.jl +++ b/src/Polynomials/PCurlGradMonomialBases.jl @@ -155,7 +155,7 @@ function _evaluate_nd_pcurlgrad!( dim = D for d in 1:dim - _evaluate_1d!(c,x,order,d) + _evaluate_1d!(MonomialPType{order},c,x,d) end o = one(T) @@ -214,8 +214,7 @@ function _gradient_nd_pcurlgrad!( dim = D for d in 1:dim - _evaluate_1d!(c,x,order,d) - _gradient_1d!(g,x,order,d) + _derivatives_1d!(MonomialPType{order},(c,g),x,d) end z = zero(Mutable(V)) diff --git a/src/Polynomials/PolynomialInterfaces.jl b/src/Polynomials/PolynomialInterfaces.jl new file mode 100644 index 000000000..379ee6750 --- /dev/null +++ b/src/Polynomials/PolynomialInterfaces.jl @@ -0,0 +1,104 @@ +################################################# +# Polynomial family/types with their parameters # +################################################# + +""" +Abstract type for 1- or n-D polynomial family of maximum order K. + +`PolynomialType` subtypes must keep `K` as last type parameter. + +Implement [`isHierarchical`](@ref) and [`get_order`](@ref). +""" +abstract type PolynomialType{K} end + +""" +Return true if the polynomial basis of order `K` of the given type is the union +of the basis of order `K-1` and an other order `K` polynomial. +""" +isHierarchical(::PolynomialType) = @abstractmethod + +""" + get_order(PT<:PolynomialType{K}}) returns K +""" +get_order(::Type{PolynomialType{K}}) where K = K + + +#""" +# JacobiPType{α, β, K} <: PolynomialType{K} +# +#Type representing Jacobi polynomials of parameters `α` and `β` and order up to `K` +#""" +#struct JacobiPType{α, β, K} <: PolynomialType{K} end +#isHierarchical(::JacobiPType) = true + + +###################### +# 1D polynomial APIs # +###################### + +""" + _evaluate_1d!(::Type{<:PolynomialType{K}}, v, x, d) + +Evaluates in place the 1D basis polynomials of the given type at one nD point `x` +along the given coordinate 1 ≤ `d` ≤ nD. + +`v` is an AbstractMatrix of size (at least} d×(K+1), such that the 1 ≤ i ≤ `K`+1 +values are stored in `v[d,i]`. +""" +function _evaluate_1d!( + ::Type{<:PolynomialType{K}}, v::AbstractMatrix{T},x,d) where {K,T<:Number} + + @abstractmethod +end + +""" + _gradient_1d!(::Type{<:PolynomialType{K}}, g, x, d) + +Like [`_evaluate_1d!`](@ref), but computes the first derivative of the basis functions. +""" +function _gradient_1d!( + ::Type{<:PolynomialType{K}}, g::AbstractMatrix{T},x,d) where {K,T<:Number} + + @abstractmethod +end + +""" + _hessian_1d!(::Type{<:PolynomialType{K}}, h, x, d) + +Like [`_evaluate_1d!`](@ref), but computes the second derivative of the basis functions. +""" +function _hessian_1d!( + ::Type{<:PolynomialType{K}}, h::AbstractMatrix{T},x,d) where {K,T<:Number} + + @abstractmethod +end + +""" + _derivatives_1d!(PT::Type{<:PolynomialType{K}}, (v,g,...), x, d) + +Same as calling +``` +_evaluate_1d!(PT, v, x d) +_gradient_1d!(PT, g, x d) + ⋮ +``` +but with possible performence optimization. +""" +function _derivatives_1d!(PT::Type{<:PolynomialType},t::NTuple{N},x,d) where N + @abstractmethod +end + +function _derivatives_1d!(PT::Type{<:PolynomialType},t::NTuple{1},x,d) + _evaluate_1d!(PT, t[1], x, d) +end + +function _derivatives_1d!(PT::Type{<:PolynomialType},t::NTuple{2},x,d) + _evaluate_1d!(PT, t[1], x, d) + _gradient_1d!(PT, t[2], x, d) +end + +function _derivatives_1d!(PT::Type{<:PolynomialType},t::NTuple{3},x,d) + _evaluate_1d!(PT, t[1], x, d) + _gradient_1d!(PT, t[2], x, d) + _hessian_1d!( PT, t[3], x, d) +end diff --git a/src/Polynomials/Polynomials.jl b/src/Polynomials/Polynomials.jl index 81a4b04c2..a423ac023 100644 --- a/src/Polynomials/Polynomials.jl +++ b/src/Polynomials/Polynomials.jl @@ -26,10 +26,10 @@ export QGradMonomialBasis export QCurlGradMonomialBasis export PCurlGradMonomialBasis export ModalC0Basis -export JacobiPolynomialBasis -export QGradJacobiPolynomialBasis -export QCurlGradJacobiPolynomialBasis -export PCurlGradJacobiPolynomialBasis +export LegendrePolynomialBasis +export QGradLegendrePolynomialBasis +export QCurlGradLegendrePolynomialBasis +export PCurlGradLegendrePolynomialBasis export ChebyshevPolynomialBasis export QGradChebyshevPolynomialBasis export QCurlGradChebyshevPolynomialBasis @@ -39,8 +39,12 @@ export get_order export get_orders export num_terms +include("PolynomialInterfaces.jl") + include("MonomialBases.jl") +include("BernsteinBases.jl") + include("QGradMonomialBases.jl") include("QCurlGradMonomialBases.jl") @@ -49,11 +53,11 @@ include("PCurlGradMonomialBases.jl") include("ModalC0Bases.jl") -include("JacobiPolynomialBases.jl") +include("LegendrePolynomialBases.jl") -include("QGradJacobiPolynomialBases.jl") +include("QGradLegendrePolynomialBases.jl") -include("PCurlGradJacobiPolynomialBases.jl") +include("PCurlGradLegendrePolynomialBases.jl") include("ChebyshevPolynomialBases.jl") diff --git a/src/Polynomials/QGradJacobiPolynomialBases.jl b/src/Polynomials/QGradLegendrePolynomialBases.jl similarity index 58% rename from src/Polynomials/QGradJacobiPolynomialBases.jl rename to src/Polynomials/QGradLegendrePolynomialBases.jl index d160363ef..41d815ee4 100644 --- a/src/Polynomials/QGradJacobiPolynomialBases.jl +++ b/src/Polynomials/QGradLegendrePolynomialBases.jl @@ -1,6 +1,6 @@ """ - struct QGradJacobiPolynomialBasis{...} <: AbstractVector{Monomial} + struct QGradLegendrePolynomialBasis{...} <: AbstractVector{Monomial} This type implements a multivariate vector-valued polynomial basis spanning the space needed for Nedelec reference elements on n-cubes. @@ -8,50 +8,50 @@ The type parameters and fields of this `struct` are not public. This type fully implements the [`Field`](@ref) interface, with up to first order derivatives. """ -struct QGradJacobiPolynomialBasis{D,T} <: AbstractVector{JacobiPolynomial} +struct QGradLegendrePolynomialBasis{D,T} <: AbstractVector{LegendrePolynomial} order::Int terms::CartesianIndices{D} perms::Matrix{Int} - function QGradJacobiPolynomialBasis(::Type{T},order::Int,terms::CartesianIndices{D},perms::Matrix{Int}) where {D,T} + function QGradLegendrePolynomialBasis(::Type{T},order::Int,terms::CartesianIndices{D},perms::Matrix{Int}) where {D,T} new{D,T}(order,terms,perms) end end -Base.size(a::QGradJacobiPolynomialBasis) = (_ndofs_qgrad_jp(a),) -Base.getindex(a::QGradJacobiPolynomialBasis,i::Integer) = JacobiPolynomial() -Base.IndexStyle(::QGradJacobiPolynomialBasis) = IndexLinear() +Base.size(a::QGradLegendrePolynomialBasis) = (_ndofs_qgrad_leg(a),) +Base.getindex(a::QGradLegendrePolynomialBasis,i::Integer) = LegendrePolynomial() +Base.IndexStyle(::QGradLegendrePolynomialBasis) = IndexLinear() """ - QGradJacobiPolynomialBasis{D}(::Type{T},order::Int) where {D,T} + QGradLegendrePolynomialBasis{D}(::Type{T},order::Int) where {D,T} -Returns a `QGradJacobiPolynomialBasis` object. `D` is the dimension +Returns a `QGradLegendrePolynomialBasis` object. `D` is the dimension of the coordinate space and `T` is the type of the components in the vector-value. The `order` argument has the following meaning: the curl of the functions in this basis is in the Q space of degree `order`. """ -function QGradJacobiPolynomialBasis{D}(::Type{T},order::Int) where {D,T} +function QGradLegendrePolynomialBasis{D}(::Type{T},order::Int) where {D,T} @check T<:Real "T needs to be <:Real since represents the type of the components of the vector value" _order = order + 1 _t = tfill(_order+1,Val{D-1}()) t = (_order,_t...) terms = CartesianIndices(t) perms = _prepare_perms(D) - QGradJacobiPolynomialBasis(T,order,terms,perms) + QGradLegendrePolynomialBasis(T,order,terms,perms) end """ - num_terms(f::QGradJacobiPolynomialBasis{D,T}) where {D,T} + num_terms(f::QGradLegendrePolynomialBasis{D,T}) where {D,T} """ -num_terms(f::QGradJacobiPolynomialBasis{D,T}) where {D,T} = length(f.terms)*D +num_terms(f::QGradLegendrePolynomialBasis{D,T}) where {D,T} = length(f.terms)*D -get_order(f::QGradJacobiPolynomialBasis) = f.order +get_order(f::QGradLegendrePolynomialBasis) = f.order -return_type(::QGradJacobiPolynomialBasis{D,T}) where {D,T} = VectorValue{D,T} +return_type(::QGradLegendrePolynomialBasis{D,T}) where {D,T} = VectorValue{D,T} -function return_cache(f::QGradJacobiPolynomialBasis{D,T},x::AbstractVector{<:Point}) where {D,T} +function return_cache(f::QGradLegendrePolynomialBasis{D,T},x::AbstractVector{<:Point}) where {D,T} @check D == length(eltype(x)) "Incorrect number of point components" np = length(x) - ndof = _ndofs_qgrad_jp(f) + ndof = _ndofs_qgrad_leg(f) n = 1 + f.order+1 V = VectorValue{D,T} r = CachedArray(zeros(V,(np,ndof))) @@ -60,17 +60,17 @@ function return_cache(f::QGradJacobiPolynomialBasis{D,T},x::AbstractVector{<:Poi (r, v, c) end -function evaluate!(cache,f::QGradJacobiPolynomialBasis{D,T},x::AbstractVector{<:Point}) where {D,T} +function evaluate!(cache,f::QGradLegendrePolynomialBasis{D,T},x::AbstractVector{<:Point}) where {D,T} r, v, c = cache np = length(x) - ndof = _ndofs_qgrad_jp(f) + ndof = _ndofs_qgrad_leg(f) n = 1 + f.order+1 setsize!(r,(np,ndof)) setsize!(v,(ndof,)) setsize!(c,(D,n)) for i in 1:np @inbounds xi = x[i] - _evaluate_nd_qgrad_jp!(v,xi,f.order+1,f.terms,f.perms,c) + _evaluate_nd_qgrad_leg!(v,xi,f.order+1,f.terms,f.perms,c) for j in 1:ndof @inbounds r[i,j] = v[j] end @@ -79,13 +79,13 @@ function evaluate!(cache,f::QGradJacobiPolynomialBasis{D,T},x::AbstractVector{<: end function return_cache( - fg::FieldGradientArray{1,QGradJacobiPolynomialBasis{D,T}}, + fg::FieldGradientArray{1,QGradLegendrePolynomialBasis{D,T}}, x::AbstractVector{<:Point}) where {D,T} f = fg.fa @check D == length(eltype(x)) "Incorrect number of point components" np = length(x) - ndof = _ndofs_qgrad_jp(f) + ndof = _ndofs_qgrad_leg(f) n = 1 + f.order+1 xi = testitem(x) V = VectorValue{D,T} @@ -99,13 +99,13 @@ end function evaluate!( cache, - fg::FieldGradientArray{1,QGradJacobiPolynomialBasis{D,T}}, + fg::FieldGradientArray{1,QGradLegendrePolynomialBasis{D,T}}, x::AbstractVector{<:Point}) where {D,T} f = fg.fa r, v, c, g = cache np = length(x) - ndof = _ndofs_qgrad_jp(f) + ndof = _ndofs_qgrad_leg(f) n = 1 + f.order+1 setsize!(r,(np,ndof)) setsize!(v,(ndof,)) @@ -114,7 +114,7 @@ function evaluate!( V = VectorValue{D,T} for i in 1:np @inbounds xi = x[i] - _gradient_nd_qgrad_jp!(v,xi,f.order+1,f.terms,f.perms,c,g,V) + _gradient_nd_qgrad_leg!(v,xi,f.order+1,f.terms,f.perms,c,g,V) for j in 1:ndof @inbounds r[i,j] = v[j] end @@ -124,9 +124,9 @@ end # Helpers -_ndofs_qgrad_jp(f::QGradJacobiPolynomialBasis{D}) where D = D*(length(f.terms)) +_ndofs_qgrad_leg(f::QGradLegendrePolynomialBasis{D}) where D = D*(length(f.terms)) -function _evaluate_nd_qgrad_jp!( +function _evaluate_nd_qgrad_leg!( v::AbstractVector{V}, x, order, @@ -136,7 +136,7 @@ function _evaluate_nd_qgrad_jp!( dim = D for d in 1:dim - _evaluate_1d_jp!(c,x,order,d) + _evaluate_1d!(LegendrePType{order},c,x,d) end o = one(T) @@ -168,7 +168,7 @@ function _evaluate_nd_qgrad_jp!( end -function _gradient_nd_qgrad_jp!( +function _gradient_nd_qgrad_leg!( v::AbstractVector{G}, x, order, @@ -180,8 +180,7 @@ function _gradient_nd_qgrad_jp!( dim = D for d in 1:dim - _evaluate_1d_jp!(c,x,order,d) - _gradient_1d_jp!(g,x,order,d) + _derivatives_1d!(LegendrePType{order},(c,g),x,d) end z = zero(Mutable(V)) @@ -230,7 +229,7 @@ end ############################################################################################ """ - struct QCurlGradJacobiPolynomialBasis{...} <: AbstractArray{Monomial} + struct QCurlGradLegendrePolynomialBasis{...} <: AbstractArray{Monomial} This type implements a multivariate vector-valued polynomial basis spanning the space needed for Raviart-Thomas reference elements on n-cubes. @@ -238,48 +237,48 @@ The type parameters and fields of this `struct` are not public. This type fully implements the [`Field`](@ref) interface, with up to first order derivatives. """ -struct QCurlGradJacobiPolynomialBasis{D,T} <: AbstractVector{JacobiPolynomial} - qgrad::QGradJacobiPolynomialBasis{D,T} - function QCurlGradJacobiPolynomialBasis(::Type{T},order::Int,terms::CartesianIndices{D},perms::Matrix{Int}) where {D,T} - qgrad = QGradJacobiPolynomialBasis(T,order,terms,perms) +struct QCurlGradLegendrePolynomialBasis{D,T} <: AbstractVector{LegendrePolynomial} + qgrad::QGradLegendrePolynomialBasis{D,T} + function QCurlGradLegendrePolynomialBasis(::Type{T},order::Int,terms::CartesianIndices{D},perms::Matrix{Int}) where {D,T} + qgrad = QGradLegendrePolynomialBasis(T,order,terms,perms) new{D,T}(qgrad) end end -Base.size(a::QCurlGradJacobiPolynomialBasis) = (length(a.qgrad),) -Base.getindex(a::QCurlGradJacobiPolynomialBasis,i::Integer) = JacobiPolynomial() -Base.IndexStyle(::QCurlGradJacobiPolynomialBasis) = IndexLinear() +Base.size(a::QCurlGradLegendrePolynomialBasis) = (length(a.qgrad),) +Base.getindex(a::QCurlGradLegendrePolynomialBasis,i::Integer) = LegendrePolynomial() +Base.IndexStyle(::QCurlGradLegendrePolynomialBasis) = IndexLinear() """ - QCurlGradJacobiPolynomialBasis{D}(::Type{T},order::Int) where {D,T} + QCurlGradLegendrePolynomialBasis{D}(::Type{T},order::Int) where {D,T} -Returns a `QCurlGradJacobiPolynomialBasis` object. `D` is the dimension +Returns a `QCurlGradLegendrePolynomialBasis` object. `D` is the dimension of the coordinate space and `T` is the type of the components in the vector-value. The `order` argument has the following meaning: the divergence of the functions in this basis is in the Q space of degree `order`. """ -function QCurlGradJacobiPolynomialBasis{D}(::Type{T},order::Int) where {D,T} +function QCurlGradLegendrePolynomialBasis{D}(::Type{T},order::Int) where {D,T} @check T<:Real "T needs to be <:Real since represents the type of the components of the vector value" _order = order+1 _t = tfill(_order,Val{D-1}()) t = (_order+1,_t...) terms = CartesianIndices(t) perms = _prepare_perms(D) - QCurlGradJacobiPolynomialBasis(T,order,terms,perms) + QCurlGradLegendrePolynomialBasis(T,order,terms,perms) end -return_type(::QCurlGradJacobiPolynomialBasis{D,T}) where {D,T} = VectorValue{D,T} +return_type(::QCurlGradLegendrePolynomialBasis{D,T}) where {D,T} = VectorValue{D,T} -function return_cache(f::QCurlGradJacobiPolynomialBasis,x::AbstractVector{<:Point}) +function return_cache(f::QCurlGradLegendrePolynomialBasis,x::AbstractVector{<:Point}) return_cache(f.qgrad,x) end -function evaluate!(cache,f::QCurlGradJacobiPolynomialBasis,x::AbstractVector{<:Point}) +function evaluate!(cache,f::QCurlGradLegendrePolynomialBasis,x::AbstractVector{<:Point}) evaluate!(cache,f.qgrad,x) end function return_cache( - fg::FieldGradientArray{N,<:QCurlGradJacobiPolynomialBasis}, + fg::FieldGradientArray{N,<:QCurlGradLegendrePolynomialBasis}, x::AbstractVector{<:Point}) where N f = fg.fa @@ -288,7 +287,7 @@ end function evaluate!( cache, - fg::FieldGradientArray{N,<:QCurlGradJacobiPolynomialBasis}, + fg::FieldGradientArray{N,<:QCurlGradLegendrePolynomialBasis}, x::AbstractVector{<:Point}) where N f = fg.fa @@ -296,10 +295,10 @@ function evaluate!( end """ - num_terms(f::QCurlGradJacobiPolynomialBasis{D,T}) where {D,T} + num_terms(f::QCurlGradLegendrePolynomialBasis{D,T}) where {D,T} """ -num_terms(f::QCurlGradJacobiPolynomialBasis{D,T}) where {D,T} = length(f.qgrad.terms)*D +num_terms(f::QCurlGradLegendrePolynomialBasis{D,T}) where {D,T} = length(f.qgrad.terms)*D -get_order(f::QCurlGradJacobiPolynomialBasis{D,T}) where {D,T} = get_order(f.qgrad) +get_order(f::QCurlGradLegendrePolynomialBasis{D,T}) where {D,T} = get_order(f.qgrad) ############################################################################################ diff --git a/src/Polynomials/QGradMonomialBases.jl b/src/Polynomials/QGradMonomialBases.jl index f31319429..1a1386c71 100644 --- a/src/Polynomials/QGradMonomialBases.jl +++ b/src/Polynomials/QGradMonomialBases.jl @@ -150,7 +150,7 @@ function _evaluate_nd_qgrad!( dim = D for d in 1:dim - _evaluate_1d!(c,x,order,d) + _evaluate_1d!(MonomialPType{order},c,x,d) end o = one(T) @@ -194,8 +194,7 @@ function _gradient_nd_qgrad!( dim = D for d in 1:dim - _evaluate_1d!(c,x,order,d) - _gradient_1d!(g,x,order,d) + _derivatives_1d!(MonomialPType{order},(c,g),x,d) end z = zero(Mutable(V)) diff --git a/src/ReferenceFEs/RaviartThomasRefFEs.jl b/src/ReferenceFEs/RaviartThomasRefFEs.jl index 29844461b..40568b456 100644 --- a/src/ReferenceFEs/RaviartThomasRefFEs.jl +++ b/src/ReferenceFEs/RaviartThomasRefFEs.jl @@ -18,13 +18,13 @@ function RaviartThomasRefFE( ) where {T,D} if is_n_cube(p) - prebasis = QCurlGradJacobiPolynomialBasis{D}(T,order) # Prebasis - cb = QGradJacobiPolynomialBasis{D}(T,order-1) # Cell basis - fb = JacobiPolynomialBasis{D-1}(T,order,Polynomials._q_filter) # Face basis + prebasis = QCurlGradLegendrePolynomialBasis{D}(T,order) # Prebasis + cb = QGradLegendrePolynomialBasis{D}(T,order-1) # Cell basis + fb = LegendrePolynomialBasis{D-1}(T,order,Polynomials._q_filter) # Face basis elseif is_simplex(p) prebasis = PCurlGradMonomialBasis{D}(T,order) # Prebasis - cb = JacobiPolynomialBasis{D}(VectorValue{D,T},order-1,Polynomials._p_filter) # Cell basis - fb = JacobiPolynomialBasis{D-1}(T,order,Polynomials._p_filter) # Face basis + cb = LegendrePolynomialBasis{D}(VectorValue{D,T},order-1,Polynomials._p_filter) # Cell basis + fb = LegendrePolynomialBasis{D-1}(T,order,Polynomials._p_filter) # Face basis else @notimplemented "Raviart-Thomas Reference FE only available for cubes and simplices" end @@ -76,17 +76,17 @@ function get_face_own_dofs(reffe::GenericRefFE{RaviartThomas}, conf::DivConformi end # TODO: Please remove me -function JacobiBasis(::Type{T},p::Polytope,orders) where T - compute_jacobi_basis(T,p,orders) +function LegendreBasis(::Type{T},p::Polytope,orders) where T + compute_legendre_basis(T,p,orders) end -function JacobiBasis(::Type{T},p::Polytope{D},order::Int) where {D,T} +function LegendreBasis(::Type{T},p::Polytope{D},order::Int) where {D,T} orders = tfill(order,Val{D}()) - JacobiBasis(T,p,orders) + LegendreBasis(T,p,orders) end -function compute_jacobi_basis(::Type{T},p::ExtrusionPolytope{D},orders) where {D,T} +function compute_legendre_basis(::Type{T},p::ExtrusionPolytope{D},orders) where {D,T} extrusion = Tuple(p.extrusion) terms = _monomial_terms(extrusion,orders) - JacobiPolynomialBasis{D}(T,orders,terms) + LegendrePolynomialBasis{D}(T,orders,terms) end # ContraVariantPiolaMap diff --git a/src/ReferenceFEs/deprecated/RaviartThomasRefFEs.jl b/src/ReferenceFEs/deprecated/RaviartThomasRefFEs.jl index 11d7f9b8a..7b46b5a5e 100644 --- a/src/ReferenceFEs/deprecated/RaviartThomasRefFEs.jl +++ b/src/ReferenceFEs/deprecated/RaviartThomasRefFEs.jl @@ -16,7 +16,7 @@ is in the Q space of degree `order`. function RaviartThomasRefFE( ::Type{et},p::Polytope,order::Integer;basis_type=:monomial,phi=GenericField(identity) ) where et - @assert basis_type ∈ (:monomial, :jacobi, :chebyshev) + @assert basis_type ∈ (:monomial, :legendre, :chebyshev) D = num_dims(p) @@ -24,10 +24,10 @@ function RaviartThomasRefFE( prebasis = QCurlGradMonomialBasis{D}(et,order) elseif is_simplex(p) && basis_type == :monomial prebasis = PCurlGradMonomialBasis{D}(et,order) - elseif is_n_cube(p) && basis_type == :jacobi - prebasis = QCurlGradJacobiPolynomialBasis{D}(et,order) - elseif is_simplex(p) && basis_type == :jacobi - prebasis = PCurlGradJacobiPolynomialBasis{D}(et,order) + elseif is_n_cube(p) && basis_type == :legendre + prebasis = QCurlGradLegendrePolynomialBasis{D}(et,order) + elseif is_simplex(p) && basis_type == :legendre + prebasis = PCurlGradLegendrePolynomialBasis{D}(et,order) elseif is_n_cube(p) && basis_type == :chebyshev prebasis = QCurlGradChebyshevPolynomialBasis{D}(et,order) else @@ -196,7 +196,7 @@ function _RT_face_values(p,et,order,phi) # Moments (fmoments) # The RT prebasis is expressed in terms of shape function #fshfs = MonomialBasis(et,fp,order) - fshfs = JacobiBasis(et,fp,order) + fshfs = LegendreBasis(et,fp,order) #fshfs = ChebyshevBasis(et,fp,order) #fshfs = get_shapefuns(LagrangianRefFE(et,fp,order)) @@ -206,17 +206,17 @@ function _RT_face_values(p,et,order,phi) return fcips, fmoments end -function JacobiBasis(::Type{T},p::Polytope,orders) where T - compute_jacobi_basis(T,p,orders) +function LegendreBasis(::Type{T},p::Polytope,orders) where T + compute_legendre_basis(T,p,orders) end -function JacobiBasis(::Type{T},p::Polytope{D},order::Int) where {D,T} +function LegendreBasis(::Type{T},p::Polytope{D},order::Int) where {D,T} orders = tfill(order,Val{D}()) - JacobiBasis(T,p,orders) + LegendreBasis(T,p,orders) end -function compute_jacobi_basis(::Type{T},p::ExtrusionPolytope{D},orders) where {D,T} +function compute_legendre_basis(::Type{T},p::ExtrusionPolytope{D},orders) where {D,T} extrusion = Tuple(p.extrusion) terms = _monomial_terms(extrusion,orders) - JacobiPolynomialBasis{D}(T,orders,terms) + LegendrePolynomialBasis{D}(T,orders,terms) end function ChebyshevBasis(::Type{T},p::Polytope,orders) where T @@ -251,7 +251,7 @@ function _RT_cell_values(p,et,order,phi) # Cell moments, i.e., M(C)_{ab} = q_C^a(xgp_C^b) w_C^b ⋅ () if is_n_cube(p) #cbasis = QGradMonomialBasis{num_dims(p)}(et,order-1) - cbasis = QGradJacobiPolynomialBasis{num_dims(p)}(et,order-1) + cbasis = QGradLegendrePolynomialBasis{num_dims(p)}(et,order-1) #cbasis = QGradChebyshevPolynomialBasis{num_dims(p)}(et,order-1) #cbasis = get_shapefuns(RaviartThomasRefFE(et,p,order-1)) elseif is_simplex(p) diff --git a/test/PolynomialsTests/JacobiPolynomialBasesTests.jl b/test/PolynomialsTests/LegendrePolynomialBasesTests.jl similarity index 85% rename from test/PolynomialsTests/JacobiPolynomialBasesTests.jl rename to test/PolynomialsTests/LegendrePolynomialBasesTests.jl index fdbfe87fc..94d356e2a 100644 --- a/test/PolynomialsTests/JacobiPolynomialBasesTests.jl +++ b/test/PolynomialsTests/LegendrePolynomialBasesTests.jl @@ -1,4 +1,4 @@ -module JacobiPolynomialBasisTests +module LegendrePolynomialBasisTests using Test using Gridap.TensorValues @@ -17,24 +17,24 @@ G = gradient_type(V,x1) H = gradient_type(G,x1) order = 3 -b1 = JacobiPolynomialBasis{1}(V,order) +b1 = LegendrePolynomialBasis{1}(V,order) ∇b1 = Broadcasting(∇)(b1) ∇∇b1 = Broadcasting(∇)(∇b1) @test evaluate(b1,[x1,x2,x3,]) ≈ [ 1.0 -1.7320508075688772 2.23606797749979 -2.6457513110645907; 1.0 0.0 -1.118033988749895 -0.0; 1.0 1.7320508075688772 2.23606797749979 2.6457513110645907 ] -@test evaluate(∇b1,[x1,x2,x3,]) ≈ G[ (0.0,) (3.4641016151377544,) (-13.416407864998739,) (31.74901573277509,); - (0.0,) (3.4641016151377544,) (0.0,) (-7.937253933193772,); +@test evaluate(∇b1,[x1,x2,x3,]) ≈ G[ (0.0,) (3.4641016151377544,) (-13.416407864998739,) (31.74901573277509,); + (0.0,) (3.4641016151377544,) (0.0,) (-7.937253933193772,); (0.0,) (3.4641016151377544,) (13.416407864998739,) (31.74901573277509,) ] -@test evaluate(∇∇b1,[x1,x2,x3,]) ≈ H[ (0.0,) (0.0,) (13.416407864998739,) (-79.37253933193772,); +@test evaluate(∇∇b1,[x1,x2,x3,]) ≈ H[ (0.0,) (0.0,) (13.416407864998739,) (-79.37253933193772,); (0.0,) (0.0,) (13.416407864998739,) (0.0,); (0.0,) (0.0,) (13.416407864998739,) (79.37253933193772,) ] x1 = Point(0.0,0.0) x2 = Point(0.5,0.5) x3 = Point(1.0,1.0) -b2 = JacobiPolynomialBasis{2}(V,order) +b2 = LegendrePolynomialBasis{2}(V,order) ∇b2 = Broadcasting(∇)(b2) ∇∇b2 = Broadcasting(∇)(∇b2) @@ -45,18 +45,18 @@ H = gradient_type(G,x1) =# -1.7320508075688772 2.9999999999999996 -3.872983346207417 #= =# 4.58257569495584 2.23606797749979 -3.872983346207417 #= =# 5.000000000000001 -5.916079783099617 -2.6457513110645907 #= - =# 4.58257569495584 -5.916079783099617 7.000000000000001; + =# 4.58257569495584 -5.916079783099617 7.000000000000001; 1.0 0.0 -1.118033988749895 -0.0 0.0 0.0 -0.0 -0.0 #= - =# -1.118033988749895 -0.0 1.2500000000000002 0.0 -0.0 -0.0 0.0 0.0; - 1.0 1.7320508075688772 2.23606797749979 2.6457513110645907 #= + =# -1.118033988749895 -0.0 1.2500000000000002 0.0 -0.0 -0.0 0.0 0.0; + 1.0 1.7320508075688772 2.23606797749979 2.6457513110645907 #= =# 1.7320508075688772 2.9999999999999996 3.872983346207417 #= =# 4.58257569495584 2.23606797749979 3.872983346207417 #= - =# 5.000000000000001 5.916079783099617 2.6457513110645907 #= + =# 5.000000000000001 5.916079783099617 2.6457513110645907 #= =# 4.58257569495584 5.916079783099617 7.000000000000001 ] -@test evaluate(∇b2,[x1,x2,x3,])[:,10] ≈ G[ (7.745966692414834, 23.2379000772445); - (-3.872983346207417, 0.0); +@test evaluate(∇b2,[x1,x2,x3,])[:,10] ≈ G[ (7.745966692414834, 23.2379000772445); + (-3.872983346207417, 0.0); (7.745966692414834, 23.2379000772445) ] -@test evaluate(∇∇b2,[x1,x2,x3,])[:,10] ≈ H[ (0.0, -46.475800154489, -46.475800154489, -23.2379000772445); +@test evaluate(∇∇b2,[x1,x2,x3,])[:,10] ≈ H[ (0.0, -46.475800154489, -46.475800154489, -23.2379000772445); (-0.0, 0.0, 0.0, 0.0); (0.0, 46.475800154489, 46.475800154489, 23.2379000772445) ] diff --git a/test/PolynomialsTests/runtests.jl b/test/PolynomialsTests/runtests.jl index 813a48417..c28a2b0cf 100644 --- a/test/PolynomialsTests/runtests.jl +++ b/test/PolynomialsTests/runtests.jl @@ -12,7 +12,7 @@ using Test @testset "ModalC0Bases" begin include("ModalC0BasesTests.jl") end -@testset "JacobiPolynomialBases" begin include("JacobiPolynomialBasesTests.jl") end +@testset "LegendrePolynomialBases" begin include("LegendrePolynomialBasesTests.jl") end #@testset "ChangeBasis" begin include("ChangeBasisTests.jl") end From b460249a8618135dd738ab8943c14861384d5934 Mon Sep 17 00:00:00 2001 From: JordiManyer Date: Tue, 10 Dec 2024 22:35:43 +1100 Subject: [PATCH 034/105] Minor --- test/.gitignore | 2 -- 1 file changed, 2 deletions(-) delete mode 100644 test/.gitignore diff --git a/test/.gitignore b/test/.gitignore deleted file mode 100644 index 5c07398da..000000000 --- a/test/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -*.jl -!runtests.jl \ No newline at end of file From b0329b65c8a3e06dc1cd7aea0993aeb5b33080e5 Mon Sep 17 00:00:00 2001 From: JordiManyer Date: Tue, 10 Dec 2024 23:28:41 +1100 Subject: [PATCH 035/105] Started reformatting docs --- docs/src/ReferenceFEs.md | 159 ++++++++++++++++++- src/ReferenceFEs/DuffyQuadratures.jl | 10 ++ src/ReferenceFEs/StrangQuadratures.jl | 10 ++ src/ReferenceFEs/TensorProductQuadratures.jl | 10 ++ src/ReferenceFEs/XiaoGimbutasQuadratures.jl | 28 +++- 5 files changed, 207 insertions(+), 10 deletions(-) diff --git a/docs/src/ReferenceFEs.md b/docs/src/ReferenceFEs.md index 430b3cb4f..9cf114fbf 100644 --- a/docs/src/ReferenceFEs.md +++ b/docs/src/ReferenceFEs.md @@ -4,7 +4,162 @@ CurrentModule = Gridap.ReferenceFEs # Gridap.ReferenceFEs -```@autodocs -Modules = [ReferenceFEs,] +## Polytopes + +```@docs +Polytope +Base.== +test_polytope +``` + +### Polytope Geometry + +```@docs +get_edge_tangent +get_facet_normal +get_facet_orientations +get_vertex_coordinates +get_face_coordinates +get_bounding_box +``` + +### Polytope Topology + +```@docs +num_dims +num_faces +num_facets +num_edges +num_vertices +get_faces +get_dimranges +get_dimrange +get_facedims +get_reffaces +get_face_dimranges +get_face_type +get_face_vertices +get_face_vertex_permutations +get_vertex_permutations +get_offsets +get_offset +is_n_cube +is_simplex +simplexify ``` +### Extrusion Polytopes + +```@docs +ExtrusionPolytope +get_extrusion +HEX_AXIS +TET_AXIS +VERTEX +SEGMENT +TRI +QUAD +TET +HEX +WEDGE +PYRAMID +next_corner! +``` + +### General Polytopes + +```@docs +GeneralPolytope +Polygon +Polyhedron +get_graph +get_metadata +isopen +isactive +check_polytope_graph +simplexify_interior +simplexify_surface +``` + +## Quadratures + +```@docs +Quadrature +get_coordinates +get_weights +get_name +num_points +num_point_dims +num_dims +test_quadratures +GenericQuadrature +``` + +### Available Quadratures + +```@docs +TensorProduct +Duffy +Strang +XiaoGimbutas +``` + +## ReferenceFEs + +```@docs +ReferenceFE +ReferenceFEName +GenericReferenceFE +num_dims +num_cell_dims +num_point_dims +num_faces +num_vertices +num_edges +num_facets +num_dofs +get_polytope +get_prebasis +get_shapefuns +get_dof_basis +compute_shapefuns +compute_dofs +Conformity +get_face_dofs +get_face_own_dofs +get_face_own_dofs_permutations +test_reference_fe +``` + +### Nodal ReferenceFEs + +```@docs +LagrangianRefFE +is_first_order +is_P +is_Q +is_S +compute_monomial_basis +compute_own_nodes +compute_face_orders +compute_nodes +compute_own_nodes_permutations +compute_lagrangian_reffaces +VERTEX1 +SEG2 +QUAD4 +TRI3 +TET4 +HEX8 +``` + +### Moment-Based ReferenceFEs + +```@docs +MomentBasedReferenceFE +RaviartThomasRefFE +NedelecRefFE +BDMRefFE +ArnoldWintherRefFE +MardalTaiWintherRefFE +``` diff --git a/src/ReferenceFEs/DuffyQuadratures.jl b/src/ReferenceFEs/DuffyQuadratures.jl index ffe444007..c7f3796d4 100644 --- a/src/ReferenceFEs/DuffyQuadratures.jl +++ b/src/ReferenceFEs/DuffyQuadratures.jl @@ -1,4 +1,14 @@ +""" + struct Duffy <: QuadratureName + +Duffy quadrature rule for simplices, obtained as the mapped +tensor product of 1d Gauss-Jacobi and Gauss-Legendre quadratures. + +# Constructor: + + Quadrature(p::Polytope,duffy,degree::Integer;T::Type{<:AbstractFloat}=Float64) +""" struct Duffy <: QuadratureName end const duffy = Duffy() diff --git a/src/ReferenceFEs/StrangQuadratures.jl b/src/ReferenceFEs/StrangQuadratures.jl index dc3054fe9..999640f16 100644 --- a/src/ReferenceFEs/StrangQuadratures.jl +++ b/src/ReferenceFEs/StrangQuadratures.jl @@ -1,5 +1,15 @@ +""" + struct Strang <: QuadratureName + +Strang quadrature rule for simplices. + +# Constructor: + + Quadrature(p::Polytope,strang,degree::Integer;T::Type{<:AbstractFloat}=Float64) +""" struct Strang <: QuadratureName end + const strang = Strang() function Quadrature(p::Polytope,::Strang,degree::Integer;T::Type{<:AbstractFloat}=Float64) diff --git a/src/ReferenceFEs/TensorProductQuadratures.jl b/src/ReferenceFEs/TensorProductQuadratures.jl index 2b47fbdfc..b8a731c62 100644 --- a/src/ReferenceFEs/TensorProductQuadratures.jl +++ b/src/ReferenceFEs/TensorProductQuadratures.jl @@ -1,4 +1,14 @@ +""" + struct TensorProduct <: QuadratureName + +Tensor product quadrature rule for n-cubes, obtained as the +tensor product of 1d Gauss-Legendre quadratures. + +# Constructor: + + Quadrature(p::Polytope{D},tensor_product,degrees::Union{Integer,NTuple{D,Integer}};T::Type{<:AbstractFloat}=Float64) where D +""" struct TensorProduct <: QuadratureName end const tensor_product = TensorProduct() diff --git a/src/ReferenceFEs/XiaoGimbutasQuadratures.jl b/src/ReferenceFEs/XiaoGimbutasQuadratures.jl index 7fd185d28..21be9a1f3 100644 --- a/src/ReferenceFEs/XiaoGimbutasQuadratures.jl +++ b/src/ReferenceFEs/XiaoGimbutasQuadratures.jl @@ -1,18 +1,30 @@ +""" + struct XiaoGimbutas <: QuadratureName end + +Xiao-Gimbutas symmetric quadrature rule for simplices. + +# Constructor: + + `Quadrature(p::Polytope,xiao_gimbutas::XiaoGimbutas,degree::Integer;T::Type{<:AbstractFloat}=Float64)` + +# Reference: + + `A numerical algorithm for the construction of efficient quadrature rules in two and higher dimensions`, + Hong Xiao, Zydrunas Gimbutas, Computers & Mathematics with Applications, (2010), + DOI : https://doi.org/10.1016/j.camwa.2009.10.027 + +Adapted from: https://github.com/FEniCS/basix/blob/main/cpp/basix/quadrature.cpp +""" struct XiaoGimbutas <: QuadratureName end + const xiao_gimbutas = XiaoGimbutas() -# Reference: -# `A numerical algorithm for the construction of efficient quadrature rules in two and higher dimensions`, -# Hong Xiao, Zydrunas Gimbutas, Computers & Mathematics with Applications, (2010) -# DOI : https://doi.org/10.1016/j.camwa.2009.10.027 -# Adapted from: -# https://github.com/FEniCS/basix/blob/main/cpp/basix/quadrature.cpp function Quadrature( p::Polytope,::XiaoGimbutas,degree::Integer;T::Type{<:AbstractFloat}=Float64 ) msg = """\n - `strang` quadrature rule only available for simplices. + `xiao_gimbutas` quadrature rule only available for simplices. Use `tensor_product` for n-cubes. """ @assert is_simplex(p) msg @@ -24,7 +36,7 @@ function Quadrature( x, w = _xiaogimbutas_quad_tet(degree) else msg = """\n - `strang` quadrature rule only available for tris and tets. + `xiao_gimbutas` quadrature rule only available for tris and tets. Use `duffy` for other simplices. """ @unreachable msg From d3b715922ce222acecf8ad8b6ce1f61aaab7f6a2 Mon Sep 17 00:00:00 2001 From: JordiManyer Date: Wed, 11 Dec 2024 00:35:51 +1100 Subject: [PATCH 036/105] More docs --- docs/src/ReferenceFEs.md | 167 +++++++-------------------- src/ReferenceFEs/LagrangianRefFEs.jl | 8 +- 2 files changed, 45 insertions(+), 130 deletions(-) diff --git a/docs/src/ReferenceFEs.md b/docs/src/ReferenceFEs.md index 9cf114fbf..c67284703 100644 --- a/docs/src/ReferenceFEs.md +++ b/docs/src/ReferenceFEs.md @@ -4,162 +4,77 @@ CurrentModule = Gridap.ReferenceFEs # Gridap.ReferenceFEs -## Polytopes - -```@docs -Polytope -Base.== -test_polytope +```@contents +Pages = ["ReferenceFEs.md"] +Depth = 2:3 ``` -### Polytope Geometry +## Polytopes -```@docs -get_edge_tangent -get_facet_normal -get_facet_orientations -get_vertex_coordinates -get_face_coordinates -get_bounding_box -``` +### Abstract API -### Polytope Topology - -```@docs -num_dims -num_faces -num_facets -num_edges -num_vertices -get_faces -get_dimranges -get_dimrange -get_facedims -get_reffaces -get_face_dimranges -get_face_type -get_face_vertices -get_face_vertex_permutations -get_vertex_permutations -get_offsets -get_offset -is_n_cube -is_simplex -simplexify +```@autodocs +Modules = [ReferenceFEs,] +Order = [:type, :constant, :macro, :function] +Pages = ["/Polytopes.jl"] ``` ### Extrusion Polytopes -```@docs -ExtrusionPolytope -get_extrusion -HEX_AXIS -TET_AXIS -VERTEX -SEGMENT -TRI -QUAD -TET -HEX -WEDGE -PYRAMID -next_corner! +```@autodocs +Modules = [ReferenceFEs,] +Order = [:type, :constant, :macro, :function] +Pages = ["ExtrusionPolytopes.jl"] ``` ### General Polytopes -```@docs -GeneralPolytope -Polygon -Polyhedron -get_graph -get_metadata -isopen -isactive -check_polytope_graph -simplexify_interior -simplexify_surface +```@autodocs +Modules = [ReferenceFEs,] +Order = [:type, :constant, :macro, :function] +Pages = ["GeneralPolytopes.jl"] ``` ## Quadratures -```@docs -Quadrature -get_coordinates -get_weights -get_name -num_points -num_point_dims -num_dims -test_quadratures -GenericQuadrature +### Abstract API + +```@autodocs +Modules = [ReferenceFEs,] +Order = [:type, :constant, :macro, :function] +Pages = ["/Quadratures.jl"] ``` ### Available Quadratures -```@docs -TensorProduct -Duffy -Strang -XiaoGimbutas +```@autodocs +Modules = [ReferenceFEs,] +Order = [:type, :constant, :macro, :function] +Pages = ["TensorProductQuadratures.jl","DuffyQuadratures.jl","StrangeQuadratures.jl","XiaoGimbutasQuadratures.jl"] ``` ## ReferenceFEs -```@docs -ReferenceFE -ReferenceFEName -GenericReferenceFE -num_dims -num_cell_dims -num_point_dims -num_faces -num_vertices -num_edges -num_facets -num_dofs -get_polytope -get_prebasis -get_shapefuns -get_dof_basis -compute_shapefuns -compute_dofs -Conformity -get_face_dofs -get_face_own_dofs -get_face_own_dofs_permutations -test_reference_fe +### Abstract API + +```@autodocs +Modules = [ReferenceFEs,] +Order = [:type, :constant, :macro, :function] +Pages = ["ReferenceFEInterfaces.jl","Dofs.jl"] ``` ### Nodal ReferenceFEs -```@docs -LagrangianRefFE -is_first_order -is_P -is_Q -is_S -compute_monomial_basis -compute_own_nodes -compute_face_orders -compute_nodes -compute_own_nodes_permutations -compute_lagrangian_reffaces -VERTEX1 -SEG2 -QUAD4 -TRI3 -TET4 -HEX8 +```@autodocs +Modules = [ReferenceFEs,] +Order = [:type, :constant, :macro, :function] +Pages = ["LagrangianRefFEs.jl","LagrangianDofBases.jl","SerendipityRefFEs.jl","BezierRefFEs.jl","ModalC0RefFEs.jl"] ``` ### Moment-Based ReferenceFEs -```@docs -MomentBasedReferenceFE -RaviartThomasRefFE -NedelecRefFE -BDMRefFE -ArnoldWintherRefFE -MardalTaiWintherRefFE +```@autodocs +Modules = [ReferenceFEs,] +Order = [:type, :constant, :macro, :function] +Pages = ["MomentBasedReferenceFEs.jl","RaviartThomasRefFEs.jl","NedelecRefFEs.jl","BDMRefFEs.jl","CRRefFEs.jl"] ``` diff --git a/src/ReferenceFEs/LagrangianRefFEs.jl b/src/ReferenceFEs/LagrangianRefFEs.jl index 0c6b738ba..28a903c63 100644 --- a/src/ReferenceFEs/LagrangianRefFEs.jl +++ b/src/ReferenceFEs/LagrangianRefFEs.jl @@ -340,10 +340,10 @@ end # Generic implementation """ - struct GenericLagrangianRefFE{C,D} <: LagrangianRefFE{D} - reffe::GenericRefFE{C,D} - face_nodes::Vector{Vector{Int}} - end + struct GenericLagrangianRefFE{C,D} <: LagrangianRefFE{D} + reffe::GenericRefFE{C,D} + face_nodes::Vector{Vector{Int}} + end """ struct GenericLagrangianRefFE{C,D} <: LagrangianRefFE{D} reffe::GenericRefFE{C,D} From 124ae27fceb0e6e5c6fd4c753552d5ee82ce81a2 Mon Sep 17 00:00:00 2001 From: JordiManyer Date: Wed, 11 Dec 2024 07:54:50 +1100 Subject: [PATCH 037/105] Minor --- docs/src/ReferenceFEs.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/src/ReferenceFEs.md b/docs/src/ReferenceFEs.md index c67284703..ff6bee434 100644 --- a/docs/src/ReferenceFEs.md +++ b/docs/src/ReferenceFEs.md @@ -60,7 +60,7 @@ Pages = ["TensorProductQuadratures.jl","DuffyQuadratures.jl","StrangeQuadratur ```@autodocs Modules = [ReferenceFEs,] Order = [:type, :constant, :macro, :function] -Pages = ["ReferenceFEInterfaces.jl","Dofs.jl"] +Pages = ["ReferenceFEInterfaces.jl","Dofs.jl","LinearCombinationDofVectors.jl"] ``` ### Nodal ReferenceFEs From aed4682a84770831e6cfa21e47f619e57f571b0b Mon Sep 17 00:00:00 2001 From: Antoine Marteau Date: Wed, 11 Dec 2024 15:41:11 +1100 Subject: [PATCH 038/105] renamed this _s_filter another one exists in Polynomials.PCurlGradMonomialBases.jl, that could lead to bugs in further refactoning --- src/ReferenceFEs/SerendipityRefFEs.jl | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/ReferenceFEs/SerendipityRefFEs.jl b/src/ReferenceFEs/SerendipityRefFEs.jl index 935a72c0e..b087a04eb 100644 --- a/src/ReferenceFEs/SerendipityRefFEs.jl +++ b/src/ReferenceFEs/SerendipityRefFEs.jl @@ -26,7 +26,7 @@ println( num_dofs(reffe) ) function SerendipityRefFE(::Type{T},p::Polytope,order::Int) where T @assert is_n_cube(p) "Polytope not compatible with serendipity elements" if order > 0 - sp = SerendipityPolytope(p) + sp = SerendipityPolytope(p) else sp = p end @@ -87,12 +87,12 @@ get_extrusion(p::SerendipityPolytope{D}) where D = Point(tfill(HEX_AXIS,Val{D}() # Implemented polytope interface for LagrangianRefFEs -function _s_filter(e,order) +function _ser_filter(e,order) sum( [ i for i in e if i>1 ] ) <= order end function compute_monomial_basis(::Type{T},p::SerendipityPolytope{D},orders) where {T,D} - MonomialBasis{D}(T,orders,_s_filter) + MonomialBasis{D}(T,orders,_ser_filter) end function compute_own_nodes(p::SerendipityPolytope{0},orders) From 8118a5511da23cda8df41afa9e3ee985e2b23ba4 Mon Sep 17 00:00:00 2001 From: JordiManyer Date: Wed, 11 Dec 2024 16:50:57 +1100 Subject: [PATCH 039/105] Save progress --- src/ReferenceFEs/Pullbacks.jl | 66 +++++++++++++++++------------------ 1 file changed, 33 insertions(+), 33 deletions(-) diff --git a/src/ReferenceFEs/Pullbacks.jl b/src/ReferenceFEs/Pullbacks.jl index 36e2d7a03..de49f3a21 100644 --- a/src/ReferenceFEs/Pullbacks.jl +++ b/src/ReferenceFEs/Pullbacks.jl @@ -1,63 +1,63 @@ """ - abstract type Pullback <: Map end + abstract type Pushforward <: Map end -Represents a pullback map F*, defined as +Represents a pushforward map F*, defined as F* : V̂ -> V where - V̂ is a function space on the reference cell K̂ and - V is a function space on the physical cell K. """ -abstract type Pullback <: Map end +abstract type Pushforward <: Map end function lazy_map( - ::Broadcasting{typeof(gradient)}, a::LazyArray{<:Fill{Broadcasting{Operation{<:Pullback}}}} + ::Broadcasting{typeof(gradient)}, a::LazyArray{<:Fill{Broadcasting{Operation{<:Pushforward}}}} ) cell_ref_basis, args = a.args cell_ref_gradient = lazy_map(Broadcasting(∇),cell_ref_basis) return lazy_map(a.maps.value,cell_ref_gradient,args...) end -# InversePullback +# InversePushforward """ - struct InversePullback{PB <: Pullback} <: Map end + struct InversePushforward{PF <: Pushforward} <: Map end -Represents the inverse of a pullback map F*, defined as +Represents the inverse of a pushforward map F*, defined as (F*)^-1 : V -> V̂ where - V̂ is a function space on the reference cell K̂ and - V is a function space on the physical cell K. """ -struct InversePullback{PB} <: Map - pullback::PB - function InversePullback(pullback::Pullback) - PB = typeof(pullback) - new{PB}(pullback) +struct InversePushforward{PF} <: Map + pushforward::PF + function InversePushforward(pushforward::Pushforward) + PF = typeof(pushforward) + new{PF}(pushforward) end end -Arrays.inverse_map(pb::Pullback) = InversePullback(pb) -Arrays.inverse_map(ipb::InversePullback) = ipb.pullback +Arrays.inverse_map(pb::Pushforward) = InversePushforward(pb) +Arrays.inverse_map(ipb::InversePushforward) = ipb.pushforward # Pushforward """ - struct Pushforward{PB <: Pullback} <: Map end + struct Pullback{PF <: Pushforward} <: Map end Represents a pushforward map F**, defined as F** : V* -> V̂* where - V̂* is a dof space on the reference cell K̂ and - V* is a dof space on the physical cell K. -Its action on physical dofs σ : V -> R is defined in terms of the pullback map F* as +Its action on physical dofs σ : V -> R is defined in terms of the pushforward map F* as F**(σ) := σ∘F* : V̂ -> R """ -struct Pushforward{PB} <: Map - pullback::PB - function Pushforward(pullback::Pullback) - PB = typeof(pullback) - new{PB}(pullback) +struct Pullback{PF} <: Map + pushforward::PF + function Pullback(pushforward::Pushforward) + PF = typeof(pushforward) + new{PF}(pushforward) end end @@ -66,46 +66,46 @@ function Arrays.lazy_map( ) pf = k.maps.value phys_cell_dofs, cell_map, pb_args = k.args - phys_cell_basis = lazy_map(pf.pullback,ref_cell_basis,cell_map,pb_args...) + phys_cell_basis = lazy_map(pf.pushforward,ref_cell_basis,cell_map,pb_args...) return lazy_map(evaluate,phys_cell_dofs,phys_cell_basis) end # InversePushforward """ - struct InversePushforward{PB <: Pullback} <: Map end + struct InversePushforward{PF <: Pushforward} <: Map end Represents the inverse of the pushforward map F**, defined as (F**)^-1 : V̂* -> V* where - V̂* is a dof space on the reference cell K̂ and - V* is a dof space on the physical cell K. -Its action on reference dofs ̂σ : V -> R is defined in terms of the pullback map F* as +Its action on reference dofs ̂σ : V -> R is defined in terms of the pushforward map F* as F**(̂σ) := ̂σ∘(F*)^-1 : V -> R """ -struct InversePushforward{PB} <: Map - pullback::PB - function InversePushforward(pullback::Pullback) - PB = typeof(pullback) - new{PB}(pullback) +struct InversePushforward{PF} <: Map + pushforward::PF + function InversePushforward(pushforward::Pushforward) + PF = typeof(pushforward) + new{PF}(pushforward) end end -Arrays.inverse_map(pf::Pushforward) = InversePushforward(pf.pullback) -Arrays.inverse_map(ipf::InversePushforward) = Pushforward(ipf.pullback) +Arrays.inverse_map(pf::Pushforward) = InversePushforward(pf.pushforward) +Arrays.inverse_map(ipf::InversePushforward) = Pushforward(ipf.pushforward) function Arrays.lazy_map( ::typeof{evaluate},k::LazyArray{<:Fill{<:InversePushforward}},phys_cell_basis ) pf = inverse_map(k.maps.value) ref_cell_dofs, cell_map, pb_args = k.args - ref_cell_basis = lazy_map(inverse_map(pf.pullback),phys_cell_basis,cell_map,pb_args...) + ref_cell_basis = lazy_map(inverse_map(pf.pushforward),phys_cell_basis,cell_map,pb_args...) return lazy_map(evaluate,ref_cell_dofs,ref_cell_basis) end # ContraVariantPiolaMap -struct ContraVariantPiolaMap <: Pullback end +struct ContraVariantPiolaMap <: Pushforward end function lazy_map( k::ContraVariantPiolaMap, From c5be3d433080083cc173ac47eedd128a79b9fd7f Mon Sep 17 00:00:00 2001 From: JordiManyer Date: Thu, 12 Dec 2024 15:40:41 +1100 Subject: [PATCH 040/105] Fixed Nedelec --- src/ReferenceFEs/MomentBasedReferenceFEs.jl | 34 ++++++++++++++----- src/ReferenceFEs/NedelecRefFEs.jl | 10 ++++-- .../CurlConformingFESpacesTests.jl | 4 +-- 3 files changed, 35 insertions(+), 13 deletions(-) diff --git a/src/ReferenceFEs/MomentBasedReferenceFEs.jl b/src/ReferenceFEs/MomentBasedReferenceFEs.jl index 4632b3ad3..27e551928 100644 --- a/src/ReferenceFEs/MomentBasedReferenceFEs.jl +++ b/src/ReferenceFEs/MomentBasedReferenceFEs.jl @@ -52,7 +52,7 @@ function num_dofs(b::MomentBasedDofBasis) n end -function return_cache(b::MomentBasedDofBasis{P,V},field) where {P,V} +function return_cache(b::MomentBasedDofBasis{P,V}, field) where {P,V} alloc_cache(vals::AbstractVector,T,ndofs) = zeros(T,ndofs) alloc_cache(vals::AbstractMatrix,T,ndofs) = zeros(T,ndofs,size(vals,2)) cf = return_cache(field,b.nodes) @@ -60,18 +60,15 @@ function return_cache(b::MomentBasedDofBasis{P,V},field) where {P,V} T = typeof(dot(zero(V),zero(eltype(vals)))) r = alloc_cache(vals,T,num_dofs(b)) c = CachedArray(r) - (c, cf) + return c, cf end -function evaluate!(cache,b::MomentBasedDofBasis,field) +function evaluate!(cache, b::MomentBasedDofBasis, field::Field) c, cf = cache + setsize!(c, size(b)) vals = evaluate!(cf,field,b.nodes) dofs = c.array - _eval_moment_dof_basis!(dofs,vals,b) - dofs -end -function _eval_moment_dof_basis!(dofs,vals::AbstractVector,b) o = 1 z = zero(eltype(dofs)) face_nodes = b.face_nodes @@ -90,9 +87,16 @@ function _eval_moment_dof_basis!(dofs,vals::AbstractVector,b) end end end + + return dofs end -function _eval_moment_dof_basis!(dofs,vals::AbstractMatrix,b) +function evaluate!(cache, b::MomentBasedDofBasis, field::AbstractVector{<:Field}) + c, cf = cache + setsize!(c, (size(b,1),length(field))) + vals = evaluate!(cf,field,b.nodes) + dofs = c.array + o = 1 na = size(vals,2) z = zero(eltype(dofs)) @@ -114,6 +118,8 @@ function _eval_moment_dof_basis!(dofs,vals::AbstractMatrix,b) end end end + + return dofs end # MomentBasedReferenceFE @@ -160,11 +166,21 @@ function get_edge_tangent(m::FaceMeasure{1,Dc}) where {Dc} return ConstantField(t[m.face]) end +# Extends a Df-dimensional vector to a Dc-dimensional one that +# lives in the tangent space of the Dc-embedded Df-dimensional manifold. +# Equivalent to transpose(∇(fmap)) function get_extension(m::FaceMeasure{Df,Dc}) where {Df,Dc} @assert Df == Dc - 1 vs = ReferenceFEs._nfaces_vertices(Float64,m.cpoly,Df)[m.face] - return ConstantField(TensorValue(hcat([vs[2]-vs[1]...],[vs[3]-vs[1]...]))) + J = TensorValue(hcat([vs[2]-vs[1]...],[vs[3]-vs[1]...])) + return ConstantField(J/meas(transpose(J))) end +# function get_extension(m::FaceMeasure{Df,Dc}) where {Df,Dc} +# @assert Df == Dc - 1 +# fmap = m.fmaps[m.face] +# J = Broadcasting(∇)(fmap) +# return Operation(*)(Operation(transpose)(J),Operation(x -> 1/meas(x))(J)) +# end # TO DO: Bug in 3D, when n==2; n==4 and D==3. Also, working on this to make better and more general. function get_facet_measure(p::Polytope{D}, face::Int) where D diff --git a/src/ReferenceFEs/NedelecRefFEs.jl b/src/ReferenceFEs/NedelecRefFEs.jl index 7cdbb98e5..3faa07361 100644 --- a/src/ReferenceFEs/NedelecRefFEs.jl +++ b/src/ReferenceFEs/NedelecRefFEs.jl @@ -30,7 +30,7 @@ function NedelecRefFE(::Type{et},p::Polytope,order::Integer) where et function cmom(φ,μ,ds) # Cell moment function: σ_K(φ,μ) = ∫(φ⋅μ)dK Broadcasting(Operation(⋅))(φ,μ) end - function fmom(φ,μ,ds) # Face moment function: σ_F(φ,μ) = ∫((φ×n)⋅μ)dF + function fmom_HEX(φ,μ,ds) # Face moment function: σ_F(φ,μ) = ∫((φ×n)⋅μ)dF o = get_facet_orientations(ds.cpoly)[ds.face] # This is a hack to avoid a sign map n = o*get_facet_normal(ds) E = get_extension(ds) @@ -38,6 +38,11 @@ function NedelecRefFE(::Type{et},p::Polytope,order::Integer) where et φn = Broadcasting(Operation(×))(n,φ) Broadcasting(Operation(⋅))(φn,Eμ) end + function fmom_TET(φ,μ,ds) # Face moment function: σ_F(φ,μ) = ∫((φ×n)⋅μ)dF + E = get_extension(ds) + Eμ = Broadcasting(Operation(⋅))(E,μ) # We have to extend the basis to 3D + Broadcasting(Operation(⋅))(φ,Eμ) + end function emom(φ,μ,ds) # Edge moment function: σ_E(φ,μ) = ∫((φ⋅t)*μ)dE t = get_edge_tangent(ds) φt = Broadcasting(Operation(⋅))(φ,t) @@ -47,7 +52,8 @@ function NedelecRefFE(::Type{et},p::Polytope,order::Integer) where et moments = Tuple[ (get_dimrange(p,1),emom,eb), # Edge moments ] - if D == 3 && order > 0 + if (D == 3) && order > 0 + fmom = ifelse(is_n_cube(p),fmom_HEX,fmom_TET) push!(moments,(get_dimrange(p,D-1),fmom,fb)) # Face moments end if (is_n_cube(p) && order > 0) || (is_simplex(p) && order > D-2) diff --git a/test/FESpacesTests/CurlConformingFESpacesTests.jl b/test/FESpacesTests/CurlConformingFESpacesTests.jl index bd46c3383..d5e983822 100644 --- a/test/FESpacesTests/CurlConformingFESpacesTests.jl +++ b/test/FESpacesTests/CurlConformingFESpacesTests.jl @@ -101,7 +101,7 @@ e = u - uh el2 = sqrt(sum( ∫( e⋅e )*dΩ )) @test el2 < 1.0e-10 -domain =(0,1,0,1,0,1) +domain = (0,1,0,1,0,1) partition = (3,3,3) model = CartesianDiscreteModel(domain,partition) @@ -128,7 +128,7 @@ e = u - uh dΩ = Measure(Ω,order) el2 = sqrt(sum( ∫( e⋅e )*dΩ )) -@test el2 < 1.0e-10 +@test el2 < 2.0e-10 # using Gridap.Visualization From 529202441b3ae8f59644cb303a424c4030b2cd05 Mon Sep 17 00:00:00 2001 From: JordiManyer Date: Thu, 12 Dec 2024 16:22:57 +1100 Subject: [PATCH 041/105] Removed FineToCoarseReferenceFEs, which have been replaced by macro-elements --- src/Adaptivity/Adaptivity.jl | 1 - src/Adaptivity/FineToCoarseReferenceFEs.jl | 132 ------------------ src/ReferenceFEs/Pullbacks.jl | 42 +++--- .../FineToCoarseFieldsTests.jl | 103 -------------- test/AdaptivityTests/runtests.jl | 1 - 5 files changed, 21 insertions(+), 258 deletions(-) delete mode 100644 src/Adaptivity/FineToCoarseReferenceFEs.jl delete mode 100644 test/AdaptivityTests/FineToCoarseFieldsTests.jl diff --git a/src/Adaptivity/Adaptivity.jl b/src/Adaptivity/Adaptivity.jl index 32a5d1594..8811c68dc 100644 --- a/src/Adaptivity/Adaptivity.jl +++ b/src/Adaptivity/Adaptivity.jl @@ -45,7 +45,6 @@ export DorflerMarking, mark, estimate include("RefinementRules.jl") include("FineToCoarseFields.jl") include("OldToNewFields.jl") -include("FineToCoarseReferenceFEs.jl") include("AdaptivityGlues.jl") include("AdaptedDiscreteModels.jl") include("AdaptedTriangulations.jl") diff --git a/src/Adaptivity/FineToCoarseReferenceFEs.jl b/src/Adaptivity/FineToCoarseReferenceFEs.jl deleted file mode 100644 index f36ad5793..000000000 --- a/src/Adaptivity/FineToCoarseReferenceFEs.jl +++ /dev/null @@ -1,132 +0,0 @@ -""" -""" -struct FineToCoarseDofBasis{T,A,B,C} <: AbstractVector{T} - dof_basis :: A - rrule :: B - child_ids :: C - - function FineToCoarseDofBasis(dof_basis::AbstractVector{T},rrule::RefinementRule) where {T<:Dof} - nodes = get_nodes(dof_basis) - child_ids = map(x -> x_to_cell(rrule,x),nodes) - - A = typeof(dof_basis) - B = typeof(rrule) - C = typeof(child_ids) - new{T,A,B,C}(dof_basis,rrule,child_ids) - end -end - -Base.size(a::FineToCoarseDofBasis) = size(a.dof_basis) -Base.axes(a::FineToCoarseDofBasis) = axes(a.dof_basis) -Base.getindex(a::FineToCoarseDofBasis,i::Integer) = getindex(a.dof_basis,i) -Base.IndexStyle(a::FineToCoarseDofBasis) = IndexStyle(a.dof_basis) - -ReferenceFEs.get_nodes(a::FineToCoarseDofBasis) = get_nodes(a.dof_basis) - -# Default behaviour -Arrays.return_cache(b::FineToCoarseDofBasis,field) = return_cache(b.dof_basis,field) -Arrays.evaluate!(cache,b::FineToCoarseDofBasis,field) = evaluate!(cache,b.dof_basis,field) - -# Spetialized behaviour -function Arrays.return_cache(s::FineToCoarseDofBasis{T,<:LagrangianDofBasis},field::FineToCoarseField) where T - b = s.dof_basis - cf = return_cache(field,b.nodes,s.child_ids) - vals = evaluate!(cf,field,b.nodes,s.child_ids) - ndofs = length(b.dof_to_node) - r = ReferenceFEs._lagr_dof_cache(vals,ndofs) - c = CachedArray(r) - return (c, cf) -end - -function Arrays.evaluate!(cache,s::FineToCoarseDofBasis{T,<:LagrangianDofBasis},field::FineToCoarseField) where T - c, cf = cache - b = s.dof_basis - vals = evaluate!(cf,field,b.nodes,s.child_ids) - ndofs = length(b.dof_to_node) - T2 = eltype(vals) - ncomps = num_indep_components(T2) - @check ncomps == num_indep_components(eltype(b.node_and_comp_to_dof)) """\n - Unable to evaluate LagrangianDofBasis. The number of components of the - given Field does not match with the LagrangianDofBasis. - If you are trying to interpolate a function on a FESpace make sure that - both objects have the same value type. - For instance, trying to interpolate a vector-valued function on a scalar-valued FE space - would raise this error. - """ - ReferenceFEs._evaluate_lagr_dof!(c,vals,b.node_and_comp_to_dof,ndofs,ncomps) -end - -function Arrays.return_cache(s::FineToCoarseDofBasis{T,<:MomentBasedDofBasis},field::FineToCoarseField) where T - b = s.dof_basis - cf = return_cache(field,b.nodes,s.child_ids) - vals = evaluate!(cf,field,b.nodes,s.child_ids) - ndofs = num_dofs(b) - r = ReferenceFEs._moment_dof_basis_cache(vals,ndofs) - c = CachedArray(r) - return (c, cf) -end - -function Arrays.evaluate!(cache,s::FineToCoarseDofBasis{T,<:MomentBasedDofBasis},field::FineToCoarseField) where T - c, cf = cache - b = s.dof_basis - vals = evaluate!(cf,field,b.nodes,s.child_ids) - dofs = c.array - ReferenceFEs._eval_moment_dof_basis!(dofs,vals,b) - dofs -end - - -""" - Wrapper for a ReferenceFE which is specialised for - efficiently evaluating FineToCoarseFields. -""" -struct FineToCoarseRefFE{T,D,A} <: ReferenceFE{D} - reffe :: T - dof_basis :: A - - function FineToCoarseRefFE(reffe::ReferenceFE{D},dof_basis::FineToCoarseDofBasis) where D - T = typeof(reffe) - A = typeof(dof_basis) - new{T,D,A}(reffe,dof_basis) - end -end - -ReferenceFEs.num_dofs(reffe::FineToCoarseRefFE) = num_dofs(reffe.reffe) -ReferenceFEs.get_polytope(reffe::FineToCoarseRefFE) = get_polytope(reffe.reffe) -ReferenceFEs.get_prebasis(reffe::FineToCoarseRefFE) = get_prebasis(reffe.reffe) -ReferenceFEs.get_dof_basis(reffe::FineToCoarseRefFE) = reffe.dof_basis -ReferenceFEs.Conformity(reffe::FineToCoarseRefFE) = Conformity(reffe.reffe) -ReferenceFEs.get_face_dofs(reffe::FineToCoarseRefFE) = get_face_dofs(reffe.reffe) -ReferenceFEs.get_shapefuns(reffe::FineToCoarseRefFE) = get_shapefuns(reffe.reffe) -ReferenceFEs.get_metadata(reffe::FineToCoarseRefFE) = get_metadata(reffe.reffe) -ReferenceFEs.get_orders(reffe::FineToCoarseRefFE) = get_orders(reffe.reffe) -ReferenceFEs.get_order(reffe::FineToCoarseRefFE) = get_order(reffe.reffe) - -ReferenceFEs.Conformity(reffe::FineToCoarseRefFE,sym::Symbol) = Conformity(reffe.reffe,sym) -ReferenceFEs.get_face_own_dofs(reffe::FineToCoarseRefFE,conf::Conformity) = get_face_own_dofs(reffe.reffe,conf) - - -function ReferenceFEs.ReferenceFE(p::Polytope,rrule::RefinementRule,name::ReferenceFEName,order) - FineToCoarseRefFE(p,rrule,name,Float64,order) -end - -function ReferenceFEs.ReferenceFE(p::Polytope,rrule::RefinementRule,name::ReferenceFEName,::Type{T},order) where T - FineToCoarseRefFE(p,rrule,name,T,order) -end - -function FineToCoarseRefFE(p::Polytope,rrule::RefinementRule,name::ReferenceFEName,::Type{T},order) where T - @check p == get_polytope(rrule) - reffe = ReferenceFE(p,name,T,order) - dof_basis = FineToCoarseDofBasis(get_dof_basis(reffe),rrule) - return FineToCoarseRefFE(reffe,dof_basis) -end - -# FESpaces constructors - -function FESpaces.TestFESpace(model::DiscreteModel,rrules::AbstractVector{<:RefinementRule},reffe::Tuple{<:ReferenceFEName,Any,Any};kwargs...) - @check num_cells(model) == length(rrules) - @check all(CompressedArray(get_polytopes(model),get_cell_type(model)) .== lazy_map(get_polytope,rrules)) - basis, reffe_args, reffe_kwargs = reffe - reffes = lazy_map(rr -> ReferenceFE(get_polytope(rr),rr,basis,reffe_args...;reffe_kwargs...),rrules) - return TestFESpace(model,reffes;kwargs...) -end diff --git a/src/ReferenceFEs/Pullbacks.jl b/src/ReferenceFEs/Pullbacks.jl index de49f3a21..dc0a9d45f 100644 --- a/src/ReferenceFEs/Pullbacks.jl +++ b/src/ReferenceFEs/Pullbacks.jl @@ -10,7 +10,7 @@ where """ abstract type Pushforward <: Map end -function lazy_map( +function Arrays.lazy_map( ::Broadcasting{typeof(gradient)}, a::LazyArray{<:Fill{Broadcasting{Operation{<:Pushforward}}}} ) cell_ref_basis, args = a.args @@ -37,21 +37,21 @@ struct InversePushforward{PF} <: Map end end -Arrays.inverse_map(pb::Pushforward) = InversePushforward(pb) -Arrays.inverse_map(ipb::InversePushforward) = ipb.pushforward +Arrays.inverse_map(pf::Pushforward) = InversePushforward(pf) +Arrays.inverse_map(ipf::InversePushforward) = ipf.pushforward # Pushforward """ struct Pullback{PF <: Pushforward} <: Map end -Represents a pushforward map F**, defined as +Represents a pullback map F**, defined as F** : V* -> V̂* where - V̂* is a dof space on the reference cell K̂ and - V* is a dof space on the physical cell K. Its action on physical dofs σ : V -> R is defined in terms of the pushforward map F* as - F**(σ) := σ∘F* : V̂ -> R + ̂σ = F**(σ) := σ∘F* : V̂ -> R """ struct Pullback{PF} <: Map pushforward::PF @@ -64,42 +64,42 @@ end function Arrays.lazy_map( ::typeof{evaluate},k::LazyArray{<:Fill{<:Pushforward}},ref_cell_basis ) - pf = k.maps.value - phys_cell_dofs, cell_map, pb_args = k.args - phys_cell_basis = lazy_map(pf.pushforward,ref_cell_basis,cell_map,pb_args...) + pb = k.maps.value + phys_cell_dofs, cell_map, pf_args = k.args + phys_cell_basis = lazy_map(pb.pushforward,ref_cell_basis,cell_map,pf_args...) return lazy_map(evaluate,phys_cell_dofs,phys_cell_basis) end -# InversePushforward +# InversePullback """ - struct InversePushforward{PF <: Pushforward} <: Map end + struct InversePullback{PF <: Pushforward} <: Map end -Represents the inverse of the pushforward map F**, defined as +Represents the inverse of the pullback map F**, defined as (F**)^-1 : V̂* -> V* where - V̂* is a dof space on the reference cell K̂ and - V* is a dof space on the physical cell K. -Its action on reference dofs ̂σ : V -> R is defined in terms of the pushforward map F* as - F**(̂σ) := ̂σ∘(F*)^-1 : V -> R +Its action on reference dofs ̂σ : V̂ -> R is defined in terms of the pushforward map F* as + σ = F**(̂σ) := ̂σ∘(F*)^-1 : V -> R """ -struct InversePushforward{PF} <: Map +struct InversePullback{PF} <: Map pushforward::PF - function InversePushforward(pushforward::Pushforward) + function InversePullback(pushforward::Pushforward) PF = typeof(pushforward) new{PF}(pushforward) end end -Arrays.inverse_map(pf::Pushforward) = InversePushforward(pf.pushforward) -Arrays.inverse_map(ipf::InversePushforward) = Pushforward(ipf.pushforward) +Arrays.inverse_map(pb::Pullback) = InversePullback(pb.pushforward) +Arrays.inverse_map(ipb::InversePullback) = Pullback(ipb.pushforward) function Arrays.lazy_map( - ::typeof{evaluate},k::LazyArray{<:Fill{<:InversePushforward}},phys_cell_basis + ::typeof{evaluate},k::LazyArray{<:Fill{<:InversePullback}},phys_cell_basis ) - pf = inverse_map(k.maps.value) - ref_cell_dofs, cell_map, pb_args = k.args - ref_cell_basis = lazy_map(inverse_map(pf.pushforward),phys_cell_basis,cell_map,pb_args...) + pb = inverse_map(k.maps.value) + ref_cell_dofs, cell_map, pf_args = k.args + ref_cell_basis = lazy_map(inverse_map(pb.pushforward),phys_cell_basis,cell_map,pf_args...) return lazy_map(evaluate,ref_cell_dofs,ref_cell_basis) end diff --git a/test/AdaptivityTests/FineToCoarseFieldsTests.jl b/test/AdaptivityTests/FineToCoarseFieldsTests.jl deleted file mode 100644 index 89beb196b..000000000 --- a/test/AdaptivityTests/FineToCoarseFieldsTests.jl +++ /dev/null @@ -1,103 +0,0 @@ -module FineToCoarseFieldsTests - -using Test -using Gridap -using Gridap.Arrays -using Gridap.Algebra -using Gridap.Geometry -using Gridap.CellData -using Gridap.Adaptivity -using Gridap.ReferenceFEs -using Gridap.FESpaces -using FillArrays - -sol(x) = x[1] + x[2] - -D = 2 -order = 1 -qorder = order*2+1 -domain = Tuple(repeat([0,1],D)) - -parent = CartesianDiscreteModel(domain,Tuple(fill(2,D))) -model = refine(parent) - -trian = Triangulation(model) -ctrian = Triangulation(parent) - -qorder = order*2+1 -dΩ_c = Measure(ctrian,qorder) -dΩ_f = Measure(trian,qorder) -dΩ_cf = Measure(ctrian,trian,qorder) - -glue = get_adaptivity_glue(model) -rrules = Adaptivity.get_old_cell_refinement_rules(glue) - -cell_reffe_lag = lazy_map(rr->ReferenceFE(get_polytope(rr),rr,lagrangian,Float64,order),rrules) -lazy_map(test_reference_fe,cell_reffe_lag) -cell_reffe_ned = lazy_map(rr->ReferenceFE(get_polytope(rr),rr,nedelec,Float64,order),rrules) -lazy_map(test_reference_fe,cell_reffe_ned) - -# Lagrangian tests -reffe = ReferenceFE(lagrangian,Float64,order) -V_c = TestFESpace(parent,rrules,reffe;conformity=:H1,dirichlet_tags="boundary") -U_c = TrialFESpace(V_c,sol) - -V_f = TestFESpace(model,reffe;conformity=:H1,dirichlet_tags="boundary") -U_f = TrialFESpace(V_f,sol) - -test_fe_space(U_c) -test_fe_space(U_f) - -u_c = interpolate_everywhere(sol,U_c) -u_f = interpolate_everywhere(sol,U_f) - -u_fc = interpolate(u_f,U_c) -u_fc2 = interpolate_everywhere(u_f,U_c) - -eh = u_c - u_f -@test sum(∫(eh⋅eh)*dΩ_f) < 1.e-12 - -eh2 = u_c - u_fc -@test sum(∫(eh2⋅eh2)*dΩ_c) < 1.e-12 - -eh3 = u_c - u_fc2 -@test sum(∫(eh3⋅eh3)*dΩ_c) < 1.e-12 - -# Moment-based (Nedelec) tests -sol((x,y)) = 2*VectorValue(-y,x) - -reffe = ReferenceFE(nedelec,Float64,order) -V_c = TestFESpace(parent,rrules,reffe;dirichlet_tags="boundary") -U_c = TrialFESpace(V_c,sol) - -V_f = TestFESpace(model,reffe;dirichlet_tags="boundary") -U_f = TrialFESpace(V_f,sol) - -test_fe_space(U_c) -test_fe_space(U_f) - -u_c = interpolate_everywhere(sol,U_c) -u_f = interpolate_everywhere(sol,U_f) - -u_fc = interpolate(u_f,U_c) -u_fc2 = interpolate_everywhere(u_f,U_c) - -eh = u_c - u_f -@test sum(∫(eh⋅eh)*dΩ_f) < 1.e-12 - -eh2 = u_c - u_fc -@test sum(∫(eh2⋅eh2)*dΩ_c) < 1.e-12 - -eh3 = u_c - u_fc2 -@test sum(∫(eh3⋅eh3)*dΩ_c) < 1.e-12 - -modelH = CartesianDiscreteModel((0,1,0,1),(1,1)) -modelh = refine(modelH,2) -reffe = LagrangianRefFE(Float64,QUAD,1) -XH = TestFESpace(modelH,reffe) -xH = get_fe_basis(XH) -xHh = change_domain(xH,get_triangulation(modelh),ReferenceDomain()) -evaluate(Gridap.CellData.get_data(xHh)[1],[Point(0.0,0.0),Point(0.5,0.5)]) -evaluate(Gridap.CellData.get_data(xHh)[1],Point(0.5,0.5)) - -end \ No newline at end of file diff --git a/test/AdaptivityTests/runtests.jl b/test/AdaptivityTests/runtests.jl index 2d2a569da..ac63b6f3f 100644 --- a/test/AdaptivityTests/runtests.jl +++ b/test/AdaptivityTests/runtests.jl @@ -9,7 +9,6 @@ using Test include("CartesianRefinementTests.jl") include("ComplexChangeDomainTests.jl") include("EdgeBasedRefinementTests.jl") - include("FineToCoarseFieldsTests.jl") include("RefinementRuleBoundaryTests.jl") include("MultifieldRefinementTests.jl") end From cfe722f7fadb9ea99caa2e6711469316575c08b2 Mon Sep 17 00:00:00 2001 From: Antoine Marteau Date: Wed, 11 Dec 2024 16:36:47 +1100 Subject: [PATCH 042/105] generic TensorPolynomialBasis renamed - ChebyshevPolynomialBases -> ChebyshevBasis - JacobiPolynomialBases -> LegendreBasis for consistency with MonomialBasis added TensorPolynomialBasis that generalises/refactors MonomialBasis, ChebyshevBasis and LegendreBasis, and any nD polynomial basis built as an anisotropic tensor product of 1D polynomial bases. --- src/Polynomials/BernsteinBases.jl | 9 + ...evPolynomialBases.jl => ChebyshevBases.jl} | 272 +------- src/Polynomials/LegendreBases.jl | 66 ++ src/Polynomials/LegendrePolynomialBases.jl | 373 ----------- src/Polynomials/ModalC0Bases.jl | 18 +- src/Polynomials/MonomialBases.jl | 580 +----------------- src/Polynomials/OldMonomialHelpers.jl | 328 ++++++++++ .../PCurlGradLegendrePolynomialBases.jl | 10 +- src/Polynomials/PCurlGradMonomialBases.jl | 15 +- src/Polynomials/PolynomialInterfaces.jl | 132 ++-- src/Polynomials/Polynomials.jl | 14 +- .../QGradLegendrePolynomialBases.jl | 12 +- src/Polynomials/QGradMonomialBases.jl | 4 +- src/Polynomials/TensorPolynomialBases.jl | 505 +++++++++++++++ src/ReferenceFEs/RaviartThomasRefFEs.jl | 14 +- .../deprecated/RaviartThomasRefFEs.jl | 2 +- .../LegendrePolynomialBasesTests.jl | 63 -- test/PolynomialsTests/runtests.jl | 2 +- 18 files changed, 1085 insertions(+), 1334 deletions(-) create mode 100644 src/Polynomials/BernsteinBases.jl rename src/Polynomials/{ChebyshevPolynomialBases.jl => ChebyshevBases.jl} (58%) create mode 100644 src/Polynomials/LegendreBases.jl delete mode 100644 src/Polynomials/LegendrePolynomialBases.jl create mode 100644 src/Polynomials/OldMonomialHelpers.jl create mode 100644 src/Polynomials/TensorPolynomialBases.jl delete mode 100644 test/PolynomialsTests/LegendrePolynomialBasesTests.jl diff --git a/src/Polynomials/BernsteinBases.jl b/src/Polynomials/BernsteinBases.jl new file mode 100644 index 000000000..3b4b0f906 --- /dev/null +++ b/src/Polynomials/BernsteinBases.jl @@ -0,0 +1,9 @@ +""" + BernsteinPType{K} <: PolynomialType{K} + +Type representing Bernstein polynomials of maximum order `K` +""" +struct BernsteinPType{K} <: PolynomialType{K} end +isHierarchical(::BernsteinPType) = false + +struct Bernstein <: PolynomialField end diff --git a/src/Polynomials/ChebyshevPolynomialBases.jl b/src/Polynomials/ChebyshevBases.jl similarity index 58% rename from src/Polynomials/ChebyshevPolynomialBases.jl rename to src/Polynomials/ChebyshevBases.jl index 9337a981d..4c0e7e9f0 100644 --- a/src/Polynomials/ChebyshevPolynomialBases.jl +++ b/src/Polynomials/ChebyshevBases.jl @@ -1,179 +1,37 @@ """ - ChebyshevPType{kind,K} <: PolynomialType{K} + Chebyshev{kind} <: Polynomial -Type representing Chebyshev polynomials of order up to `K`, +Type representing Chebyshev polynomials of first and second kind where `kind` is either `:T` or `:U` for first and second kind Chebyshev polynomials respectively. """ -struct ChebyshevPType{kind, K} <: PolynomialType{K} end -isHierarchical(::ChebyshevPType) = true +struct Chebyshev{kind} <: Polynomial end -struct ChebyshevPolynomial <: Field end +isHierarchical(::Chebyshev) = true -struct ChebyshevPolynomialBasis{D,T} <: AbstractVector{ChebyshevPolynomial} - orders::NTuple{D,Int} - terms::Vector{CartesianIndex{D}} - function ChebyshevPolynomialBasis{D}( - ::Type{T}, orders::NTuple{D,Int}, terms::Vector{CartesianIndex{D}}) where {D,T} - new{D,T}(orders,terms) - end -end - -@inline Base.size(a::ChebyshevPolynomialBasis{D,T}) where {D,T} = (length(a.terms)*num_components(T),) -@inline Base.getindex(a::ChebyshevPolynomialBasis,i::Integer) = ChebyshevPolynomial() -@inline Base.IndexStyle(::ChebyshevPolynomialBasis) = IndexLinear() - -function ChebyshevPolynomialBasis{D}( - ::Type{T}, orders::NTuple{D,Int}, filter::Function=_q_filter) where {D,T} - - terms = _define_terms(filter, orders) - ChebyshevPolynomialBasis{D}(T,orders,terms) -end - -function ChebyshevPolynomialBasis{D}( - ::Type{T}, order::Int, filter::Function=_q_filter) where {D,T} - - orders = tfill(order,Val{D}()) - ChebyshevPolynomialBasis{D}(T,orders,filter) -end - -# API - -function get_exponents(b::ChebyshevPolynomialBasis) - indexbase = 1 - [Tuple(t) .- indexbase for t in b.terms] -end - -function get_order(b::ChebyshevPolynomialBasis) - maximum(b.orders) -end - -function get_orders(b::ChebyshevPolynomialBasis) - b.orders -end - -return_type(::ChebyshevPolynomialBasis{D,T}) where {D,T} = T - -# Field implementation - -function return_cache(f::ChebyshevPolynomialBasis{D,T},x::AbstractVector{<:Point}) where {D,T} - @check 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::ChebyshevPolynomialBasis{D,T},x::AbstractVector{<:Point}) where {D,T} - 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_ch!(v,xi,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,ChebyshevPolynomialBasis{D,V}}, - x::AbstractVector{<:Point}) where {D,V} - - 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,ChebyshevPolynomialBasis{D,T}}, - x::AbstractVector{<:Point}) where {D,T} - - 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_ch!(v,xi,f.orders,f.terms,c,g,T) - for j in 1:ndof - @inbounds r[i,j] = v[j] - end - end - r.array -end - -# Optimizing evaluation at a single point +""" + ChebyshevBasis{D,T,kind,K} = TensorPolynomialBasis{D,T,K,Chebyshev{kind}} -function return_cache(f::ChebyshevPolynomialBasis{D,T},x::Point) where {D,T} - ndof = length(f.terms)*num_components(T) - r = CachedArray(zeros(T,(ndof,))) - xs = [x] - cf = return_cache(f,xs) - r, cf, xs -end +Multivariate scalar' or `Multivalue`'d Chebyshev basis, see [`TensorPolynomialBasis`](@ref) +""" +const ChebyshevBasis{D,T,kind,K} = TensorPolynomialBasis{D,T,K,Chebyshev{kind}} -function evaluate!(cache,f::ChebyshevPolynomialBasis{D,T},x::Point) where {D,T} - 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 +""" + ChebyshevBasis{D}(::Type{T}, order::Int, terms::Vector; kind=:T) where {D,T} + ChebyshevBasis{D}(::Type{T}, orders::Tuple [, filter::Function; kind=:T]) where {D,T} + ChebyshevBasis{D}(::Type{T}, order::Int [, filter::Function]; kind=:T) where {D,T} -function return_cache( - f::FieldGradientArray{N,ChebyshevPolynomialBasis{D,V}}, x::Point) where {N,D,V} - xs = [x] - cf = return_cache(f,xs) - v = evaluate!(cf,f,xs) - r = CachedArray(zeros(eltype(v),(size(v,2),))) - r, cf, xs -end +Convenience constructors of `ChebyshevBasis{D,T,kind}`. The default kind is the first kind (cf. [`Chebyshev`](@ref)). +""" +ChebyshevBasis{D}(args...; kind=:T) where {D} = TensorPolynomialBasis{D}(Chebyshev{kind}, args...) -function evaluate!( - cache, f::FieldGradientArray{N,ChebyshevPolynomialBasis{D,V}}, x::Point) where {N,D,V} - 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 +TensorPolynomialBasis{D}(::Type{Chebyshev{:U}}, args...) where D = @notimplemented "1D evaluation for second kind needed here" -# Helpers +# 1D evaluation implementation function _evaluate_1d!( - ::Type{ChebyshevPType{:T,K}}, v::AbstractMatrix{T},x,d) where {K,T<:Number} + ::Type{Chebyshev{:T}}, k, v::AbstractMatrix{T},x,d) where T<:Number - n = K + 1 + n = k + 1 o = one(T) @inbounds v[d,1] = o if n > 1 @@ -187,9 +45,9 @@ function _evaluate_1d!( end function _gradient_1d!( - ::Type{ChebyshevPType{:T,K}}, g::AbstractMatrix{T},x,d) where {K,T<:Number} + ::Type{Chebyshev{:T}}, k, g::AbstractMatrix{T},x,d) where T<:Number - n = K + 1 + n = k + 1 z = zero(T) o = one(T) dξdx = T(2.0) @@ -206,78 +64,10 @@ function _gradient_1d!( end end -function _evaluate_nd_ch!( - v::AbstractVector{V}, - x, - orders, - terms::AbstractVector{CartesianIndex{D}}, - c::AbstractMatrix{T}) where {V,T,D} - - dim = D - for d in 1:dim - _evaluate_1d!(ChebyshevPType{:T,orders[d]},c,x,d) - end - - o = one(T) - k = 1 - - for ci in terms - - s = o - for d in 1:dim - @inbounds s *= c[d,ci[d]] - end - - k = _set_value!(v,s,k) - - end - -end - -function _gradient_nd_ch!( - v::AbstractVector{G}, - x, - orders, - terms::AbstractVector{CartesianIndex{D}}, - c::AbstractMatrix{T}, - g::AbstractMatrix{T}, - ::Type{V}) where {G,T,D,V} - - dim = D - for d in 1:dim - _derivatives_1d!(ChebyshevPType{:T,orders[d]},(c,g),x,d) - end - - z = zero(Mutable(VectorValue{D,T})) - o = one(T) - k = 1 - - for ci in terms - - 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!(v,s,k,V) - - end - -end - ############################################################################################ """ - struct QGradChebyshevPolynomialBasis{...} <: AbstractVector{Monomial} + struct QGradChebyshevPolynomialBasis{...} <: AbstractVector{Chebyshev{:T}} This type implements a multivariate vector-valued polynomial basis spanning the space needed for Nedelec reference elements on n-cubes. @@ -285,7 +75,7 @@ The type parameters and fields of this `struct` are not public. This type fully implements the [`Field`](@ref) interface, with up to first order derivatives. """ -struct QGradChebyshevPolynomialBasis{D,T} <: AbstractVector{ChebyshevPolynomial} +struct QGradChebyshevPolynomialBasis{D,T} <: AbstractVector{Chebyshev{:T}} order::Int terms::CartesianIndices{D} perms::Matrix{Int} @@ -295,7 +85,7 @@ struct QGradChebyshevPolynomialBasis{D,T} <: AbstractVector{ChebyshevPolynomial} end Base.size(a::QGradChebyshevPolynomialBasis) = (_ndofs_qgrad_ch(a),) -Base.getindex(a::QGradChebyshevPolynomialBasis,i::Integer) = ChebyshevPolynomial() +Base.getindex(a::QGradChebyshevPolynomialBasis,i::Integer) = Chebyshev{:T}() Base.IndexStyle(::QGradChebyshevPolynomialBasis) = IndexLinear() """ @@ -317,9 +107,9 @@ function QGradChebyshevPolynomialBasis{D}(::Type{T},order::Int) where {D,T} end """ - num_terms(f::QGradChebyshevPolynomialBasis{D,T}) where {D,T} + num_terms(f::QGradChebyshevPolynomialBasis{D}) where {D} """ -num_terms(f::QGradChebyshevPolynomialBasis{D,T}) where {D,T} = length(f.terms)*D +num_terms(f::QGradChebyshevPolynomialBasis{D}) where {D} = length(f.terms)*D get_order(f::QGradChebyshevPolynomialBasis) = f.order @@ -411,7 +201,7 @@ function _evaluate_nd_qgrad_ch!( dim = D for d in 1:dim - _evaluate_1d!(ChebyshevPType{:T,order},c,x,d) + _evaluate_1d!(Chebyshev{:T},order,c,x,d) end o = one(T) @@ -455,7 +245,7 @@ function _gradient_nd_qgrad_ch!( dim = D for d in 1:dim - _derivatives_1d!(ChebyshevPType{:T,order},(c,g),x,d) + _derivatives_1d!(Chebyshev{:T},order,(c,g),x,d) end z = zero(Mutable(V)) @@ -512,7 +302,7 @@ The type parameters and fields of this `struct` are not public. This type fully implements the [`Field`](@ref) interface, with up to first order derivatives. """ -struct QCurlGradChebyshevPolynomialBasis{D,T} <: AbstractVector{ChebyshevPolynomial} +struct QCurlGradChebyshevPolynomialBasis{D,T} <: AbstractVector{Chebyshev{:T}} qgrad::QGradChebyshevPolynomialBasis{D,T} function QCurlGradChebyshevPolynomialBasis(::Type{T},order::Int,terms::CartesianIndices{D},perms::Matrix{Int}) where {D,T} qgrad = QGradChebyshevPolynomialBasis(T,order,terms,perms) @@ -521,7 +311,7 @@ struct QCurlGradChebyshevPolynomialBasis{D,T} <: AbstractVector{ChebyshevPolynom end Base.size(a::QCurlGradChebyshevPolynomialBasis) = (length(a.qgrad),) -Base.getindex(a::QCurlGradChebyshevPolynomialBasis,i::Integer) = ChebyshevPolynomial() +Base.getindex(a::QCurlGradChebyshevPolynomialBasis,i::Integer) = Chebyshev{:T}() Base.IndexStyle(::QCurlGradChebyshevPolynomialBasis) = IndexLinear() """ diff --git a/src/Polynomials/LegendreBases.jl b/src/Polynomials/LegendreBases.jl new file mode 100644 index 000000000..951dcc01d --- /dev/null +++ b/src/Polynomials/LegendreBases.jl @@ -0,0 +1,66 @@ +""" + Legendre <: Polynomial + +Type representing the Legendre polynomials +""" +struct Legendre <: Polynomial end + +isHierarchical(::Legendre) = true + +""" + LegendreBasis{D,T,K} = TensorPolynomialBasis{D,T,K,Legendre} + +Multivariate scalar' or `Multivalue`'d Legendre basis, see [`TensorPolynomialBasis`](@ref) +""" +const LegendreBasis{D,T,K} = TensorPolynomialBasis{D,T,K,Legendre} + +""" + LegendreBasis{D}(::Type{T}, order::Int, terms::Vector) where {D,T} + LegendreBasis{D}(::Type{T}, orders::Tuple [, filter::Function]) where {D,T} + LegendreBasis{D}(::Type{T}, order::Int [, filter::Function]) where {D,T} + +Convenience constructors of LegendreBasis{D,T}. +""" +LegendreBasis{D}(args...) where {D} = TensorPolynomialBasis{D}(Legendre, args...) + +# 1D evaluation implementation + +function _evaluate_1d!(::Type{Legendre}, k, v::AbstractMatrix{T},x,d) where T<:Number + n = k + 1 + o = one(T) + @inbounds v[d,1] = o + if n > 1 + ξ = ( 2*x[d] - 1 ) + for i in 2:n + # The sqrt(2i-1) factor normalizes the basis polynomial for L2 scalar product on ξ∈[0,1], indeed: + # ∫[0,1] Pn(2ξ-1)^2 dξ = 1/2 ∫[-1,1] Pn(t)^2 dt = 1/(2n+1) + # C.f. Eq. (1.25) in Section 1.1.5 in Ern & Guermond book (2013). + @inbounds v[d,i] = sqrt(2*i-1)*jacobi(ξ,i-1,0,0) + end + end +end + +function _gradient_1d!(::Type{Legendre}, k, g::AbstractMatrix{T},x,d) where T<:Number + n = k + 1 + z = zero(T) + @inbounds g[d,1] = z + if n > 1 + ξ = ( 2*x[d] - 1 ) + for i in 2:n + @inbounds g[d,i] = sqrt(2*i-1)*i*jacobi(ξ,i-2,1,1) + end + end +end + +function _hessian_1d!(::Type{Legendre}, k, h::AbstractMatrix{T},x,d) where T<:Number + n = k + 1 + z = zero(T) + @inbounds h[d,1] = z + if n > 1 + @inbounds h[d,2] = z + ξ = ( 2*x[d] - 1 ) + for i in 3:n + @inbounds h[d,i] = sqrt(2*i-1)*(i*(i+1)/2)*jacobi(ξ,i-3,2,2) + end + end +end diff --git a/src/Polynomials/LegendrePolynomialBases.jl b/src/Polynomials/LegendrePolynomialBases.jl deleted file mode 100644 index 1ee26003c..000000000 --- a/src/Polynomials/LegendrePolynomialBases.jl +++ /dev/null @@ -1,373 +0,0 @@ -""" - LegendrePType{K} <: PolynomialType{K} - -Type representing Legendre polynomials of order up to `K` -""" -struct LegendrePType{K} <: PolynomialType{K} end -isHierarchical(::LegendrePType) = true - -struct LegendrePolynomial <: Field end - -struct LegendrePolynomialBasis{D,T} <: AbstractVector{LegendrePolynomial} - orders::NTuple{D,Int} - terms::Vector{CartesianIndex{D}} - function LegendrePolynomialBasis{D}( - ::Type{T}, orders::NTuple{D,Int}, terms::Vector{CartesianIndex{D}}) where {D,T} - new{D,T}(orders,terms) - end -end - -@inline Base.size(a::LegendrePolynomialBasis{D,T}) where {D,T} = (length(a.terms)*num_indep_components(T),) -@inline Base.getindex(a::LegendrePolynomialBasis,i::Integer) = LegendrePolynomial() -@inline Base.IndexStyle(::LegendrePolynomialBasis) = IndexLinear() - -function LegendrePolynomialBasis{D}( - ::Type{T}, orders::NTuple{D,Int}, filter::Function=_q_filter) where {D,T} - - terms = _define_terms(filter, orders) - LegendrePolynomialBasis{D}(T,orders,terms) -end - -function LegendrePolynomialBasis{D}( - ::Type{T}, order::Int, filter::Function=_q_filter) where {D,T} - - orders = tfill(order,Val{D}()) - LegendrePolynomialBasis{D}(T,orders,filter) -end - -# API - -function get_exponents(b::LegendrePolynomialBasis) - indexbase = 1 - [Tuple(t) .- indexbase for t in b.terms] -end - -function get_order(b::LegendrePolynomialBasis) - maximum(b.orders) -end - -function get_orders(b::LegendrePolynomialBasis) - b.orders -end - -return_type(::LegendrePolynomialBasis{D,T}) where {D,T} = T - -# Field implementation - -function return_cache(f::LegendrePolynomialBasis{D,T},x::AbstractVector{<:Point}) where {D,T} - @check D == length(eltype(x)) "Incorrect number of point components" - np = length(x) - ndof = length(f) - 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::LegendrePolynomialBasis{D,T},x::AbstractVector{<:Point}) where {D,T} - r, v, c = cache - np = length(x) - ndof = length(f) - 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_leg!(v,xi,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,LegendrePolynomialBasis{D,V}}, - x::AbstractVector{<:Point}) where {D,V} - - f = fg.fa - @assert D == length(eltype(x)) "Incorrect number of point components" - np = length(x) - ndof = length(f) - 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,LegendrePolynomialBasis{D,T}}, - x::AbstractVector{<:Point}) where {D,T} - - f = fg.fa - r, v, c, g = cache - np = length(x) - ndof = length(f) - 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_leg!(v,xi,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,LegendrePolynomialBasis{D,V}}, - x::AbstractVector{<:Point}) where {D,V} - - f = fg.fa - @assert D == length(eltype(x)) "Incorrect number of point components" - np = length(x) - ndof = length(f) - 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,LegendrePolynomialBasis{D,T}}, - x::AbstractVector{<:Point}) where {D,T} - - f = fg.fa - r, v, c, g, h = cache - np = length(x) - ndof = length(f) - 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_leg!(v,xi,f.orders,f.terms,c,g,h,T) - for j in 1:ndof - @inbounds r[i,j] = v[j] - end - end - r.array -end - -# Optimizing evaluation at a single point - -function return_cache(f::AbstractVector{LegendrePolynomial},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{LegendrePolynomial},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{LegendrePolynomial}}, 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{LegendrePolynomial}}, 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 - -function _evaluate_1d!( - ::Type{LegendrePType{K}}, v::AbstractMatrix{T},x,d) where {K,T<:Number} - - n = K + 1 - o = one(T) - @inbounds v[d,1] = o - if n > 1 - ξ = ( 2*x[d] - 1 ) - for i in 2:n - # The sqrt(2i-1) factor normalizes the basis polynomial for L2 scalar product on ξ∈[0,1], indeed: - # ∫[0,1] Pn(2ξ-1)^2 dξ = 1/2 ∫[-1,1] Pn(t)^2 dt = 1/(2n+1) - # C.f. Eq. (1.25) in Section 1.1.5 in Ern & Guermond book (2013). - @inbounds v[d,i] = sqrt(2*i-1)*jacobi(ξ,i-1,0,0) - end - end -end - -function _gradient_1d!( - ::Type{LegendrePType{K}}, g::AbstractMatrix{T},x,d) where {K,T<:Number} - - n = K + 1 - z = zero(T) - @inbounds g[d,1] = z - if n > 1 - ξ = ( 2*x[d] - 1 ) - for i in 2:n - @inbounds g[d,i] = sqrt(2*i-1)*i*jacobi(ξ,i-2,1,1) - end - end -end - -function _hessian_1d!( - ::Type{LegendrePType{K}}, h::AbstractMatrix{T},x,d) where {K,T<:Number} - - n = K + 1 - z = zero(T) - @inbounds h[d,1] = z - if n > 1 - @inbounds h[d,2] = z - ξ = ( 2*x[d] - 1 ) - for i in 3:n - @inbounds h[d,i] = sqrt(2*i-1)*(i*(i+1)/2)*jacobi(ξ,i-3,2,2) - end - end -end - -function _evaluate_nd_leg!( - v::AbstractVector{V}, - x, - orders, - terms::AbstractVector{CartesianIndex{D}}, - c::AbstractMatrix{T}) where {V,T,D} - - dim = D - for d in 1:dim - _evaluate_1d!(LegendrePType{orders[d]},c,x,d) - end - - o = one(T) - k = 1 - - for ci in terms - - s = o - for d in 1:dim - @inbounds s *= c[d,ci[d]] - end - - k = _set_value!(v,s,k) - - end - -end - -function _gradient_nd_leg!( - v::AbstractVector{G}, - x, - orders, - terms::AbstractVector{CartesianIndex{D}}, - c::AbstractMatrix{T}, - g::AbstractMatrix{T}, - ::Type{V}) where {G,T,D,V} - - dim = D - for d in 1:dim - _derivatives_1d!(LegendrePType{orders[d]},(c,g),x,d) - end - - z = zero(Mutable(VectorValue{D,T})) - o = one(T) - k = 1 - - for ci in terms - - 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!(v,s,k,V) - - end - -end - -function _hessian_nd_leg!( - v::AbstractVector{G}, - x, - orders, - terms::AbstractVector{CartesianIndex{D}}, - c::AbstractMatrix{T}, - g::AbstractMatrix{T}, - h::AbstractMatrix{T}, - ::Type{V}) where {G,T,D,V} - - dim = D - for d in 1:dim - _derivatives_1d!(LegendrePType{orders[d]},(c,g,h),x,d) - end - - z = zero(Mutable(TensorValue{D,D,T})) - o = one(T) - k = 1 - - for ci in terms - - 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!(v,s,k,V) - - end - -end diff --git a/src/Polynomials/ModalC0Bases.jl b/src/Polynomials/ModalC0Bases.jl index e5a02cc53..903114dde 100644 --- a/src/Polynomials/ModalC0Bases.jl +++ b/src/Polynomials/ModalC0Bases.jl @@ -85,7 +85,7 @@ end get_order(b::ModalC0Basis) """ function get_order(b::ModalC0Basis) - maximum(b.orders) + maximum(b.orders, init=0) end """ @@ -103,7 +103,7 @@ function return_cache(f::ModalC0Basis{D,T,V},x::AbstractVector{<:Point}) where { @assert D == length(eltype(x)) "Incorrect number of point components" np = length(x) ndof = length(f) - n = 1 + _maximum(f.orders) + n = get_order(f) + 1 r = CachedArray(zeros(T,(np,ndof))) v = CachedArray(zeros(T,(ndof,))) c = CachedArray(zeros(eltype(T),(D,n))) @@ -114,7 +114,7 @@ function evaluate!(cache,f::ModalC0Basis{D,T,V},x::AbstractVector{<:Point}) wher r, v, c = cache np = length(x) ndof = length(f) - n = 1 + _maximum(f.orders) + n = get_order(f) + 1 setsize!(r,(np,ndof)) setsize!(v,(ndof,)) setsize!(c,(D,n)) @@ -138,7 +138,7 @@ function return_cache( ndof = length(f) xi = testitem(x) T = gradient_type(V,xi) - n = 1 + _maximum(f.orders) + n = get_order(f) + 1 r = CachedArray(zeros(T,(np,ndof))) v = CachedArray(zeros(T,(ndof,))) c = CachedArray(zeros(eltype(T),(D,n))) @@ -155,7 +155,7 @@ function evaluate!( r, v, c, g = cache np = length(x) ndof = length(f) - n = 1 + _maximum(f.orders) + n = get_order(f) + 1 setsize!(r,(np,ndof)) setsize!(v,(ndof,)) setsize!(c,(D,n)) @@ -180,7 +180,7 @@ function return_cache( ndof = length(f) xi = testitem(x) T = gradient_type(gradient_type(V,xi),xi) - n = 1 + _maximum(f.orders) + n = get_order(f) + 1 r = CachedArray(zeros(T,(np,ndof))) v = CachedArray(zeros(T,(ndof,))) c = CachedArray(zeros(eltype(T),(D,n))) @@ -198,7 +198,7 @@ function evaluate!( r, v, c, g, h = cache np = length(x) ndof = length(f) - n = 1 + _maximum(f.orders) + n = get_order(f) + 1 setsize!(r,(np,ndof)) setsize!(v,(ndof,)) setsize!(c,(D,n)) @@ -309,7 +309,7 @@ function _define_terms_mc0(filter,sort!,orders) end """ -Reference: equation (16) in +Reference: equation (17) in Badia, S.; Neiva, E. & Verdugo, F.; (2022); Robust high-order unfitted finite elements by interpolation-based discrete extension, @@ -363,7 +363,7 @@ function _hessian_1d_mc0!(v::AbstractMatrix{T},x,a,b,order,d) where T 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) + @inbounds v[d,i] = -sqrt(2*i-3)*(2*dv1*dv2*j+2*(dv1*v2+v1*dv2)*(2/(b[d]-[d]))*dj+v1*v2*d2j*2*i*((b[d]-a[d])^2))/(i-2) end end end diff --git a/src/Polynomials/MonomialBases.jl b/src/Polynomials/MonomialBases.jl index 957906056..792b392d6 100644 --- a/src/Polynomials/MonomialBases.jl +++ b/src/Polynomials/MonomialBases.jl @@ -1,364 +1,32 @@ """ - MonomialPType{K} <: PolynomialType{K} + Monomial <: Polynomial -Type representing the monomial polynomials of order up to `K` +Type representing the monomial polynomials """ -struct MonomialPType{K} <: PolynomialType{K} end -isHierarchical(::MonomialPType) = true +struct Monomial <: Polynomial end -struct Monomial <: Field end +isHierarchical(::Monomial) = true """ - struct MonomialBasis{D,T} <: AbstractVector{Monomial} + MonomialBasis{D,T,K} = TensorPolynomialBasis{D,T,K,Monomial} -Type representing a basis of multivariate scalar-valued, vector-valued, or -tensor-valued, iso- or aniso-tropic monomials. The fields -of this `struct` are not public. -This type fully implements the [`Field`](@ref) interface, with up to second order -derivatives. +Multivariate scalar' or `Multivalue`'d monomial basis, see [`TensorPolynomialBasis`](@ref) """ -struct MonomialBasis{D,T} <: AbstractVector{Monomial} - orders::NTuple{D,Int} - terms::Vector{CartesianIndex{D}} - function MonomialBasis{D}( - ::Type{T}, orders::NTuple{D,Int}, terms::Vector{CartesianIndex{D}}) where {D,T} - new{D,T}(orders,terms) - end -end - -Base.size(a::MonomialBasis{D,T}) where {D,T} = (length(a.terms)*num_indep_components(T),) -# @santiagobadia : Not sure we want to create the monomial machinery -Base.getindex(a::MonomialBasis,i::Integer) = Monomial() -Base.IndexStyle(::MonomialBasis) = IndexLinear() +const MonomialBasis{D,T,K} = TensorPolynomialBasis{D,T,K,Monomial} """ + MonomialBasis{D}(::Type{T}, order::Int, terms::Vector) where {D,T} MonomialBasis{D}(::Type{T}, orders::Tuple [, filter::Function]) where {D,T} - -This version of the constructor allows to pass a tuple `orders` containing the -polynomial order to be used in each of the `D` dimensions in order to construct -an anisotropic tensor-product space. -""" -function MonomialBasis{D}( - ::Type{T}, orders::NTuple{D,Int}, filter::Function=_q_filter) where {D,T} - - terms = _define_terms(filter, orders) - MonomialBasis{D}(T,orders,terms) -end - -""" MonomialBasis{D}(::Type{T}, order::Int [, filter::Function]) where {D,T} -Returns an instance of `MonomialBasis` representing a multivariate polynomial basis -in `D` dimensions, of polynomial degree `order`, whose value is represented by the type `T`. -The type `T` is typically `<:Number`, e.g., `Float64` for scalar-valued functions and `VectorValue{D,Float64}` -for vector-valued ones. - -# Filter function - -The `filter` function is used to select which terms of the tensor product space -of order `order` in `D` dimensions are to be used. If the filter is not provided, the full tensor-product -space is used by default leading to a multivariate polynomial space of type Q. -The signature of the filter function is - - (e,order) -> Bool - -where `e` is a tuple of `D` integers containing the exponents of a multivariate monomial. The following filters -are used to select well known polynomial spaces - -- Q space: `(e,order) -> true` -- P space: `(e,order) -> sum(e) <= order` -- "Serendipity" space: `(e,order) -> sum( [ i for i in e if i>1 ] ) <= order` - +Convenience constructors of MonomialBasis{D,T}. """ -function MonomialBasis{D}( - ::Type{T}, order::Int, filter::Function=_q_filter) where {D,T} - - orders = tfill(order,Val{D}()) - MonomialBasis{D}(T,orders,filter) -end - -# API - -""" - get_exponents(b::MonomialBasis) - -Get a vector of tuples with the exponents of all the terms in the -monomial basis. - -# Examples - -```jldoctest -using Gridap.Polynomials - -b = MonomialBasis{2}(Float64,2) - -exponents = get_exponents(b) - -println(exponents) - -# output -Tuple{Int,Int}[(0, 0), (1, 0), (2, 0), (0, 1), (1, 1), (2, 1), (0, 2), (1, 2), (2, 2)] -``` -""" -function get_exponents(b::MonomialBasis) - indexbase = 1 - [Tuple(t) .- indexbase for t in b.terms] -end - -""" - get_order(b::MonomialBasis) -""" -function get_order(b::MonomialBasis) - maximum(b.orders) -end - -""" - get_orders(b::MonomialBasis) -""" -function get_orders(b::MonomialBasis) - b.orders -end - -""" -""" -return_type(::MonomialBasis{D,T}) where {D,T} = T - -# Field implementation -function return_cache(f::MonomialBasis{D,T},x::AbstractVector{<:Point}) where {D,T} - @check D == length(eltype(x)) "Incorrect number of point components" - zT = zero(T) - zxi = zero(eltype(eltype(x))) - Tp = typeof( zT*zxi*zxi + zT*zxi*zxi ) - np = length(x) - ndof = length(f) - n = 1 + _maximum(f.orders) - r = CachedArray(zeros(Tp,(np,ndof))) - v = CachedArray(zeros(Tp,(ndof,))) - c = CachedArray(zeros(eltype(Tp),(D,n))) - (r, v, c) -end - -function evaluate!(cache,f::MonomialBasis{D,T},x::AbstractVector{<:Point}) where {D,T} - r, v, c = cache - np = length(x) - ndof = length(f) - 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!(v,xi,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,MonomialBasis{D,V}}, - x::AbstractVector{<:Point}, - ::Type{T}, - TisbitsType::Val{true}) where {D,V,T} - - f = fg.fa - @check D == length(eltype(x)) "Incorrect number of point components" - np = length(x) - ndof = length(f) - 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 _return_cache( - fg::FieldGradientArray{1,MonomialBasis{D,V}}, - x::AbstractVector{<:Point}, - ::Type{T}, - TisbitsType::Val{false}) where {D,V,T} - - cache = _return_cache(fg,x,T,Val{true}()) - z = CachedArray(zeros(eltype(T),D)) - (cache...,z) -end - -function return_cache( - fg::FieldGradientArray{1,MonomialBasis{D,V}}, - x::AbstractVector{<:Point}) where {D,V} - - xi = testitem(x) - T = gradient_type(V,xi) - TisbitsType = Val(isbitstype(T)) - _return_cache(fg,x,T,TisbitsType) -end - -function _evaluate!( - cache, - fg::FieldGradientArray{1,MonomialBasis{D,T}}, - x::AbstractVector{<:Point}, - TisbitsType::Val{true}) where {D,T} - - f = fg.fa - r, v, c, g = cache - z = zero(Mutable(VectorValue{D,eltype(T)})) - np = length(x) - ndof = length(f) - 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!(v,xi,f.orders,f.terms,c,g,z,T) - for j in 1:ndof - @inbounds r[i,j] = v[j] - end - end - r.array -end - -function _evaluate!( - cache, - fg::FieldGradientArray{1,MonomialBasis{D,T}}, - x::AbstractVector{<:Point}, - TisbitsType::Val{false}) where {D,T} - - f = fg.fa - r, v, c, g, z = cache - np = length(x) - ndof = length(f) - 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!(v,xi,f.orders,f.terms,c,g,z,T) - for j in 1:ndof - @inbounds r[i,j] = v[j] - end - end - r.array -end - -function evaluate!( - cache, - fg::FieldGradientArray{1,MonomialBasis{D,T}}, - x::AbstractVector{<:Point}) where {D,T} - - r, v, c, g = cache - TisbitsType = Val(isbitstype(eltype(c))) - _evaluate!(cache,fg,x,TisbitsType) -end - -function return_cache( - fg::FieldGradientArray{2,MonomialBasis{D,V}}, - x::AbstractVector{<:Point}) where {D,V} - - f = fg.fa - @check D == length(eltype(x)) "Incorrect number of point components" - np = length(x) - ndof = length(f) - 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,MonomialBasis{D,T}}, - x::AbstractVector{<:Point}) where {D,T} - - f = fg.fa - r, v, c, g, h = cache - np = length(x) - ndof = length(f) - 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!(v,xi,f.orders,f.terms,c,g,h,T) - for j in 1:ndof - @inbounds r[i,j] = v[j] - end - end - r.array -end - -# Optimizing evaluation at a single point +MonomialBasis{D}(args...) where {D} = TensorPolynomialBasis{D}(Monomial, args...) -function return_cache(f::AbstractVector{Monomial},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{Monomial},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{Monomial}}, 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 +# 1D evaluation implementation -function evaluate!( - cache, f::FieldGradientArray{N,<:AbstractVector{Monomial}}, 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 - -_q_filter(e,o) = true - -function _define_terms(filter,orders) - t = orders .+ 1 - g = (0 .* orders) .+ 1 - cis = CartesianIndices(t) - co = CartesianIndex(g) - maxorder = _maximum(orders) - [ ci for ci in cis if filter(Int[Tuple(ci-co)...],maxorder) ] -end - -function _evaluate_1d!( - ::Type{MonomialPType{K}}, v::AbstractMatrix{T},x,d) where {K,T<:Number} - - n = K + 1 +function _evaluate_1d!(::Type{Monomial}, k, v::AbstractMatrix{T},x,d) where T<:Number + n = k + 1 @inbounds xd = x[d] xn = one(T) for i in 1:n @@ -367,10 +35,8 @@ function _evaluate_1d!( end end -function _gradient_1d!( - ::Type{MonomialPType{K}}, g::AbstractMatrix{T},x,d) where {K,T<:Number} - - n = K + 1 +function _gradient_1d!(::Type{Monomial}, k, g::AbstractMatrix{T},x,d) where T<:Number + n = k + 1 z = zero(T) @inbounds g[d,1] = z @inbounds xd = x[d] @@ -381,10 +47,8 @@ function _gradient_1d!( end end -function _hessian_1d!( - ::Type{MonomialPType{K}}, h::AbstractMatrix{T},x,d) where {K,T<:Number} - - n = K + 1 +function _hessian_1d!(::Type{Monomial}, k, h::AbstractMatrix{T},x,d) where T<:Number + n = k + 1 z = zero(T) @inbounds h[d,1] = z if n>1 @@ -398,213 +62,3 @@ function _hessian_1d!( end end -function _evaluate_nd!( - v::AbstractVector{V}, - x, - orders, - terms::AbstractVector{CartesianIndex{D}}, - c::AbstractMatrix{T}) where {V,T,D} - - dim = D - for d in 1:dim - _evaluate_1d!(MonomialPType{orders[d]},c,x,d) - end - - o = one(T) - k = 1 - - for ci in terms - - s = o - for d in 1:dim - @inbounds s *= c[d,ci[d]] - end - - k = _set_value!(v,s,k) - - end - -end - -function _set_value!(v::AbstractVector{V},s::T,k) where {V,T} - ncomp = num_indep_components(V) - z = zero(T) - @inbounds for j in 1:ncomp - v[k] = ntuple(i -> ifelse(i == j, s, z),Val(ncomp)) - k += 1 - end - k -end - -function _set_value!(v::AbstractVector{<:Real},s,k) - @inbounds v[k] = s - k+1 -end - -function _gradient_nd!( - v::AbstractVector{G}, - x, - orders, - terms::AbstractVector{CartesianIndex{D}}, - c::AbstractMatrix{T}, - g::AbstractMatrix{T}, - z::AbstractVector{T}, - ::Type{V}) where {G,T,D,V} - - dim = D - for d in 1:dim - _derivatives_1d!(MonomialPType{orders[d]},(c,g),x,d) - end - - o = one(T) - k = 1 - - for ci in terms - - 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!(v,s,k,V) - - end - -end - -function _set_gradient!( - v::AbstractVector{G},s,k,::Type{<:Real}) where G - - @inbounds v[k] = s - k+1 -end - -@generated function  _set_gradient!( - v::AbstractVector{G},s,k,::Type{V}) where {V,G} - # Git blame me for readable non-generated version - - w = zero(V) - m = Array{String}(undef, size(G)) - N_val_dims = length(size(V)) - s_size = size(G)[1:end-N_val_dims] - - body = "T = eltype(s); z = zero(T);" - for ci in CartesianIndices(s_size) - id = join(Tuple(ci)) - body *= "@inbounds s$id = s[$ci];" - end - - for j in CartesianIndices(w) - for i in CartesianIndices(m) - m[i] = "z" - end - for ci in CartesianIndices(s_size) - id = join(Tuple(ci)) - m[ci,j] = "s$id" - end - body *= "@inbounds v[k] = ($(join(tuple(m...), ", ")));" - body *= "k = k + 1;" - end - - body = Meta.parse(string("begin ",body," end")) - return Expr(:block, body ,:(return k)) -end - -# Specialization for SymTensorValue and SymTracelessTensorValue, -# necessary as long as outer(Point, V<:AbstractSymTensorValue)::G does not -# return a tensor type that implements the appropriate symmetries of the -# gradient (and hessian) -@generated function _set_gradient!( - v::AbstractVector{G},s,k,::Type{V}) where {V<:AbstractSymTensorValue{D},G} where D - # Git blame me for readable non-generated version - - T = eltype(s) - m = Array{String}(undef, size(G)) - s_length = size(G)[1] - - is_traceless = V <: SymTracelessTensorValue - skip_last_diagval = is_traceless ? 1 : 0 # Skid V_DD if traceless - - body = "z = $(zero(T));" - for i in 1:s_length - body *= "@inbounds s$i = s[$i];" - end - - for c in 1:(D-skip_last_diagval) # Go over cols - for r in c:D # Go over lower triangle, current col - for i in eachindex(m) - m[i] = "z" - end - for i in 1:s_length # indices of the Vector s - m[i,r,c] = "s$i" - if (r!=c) - m[i,c,r] = "s$i" - elseif is_traceless # V_rr contributes negatively to V_DD (tracelessness) - m[i,D,D] = "-s$i" - end - end - body *= "@inbounds v[k] = ($(join(tuple(m...), ", ")));" - body *= "k = k + 1;" - end - end - - body = Meta.parse(string("begin ",body," end")) - return Expr(:block, body ,:(return k)) -end - -function _hessian_nd!( - v::AbstractVector{G}, - x, - orders, - terms::AbstractVector{CartesianIndex{D}}, - c::AbstractMatrix{T}, - g::AbstractMatrix{T}, - h::AbstractMatrix{T}, - ::Type{V}) where {G,T,D,V} - - dim = D - for d in 1:dim - _derivatives_1d!(MonomialPType{orders[d]},(c,g,h),x,d) - end - - z = zero(Mutable(TensorValue{D,D,T})) - o = one(T) - k = 1 - - for ci in terms - - 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!(v,s,k,V) - - end - -end - -_maximum(orders::Tuple{}) = 0 -_maximum(orders) = maximum(orders) diff --git a/src/Polynomials/OldMonomialHelpers.jl b/src/Polynomials/OldMonomialHelpers.jl new file mode 100644 index 000000000..951965cce --- /dev/null +++ b/src/Polynomials/OldMonomialHelpers.jl @@ -0,0 +1,328 @@ + +function _evaluate_nd!( + v::AbstractVector{V}, + x, + orders, + terms::AbstractVector{CartesianIndex{D}}, + c::AbstractMatrix{T}) where {V,T,D} + + dim = D + for d in 1:dim + _evaluate_1d!(Monomial,orders[d],c,x,d) + end + + o = one(T) + k = 1 + + for ci in terms + + s = o + for d in 1:dim + @inbounds s *= c[d,ci[d]] + end + + k = _set_value!(v,s,k) + + end + +end + +function _set_value!(v::AbstractVector{V},s::T,k) where {V,T} + ncomp = num_indep_components(V) + z = zero(T) + @inbounds for j in 1:ncomp + v[k] = ntuple(i -> ifelse(i == j, s, z),Val(ncomp)) + k += 1 + end + k +end + +function _set_value!(v::AbstractVector{<:Real},s,k) + @inbounds v[k] = s + k+1 +end + +function _gradient_nd!( + v::AbstractVector{G}, + x, + orders, + terms::AbstractVector{CartesianIndex{D}}, + c::AbstractMatrix{T}, + g::AbstractMatrix{T}, + z::AbstractVector{T}, + ::Type{V}) where {G,T,D,V} + + dim = D + for d in 1:dim + _derivatives_1d!(Monomial,orders[d],(c,g),x,d) + end + + o = one(T) + k = 1 + + for ci in terms + + 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!(v,s,k,V) + + end + +end + +function _set_gradient!( + v::AbstractVector{G},s,k,::Type{<:Real}) where G + + @inbounds v[k] = s + k+1 +end + +@generated function _set_gradient!( + v::AbstractVector{G},s,k,::Type{V}) where {V,G} + # Git blame me for readable non-generated version + + w = zero(V) + m = Array{String}(undef, size(G)) + N_val_dims = length(size(V)) + s_size = size(G)[1:end-N_val_dims] + + body = "T = eltype(s); z = zero(T);" + for ci in CartesianIndices(s_size) + id = join(Tuple(ci)) + body *= "@inbounds s$id = s[$ci];" + end + + for j in CartesianIndices(w) + for i in CartesianIndices(m) + m[i] = "z" + end + for ci in CartesianIndices(s_size) + id = join(Tuple(ci)) + m[ci,j] = "s$id" + end + body *= "@inbounds v[k] = ($(join(tuple(m...), ", ")));" + body *= "k = k + 1;" + end + + body = Meta.parse(string("begin ",body," end")) + return Expr(:block, body ,:(return k)) +end + +# Specialization for SymTensorValue and SymTracelessTensorValue, +# necessary as long as outer(Point, V<:AbstractSymTensorValue)::G does not +# return a tensor type that implements the appropriate symmetries of the +# gradient (and hessian) +@generated function _set_gradient!( + v::AbstractVector{G},s,k,::Type{V}) where {V<:AbstractSymTensorValue{D},G} where D + # Git blame me for readable non-generated version + + T = eltype(s) + m = Array{String}(undef, size(G)) + s_length = size(G)[1] + + is_traceless = V <: SymTracelessTensorValue + skip_last_diagval = is_traceless ? 1 : 0 # Skid V_DD if traceless + + body = "z = $(zero(T));" + for i in 1:s_length + body *= "@inbounds s$i = s[$i];" + end + + for c in 1:(D-skip_last_diagval) # Go over cols + for r in c:D # Go over lower triangle, current col + for i in eachindex(m) + m[i] = "z" + end + for i in 1:s_length # indices of the Vector s + m[i,r,c] = "s$i" + if (r!=c) + m[i,c,r] = "s$i" + elseif is_traceless # V_rr contributes negatively to V_DD (tracelessness) + m[i,D,D] = "-s$i" + end + end + body *= "@inbounds v[k] = ($(join(tuple(m...), ", ")));" + body *= "k = k + 1;" + end + end + + body = Meta.parse(string("begin ",body," end")) + return Expr(:block, body ,:(return k)) +end + +function _hessian_nd!( + v::AbstractVector{G}, + x, + orders, + terms::AbstractVector{CartesianIndex{D}}, + c::AbstractMatrix{T}, + g::AbstractMatrix{T}, + h::AbstractMatrix{T}, + ::Type{V}) where {G,T,D,V} + + dim = D + for d in 1:dim + _derivatives_1d!(Monomial,orders[d],(c,g,h),x,d) + end + + z = zero(Mutable(TensorValue{D,D,T})) + o = one(T) + k = 1 + + for ci in terms + + 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!(v,s,k,V) + + end + +end + +#function evaluate!( +# #cache, f::FieldGradientArray{N,<:AbstractVector{Monomial}}, x::VectorValue) where {N} +# cache, f::FieldGradientArray{N,<:AbstractVector{<:Type{Polynomial}}}, x::VectorValue) 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 + +# Field implementation +#function _evaluate!( +# cache, +# fg::FieldGradientArray{1,MonomialBasis{D,T}}, +# x::AbstractVector{<:Point}, +# TisbitsType::Val{true}) where {D,T} +# +# f = fg.fa +# r, v, c, g = cache +# z = zero(Mutable(VectorValue{D,eltype(T)})) +# np = length(x) +# ndof = length(f) +# 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!(v,xi,f.orders,f.terms,c,g,z,T) +# for j in 1:ndof +# @inbounds r[i,j] = v[j] +# end +# end +# r.array +#end +# +#function _evaluate!( +# cache, +# fg::FieldGradientArray{1,MonomialBasis{D,T}}, +# x::AbstractVector{<:Point}, +# TisbitsType::Val{false}) where {D,T} +# +# f = fg.fa +# r, v, c, g, z = cache +# np = length(x) +# ndof = length(f) +# 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!(v,xi,f.orders,f.terms,c,g,z,T) +# for j in 1:ndof +# @inbounds r[i,j] = v[j] +# end +# end +# r.array +#end +# +#function evaluate!( +# cache, +# fg::FieldGradientArray{1,MonomialBasis{D,T}}, +# x::AbstractVector{<:Point}) where {D,T} +# +# r, v, c, g = cache +# TisbitsType = Val(isbitstype(eltype(c))) +# _evaluate!(cache,fg,x,TisbitsType) +#end +# +#function return_cache( +# fg::FieldGradientArray{2,MonomialBasis{D,V}}, +# x::AbstractVector{<:Point}) where {D,V} +# +# f = fg.fa +# @check D == length(eltype(x)) "Incorrect number of point components" +# np = length(x) +# ndof = length(f) +# 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,MonomialBasis{D,T}}, +# x::AbstractVector{<:Point}) where {D,T} +# +# f = fg.fa +# r, v, c, g, h = cache +# np = length(x) +# ndof = length(f) +# 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!(v,xi,f.orders,f.terms,c,g,h,T) +# for j in 1:ndof +# @inbounds r[i,j] = v[j] +# end +# end +# r.array +#end + diff --git a/src/Polynomials/PCurlGradLegendrePolynomialBases.jl b/src/Polynomials/PCurlGradLegendrePolynomialBases.jl index b3ed05f76..a7fe8e03e 100644 --- a/src/Polynomials/PCurlGradLegendrePolynomialBases.jl +++ b/src/Polynomials/PCurlGradLegendrePolynomialBases.jl @@ -1,5 +1,5 @@ """ -struct PCurlGradLegendrePolynomialBasis{...} <: AbstractArray{LegendrePolynomial} +struct PCurlGradLegendrePolynomialBasis{...} <: AbstractArray{Legendre} This type implements a multivariate vector-valued polynomial basis spanning the space needed for Raviart-Thomas reference elements on simplices. @@ -7,7 +7,7 @@ The type parameters and fields of this `struct` are not public. This type fully implements the [`Field`](@ref) interface, with up to first order derivatives. """ -struct PCurlGradLegendrePolynomialBasis{D,T} <: AbstractVector{LegendrePolynomial} +struct PCurlGradLegendrePolynomialBasis{D,T} <: AbstractVector{Legendre} order::Int pterms::Array{CartesianIndex{D},1} sterms::Array{CartesianIndex{D},1} @@ -21,7 +21,7 @@ end Base.size(a::PCurlGradLegendrePolynomialBasis) = (_ndofs_pgrad(a),) # @santiagobadia : Not sure we want to create the monomial machinery -Base.getindex(a::PCurlGradLegendrePolynomialBasis,i::Integer) = LegendrePolynomial() +Base.getindex(a::PCurlGradLegendrePolynomialBasis,i::Integer) = Legendre() Base.IndexStyle(::PCurlGradLegendrePolynomialBasis) = IndexLinear() """ @@ -142,7 +142,7 @@ function _evaluate_nd_pcurlgrad_leg!( dim = D for d in 1:dim - _evaluate_1d!(LegendrePType{order},c,x,d) + _evaluate_1d!(Legendre,order,c,x,d) end o = one(T) @@ -201,7 +201,7 @@ function _gradient_nd_pcurlgrad_leg!( dim = D for d in 1:dim - _derivatives_1d!(LegendrePType{order},(c,g),x,d) + _derivatives_1d!(Legendre,order,(c,g),x,d) end z = zero(Mutable(V)) diff --git a/src/Polynomials/PCurlGradMonomialBases.jl b/src/Polynomials/PCurlGradMonomialBases.jl index 5f5358c55..5a59848b8 100644 --- a/src/Polynomials/PCurlGradMonomialBases.jl +++ b/src/Polynomials/PCurlGradMonomialBases.jl @@ -130,17 +130,6 @@ end # Helpers -_p_filter(e,order) = (sum(e) <= order) -_s_filter(e,order) = (sum(e) == order) - -function _p_dim(order,D) - dim = 1 - for d in 1:D - dim *= order+d - end - dim/factorial(D) -end - _ndofs_pgrad(f::PCurlGradMonomialBasis{D}) where D = num_terms(f) @@ -155,7 +144,7 @@ function _evaluate_nd_pcurlgrad!( dim = D for d in 1:dim - _evaluate_1d!(MonomialPType{order},c,x,d) + _evaluate_1d!(Monomial,order,c,x,d) end o = one(T) @@ -214,7 +203,7 @@ function _gradient_nd_pcurlgrad!( dim = D for d in 1:dim - _derivatives_1d!(MonomialPType{order},(c,g),x,d) + _derivatives_1d!(Monomial,order,(c,g),x,d) end z = zero(Mutable(V)) diff --git a/src/Polynomials/PolynomialInterfaces.jl b/src/Polynomials/PolynomialInterfaces.jl index 379ee6750..b4b2329e0 100644 --- a/src/Polynomials/PolynomialInterfaces.jl +++ b/src/Polynomials/PolynomialInterfaces.jl @@ -3,102 +3,142 @@ ################################################# """ -Abstract type for 1- or n-D polynomial family of maximum order K. - -`PolynomialType` subtypes must keep `K` as last type parameter. - -Implement [`isHierarchical`](@ref) and [`get_order`](@ref). -""" -abstract type PolynomialType{K} end +Abstract type for 1- or n-D polynomial family of maximum order k. +Implements [`isHierarchical`](@ref). """ -Return true if the polynomial basis of order `K` of the given type is the union -of the basis of order `K-1` and an other order `K` polynomial. -""" -isHierarchical(::PolynomialType) = @abstractmethod +abstract type Polynomial <: Field end """ - get_order(PT<:PolynomialType{K}}) returns K +Return true if the basis of order `k` of the given `<:Polynomial` type is the union +of the basis of order `k-1` and an other order `k` polynomial. """ -get_order(::Type{PolynomialType{K}}) where K = K +isHierarchical(::Polynomial) = @abstractmethod +#""" +# get_order(::Type{PolynomialType{K}}) where K = K +#""" +#get_order(::Type{PolynomialType{K}}) where K = K #""" -# JacobiPType{α, β, K} <: PolynomialType{K} +# Jacobi{α, β} <: Polynomial # -#Type representing Jacobi polynomials of parameters `α` and `β` and order up to `K` +#Type representing Jacobi polynomials of parameters `α` and `β` #""" -#struct JacobiPType{α, β, K} <: PolynomialType{K} end -#isHierarchical(::JacobiPType) = true +#struct Jacobi{α, β} <: Polynomial end +#isHierarchical(::Jacobi) = true -###################### -# 1D polynomial APIs # -###################### +############################### +# 1D internal polynomial APIs # +############################### + +# TODO pass order argument as Val{k} to use compile time dispatch to optimize +# edge cases (k=0,1) and possible polynomial coefficients that can be +# pre-computed at compile time """ - _evaluate_1d!(::Type{<:PolynomialType{K}}, v, x, d) + _evaluate_1d!(::Type{<:Polynomial}, k v, x, d) Evaluates in place the 1D basis polynomials of the given type at one nD point `x` along the given coordinate 1 ≤ `d` ≤ nD. -`v` is an AbstractMatrix of size (at least} d×(K+1), such that the 1 ≤ i ≤ `K`+1 +`v` is an AbstractMatrix of size (at least} d×(k+1), such that the 1 ≤ i ≤ `k`+1 values are stored in `v[d,i]`. """ -function _evaluate_1d!( - ::Type{<:PolynomialType{K}}, v::AbstractMatrix{T},x,d) where {K,T<:Number} - +function _evaluate_1d!( ::Type{<:Polynomial},k,v::AbstractMatrix{T},x,d) where T<:Number @abstractmethod end """ - _gradient_1d!(::Type{<:PolynomialType{K}}, g, x, d) + _gradient_1d!(::Type{<:Polynomial}, k, g, x, d) Like [`_evaluate_1d!`](@ref), but computes the first derivative of the basis functions. """ -function _gradient_1d!( - ::Type{<:PolynomialType{K}}, g::AbstractMatrix{T},x,d) where {K,T<:Number} - +function _gradient_1d!( ::Type{<:Polynomial},k,g::AbstractMatrix{T},x,d) where T<:Number @abstractmethod end """ - _hessian_1d!(::Type{<:PolynomialType{K}}, h, x, d) + _hessian_1d!(::Type{<:Polynomial}, k, h, x, d) Like [`_evaluate_1d!`](@ref), but computes the second derivative of the basis functions. """ -function _hessian_1d!( - ::Type{<:PolynomialType{K}}, h::AbstractMatrix{T},x,d) where {K,T<:Number} - +function _hessian_1d!( ::Type{<:Polynomial},k,h::AbstractMatrix{T},x,d) where T<:Number @abstractmethod end """ - _derivatives_1d!(PT::Type{<:PolynomialType{K}}, (v,g,...), x, d) + _derivatives_1d!(PT::Type{<:Polynomial}, k, (v,g,...), x, d) Same as calling ``` -_evaluate_1d!(PT, v, x d) -_gradient_1d!(PT, g, x d) +_evaluate_1d!(PT, k, v, x d) +_gradient_1d!(PT, k, g, x d) ⋮ ``` but with possible performence optimization. """ -function _derivatives_1d!(PT::Type{<:PolynomialType},t::NTuple{N},x,d) where N +function _derivatives_1d!(PT::Type{<:Polynomial},k,t::NTuple{N},x,d) where N @abstractmethod end -function _derivatives_1d!(PT::Type{<:PolynomialType},t::NTuple{1},x,d) - _evaluate_1d!(PT, t[1], x, d) +function _derivatives_1d!(PT::Type{<:Polynomial},k,t::NTuple{1},x,d) + _evaluate_1d!(PT, k, t[1], x, d) end -function _derivatives_1d!(PT::Type{<:PolynomialType},t::NTuple{2},x,d) - _evaluate_1d!(PT, t[1], x, d) - _gradient_1d!(PT, t[2], x, d) +function _derivatives_1d!(PT::Type{<:Polynomial},k,t::NTuple{2},x,d) + _evaluate_1d!(PT, k, t[1], x, d) + _gradient_1d!(PT, k, t[2], x, d) end -function _derivatives_1d!(PT::Type{<:PolynomialType},t::NTuple{3},x,d) - _evaluate_1d!(PT, t[1], x, d) - _gradient_1d!(PT, t[2], x, d) - _hessian_1d!( PT, t[3], x, d) +function _derivatives_1d!(PT::Type{<:Polynomial},k,t::NTuple{3},x,d) + _evaluate_1d!(PT, k, t[1], x, d) + _gradient_1d!(PT, k, t[2], x, d) + _hessian_1d!( PT, k, t[3], x, d) end + +# Optimizing evaluation at a single point + +function return_cache(f::AbstractVector{PT},x::Point) where PT<:Polynomial + 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{PT},x::Point) where PT<:Polynomial + 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{PT}}, x::Point) where {N,PT<:Polynomial} + + 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{PT}}, x::Point) where {N,PT<:Polynomial} + + 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 + diff --git a/src/Polynomials/Polynomials.jl b/src/Polynomials/Polynomials.jl index a423ac023..34b245f8a 100644 --- a/src/Polynomials/Polynomials.jl +++ b/src/Polynomials/Polynomials.jl @@ -21,16 +21,17 @@ import Gridap.Fields: evaluate! import Gridap.Fields: return_cache import Gridap.Arrays: return_type +export Polynomial export MonomialBasis export QGradMonomialBasis export QCurlGradMonomialBasis export PCurlGradMonomialBasis export ModalC0Basis -export LegendrePolynomialBasis +export LegendreBasis export QGradLegendrePolynomialBasis export QCurlGradLegendrePolynomialBasis export PCurlGradLegendrePolynomialBasis -export ChebyshevPolynomialBasis +export ChebyshevBasis export QGradChebyshevPolynomialBasis export QCurlGradChebyshevPolynomialBasis export get_exponents @@ -38,11 +39,16 @@ export get_exponents export get_order export get_orders export num_terms +export isHierarchical include("PolynomialInterfaces.jl") +include("TensorPolynomialBases.jl") + include("MonomialBases.jl") +include("OldMonomialHelpers.jl") + include("BernsteinBases.jl") include("QGradMonomialBases.jl") @@ -53,12 +59,12 @@ include("PCurlGradMonomialBases.jl") include("ModalC0Bases.jl") -include("LegendrePolynomialBases.jl") +include("LegendreBases.jl") include("QGradLegendrePolynomialBases.jl") include("PCurlGradLegendrePolynomialBases.jl") -include("ChebyshevPolynomialBases.jl") +include("ChebyshevBases.jl") end # module diff --git a/src/Polynomials/QGradLegendrePolynomialBases.jl b/src/Polynomials/QGradLegendrePolynomialBases.jl index 41d815ee4..bc4c31c76 100644 --- a/src/Polynomials/QGradLegendrePolynomialBases.jl +++ b/src/Polynomials/QGradLegendrePolynomialBases.jl @@ -8,7 +8,7 @@ The type parameters and fields of this `struct` are not public. This type fully implements the [`Field`](@ref) interface, with up to first order derivatives. """ -struct QGradLegendrePolynomialBasis{D,T} <: AbstractVector{LegendrePolynomial} +struct QGradLegendrePolynomialBasis{D,T} <: AbstractVector{Legendre} order::Int terms::CartesianIndices{D} perms::Matrix{Int} @@ -18,7 +18,7 @@ struct QGradLegendrePolynomialBasis{D,T} <: AbstractVector{LegendrePolynomial} end Base.size(a::QGradLegendrePolynomialBasis) = (_ndofs_qgrad_leg(a),) -Base.getindex(a::QGradLegendrePolynomialBasis,i::Integer) = LegendrePolynomial() +Base.getindex(a::QGradLegendrePolynomialBasis,i::Integer) = Legendre() Base.IndexStyle(::QGradLegendrePolynomialBasis) = IndexLinear() """ @@ -136,7 +136,7 @@ function _evaluate_nd_qgrad_leg!( dim = D for d in 1:dim - _evaluate_1d!(LegendrePType{order},c,x,d) + _evaluate_1d!(Legendre,order,c,x,d) end o = one(T) @@ -180,7 +180,7 @@ function _gradient_nd_qgrad_leg!( dim = D for d in 1:dim - _derivatives_1d!(LegendrePType{order},(c,g),x,d) + _derivatives_1d!(Legendre,order,(c,g),x,d) end z = zero(Mutable(V)) @@ -237,7 +237,7 @@ The type parameters and fields of this `struct` are not public. This type fully implements the [`Field`](@ref) interface, with up to first order derivatives. """ -struct QCurlGradLegendrePolynomialBasis{D,T} <: AbstractVector{LegendrePolynomial} +struct QCurlGradLegendrePolynomialBasis{D,T} <: AbstractVector{Legendre} qgrad::QGradLegendrePolynomialBasis{D,T} function QCurlGradLegendrePolynomialBasis(::Type{T},order::Int,terms::CartesianIndices{D},perms::Matrix{Int}) where {D,T} qgrad = QGradLegendrePolynomialBasis(T,order,terms,perms) @@ -246,7 +246,7 @@ struct QCurlGradLegendrePolynomialBasis{D,T} <: AbstractVector{LegendrePolynomia end Base.size(a::QCurlGradLegendrePolynomialBasis) = (length(a.qgrad),) -Base.getindex(a::QCurlGradLegendrePolynomialBasis,i::Integer) = LegendrePolynomial() +Base.getindex(a::QCurlGradLegendrePolynomialBasis,i::Integer) = Legendre() Base.IndexStyle(::QCurlGradLegendrePolynomialBasis) = IndexLinear() """ diff --git a/src/Polynomials/QGradMonomialBases.jl b/src/Polynomials/QGradMonomialBases.jl index 1a1386c71..9de6ac25e 100644 --- a/src/Polynomials/QGradMonomialBases.jl +++ b/src/Polynomials/QGradMonomialBases.jl @@ -150,7 +150,7 @@ function _evaluate_nd_qgrad!( dim = D for d in 1:dim - _evaluate_1d!(MonomialPType{order},c,x,d) + _evaluate_1d!(Monomial,order,c,x,d) end o = one(T) @@ -194,7 +194,7 @@ function _gradient_nd_qgrad!( dim = D for d in 1:dim - _derivatives_1d!(MonomialPType{order},(c,g),x,d) + _derivatives_1d!(Monomial,order,(c,g),x,d) end z = zero(Mutable(V)) diff --git a/src/Polynomials/TensorPolynomialBases.jl b/src/Polynomials/TensorPolynomialBases.jl new file mode 100644 index 000000000..c7a785b71 --- /dev/null +++ b/src/Polynomials/TensorPolynomialBases.jl @@ -0,0 +1,505 @@ +################################# +# Tensorial nD polynomial bases # +################################# + +# Notations: +# D: spatial / input space dimension +# T: scalar type (Float64, ...) +# V: concrete type of values image space (T, VectorValue{D,T} etc.) +# G: concrete MultiValue type holding the gradient of a function of value V, i.e. gradient_type(V,Point{D}) +# +# PT: a concrete `Polynomial` type +# np: number of points at which a basis is evaluated +# ndof: number of basis vectors, num_indep_components(V) × dimension of the polynomial space +# ndof_1d: maximum of 1D basis vector in any spatial dimension + +""" + PolynomialBasis{D,V,K,PT<:Polynomial} <: AbstractVector{PT} + +Abstract type representing a generic multivariate polynomial basis. +The parameters are: +- `D`: the spatial dimension +- `V`: the image value type, e.g. `Real` or `<:MultiValue` +- `K`: the maximum order of a basis polynomial in one dimension +- `PT <: Polynomial`: the polynomial family (must be a concrete type) +""" +abstract type PolynomialBasis{D,V,K,PT<:Polynomial} <: AbstractVector{PT} end + +""" + struct TensorPolynomialBasis{D,V,K,PT} <: PolynomialBasis{D,V,K,PT} + +Type representing a basis of isotropic multivariate scalar-valued, vector-valued, or +tensor-valued polynomial that have a of tensor product structure of 1D +polynomial in each component. + +The fields of this `struct` are not public. +This type fully implements the [`Field`](@ref) interface, with up to second order +derivatives. +""" +struct TensorPolynomialBasis{D,V,K,PT} <: PolynomialBasis{D,V,K,PT} + orders::NTuple{D,Int} + terms::Vector{CartesianIndex{D}} + + function TensorPolynomialBasis{D}( + ::Type{PT}, + ::Type{V}, + orders::NTuple{D,Int}, + terms::Vector{CartesianIndex{D}}) where {D,V,PT<:Polynomial} + + K = maximum(orders; init=0) + new{D,V,K,PT}(orders,terms) + end +end + +@inline Base.size(a::TensorPolynomialBasis{D,V}) where {D,V} = (length(a.terms)*num_indep_components(V),) +@inline Base.getindex(a::TensorPolynomialBasis{D,V,K,PT}, i::Integer) where {D,V,K,PT} = PT() +@inline Base.IndexStyle(::TensorPolynomialBasis) = IndexLinear() + +""" + TensorPolynomialBasis{D}(::Type{PT}, ::Type{V}, orders::Tuple [, filter::Function]) + +This version of the constructor allows to pass a tuple `orders` containing the +polynomial order to be used in each of the `D` dimensions in order to construct +an anisotropic tensor-product space. +""" +function TensorPolynomialBasis{D}( + ::Type{PT}, ::Type{V}, orders::NTuple{D,Int}, filter::Function=_q_filter + ) where {D,V,PT} + + terms = _define_terms(filter, orders) + TensorPolynomialBasis{D}(PT,V,orders,terms) +end + +""" + TensorPolynomialBasis{D}(::Type{V}, order::Int [, filter::Function]) where {D,V} + +Returns an instance of `TensorPolynomialBasis` representing a multivariate polynomial basis +in `D` dimensions, of polynomial degree `order`, whose value is represented by the type `V`. +The type `V` is typically `<:Number`, e.g., `Float64` for scalar-valued functions and `VectorValue{D,Float64}` +for vector-valued ones. + +# Filter function + +The `filter` function is used to select which terms of the tensor product space +of order `order` in `D` dimensions are to be used. If the filter is not provided, the full tensor-product +space is used by default leading to a multivariate polynomial space of type Q. +The signature of the filter function is + + (e,order) -> Bool + +where `e` is a tuple of `D` integers containing the exponents of a multivariate monomial. The following filters +are used to select well known polynomial spaces + +- Q space: `(e,order) -> true` +- P space: `(e,order) -> sum(e) <= order` +- "Serendipity" space: `(e,order) -> sum( [ i for i in e if i>1 ] ) <= order` + +""" +function TensorPolynomialBasis{D}( + ::Type{PT}, ::Type{V}, order::Int, filter::Function=_q_filter) where {D,V,PT} + + orders = tfill(order,Val{D}()) + TensorPolynomialBasis{D}(PT,V,orders,filter) +end + +# API + +""" + get_exponents(b::TensorPolynomialBasis) + +Get a vector of tuples with the exponents of all the terms in the basis. + +# Examples + +```jldoctest +using Gridap.Polynomials + +b = MonomialBasis{2}(Float64,2) + +exponents = get_exponents(b) + +println(exponents) + +# output +Tuple{Int,Int}[(0, 0), (1, 0), (2, 0), (0, 1), (1, 1), (2, 1), (0, 2), (1, 2), (2, 2)] +``` +""" +function get_exponents(b::TensorPolynomialBasis) + indexbase = 1 + [Tuple(t) .- indexbase for t in b.terms] +end + +""" + get_order(b::TensorPolynomialBasis{D,V,K) = K + +Return the maximum polynomial order in a dimension, or `0` in 0D. +""" +get_order(::TensorPolynomialBasis{D,V,K}) where {D,V,K} = K + +""" + get_orders(b::TensorPolynomialBasis) + +Return the D-tuple of polynomial orders in each dimension +""" +function get_orders(b::TensorPolynomialBasis) + b.orders +end + +return_type(::TensorPolynomialBasis{D,V}) where {D,V} = V + + +########### +# Helpers # +########### + +_q_filter(e,o) = true +_p_filter(e,order) = (sum(e) <= order) +_s_filter(e,order) = (sum(e) == order) + +function _p_dim(order,D) + dim = 1 + for d in 1:D + dim *= order+d + end + dim/factorial(D) +end + +function _define_terms(filter,orders) + t = orders .+ 1 + g = (0 .* orders) .+ 1 + cis = CartesianIndices(t) + co = CartesianIndex(g) + maxorder = maximum(orders, init=0) + [ ci for ci in cis if filter(Int[Tuple(ci-co)...],maxorder) ] +end + + +######################## +# Field implementation # +######################## + +function _return_cache(f::TensorPolynomialBasis{D},x,::Type{G},N_deriv) where {D,G} + @assert D == length(eltype(x)) "Incorrect number of point components" + np = length(x) + ndof = length(f) + n = get_order(f) + 1 + # Cache for the returned array + r = CachedArray(zeros(G,(np,ndof))) + # Cache for basis functions at one point x[i] + v = CachedArray(zeros(G,(ndof,))) + # Cache for the 1D basis function values in each dimension (to be + # tensor-producted), and of their 1D N_deriv'th derivatives + t = ntuple( _ -> CachedArray(zeros(eltype(G),(D,n))), N_deriv) + (r, v, t...) +end + +function return_cache(f::TensorPolynomialBasis{D,V},x::AbstractVector{<:Point}) where {D,V} + _return_cache(f,x,V,1) +end + +function evaluate!(cache,f::TensorPolynomialBasis{D,V,K,PT},x::AbstractVector{<:Point}) where {D,V,K,PT} + r, v, c = cache + np = length(x) + ndof = length(f) + ndof_1d = get_order(f) + 1 # K+1 + setsize!(r,(np,ndof)) + setsize!(v,(ndof,)) + setsize!(c,(D,ndof_1d)) + for i in 1:np + # TODO Shouldn't we avoid accessing here ? (pass x and i) + @inbounds xi = x[i] + _tensorial_evaluate_nd!(PT,v,xi,f.orders,f.terms,c) + for j in 1:ndof + # TODO Shouldn't we assign in place in _tensorial_evaluate_nd instead of copying everything? + @inbounds r[i,j] = v[j] + end + end + r.array +end + +function return_cache( + fg::FieldGradientArray{1,<:TensorPolynomialBasis{D,V}}, + x::AbstractVector{<:Point}) where {D,V} + + f = fg.fa + xi = testitem(x) + G = gradient_type(V,xi) + _return_cache(f,x,G,2) +end + +function evaluate!( + cache, + fg::FieldGradientArray{1,<:TensorPolynomialBasis{D,V,K,PT}}, + x::AbstractVector{<:Point}) where {D,V,K,PT} + + f = fg.fa + r, v, c, g = cache + z = zero(Mutable(VectorValue{D,eltype(V)})) + np = length(x) + ndof = length(f) + ndof_1d = get_order(f) + 1 # K+1 + setsize!(r,(np,ndof)) + setsize!(v,(ndof,)) + setsize!(c,(D,ndof_1d)) + setsize!(g,(D,ndof_1d)) + for i in 1:np + # TODO Shouldn't we avoid accessing here ? (pass x and i) + @inbounds xi = x[i] + _tensorial_gradient_nd!(PT,v,xi,f.orders,f.terms,c,g,z,V) + for j in 1:ndof + # TODO Shouldn't we assign in place in _tensorial_gradient_nd instead of copying everything? + @inbounds r[i,j] = v[j] + end + end + r.array +end + +function return_cache( + fg::FieldGradientArray{2,<:TensorPolynomialBasis{D,V}}, + x::AbstractVector{<:Point}) where {D,V} + + f = fg.fa + xi = testitem(x) + G = gradient_type(gradient_type(V,xi),xi) + _return_cache(f,x,G,3) +end + +function evaluate!( + cache, + fg::FieldGradientArray{2,<:TensorPolynomialBasis{D,V,K,PT}}, + x::AbstractVector{<:Point}) where {D,V,K,PT} + + f = fg.fa + r, v, c, g, h = cache + np = length(x) + ndof = length(f) + ndof_1d = get_order(f) + 1 # K+1 + setsize!(r,(np,ndof)) + setsize!(v,(ndof,)) + setsize!(c,(D,ndof_1d)) + setsize!(g,(D,ndof_1d)) + setsize!(h,(D,ndof_1d)) + for i in 1:np + # TODO Shouldn't we avoid accessing here ? (pass x and i) + @inbounds xi = x[i] + _tensorial_hessian_nd!(PT,v,xi,f.orders,f.terms,c,g,h,V) + for j in 1:ndof + # TODO Shouldn't we assign in place in _tensorial_hessian_nd instead of copying everything? + @inbounds r[i,j] = v[j] + end + end + r.array +end + +# Evaluates + +function _tensorial_evaluate_nd!( + PT::Type{<:Polynomial}, + v::AbstractVector{V}, + x, + orders, + terms::AbstractVector{CartesianIndex{D}}, + c::AbstractMatrix{T}) where {V,D,T} + + for d in 1:D + _evaluate_1d!(PT,orders[d],c,x,d) + end + + o = one(T) + k = 1 + for ci in terms + + s = o + for d in 1:D + @inbounds s *= c[d,ci[d]] + end + + k = _isotropic_set_value!(v,s,k) + end +end + +function _isotropic_set_value!(v::AbstractVector{<:Real},s,k) + @inbounds v[k] = s + k+1 +end + +# If ncomp is the number of independent components of V, sets ncomp values in v +# such that the j^th is V(0, ..., 0, s, 0, ...) with s at position j. +# Values are set in v from position k to k+ncomp-1, k+ncomp is returned +# +# This means that the basis has the same polynomial space in each component, +# so it is isotropic in V components +function _isotropic_set_value!(v::AbstractVector{V},s::T,k) where {V,T} + ncomp = num_indep_components(V) + z = zero(T) + @inbounds for j in 1:ncomp + v[k] = ntuple(i -> ifelse(i == j, s, z),Val(ncomp)) + k += 1 + end + k +end + +function _tensorial_gradient_nd!( + PT::Type{<:Polynomial}, + v::AbstractVector{G}, + x, + orders, + terms::AbstractVector{CartesianIndex{D}}, + c::AbstractMatrix{T}, + g::AbstractMatrix{T}, + z::AbstractVector{T}, + ::Type{V}) where {G,D,T,V} + + for d in 1:D + _derivatives_1d!(PT,orders[d],(c,g),x,d) + end + + o = one(T) + k = 1 + + for ci in terms + + s = z + for i in eachindex(s) + @inbounds s[i] = o + end + for q in 1:D + for d in 1:D + if d != q + @inbounds s[q] *= c[d,ci[d]] + else + @inbounds s[q] *= g[d,ci[d]] + end + end + end + + k = _isotropic_set_gradient!(v,s,k,V) + end +end + +function _isotropic_set_gradient!( + v::AbstractVector{G},s,k,::Type{<:Real}) where G + + @inbounds v[k] = s + k+1 +end + +@generated function _isotropic_set_gradient!( + v::AbstractVector{G},s,k,::Type{V}) where {G,V} + # Git blame me for readable non-generated version + + w = zero(V) + m = Array{String}(undef, size(G)) + N_val_dims = length(size(V)) + s_size = size(G)[1:end-N_val_dims] + + body = "T = eltype(s); z = zero(T);" + for ci in CartesianIndices(s_size) + id = join(Tuple(ci)) + body *= "@inbounds s$id = s[$ci];" + end + + for j in CartesianIndices(w) + for i in CartesianIndices(m) + m[i] = "z" + end + for ci in CartesianIndices(s_size) + id = join(Tuple(ci)) + m[ci,j] = "s$id" + end + body *= "@inbounds v[k] = ($(join(tuple(m...), ", ")));" + body *= "k = k + 1;" + end + + body = Meta.parse(string("begin ",body," end")) + return Expr(:block, body ,:(return k)) +end + +# Specialization for SymTensorValue and SymTracelessTensorValue, +# necessary as long as outer(Point, V<:AbstractSymTensorValue)::G does not +# return a tensor type G that implements the appropriate symmetries of the +# gradient (and hessian) +# +# This is still called "isotropic" as each independent SymTensor component holds +# the same (scalar multivariate) polynomial space. +@generated function _isotropic_set_gradient!( + v::AbstractVector{G},s,k,::Type{V}) where {G,V<:AbstractSymTensorValue{D}} where D + # Git blame me for readable non-generated version + + T = eltype(s) + m = Array{String}(undef, size(G)) + s_length = size(G)[1] + + is_traceless = V <: SymTracelessTensorValue + skip_last_diagval = is_traceless ? 1 : 0 # Skid V_DD if traceless + + body = "z = $(zero(T));" + for i in 1:s_length + body *= "@inbounds s$i = s[$i];" + end + + for c in 1:(D-skip_last_diagval) # Go over cols + for r in c:D # Go over lower triangle, current col + for i in eachindex(m) + m[i] = "z" + end + for i in 1:s_length # indices of the Vector s + m[i,r,c] = "s$i" + if (r!=c) + m[i,c,r] = "s$i" + elseif is_traceless # V_rr contributes negatively to V_DD (tracelessness) + m[i,D,D] = "-s$i" + end + end + body *= "@inbounds v[k] = ($(join(tuple(m...), ", ")));" + body *= "k = k + 1;" + end + end + + body = Meta.parse(string("begin ",body," end")) + return Expr(:block, body ,:(return k)) +end + +function _tensorial_hessian_nd!( + PT::Type{<:Polynomial}, + v::AbstractVector{G}, + x, + orders, + terms::AbstractVector{CartesianIndex{D}}, + c::AbstractMatrix{T}, + g::AbstractMatrix{T}, + h::AbstractMatrix{T}, + ::Type{V}) where {G,D,T,V} + + for d in 1:D + _derivatives_1d!(PT,orders[d],(c,g,h),x,d) + end + + z = zero(Mutable(TensorValue{D,D,T})) + o = one(T) + k = 1 + + for ci in terms + + s = z + for i in eachindex(s) + @inbounds s[i] = o + end + for r in 1:D + for q in 1:D + for d in 1:D + 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!(v,s,k,V) + end +end + diff --git a/src/ReferenceFEs/RaviartThomasRefFEs.jl b/src/ReferenceFEs/RaviartThomasRefFEs.jl index 40568b456..af04aa2c0 100644 --- a/src/ReferenceFEs/RaviartThomasRefFEs.jl +++ b/src/ReferenceFEs/RaviartThomasRefFEs.jl @@ -20,11 +20,11 @@ function RaviartThomasRefFE( if is_n_cube(p) prebasis = QCurlGradLegendrePolynomialBasis{D}(T,order) # Prebasis cb = QGradLegendrePolynomialBasis{D}(T,order-1) # Cell basis - fb = LegendrePolynomialBasis{D-1}(T,order,Polynomials._q_filter) # Face basis + fb = LegendreBasis{D-1}(T,order,Polynomials._q_filter) # Face basis elseif is_simplex(p) prebasis = PCurlGradMonomialBasis{D}(T,order) # Prebasis - cb = LegendrePolynomialBasis{D}(VectorValue{D,T},order-1,Polynomials._p_filter) # Cell basis - fb = LegendrePolynomialBasis{D-1}(T,order,Polynomials._p_filter) # Face basis + cb = LegendreBasis{D}(VectorValue{D,T},order-1,Polynomials._p_filter) # Cell basis + fb = LegendreBasis{D-1}(T,order,Polynomials._p_filter) # Face basis else @notimplemented "Raviart-Thomas Reference FE only available for cubes and simplices" end @@ -76,17 +76,17 @@ function get_face_own_dofs(reffe::GenericRefFE{RaviartThomas}, conf::DivConformi end # TODO: Please remove me -function LegendreBasis(::Type{T},p::Polytope,orders) where T +function legendreBasis(::Type{T},p::Polytope,orders) where T compute_legendre_basis(T,p,orders) end -function LegendreBasis(::Type{T},p::Polytope{D},order::Int) where {D,T} +function legendreBasis(::Type{T},p::Polytope{D},order::Int) where {D,T} orders = tfill(order,Val{D}()) - LegendreBasis(T,p,orders) + legendreBasis(T,p,orders) end function compute_legendre_basis(::Type{T},p::ExtrusionPolytope{D},orders) where {D,T} extrusion = Tuple(p.extrusion) terms = _monomial_terms(extrusion,orders) - LegendrePolynomialBasis{D}(T,orders,terms) + LegendreBasis{D}(T,orders,terms) end # ContraVariantPiolaMap diff --git a/src/ReferenceFEs/deprecated/RaviartThomasRefFEs.jl b/src/ReferenceFEs/deprecated/RaviartThomasRefFEs.jl index 7b46b5a5e..c61538800 100644 --- a/src/ReferenceFEs/deprecated/RaviartThomasRefFEs.jl +++ b/src/ReferenceFEs/deprecated/RaviartThomasRefFEs.jl @@ -216,7 +216,7 @@ end function compute_legendre_basis(::Type{T},p::ExtrusionPolytope{D},orders) where {D,T} extrusion = Tuple(p.extrusion) terms = _monomial_terms(extrusion,orders) - LegendrePolynomialBasis{D}(T,orders,terms) + LegendreBasis{D}(T,orders,terms) end function ChebyshevBasis(::Type{T},p::Polytope,orders) where T diff --git a/test/PolynomialsTests/LegendrePolynomialBasesTests.jl b/test/PolynomialsTests/LegendrePolynomialBasesTests.jl deleted file mode 100644 index 94d356e2a..000000000 --- a/test/PolynomialsTests/LegendrePolynomialBasesTests.jl +++ /dev/null @@ -1,63 +0,0 @@ -module LegendrePolynomialBasisTests - -using Test -using Gridap.TensorValues -using Gridap.Fields -using Gridap.Fields: Broadcasting -using Gridap.Polynomials - -# 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 = 3 -b1 = LegendrePolynomialBasis{1}(V,order) -∇b1 = Broadcasting(∇)(b1) -∇∇b1 = Broadcasting(∇)(∇b1) - -@test evaluate(b1,[x1,x2,x3,]) ≈ [ 1.0 -1.7320508075688772 2.23606797749979 -2.6457513110645907; - 1.0 0.0 -1.118033988749895 -0.0; - 1.0 1.7320508075688772 2.23606797749979 2.6457513110645907 ] -@test evaluate(∇b1,[x1,x2,x3,]) ≈ G[ (0.0,) (3.4641016151377544,) (-13.416407864998739,) (31.74901573277509,); - (0.0,) (3.4641016151377544,) (0.0,) (-7.937253933193772,); - (0.0,) (3.4641016151377544,) (13.416407864998739,) (31.74901573277509,) ] -@test evaluate(∇∇b1,[x1,x2,x3,]) ≈ H[ (0.0,) (0.0,) (13.416407864998739,) (-79.37253933193772,); - (0.0,) (0.0,) (13.416407864998739,) (0.0,); - (0.0,) (0.0,) (13.416407864998739,) (79.37253933193772,) ] - -x1 = Point(0.0,0.0) -x2 = Point(0.5,0.5) -x3 = Point(1.0,1.0) -b2 = LegendrePolynomialBasis{2}(V,order) -∇b2 = Broadcasting(∇)(b2) -∇∇b2 = Broadcasting(∇)(∇b2) - -G = gradient_type(V,x1) -H = gradient_type(G,x1) - -@test evaluate(b2,[x1,x2,x3,]) ≈ [ 1.0 -1.7320508075688772 2.23606797749979 -2.6457513110645907 #= - =# -1.7320508075688772 2.9999999999999996 -3.872983346207417 #= - =# 4.58257569495584 2.23606797749979 -3.872983346207417 #= - =# 5.000000000000001 -5.916079783099617 -2.6457513110645907 #= - =# 4.58257569495584 -5.916079783099617 7.000000000000001; - 1.0 0.0 -1.118033988749895 -0.0 0.0 0.0 -0.0 -0.0 #= - =# -1.118033988749895 -0.0 1.2500000000000002 0.0 -0.0 -0.0 0.0 0.0; - 1.0 1.7320508075688772 2.23606797749979 2.6457513110645907 #= - =# 1.7320508075688772 2.9999999999999996 3.872983346207417 #= - =# 4.58257569495584 2.23606797749979 3.872983346207417 #= - =# 5.000000000000001 5.916079783099617 2.6457513110645907 #= - =# 4.58257569495584 5.916079783099617 7.000000000000001 ] -@test evaluate(∇b2,[x1,x2,x3,])[:,10] ≈ G[ (7.745966692414834, 23.2379000772445); - (-3.872983346207417, 0.0); - (7.745966692414834, 23.2379000772445) ] -@test evaluate(∇∇b2,[x1,x2,x3,])[:,10] ≈ H[ (0.0, -46.475800154489, -46.475800154489, -23.2379000772445); - (-0.0, 0.0, 0.0, 0.0); - (0.0, 46.475800154489, 46.475800154489, 23.2379000772445) ] - -end # module diff --git a/test/PolynomialsTests/runtests.jl b/test/PolynomialsTests/runtests.jl index c28a2b0cf..f4af92f3b 100644 --- a/test/PolynomialsTests/runtests.jl +++ b/test/PolynomialsTests/runtests.jl @@ -12,7 +12,7 @@ using Test @testset "ModalC0Bases" begin include("ModalC0BasesTests.jl") end -@testset "LegendrePolynomialBases" begin include("LegendrePolynomialBasesTests.jl") end +@testset "LegendreBases" begin include("LegendreBasesTests.jl") end #@testset "ChangeBasis" begin include("ChangeBasisTests.jl") end From 6aa5a25231ad7d4088a662d157c45039432c91f1 Mon Sep 17 00:00:00 2001 From: Antoine Marteau Date: Thu, 12 Dec 2024 17:21:04 +1100 Subject: [PATCH 043/105] WIP BernsteinBases 1D / TensorPolynomialBasis to be tested --- src/Polynomials/BernsteinBases.jl | 120 ++++++++++++++++++++++++++++-- 1 file changed, 115 insertions(+), 5 deletions(-) diff --git a/src/Polynomials/BernsteinBases.jl b/src/Polynomials/BernsteinBases.jl index 3b4b0f906..61b5a1f41 100644 --- a/src/Polynomials/BernsteinBases.jl +++ b/src/Polynomials/BernsteinBases.jl @@ -1,9 +1,119 @@ """ - BernsteinPType{K} <: PolynomialType{K} + Bernstein <: Polynomial -Type representing Bernstein polynomials of maximum order `K` +Type representing Bernstein polynomials """ -struct BernsteinPType{K} <: PolynomialType{K} end -isHierarchical(::BernsteinPType) = false +struct Bernstein <: Polynomial end -struct Bernstein <: PolynomialField end +isHierarchical(::Bernstein) = false + +""" + BernsteinBasis{D,T,K} = TensorPolynomialBasis{D,T,K,Bernstein} + +Multivariate scalar' or `Multivalue`'d monomial basis, see [`TensorPolynomialBasis`](@ref) +""" +const BernsteinBasis{D,T,K} = TensorPolynomialBasis{D,T,K,Bernstein} + +""" + BernsteinBasis{D}(::Type{T}, order::Int, terms::Vector) where {D,T} + BernsteinBasis{D}(::Type{T}, orders::Tuple [, filter::Function]) where {D,T} + BernsteinBasis{D}(::Type{T}, order::Int [, filter::Function]) where {D,T} + +Convenience constructors of BernsteinBasis{D,T}. +""" +BernsteinBasis{D}(args...) where {D} = TensorPolynomialBasis{D}(Bernstein, args...) + +# 1D evaluation implementation + +# TODO Optimize with in-place De Casteljau + +binoms(::Val{K}) where K = ntuple( i -> binomial(K,i-1), K+1) + +# jth Bernstein poly of order k at x: +# Bᵏⱼ(x) = binom(k,j) * x^j * (1-x)^(k-j) +function _evaluate_1d!(::Type{Bernstein}, k, v::AbstractMatrix{T},x,d) where T<:Number + b = binoms(Val(k)) + n = k + 1 + @inbounds λ1 = x[d] + λ2 = one(T) - λ1 + for i in 1:n + j = i-1 + λ1_j = λ1^(j) + λ2_j = λ2^(k-j) + @inbounds v[d,i] = b[j] * λ1_j * λ2_j # order k + end +end + +# First derivative of the jth Bernstein poly of order k at x: +# (Bᵏⱼ)'(x) = k * ( Bᵏ⁻¹ⱼ₋₁(x) - Bᵏ⁻¹ⱼ(x) ) +# = k * x^(j-1) * (1-x)^(k-j-1) * ((1-x)*binom(k-1,j-1) - x*binom(k-1,j)) +function _gradient_1d!(::Type{Bernstein}, k, g::AbstractMatrix{T},x,d) where T<:Number + if iszero(k) + @inbounds g[d,1] = zero(T) + return + end + + o = one(T) + if isone(k) + @inbounds g[d,1] = o + @inbounds g[d,2] = -o + return + end + + b = binoms(Val(k-1)) + n = k + 1 + @inbounds λ1 = x[d] + λ2 = o - λ1 + + @inbounds g[1] = k * λ2^(k-1) + @inbounds g[n] = k * λ1^(k-1) + for i in 2:n-1 + j = i-1 + λ1_j = λ1^(j-1) + λ2_j = λ2^(k-j-1) + @inbounds g[d,i] = k * λ1_j * λ2_j *(λ2*b[j-1] - λ1*b[j]) # order k-1 + end +end + +# Second derivative of the jth Bernstein poly of order k at x: +# (Bᵏⱼ)''(x) = k(k-1) * ( Bᵏ⁻²ⱼ₋₂(x) -2*Bᵏ⁻²ⱼ₋₁(x) + Bᵏ⁻²ⱼ(x) ) +# = k(k-1) * x^(j-2) * (1-x)^(k-j-2) * ( (1-x)^2*binom(k-2,j-2) +# - 2x*(1-x)*binom(k-2,j-1) + (x)^2*binom(k-2,j) +# ) +function _hessian_1d!(::Type{Bernstein}, k, h::AbstractMatrix{T},x,d) where T<:Number + z = zero(T) + if iszero(k) + @inbounds h[d,1] = z + return + end + if isone(k) + @inbounds h[d,1] = z + @inbounds h[d,2] = -z + return + end + + o = one(T) + if k == 2 + @inbounds h[d,1] = 2o + @inbounds h[d,2] = -4o + @inbounds h[d,3] = 2o + return + end + + b = binoms(Val(k-2)) + n = k + 1 + C = k*(k-1) + @inbounds λ1 = x[d] + λ2 = o - λ1 + + @inbounds h[1] = C * λ2^(k-2) + @inbounds h[2] = C * (-2λ2^(k-2) + (k-2)*λ2^(k-3)*λ1) + @inbounds h[n-1]=C * (-2λ1^(k-2) + (k-2)*λ1^(k-3)*λ2) + @inbounds h[n] = C * λ1^(k-2) + for i in 3:n-2 + j = i-1 + λ1_j = λ1^(j-2) + λ2_j = λ2^(k-j-2) + @inbounds h[d,i] = C * λ1_j * λ2_j *(λ2*λ2*b[j-2] -2λ2*λ1*b[j-1] + λ1*λ1*b[j]) # order k-2 + end +end From 2a4ff4ecb7a7e6733b273f4c8adf385f9d0528c1 Mon Sep 17 00:00:00 2001 From: JordiManyer Date: Thu, 12 Dec 2024 17:36:28 +1100 Subject: [PATCH 044/105] Save changes --- src/ReferenceFEs/Pullbacks.jl | 64 ++++++++++++++++++++++++----------- 1 file changed, 44 insertions(+), 20 deletions(-) diff --git a/src/ReferenceFEs/Pullbacks.jl b/src/ReferenceFEs/Pullbacks.jl index dc0a9d45f..9c6477238 100644 --- a/src/ReferenceFEs/Pullbacks.jl +++ b/src/ReferenceFEs/Pullbacks.jl @@ -10,11 +10,23 @@ where """ abstract type Pushforward <: Map end +function Arrays.lazy_map( + k::Pushforward, ref_cell_fields, pf_args... +) + lazy_map(Broadcasting(Operation(k)), ref_cell_fields, pf_args...) +end + +function Arrays.evaluate!( + cache, k::Pushforward, v_ref::Number, args... +) + @abstractmethod +end + function Arrays.lazy_map( ::Broadcasting{typeof(gradient)}, a::LazyArray{<:Fill{Broadcasting{Operation{<:Pushforward}}}} ) - cell_ref_basis, args = a.args - cell_ref_gradient = lazy_map(Broadcasting(∇),cell_ref_basis) + cell_ref_fields, args = a.args + cell_ref_gradient = lazy_map(Broadcasting(∇),cell_ref_fields) return lazy_map(a.maps.value,cell_ref_gradient,args...) end @@ -40,6 +52,28 @@ end Arrays.inverse_map(pf::Pushforward) = InversePushforward(pf) Arrays.inverse_map(ipf::InversePushforward) = ipf.pushforward +function Arrays.lazy_map( + k::InversePushforward, phys_cell_fields, pf_args... +) + lazy_map(Broadcasting(Operation(k)), phys_cell_fields, pf_args...) +end + +function Arrays.return_cache( + k::InversePushforward, v_phys::Number, args... +) + v_ref_basis = mock_basis(v_phys) + pf_cache = return_cache(k.pushforward,v_ref_basis,args...) + return v_ref_basis, pf_cache +end + +function Arrays.evaluate!( + cache, k::InversePushforward, v_phys::Number, args... +) + v_ref_basis, pf_cache = cache + change = evaluate!(pf_cache,k.pushforward,v_ref_basis,args...) + return inv(change)⋅v_phys +end + # Pushforward """ @@ -62,12 +96,12 @@ struct Pullback{PF} <: Map end function Arrays.lazy_map( - ::typeof{evaluate},k::LazyArray{<:Fill{<:Pushforward}},ref_cell_basis + ::typeof{evaluate},k::LazyArray{<:Fill{<:Pullback}},ref_cell_fields ) pb = k.maps.value - phys_cell_dofs, cell_map, pf_args = k.args - phys_cell_basis = lazy_map(pb.pushforward,ref_cell_basis,cell_map,pf_args...) - return lazy_map(evaluate,phys_cell_dofs,phys_cell_basis) + phys_cell_dofs, pf_args = k.args + phys_cell_fields = lazy_map(pb.pushforward,ref_cell_fields,pf_args...) + return lazy_map(evaluate,phys_cell_dofs,phys_cell_fields) end # InversePullback @@ -95,28 +129,18 @@ Arrays.inverse_map(pb::Pullback) = InversePullback(pb.pushforward) Arrays.inverse_map(ipb::InversePullback) = Pullback(ipb.pushforward) function Arrays.lazy_map( - ::typeof{evaluate},k::LazyArray{<:Fill{<:InversePullback}},phys_cell_basis + ::typeof{evaluate},k::LazyArray{<:Fill{<:InversePullback}},phys_cell_fields ) pb = inverse_map(k.maps.value) - ref_cell_dofs, cell_map, pf_args = k.args - ref_cell_basis = lazy_map(inverse_map(pb.pushforward),phys_cell_basis,cell_map,pf_args...) - return lazy_map(evaluate,ref_cell_dofs,ref_cell_basis) + ref_cell_dofs, pf_args = k.args + ref_cell_fields = lazy_map(inverse_map(pb.pushforward),phys_cell_fields,pf_args...) + return lazy_map(evaluate,ref_cell_dofs,ref_cell_fields) end # ContraVariantPiolaMap struct ContraVariantPiolaMap <: Pushforward end -function lazy_map( - k::ContraVariantPiolaMap, - cell_ref_shapefuns::AbstractArray{<:AbstractArray{<:Field}}, - cell_map::AbstractArray{<:Field}, - sign_flip::AbstractArray{<:AbstractArray{<:Field}} -) - cell_Jt = lazy_map(∇,cell_map) - lazy_map(Broadcasting(Operation(k)),cell_ref_shapefuns,cell_Jt,sign_flip) -end - function evaluate!( cache,::ContraVariantPiolaMap, v::Number, From 711722f92aaca39f0729d80f560e15720592e3e1 Mon Sep 17 00:00:00 2001 From: JordiManyer Date: Thu, 12 Dec 2024 19:47:01 +1100 Subject: [PATCH 045/105] Ported Piola maps to the Pullback structure --- src/FESpaces/CurlConformingFESpaces.jl | 4 +- src/FESpaces/DivConformingFESpaces.jl | 3 +- src/ReferenceFEs/MomentBasedReferenceFEs.jl | 2 +- src/ReferenceFEs/NedelecRefFEs.jl | 40 ------------- src/ReferenceFEs/Pullbacks.jl | 63 +++++++++++++++++---- src/ReferenceFEs/RaviartThomasRefFEs.jl | 56 ------------------ src/ReferenceFEs/ReferenceFEs.jl | 2 + test/pullbacks.jl | 43 ++++++++++++++ 8 files changed, 101 insertions(+), 112 deletions(-) create mode 100644 test/pullbacks.jl diff --git a/src/FESpaces/CurlConformingFESpaces.jl b/src/FESpaces/CurlConformingFESpaces.jl index 0bf9e45cc..55b3d484a 100644 --- a/src/FESpaces/CurlConformingFESpaces.jl +++ b/src/FESpaces/CurlConformingFESpaces.jl @@ -45,7 +45,7 @@ function get_cell_shapefuns( cell_reffe_shapefuns = lazy_map(get_shapefuns,cell_reffe) cell_map = get_cell_map(Triangulation(model)) + cell_Jt = lazy_map(Broadcasting(∇),cell_map) k = ReferenceFEs.CoVariantPiolaMap() - lazy_map(k,cell_reffe_shapefuns,cell_map) + lazy_map(k,cell_reffe_shapefuns,cell_Jt) end - diff --git a/src/FESpaces/DivConformingFESpaces.jl b/src/FESpaces/DivConformingFESpaces.jl index db08c8ba2..e72724247 100644 --- a/src/FESpaces/DivConformingFESpaces.jl +++ b/src/FESpaces/DivConformingFESpaces.jl @@ -47,9 +47,10 @@ function get_cell_shapefuns( sign_flip = get_sign_flip(model, cell_reffe) ) cell_map = get_cell_map(get_grid(model)) + cell_Jt = lazy_map(Broadcasting(∇),cell_map) cell_shapefuns = lazy_map(get_shapefuns,cell_reffe) k = ContraVariantPiolaMap() - lazy_map(k,cell_shapefuns,cell_map,lazy_map(Broadcasting(constant_field),sign_flip)) + lazy_map(k,cell_shapefuns,cell_Jt,lazy_map(Broadcasting(constant_field),sign_flip)) end struct SignFlipMap{T} <: Map diff --git a/src/ReferenceFEs/MomentBasedReferenceFEs.jl b/src/ReferenceFEs/MomentBasedReferenceFEs.jl index 27e551928..72abb3af9 100644 --- a/src/ReferenceFEs/MomentBasedReferenceFEs.jl +++ b/src/ReferenceFEs/MomentBasedReferenceFEs.jl @@ -36,7 +36,7 @@ struct MomentBasedDofBasis{P,V} <: AbstractVector{Moment} end Base.size(a::MomentBasedDofBasis) = (num_dofs(a),) -Base.axes(a::MomentBasedDofBasis) = (Base.OneTo(num_dofs),) +Base.axes(a::MomentBasedDofBasis) = (Base.OneTo(num_dofs(a)),) Base.getindex(a::MomentBasedDofBasis,i::Integer) = Moment() Base.IndexStyle(::MomentBasedDofBasis) = IndexLinear() diff --git a/src/ReferenceFEs/NedelecRefFEs.jl b/src/ReferenceFEs/NedelecRefFEs.jl index 3faa07361..985984b92 100644 --- a/src/ReferenceFEs/NedelecRefFEs.jl +++ b/src/ReferenceFEs/NedelecRefFEs.jl @@ -115,43 +115,3 @@ function get_face_dofs(reffe::GenericRefFE{Nedelec,Dc}) where Dc end face_dofs 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) - v⋅transpose(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 diff --git a/src/ReferenceFEs/Pullbacks.jl b/src/ReferenceFEs/Pullbacks.jl index 9c6477238..d339d446b 100644 --- a/src/ReferenceFEs/Pullbacks.jl +++ b/src/ReferenceFEs/Pullbacks.jl @@ -11,7 +11,7 @@ where abstract type Pushforward <: Map end function Arrays.lazy_map( - k::Pushforward, ref_cell_fields, pf_args... + k::Pushforward, ref_cell_fields::AbstractArray, pf_args::AbstractArray... ) lazy_map(Broadcasting(Operation(k)), ref_cell_fields, pf_args...) end @@ -22,6 +22,12 @@ function Arrays.evaluate!( @abstractmethod end +function evaluate!( + cache, k::Pushforward, f_ref::AbstractVector{<:Field}, args... +) + Broadcasting(Operation(k))(f_ref,args...) +end + function Arrays.lazy_map( ::Broadcasting{typeof(gradient)}, a::LazyArray{<:Fill{Broadcasting{Operation{<:Pushforward}}}} ) @@ -30,6 +36,17 @@ function Arrays.lazy_map( return lazy_map(a.maps.value,cell_ref_gradient,args...) end +function Arrays.evaluate!( + cache, + ::Broadcasting{typeof(∇)}, + a::Fields.BroadcastOpFieldArray{<:Pushforward} +) + v, Jt, sign_flip = a.args + ∇v = Broadcasting(∇)(v) + k = ContraVariantPiolaMap() + Broadcasting(Operation(k))(∇v,Jt,sign_flip) +end + # InversePushforward """ @@ -53,7 +70,7 @@ Arrays.inverse_map(pf::Pushforward) = InversePushforward(pf) Arrays.inverse_map(ipf::InversePushforward) = ipf.pushforward function Arrays.lazy_map( - k::InversePushforward, phys_cell_fields, pf_args... + k::InversePushforward, phys_cell_fields::AbstractArray, pf_args::AbstractArray... ) lazy_map(Broadcasting(Operation(k)), phys_cell_fields, pf_args...) end @@ -61,6 +78,7 @@ end function Arrays.return_cache( k::InversePushforward, v_phys::Number, args... ) + mock_basis(::VectorValue{D,T}) where {D,T} = one(TensorValue{D,D,T}) v_ref_basis = mock_basis(v_phys) pf_cache = return_cache(k.pushforward,v_ref_basis,args...) return v_ref_basis, pf_cache @@ -71,10 +89,16 @@ function Arrays.evaluate!( ) v_ref_basis, pf_cache = cache change = evaluate!(pf_cache,k.pushforward,v_ref_basis,args...) - return inv(change)⋅v_phys + return v_phys⋅inv(change) +end + +function evaluate!( + cache, k::InversePushforward, f_phys::AbstractVector{<:Field}, args... +) + Broadcasting(Operation(k))(f_phys,args...) end -# Pushforward +# Pullback """ struct Pullback{PF <: Pushforward} <: Map end @@ -96,7 +120,7 @@ struct Pullback{PF} <: Map end function Arrays.lazy_map( - ::typeof{evaluate},k::LazyArray{<:Fill{<:Pullback}},ref_cell_fields + ::typeof(evaluate),k::LazyArray{<:Fill{<:Pullback}},ref_cell_fields::AbstractArray ) pb = k.maps.value phys_cell_dofs, pf_args = k.args @@ -104,6 +128,12 @@ function Arrays.lazy_map( return lazy_map(evaluate,phys_cell_dofs,phys_cell_fields) end +function evaluate!( + cache, k::PullBack, σ_phys::MomentBasedDofBasis, args... +) + Broadcasting(Operation(k))(f_phys,args...) +end + # InversePullback """ @@ -129,7 +159,7 @@ Arrays.inverse_map(pb::Pullback) = InversePullback(pb.pushforward) Arrays.inverse_map(ipb::InversePullback) = Pullback(ipb.pushforward) function Arrays.lazy_map( - ::typeof{evaluate},k::LazyArray{<:Fill{<:InversePullback}},phys_cell_fields + ::typeof(evaluate),k::LazyArray{<:Fill{<:InversePullback}},phys_cell_fields::AbstractArray ) pb = inverse_map(k.maps.value) ref_cell_dofs, pf_args = k.args @@ -142,11 +172,20 @@ end struct ContraVariantPiolaMap <: Pushforward end function evaluate!( - cache,::ContraVariantPiolaMap, - v::Number, - Jt::Number, - sign_flip::Bool + cache, ::ContraVariantPiolaMap, v_ref::Number, Jt::Number, sign_flip::Bool +) + sign = (-1)^sign_flip + idetJ = 1. / meas(Jt) + return (sign*v_ref)⋅(idetJ*Jt) +end + +# CoVariantPiolaMap + +struct CoVariantPiolaMap <: Pushforward end + +function evaluate!( + cache, ::CoVariantPiolaMap, v_ref::Number, Jt::Number ) - idetJ = 1/meas(Jt) - ((-1)^sign_flip*v)⋅(idetJ*Jt) + # we right-multiply to compute the gradient correctly + return v_ref⋅transpose(inv(Jt)) end diff --git a/src/ReferenceFEs/RaviartThomasRefFEs.jl b/src/ReferenceFEs/RaviartThomasRefFEs.jl index 29844461b..b507293b8 100644 --- a/src/ReferenceFEs/RaviartThomasRefFEs.jl +++ b/src/ReferenceFEs/RaviartThomasRefFEs.jl @@ -88,59 +88,3 @@ function compute_jacobi_basis(::Type{T},p::ExtrusionPolytope{D},orders) where {D terms = _monomial_terms(extrusion,orders) JacobiPolynomialBasis{D}(T,orders,terms) end - -# ContraVariantPiolaMap - -struct ContraVariantPiolaMap <: Map end - -function evaluate!( - cache, - ::Broadcasting{typeof(∇)}, - a::Fields.BroadcastOpFieldArray{ContraVariantPiolaMap} -) - v, Jt, sign_flip = a.args - ∇v = Broadcasting(∇)(v) - k = ContraVariantPiolaMap() - Broadcasting(Operation(k))(∇v,Jt,sign_flip) -end - -function lazy_map( - ::Broadcasting{typeof(gradient)}, - a::LazyArray{<:Fill{Broadcasting{Operation{ContraVariantPiolaMap}}}} -) - v, Jt, sign_flip = a.args - ∇v = lazy_map(Broadcasting(∇),v) - k = ContraVariantPiolaMap() - lazy_map(Broadcasting(Operation(k)),∇v,Jt,sign_flip) -end - -function lazy_map( - k::ContraVariantPiolaMap, - cell_ref_shapefuns::AbstractArray{<:AbstractArray{<:Field}}, - cell_map::AbstractArray{<:Field}, - sign_flip::AbstractArray{<:AbstractArray{<:Field}} -) - cell_Jt = lazy_map(∇,cell_map) - lazy_map(Broadcasting(Operation(k)),cell_ref_shapefuns,cell_Jt,sign_flip) -end - -function evaluate!( - cache,::ContraVariantPiolaMap, - v::Number, - Jt::Number, - sign_flip::Bool -) - idetJ = 1/meas(Jt) - ((-1)^sign_flip*v)⋅(idetJ*Jt) -end - -function evaluate!( - cache, - k::ContraVariantPiolaMap, - v::AbstractVector{<:Field}, - phi::Field, - sign_flip::AbstractVector{<:Field} -) - Jt = ∇(phi) - Broadcasting(Operation(k))(v,Jt,sign_flip) -end diff --git a/src/ReferenceFEs/ReferenceFEs.jl b/src/ReferenceFEs/ReferenceFEs.jl index abfc4766d..513ce5ada 100644 --- a/src/ReferenceFEs/ReferenceFEs.jl +++ b/src/ReferenceFEs/ReferenceFEs.jl @@ -248,6 +248,8 @@ include("StrangQuadratures.jl") include("XiaoGimbutasQuadratures.jl") +include("Pullbacks.jl") + include("MomentBasedReferenceFEs.jl") include("RaviartThomasRefFEs.jl") diff --git a/test/pullbacks.jl b/test/pullbacks.jl new file mode 100644 index 000000000..a59f851b3 --- /dev/null +++ b/test/pullbacks.jl @@ -0,0 +1,43 @@ + +using FillArrays +using Gridap +using Gridap.ReferenceFEs, Gridap.FESpaces + +using Gridap.ReferenceFEs: Pullback + +model = CartesianDiscreteModel((0,2,0,4),(2,2)) +Ω = Triangulation(model) +dΩ = Measure(Ω,2) +pts = CellData.get_data(get_cell_points(dΩ)) + +reffe = RaviartThomasRefFE(Float64,QUAD,1) + +V = FESpace(model,reffe) + +u(x) = VectorValue(x[1], -x[2]) +uh = interpolate(u,V) + +φ_phys = get_fe_basis(V).cell_basis +φ_ref = φ_phys.args[1] +Jt, sign = φ_phys.args[2:end] + +σ_phys = get_fe_dof_basis(V).cell_dof +σ_ref = Fill(get_dof_basis(reffe),num_cells(model)) + +App = lazy_map(evaluate,σ_phys,φ_phys)[1] +Arr = lazy_map(evaluate,σ_ref,φ_ref)[1] + +pf = ContraVariantPiolaMap() + +ipf = inverse_map(pf) +f_ref = lazy_map(ipf,φ_phys,Jt,sign) +Brr = lazy_map(evaluate,σ_ref,f_ref)[1] +Brr == Arr +φ_ref_x = lazy_map(evaluate,φ_ref,pts)[1] +f_ref_x = lazy_map(evaluate,f_ref,pts)[1] +f_ref_x == φ_ref_x + +pb = Pullback(pf) +θ_ref = lazy_map(pb,σ_phys,Jt,sign) + +ipb = inverse_map(pb) From b5ab282aa5c962b2b9dd322730168ad157933919 Mon Sep 17 00:00:00 2001 From: JordiManyer Date: Thu, 12 Dec 2024 23:35:43 +1100 Subject: [PATCH 046/105] Pullback API working --- src/ReferenceFEs/Pullbacks.jl | 20 ++++++++++++++------ test/pullbacks.jl | 15 +++++++++++++-- 2 files changed, 27 insertions(+), 8 deletions(-) diff --git a/src/ReferenceFEs/Pullbacks.jl b/src/ReferenceFEs/Pullbacks.jl index d339d446b..f9f8ad81f 100644 --- a/src/ReferenceFEs/Pullbacks.jl +++ b/src/ReferenceFEs/Pullbacks.jl @@ -31,7 +31,7 @@ end function Arrays.lazy_map( ::Broadcasting{typeof(gradient)}, a::LazyArray{<:Fill{Broadcasting{Operation{<:Pushforward}}}} ) - cell_ref_fields, args = a.args + cell_ref_fields, args... = a.args cell_ref_gradient = lazy_map(Broadcasting(∇),cell_ref_fields) return lazy_map(a.maps.value,cell_ref_gradient,args...) end @@ -123,15 +123,16 @@ function Arrays.lazy_map( ::typeof(evaluate),k::LazyArray{<:Fill{<:Pullback}},ref_cell_fields::AbstractArray ) pb = k.maps.value - phys_cell_dofs, pf_args = k.args + phys_cell_dofs, pf_args... = k.args phys_cell_fields = lazy_map(pb.pushforward,ref_cell_fields,pf_args...) return lazy_map(evaluate,phys_cell_dofs,phys_cell_fields) end function evaluate!( - cache, k::PullBack, σ_phys::MomentBasedDofBasis, args... + cache, k::Pullback, σ_phys::AbstractVector{<:Dof}, args... ) - Broadcasting(Operation(k))(f_phys,args...) + pf(f_ref) = evaluate(k.pushforward,f_ref,args...) # TODO: Can we avoid this? + return Arrays.OperationMap(σ_phys,pf) end # InversePullback @@ -145,7 +146,7 @@ where - V̂* is a dof space on the reference cell K̂ and - V* is a dof space on the physical cell K. Its action on reference dofs ̂σ : V̂ -> R is defined in terms of the pushforward map F* as - σ = F**(̂σ) := ̂σ∘(F*)^-1 : V -> R + σ = (F**)^-1(̂σ) := ̂σ∘(F*)^-1 : V -> R """ struct InversePullback{PF} <: Map pushforward::PF @@ -162,11 +163,18 @@ function Arrays.lazy_map( ::typeof(evaluate),k::LazyArray{<:Fill{<:InversePullback}},phys_cell_fields::AbstractArray ) pb = inverse_map(k.maps.value) - ref_cell_dofs, pf_args = k.args + ref_cell_dofs, pf_args... = k.args ref_cell_fields = lazy_map(inverse_map(pb.pushforward),phys_cell_fields,pf_args...) return lazy_map(evaluate,ref_cell_dofs,ref_cell_fields) end +function evaluate!( + cache, k::InversePullback, σ_ref::AbstractVector{<:Dof}, args... +) + ipf(f_phys) = evaluate(inverse_map(k.pushforward),f_phys,args...) # TODO: Can we avoid this? + return Arrays.OperationMap(σ_ref,ipf) +end + # ContraVariantPiolaMap struct ContraVariantPiolaMap <: Pushforward end diff --git a/test/pullbacks.jl b/test/pullbacks.jl index a59f851b3..ab9573288 100644 --- a/test/pullbacks.jl +++ b/test/pullbacks.jl @@ -1,7 +1,8 @@ using FillArrays using Gridap -using Gridap.ReferenceFEs, Gridap.FESpaces +using Gridap.ReferenceFEs, Gridap.FESpaces, Gridap.CellData +using Gridap.Fields using Gridap.ReferenceFEs: Pullback @@ -29,15 +30,25 @@ Arr = lazy_map(evaluate,σ_ref,φ_ref)[1] pf = ContraVariantPiolaMap() +# Inverse Pushforward ipf = inverse_map(pf) f_ref = lazy_map(ipf,φ_phys,Jt,sign) Brr = lazy_map(evaluate,σ_ref,f_ref)[1] Brr == Arr φ_ref_x = lazy_map(evaluate,φ_ref,pts)[1] f_ref_x = lazy_map(evaluate,f_ref,pts)[1] -f_ref_x == φ_ref_x +f_ref_x ≈ φ_ref_x +# Pullback pb = Pullback(pf) θ_ref = lazy_map(pb,σ_phys,Jt,sign) +θ_ref_x = lazy_map(evaluate,θ_ref,φ_ref) +σ_ref_x = lazy_map(evaluate,σ_ref,φ_ref) +θ_ref_x ≈ σ_ref_x +# Inverse Pullback ipb = inverse_map(pb) +θ_phys = lazy_map(ipb,σ_ref,Jt,sign) +θ_phys_x = lazy_map(evaluate,θ_phys,φ_phys) +σ_phys_x = lazy_map(evaluate,σ_phys,φ_phys) +θ_phys_x ≈ σ_phys_x From ea4e350a99fbadd57f4869508ec31cbb90b1f11b Mon Sep 17 00:00:00 2001 From: JordiManyer Date: Fri, 13 Dec 2024 09:29:17 +1100 Subject: [PATCH 047/105] Added MappedDofBasis --- src/ReferenceFEs/Pullbacks.jl | 98 +++++++++++++++++++++++++++++++++-- 1 file changed, 93 insertions(+), 5 deletions(-) diff --git a/src/ReferenceFEs/Pullbacks.jl b/src/ReferenceFEs/Pullbacks.jl index f9f8ad81f..5e0a47a43 100644 --- a/src/ReferenceFEs/Pullbacks.jl +++ b/src/ReferenceFEs/Pullbacks.jl @@ -57,6 +57,18 @@ Represents the inverse of a pushforward map F*, defined as where - V̂ is a function space on the reference cell K̂ and - V is a function space on the physical cell K. + +# Note: + +Given a pushforward F*, we provide a default implementation for the inverse (F*)^-1 that +is not optimal (and I think wrong for nonlinear geometries???). + +For better performance, the user should overload the following methods for each +specific pushforward type: + +- `return_cache(k::InversePushforward{PF}, v_phys::Number, args...)` +- `evaluate!(cache, k::InversePushforward{PF}, v_phys::Number, args...)` + """ struct InversePushforward{PF} <: Map pushforward::PF @@ -98,6 +110,58 @@ function evaluate!( Broadcasting(Operation(k))(f_phys,args...) end +# MappedDofBasis + +""" + struct MappedDofBasis{T<:Dof,MT,BT} <: AbstractVector{T} + F :: MT + σ :: BT + args + end + +Represents η = σ∘F, evaluated as η(φ) = σ(F(φ,args...)) + + - σ : V* -> R is a dof basis + - F : W -> V is a map between function spaces + +Intended combinations would be: + +- σ : V* -> R dof basis in the physical domain and F* : V̂ -> V is a pushforward map. +- ̂σ : V̂* -> R dof basis in the reference domain and (F*)^-1 : V -> V̂ is an inverse pushforward map. + +""" +struct MappedDofBasis{T<:Dof,MT,BT,A} <: AbstractVector{T} + F :: MT + dofs :: BT + args :: A + + function MappedDofBasis(F::Map, dofs::AbstractVector{<:Dof}, args...) + T = eltype(dofs) + MT = typeof(F) + BT = typeof(dofs) + A = typeof(args) + new{T,MT,BT,A}(F,dofs,args) + end +end + +Base.size(b::MappedDofBasis) = size(b.dofs) + +# Technically wrong, but the individual dofs are fake anyway +Base.getindex(b::MappedDofBasis, i) = getindex(b.dofs,i) + +function Arrays.return_cache(b::MappedDofBasis, fields) + f_cache = return_cache(b.F,fields,b.args...) + ffields = evaluate!(f_cache,b.F,fields,b.args...) + dofs_cache = return_cache(b.dofs,ffields) + return f_cache, dofs_cache +end + +function Arrays.evaluate!(cache, b::MappedDofBasis, fields) + f_cache, dofs_cache = cache + ffields = evaluate!(f_cache,b.F,fields,b.args...) + evaluate!(dofs_cache,b.dofs,ffields) +end + # Pullback """ @@ -131,8 +195,7 @@ end function evaluate!( cache, k::Pullback, σ_phys::AbstractVector{<:Dof}, args... ) - pf(f_ref) = evaluate(k.pushforward,f_ref,args...) # TODO: Can we avoid this? - return Arrays.OperationMap(σ_phys,pf) + return MappedDofBasis(k.pushforward,σ_phys,args...) end # InversePullback @@ -171,8 +234,7 @@ end function evaluate!( cache, k::InversePullback, σ_ref::AbstractVector{<:Dof}, args... ) - ipf(f_phys) = evaluate(inverse_map(k.pushforward),f_phys,args...) # TODO: Can we avoid this? - return Arrays.OperationMap(σ_ref,ipf) + return MappedDofBasis(inverse_map(k.pushforward),σ_ref,args...) end # ContraVariantPiolaMap @@ -184,7 +246,21 @@ function evaluate!( ) sign = (-1)^sign_flip idetJ = 1. / meas(Jt) - return (sign*v_ref)⋅(idetJ*Jt) + return sign * idetJ * (v_ref⋅Jt) +end + +function return_cache( + ::InversePushforward{ContraVariantPiolaMap}, v_phys::Number, Jt::Number, sign_flip::Bool +) + nothing +end + +function evaluate!( + cache, ::InversePushforward{ContraVariantPiolaMap}, v_phys::Number, Jt::Number, sign_flip::Bool +) + sign = (-1)^sign_flip + detJ = meas(Jt) + return sign * detJ * (inv(Jt)⋅v_phys) end # CoVariantPiolaMap @@ -197,3 +273,15 @@ function evaluate!( # we right-multiply to compute the gradient correctly return v_ref⋅transpose(inv(Jt)) end + +function return_cache( + ::InversePushforward{CoVariantPiolaMap}, v_phys::Number, Jt::Number +) + return nothing +end + +function evaluate!( + cache, ::InversePushforward{CoVariantPiolaMap}, v_phys::Number, Jt::Number +) + return transpose(Jt)⋅v_phys +end From 74cfd2911133b042c56d57c4e0d88d7b5b6fa1c6 Mon Sep 17 00:00:00 2001 From: JordiManyer Date: Fri, 13 Dec 2024 12:26:47 +1100 Subject: [PATCH 048/105] Deprecated old machinery in favor of Pushforwards --- src/FESpaces/FESpaces.jl | 6 +- src/FESpaces/Pullbacks.jl | 119 ++++++++++++++++++ .../CurlConformingFESpaces.jl | 0 .../{ => deprecated}/DivConformingFESpaces.jl | 0 src/ReferenceFEs/AWRefFEs.jl | 6 +- src/ReferenceFEs/BDMRefFEs.jl | 4 +- src/ReferenceFEs/MTWRefFEs.jl | 6 +- src/ReferenceFEs/NedelecRefFEs.jl | 4 +- src/ReferenceFEs/Pullbacks.jl | 27 ++++ src/ReferenceFEs/RaviartThomasRefFEs.jl | 5 +- 10 files changed, 164 insertions(+), 13 deletions(-) create mode 100644 src/FESpaces/Pullbacks.jl rename src/FESpaces/{ => deprecated}/CurlConformingFESpaces.jl (100%) rename src/FESpaces/{ => deprecated}/DivConformingFESpaces.jl (100%) diff --git a/src/FESpaces/FESpaces.jl b/src/FESpaces/FESpaces.jl index b42054859..5537ed784 100644 --- a/src/FESpaces/FESpaces.jl +++ b/src/FESpaces/FESpaces.jl @@ -219,9 +219,7 @@ include("UnconstrainedFESpaces.jl") include("ConformingFESpaces.jl") -include("DivConformingFESpaces.jl") - -include("CurlConformingFESpaces.jl") +include("Pullbacks.jl") include("FESpaceFactories.jl") @@ -253,8 +251,6 @@ include("CLagrangianFESpaces.jl") include("DirichletFESpaces.jl") -#include("ExtendedFESpaces.jl") - include("FESpacesWithLinearConstraints.jl") include("DiscreteModelWithFEMaps.jl") diff --git a/src/FESpaces/Pullbacks.jl b/src/FESpaces/Pullbacks.jl new file mode 100644 index 000000000..a40c36202 --- /dev/null +++ b/src/FESpaces/Pullbacks.jl @@ -0,0 +1,119 @@ + +# TODO: We probably want to export these from Gridap.ReferenceFEs +using Gridap.ReferenceFEs: PushforwardRefFE, Pushforward, InversePullback +using Gridap.ReferenceFEs: ContraVariantPiolaMap, CoVariantPiolaMap + +function get_cell_dof_basis( + model::DiscreteModel, cell_reffe::AbstractArray{<:GenericRefFE{T}}, conformity::Conformity +) where T <: PushforwardRefFE + pushforward = Pushforward(T) + cell_args = get_cell_pushforward_arguments(pushforward, model, cell_reffe, conformity) + cell_dofs = lazy_map(get_dof_basis, cell_reffe) + return lazy_map(InversePullback(pushforward), cell_dofs, cell_args...) +end + +function get_cell_shapefuns( + model::DiscreteModel, cell_reffe::AbstractArray{<:GenericRefFE{T}}, conformity::Conformity +) where T <: PushforwardRefFE + pushforward = Pushforward(T) + cell_args = get_cell_pushforward_arguments(pushforward, model, cell_reffe, conformity) + cell_dofs = lazy_map(get_shapefuns, cell_reffe) + return lazy_map(pushforward, cell_dofs, cell_args...) +end + +function get_cell_pushforward_arguments( + ::Pushforward, model::DiscreteModel, cell_reffe, conformity +) + @abstractmethod +end + +# ContraVariantPiolaMap + +function get_cell_pushforward_arguments( + ::ContraVariantPiolaMap, model::DiscreteModel, cell_reffe, conformity +) + cell_map = get_cell_map(get_grid(model)) + Jt = lazy_map(Broadcasting(∇),cell_map) + sign_flip = lazy_map(Broadcasting(constant_field),get_sign_flip(model, cell_reffe)) + return Jt, sign_flip +end + +# CoVariantPiolaMap + +function get_cell_pushforward_arguments( + ::CoVariantPiolaMap, model::DiscreteModel, cell_reffe, conformity +) + cell_map = get_cell_map(get_grid(model)) + Jt = lazy_map(Broadcasting(∇),cell_map) + return Jt +end + +# SignFlipMap + +struct SignFlipMap{T} <: Map + model::T + facet_owners::Vector{Int32} +end + +function SignFlipMap(model) + facet_owners = compute_facet_owners(model) + SignFlipMap(model,facet_owners) +end + +function return_value(k::SignFlipMap,reffe,facet_own_dofs,cell) + fill(false, num_dofs(reffe)) +end + +function return_cache(k::SignFlipMap,reffe,facet_own_dofs,cell) + model = k.model + Dc = num_cell_dims(model) + topo = get_grid_topology(model) + + cell_facets = get_faces(topo, Dc, Dc-1) + cell_facets_cache = array_cache(cell_facets) + + return cell_facets, cell_facets_cache, CachedVector(Bool) +end + +function evaluate!(cache,k::SignFlipMap,reffe,facet_own_dofs,cell) + cell_facets,cell_facets_cache,sign_flip_cache = cache + facet_owners = k.facet_owners + + setsize!(sign_flip_cache, (num_dofs(reffe),)) + sign_flip = sign_flip_cache.array + sign_flip .= false + + facets = getindex!(cell_facets_cache,cell_facets,cell) + for (lfacet,facet) in enumerate(facets) + owner = facet_owners[facet] + if owner != cell + for dof in facet_own_dofs[lfacet] + sign_flip[dof] = true + end + end + end + + return sign_flip +end + +function get_sign_flip(model::DiscreteModel{Dc}, cell_reffe) where Dc + # Comment: lazy_maps on cell_reffes are very optimised, since they are CompressedArray/FillArray + get_facet_own_dofs(reffe) = view(get_face_own_dofs(reffe),get_dimrange(get_polytope(reffe),Dc-1)) + cell_facet_own_dofs = lazy_map(get_facet_own_dofs, cell_reffe) + cell_ids = IdentityVector(Int32(num_cells(model))) + return lazy_map(SignFlipMap(model), cell_reffe, cell_facet_own_dofs, cell_ids) +end + +function compute_facet_owners(model::DiscreteModel{Dc}) where {Dc} + topo = get_grid_topology(model) + facet_to_cell = get_faces(topo, Dc-1, Dc) + + nfacets = num_faces(topo, Dc-1) + owners = Vector{Int32}(undef, nfacets) + for facet in 1:nfacets + facet_cells = view(facet_to_cell, facet) + owners[facet] = first(facet_cells) + end + + return owners +end diff --git a/src/FESpaces/CurlConformingFESpaces.jl b/src/FESpaces/deprecated/CurlConformingFESpaces.jl similarity index 100% rename from src/FESpaces/CurlConformingFESpaces.jl rename to src/FESpaces/deprecated/CurlConformingFESpaces.jl diff --git a/src/FESpaces/DivConformingFESpaces.jl b/src/FESpaces/deprecated/DivConformingFESpaces.jl similarity index 100% rename from src/FESpaces/DivConformingFESpaces.jl rename to src/FESpaces/deprecated/DivConformingFESpaces.jl diff --git a/src/ReferenceFEs/AWRefFEs.jl b/src/ReferenceFEs/AWRefFEs.jl index 2f6850afb..a2c4810e8 100644 --- a/src/ReferenceFEs/AWRefFEs.jl +++ b/src/ReferenceFEs/AWRefFEs.jl @@ -1,9 +1,11 @@ -struct ArnoldWinther <: ReferenceFEName end + +struct ArnoldWinther <: PushforwardRefFE end const arnoldwinther = ArnoldWinther() """ -ArnoldWintherRefFE(::Type{T},p::Polytope,order::Integer) where T + struct ArnoldWinther <: PushforwardRefFE end + ArnoldWintherRefFE(::Type{T},p::Polytope,order::Integer) where T Arnold-Winther reference finite element. diff --git a/src/ReferenceFEs/BDMRefFEs.jl b/src/ReferenceFEs/BDMRefFEs.jl index 984bb0bba..8fee9e4eb 100644 --- a/src/ReferenceFEs/BDMRefFEs.jl +++ b/src/ReferenceFEs/BDMRefFEs.jl @@ -1,7 +1,9 @@ -struct BDM <: DivConforming end +struct BDM <: PushforwardRefFE end const bdm = BDM() +Pushforward(::Type{<:BDM}) = ContraVariantPiolaMap() + """ BDMRefFE(::Type{et},p::Polytope,order::Integer) where et diff --git a/src/ReferenceFEs/MTWRefFEs.jl b/src/ReferenceFEs/MTWRefFEs.jl index c334c10a1..d2f3ede6e 100644 --- a/src/ReferenceFEs/MTWRefFEs.jl +++ b/src/ReferenceFEs/MTWRefFEs.jl @@ -1,9 +1,11 @@ -struct MardalTaiWinther <: ReferenceFEName end + +struct MardalTaiWinther <: PushforwardRefFE end const mtw = MardalTaiWinther() """ -MardalTaiWintherRefFE(::Type{et},p::Polytope,order::Integer) where et + struct MardalTaiWinther <: PushforwardRefFE end + MardalTaiWintherRefFE(::Type{et},p::Polytope,order::Integer) where et Mardal-Tai-Winther reference finite element. diff --git a/src/ReferenceFEs/NedelecRefFEs.jl b/src/ReferenceFEs/NedelecRefFEs.jl index 985984b92..f8bb0aef4 100644 --- a/src/ReferenceFEs/NedelecRefFEs.jl +++ b/src/ReferenceFEs/NedelecRefFEs.jl @@ -1,9 +1,11 @@ struct CurlConformity <: Conformity end -struct Nedelec <: ReferenceFEName end +struct Nedelec <: PushforwardRefFE end const nedelec = Nedelec() +Pushforward(::Type{<:Nedelec}) = CoVariantPiolaMap() + """ NedelecRefFE(::Type{et},p::Polytope,order::Integer) where et diff --git a/src/ReferenceFEs/Pullbacks.jl b/src/ReferenceFEs/Pullbacks.jl index 5e0a47a43..9ca1f036b 100644 --- a/src/ReferenceFEs/Pullbacks.jl +++ b/src/ReferenceFEs/Pullbacks.jl @@ -10,6 +10,11 @@ where """ abstract type Pushforward <: Map end +abstract type PushforwardRefFE <: ReferenceFEName end + +Pushforward(::Type{<:PushforwardRefFE}) = @abstractmethod +Pushforward(name::PushforwardRefFE) = Pushforward(typeof(name)) + function Arrays.lazy_map( k::Pushforward, ref_cell_fields::AbstractArray, pf_args::AbstractArray... ) @@ -263,6 +268,28 @@ function evaluate!( return sign * detJ * (inv(Jt)⋅v_phys) end +# TODO: Should this be here? Probably not... + +function Fields.DIV(f::LazyArray{<:Fill}) + df = Fields.DIV(f.args[1]) + k = f.maps.value + lazy_map(k,df) +end + +function Fields.DIV(a::LazyArray{<:Fill{typeof(linear_combination)}}) + i_to_basis = Fields.DIV(a.args[2]) + i_to_values = a.args[1] + lazy_map(linear_combination,i_to_values,i_to_basis) +end + +function Fields.DIV(f::LazyArray{<:Fill{Broadcasting{Operation{ContraVariantPiolaMap}}}}) + ϕrgₖ = f.args[1] + fsign_flip = f.args[3] + div_ϕrgₖ = lazy_map(Broadcasting(divergence),ϕrgₖ) + fsign_flip = lazy_map(Broadcasting(Operation(x->(-1)^x)), fsign_flip) + lazy_map(Broadcasting(Operation(*)),fsign_flip,div_ϕrgₖ) +end + # CoVariantPiolaMap struct CoVariantPiolaMap <: Pushforward end diff --git a/src/ReferenceFEs/RaviartThomasRefFEs.jl b/src/ReferenceFEs/RaviartThomasRefFEs.jl index b507293b8..4920de0c4 100644 --- a/src/ReferenceFEs/RaviartThomasRefFEs.jl +++ b/src/ReferenceFEs/RaviartThomasRefFEs.jl @@ -1,12 +1,13 @@ struct DivConformity <: Conformity end -abstract type DivConforming <: ReferenceFEName end # RaviartThomas -struct RaviartThomas <: DivConforming end +struct RaviartThomas <: PushforwardRefFE end const raviart_thomas = RaviartThomas() +Pushforward(::Type{<:RaviartThomas}) = ContraVariantPiolaMap() + """ RaviartThomasRefFE(::Type{et},p::Polytope,order::Integer) where et From f9332403f5fe9588f39f8883e2dc48d3bd3fa451 Mon Sep 17 00:00:00 2001 From: JordiManyer Date: Fri, 13 Dec 2024 12:30:41 +1100 Subject: [PATCH 049/105] Minor --- src/ReferenceFEs/ReferenceFEs.jl | 1 - test/pullbacks.jl | 1 - 2 files changed, 2 deletions(-) diff --git a/src/ReferenceFEs/ReferenceFEs.jl b/src/ReferenceFEs/ReferenceFEs.jl index 513ce5ada..12a531f63 100644 --- a/src/ReferenceFEs/ReferenceFEs.jl +++ b/src/ReferenceFEs/ReferenceFEs.jl @@ -186,7 +186,6 @@ export ModalC0RefFE export CRRefFE export Lagrangian -export DivConforming export RaviartThomas export BDM export Nedelec diff --git a/test/pullbacks.jl b/test/pullbacks.jl index ab9573288..716c68658 100644 --- a/test/pullbacks.jl +++ b/test/pullbacks.jl @@ -12,7 +12,6 @@ dΩ = Measure(Ω,2) pts = CellData.get_data(get_cell_points(dΩ)) reffe = RaviartThomasRefFE(Float64,QUAD,1) - V = FESpace(model,reffe) u(x) = VectorValue(x[1], -x[2]) From 04e4b20d0e74fb65bf21fd106c0bcaeb711f13a1 Mon Sep 17 00:00:00 2001 From: Antoine Marteau Date: Fri, 13 Dec 2024 10:36:31 +1100 Subject: [PATCH 050/105] More consistent namings --- src/Polynomials/BernsteinBases.jl | 12 ++-- src/Polynomials/ChebyshevBases.jl | 12 ++-- src/Polynomials/LegendreBases.jl | 12 ++-- src/Polynomials/MonomialBases.jl | 12 ++-- src/Polynomials/OldMonomialHelpers.jl | 16 ++--- src/Polynomials/PolynomialInterfaces.jl | 23 ++++--- src/Polynomials/TensorPolynomialBases.jl | 80 ++++++++++++++---------- 7 files changed, 89 insertions(+), 78 deletions(-) diff --git a/src/Polynomials/BernsteinBases.jl b/src/Polynomials/BernsteinBases.jl index 61b5a1f41..850b10ad7 100644 --- a/src/Polynomials/BernsteinBases.jl +++ b/src/Polynomials/BernsteinBases.jl @@ -8,18 +8,18 @@ struct Bernstein <: Polynomial end isHierarchical(::Bernstein) = false """ - BernsteinBasis{D,T,K} = TensorPolynomialBasis{D,T,K,Bernstein} + BernsteinBasis{D,V,K} = TensorPolynomialBasis{D,V,K,Bernstein} Multivariate scalar' or `Multivalue`'d monomial basis, see [`TensorPolynomialBasis`](@ref) """ -const BernsteinBasis{D,T,K} = TensorPolynomialBasis{D,T,K,Bernstein} +const BernsteinBasis{D,V,K} = TensorPolynomialBasis{D,V,K,Bernstein} """ - BernsteinBasis{D}(::Type{T}, order::Int, terms::Vector) where {D,T} - BernsteinBasis{D}(::Type{T}, orders::Tuple [, filter::Function]) where {D,T} - BernsteinBasis{D}(::Type{T}, order::Int [, filter::Function]) where {D,T} + BernsteinBasis{D}(::Type{V}, order::Int, terms::Vector) where {D,V} + BernsteinBasis{D}(::Type{V}, orders::Tuple [, filter::Function]) where {D,V} + BernsteinBasis{D}(::Type{V}, order::Int [, filter::Function]) where {D,V} -Convenience constructors of BernsteinBasis{D,T}. +Convenience constructors of BernsteinBasis{D,V}. """ BernsteinBasis{D}(args...) where {D} = TensorPolynomialBasis{D}(Bernstein, args...) diff --git a/src/Polynomials/ChebyshevBases.jl b/src/Polynomials/ChebyshevBases.jl index 4c0e7e9f0..ef7d49bfa 100644 --- a/src/Polynomials/ChebyshevBases.jl +++ b/src/Polynomials/ChebyshevBases.jl @@ -9,18 +9,18 @@ struct Chebyshev{kind} <: Polynomial end isHierarchical(::Chebyshev) = true """ - ChebyshevBasis{D,T,kind,K} = TensorPolynomialBasis{D,T,K,Chebyshev{kind}} + ChebyshevBasis{D,V,kind,K} = TensorPolynomialBasis{D,V,K,Chebyshev{kind}} Multivariate scalar' or `Multivalue`'d Chebyshev basis, see [`TensorPolynomialBasis`](@ref) """ -const ChebyshevBasis{D,T,kind,K} = TensorPolynomialBasis{D,T,K,Chebyshev{kind}} +const ChebyshevBasis{D,V,kind,K} = TensorPolynomialBasis{D,V,K,Chebyshev{kind}} """ - ChebyshevBasis{D}(::Type{T}, order::Int, terms::Vector; kind=:T) where {D,T} - ChebyshevBasis{D}(::Type{T}, orders::Tuple [, filter::Function; kind=:T]) where {D,T} - ChebyshevBasis{D}(::Type{T}, order::Int [, filter::Function]; kind=:T) where {D,T} + ChebyshevBasis{D}(::Type{V}, order::Int, terms::Vector; kind=:T) where {D,V} + ChebyshevBasis{D}(::Type{V}, orders::Tuple [, filter::Function; kind=:T]) where {D,V} + ChebyshevBasis{D}(::Type{V}, order::Int [, filter::Function]; kind=:T) where {D,V} -Convenience constructors of `ChebyshevBasis{D,T,kind}`. The default kind is the first kind (cf. [`Chebyshev`](@ref)). +Convenience constructors of `ChebyshevBasis{D,V,kind}`. The default kind is the first kind (cf. [`Chebyshev`](@ref)). """ ChebyshevBasis{D}(args...; kind=:T) where {D} = TensorPolynomialBasis{D}(Chebyshev{kind}, args...) diff --git a/src/Polynomials/LegendreBases.jl b/src/Polynomials/LegendreBases.jl index 951dcc01d..cb755215d 100644 --- a/src/Polynomials/LegendreBases.jl +++ b/src/Polynomials/LegendreBases.jl @@ -8,18 +8,18 @@ struct Legendre <: Polynomial end isHierarchical(::Legendre) = true """ - LegendreBasis{D,T,K} = TensorPolynomialBasis{D,T,K,Legendre} + LegendreBasis{D,V,K} = TensorPolynomialBasis{D,V,K,Legendre} Multivariate scalar' or `Multivalue`'d Legendre basis, see [`TensorPolynomialBasis`](@ref) """ -const LegendreBasis{D,T,K} = TensorPolynomialBasis{D,T,K,Legendre} +const LegendreBasis{D,V,K} = TensorPolynomialBasis{D,V,K,Legendre} """ - LegendreBasis{D}(::Type{T}, order::Int, terms::Vector) where {D,T} - LegendreBasis{D}(::Type{T}, orders::Tuple [, filter::Function]) where {D,T} - LegendreBasis{D}(::Type{T}, order::Int [, filter::Function]) where {D,T} + LegendreBasis{D}(::Type{V}, order::Int, terms::Vector) where {D,V} + LegendreBasis{D}(::Type{V}, orders::Tuple [, filter::Function]) where {D,V} + LegendreBasis{D}(::Type{V}, order::Int [, filter::Function]) where {D,V} -Convenience constructors of LegendreBasis{D,T}. +Convenience constructors of LegendreBasis{D,V}. """ LegendreBasis{D}(args...) where {D} = TensorPolynomialBasis{D}(Legendre, args...) diff --git a/src/Polynomials/MonomialBases.jl b/src/Polynomials/MonomialBases.jl index 792b392d6..b113393c3 100644 --- a/src/Polynomials/MonomialBases.jl +++ b/src/Polynomials/MonomialBases.jl @@ -8,18 +8,18 @@ struct Monomial <: Polynomial end isHierarchical(::Monomial) = true """ - MonomialBasis{D,T,K} = TensorPolynomialBasis{D,T,K,Monomial} + MonomialBasis{D,V,K} = TensorPolynomialBasis{D,V,K,Monomial} Multivariate scalar' or `Multivalue`'d monomial basis, see [`TensorPolynomialBasis`](@ref) """ -const MonomialBasis{D,T,K} = TensorPolynomialBasis{D,T,K,Monomial} +const MonomialBasis{D,V,K} = TensorPolynomialBasis{D,V,K,Monomial} """ - MonomialBasis{D}(::Type{T}, order::Int, terms::Vector) where {D,T} - MonomialBasis{D}(::Type{T}, orders::Tuple [, filter::Function]) where {D,T} - MonomialBasis{D}(::Type{T}, order::Int [, filter::Function]) where {D,T} + MonomialBasis{D}(::Type{V}, order::Int, terms::Vector) where {D,V} + MonomialBasis{D}(::Type{V}, orders::Tuple [, filter::Function]) where {D,V} + MonomialBasis{D}(::Type{V}, order::Int [, filter::Function]) where {D,V} -Convenience constructors of MonomialBasis{D,T}. +Convenience constructors of MonomialBasis{D,V}. """ MonomialBasis{D}(args...) where {D} = TensorPolynomialBasis{D}(Monomial, args...) diff --git a/src/Polynomials/OldMonomialHelpers.jl b/src/Polynomials/OldMonomialHelpers.jl index 951965cce..b31e927fc 100644 --- a/src/Polynomials/OldMonomialHelpers.jl +++ b/src/Polynomials/OldMonomialHelpers.jl @@ -223,9 +223,9 @@ end # Field implementation #function _evaluate!( # cache, -# fg::FieldGradientArray{1,MonomialBasis{D,T}}, +# fg::FieldGradientArray{1,MonomialBasis{D,V}}, # x::AbstractVector{<:Point}, -# TisbitsType::Val{true}) where {D,T} +# TisbitsType::Val{true}) where {D,V} # # f = fg.fa # r, v, c, g = cache @@ -249,9 +249,9 @@ end # #function _evaluate!( # cache, -# fg::FieldGradientArray{1,MonomialBasis{D,T}}, +# fg::FieldGradientArray{1,MonomialBasis{D,V}}, # x::AbstractVector{<:Point}, -# TisbitsType::Val{false}) where {D,T} +# TisbitsType::Val{false}) where {D,V} # # f = fg.fa # r, v, c, g, z = cache @@ -274,8 +274,8 @@ end # #function evaluate!( # cache, -# fg::FieldGradientArray{1,MonomialBasis{D,T}}, -# x::AbstractVector{<:Point}) where {D,T} +# fg::FieldGradientArray{1,MonomialBasis{D,V}}, +# x::AbstractVector{<:Point}) where {D,V} # # r, v, c, g = cache # TisbitsType = Val(isbitstype(eltype(c))) @@ -303,8 +303,8 @@ end # #function evaluate!( # cache, -# fg::FieldGradientArray{2,MonomialBasis{D,T}}, -# x::AbstractVector{<:Point}) where {D,T} +# fg::FieldGradientArray{2,MonomialBasis{D,V}}, +# x::AbstractVector{<:Point}) where {D,V} # # f = fg.fa # r, v, c, g, h = cache diff --git a/src/Polynomials/PolynomialInterfaces.jl b/src/Polynomials/PolynomialInterfaces.jl index b4b2329e0..884f69e7a 100644 --- a/src/Polynomials/PolynomialInterfaces.jl +++ b/src/Polynomials/PolynomialInterfaces.jl @@ -15,18 +15,17 @@ of the basis of order `k-1` and an other order `k` polynomial. """ isHierarchical(::Polynomial) = @abstractmethod -#""" -# get_order(::Type{PolynomialType{K}}) where K = K -#""" -#get_order(::Type{PolynomialType{K}}) where K = K - -#""" -# Jacobi{α, β} <: Polynomial -# -#Type representing Jacobi polynomials of parameters `α` and `β` -#""" -#struct Jacobi{α, β} <: Polynomial end -#isHierarchical(::Jacobi) = true +""" + PolynomialBasis{D,V,K,PT<:Polynomial} <: AbstractVector{PT} + +Abstract type representing a generic multivariate polynomial basis. +The parameters are: +- `D`: the spatial dimension +- `V`: the image values type, e.g. `Real` or `<:MultiValue` +- `K`: the maximum order of a basis polynomial in a spatial component +- `PT <: Polynomial`: the polynomial family (must be a concrete type) +""" +abstract type PolynomialBasis{D,V,K,PT<:Polynomial} <: AbstractVector{PT} end ############################### diff --git a/src/Polynomials/TensorPolynomialBases.jl b/src/Polynomials/TensorPolynomialBases.jl index c7a785b71..005831f5b 100644 --- a/src/Polynomials/TensorPolynomialBases.jl +++ b/src/Polynomials/TensorPolynomialBases.jl @@ -5,36 +5,35 @@ # Notations: # D: spatial / input space dimension # T: scalar type (Float64, ...) -# V: concrete type of values image space (T, VectorValue{D,T} etc.) -# G: concrete MultiValue type holding the gradient of a function of value V, i.e. gradient_type(V,Point{D}) +# V: concrete type of image values (T, VectorValue{D,T} etc.) +# G: concrete MultiValue type holding the gradient or hessian of a function of +# value V, i.e. gradient_type(V,Point{D}) or gradient_type(gradient_type(V,p::Point{D}), p) # # PT: a concrete `Polynomial` type # np: number of points at which a basis is evaluated # ndof: number of basis vectors, num_indep_components(V) × dimension of the polynomial space # ndof_1d: maximum of 1D basis vector in any spatial dimension -""" - PolynomialBasis{D,V,K,PT<:Polynomial} <: AbstractVector{PT} - -Abstract type representing a generic multivariate polynomial basis. -The parameters are: -- `D`: the spatial dimension -- `V`: the image value type, e.g. `Real` or `<:MultiValue` -- `K`: the maximum order of a basis polynomial in one dimension -- `PT <: Polynomial`: the polynomial family (must be a concrete type) -""" -abstract type PolynomialBasis{D,V,K,PT<:Polynomial} <: AbstractVector{PT} end - """ struct TensorPolynomialBasis{D,V,K,PT} <: PolynomialBasis{D,V,K,PT} -Type representing a basis of isotropic multivariate scalar-valued, vector-valued, or -tensor-valued polynomial that have a of tensor product structure of 1D -polynomial in each component. +Type representing a tensorial basis of (an)isotropic `D`-multivariate `V`-valued +(scalar, vector, or tensor) polynomial basis, that is: + +The polynomial space is the tensor product of a scalar polynomial space (one for +each independant component of V). + +The scalar polynomial basis is + + { x ⟶ b`ᴷ`\\_α(x) = b`ᴷ`\\_α₁(x₁) × b`ᴷ`\\_α₂(x₂) × ... × b`ᴷ`\\_α`D`(x`D`) | α ∈ `terms` } + +where b`ᴷ`\\_αᵢ(xᵢ) is the αᵢth 1D basis polynomial of the basis `PT` of order `K` +evaluated at xᵢ, and where α = (α₁, α₂, ..., α`D`) is a multi-index in `terms`, +a subset of ⟦0,`K`⟧`ᴰ`. `terms` is a field that can be passed in a constructor. The fields of this `struct` are not public. -This type fully implements the [`Field`](@ref) interface, with up to second order -derivatives. +This type fully implements the [`Field`](@ref) interface, with up to second +order derivatives. """ struct TensorPolynomialBasis{D,V,K,PT} <: PolynomialBasis{D,V,K,PT} orders::NTuple{D,Int} @@ -314,22 +313,35 @@ function _tensorial_evaluate_nd!( @inbounds s *= c[d,ci[d]] end - k = _isotropic_set_value!(v,s,k) + k = _tensorial_set_value!(v,s,k) end end -function _isotropic_set_value!(v::AbstractVector{<:Real},s,k) +""" + _tensorial_set_value!(v::AbstractVector{<:Real},s,k) + +v[k] = s; return k+1 +""" +function _tensorial_set_value!(v::AbstractVector{<:Real},s,k) @inbounds v[k] = s k+1 end -# If ncomp is the number of independent components of V, sets ncomp values in v -# such that the j^th is V(0, ..., 0, s, 0, ...) with s at position j. -# Values are set in v from position k to k+ncomp-1, k+ncomp is returned -# -# This means that the basis has the same polynomial space in each component, -# so it is isotropic in V components -function _isotropic_set_value!(v::AbstractVector{V},s::T,k) where {V,T} +""" + _tensorial_set_value!(v::AbstractVector{V},s::T,k) + +v[k] = V(s, 0, ..., 0) +v[k+1] = V(0, s, ..., 0) + ⋮ +v[k+N] = V(0, ..., 0, s) +return k+N + +Where N is the number of independent components of V + +This means that the basis has the same polynomial space in each component, so it +is tensorial relative to V components (not necessarily relative to evaluation point x) +""" +function _tensorial_set_value!(v::AbstractVector{V},s::T,k) where {V,T} ncomp = num_indep_components(V) z = zero(T) @inbounds for j in 1:ncomp @@ -373,18 +385,18 @@ function _tensorial_gradient_nd!( end end - k = _isotropic_set_gradient!(v,s,k,V) + k = _tensorial_set_gradient!(v,s,k,V) end end -function _isotropic_set_gradient!( +function _tensorial_set_gradient!( v::AbstractVector{G},s,k,::Type{<:Real}) where G @inbounds v[k] = s k+1 end -@generated function _isotropic_set_gradient!( +@generated function _tensorial_set_gradient!( v::AbstractVector{G},s,k,::Type{V}) where {G,V} # Git blame me for readable non-generated version @@ -420,9 +432,9 @@ end # return a tensor type G that implements the appropriate symmetries of the # gradient (and hessian) # -# This is still called "isotropic" as each independent SymTensor component holds -# the same (scalar multivariate) polynomial space. -@generated function _isotropic_set_gradient!( +# This is still (independant-)component tensorial as each independent SymTensor +# component holds the same (scalar multivariate) polynomial space. +@generated function _tensorial_set_gradient!( v::AbstractVector{G},s,k,::Type{V}) where {G,V<:AbstractSymTensorValue{D}} where D # Git blame me for readable non-generated version From fb94e22a4b8e1e38ad0f72726b1089256ba93b35 Mon Sep 17 00:00:00 2001 From: Antoine Marteau Date: Fri, 13 Dec 2024 13:28:43 +1100 Subject: [PATCH 051/105] revert typo and minor fix --- src/Polynomials/ModalC0Bases.jl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Polynomials/ModalC0Bases.jl b/src/Polynomials/ModalC0Bases.jl index 903114dde..1789a5ac7 100644 --- a/src/Polynomials/ModalC0Bases.jl +++ b/src/Polynomials/ModalC0Bases.jl @@ -296,7 +296,7 @@ end function _compute_filter_mask(terms,filter,orders) g = (0 .* orders) .+ 1 to = CartesianIndex(g) - maxorder = _maximum(orders) + maxorder = maximum(orders) term_to_is_fterm = lazy_map(t->filter(Int[Tuple(t-to)...],maxorder),terms) findall(term_to_is_fterm) end @@ -363,7 +363,7 @@ function _hessian_1d_mc0!(v::AbstractMatrix{T},x,a,b,order,d) where T 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]-[d]))*dj+v1*v2*d2j*2*i*((b[d]-a[d])^2))/(i-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 From 29d5cfd65765ac41f876d27e78d1a3d10f807aae Mon Sep 17 00:00:00 2001 From: Antoine Marteau Date: Fri, 13 Dec 2024 13:33:40 +1100 Subject: [PATCH 052/105] test and fix BernsteinBases --- src/Polynomials/BernsteinBases.jl | 16 +-- src/Polynomials/LegendreBases.jl | 2 + src/Polynomials/Polynomials.jl | 7 +- test/PolynomialsTests/BernsteinBasesTests.jl | 100 +++++++++++++++++++ test/PolynomialsTests/runtests.jl | 2 + 5 files changed, 116 insertions(+), 11 deletions(-) create mode 100644 test/PolynomialsTests/BernsteinBasesTests.jl diff --git a/src/Polynomials/BernsteinBases.jl b/src/Polynomials/BernsteinBases.jl index 850b10ad7..2bf1019f3 100644 --- a/src/Polynomials/BernsteinBases.jl +++ b/src/Polynomials/BernsteinBases.jl @@ -40,7 +40,7 @@ function _evaluate_1d!(::Type{Bernstein}, k, v::AbstractMatrix{T},x,d) where T<: j = i-1 λ1_j = λ1^(j) λ2_j = λ2^(k-j) - @inbounds v[d,i] = b[j] * λ1_j * λ2_j # order k + @inbounds v[d,i] = b[i] * λ1_j * λ2_j # order k end end @@ -55,8 +55,8 @@ function _gradient_1d!(::Type{Bernstein}, k, g::AbstractMatrix{T},x,d) where T<: o = one(T) if isone(k) - @inbounds g[d,1] = o - @inbounds g[d,2] = -o + @inbounds g[d,1] = -o + @inbounds g[d,2] = o return end @@ -65,13 +65,13 @@ function _gradient_1d!(::Type{Bernstein}, k, g::AbstractMatrix{T},x,d) where T<: @inbounds λ1 = x[d] λ2 = o - λ1 - @inbounds g[1] = k * λ2^(k-1) + @inbounds g[1] =-k * λ2^(k-1) @inbounds g[n] = k * λ1^(k-1) for i in 2:n-1 j = i-1 λ1_j = λ1^(j-1) λ2_j = λ2^(k-j-1) - @inbounds g[d,i] = k * λ1_j * λ2_j *(λ2*b[j-1] - λ1*b[j]) # order k-1 + @inbounds g[d,i] = k * λ1_j * λ2_j *(λ2*b[i-1] - λ1*b[i]) # order k-1 end end @@ -87,8 +87,8 @@ function _hessian_1d!(::Type{Bernstein}, k, h::AbstractMatrix{T},x,d) where T<:N return end if isone(k) - @inbounds h[d,1] = z - @inbounds h[d,2] = -z + @inbounds h[d,1] = z + @inbounds h[d,2] = z return end @@ -114,6 +114,6 @@ function _hessian_1d!(::Type{Bernstein}, k, h::AbstractMatrix{T},x,d) where T<:N j = i-1 λ1_j = λ1^(j-2) λ2_j = λ2^(k-j-2) - @inbounds h[d,i] = C * λ1_j * λ2_j *(λ2*λ2*b[j-2] -2λ2*λ1*b[j-1] + λ1*λ1*b[j]) # order k-2 + @inbounds h[d,i] = C * λ1_j * λ2_j *(λ2*λ2*b[i-2] -2λ2*λ1*b[i-1] + λ1*λ1*b[i]) # order k-2 end end diff --git a/src/Polynomials/LegendreBases.jl b/src/Polynomials/LegendreBases.jl index cb755215d..3e2a5d8b1 100644 --- a/src/Polynomials/LegendreBases.jl +++ b/src/Polynomials/LegendreBases.jl @@ -25,6 +25,8 @@ LegendreBasis{D}(args...) where {D} = TensorPolynomialBasis{D}(Legendre, args... # 1D evaluation implementation +# TODO optimize evaluation by using the iterative formula explicitely + function _evaluate_1d!(::Type{Legendre}, k, v::AbstractMatrix{T},x,d) where T<:Number n = k + 1 o = one(T) diff --git a/src/Polynomials/Polynomials.jl b/src/Polynomials/Polynomials.jl index 34b245f8a..b63c03785 100644 --- a/src/Polynomials/Polynomials.jl +++ b/src/Polynomials/Polynomials.jl @@ -34,8 +34,9 @@ export PCurlGradLegendrePolynomialBasis export ChebyshevBasis export QGradChebyshevPolynomialBasis export QCurlGradChebyshevPolynomialBasis -export get_exponents +export BernsteinBasis +export get_exponents export get_order export get_orders export num_terms @@ -49,8 +50,6 @@ include("MonomialBases.jl") include("OldMonomialHelpers.jl") -include("BernsteinBases.jl") - include("QGradMonomialBases.jl") include("QCurlGradMonomialBases.jl") @@ -67,4 +66,6 @@ include("PCurlGradLegendrePolynomialBases.jl") include("ChebyshevBases.jl") +include("BernsteinBases.jl") + end # module diff --git a/test/PolynomialsTests/BernsteinBasesTests.jl b/test/PolynomialsTests/BernsteinBasesTests.jl new file mode 100644 index 000000000..432953b49 --- /dev/null +++ b/test/PolynomialsTests/BernsteinBasesTests.jl @@ -0,0 +1,100 @@ +module BernsteinBasisTests + +using Test +using Gridap.TensorValues +using Gridap.Fields +using Gridap.Polynomials + +np = 3 +x = [Point(0.),Point(1.),Point(.4)] +xi = x[1] + +# Only test 1D evaluations as tensor product structure is tested in monomial tests +# +V = Float64 +G = gradient_type(V,xi) +H = gradient_type(G,xi) + +# order 0 degenerated case + +order = 0 +b = BernsteinBasis{1}(V,order) +@test get_order(b) == 0 +@test get_orders(b) == (0,) + +v = V[1.0,] +g = G[(0.0)] +h = H[(0.0)] + +bx = repeat(permutedims(v),np) +∇bx = repeat(permutedims(g),np) +Hbx = repeat(permutedims(h),np) +test_field_array(b,x,bx,grad=∇bx,gradgrad=Hbx) +test_field_array(b,x[1],bx[1,:],grad=∇bx[1,:],gradgrad=Hbx[1,:]) + +# Order 1 + +order = 1 +b = BernsteinBasis{1}(V,order) + +bx = [ 1.0 0.0 + 0.0 1.0 + 0.6 0.4] + +∇bx = G[ -1. 1. + -1. 1. + -1. 1. ] + +Hbx = H[ 0. 0. + 0. 0. + 0. 0. ] + +test_field_array(b,x,bx,grad=∇bx,gradgrad=Hbx) +test_field_array(b,x[1],bx[1,:],grad=∇bx[1,:],gradgrad=Hbx[1,:]) + +# Order 2 + +order = 2 +b = BernsteinBasis{1}(V,order) + +bx = [ 1.0 0.0 0.0 + 0.0 0.0 1.0 + 0.36 0.48 0.16] + + +∇bx = G[ -2. 2. 0. + 0. -2. 2. + -1.2 0.4 .8 ] + +Hbx = H[ 2. -4. 2. + 2. -4. 2. + 2. -4. 2. ] + +test_field_array(b,x,bx,≈, grad=∇bx,gradgrad=Hbx) +test_field_array(b,x[1],bx[1,:],grad=∇bx[1,:],gradgrad=Hbx[1,:]) + +# Order 3 + +order = 3 +b = BernsteinBasis{1}(V,order) + +# x=x^1; x2 = x^2; x3 = x^3 +# -x3+3x2-3x+1 3x3-6x2+3x -3x3+3x2 x3 +bx = [ 1.0 0.0 0.0 0.0 + 0.0 0.0 0.0 1.0 + .216 .432 .288 .064] + +# -3x2+6x-3 9x2-12x+3 -9x2+6x 3x2 +∇bx = G[ -3. 3. 0. 0. + 0. 0. -3. 3. + -1.08 -.36 .96 .48] + +# -6x+6 18x-12 -18x+6 6x +Hbx = H[ 6. -12. 6. 0. + 0. 6. -12. 6. + 3.6 -4.8 -1.2 2.4] + +test_field_array(b,x,bx,≈, grad=∇bx,gradgrad=Hbx) +test_field_array(b,x[1],bx[1,:],grad=∇bx[1,:],gradgrad=Hbx[1,:]) + +end # module diff --git a/test/PolynomialsTests/runtests.jl b/test/PolynomialsTests/runtests.jl index f4af92f3b..5788a2571 100644 --- a/test/PolynomialsTests/runtests.jl +++ b/test/PolynomialsTests/runtests.jl @@ -14,6 +14,8 @@ using Test @testset "LegendreBases" begin include("LegendreBasesTests.jl") end +@testset "BernsteinBases" begin include("BernsteinBasesTests.jl") end + #@testset "ChangeBasis" begin include("ChangeBasisTests.jl") end end # module From 914487882db6998691cd77d27f9fa8ecb9920444 Mon Sep 17 00:00:00 2001 From: JordiManyer Date: Sat, 14 Dec 2024 09:50:03 +1100 Subject: [PATCH 053/105] Saved progress. --- src/FESpaces/Pullbacks.jl | 2 +- src/ReferenceFEs/Pullbacks.jl | 11 +++-- .../DivConformingFESpacesTests.jl | 46 +++++++++---------- 3 files changed, 28 insertions(+), 31 deletions(-) diff --git a/src/FESpaces/Pullbacks.jl b/src/FESpaces/Pullbacks.jl index a40c36202..0f3bce9cd 100644 --- a/src/FESpaces/Pullbacks.jl +++ b/src/FESpaces/Pullbacks.jl @@ -43,7 +43,7 @@ end function get_cell_pushforward_arguments( ::CoVariantPiolaMap, model::DiscreteModel, cell_reffe, conformity ) - cell_map = get_cell_map(get_grid(model)) + cell_map = get_cell_map(get_grid(model)) Jt = lazy_map(Broadcasting(∇),cell_map) return Jt end diff --git a/src/ReferenceFEs/Pullbacks.jl b/src/ReferenceFEs/Pullbacks.jl index 9ca1f036b..cc211e8f3 100644 --- a/src/ReferenceFEs/Pullbacks.jl +++ b/src/ReferenceFEs/Pullbacks.jl @@ -228,11 +228,12 @@ Arrays.inverse_map(pb::Pullback) = InversePullback(pb.pushforward) Arrays.inverse_map(ipb::InversePullback) = Pullback(ipb.pushforward) function Arrays.lazy_map( - ::typeof(evaluate),k::LazyArray{<:Fill{<:InversePullback}},phys_cell_fields::AbstractArray + ::typeof(evaluate), k::LazyArray{<:Fill{<:InversePullback}}, phys_cell_fields::AbstractArray ) + println("InversePullback dispatch") pb = inverse_map(k.maps.value) ref_cell_dofs, pf_args... = k.args - ref_cell_fields = lazy_map(inverse_map(pb.pushforward),phys_cell_fields,pf_args...) + ref_cell_fields = lazy_map(inverse_map(pb.pushforward), phys_cell_fields, pf_args...) return lazy_map(evaluate,ref_cell_dofs,ref_cell_fields) end @@ -251,7 +252,7 @@ function evaluate!( ) sign = (-1)^sign_flip idetJ = 1. / meas(Jt) - return sign * idetJ * (v_ref⋅Jt) + return (sign * v_ref) ⋅(idetJ * Jt) end function return_cache( @@ -263,9 +264,9 @@ end function evaluate!( cache, ::InversePushforward{ContraVariantPiolaMap}, v_phys::Number, Jt::Number, sign_flip::Bool ) - sign = (-1)^sign_flip + sign = (-1)^sign_flip detJ = meas(Jt) - return sign * detJ * (inv(Jt)⋅v_phys) + return (sign * v_phys) ⋅ (detJ * pinvJt(Jt)) end # TODO: Should this be here? Probably not... diff --git a/test/FESpacesTests/DivConformingFESpacesTests.jl b/test/FESpacesTests/DivConformingFESpacesTests.jl index 39ac66419..46dbd805d 100644 --- a/test/FESpacesTests/DivConformingFESpacesTests.jl +++ b/test/FESpacesTests/DivConformingFESpacesTests.jl @@ -10,19 +10,19 @@ using Gridap.Fields using Gridap.Io function test_div_v_q_equiv(U,V,P,Q,Ω) - v=get_fe_basis(V) - u=get_trial_fe_basis(U) + v = get_fe_basis(V) + u = get_trial_fe_basis(U) - q=get_fe_basis(Q) - p=get_trial_fe_basis(P) + q = get_fe_basis(Q) + p = get_trial_fe_basis(P) - dΩ=Measure(Ω,1) - dΩᵣ=Measure(Ω,1,integration_domain_style=ReferenceDomain()) + dΩ = Measure(Ω,1) + dΩᵣ = Measure(Ω,1,integration_domain_style = ReferenceDomain()) - a1(p,v)=∫(divergence(v)*p)dΩ - a2(p,v)=∫(DIV(v)*p)dΩᵣ + a1(p,v) = ∫(divergence(v)*p)dΩ + a2(p,v) = ∫(DIV(v)*p)dΩᵣ - tol=1.0e-12 + tol = 1.0e-12 assem = SparseMatrixAssembler(P,V) data = collect_cell_matrix(P,V,a1(p,v)) A1 = assemble_matrix(assem,data) @@ -30,8 +30,8 @@ function test_div_v_q_equiv(U,V,P,Q,Ω) A2 = assemble_matrix(assem,data) @test norm(A1-A2) < tol - a3(u,q)=∫(q*divergence(u))dΩ - a4(u,q)=∫(q*DIV(u))dΩᵣ + a3(u,q) = ∫(q*divergence(u))dΩ + a4(u,q) = ∫(q*DIV(u))dΩᵣ assem = SparseMatrixAssembler(U,Q) data = collect_cell_matrix(U,Q,a3(u,q)) A3 = assemble_matrix(assem,data) @@ -39,36 +39,33 @@ function test_div_v_q_equiv(U,V,P,Q,Ω) A4 = assemble_matrix(assem,data) @test norm(A3-A4) < tol - uh=FEFunction(U,rand(num_free_dofs(U))) - l1(q)=∫(q*divergence(uh))dΩ - l2(q)=∫(q*DIV(uh))dΩᵣ - v1=assemble_vector(l1,Q) - v2=assemble_vector(l2,Q) + uh = FEFunction(U,rand(num_free_dofs(U))) + l1(q) = ∫(q*divergence(uh))dΩ + l2(q) = ∫(q*DIV(uh))dΩᵣ + v1 = assemble_vector(l1,Q) + v2 = assemble_vector(l2,Q) @test norm(v1-v2) < tol end -@testset "Test Raviart-Thomas" begin +#@testset "Test Raviart-Thomas" begin - domain =(0,1,0,1) + domain = (0,1,0,1) partition = (3,3) model = CartesianDiscreteModel(domain,partition) order = 1 - u(x) = x reffe = ReferenceFE(raviart_thomas,order) - V = TestFESpace(model,reffe,dirichlet_tags = [1,6]) test_single_field_fe_space(V) U = TrialFESpace(V,u) - reffe = ReferenceFE(lagrangian,Float64,order) - Q = TestFESpace(model,reffe,conformity=:L2) + reffe_p = ReferenceFE(lagrangian,Float64,order) + Q = TestFESpace(model,reffe_p,conformity=:L2) P = TrialFESpace(Q) uh = interpolate(u,U) - e = u - uh Ω = Triangulation(model) @@ -103,7 +100,6 @@ end v(x) = VectorValue(-0.5*x[1]+1.0,-0.5*x[2],-0.5*x[3]) vh = interpolate(v,V) - e = v - vh Ω = Triangulation(model) @@ -150,7 +146,7 @@ end test_div_v_q_equiv(U,V,P,Q,Ω) -end +#end @testset "Test BDM" begin From 1e9431b233f5d14b0a94020738ef3b965a79920c Mon Sep 17 00:00:00 2001 From: Antoine Marteau Date: Fri, 13 Dec 2024 16:33:17 +1100 Subject: [PATCH 054/105] Passed poly order by Type in low-level evals enable edge case (first orders) optimizations via dispatch --- src/Polynomials/BernsteinBases.jl | 118 +++++++++--------- src/Polynomials/ChebyshevBases.jl | 69 ++++++---- src/Polynomials/LegendreBases.jl | 17 +-- src/Polynomials/MonomialBases.jl | 35 +++--- src/Polynomials/OldMonomialHelpers.jl | 9 +- .../PCurlGradLegendrePolynomialBases.jl | 6 +- src/Polynomials/PCurlGradMonomialBases.jl | 6 +- src/Polynomials/PolynomialInterfaces.jl | 48 ++++--- .../QGradLegendrePolynomialBases.jl | 6 +- src/Polynomials/QGradMonomialBases.jl | 8 +- src/Polynomials/TensorPolynomialBases.jl | 13 +- 11 files changed, 185 insertions(+), 150 deletions(-) diff --git a/src/Polynomials/BernsteinBases.jl b/src/Polynomials/BernsteinBases.jl index 2bf1019f3..2bde62a9e 100644 --- a/src/Polynomials/BernsteinBases.jl +++ b/src/Polynomials/BernsteinBases.jl @@ -27,93 +27,91 @@ BernsteinBasis{D}(args...) where {D} = TensorPolynomialBasis{D}(Bernstein, args. # TODO Optimize with in-place De Casteljau -binoms(::Val{K}) where K = ntuple( i -> binomial(K,i-1), K+1) +binoms(::Val{K}) where K = ntuple( i -> binomial(K,i-1), Val(K+1)) -# jth Bernstein poly of order k at x: -# Bᵏⱼ(x) = binom(k,j) * x^j * (1-x)^(k-j) -function _evaluate_1d!(::Type{Bernstein}, k, v::AbstractMatrix{T},x,d) where T<:Number - b = binoms(Val(k)) - n = k + 1 + +# jth Bernstein poly of order K at x: +# Bᵏⱼ(x) = binom(K,j) * x^j * (1-x)^(K-j) +function _evaluate_1d!(::Type{Bernstein},::Val{K}, v::AbstractMatrix{T},x,d) where {K,T<:Number} + b = binoms(Val(K)) + n = K + 1 @inbounds λ1 = x[d] λ2 = one(T) - λ1 + for i in 1:n j = i-1 λ1_j = λ1^(j) - λ2_j = λ2^(k-j) - @inbounds v[d,i] = b[i] * λ1_j * λ2_j # order k + λ2_j = λ2^(K-j) + @inbounds v[d,i] = b[i] * λ1_j * λ2_j # order K end end -# First derivative of the jth Bernstein poly of order k at x: -# (Bᵏⱼ)'(x) = k * ( Bᵏ⁻¹ⱼ₋₁(x) - Bᵏ⁻¹ⱼ(x) ) -# = k * x^(j-1) * (1-x)^(k-j-1) * ((1-x)*binom(k-1,j-1) - x*binom(k-1,j)) -function _gradient_1d!(::Type{Bernstein}, k, g::AbstractMatrix{T},x,d) where T<:Number - if iszero(k) - @inbounds g[d,1] = zero(T) - return - end +function _gradient_1d!(::Type{Bernstein},::Val{0},g::AbstractMatrix{T},x,d) where {T<:Number} + @inbounds g[d,1] = zero(T) +end +function _gradient_1d!(::Type{Bernstein},::Val{1},g::AbstractMatrix{T},x,d) where {T<:Number} o = one(T) - if isone(k) - @inbounds g[d,1] = -o - @inbounds g[d,2] = o - return - end + @inbounds g[d,1] = -o + @inbounds g[d,2] = o +end + +# First derivative of the jth Bernstein poly of order K at x: +# (Bᵏⱼ)'(x) = K * ( Bᵏ⁻¹ⱼ₋₁(x) - Bᵏ⁻¹ⱼ(x) ) +# = K * x^(j-1) * (1-x)^(K-j-1) * ((1-x)*binom(K-1,j-1) - x*binom(K-1,j)) +function _gradient_1d!(::Type{Bernstein},::Val{K}, g::AbstractMatrix{T},x,d) where {K,T<:Number} + b = binoms(Val(K-1)) + n = K + 1 - b = binoms(Val(k-1)) - n = k + 1 @inbounds λ1 = x[d] - λ2 = o - λ1 + λ2 = one(T) - λ1 - @inbounds g[1] =-k * λ2^(k-1) - @inbounds g[n] = k * λ1^(k-1) + @inbounds g[1] =-K * λ2^(K-1) + @inbounds g[n] = K * λ1^(K-1) for i in 2:n-1 j = i-1 λ1_j = λ1^(j-1) - λ2_j = λ2^(k-j-1) - @inbounds g[d,i] = k * λ1_j * λ2_j *(λ2*b[i-1] - λ1*b[i]) # order k-1 + λ2_j = λ2^(K-j-1) + @inbounds g[d,i] = K * λ1_j * λ2_j *(λ2*b[i-1] - λ1*b[i]) # order K-1 end end -# Second derivative of the jth Bernstein poly of order k at x: -# (Bᵏⱼ)''(x) = k(k-1) * ( Bᵏ⁻²ⱼ₋₂(x) -2*Bᵏ⁻²ⱼ₋₁(x) + Bᵏ⁻²ⱼ(x) ) -# = k(k-1) * x^(j-2) * (1-x)^(k-j-2) * ( (1-x)^2*binom(k-2,j-2) -# - 2x*(1-x)*binom(k-2,j-1) + (x)^2*binom(k-2,j) -# ) -function _hessian_1d!(::Type{Bernstein}, k, h::AbstractMatrix{T},x,d) where T<:Number - z = zero(T) - if iszero(k) - @inbounds h[d,1] = z - return - end - if isone(k) - @inbounds h[d,1] = z - @inbounds h[d,2] = z - return - end +function _hessian_1d!(::Type{Bernstein},::Val{0},h::AbstractMatrix{T},x,d) where {T<:Number} + @inbounds h[d,1] = zero(T) +end +function _hessian_1d!(::Type{Bernstein},::Val{1},h::AbstractMatrix{T},x,d) where {T<:Number} + @inbounds h[d,1] = zero(T) + @inbounds h[d,2] = zero(T) +end +function _hessian_1d!(::Type{Bernstein},::Val{2},h::AbstractMatrix{T},x,d) where {T<:Number} o = one(T) - if k == 2 - @inbounds h[d,1] = 2o - @inbounds h[d,2] = -4o - @inbounds h[d,3] = 2o - return - end + @inbounds h[d,1] = 2o + @inbounds h[d,2] = -4o + @inbounds h[d,3] = 2o +end + +# Second derivative of the jth Bernstein poly of order K at x: +# (Bᵏⱼ)''(x) = K(K-1) * ( Bᵏ⁻²ⱼ₋₂(x) -2*Bᵏ⁻²ⱼ₋₁(x) + Bᵏ⁻²ⱼ(x) ) +# = K(K-1) * x^(j-2) * (1-x)^(K-j-2) * ( (1-x)^2*binom(K-2,j-2) +# - 2x*(1-x)*binom(K-2,j-1) + (x)^2*binom(K-2,j) +# ) +function _hessian_1d!(::Type{Bernstein},::Val{K},h::AbstractMatrix{T},x,d) where {K,T<:Number} + b = binoms(Val(K-2)) + n = K + 1 + C = K*(K-1) - b = binoms(Val(k-2)) - n = k + 1 - C = k*(k-1) @inbounds λ1 = x[d] - λ2 = o - λ1 + λ2 = one(T) - λ1 - @inbounds h[1] = C * λ2^(k-2) - @inbounds h[2] = C * (-2λ2^(k-2) + (k-2)*λ2^(k-3)*λ1) - @inbounds h[n-1]=C * (-2λ1^(k-2) + (k-2)*λ1^(k-3)*λ2) - @inbounds h[n] = C * λ1^(k-2) + @inbounds h[1] = C * λ2^(K-2) + @inbounds h[2] = C * (-2λ2^(K-2) + (K-2)*λ2^(K-3)*λ1) + @inbounds h[n-1]=C * (-2λ1^(K-2) + (K-2)*λ1^(K-3)*λ2) + @inbounds h[n] = C * λ1^(K-2) for i in 3:n-2 j = i-1 λ1_j = λ1^(j-2) - λ2_j = λ2^(k-j-2) - @inbounds h[d,i] = C * λ1_j * λ2_j *(λ2*λ2*b[i-2] -2λ2*λ1*b[i-1] + λ1*λ1*b[i]) # order k-2 + λ2_j = λ2^(K-j-2) + @inbounds h[d,i] = C * λ1_j * λ2_j *(λ2*λ2*b[i-2] -2λ2*λ1*b[i-1] + λ1*λ1*b[i]) # order K-2 end end diff --git a/src/Polynomials/ChebyshevBases.jl b/src/Polynomials/ChebyshevBases.jl index ef7d49bfa..3b6b883b4 100644 --- a/src/Polynomials/ChebyshevBases.jl +++ b/src/Polynomials/ChebyshevBases.jl @@ -29,41 +29,58 @@ TensorPolynomialBasis{D}(::Type{Chebyshev{:U}}, args...) where D = @notimplement # 1D evaluation implementation function _evaluate_1d!( - ::Type{Chebyshev{:T}}, k, v::AbstractMatrix{T},x,d) where T<:Number + ::Type{Chebyshev{:T}},::Val{0},v::AbstractMatrix{T},x,d) where T<:Number - n = k + 1 - o = one(T) - @inbounds v[d,1] = o - if n > 1 - ξ = (2*x[d] - 1) # ξ ∈ [-1,1] - ξ2 = 2*ξ - v[d,2] = ξ - for i in 3:n - @inbounds v[d,i] = v[d,i-1]*ξ2 - v[d,i-2] - end + @inbounds v[d,1] = one(T) +end + +function _evaluate_1d!( + ::Type{Chebyshev{:T}},::Val{K},v::AbstractMatrix{T},x,d) where {K,T<:Number} + + n = K + 1 # n > 1 + ξ = (2*x[d] - 1) # ξ ∈ [-1,1] + ξ2 = 2*ξ + + @inbounds v[d,1] = one(T) + @inbounds v[d,2] = ξ + for i in 3:n + @inbounds v[d,i] = v[d,i-1]*ξ2 - v[d,i-2] end end + +function _gradient_1d!( + ::Type{Chebyshev{:T}},::Val{0},g::AbstractMatrix{T},x,d) where T<:Number + + @inbounds g[d,1] = zero(T) +end + function _gradient_1d!( - ::Type{Chebyshev{:T}}, k, g::AbstractMatrix{T},x,d) where T<:Number + ::Type{Chebyshev{:T}},::Val{K},g::AbstractMatrix{T},x,d) where {K,T<:Number} - n = k + 1 + n = K + 1 # n>1 z = zero(T) o = one(T) + ξ = T(2*x[d] - 1) dξdx = T(2.0) - @inbounds g[d,1] = z # dT_0 = 0 - if n > 1 - ξ = T(2*x[d] - 1) - @inbounds g[d,2] = dξdx*o # dT_1 = 1*U_0 = 1 - unm1 = o - un = 2*ξ - for i in 3:n - @inbounds g[d,i] = dξdx*(i-1)*un # dT_i = i*U_{i-1} - un, unm1 = 2*ξ*un - unm1, un - end + + unm1 = o + un = 2*ξ + @inbounds g[d,1] = z # dT_0 = 0 + @inbounds g[d,2] = dξdx*o # dT_1 = 1*U_0 = 1 + for i in 3:n + @inbounds g[d,i] = dξdx*(i-1)*un # dT_i = i*U_{i-1} + un, unm1 = 2*ξ*un - unm1, un end end + +function _hessian_1d!( + ::Type{Chebyshev{:T}},::Val{K},g::AbstractMatrix{T},x,d) where {K,T<:Number} + + @notimplemented +end + ############################################################################################ """ @@ -201,7 +218,8 @@ function _evaluate_nd_qgrad_ch!( dim = D for d in 1:dim - _evaluate_1d!(Chebyshev{:T},order,c,x,d) + K = Val(order) + _evaluate_1d!(Chebyshev{:T},K,c,x,d) end o = one(T) @@ -245,7 +263,8 @@ function _gradient_nd_qgrad_ch!( dim = D for d in 1:dim - _derivatives_1d!(Chebyshev{:T},order,(c,g),x,d) + K = Val(order) + _derivatives_1d!(Chebyshev{:T},K,(c,g),x,d) end z = zero(Mutable(V)) diff --git a/src/Polynomials/LegendreBases.jl b/src/Polynomials/LegendreBases.jl index 3e2a5d8b1..ce3962eec 100644 --- a/src/Polynomials/LegendreBases.jl +++ b/src/Polynomials/LegendreBases.jl @@ -25,16 +25,17 @@ LegendreBasis{D}(args...) where {D} = TensorPolynomialBasis{D}(Legendre, args... # 1D evaluation implementation -# TODO optimize evaluation by using the iterative formula explicitely +# TODO optimize evaluation by using the iterative formula explicitely -function _evaluate_1d!(::Type{Legendre}, k, v::AbstractMatrix{T},x,d) where T<:Number - n = k + 1 +function _evaluate_1d!(::Type{Legendre},::Val{K},v::AbstractMatrix{T},x,d) where {K,T<:Number} + n = K + 1 o = one(T) @inbounds v[d,1] = o if n > 1 ξ = ( 2*x[d] - 1 ) for i in 2:n - # The sqrt(2i-1) factor normalizes the basis polynomial for L2 scalar product on ξ∈[0,1], indeed: + # The sqrt(2i-1) factor normalizes the basis polynomial for L2 scalar + # product on ξ∈[0,1], indeed: # ∫[0,1] Pn(2ξ-1)^2 dξ = 1/2 ∫[-1,1] Pn(t)^2 dt = 1/(2n+1) # C.f. Eq. (1.25) in Section 1.1.5 in Ern & Guermond book (2013). @inbounds v[d,i] = sqrt(2*i-1)*jacobi(ξ,i-1,0,0) @@ -42,8 +43,8 @@ function _evaluate_1d!(::Type{Legendre}, k, v::AbstractMatrix{T},x,d) where T<:N end end -function _gradient_1d!(::Type{Legendre}, k, g::AbstractMatrix{T},x,d) where T<:Number - n = k + 1 +function _gradient_1d!(::Type{Legendre},::Val{K},g::AbstractMatrix{T},x,d) where {K,T<:Number} + n = K + 1 z = zero(T) @inbounds g[d,1] = z if n > 1 @@ -54,8 +55,8 @@ function _gradient_1d!(::Type{Legendre}, k, g::AbstractMatrix{T},x,d) where T<:N end end -function _hessian_1d!(::Type{Legendre}, k, h::AbstractMatrix{T},x,d) where T<:Number - n = k + 1 +function _hessian_1d!(::Type{Legendre},::Val{K},h::AbstractMatrix{T},x,d) where {K,T<:Number} + n = K + 1 z = zero(T) @inbounds h[d,1] = z if n > 1 diff --git a/src/Polynomials/MonomialBases.jl b/src/Polynomials/MonomialBases.jl index b113393c3..c257da9d8 100644 --- a/src/Polynomials/MonomialBases.jl +++ b/src/Polynomials/MonomialBases.jl @@ -25,37 +25,44 @@ MonomialBasis{D}(args...) where {D} = TensorPolynomialBasis{D}(Monomial, args... # 1D evaluation implementation -function _evaluate_1d!(::Type{Monomial}, k, v::AbstractMatrix{T},x,d) where T<:Number - n = k + 1 - @inbounds xd = x[d] +function _evaluate_1d!(::Type{Monomial},::Val{K},v::AbstractMatrix{T},x,d) where {K,T<:Number} + n = K + 1 xn = one(T) + @inbounds xd = x[d] + for i in 1:n @inbounds v[d,i] = xn xn *= xd end end -function _gradient_1d!(::Type{Monomial}, k, g::AbstractMatrix{T},x,d) where T<:Number - n = k + 1 + +function _gradient_1d!(::Type{Monomial},::Val{K},g::AbstractMatrix{T},x,d) where {K,T<:Number} + n = K + 1 z = zero(T) - @inbounds g[d,1] = z - @inbounds xd = x[d] xn = one(T) + @inbounds xd = x[d] + + @inbounds g[d,1] = z for i in 2:n @inbounds g[d,i] = (i-1)*xn xn *= xd end end -function _hessian_1d!(::Type{Monomial}, k, h::AbstractMatrix{T},x,d) where T<:Number - n = k + 1 + +function _hessian_1d!(::Type{Monomial},::Val{0},h::AbstractMatrix{T},x,d) where {T<:Number} + @inbounds h[d,1] = zero(T) +end + +function _hessian_1d!(::Type{Monomial},::Val{K},h::AbstractMatrix{T},x,d) where {K,T<:Number} + n = K + 1 # n>1 z = zero(T) - @inbounds h[d,1] = z - if n>1 - @inbounds h[d,2] = z - end - @inbounds xd = x[d] xn = one(T) + @inbounds xd = x[d] + + @inbounds h[d,1] = z + @inbounds h[d,2] = z for i in 3:n @inbounds h[d,i] = (i-1)*(i-2)*xn xn *= xd diff --git a/src/Polynomials/OldMonomialHelpers.jl b/src/Polynomials/OldMonomialHelpers.jl index b31e927fc..ebcd9a827 100644 --- a/src/Polynomials/OldMonomialHelpers.jl +++ b/src/Polynomials/OldMonomialHelpers.jl @@ -8,7 +8,8 @@ function _evaluate_nd!( dim = D for d in 1:dim - _evaluate_1d!(Monomial,orders[d],c,x,d) + Kd = Val(orders[d]) + _evaluate_1d!(Monomial,Kd,c,x,d) end o = one(T) @@ -54,7 +55,8 @@ function _gradient_nd!( dim = D for d in 1:dim - _derivatives_1d!(Monomial,orders[d],(c,g),x,d) + Kd = Val(orders[d]) + _derivatives_1d!(Monomial,Kd,(c,g),x,d) end o = one(T) @@ -174,7 +176,8 @@ function _hessian_nd!( dim = D for d in 1:dim - _derivatives_1d!(Monomial,orders[d],(c,g,h),x,d) + Kd = Val(orders[d]) + _derivatives_1d!(Monomial,Kd,(c,g,h),x,d) end z = zero(Mutable(TensorValue{D,D,T})) diff --git a/src/Polynomials/PCurlGradLegendrePolynomialBases.jl b/src/Polynomials/PCurlGradLegendrePolynomialBases.jl index a7fe8e03e..362cc3ec7 100644 --- a/src/Polynomials/PCurlGradLegendrePolynomialBases.jl +++ b/src/Polynomials/PCurlGradLegendrePolynomialBases.jl @@ -142,7 +142,8 @@ function _evaluate_nd_pcurlgrad_leg!( dim = D for d in 1:dim - _evaluate_1d!(Legendre,order,c,x,d) + K = Val(order) + _evaluate_1d!(Legendre,K,c,x,d) end o = one(T) @@ -201,7 +202,8 @@ function _gradient_nd_pcurlgrad_leg!( dim = D for d in 1:dim - _derivatives_1d!(Legendre,order,(c,g),x,d) + K = Val(order) + _derivatives_1d!(Legendre,K,(c,g),x,d) end z = zero(Mutable(V)) diff --git a/src/Polynomials/PCurlGradMonomialBases.jl b/src/Polynomials/PCurlGradMonomialBases.jl index 5a59848b8..9099515c1 100644 --- a/src/Polynomials/PCurlGradMonomialBases.jl +++ b/src/Polynomials/PCurlGradMonomialBases.jl @@ -144,7 +144,8 @@ function _evaluate_nd_pcurlgrad!( dim = D for d in 1:dim - _evaluate_1d!(Monomial,order,c,x,d) + K = Val(order) + _evaluate_1d!(Monomial,K,c,x,d) end o = one(T) @@ -203,7 +204,8 @@ function _gradient_nd_pcurlgrad!( dim = D for d in 1:dim - _derivatives_1d!(Monomial,order,(c,g),x,d) + K = Val(order) + _derivatives_1d!(Monomial,K,(c,g),x,d) end z = zero(Mutable(V)) diff --git a/src/Polynomials/PolynomialInterfaces.jl b/src/Polynomials/PolynomialInterfaces.jl index 884f69e7a..ada24a5ce 100644 --- a/src/Polynomials/PolynomialInterfaces.jl +++ b/src/Polynomials/PolynomialInterfaces.jl @@ -21,7 +21,7 @@ isHierarchical(::Polynomial) = @abstractmethod Abstract type representing a generic multivariate polynomial basis. The parameters are: - `D`: the spatial dimension -- `V`: the image values type, e.g. `Real` or `<:MultiValue` +- `V`: the image values type, of type `<:Real` or `<:MultiValue` - `K`: the maximum order of a basis polynomial in a spatial component - `PT <: Polynomial`: the polynomial family (must be a concrete type) """ @@ -32,69 +32,65 @@ abstract type PolynomialBasis{D,V,K,PT<:Polynomial} <: AbstractVector{PT} end # 1D internal polynomial APIs # ############################### -# TODO pass order argument as Val{k} to use compile time dispatch to optimize -# edge cases (k=0,1) and possible polynomial coefficients that can be -# pre-computed at compile time - """ - _evaluate_1d!(::Type{<:Polynomial}, k v, x, d) + _evaluate_1d!(PT::Type{<:Polynomial},::Val{K},v,x,d) Evaluates in place the 1D basis polynomials of the given type at one nD point `x` along the given coordinate 1 ≤ `d` ≤ nD. -`v` is an AbstractMatrix of size (at least} d×(k+1), such that the 1 ≤ i ≤ `k`+1 +`v` is an AbstractMatrix of size (at least} `d`×(`K`+1), such that the 1 ≤ i ≤ `k`+1 values are stored in `v[d,i]`. """ -function _evaluate_1d!( ::Type{<:Polynomial},k,v::AbstractMatrix{T},x,d) where T<:Number +function _evaluate_1d!(::Type{<:Polynomial},::Val{K},v::AbstractMatrix{T},x,d) where {K,T<:Number} @abstractmethod end """ - _gradient_1d!(::Type{<:Polynomial}, k, g, x, d) + _gradient_1d!(PT::Type{<:Polynomial},::Val{K},g,x,d) Like [`_evaluate_1d!`](@ref), but computes the first derivative of the basis functions. """ -function _gradient_1d!( ::Type{<:Polynomial},k,g::AbstractMatrix{T},x,d) where T<:Number +function _gradient_1d!(::Type{<:Polynomial},::Val{K},g::AbstractMatrix{T},x,d) where {K,T<:Number} @abstractmethod end """ - _hessian_1d!(::Type{<:Polynomial}, k, h, x, d) + _hessian_1d!(PT::Type{<:Polynomial},::Val{K},g,x,d) Like [`_evaluate_1d!`](@ref), but computes the second derivative of the basis functions. """ -function _hessian_1d!( ::Type{<:Polynomial},k,h::AbstractMatrix{T},x,d) where T<:Number +function _hessian_1d!(::Type{<:Polynomial},::Val{K},h::AbstractMatrix{T},x,d) where {K,T<:Number} @abstractmethod end """ - _derivatives_1d!(PT::Type{<:Polynomial}, k, (v,g,...), x, d) + _derivatives_1d!(PT::Type{<:Polynomial}, ::Val{K}, (v,g,...), x, d) Same as calling ``` -_evaluate_1d!(PT, k, v, x d) -_gradient_1d!(PT, k, g, x d) +_evaluate_1d!(PT, Val(K), v, x d) +_gradient_1d!(PT, Val(K), g, x d) ⋮ ``` -but with possible performence optimization. +but with possible performance optimization. """ -function _derivatives_1d!(PT::Type{<:Polynomial},k,t::NTuple{N},x,d) where N +function _derivatives_1d!( ::Type{<:Polynomial},::Val{K},t::NTuple{N},x,d) where {K,N} @abstractmethod end -function _derivatives_1d!(PT::Type{<:Polynomial},k,t::NTuple{1},x,d) - _evaluate_1d!(PT, k, t[1], x, d) +function _derivatives_1d!(PT::Type{<:Polynomial},::Val{K},t::NTuple{1},x,d) where K + _evaluate_1d!(PT, Val(K), t[1], x, d) end -function _derivatives_1d!(PT::Type{<:Polynomial},k,t::NTuple{2},x,d) - _evaluate_1d!(PT, k, t[1], x, d) - _gradient_1d!(PT, k, t[2], x, d) +function _derivatives_1d!(PT::Type{<:Polynomial},::Val{K},t::NTuple{2},x,d) where K + _evaluate_1d!(PT, Val(K), t[1], x, d) + _gradient_1d!(PT, Val(K), t[2], x, d) end -function _derivatives_1d!(PT::Type{<:Polynomial},k,t::NTuple{3},x,d) - _evaluate_1d!(PT, k, t[1], x, d) - _gradient_1d!(PT, k, t[2], x, d) - _hessian_1d!( PT, k, t[3], x, d) +function _derivatives_1d!(PT::Type{<:Polynomial},::Val{K},t::NTuple{3},x,d) where K + _evaluate_1d!(PT, Val(K), t[1], x, d) + _gradient_1d!(PT, Val(K), t[2], x, d) + _hessian_1d!( PT, Val(K), t[3], x, d) end # Optimizing evaluation at a single point diff --git a/src/Polynomials/QGradLegendrePolynomialBases.jl b/src/Polynomials/QGradLegendrePolynomialBases.jl index bc4c31c76..6ef867097 100644 --- a/src/Polynomials/QGradLegendrePolynomialBases.jl +++ b/src/Polynomials/QGradLegendrePolynomialBases.jl @@ -136,7 +136,8 @@ function _evaluate_nd_qgrad_leg!( dim = D for d in 1:dim - _evaluate_1d!(Legendre,order,c,x,d) + K = Val(order) + _evaluate_1d!(Legendre,K,c,x,d) end o = one(T) @@ -180,7 +181,8 @@ function _gradient_nd_qgrad_leg!( dim = D for d in 1:dim - _derivatives_1d!(Legendre,order,(c,g),x,d) + K = Val(order) + _derivatives_1d!(Legendre,K,(c,g),x,d) end z = zero(Mutable(V)) diff --git a/src/Polynomials/QGradMonomialBases.jl b/src/Polynomials/QGradMonomialBases.jl index 9de6ac25e..4bc2b90b0 100644 --- a/src/Polynomials/QGradMonomialBases.jl +++ b/src/Polynomials/QGradMonomialBases.jl @@ -150,7 +150,8 @@ function _evaluate_nd_qgrad!( dim = D for d in 1:dim - _evaluate_1d!(Monomial,order,c,x,d) + K = Val(order) + _evaluate_1d!(Monomial,K,c,x,d) end o = one(T) @@ -194,7 +195,8 @@ function _gradient_nd_qgrad!( dim = D for d in 1:dim - _derivatives_1d!(Monomial,order,(c,g),x,d) + K = Val(order) + _derivatives_1d!(Monomial,K,(c,g),x,d) end z = zero(Mutable(V)) @@ -231,7 +233,7 @@ function _gradient_nd_qgrad!( for i in js @inbounds m[i,j] = s[i] end - @inbounds v[k] = m + @inbounds v[k] = m k += 1 end diff --git a/src/Polynomials/TensorPolynomialBases.jl b/src/Polynomials/TensorPolynomialBases.jl index 005831f5b..ba3f4ad03 100644 --- a/src/Polynomials/TensorPolynomialBases.jl +++ b/src/Polynomials/TensorPolynomialBases.jl @@ -301,7 +301,8 @@ function _tensorial_evaluate_nd!( c::AbstractMatrix{T}) where {V,D,T} for d in 1:D - _evaluate_1d!(PT,orders[d],c,x,d) + Kd = Val(orders[d]) + _evaluate_1d!(PT,Kd,c,x,d) end o = one(T) @@ -336,10 +337,10 @@ v[k+1] = V(0, s, ..., 0) v[k+N] = V(0, ..., 0, s) return k+N -Where N is the number of independent components of V +where N is the number of independent components of V. This means that the basis has the same polynomial space in each component, so it -is tensorial relative to V components (not necessarily relative to evaluation point x) +is tensorial relative to the V components. """ function _tensorial_set_value!(v::AbstractVector{V},s::T,k) where {V,T} ncomp = num_indep_components(V) @@ -363,7 +364,8 @@ function _tensorial_gradient_nd!( ::Type{V}) where {G,D,T,V} for d in 1:D - _derivatives_1d!(PT,orders[d],(c,g),x,d) + Kd = Val(orders[d]) + _derivatives_1d!(PT,Kd,(c,g),x,d) end o = one(T) @@ -484,7 +486,8 @@ function _tensorial_hessian_nd!( ::Type{V}) where {G,D,T,V} for d in 1:D - _derivatives_1d!(PT,orders[d],(c,g,h),x,d) + Kd = Val(orders[d]) + _derivatives_1d!(PT,Kd,(c,g,h),x,d) end z = zero(Mutable(TensorValue{D,D,T})) From d3bfb70fa09d512a71f7649a0c9699e08a0472b9 Mon Sep 17 00:00:00 2001 From: Antoine Marteau Date: Mon, 16 Dec 2024 10:13:02 +1100 Subject: [PATCH 055/105] added consistency check for term TensorPolyBase constructor --- src/Polynomials/TensorPolynomialBases.jl | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Polynomials/TensorPolynomialBases.jl b/src/Polynomials/TensorPolynomialBases.jl index ba3f4ad03..8cc34a101 100644 --- a/src/Polynomials/TensorPolynomialBases.jl +++ b/src/Polynomials/TensorPolynomialBases.jl @@ -46,6 +46,7 @@ struct TensorPolynomialBasis{D,V,K,PT} <: PolynomialBasis{D,V,K,PT} terms::Vector{CartesianIndex{D}}) where {D,V,PT<:Polynomial} K = maximum(orders; init=0) + @check all( term -> (maximum(Tuple(term), init=0) <= K+1), terms) "Some term contain a higher index than the maximum degree + 1." new{D,V,K,PT}(orders,terms) end end From e618113875b26cd14c15b20f4d2555bec421cf9e Mon Sep 17 00:00:00 2001 From: JordiManyer Date: Wed, 18 Dec 2024 15:01:52 +1100 Subject: [PATCH 056/105] Added documentation on pullbacks --- docs/make.jl | 1 + docs/src/ODEs.md | 71 ++++++++++++++++++++++----------- docs/src/dev-notes/pullbacks.md | 37 +++++++++++++++++ 3 files changed, 86 insertions(+), 23 deletions(-) create mode 100644 docs/src/dev-notes/pullbacks.md diff --git a/docs/make.jl b/docs/make.jl index a2e8cf3ec..b05b679a5 100644 --- a/docs/make.jl +++ b/docs/make.jl @@ -23,6 +23,7 @@ pages = [ "Gridap.Adaptivity" => "Adaptivity.md", "Developper notes" => Any[ "dev-notes/block-assemblers.md", + "dev-notes/pullbacks.md", ], ] diff --git a/docs/src/ODEs.md b/docs/src/ODEs.md index 3701457b9..abd79dd57 100644 --- a/docs/src/ODEs.md +++ b/docs/src/ODEs.md @@ -44,10 +44,12 @@ More precisely, we would like ``\boldsymbol{u}_{n}`` to be close to ``\boldsymbo ``` This is a condition on the design of the pair (``\mathcal{I}``, ``\mathcal{F}``). -# Classification of ODEs and numerical schemes +## Classification of ODEs and numerical schemes + Essentially, a numerical scheme converts a (continuous) ODE into (discrete) nonlinear systems of equations. These systems of equations can be linear under special conditions on the nature of the ODE and the numerical scheme. Since numerical methods for linear and nonlinear systems of equations can be quite different in terms of cost and implementation, we are interested in solving linear systems whenever possible. This leads us to perform the following classifications. -## Classification of ODEs +### Classification of ODEs + We define a few nonlinearity types based on the expression of the residual. * **Nonlinear**. Nothing special can be said about the residual. * **Quasilinear**. The residual is linear with respect to the highest-order time derivative and the corresponding linear form may depend on time and lower-order time derivatives, i.e. @@ -83,7 +85,8 @@ In particular, for the global residual to be linear, both the implicit and expli > In the special case where the implicit part is linear and the explicit part is quasilinear or semilinear, we could, in theory, identify two linear forms for the global residual. However, introducing this difference would call for an order-dependent classification of ODEs and this would create (infinitely) many new types. Since numerical schemes can rarely take advantage of this extra structure in practice, we still say that the global residual is semilinear in these cases. -## Classification of numerical schemes +### Classification of numerical schemes + We introduce a classification of numerical schemes based on where they evaluate the residual during the state update. * If it is possible (up to a change of variables) to write the system of equations for the state update as evaluations of the residual at known values (that depend on the solution at the current time) for all but the highest-order derivative, we say that the scheme is explicit. @@ -95,7 +98,8 @@ We introduce a classification of numerical schemes based on where they evaluate > ``` > where ``\boldsymbol{x}`` and the unknown of the state update. The scheme is explicit if it is possible to introduce a change of variables such that ``\boldsymbol{u}_{k}`` does not depend on ``\boldsymbol{x}``. Otherwise, it is implicit. -## Classification of systems of equations +### Classification of systems of equations + It is advantageous to introduce this classification of ODE and numerical schemes because the system of equations arising from the discretisation of the ODE by a numerical scheme will be linear or nonlinear depending on whether the scheme is explicit, implicit, or implicit-explicit, and on the type of the ODE. More precisely, we have the following table. | | Nonlinear | Quasilinear | Semilinear | Linear | @@ -107,7 +111,8 @@ When the system is linear, another important practical consideration is whether * If the linear system comes from an explicit scheme, the matrix of the system is constant if the mass matrix is. This means that the ODE has to be quasilinear. * If the linear system comes from an implicit scheme, all the linear forms must be constant for the system to have a constant matrix. -## Reuse across iterations +### Reuse across iterations + For performance reasons, it is thus important that the ODE be described in the most specific way. In particular, we consider that the mass term of a quasilinear ODE is not constant, because if it is, the ODE is semilinear. We enable the user to specify the following constant annotations: * For nonlinear and quasilinear ODE, no quantity can be described as constant. * For a semilinear ODE, whether the mass term is constant. @@ -115,10 +120,12 @@ For performance reasons, it is thus important that the ODE be described in the m If a linear form is constant, regardless of whether the numerical scheme relies on a linear or nonlinear system, it is always possible to compute the jacobian of the residual with respect to the corresponding time derivative only once and retrieve it in subsequent computations of the jacobian. -# High-level API in Gridap +## High-level API in Gridap + The ODE module of `Gridap` relies on the following structure. -## Finite element spaces +### Finite element spaces + The time-dependent counterpart of `TrialFESpace` is `TransientTrialFESpace`. It is built from a standard `TestFESpace` and is equipped with time-dependent Dirichlet boundary conditions. > By definition, test spaces have zero Dirichlet boundary conditions so they need not be seen as time-dependent objects. @@ -140,7 +147,8 @@ U0 = U(t0) ∂ttU0 = ∂ttU(t0) ``` -## Cell fields +### Cell fields + The time-dependent equivalent of `CellField` is `TransientCellField`. It stores the cell field itself together with its derivatives up to the order of the ODE. For example, the following creates a `TransientCellField` with two time derivatives. @@ -151,7 +159,8 @@ u0 = zero(get_free_dof_values(U0)) u = TransientCellField(u0, (∂tu0, ∂ttu0)) ``` -## Finite element operators +### Finite element operators + The time-dependent analog of `FEOperator` is `TransientFEOperator`. It has the following constructors based on the nonlinearity type of the underlying ODE. * `TransientFEOperator(res, jacs, trial, test)` and `TransientFEOperator(res, trial, test; order)` for the version with automatic jacobians. The residual is expected to have the signature `residual(t, u, v)`. @@ -199,7 +208,8 @@ TransientLinearFEOperator((stiffness, mass), res, U, V, constant_forms=(false, t ``` If ``\kappa`` is constant, the keyword `constant_forms` could be replaced by `(true, true)`. -## The `TimeSpaceFunction` constructor +### The `TimeSpaceFunction` constructor + Apply differential operators on a function that depends on time and space is somewhat cumbersome. Let `f` be a function of time and space, and `g(t) = x -> f(t, x)` (as in the prescription of the boundary conditions `g` above). Applying the operator ``\partial_{t} - \Delta`` to `g` and evaluating at ``(t, x)`` is written `∂t(g)(t)(x) - Δ(g(t))(x)`. The constructor `TimeSpaceFunction` allows for simpler notations: let `h = TimeSpaceFunction(g)`. The object `h` is a functor that supports the notations @@ -209,7 +219,8 @@ The constructor `TimeSpaceFunction` allows for simpler notations: let `h = TimeS for all spatial and temporal differential operator, i.e. `op` in `(time_derivative, gradient, symmetric_gradient, divergence, curl, laplacian)` and their symbolic aliases (`∂t`, `∂tt`, `∇`, ...). The operator above applied to `h` and evaluated at `(t, x)` can be conveniently written `∂t(h)(t, x) - Δ(h)(t, x)`. -## Solver and solution +### Solver and solution + The next step is to choose an ODE solver (see below for a full list) and specify the boundary conditions. The solution can then be iterated over until the final time is reached. For example, to use the ``\theta``-method with a nonlinear solver, one could write @@ -234,32 +245,38 @@ for (tn, un) in enumerate(sol) end ``` -# Low-level implementation +## Low-level implementation + We now briefly describe the low-level implementation of the ODE module in `Gridap`. -## ODE operators +### ODE operators + The `ODEOperator` type represents an ODE according to the description above. It implements the `NonlinearOperator` interface, which enables the computation of residuals and jacobians. The algebraic equivalent of `TransientFEOperator` is an `ODEOpFromTFEOp`, which is a subtype of `ODEOperator`. Conceptually, `ODEOpFromTFEOp` can be thought of as an assembled `TransientFEOperator`, i.e. it deals with vectors of degrees of freedom. This operator comes with a cache (`ODEOpFromTFEOpCache`) that stores the transient space, its evaluation at the current time step, a cache for the `TransientFEOperator` itself (if any), and the constant forms (if any). > For now `TransientFEOperator` does not implement the `FEOperator` interface, i.e. it is not possible to evaluate residuals and jacobians directly on it. Rather, they are meant to be evaluated on the `ODEOpFromFEOp`. This is to cut down on the number of conversions between a `TransientCellField` and its vectors of degrees of freedom (one per time derivative). Indeed, when linear forms are constant, no conversion is needed as the jacobian matrix will be stored. -## ODE solvers +### ODE solvers + An ODE solver has to implement the following interface. * `allocate_odecache(odeslvr, odeop, t0, us0)`. This function allocates a cache that can be reused across the three functions `ode_start`, `ode_march!`, and `ode_finish!`. In particular, it is necessary to call `allocate_odeopcache` within this function, so as to instantiate the `ODEOpFromTFEOpCache` and be able to update the Dirichlet boundary conditions in the subsequent functions. * `ode_start(odeslvr, odeop, t0, us0, odecache)`. This function creates the state vectors from the initial conditions. By default, this is the identity. * `ode_march!(stateF, odeslvr, odeop, t0, state0, odecache)`. This is the update map that evolves the state vectors. * `ode_finish!(uF, odeslvr, odeop, t0, tF, stateF, odecache)`. This function converts the state vectors into the evaluation of the solution at the current time step. By default, this copies the first state vector into `uF`. -## Stage operator +### Stage operator + A `StageOperator` represents the linear or nonlinear operator that a numerical scheme relies on to evolve the state vector. It is essentially a special kind of `NonlinearOperator` but it overwrites the behaviour of nonlinear and linear solvers to take advantage of the matrix of a linear system being constant. The following subtypes of `StageOperator` are the building blocks of all numerical schemes. * `LinearStageOperator` represents the system ``\boldsymbol{J} \boldsymbol{x} + \boldsymbol{r} = \boldsymbol{0}``, and can build ``\boldsymbol{J}`` and ``\boldsymbol{r}`` by evaluating the residual at a given point. * `NonlinearStageOperator` represents ``\boldsymbol{r}(\boldsymbol{t}, \boldsymbol{\ell}_{0}(\boldsymbol{x}), \ldots, \boldsymbol{\ell}_{N}(\boldsymbol{x})) = \boldsymbol{0}``, where it is assumed that all the ``\boldsymbol{\ell}_{k}(\boldsymbol{x})`` are linear in ``\boldsymbol{x}``. -## ODE solution +### ODE solution + This type is a simple wrapper around an `ODEOperator`, an `ODESolver`, and initial conditions that can be iterated on to evolve the ODE. -# Numerical schemes formulation and implementation +## Numerical schemes formulation and implementation + We conclude this note by describing some numerical schemes and their implementation in `Gridap`. Suppose that the scheme has been evolved up to time ``t_{n}`` already and that the state vectors ``\{\boldsymbol{s}\}_{n}`` are known. We are willing to evolve the ODE up to time ``t_{n+1} > t_{n}``, i.e. compute the state vectors ``\{\boldsymbol{s}\}_{n+1}``. Generally speaking, a numerical scheme constructs an approximation of the map ``\{\boldsymbol{s}\}_{n} \to \{\boldsymbol{s}\}_{n+1}`` by solving one or more relationships of the type @@ -272,7 +289,8 @@ We now describe the numerical schemes implemented in `Gridap` using this framewo We also briefly characterise these schemes in terms of their order and linear stability. -## ``\theta``-method +### ``\theta``-method + This scheme is used to solve first-order ODEs and relies on the simple state vector ``\{\boldsymbol{s}(t)\} = \{\boldsymbol{u}(t)\}``. This means that the starting and finishing procedures are simply the identity. The ``\theta``-method relies on the following approximation @@ -312,7 +330,8 @@ By looking at the behaviour of the stability function at infinity, we find that * ``\theta = \frac{1}{2}``. The stability region is the whole left complex plane, so the scheme is ``A``-stable. This case is known as the implicit midpoint scheme. * ``\theta > \frac{1}{2}``. The stability region is the whole complex plane except the circle of radius ``\frac{1}{2 \theta - 1}`` centered at ``\left(\frac{1}{2 \theta - 1}, 0\right)``. In particular, the scheme is ``A``-stable. The special case ``\theta = 1`` is known as the Backward Euler scheme. -## Generalised-``\alpha`` scheme for first-order ODEs +### Generalised-``\alpha`` scheme for first-order ODEs + This scheme relies on the state vector ``\{\boldsymbol{s}(t)\} = \{\boldsymbol{u}(t), \partial_{t} \boldsymbol{u}(t)\}``. In particular, it needs a nontrivial starting procedure that evaluates ``\partial_{t} \boldsymbol{u}(t_{0})`` by enforcing a zero residual at ``t_{0}``. The finaliser can still return the first vector of the state vectors. For convenience, let ``\partial_{t} \boldsymbol{u}_{n}`` denote the approximation ``\partial_{t} \boldsymbol{u}(t_{n})``. > Alternatively, the initial velocity can be provided manually: when calling `solve(odeslvr, tfeop, t0, tF, uhs0)`, set `uhs0 = (u0, v0, a0)` instead of `uhs0 = (u0, v0)`. This is useful when enforcing a zero initial residual would lead to a singular system. @@ -344,6 +363,7 @@ t_{n + \alpha_{F}} &= (1 - \alpha_{F}) t_{n} + \alpha_{F} t_{n+1}, \\ The state vector is updated to ``\{\boldsymbol{s}\}_{n+1} = \{\boldsymbol{u}_{n+1}, \partial_{t} \boldsymbol{u}_{n+1}\}``. ##### Analysis + The amplification matrix for the state vector is ```math \boldsymbol{A}(z) = \frac{1}{\alpha_{M} - \alpha_{F} \gamma z} \begin{bmatrix}\alpha_{M} + (1 - \alpha_{F}) \gamma z & \alpha_{M} - \gamma \\ z & \alpha_{M} - 1 + \alpha_{F} (1 - \gamma) z\end{bmatrix}. @@ -384,7 +404,8 @@ This scheme was originally devised to control the damping of high frequencies. O ``` where ``\rho_{\infty}`` is the spectral radius at infinity. Setting ``\rho_{\infty}`` cuts all the highest frequencies in one step, whereas taking ``\rho_{\infty} = 1`` preserves high frequencies. -## Runge-Kutta +### Runge-Kutta + Runge-Kutta methods are multi-stage, i.e. they build estimates of ``\boldsymbol{u}`` at intermediate times between ``t_{n}`` and ``t_{n+1}``. They can be written as follows ```math \begin{align*} @@ -399,6 +420,7 @@ where ``p`` is the number of stages, ``\boldsymbol{A} = (a_{ij})_{1 \leq i, j \l **Implementation details** It is particularly advantageous to save the factorisation of the matrices of the stage operators for Runge-Kutta methods. This is always possible when the method is explicit and the mass matrix is constant, in which case all the stage matrices are the mass matrix. When the method is diagonally-implicit and the stiffness and mass matrices are constant, the matrices of the stage operators are ``\boldsymbol{M} + a_{ii} h_{n} \boldsymbol{K}``. In particular, if two diagonal coefficients coincide, the corresponding operators will have the same matrix. We implement these reuse strategies by storing them in `CompressedArray`s, and introducing a map `i -> NumericalSetup`. ##### Analysis + The stability function of a Runge-Kutta scheme is ```math \rho(z) = 1 + z \boldsymbol{b}^{T} (\boldsymbol{I} - z \boldsymbol{A})^{-1} \boldsymbol{1}. @@ -445,7 +467,8 @@ The analysis of Runge-Kutta methods is well-established but we only derive order \end{array}. ``` -## Implicit-Explicit Runge-Kutta +### Implicit-Explicit Runge-Kutta + When the residual has an implicit-explicit decomposition, usually because we can identify a stiff part that we want to solve implicitly and a nonstiff part that we want to solve explicitly, the Runge-Kutta method reads as follows ```math \begin{align*} @@ -475,7 +498,8 @@ Many methods can be created by padding a DIRK tableau with zeros to give it an a ``` We note that the first column of the matrix and the first weight are all zero, so the first stage for the implicit part does not need to be solved. -## Generalised-``\alpha`` scheme for second-order ODEs +### Generalised-``\alpha`` scheme for second-order ODEs + This scheme relies on the state vector ``\{\boldsymbol{s}(t)\} = \{\boldsymbol{u}(t), \partial_{t} \boldsymbol{u}(t), \partial_{tt} \boldsymbol{u}(t)\}``. It needs a nontrivial starting procedure that evaluates ``\partial_{tt} \boldsymbol{u}(t_{0})`` by enforcing a zero residual at ``t_{0}``. The finaliser can still return the first vector of the state vectors. For convenience, let ``\partial_{tt} \boldsymbol{u}_{n}`` denote the approximation ``\partial_{tt} \boldsymbol{u}(t_{n})``. > The initial acceleration can alternatively be provided manually: when calling `solve(odeslvr, tfeop, t0, tF, uhs0)`, set `uhs0 = (u0, v0, a0)` instead of `uhs0 = (u0, v0)`. This is useful when enforcing a zero initial residual would lead to a singular system. @@ -496,6 +520,7 @@ t_{n + 1 - \alpha_{F}} &= \alpha_{F} t_{n} + (1 - \alpha_{F}) t_{n+1}, \\ The state vector is then updated to ``\{\boldsymbol{s}\}_{n+1} = \{\boldsymbol{u}_{n+1}, \partial_{t} \boldsymbol{u}_{n+1}, \partial_{tt} \boldsymbol{u}_{n+1}\}``. ##### Analysis + The amplification matrix for the state vector is ```math \boldsymbol{A}(z) = \frac{1}{\overline{\alpha_{M}} + \overline{\alpha_{F}} \beta z^{2}} \begin{bmatrix} @@ -526,7 +551,7 @@ This method was also designed to damp high-frequency perturbations so it is comm * The standard generalised-``\alpha`` method is obtained by setting ``\alpha_{M} = \frac{2 \rho_{\infty - 1}}{\rho_{\infty} + 1}``, ``\alpha_{F} = \frac{\rho_{\infty}}{\rho_{\infty} + 1}``. * The Newmark method corresponds to ``\alpha_{F} = \alpha_{M} = 0``. In this case, the values of ``\beta`` and ``\gamma`` are usually chosen as ``\beta = 0``, ``\gamma = \frac{1}{2}`` (explicit central difference scheme), or ``\beta = \frac{1}{4}`` and ``\gamma = \frac{1}{2}`` (midpoint rule). -# Reference +## Reference ```@autodocs Modules = [ODEs,] diff --git a/docs/src/dev-notes/pullbacks.md b/docs/src/dev-notes/pullbacks.md new file mode 100644 index 000000000..c658ec3a6 --- /dev/null +++ b/docs/src/dev-notes/pullbacks.md @@ -0,0 +1,37 @@ + +# Pullbacks + +Consider a reference polytope ``\hat{K}``, mapped to the physical space by a **geometrical map** ``G``, i.e. ``K = G(\hat{K})``. Consider also a linear function space on the reference polytope ``\hat{V}``, and a set of unisolvent degrees of freedom represented by moments in the dual space ``\hat{V}^*``. + +Throughout this document, we will use the following notation: + +- ``\varphi \in V`` is a **physical field** ``\varphi : K \rightarrow \mathbb{R}^k``. A basis of ``V`` is denoted by ``\Phi = \{\varphi\}``. +- ``\hat{\varphi} \in \hat{V}`` is a **reference field** ``\hat{\varphi} : \hat{K} \rightarrow \mathbb{R}^k``. A basis of ``\hat{V}`` is denoted by ``\hat{\Phi} = \{\hat{\varphi}\}``. +- ``\sigma \in V^*`` is a **physical moment** ``\sigma : V \rightarrow \mathbb{R}``. A basis of ``V^*`` is denoted by ``\Sigma = \{\sigma\}``. +- ``\hat{\sigma} \in \hat{V}^*`` is a **reference moment** ``\hat{\sigma} : \hat{V} \rightarrow \mathbb{R}``. A basis of ``\hat{V}^*`` is denoted by ``\hat{\Sigma} = \{\hat{\sigma}\}``. + +We define a **pushforward** map as ``F^* : \hat{V} \rightarrow V``, mapping reference fields to physical fields. Given a pushforward ``F^*``, we define: + +- The **pullback** ``F_* : V^* \rightarrow \hat{V}^*``, mapping physical moments to reference moments. Its action on physical dofs is defined in terms of the pushforward map ``F^*`` as ``\hat{\sigma} = F_*(\sigma) := \sigma \circ F^*``. +- The **inverse pushforward** ``(F^*)^{-1} : V \rightarrow \hat{V}``, mapping physical fields to reference fields. +- The **inverse pullback** ``(F_*)^{-1} : \hat{V}^* \rightarrow V^*``, mapping reference moments to physical moments. Its action on reference dofs is defined in terms of the inverse pushforward map ``(F^*)^{-1}`` as ``\sigma = (F_*)^{-1}(\hat{\sigma}) := \hat{\sigma} \circ (F^*)^{-1}``. + +In many occasions, we will have that (as a basis) + +```math +\hat{\Sigma} \neq F_*(\Sigma), \quad \text{and} \quad \Phi \neq F^*(\hat{\Phi}) +``` + +To maintain conformity and proper scaling in these cases, we define cell-dependent invertible changes of basis ``P`` and ``M``, such that + +```math +\hat{\Sigma} = P F_*(\Sigma), \quad \text{and} \quad \Phi = M F^*(\hat{\Phi}) +``` + +An important result from linear algebra is that ``P = M^*`` (or just the transpose if we represent them as real matrices). +From an implementation point of view, it is more natural to build ``P^{-1}`` and then retrieve all other matrices by transposition/inversion. + +## References + +- [Kirby 2017, A general approach to transforming finite elements.](https://arxiv.org/abs/1706.09017) +- [Aznaran et al. 2021, Transformations for Piola-mapped elements.](https://arxiv.org/abs/2110.13224) From 21782768e1423fcb543725ecd5a4bae6c9affe94 Mon Sep 17 00:00:00 2001 From: JordiManyer Date: Wed, 18 Dec 2024 16:45:08 +1100 Subject: [PATCH 057/105] Saved progress --- docs/src/ReferenceFEs.md | 12 +++++++++++- src/Arrays/Maps.jl | 13 ++++++++++++ src/Fields/AffineMaps.jl | 12 ++++++------ src/Fields/InverseFields.jl | 4 ++-- src/ReferenceFEs/Pullbacks.jl | 37 ++++++++--------------------------- 5 files changed, 40 insertions(+), 38 deletions(-) diff --git a/docs/src/ReferenceFEs.md b/docs/src/ReferenceFEs.md index ff6bee434..8cc845992 100644 --- a/docs/src/ReferenceFEs.md +++ b/docs/src/ReferenceFEs.md @@ -73,8 +73,18 @@ Pages = ["LagrangianRefFEs.jl","LagrangianDofBases.jl","SerendipityRefFEs.jl", ### Moment-Based ReferenceFEs +#### Framework + +```@autodocs +Modules = [ReferenceFEs,] +Order = [:type, :constant, :macro, :function] +Pages = ["MomentBasedReferenceFEs.jl","Pullbacks.jl"] +``` + +#### Available Moment-Based ReferenceFEs + ```@autodocs Modules = [ReferenceFEs,] Order = [:type, :constant, :macro, :function] -Pages = ["MomentBasedReferenceFEs.jl","RaviartThomasRefFEs.jl","NedelecRefFEs.jl","BDMRefFEs.jl","CRRefFEs.jl"] +Pages = ["RaviartThomasRefFEs.jl","NedelecRefFEs.jl","BDMRefFEs.jl","CRRefFEs.jl"] ``` diff --git a/src/Arrays/Maps.jl b/src/Arrays/Maps.jl index 7552b80c8..77b5729cf 100644 --- a/src/Arrays/Maps.jl +++ b/src/Arrays/Maps.jl @@ -296,3 +296,16 @@ function inverse_map(f) Function inverse_map is not implemented yet for objects of type $(typeof(f)) """ end + +struct InverseMap{F} <: Map + original::F +end + +function evaluate!(cache,k::InverseMap,args...) + @notimplemented """\n + The inverse evaluation is not implemented yet for maps of type $(typeof(k.original)) + """ +end + +inverse_map(k::Map) = InverseMap(k) +inverse_map(k::InverseMap) = k.original diff --git a/src/Fields/AffineMaps.jl b/src/Fields/AffineMaps.jl index e99221107..33bbe385f 100644 --- a/src/Fields/AffineMaps.jl +++ b/src/Fields/AffineMaps.jl @@ -29,6 +29,12 @@ end affine_map(gradient,origin) = AffineField(gradient,origin) +function Base.zero(::Type{<:AffineField{D1,D2,T}}) where {D1,D2,T} + gradient = TensorValue{D1,D2}(tfill(zero(T),Val{D1*D2}())) + origin = Point{D2,T}(tfill(zero(T),Val{D2}())) + AffineField(gradient,origin) +end + function evaluate!(cache,f::AffineField,x::Point) G = f.gradient y0 = f.origin @@ -129,9 +135,3 @@ function lazy_map( origins = a.args[2] lazy_map(Broadcasting(AffineMap()),gradients,origins,x) end - -function Base.zero(::Type{<:AffineField{D1,D2,T}}) where {D1,D2,T} - gradient = TensorValue{D1,D2}(tfill(zero(T),Val{D1*D2}())) - origin = Point{D2,T}(tfill(zero(T),Val{D2}())) - AffineField(gradient,origin) -end diff --git a/src/Fields/InverseFields.jl b/src/Fields/InverseFields.jl index 2248eda8a..f760f3dc6 100644 --- a/src/Fields/InverseFields.jl +++ b/src/Fields/InverseFields.jl @@ -8,8 +8,8 @@ inverse_map(a::Field) = InverseField(a) inverse_map(a::InverseField) = a.original function return_cache(a::InverseField,x::Point) - y₀ = [zero(x)...] # initial guess and solution - F₀ = [zero(x)...] # error + y₀ = [zero(x)...] # initial guess and solution + F₀ = [zero(x)...] # error return return_cache(a.original,x), return_cache(∇(a.original),x), y₀, F₀ end diff --git a/src/ReferenceFEs/Pullbacks.jl b/src/ReferenceFEs/Pullbacks.jl index cc211e8f3..5ab1f03cf 100644 --- a/src/ReferenceFEs/Pullbacks.jl +++ b/src/ReferenceFEs/Pullbacks.jl @@ -55,7 +55,7 @@ end # InversePushforward """ - struct InversePushforward{PF <: Pushforward} <: Map end + const InversePushforward{PF} = InverseMap{PF} where PF <: Pushforward Represents the inverse of a pushforward map F*, defined as (F*)^-1 : V -> V̂ @@ -75,16 +75,7 @@ specific pushforward type: - `evaluate!(cache, k::InversePushforward{PF}, v_phys::Number, args...)` """ -struct InversePushforward{PF} <: Map - pushforward::PF - function InversePushforward(pushforward::Pushforward) - PF = typeof(pushforward) - new{PF}(pushforward) - end -end - -Arrays.inverse_map(pf::Pushforward) = InversePushforward(pf) -Arrays.inverse_map(ipf::InversePushforward) = ipf.pushforward +const InversePushforward{PF} = InverseMap{PF} where PF <: Pushforward function Arrays.lazy_map( k::InversePushforward, phys_cell_fields::AbstractArray, pf_args::AbstractArray... @@ -97,7 +88,7 @@ function Arrays.return_cache( ) mock_basis(::VectorValue{D,T}) where {D,T} = one(TensorValue{D,D,T}) v_ref_basis = mock_basis(v_phys) - pf_cache = return_cache(k.pushforward,v_ref_basis,args...) + pf_cache = return_cache(inverse_map(k),v_ref_basis,args...) return v_ref_basis, pf_cache end @@ -105,7 +96,7 @@ function Arrays.evaluate!( cache, k::InversePushforward, v_phys::Number, args... ) v_ref_basis, pf_cache = cache - change = evaluate!(pf_cache,k.pushforward,v_ref_basis,args...) + change = evaluate!(pf_cache,inverse_map(k),v_ref_basis,args...) return v_phys⋅inv(change) end @@ -180,12 +171,8 @@ where Its action on physical dofs σ : V -> R is defined in terms of the pushforward map F* as ̂σ = F**(σ) := σ∘F* : V̂ -> R """ -struct Pullback{PF} <: Map +struct Pullback{PF <: Pushforward} <: Map pushforward::PF - function Pullback(pushforward::Pushforward) - PF = typeof(pushforward) - new{PF}(pushforward) - end end function Arrays.lazy_map( @@ -216,16 +203,7 @@ where Its action on reference dofs ̂σ : V̂ -> R is defined in terms of the pushforward map F* as σ = (F**)^-1(̂σ) := ̂σ∘(F*)^-1 : V -> R """ -struct InversePullback{PF} <: Map - pushforward::PF - function InversePullback(pushforward::Pushforward) - PF = typeof(pushforward) - new{PF}(pushforward) - end -end - -Arrays.inverse_map(pb::Pullback) = InversePullback(pb.pushforward) -Arrays.inverse_map(ipb::InversePullback) = Pullback(ipb.pushforward) +const InversePullback{PB} = InverseMap{PB} where PB <: Pullback function Arrays.lazy_map( ::typeof(evaluate), k::LazyArray{<:Fill{<:InversePullback}}, phys_cell_fields::AbstractArray @@ -240,7 +218,8 @@ end function evaluate!( cache, k::InversePullback, σ_ref::AbstractVector{<:Dof}, args... ) - return MappedDofBasis(inverse_map(k.pushforward),σ_ref,args...) + pb = inverse_map(k) + return MappedDofBasis(inverse_map(pb.pushforward),σ_ref,args...) end # ContraVariantPiolaMap From 3bddd5bbed0336c208e3084acef6c3035da990af Mon Sep 17 00:00:00 2001 From: Antoine Marteau Date: Mon, 16 Dec 2024 17:42:47 +1100 Subject: [PATCH 058/105] Wip Q and P factorization - factorized Q[Curl]Grad bases as instences of CompWiseTensorPolyBasis, enabling using any polynomial family as underlying 1D basis - deprecated num_terms in favor of length of Q[Curl]Grad spaces - changed the value of get_order(::Q[Curl]GradBasis), it now returns the actual maximum order of the basis functions, for consistency. the order of the basis polynomial changed in the Q[Curl]Grad basis, cf. CompWiseTensorPolyBasis --- NEWS.md | 2 + src/Polynomials/BernsteinBases.jl | 19 +- src/Polynomials/ChangeBasis.jl | 2 +- src/Polynomials/ChebyshevBases.jl | 16 +- src/Polynomials/CompWiseTensorPolyBases.jl | 419 ++++++++++++++++++ src/Polynomials/LegendreBases.jl | 16 +- src/Polynomials/MonomialBases.jl | 16 +- src/Polynomials/NedelecPrebasisOnSimplex.jl | 244 ++++++++++ src/Polynomials/OldMonomialHelpers.jl | 13 + ...mialBases.jl => PCurlGradLegendreBases.jl} | 4 +- src/Polynomials/PCurlGradMonomialBases.jl | 4 +- src/Polynomials/PolynomialInterfaces.jl | 12 + src/Polynomials/Polynomials.jl | 58 +-- src/Polynomials/QGradMonomialBases.jl | 257 ----------- src/Polynomials/QSpaceBases.jl | 0 src/Polynomials/TensorPolynomialBases.jl | 80 ++-- src/ReferenceFEs/AWRefFEs.jl | 6 +- src/ReferenceFEs/BDMRefFEs.jl | 4 +- src/ReferenceFEs/BezierRefFEs.jl | 2 +- src/ReferenceFEs/CLagrangianRefFEs.jl | 2 +- src/ReferenceFEs/CRRefFEs.jl | 10 +- src/ReferenceFEs/MTWRefFEs.jl | 8 +- src/ReferenceFEs/NedelecRefFEs.jl | 16 +- src/ReferenceFEs/RaviartThomasRefFEs.jl | 12 +- src/ReferenceFEs/SerendipityRefFEs.jl | 2 +- src/ReferenceFEs/deprecated/BDMRefFEs.jl | 2 +- src/ReferenceFEs/deprecated/NedelecRefFEs.jl | 12 +- .../deprecated/RaviartThomasRefFEs.jl | 38 +- test/PolynomialsTests/BernsteinBasesTests.jl | 8 +- test/PolynomialsTests/ChangeBasisTests.jl | 2 +- test/PolynomialsTests/ModalC0BasesTests.jl | 4 +- test/PolynomialsTests/MonomialBasesTests.jl | 28 +- .../PCurlGradMonomialBasesTests.jl | 6 +- .../QCurlGradMonomialBasesTests.jl | 42 +- .../QGradMonomialBasesTests.jl | 55 ++- test/ReferenceFEsTests/BezierRefFEsTests.jl | 8 +- .../LagrangianRefFEsTests.jl | 2 +- test/ReferenceFEsTests/NedelecRefFEsTests.jl | 38 +- .../RaviartThomasRefFEsTests.jl | 23 +- .../ReferenceFEInterfacesTests.jl | 2 +- 40 files changed, 996 insertions(+), 498 deletions(-) create mode 100644 src/Polynomials/CompWiseTensorPolyBases.jl create mode 100644 src/Polynomials/NedelecPrebasisOnSimplex.jl rename src/Polynomials/{PCurlGradLegendrePolynomialBases.jl => PCurlGradLegendreBases.jl} (98%) create mode 100644 src/Polynomials/QSpaceBases.jl diff --git a/NEWS.md b/NEWS.md index 8072d90f5..a3e6ace1f 100644 --- a/NEWS.md +++ b/NEWS.md @@ -13,6 +13,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Changed - Existing Jacobi polynomial bases/spaces were renamed to Legendre (which they are). TODO +- `num_terms(b::Q/P(Curl)Grad...)` is deprecated, use `length(b)` instead +- `get_order(b::Q/P(Curl)Grad...)`, now returns the order of the basis, +1 that used to create it. ## [0.18.8] - 2024-12-2 diff --git a/src/Polynomials/BernsteinBases.jl b/src/Polynomials/BernsteinBases.jl index 2bde62a9e..10881dd76 100644 --- a/src/Polynomials/BernsteinBases.jl +++ b/src/Polynomials/BernsteinBases.jl @@ -15,18 +15,27 @@ Multivariate scalar' or `Multivalue`'d monomial basis, see [`TensorPolynomialBas const BernsteinBasis{D,V,K} = TensorPolynomialBasis{D,V,K,Bernstein} """ - BernsteinBasis{D}(::Type{V}, order::Int, terms::Vector) where {D,V} - BernsteinBasis{D}(::Type{V}, orders::Tuple [, filter::Function]) where {D,V} - BernsteinBasis{D}(::Type{V}, order::Int [, filter::Function]) where {D,V} + BernsteinBasis(::Val{D}, ::Type{V}, order::Int, terms::Vector) + BernsteinBasis(::Val{D}, ::Type{V}, orders::Tuple [, filter::Function]) + BernsteinBasis(::Val{D}, ::Type{V}, order::Int [, filter::Function]) -Convenience constructors of BernsteinBasis{D,V}. +Convenience constructors of [`BernsteinBasis`](@ref). """ -BernsteinBasis{D}(args...) where {D} = TensorPolynomialBasis{D}(Bernstein, args...) +BernsteinBasis(args...) = TensorPolynomialBasis(Bernstein, args...) + +QGradBernsteinBasis(args...) = QGradBasis(Bernstein, args...) +QCurlGradBernsteinBasis(args...) = QCurlGradBasis(Bernstein, args...) + # 1D evaluation implementation # TODO Optimize with in-place De Casteljau +""" + binoms(::Val{K}) + +Returns the tuple of binomials ( C₍ₖ₀₎, C₍ₖ₁₎, ..., C₍ₖₖ₎ ) +""" binoms(::Val{K}) where K = ntuple( i -> binomial(K,i-1), Val(K+1)) diff --git a/src/Polynomials/ChangeBasis.jl b/src/Polynomials/ChangeBasis.jl index d10427338..8380b365c 100644 --- a/src/Polynomials/ChangeBasis.jl +++ b/src/Polynomials/ChangeBasis.jl @@ -13,7 +13,7 @@ using Gridap.Polynomials D = 2 order = 1 -f = MonomialBasis{D}(Float64,order) +f = MonomialBasis(Val(D),Float64,order) nodes = Point{2,Int}[(0,0),(1,0),(0,1),(1,1)] change = inv(evaluate(f,nodes)) diff --git a/src/Polynomials/ChebyshevBases.jl b/src/Polynomials/ChebyshevBases.jl index 3b6b883b4..4489d4b01 100644 --- a/src/Polynomials/ChebyshevBases.jl +++ b/src/Polynomials/ChebyshevBases.jl @@ -16,16 +16,22 @@ Multivariate scalar' or `Multivalue`'d Chebyshev basis, see [`TensorPolynomialBa const ChebyshevBasis{D,V,kind,K} = TensorPolynomialBasis{D,V,K,Chebyshev{kind}} """ - ChebyshevBasis{D}(::Type{V}, order::Int, terms::Vector; kind=:T) where {D,V} - ChebyshevBasis{D}(::Type{V}, orders::Tuple [, filter::Function; kind=:T]) where {D,V} - ChebyshevBasis{D}(::Type{V}, order::Int [, filter::Function]; kind=:T) where {D,V} + ChebyshevBasis(::Val{D}, ::Type{V}, order::Int, terms::Vector; kind=:T) + ChebyshevBasis(::Val{D}, ::Type{V}, orders::Tuple [, filter::Function; kind=:T]) + ChebyshevBasis(::Val{D}, ::Type{V}, order::Int [, filter::Function; kind=:T]) -Convenience constructors of `ChebyshevBasis{D,V,kind}`. The default kind is the first kind (cf. [`Chebyshev`](@ref)). +Convenience constructors of [`ChebyshevBasis`](@ref). """ -ChebyshevBasis{D}(args...; kind=:T) where {D} = TensorPolynomialBasis{D}(Chebyshev{kind}, args...) +ChebyshevBasis(args...; kind=:T) = TensorPolynomialBasis(Chebyshev{kind}, args...) TensorPolynomialBasis{D}(::Type{Chebyshev{:U}}, args...) where D = @notimplemented "1D evaluation for second kind needed here" +QGradChebyshevBasis(args...; kind=:T) = QGradBasis(Chebyshev{kind}, args...) +#PGradChebyshevBasis(args...; kind=:T) = PGradBasis(Chebyshev{kind}, args...) +QCurlGradChebyshevBasis(args...; kind=:T) = QCurlGradBasis(Chebyshev{kind}, args...) +#PCurlGradChebyshevBasis(args...; kind=:T) = PCurlGradBasis(Chebyshev{kind}, args...) + + # 1D evaluation implementation function _evaluate_1d!( diff --git a/src/Polynomials/CompWiseTensorPolyBases.jl b/src/Polynomials/CompWiseTensorPolyBases.jl new file mode 100644 index 000000000..b44127edd --- /dev/null +++ b/src/Polynomials/CompWiseTensorPolyBases.jl @@ -0,0 +1,419 @@ +""" + CompWiseTensorPolyBasis{D,V,K,PT,L} <: PolynomialBasis{D,V,K,PT} + +"Polynomial basis of component wise tensor product spaces" + +Polynomial basis for a multivariate `MultiValue`'d polynomial space +`V`(𝕊¹, 𝕊², ..., 𝕊ᴸ) with `L`>1, where the scalar multivariate spaces 𝕊ˡ +(for 1 ≤ l ≤ `L`) of each (independent) component of `V` is the tensor product +of 1D ℙ¹ spaces of order oₗₙ for 1 ≤ n ≤ `D`, that is: + +𝕊¹ = ℙ¹ₒ(1,1) ⊗ … ⊗ ℙ¹ₒ(1,`D`)\\ +⋮\\ +𝕊ˡ = ⊗ₙ ℙ¹ₒ₍ₗ,ₙ₎\\ +⋮\\ +𝕊ᴸ = ℙ¹ₒ(`L`,1) ⊗ … ⊗ ℙ¹ₒ(`L`,`D`) + +The `L`×`D` matrix of orders o is given in the constructor, and `K` is the maximum of o. +""" +struct CompWiseTensorPolyBasis{D,V,K,PT,L} <: PolynomialBasis{D,V,K,PT} + orders::SMatrix{L,D,Int} + + function CompWiseTensorPolyBasis{D}( + ::Type{PT}, ::Type{V}, orders::SMatrix{L,D,Int}) where {D,PT<:Polynomial,V,L} + + msg1 = "The orders matrix rows number must match the number of independent components of V" + @check L == num_indep_components(V) msg1 + msg2 = "The Component Wise construction is useless for one component, use TensorPolynomialBasis instead" + @check L > 1 msg2 + @check D > 0 + K = maximum(orders) + + new{D,V,K,PT,L}(orders) + end +end + +Base.size(a::CompWiseTensorPolyBasis) = ( sum(prod.(eachrow(a.orders .+ 1))), ) + +""" + get_comp_terms(f::CompWiseTensorPolyBasis) + +Return a `NTuple{L,CartesianIndices{D}}` containing, for each component +1 ≤ l ≤ `L`, the Cartesian indices of the terms of the tensor product basis. +""" +function get_comp_terms(f::CompWiseTensorPolyBasis{D,V,K,PT,L}) where {D,V,K,PT,L} + _terms(l) = CartesianIndices( Tuple(f.orders[l,:] .+ 1) ) + comp_terms = ntuple(l -> _terms(l), Val(L)) + comp_terms::NTuple{L,CartesianIndices{D}} +end + +function return_cache( + f::CompWiseTensorPolyBasis{D,V}, + x::AbstractVector{<:Point}) where {D,V} + + @check D == length(eltype(x)) "Incorrect number of point components" + T = eltype(V) + np = length(x) + ndof = length(f) + ndof_1d = get_order(f) + 1 + r = CachedArray(zeros(V,(np,ndof))) + v = CachedArray(zeros(V,(ndof,))) + c = CachedArray(zeros(T,(D,ndof_1d))) + (r, v, c) +end + +function evaluate!( + cache, + f::CompWiseTensorPolyBasis{D,V,K,PT,L}, + x::AbstractVector{<:Point}) where {D,V,K,PT,L} + + r, v, c = cache + np = length(x) + ndof = length(f) + ndof_1d = get_order(f) + 1 # K + 1 + comp_terms = get_comp_terms(f) + setsize!(r,(np,ndof)) + setsize!(v,(ndof,)) + setsize!(c,(D,ndof_1d)) + for i in 1:np + @inbounds xi = x[i] + _evaluate_nd_cwtpb!(PT,v,xi,f.orders,comp_terms,c) + for j in 1:ndof + @inbounds r[i,j] = v[j] + end + end + r.array +end + +function return_cache( + fg::FieldGradientArray{1,<:CompWiseTensorPolyBasis{D,V}}, + x::AbstractVector{<:Point}) where {D,V} + + @check D == length(eltype(x)) "Incorrect number of point components" + T = eltype(V) + f = fg.fa + np = length(x) + ndof = length(f) + ndof_1d = get_order(f) + 1 + xi = testitem(x) + G = gradient_type(V,xi) + r = CachedArray(zeros(G,(np,ndof))) + v = CachedArray(zeros(G,(ndof,))) + c = CachedArray(zeros(T,(D,ndof_1d))) + g = CachedArray(zeros(T,(D,ndof_1d))) + (r, v, c, g) +end + +function evaluate!( + cache, + fg::FieldGradientArray{1,<:CompWiseTensorPolyBasis{D,V,K,PT}}, + x::AbstractVector{<:Point}) where {D,V,K,PT} + + f = fg.fa + r, v, c, g = cache + np = length(x) + ndof = length(f) + ndof_1d = get_order(f) + 1 + comp_terms = get_comp_terms(f) + setsize!(r,(np,ndof)) + setsize!(v,(ndof,)) + setsize!(c,(D,ndof_1d)) + setsize!(g,(D,ndof_1d)) + for i in 1:np + @inbounds xi = x[i] + _gradient_nd_cwtpb!(PT,v,xi,f.orders,comp_terms,c,g,V) + for j in 1:ndof + @inbounds r[i,j] = v[j] + end + end + r.array +end + +function return_cache( + fg::FieldGradientArray{2,<:CompWiseTensorPolyBasis{D,V}}, + x::AbstractVector{<:Point}) where {D,V} + + @check D == length(eltype(x)) "Incorrect number of point components" + T = eltype(V) + f = fg.fa + np = length(x) + ndof = length(f) + ndof_1d = get_order(f) + 1 + xi = testitem(x) + G = gradient_type(V,xi) + r = CachedArray(zeros(G,(np,ndof))) + v = CachedArray(zeros(G,(ndof,))) + c = CachedArray(zeros(T,(D,ndof_1d))) + g = CachedArray(zeros(T,(D,ndof_1d))) + h = CachedArray(zeros(T,(D,ndof_1d))) + (r, v, c, g, h) +end + +function evaluate!( + cache, + fg::FieldGradientArray{2,<:CompWiseTensorPolyBasis{D,V,K,PT}}, + x::AbstractVector{<:Point}) where {D,V,K,PT} + + f = fg.fa + r, v, c, g, h = cache + np = length(x) + ndof = length(f) + ndof_1d = get_order(f) + 1 + comp_terms = get_comp_terms(f) + setsize!(r,(np,ndof)) + setsize!(v,(ndof,)) + setsize!(c,(D,ndof_1d)) + setsize!(g,(D,ndof_1d)) + setsize!(h,(D,ndof_1d)) + for i in 1:np + @inbounds xi = x[i] + _hessian_nd_cwtpb!(PT,v,xi,f.orders,comp_terms,c,g,h,V) + for j in 1:ndof + @inbounds r[i,j] = v[j] + end + end + r.array +end + +# Helpers + +function _evaluate_nd_cwtpb!( + PT::Type{<:Polynomial}, + v::AbstractVector{V}, + x, + orders::SMatrix{L,D,Int}, + comp_terms::NTuple{L,CartesianIndices{D}}, + c::AbstractMatrix{T}) where {V,L,D,T} + + for d in 1:D + # for each coordinate d, the order at which the basis should be evaluated is + # the maximum d-order for any component l + Kd = Val(maximum(orders[:,d])) + _evaluate_1d!(PT,Kd,c,x,d) + end + + m = zero(Mutable(V)) + k = 1 + + for (l,terms) in enumerate(comp_terms) + for ci in terms + + s = one(T) + @inbounds for d in 1:D + s *= c[d,ci[d]] + end + + k = _comp_wize_set_value!(v,s,k,l) + end + end +end + +""" + _comp_wize_set_value!(v::AbstractVector{V},s::T,k,l) + +``` +v[k] = V(0, ..., 0, s, 0, ..., 0); return k+1 +``` + +where `s` is at position `l` in `V<:MultiValue`. +""" +function _comp_wize_set_value!(v::AbstractVector{V},s::T,k,l) where {V,T} + z = zero(T) + ncomp = num_indep_components(V) + v[k] = ntuple(i -> ifelse(i == l, s, z),Val(ncomp)) + return k + 1 +end + +function _gradient_nd_cwtpb!( + PT::Type{<:Polynomial}, + v::AbstractVector{G}, + x, + orders::SMatrix{L,D,Int}, + comp_terms::NTuple{L,CartesianIndices{D}}, + c::AbstractMatrix{T}, + g::AbstractMatrix{T}, + ::Type{V}) where {G,L,D,T,V} + + for d in 1:D + # for each spatial coordinate d, the order at which the basis should be + # evaluated is the maximum d-order for any component l + Kd = Val(maximum(orders[:,d])) + _derivatives_1d!(PT,Kd,(c,g),x,d) + end + + s = zero(Mutable(V)) + k = 1 + + for (l,terms) in enumerate(comp_terms) + for ci in terms + + for i in eachindex(s) + s[i] = one(T) + end + + for q in 1:D + for d in 1:D + if d != q + @inbounds s[q] *= c[d,ci[d]] + else + @inbounds s[q] *= g[d,ci[d]] + end + end + end + + k = _comp_wize_set_gradient!(v,s,k,Val(l),V) + end + end +end + +function _comp_wize_set_gradient!( + v::AbstractVector{G},s,k,l,::Type{<:Real}) where G + + @inbounds v[k] = s + k+1 +end + +@generated function _comp_wize_set_gradient!( + v::AbstractVector{G},s,k,::Val{l},::Type{V}) where {G,l,V} + + m = Array{String}(undef, size(G)) + N_val_dims = length(size(V)) + s_size = size(G)[1:end-N_val_dims] + + body = "T = eltype(s); z = zero(T);" + #for i in CartesianIndices(m) + # m[i] = "z" + #end + m .= "z" + for ci in CartesianIndices(s_size) + m[ci,l] = "(@inbounds s[$ci])" + end + body *= "@inbounds v[k] = ($(join(tuple(m...), ", ")));" + + body = Meta.parse(string("begin ",body," end")) + #println(body) + return Expr(:block, body ,:(return k+1)) +end + +@generated function _comp_wize_set_gradient!( + v::AbstractVector{G},s,k,::Type{V}) where {G,V<:AbstractSymTensorValue{D}} where D + + @notimplemented +end + +function _hessian_nd_cwtpb!( + PT::Type{<:Polynomial}, + v::AbstractVector{G}, + x, + orders::SMatrix{L,D,Int}, + comp_terms::NTuple{L,CartesianIndices{D}}, + c::AbstractMatrix{T}, + g::AbstractMatrix{T}, + h::AbstractMatrix{T}, + ::Type{V}) where {G,L,D,T,V} + + for d in 1:D + # for each spatial coordinate d, the order at which the basis should be + # evaluated is the maximum d-order for any component l + Kd = Val(maximum(orders[:,d])) + _derivatives_1d!(PT,Kd,(c,g,h),x,d) + end + + s = zero(Mutable(V)) + k = 1 + + for (l,terms) in enumerate(comp_terms) + for ci in terms + + for i in eachindex(s) + s[i] = one(T) + end + + for r in 1:D + for q in 1:D + for d in 1:D + 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 = _comp_wize_set_gradient!(v,s,k,l,V) + end + end +end + + +################################ +# Basis for Nedelec on D-cubes # +################################ + +""" + QGradBasis(::Type{PT}, ::Val{D}, ::Type{T}, order::Int) :: PolynomialBasis + +Return a basis of ℚ\\_order ⊕ x × (ℚ\\_order \\ ℚ\\_{order-1}), the polynomial space +for Nedelec elements on `D`-dimensonal cubes with scalar type `T`. + +The `order` argument has the following meaning: the curl of the functions in +this basis is in the ℚ space of degree `order`. + +`PT<:Polynomial` is the choice of scalar 1D polynomial basis. + +For more details, see [`CompWiseTensorPolyBasis`](@ref), as `QGradBasis` returns +an instance of `CompWiseTensorPolyBasis{D,VectorValue{D,T},order+1,PT,D}`. +""" +function QGradBasis(::Type{PT},::Val{D},::Type{T},order::Int) where {PT,D,T} + @check T<:Real "T needs to be <:Real since represents the type of the components of the vector value" + + V = VectorValue{D,T} + m = [ order + (i==j ? 0 : 1) for i in 1:D, j in 1:D ] + orders = SMatrix{D,D,Int}(m) + CompWiseTensorPolyBasis{D}(PT, V, orders) +end + +function QGradBasis(::Type{PT},::Val{1},::Type{T},order::Int) where {PT,T} + @check T<:Real "T needs to be <:Real since represents the type of the components of the vector value" + + V = VectorValue{1,T} + TensorPolynomialBasis(PT, Val(1), V, order+1) +end + + +####################################### +# Basis for Raviart-Thomas on D-cubes # +####################################### + +""" + QCurlGradBasis(::Type{PT}, ::Val{D}, ::Type{T}, order::Int) :: PolynomialBasis + +Return a basis of ℚ\\_order ⊕ x ⋅ (ℚ\\_order \\ ℚ\\_{order-1}), the polynomial space +for Raviart-Thomas elements on `D`-dimensonal cubes with scalar type `T`. + +The `order` argument has the following meaning: the divergence of the functions +in this basis is in the ℚ space of degree `order`. + +`PT<:Polynomial` is the choice of scalar 1D polynomial basis. + +For more details, see [`CompWiseTensorPolyBasis`](@ref), as `QCurlGradBasis` returns +an instance of `CompWiseTensorPolyBasis{D,VectorValue{D,T},order+1,PT,D}`. +""" +function QCurlGradBasis(::Type{PT},::Val{D},::Type{T},order::Int) where {PT,D,T} + @check T<:Real "T needs to be <:Real since represents the type of the components of the vector value" + + V = VectorValue{D,T} + m = [ order + (i==j ? 1 : 0) for i in 1:D, j in 1:D ] + orders = SMatrix{D,D,Int}(m) + CompWiseTensorPolyBasis{D}(PT, V, orders) +end + +function QCurlGradBasis(::Type{PT},::Val{1},::Type{T},order::Int) where {PT,T} + @check T<:Real "T needs to be <:Real since represents the type of the components of the vector value" + + V = VectorValue{1,T} + TensorPolynomialBasis(PT, Val(1), V, order+1) +end diff --git a/src/Polynomials/LegendreBases.jl b/src/Polynomials/LegendreBases.jl index ce3962eec..f42ded5e1 100644 --- a/src/Polynomials/LegendreBases.jl +++ b/src/Polynomials/LegendreBases.jl @@ -15,13 +15,19 @@ Multivariate scalar' or `Multivalue`'d Legendre basis, see [`TensorPolynomialBas const LegendreBasis{D,V,K} = TensorPolynomialBasis{D,V,K,Legendre} """ - LegendreBasis{D}(::Type{V}, order::Int, terms::Vector) where {D,V} - LegendreBasis{D}(::Type{V}, orders::Tuple [, filter::Function]) where {D,V} - LegendreBasis{D}(::Type{V}, order::Int [, filter::Function]) where {D,V} + LegendreBasis(::Val{D}, ::Type{V}, order::Int, terms::Vector) + LegendreBasis(::Val{D}, ::Type{V}, orders::Tuple [, filter::Function]) + LegendreBasis(::Val{D}, ::Type{V}, order::Int [, filter::Function]) -Convenience constructors of LegendreBasis{D,V}. +Convenience constructors of [`LegendreBasis`](@ref). """ -LegendreBasis{D}(args...) where {D} = TensorPolynomialBasis{D}(Legendre, args...) +LegendreBasis(args...) = TensorPolynomialBasis(Legendre, args...) + +QGradLegendreBasis(args...) = QGradBasis(Legendre, args...) +#PGradLegendreBasis(args...) = PGradBasis(Legendre, args...) +QCurlGradLegendreBasis(args...) = QCurlGradBasis(Legendre, args...) +#PCurlGradLegendreBasis(args...) = PCurlGradBasis(Legendre, args...) + # 1D evaluation implementation diff --git a/src/Polynomials/MonomialBases.jl b/src/Polynomials/MonomialBases.jl index c257da9d8..252cebf22 100644 --- a/src/Polynomials/MonomialBases.jl +++ b/src/Polynomials/MonomialBases.jl @@ -15,13 +15,19 @@ Multivariate scalar' or `Multivalue`'d monomial basis, see [`TensorPolynomialBas const MonomialBasis{D,V,K} = TensorPolynomialBasis{D,V,K,Monomial} """ - MonomialBasis{D}(::Type{V}, order::Int, terms::Vector) where {D,V} - MonomialBasis{D}(::Type{V}, orders::Tuple [, filter::Function]) where {D,V} - MonomialBasis{D}(::Type{V}, order::Int [, filter::Function]) where {D,V} + MonomialBasis(::Val{D}, ::Type{V}, order::Int, terms::Vector) + MonomialBasis(::Val{D}, ::Type{V}, orders::Tuple [, filter::Function]) + MonomialBasis(::Val{D}, ::Type{V}, order::Int [, filter::Function]) -Convenience constructors of MonomialBasis{D,V}. +Convenience constructors of [`MonomialBasis`](@ref). """ -MonomialBasis{D}(args...) where {D} = TensorPolynomialBasis{D}(Monomial, args...) +MonomialBasis(args...) = TensorPolynomialBasis(Monomial, args...) + +QGradMonomialBasis(args...) = QGradBasis(Monomial, args...) +#PGradMonomialBasis(args...) = PGradBasis(Monomial, args...) +QCurlGradMonomialBasis(args...) = QCurlGradBasis(Monomial, args...) +#PCurlGradMonomialBasis(args...) = PCurlGradBasis(Monomial, args...) + # 1D evaluation implementation diff --git a/src/Polynomials/NedelecPrebasisOnSimplex.jl b/src/Polynomials/NedelecPrebasisOnSimplex.jl new file mode 100644 index 000000000..895c4da1f --- /dev/null +++ b/src/Polynomials/NedelecPrebasisOnSimplex.jl @@ -0,0 +1,244 @@ +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{D}) where D + k = a.order+1 + n = div(k*prod(i->(k+i),2:D),factorial(D-1)) + (n,) +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 + +return_type(::NedelecPrebasisOnSimplex{D}) where {D} = VectorValue{D,Float64} + +function return_cache( + f::NedelecPrebasisOnSimplex{D},x::AbstractVector{<:Point}) where D + np = length(x) + ndofs = num_terms(f) + V = eltype(x) + a = zeros(V,(np,ndofs)) + k = f.order+1 + P = MonomialBasis(Val(D),VectorValue{D,Float64},k-1,(e,order)->sum(e)<=order) + cP = return_cache(P,x) + CachedArray(a), cP, P +end + +function evaluate!( + cache,f::NedelecPrebasisOnSimplex{3},x::AbstractVector{<:Point}) + ca,cP,P = cache + k = f.order+1 + np = length(x) + ndofs = num_terms(f) + ndofsP = length(P) + setsize!(ca,(np,ndofs)) + Px = evaluate!(cP,P,x) + a = ca.array + V = eltype(x) + T = eltype(V) + z = zero(T) + u = one(T) + for (ip,p) in enumerate(x) + for j in 1:ndofsP + a[ip,j] = Px[ip,j] + end + i = ndofsP + x1,x2,x3 = x[ip] + zp = zero(x1) + for β in 1:k + for α in 1:(k+1-β) + i += 1 + a[ip,i] = VectorValue( + -x1^(α-1)*x2^(k-α-β+2)*x3^(β-1), + x1^α*x2^(k-α-β+1)*x3^(β-1), + zp) + i += 1 + a[ip,i] = VectorValue( + -x1^(k-α-β+1)*x2^(β-1)*x3^α, + zp, + x1^(k-α-β+2)*x2^(β-1)*x3^(α-1)) + end + end + for γ in 1:k + i += 1 + a[ip,i] = VectorValue( + zp, + -x2^(γ-1)*x3^(k-γ+1), + x2^γ*x3^(k-γ)) + end + end + a +end + +function evaluate!( + cache,f::NedelecPrebasisOnSimplex{2},x::AbstractVector{<:Point}) + ca,cP,P = cache + k = f.order+1 + np = length(x) + ndofs = num_terms(f) + ndofsP = length(P) + setsize!(ca,(np,ndofs)) + a = ca.array + V = eltype(x) + T = eltype(V) + z = zero(T) + u = one(T) + Px = evaluate!(cP,P,x) + for (ip,p) in enumerate(x) + for j in 1:ndofsP + a[ip,j] = Px[ip,j] + end + i = ndofsP + x1,x2 = x[ip] + zp = zero(x1) + for α in 1:k + i += 1 + a[ip,i] = VectorValue(-x1^(α-1)*x2^(k-α+1),x1^α*x2^(k-α)) + end + #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 + np = length(x) + ndofs = num_terms(f) + xi = testitem(x) + V = eltype(x) + G = gradient_type(V,xi) + a = zeros(G,(np,ndofs)) + k = f.order+1 + mb = MonomialBasis(Val(D),VectorValue{D,Float64},k-1,(e,order)->sum(e)<=order) + P = Broadcasting(∇)(mb) + cP = return_cache(P,x) + CachedArray(a), cP, P +end + +function evaluate!( + cache, + g::FieldGradientArray{1,<:NedelecPrebasisOnSimplex{3}}, + x::AbstractVector{<:Point}) + ca,cP,P = cache + f = g.fa + k = f.order+1 + np = length(x) + ndofs = num_terms(f) + setsize!(ca,(np,ndofs)) + a = ca.array + fill!(a,zero(eltype(a))) + ndofsP = length(P) + Px = evaluate!(cP,P,x) + V = eltype(x) + T = eltype(V) + z = zero(T) + u = one(T) + for (ip,p) in enumerate(x) + for j in 1:ndofsP + a[ip,j] = Px[ip,j] + end + i = ndofsP + x1,x2,x3 = x[ip] + zp = zero(x1) + for β in 1:k + for α in 1:(k+1-β) + i += 1 + a[ip,i] = TensorValue( + #-x1^(α-1)*x2^(k-α-β+2)*x3^(β-1), + -(α-1)*_exp(x1,α-2)*x2^(k-α-β+2)*x3^(β-1), + -x1^(α-1)*(k-α-β+2)*_exp(x2,k-α-β+1)*x3^(β-1), + -x1^(α-1)*x2^(k-α-β+2)*(β-1)*_exp(x3,β-2), + #x1^α*x2^(k-α-β+1)*x3^(β-1), + α*_exp(x1,α-1)*x2^(k-α-β+1)*x3^(β-1), + x1^α*(k-α-β+1)*_exp(x2,k-α-β)*x3^(β-1), + x1^α*x2^(k-α-β+1)*(β-1)*_exp(x3,β-2), + #zp, + zp,zp,zp) + i += 1 + a[ip,i] = TensorValue( + #-x1^(k-α-β+1)*x2^(β-1)*x3^α, + -(k-α-β+1)*_exp(x1,k-α-β)*x2^(β-1)*x3^α, + -x1^(k-α-β+1)*(β-1)*_exp(x2,β-2)*x3^α, + -x1^(k-α-β+1)*x2^(β-1)*α*_exp(x3,α-1), + # zp + zp,zp,zp, + #x1^(k-α-β+2)*x2^(β-1)*x3^(α-1), + (k-α-β+2)*_exp(x1,k-α-β+1)*x2^(β-1)*x3^(α-1), + x1^(k-α-β+2)*(β-1)*_exp(x2,β-2)*x3^(α-1), + x1^(k-α-β+2)*x2^(β-1)*(α-1)*_exp(x3,α-2)) + end + end + for γ in 1:k + i += 1 + a[ip,i] = TensorValue( + #zp + zp,zp,zp, + #-x2^(γ-1)*x3^(k-γ+1), + -0*x2^(γ-1)*x3^(k-γ+1), + -(γ-1)*_exp(x2,γ-2)*x3^(k-γ+1), + -x2^(γ-1)*(k-γ+1)*_exp(x3,k-γ), + #x2^γ*x3^(k-γ), + 0*x2^γ*x3^(k-γ), + γ*_exp(x2,γ-1)*x3^(k-γ), + x2^γ*(k-γ)*_exp(x3,k-γ-1)) + end + #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 + +_exp(a,y) = y>0 ? a^y : one(a) + +function evaluate!( + cache, + g::FieldGradientArray{1,<:NedelecPrebasisOnSimplex{2}}, + x::AbstractVector{<:Point}) + f = g.fa + ca,cP,P = cache + k = f.order+1 + np = length(x) + ndofs = num_terms(f) + setsize!(ca,(np,ndofs)) + a = ca.array + fill!(a,zero(eltype(a))) + V = eltype(x) + T = eltype(V) + z = zero(T) + u = one(T) + ndofsP = length(P) + Px = evaluate!(cP,P,x) + for (ip,p) in enumerate(x) + for j in 1:ndofsP + a[ip,j] = Px[ip,j] + end + i = ndofsP + x1,x2 = x[ip] + zp = zero(x1) + for α in 1:k + i += 1 + a[ip,i] = TensorValue( + #-x1^(α-1)*x2^(k-α+1), + -(α-1)*_exp(x1,α-2)*x2^(k-α+1), + -x1^(α-1)*(k-α+1)*_exp(x2,k-α), + #x1^α*x2^(k-α), + α*_exp(x1,α-1)*x2^(k-α), + x1^α*(k-α)*_exp(x2,k-α-1)) + end + #a[ip,3] = TensorValue((z,-u, u,z)) + end + a +end + diff --git a/src/Polynomials/OldMonomialHelpers.jl b/src/Polynomials/OldMonomialHelpers.jl index ebcd9a827..413b24013 100644 --- a/src/Polynomials/OldMonomialHelpers.jl +++ b/src/Polynomials/OldMonomialHelpers.jl @@ -1,4 +1,17 @@ +function _prepare_perms(D) + perms = zeros(Int,D,D) + for j in 1:D + for d in j:D + perms[d,j] = d-j+1 + end + for d in 1:(j-1) + perms[d,j] = d+(D-j)+1 + end + end + perms +end + function _evaluate_nd!( v::AbstractVector{V}, x, diff --git a/src/Polynomials/PCurlGradLegendrePolynomialBases.jl b/src/Polynomials/PCurlGradLegendreBases.jl similarity index 98% rename from src/Polynomials/PCurlGradLegendrePolynomialBases.jl rename to src/Polynomials/PCurlGradLegendreBases.jl index 362cc3ec7..4cf568e13 100644 --- a/src/Polynomials/PCurlGradLegendrePolynomialBases.jl +++ b/src/Polynomials/PCurlGradLegendreBases.jl @@ -34,8 +34,8 @@ in this basis is in the P space of degree `order`. """ function PCurlGradLegendrePolynomialBasis{D}(::Type{T},order::Int) where {D,T} @check T<:Real "T needs to be <:Real since represents the type of the components of the vector value" - P_k = MonomialBasis{D}(T, order, _p_filter) - S_k = MonomialBasis{D}(T, order, _s_filter) + P_k = MonomialBasis(Val(D),T, order, _p_filter) + S_k = MonomialBasis(Val(D),T, order, _s_filter) pterms = P_k.terms sterms = S_k.terms perms = _prepare_perms(D) diff --git a/src/Polynomials/PCurlGradMonomialBases.jl b/src/Polynomials/PCurlGradMonomialBases.jl index 9099515c1..51453ab71 100644 --- a/src/Polynomials/PCurlGradMonomialBases.jl +++ b/src/Polynomials/PCurlGradMonomialBases.jl @@ -35,8 +35,8 @@ in this basis is in the P space of degree `order`. """ function PCurlGradMonomialBasis{D}(::Type{T},order::Int) where {D,T} @check T<:Real "T needs to be <:Real since represents the type of the components of the vector value" - P_k = MonomialBasis{D}(T, order, _p_filter) - S_k = MonomialBasis{D}(T, order, _s_filter) + P_k = MonomialBasis(Val(D), T, order, _p_filter) + S_k = MonomialBasis(Val(D), T, order, _s_filter) pterms = P_k.terms sterms = S_k.terms perms = _prepare_perms(D) diff --git a/src/Polynomials/PolynomialInterfaces.jl b/src/Polynomials/PolynomialInterfaces.jl index ada24a5ce..7af211995 100644 --- a/src/Polynomials/PolynomialInterfaces.jl +++ b/src/Polynomials/PolynomialInterfaces.jl @@ -27,6 +27,18 @@ The parameters are: """ abstract type PolynomialBasis{D,V,K,PT<:Polynomial} <: AbstractVector{PT} end +@inline Base.size(a::PolynomialBasis{D,V}) where {D,V} = @abstractmethod +@inline Base.getindex(a::PolynomialBasis{D,V,K,PT}, i::Integer) where {D,V,K,PT} = PT() +@inline Base.IndexStyle(::PolynomialBasis) = IndexLinear() +@inline return_type(::PolynomialBasis{D,V}) where {D,V} = V + +""" + get_order(b::PolynomialBasis{D,V,K) = K + +Return the maximum polynomial order in a dimension, or `0` in 0D. +""" +get_order(::PolynomialBasis{D,V,K}) where {D,V,K} = K + ############################### # 1D internal polynomial APIs # diff --git a/src/Polynomials/Polynomials.jl b/src/Polynomials/Polynomials.jl index b63c03785..9160ac29d 100644 --- a/src/Polynomials/Polynomials.jl +++ b/src/Polynomials/Polynomials.jl @@ -22,50 +22,58 @@ import Gridap.Fields: return_cache import Gridap.Arrays: return_type export Polynomial -export MonomialBasis -export QGradMonomialBasis -export QCurlGradMonomialBasis -export PCurlGradMonomialBasis -export ModalC0Basis -export LegendreBasis -export QGradLegendrePolynomialBasis -export QCurlGradLegendrePolynomialBasis -export PCurlGradLegendrePolynomialBasis -export ChebyshevBasis -export QGradChebyshevPolynomialBasis -export QCurlGradChebyshevPolynomialBasis -export BernsteinBasis +export isHierarchical +export PolynomialBasis +export TensorPolynomialBasis export get_exponents export get_order export get_orders export num_terms -export isHierarchical -include("PolynomialInterfaces.jl") +export MonomialBasis +export LegendreBasis +export ChebyshevBasis +export ModalC0Basis +export BernsteinBasis -include("TensorPolynomialBases.jl") +export QGradMonomialBasis +export QGradLegendreBasis +export QGradChebyshevBasis -include("MonomialBases.jl") +export QCurlGradMonomialBasis +export QCurlGradLegendreBasis +export QCurlGradChebyshevBasis -include("OldMonomialHelpers.jl") +export PCurlGradMonomialBasis +export PCurlGradLegendreBasis -include("QGradMonomialBases.jl") -include("QCurlGradMonomialBases.jl") +include("PolynomialInterfaces.jl") -include("PCurlGradMonomialBases.jl") +include("OldMonomialHelpers.jl") -include("ModalC0Bases.jl") +include("TensorPolynomialBases.jl") -include("LegendreBases.jl") +include("CompWiseTensorPolyBases.jl") -include("QGradLegendrePolynomialBases.jl") +include("MonomialBases.jl") -include("PCurlGradLegendrePolynomialBases.jl") +include("LegendreBases.jl") + +#include("JacobiBases.jl") include("ChebyshevBases.jl") +include("ModalC0Bases.jl") + include("BernsteinBases.jl") +#include("newfile.jl") +#include("PSpaceBases.jl") +include("PCurlGradMonomialBases.jl") +include("PCurlGradLegendreBases.jl") + +include("NedelecPrebasisOnSimplex.jl") + end # module diff --git a/src/Polynomials/QGradMonomialBases.jl b/src/Polynomials/QGradMonomialBases.jl index 4bc2b90b0..1bf953a1b 100644 --- a/src/Polynomials/QGradMonomialBases.jl +++ b/src/Polynomials/QGradMonomialBases.jl @@ -127,19 +127,6 @@ end _ndofs_qgrad(f::QGradMonomialBasis{D}) where D = D*(length(f.terms)) -function _prepare_perms(D) - perms = zeros(Int,D,D) - for j in 1:D - for d in j:D - perms[d,j] = d-j+1 - end - for d in 1:(j-1) - perms[d,j] = d+(D-j)+1 - end - end - perms -end - function _evaluate_nd_qgrad!( v::AbstractVector{V}, x, @@ -242,247 +229,3 @@ function _gradient_nd_qgrad!( 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{d}) where d - k = a.order+1 - n = div(k*prod(i->(k+i),2:d),factorial(d-1)) - (n,) -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 - -return_type(::NedelecPrebasisOnSimplex{D}) where {D} = VectorValue{D,Float64} - -function return_cache( - f::NedelecPrebasisOnSimplex{d},x::AbstractVector{<:Point}) where d - np = length(x) - ndofs = num_terms(f) - V = eltype(x) - a = zeros(V,(np,ndofs)) - k = f.order+1 - P = MonomialBasis{d}(VectorValue{d,Float64},k-1,(e,order)->sum(e)<=order) - cP = return_cache(P,x) - CachedArray(a), cP, P -end - -function evaluate!( - cache,f::NedelecPrebasisOnSimplex{3},x::AbstractVector{<:Point}) - ca,cP,P = cache - k = f.order+1 - np = length(x) - ndofs = num_terms(f) - ndofsP = length(P) - setsize!(ca,(np,ndofs)) - Px = evaluate!(cP,P,x) - a = ca.array - V = eltype(x) - T = eltype(V) - z = zero(T) - u = one(T) - for (ip,p) in enumerate(x) - for j in 1:ndofsP - a[ip,j] = Px[ip,j] - end - i = ndofsP - x1,x2,x3 = x[ip] - zp = zero(x1) - for β in 1:k - for α in 1:(k+1-β) - i += 1 - a[ip,i] = VectorValue( - -x1^(α-1)*x2^(k-α-β+2)*x3^(β-1), - x1^α*x2^(k-α-β+1)*x3^(β-1), - zp) - i += 1 - a[ip,i] = VectorValue( - -x1^(k-α-β+1)*x2^(β-1)*x3^α, - zp, - x1^(k-α-β+2)*x2^(β-1)*x3^(α-1)) - end - end - for γ in 1:k - i += 1 - a[ip,i] = VectorValue( - zp, - -x2^(γ-1)*x3^(k-γ+1), - x2^γ*x3^(k-γ)) - end - end - a -end - -function evaluate!( - cache,f::NedelecPrebasisOnSimplex{2},x::AbstractVector{<:Point}) - ca,cP,P = cache - k = f.order+1 - np = length(x) - ndofs = num_terms(f) - ndofsP = length(P) - setsize!(ca,(np,ndofs)) - a = ca.array - V = eltype(x) - T = eltype(V) - z = zero(T) - u = one(T) - Px = evaluate!(cP,P,x) - for (ip,p) in enumerate(x) - for j in 1:ndofsP - a[ip,j] = Px[ip,j] - end - i = ndofsP - x1,x2 = x[ip] - zp = zero(x1) - for α in 1:k - i += 1 - a[ip,i] = VectorValue(-x1^(α-1)*x2^(k-α+1),x1^α*x2^(k-α)) - end - #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 - np = length(x) - ndofs = num_terms(f) - xi = testitem(x) - V = eltype(x) - G = gradient_type(V,xi) - a = zeros(G,(np,ndofs)) - k = f.order+1 - mb = MonomialBasis{D}(VectorValue{D,Float64},k-1,(e,order)->sum(e)<=order) - P = Broadcasting(∇)(mb) - cP = return_cache(P,x) - CachedArray(a), cP, P -end - -function evaluate!( - cache, - g::FieldGradientArray{1,<:NedelecPrebasisOnSimplex{3}}, - x::AbstractVector{<:Point}) - ca,cP,P = cache - f = g.fa - k = f.order+1 - np = length(x) - ndofs = num_terms(f) - setsize!(ca,(np,ndofs)) - a = ca.array - fill!(a,zero(eltype(a))) - ndofsP = length(P) - Px = evaluate!(cP,P,x) - V = eltype(x) - T = eltype(V) - z = zero(T) - u = one(T) - for (ip,p) in enumerate(x) - for j in 1:ndofsP - a[ip,j] = Px[ip,j] - end - i = ndofsP - x1,x2,x3 = x[ip] - zp = zero(x1) - for β in 1:k - for α in 1:(k+1-β) - i += 1 - a[ip,i] = TensorValue( - #-x1^(α-1)*x2^(k-α-β+2)*x3^(β-1), - -(α-1)*_exp(x1,α-2)*x2^(k-α-β+2)*x3^(β-1), - -x1^(α-1)*(k-α-β+2)*_exp(x2,k-α-β+1)*x3^(β-1), - -x1^(α-1)*x2^(k-α-β+2)*(β-1)*_exp(x3,β-2), - #x1^α*x2^(k-α-β+1)*x3^(β-1), - α*_exp(x1,α-1)*x2^(k-α-β+1)*x3^(β-1), - x1^α*(k-α-β+1)*_exp(x2,k-α-β)*x3^(β-1), - x1^α*x2^(k-α-β+1)*(β-1)*_exp(x3,β-2), - #zp, - zp,zp,zp) - i += 1 - a[ip,i] = TensorValue( - #-x1^(k-α-β+1)*x2^(β-1)*x3^α, - -(k-α-β+1)*_exp(x1,k-α-β)*x2^(β-1)*x3^α, - -x1^(k-α-β+1)*(β-1)*_exp(x2,β-2)*x3^α, - -x1^(k-α-β+1)*x2^(β-1)*α*_exp(x3,α-1), - # zp - zp,zp,zp, - #x1^(k-α-β+2)*x2^(β-1)*x3^(α-1), - (k-α-β+2)*_exp(x1,k-α-β+1)*x2^(β-1)*x3^(α-1), - x1^(k-α-β+2)*(β-1)*_exp(x2,β-2)*x3^(α-1), - x1^(k-α-β+2)*x2^(β-1)*(α-1)*_exp(x3,α-2)) - end - end - for γ in 1:k - i += 1 - a[ip,i] = TensorValue( - #zp - zp,zp,zp, - #-x2^(γ-1)*x3^(k-γ+1), - -0*x2^(γ-1)*x3^(k-γ+1), - -(γ-1)*_exp(x2,γ-2)*x3^(k-γ+1), - -x2^(γ-1)*(k-γ+1)*_exp(x3,k-γ), - #x2^γ*x3^(k-γ), - 0*x2^γ*x3^(k-γ), - γ*_exp(x2,γ-1)*x3^(k-γ), - x2^γ*(k-γ)*_exp(x3,k-γ-1)) - end - #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 - -_exp(a,y) = y>0 ? a^y : one(a) - -function evaluate!( - cache, - g::FieldGradientArray{1,<:NedelecPrebasisOnSimplex{2}}, - x::AbstractVector{<:Point}) - f = g.fa - ca,cP,P = cache - k = f.order+1 - np = length(x) - ndofs = num_terms(f) - setsize!(ca,(np,ndofs)) - a = ca.array - fill!(a,zero(eltype(a))) - V = eltype(x) - T = eltype(V) - z = zero(T) - u = one(T) - ndofsP = length(P) - Px = evaluate!(cP,P,x) - for (ip,p) in enumerate(x) - for j in 1:ndofsP - a[ip,j] = Px[ip,j] - end - i = ndofsP - x1,x2 = x[ip] - zp = zero(x1) - for α in 1:k - i += 1 - a[ip,i] = TensorValue( - #-x1^(α-1)*x2^(k-α+1), - -(α-1)*_exp(x1,α-2)*x2^(k-α+1), - -x1^(α-1)*(k-α+1)*_exp(x2,k-α), - #x1^α*x2^(k-α), - α*_exp(x1,α-1)*x2^(k-α), - x1^α*(k-α)*_exp(x2,k-α-1)) - end - #a[ip,3] = TensorValue((z,-u, u,z)) - end - a -end - diff --git a/src/Polynomials/QSpaceBases.jl b/src/Polynomials/QSpaceBases.jl new file mode 100644 index 000000000..e69de29bb diff --git a/src/Polynomials/TensorPolynomialBases.jl b/src/Polynomials/TensorPolynomialBases.jl index 8cc34a101..5fcceed27 100644 --- a/src/Polynomials/TensorPolynomialBases.jl +++ b/src/Polynomials/TensorPolynomialBases.jl @@ -46,32 +46,41 @@ struct TensorPolynomialBasis{D,V,K,PT} <: PolynomialBasis{D,V,K,PT} terms::Vector{CartesianIndex{D}}) where {D,V,PT<:Polynomial} K = maximum(orders; init=0) - @check all( term -> (maximum(Tuple(term), init=0) <= K+1), terms) "Some term contain a higher index than the maximum degree + 1." + msg = "Some term contain a higher index than the maximum degree + 1." + @check all( term -> (maximum(Tuple(term), init=0) <= K+1), terms) msg new{D,V,K,PT}(orders,terms) end end @inline Base.size(a::TensorPolynomialBasis{D,V}) where {D,V} = (length(a.terms)*num_indep_components(V),) -@inline Base.getindex(a::TensorPolynomialBasis{D,V,K,PT}, i::Integer) where {D,V,K,PT} = PT() -@inline Base.IndexStyle(::TensorPolynomialBasis) = IndexLinear() + +function TensorPolynomialBasis( + ::Type{PT}, + ::Val{D}, + ::Type{V}, + orders::NTuple{D,Int}, + terms::Vector{CartesianIndex{D}}) where {PT<:Polynomial,D,V} + + TensorPolynomialBasis{D}(PT,V,orders,terms) +end """ - TensorPolynomialBasis{D}(::Type{PT}, ::Type{V}, orders::Tuple [, filter::Function]) + TensorPolynomialBasis(::Type{PT}, ::Val{D}, ::Type{V}, orders::Tuple [, filter::Function]) This version of the constructor allows to pass a tuple `orders` containing the polynomial order to be used in each of the `D` dimensions in order to construct an anisotropic tensor-product space. """ -function TensorPolynomialBasis{D}( - ::Type{PT}, ::Type{V}, orders::NTuple{D,Int}, filter::Function=_q_filter - ) where {D,V,PT} +function TensorPolynomialBasis( + ::Type{PT}, ::Val{D}, ::Type{V}, orders::NTuple{D,Int}, filter::Function=_q_filter + ) where {PT,D,V} terms = _define_terms(filter, orders) TensorPolynomialBasis{D}(PT,V,orders,terms) end """ - TensorPolynomialBasis{D}(::Type{V}, order::Int [, filter::Function]) where {D,V} + TensorPolynomialBasis(::Type{V}, ::Val{D}, order::Int [, filter::Function]) where {D,V} Returns an instance of `TensorPolynomialBasis` representing a multivariate polynomial basis in `D` dimensions, of polynomial degree `order`, whose value is represented by the type `V`. @@ -95,11 +104,11 @@ are used to select well known polynomial spaces - "Serendipity" space: `(e,order) -> sum( [ i for i in e if i>1 ] ) <= order` """ -function TensorPolynomialBasis{D}( - ::Type{PT}, ::Type{V}, order::Int, filter::Function=_q_filter) where {D,V,PT} +function TensorPolynomialBasis( + ::Type{PT}, VD::Val{D}, ::Type{V}, order::Int, filter::Function=_q_filter) where {PT,D,V} - orders = tfill(order,Val{D}()) - TensorPolynomialBasis{D}(PT,V,orders,filter) + orders = tfill(order,VD) + TensorPolynomialBasis(PT,Val(D),V,orders,filter) end # API @@ -114,7 +123,7 @@ Get a vector of tuples with the exponents of all the terms in the basis. ```jldoctest using Gridap.Polynomials -b = MonomialBasis{2}(Float64,2) +b = MonomialBasis(Val(2),Float64,2) exponents = get_exponents(b) @@ -129,13 +138,6 @@ function get_exponents(b::TensorPolynomialBasis) [Tuple(t) .- indexbase for t in b.terms] end -""" - get_order(b::TensorPolynomialBasis{D,V,K) = K - -Return the maximum polynomial order in a dimension, or `0` in 0D. -""" -get_order(::TensorPolynomialBasis{D,V,K}) where {D,V,K} = K - """ get_orders(b::TensorPolynomialBasis) @@ -145,8 +147,6 @@ function get_orders(b::TensorPolynomialBasis) b.orders end -return_type(::TensorPolynomialBasis{D,V}) where {D,V} = V - ########### # Helpers # @@ -180,16 +180,17 @@ end function _return_cache(f::TensorPolynomialBasis{D},x,::Type{G},N_deriv) where {D,G} @assert D == length(eltype(x)) "Incorrect number of point components" + T = eltype(G) np = length(x) ndof = length(f) - n = get_order(f) + 1 + ndof_1d = get_order(f) + 1 # Cache for the returned array r = CachedArray(zeros(G,(np,ndof))) # Cache for basis functions at one point x[i] v = CachedArray(zeros(G,(ndof,))) # Cache for the 1D basis function values in each dimension (to be # tensor-producted), and of their 1D N_deriv'th derivatives - t = ntuple( _ -> CachedArray(zeros(eltype(G),(D,n))), N_deriv) + t = ntuple( _ -> CachedArray(zeros(T,(D,ndof_1d ))), N_deriv) (r, v, t...) end @@ -234,7 +235,7 @@ function evaluate!( f = fg.fa r, v, c, g = cache - z = zero(Mutable(VectorValue{D,eltype(V)})) + s = zero(Mutable(VectorValue{D,eltype(V)})) np = length(x) ndof = length(f) ndof_1d = get_order(f) + 1 # K+1 @@ -245,7 +246,7 @@ function evaluate!( for i in 1:np # TODO Shouldn't we avoid accessing here ? (pass x and i) @inbounds xi = x[i] - _tensorial_gradient_nd!(PT,v,xi,f.orders,f.terms,c,g,z,V) + _tensorial_gradient_nd!(PT,v,xi,f.orders,f.terms,c,g,s,V) for j in 1:ndof # TODO Shouldn't we assign in place in _tensorial_gradient_nd instead of copying everything? @inbounds r[i,j] = v[j] @@ -306,11 +307,10 @@ function _tensorial_evaluate_nd!( _evaluate_1d!(PT,Kd,c,x,d) end - o = one(T) k = 1 for ci in terms - s = o + s = one(T) for d in 1:D @inbounds s *= c[d,ci[d]] end @@ -322,7 +322,7 @@ end """ _tensorial_set_value!(v::AbstractVector{<:Real},s,k) -v[k] = s; return k+1 +v[k] = s; return k+1 """ function _tensorial_set_value!(v::AbstractVector{<:Real},s,k) @inbounds v[k] = s @@ -332,16 +332,18 @@ end """ _tensorial_set_value!(v::AbstractVector{V},s::T,k) +``` v[k] = V(s, 0, ..., 0) v[k+1] = V(0, s, ..., 0) ⋮ v[k+N] = V(0, ..., 0, s) return k+N +``` -where N is the number of independent components of V. +where `N` is the number of independent components of `V<:MultiValue`. This means that the basis has the same polynomial space in each component, so it -is tensorial relative to the V components. +is tensorial relative to the `V` components. """ function _tensorial_set_value!(v::AbstractVector{V},s::T,k) where {V,T} ncomp = num_indep_components(V) @@ -361,7 +363,7 @@ function _tensorial_gradient_nd!( terms::AbstractVector{CartesianIndex{D}}, c::AbstractMatrix{T}, g::AbstractMatrix{T}, - z::AbstractVector{T}, + s::AbstractVector{T}, ::Type{V}) where {G,D,T,V} for d in 1:D @@ -369,15 +371,13 @@ function _tensorial_gradient_nd!( _derivatives_1d!(PT,Kd,(c,g),x,d) end - o = one(T) k = 1 - for ci in terms - s = z for i in eachindex(s) - @inbounds s[i] = o + @inbounds s[i] = one(T) end + for q in 1:D for d in 1:D if d != q @@ -399,6 +399,7 @@ function _tensorial_set_gradient!( k+1 end +# TODO comment @generated function _tensorial_set_gradient!( v::AbstractVector{G},s,k,::Type{V}) where {G,V} # Git blame me for readable non-generated version @@ -491,16 +492,15 @@ function _tensorial_hessian_nd!( _derivatives_1d!(PT,Kd,(c,g,h),x,d) end - z = zero(Mutable(TensorValue{D,D,T})) - o = one(T) + s = zero(Mutable(TensorValue{D,D,T})) k = 1 for ci in terms - s = z for i in eachindex(s) - @inbounds s[i] = o + @inbounds s[i] = one(T) end + for r in 1:D for q in 1:D for d in 1:D diff --git a/src/ReferenceFEs/AWRefFEs.jl b/src/ReferenceFEs/AWRefFEs.jl index 2f6850afb..0a3a3f508 100644 --- a/src/ReferenceFEs/AWRefFEs.jl +++ b/src/ReferenceFEs/AWRefFEs.jl @@ -17,10 +17,10 @@ References: function ArnoldWintherRefFE(::Type{T},p::Polytope,order::Integer) where T @assert p == TRI "ArnoldWinther Reference FE only defined for TRIangles" conforming = true # TODO: Make this an argument - + VT = SymTensorValue{2,T} - prebasis = MonomialBasis{2}(VT,3,Polynomials._p_filter) - fb = MonomialBasis{D-1}(T,0,Polynomials._p_filter) + prebasis = MonomialBasis(Val(2),VT,3,Polynomials._p_filter) + fb = MonomialBasis(Val(D-1),T,0,Polynomials._p_filter) cb = map(constant_field,component_basis(VT)) function cmom(φ,μ,ds) # Cell and Node moment function: σ_K(φ,μ) = ∫(φ:μ)dK diff --git a/src/ReferenceFEs/BDMRefFEs.jl b/src/ReferenceFEs/BDMRefFEs.jl index 984bb0bba..a308b3076 100644 --- a/src/ReferenceFEs/BDMRefFEs.jl +++ b/src/ReferenceFEs/BDMRefFEs.jl @@ -13,8 +13,8 @@ function BDMRefFE(::Type{T},p::Polytope,order::Integer) where T D = num_dims(p) if is_simplex(p) - prebasis = MonomialBasis{D}(VectorValue{D,T},order,Polynomials._p_filter) - fb = MonomialBasis{D-1}(T,order,Polynomials._p_filter) + prebasis = MonomialBasis(Val(D),VectorValue{D,T},order,Polynomials._p_filter) + fb = MonomialBasis(Val(D-1),T,order,Polynomials._p_filter) cb = Polynomials.NedelecPrebasisOnSimplex{D}(order-2) else @notimplemented "BDM Reference FE only available for simplices" diff --git a/src/ReferenceFEs/BezierRefFEs.jl b/src/ReferenceFEs/BezierRefFEs.jl index 4620d5620..0273b5429 100644 --- a/src/ReferenceFEs/BezierRefFEs.jl +++ b/src/ReferenceFEs/BezierRefFEs.jl @@ -94,7 +94,7 @@ end function compute_node_to_bezier_node(prebasis::MonomialBasis{D,T},nodes) where {D,T} orders = get_orders(prebasis) terms = _coords_to_terms(nodes,orders) - _prebasis = MonomialBasis{D}(T,orders,terms) + _prebasis = MonomialBasis(Val(D),T,orders,terms) _exps = get_exponents(_prebasis) exps = get_exponents(prebasis) [ findfirst( isequal(i), exps) for i in _exps ] diff --git a/src/ReferenceFEs/CLagrangianRefFEs.jl b/src/ReferenceFEs/CLagrangianRefFEs.jl index a4eb860ad..56d1ce99b 100644 --- a/src/ReferenceFEs/CLagrangianRefFEs.jl +++ b/src/ReferenceFEs/CLagrangianRefFEs.jl @@ -594,7 +594,7 @@ end function compute_monomial_basis(::Type{T},p::ExtrusionPolytope{D},orders) where {D,T} extrusion = Tuple(p.extrusion) terms = _monomial_terms(extrusion,orders) - MonomialBasis{D}(T,orders,terms) + MonomialBasis(Val(D),T,orders,terms) end function compute_own_nodes(p::ExtrusionPolytope{D},orders) where D diff --git a/src/ReferenceFEs/CRRefFEs.jl b/src/ReferenceFEs/CRRefFEs.jl index 261fcd64b..c48874ac1 100644 --- a/src/ReferenceFEs/CRRefFEs.jl +++ b/src/ReferenceFEs/CRRefFEs.jl @@ -1,4 +1,4 @@ -struct CR <: ReferenceFEName end +struct CR <: ReferenceFEName end const cr = CR() """ @@ -8,19 +8,19 @@ The `order` argument has the following meaning: the divergence of the functions is in the P space of degree `order-1`. """ -function CRRefFE(::Type{T},p::Polytope,order::Integer) where T +function CRRefFE(::Type{T},p::Polytope,order::Integer) where T D = num_dims(p) if is_simplex(p) && order == 1 - prebasis = MonomialBasis{D}(T,order,Polynomials._p_filter) - fb = MonomialBasis{D-1}(T,0,Polynomials._p_filter) + prebasis = MonomialBasis(Val(D),T,order,Polynomials._p_filter) + fb = MonomialBasis(Val(D-1),T,0,Polynomials._p_filter) else @notimplemented "CR Reference FE only available for simplices and lowest order" end function fmom(φ,μ,ds) # Face moment function : σ_F(φ,μ) = 1/|F| ( ∫((φ)*μ)dF ) D = num_dims(ds.cpoly) - facet_measure = get_facet_measure(ds.cpoly, D-1) + facet_measure = get_facet_measure(ds.cpoly, D-1) facet_measure_1 = Gridap.Fields.ConstantField(1 / facet_measure[ds.face]) φμ = Broadcasting(Operation(⋅))(φ,μ) Broadcasting(Operation(*))(φμ,facet_measure_1) diff --git a/src/ReferenceFEs/MTWRefFEs.jl b/src/ReferenceFEs/MTWRefFEs.jl index c334c10a1..e02407e35 100644 --- a/src/ReferenceFEs/MTWRefFEs.jl +++ b/src/ReferenceFEs/MTWRefFEs.jl @@ -7,7 +7,7 @@ MardalTaiWintherRefFE(::Type{et},p::Polytope,order::Integer) where et Mardal-Tai-Winther reference finite element. -References: +References: - `A Robust Finite Element Method for Darcy-Stokes Flow`, Mardal, Tai and Winther (2002) - `Transformations for Piola-mapped elements`, Aznaran, Farrell and Kirby (2022) @@ -19,9 +19,9 @@ function MardalTaiWintherRefFE(::Type{T},p::Polytope,order::Integer) where T @asset order == 3 "MardalTaiWinther Reference FE is by definition of order 3" # TODO: We should just not allow this to be an argument - prebasis = MonomialBasis{D}(VectorValue{D,T},3,Polynomials._p_filter) - eb = MonomialBasis{1}(T,0,Polynomials._p_filter) - fb = MonomialBasis{D-1}(T,1,Polynomials._p_filter) + prebasis = MonomialBasis(Val(D),VectorValue{D,T},3,Polynomials._p_filter) + eb = MonomialBasis(Val(1),T,0,Polynomials._p_filter) + fb = MonomialBasis(Val(D-1),T,1,Polynomials._p_filter) function emom(φ,μ,ds) # Edge moment function: σ_K(φ,μ) = ∫((φ⋅t)*μ)dK t = get_edge_tangent(ds) diff --git a/src/ReferenceFEs/NedelecRefFEs.jl b/src/ReferenceFEs/NedelecRefFEs.jl index 7cdbb98e5..5243b6e1a 100644 --- a/src/ReferenceFEs/NedelecRefFEs.jl +++ b/src/ReferenceFEs/NedelecRefFEs.jl @@ -14,15 +14,15 @@ function NedelecRefFE(::Type{et},p::Polytope,order::Integer) where et D = num_dims(p) if is_n_cube(p) - prebasis = QGradMonomialBasis{D}(et,order) # Prebasis - eb = MonomialBasis{1}(et,order) # Edge basis - fb = QGradMonomialBasis{D-1}(et,order-1) # Face basis - cb = QCurlGradMonomialBasis{D}(et,order-1) # Cell basis + prebasis = QGradMonomialBasis(Val(D),et,order) # Prebasis + eb = MonomialBasis(Val(1),et,order) # Edge basis + fb = QGradMonomialBasis(Val(D-1),et,order-1) # Face basis + cb = QCurlGradMonomialBasis(Val(D),et,order-1) # Cell basis elseif is_simplex(p) prebasis = Polynomials.NedelecPrebasisOnSimplex{D}(order) # Prebasis - eb = MonomialBasis{1}(et,order) # Edge basis - fb = MonomialBasis{D-1}(VectorValue{D-1,et},order-1,Polynomials._p_filter) # Face basis - cb = MonomialBasis{D}(VectorValue{D,et},order-D+1,Polynomials._p_filter) # Cell basis + eb = MonomialBasis(Val(1),et,order) # Edge basis + fb = MonomialBasis(Val(D-1),VectorValue{D-1,et},order-1,Polynomials._p_filter) # Face basis + cb = MonomialBasis(Val(D),VectorValue{D,et},order-D+1,Polynomials._p_filter) # Cell basis else @unreachable "Nedelec Reference FE only implemented for n-cubes and simplices" end @@ -100,7 +100,7 @@ function get_face_dofs(reffe::GenericRefFE{Nedelec,Dc}) where Dc for dof in cface_own_dofs push!(face_dofs[face],dof) end - end + end end for dof in face_own_dofs[face] push!(face_dofs[face],dof) diff --git a/src/ReferenceFEs/RaviartThomasRefFEs.jl b/src/ReferenceFEs/RaviartThomasRefFEs.jl index af04aa2c0..b89ff738d 100644 --- a/src/ReferenceFEs/RaviartThomasRefFEs.jl +++ b/src/ReferenceFEs/RaviartThomasRefFEs.jl @@ -18,13 +18,13 @@ function RaviartThomasRefFE( ) where {T,D} if is_n_cube(p) - prebasis = QCurlGradLegendrePolynomialBasis{D}(T,order) # Prebasis - cb = QGradLegendrePolynomialBasis{D}(T,order-1) # Cell basis - fb = LegendreBasis{D-1}(T,order,Polynomials._q_filter) # Face basis + prebasis = QCurlGradLegendreBasis(Val(D),T,order) # Prebasis + cb = QGradLegendreBasis(Val(D),T,order-1) # Cell basis + fb = LegendreBasis(Val(D-1),T,order,Polynomials._q_filter) # Face basis elseif is_simplex(p) prebasis = PCurlGradMonomialBasis{D}(T,order) # Prebasis - cb = LegendreBasis{D}(VectorValue{D,T},order-1,Polynomials._p_filter) # Cell basis - fb = LegendreBasis{D-1}(T,order,Polynomials._p_filter) # Face basis + cb = LegendreBasis(Val(D),VectorValue{D,T},order-1,Polynomials._p_filter) # Cell basis + fb = LegendreBasis(Val(D-1),T,order,Polynomials._p_filter) # Face basis else @notimplemented "Raviart-Thomas Reference FE only available for cubes and simplices" end @@ -86,7 +86,7 @@ end function compute_legendre_basis(::Type{T},p::ExtrusionPolytope{D},orders) where {D,T} extrusion = Tuple(p.extrusion) terms = _monomial_terms(extrusion,orders) - LegendreBasis{D}(T,orders,terms) + LegendreBasis(Val(D),T,orders,terms) end # ContraVariantPiolaMap diff --git a/src/ReferenceFEs/SerendipityRefFEs.jl b/src/ReferenceFEs/SerendipityRefFEs.jl index b087a04eb..3fe15c0a7 100644 --- a/src/ReferenceFEs/SerendipityRefFEs.jl +++ b/src/ReferenceFEs/SerendipityRefFEs.jl @@ -92,7 +92,7 @@ function _ser_filter(e,order) end function compute_monomial_basis(::Type{T},p::SerendipityPolytope{D},orders) where {T,D} - MonomialBasis{D}(T,orders,_ser_filter) + MonomialBasis(Val(D),T,orders,_ser_filter) end function compute_own_nodes(p::SerendipityPolytope{0},orders) diff --git a/src/ReferenceFEs/deprecated/BDMRefFEs.jl b/src/ReferenceFEs/deprecated/BDMRefFEs.jl index 81d3bd9af..001eb1d15 100644 --- a/src/ReferenceFEs/deprecated/BDMRefFEs.jl +++ b/src/ReferenceFEs/deprecated/BDMRefFEs.jl @@ -170,7 +170,7 @@ function Conformity(reffe::GenericRefFE{BDM},sym::Symbol) # Cell moments, i.e., M(C)_{ab} = q_C^a(xgp_C^b) w_C^b ⋅ () if is_simplex(p) T = VectorValue{num_dims(p),et} - # cbasis = GradMonomialBasis{num_dims(p)}(T,order-1) + # cbasis = GradMonomialBasis(Val(num_dims(p)),T,order-1) cbasis = Polynomials.NedelecPrebasisOnSimplex{num_dims(p)}(order-2) else @notimplemented diff --git a/src/ReferenceFEs/deprecated/NedelecRefFEs.jl b/src/ReferenceFEs/deprecated/NedelecRefFEs.jl index 81924f856..fb28eac7a 100644 --- a/src/ReferenceFEs/deprecated/NedelecRefFEs.jl +++ b/src/ReferenceFEs/deprecated/NedelecRefFEs.jl @@ -16,7 +16,7 @@ function NedelecRefFE(::Type{et},p::Polytope,order::Integer) where et D = num_dims(p) if is_n_cube(p) - prebasis = QGradMonomialBasis{D}(et,order) + prebasis = QGradMonomialBasis(Val(D),et,order) elseif is_simplex(p) prebasis = Polynomials.NedelecPrebasisOnSimplex{D}(order) else @@ -96,7 +96,7 @@ function get_face_dofs(reffe::GenericRefFE{Nedelec,Dc}) where Dc for dof in cface_own_dofs push!(face_dofs[face],dof) end - end + end end for dof in face_own_dofs[face] push!(face_dofs[face],dof) @@ -204,7 +204,7 @@ function _Nedelec_face_values(p,et,order) c_fips, fcips, fwips = _nfaces_evaluation_points_weights(p, fgeomap, fips, wips) # Face moments, i.e., M(Fi)_{ab} = w_Fi^b q_RF^a(xgp_RFi^b) (n_Fi × ()) - fshfs = QGradMonomialBasis{num_dims(fp)}(et,order-1) + fshfs = QGradMonomialBasis(Val(num_dims(fp)),et,order-1) fmoments = _Nedelec_face_moments(p, fshfs, c_fips, fcips, fwips) @@ -250,7 +250,7 @@ function _Nedelec_face_values_simplex(p,et,order) c_fips, fcips, fwips, fJtips = _nfaces_evaluation_points_weights_with_jac(p, fgeomap, fips, wips) Df = num_dims(fp) - fshfs = MonomialBasis{Df}(VectorValue{Df,et},order-1,(e,k)->sum(e)<=k) + fshfs = MonomialBasis(Val(Df),VectorValue{Df,et},order-1,(e,k)->sum(e)<=k) fmoments = _Nedelec_face_moments_simplex(p, fshfs, c_fips, fcips, fwips, fJtips) @@ -296,10 +296,10 @@ function _Nedelec_cell_values(p,et,order) # Cell moments, i.e., M(C)_{ab} = q_C^a(xgp_C^b) w_C^b ⋅ () if is_n_cube(p) - cbasis = QCurlGradMonomialBasis{num_dims(p)}(et,order-1) + cbasis = QCurlGradMonomialBasis(Val(num_dims(p)),et,order-1) else D = num_dims(p) - cbasis = MonomialBasis{D}(VectorValue{D,et},order-D+1,(e,k)->sum(e)<=k) + cbasis = MonomialBasis(Val(D),VectorValue{D,et},order-D+1,(e,k)->sum(e)<=k) end cmoments = _Nedelec_cell_moments(p, cbasis, ccips, cwips ) diff --git a/src/ReferenceFEs/deprecated/RaviartThomasRefFEs.jl b/src/ReferenceFEs/deprecated/RaviartThomasRefFEs.jl index c61538800..18eeb8e60 100644 --- a/src/ReferenceFEs/deprecated/RaviartThomasRefFEs.jl +++ b/src/ReferenceFEs/deprecated/RaviartThomasRefFEs.jl @@ -21,15 +21,17 @@ function RaviartThomasRefFE( D = num_dims(p) if is_n_cube(p) && basis_type == :monomial - prebasis = QCurlGradMonomialBasis{D}(et,order) + prebasis = QCurlGradMonomialBasis(Val(D),et,order) elseif is_simplex(p) && basis_type == :monomial prebasis = PCurlGradMonomialBasis{D}(et,order) + #prebasis = PCurlGradMonomialBasis(Val(D),et,order) elseif is_n_cube(p) && basis_type == :legendre - prebasis = QCurlGradLegendrePolynomialBasis{D}(et,order) + prebasis = QCurlGradLegendreBasis(Val(D),et,order) elseif is_simplex(p) && basis_type == :legendre - prebasis = PCurlGradLegendrePolynomialBasis{D}(et,order) + prebasis = PCurlGradLegendreBasis{D}(et,order) + #prebasis = PCurlGradLegendreBasis(Val(D),et,order) elseif is_n_cube(p) && basis_type == :chebyshev - prebasis = QCurlGradChebyshevPolynomialBasis{D}(et,order) + prebasis = QCurlGradChebyshevBasis(Val(D),et,order) else @notimplemented "H(div) Reference FE only available for cubes and simplices" end @@ -196,8 +198,8 @@ function _RT_face_values(p,et,order,phi) # Moments (fmoments) # The RT prebasis is expressed in terms of shape function #fshfs = MonomialBasis(et,fp,order) - fshfs = LegendreBasis(et,fp,order) - #fshfs = ChebyshevBasis(et,fp,order) + fshfs = legendreBasis(et,fp,order) + #fshfs = chebyshevBasis(et,fp,order) #fshfs = get_shapefuns(LagrangianRefFE(et,fp,order)) # Face moments, i.e., M(Fi)_{ab} = q_RF^a(xgp_RFi^b) w_Fi^b n_Fi ⋅ () @@ -206,30 +208,30 @@ function _RT_face_values(p,et,order,phi) return fcips, fmoments end -function LegendreBasis(::Type{T},p::Polytope,orders) where T +function legendreBasis(::Type{T},p::Polytope,orders) where T compute_legendre_basis(T,p,orders) end -function LegendreBasis(::Type{T},p::Polytope{D},order::Int) where {D,T} +function legendreBasis(::Type{T},p::Polytope{D},order::Int) where {D,T} orders = tfill(order,Val{D}()) - LegendreBasis(T,p,orders) + legendreBasis(T,p,orders) end function compute_legendre_basis(::Type{T},p::ExtrusionPolytope{D},orders) where {D,T} extrusion = Tuple(p.extrusion) terms = _monomial_terms(extrusion,orders) - LegendreBasis{D}(T,orders,terms) + LegendreBasis(Val(D),T,orders,terms) end -function ChebyshevBasis(::Type{T},p::Polytope,orders) where T +function chebyshevBasis(::Type{T},p::Polytope,orders) where T compute_chebyshev_basis(T,p,orders) end -function ChebyshevBasis(::Type{T},p::Polytope{D},order::Int) where {D,T} +function chebyshevBasis(::Type{T},p::Polytope{D},order::Int) where {D,T} orders = tfill(order,Val{D}()) - ChebyshevBasis(T,p,orders) + chebyshevBasis(T,p,orders) end function compute_chebyshev_basis(::Type{T},p::ExtrusionPolytope{D},orders) where {D,T} extrusion = Tuple(p.extrusion) terms = _monomial_terms(extrusion,orders) - ChebyshevPolynomialBasis{D}(T,orders,terms) + ChebyshevBasis(Val(D),T,orders,terms) end function _RT_cell_moments(p, cbasis, ccips, cwips) @@ -250,13 +252,13 @@ function _RT_cell_values(p,et,order,phi) # Cell moments, i.e., M(C)_{ab} = q_C^a(xgp_C^b) w_C^b ⋅ () if is_n_cube(p) - #cbasis = QGradMonomialBasis{num_dims(p)}(et,order-1) - cbasis = QGradLegendrePolynomialBasis{num_dims(p)}(et,order-1) - #cbasis = QGradChebyshevPolynomialBasis{num_dims(p)}(et,order-1) + #cbasis = QGradMonomialBasis(Val(num_dims(p)),et,order-1) + cbasis = QGradLegendreBasis(Val(num_dims(p)),et,order-1) + #cbasis = QGradChebyshevBasis(Val(num_dims(p)),et,order-1) #cbasis = get_shapefuns(RaviartThomasRefFE(et,p,order-1)) elseif is_simplex(p) T = VectorValue{num_dims(p),et} - cbasis = MonomialBasis{num_dims(p)}(T,order-1, _p_filter) + cbasis = MonomialBasis(Val(num_dims(p)),T,order-1, _p_filter) else @notimplemented end diff --git a/test/PolynomialsTests/BernsteinBasesTests.jl b/test/PolynomialsTests/BernsteinBasesTests.jl index 432953b49..33f007b4c 100644 --- a/test/PolynomialsTests/BernsteinBasesTests.jl +++ b/test/PolynomialsTests/BernsteinBasesTests.jl @@ -18,7 +18,7 @@ H = gradient_type(G,xi) # order 0 degenerated case order = 0 -b = BernsteinBasis{1}(V,order) +b = BernsteinBasis(Val(1),V,order) @test get_order(b) == 0 @test get_orders(b) == (0,) @@ -35,7 +35,7 @@ test_field_array(b,x[1],bx[1,:],grad=∇bx[1,:],gradgrad=Hbx[1,:]) # Order 1 order = 1 -b = BernsteinBasis{1}(V,order) +b = BernsteinBasis(Val(1),V,order) bx = [ 1.0 0.0 0.0 1.0 @@ -55,7 +55,7 @@ test_field_array(b,x[1],bx[1,:],grad=∇bx[1,:],gradgrad=Hbx[1,:]) # Order 2 order = 2 -b = BernsteinBasis{1}(V,order) +b = BernsteinBasis(Val(1),V,order) bx = [ 1.0 0.0 0.0 0.0 0.0 1.0 @@ -76,7 +76,7 @@ test_field_array(b,x[1],bx[1,:],grad=∇bx[1,:],gradgrad=Hbx[1,:]) # Order 3 order = 3 -b = BernsteinBasis{1}(V,order) +b = BernsteinBasis(Val(1),V,order) # x=x^1; x2 = x^2; x3 = x^3 # -x3+3x2-3x+1 3x3-6x2+3x -3x3+3x2 x3 diff --git a/test/PolynomialsTests/ChangeBasisTests.jl b/test/PolynomialsTests/ChangeBasisTests.jl index 9119d3609..09dee2b58 100644 --- a/test/PolynomialsTests/ChangeBasisTests.jl +++ b/test/PolynomialsTests/ChangeBasisTests.jl @@ -13,7 +13,7 @@ order = 1 V = Float64 G = gradient_type(V,xi) H = gradient_type(G,xi) -f = MonomialBasis{2}(V,order) +f = MonomialBasis(Val(2),V,order) change = inv(evaluate(f,nodes)) diff --git a/test/PolynomialsTests/ModalC0BasesTests.jl b/test/PolynomialsTests/ModalC0BasesTests.jl index f5e084633..7b1b14c1d 100644 --- a/test/PolynomialsTests/ModalC0BasesTests.jl +++ b/test/PolynomialsTests/ModalC0BasesTests.jl @@ -22,6 +22,7 @@ order = 3 a = fill(Point(-0.5),order+1) b = fill(Point(2.5),order+1) b1 = ModalC0Basis{1}(V,order,a,b) +#b1 = ModalC0Basis(Val(1),V,order,a,b) ∇b1 = Broadcasting(∇)(b1) ∇∇b1 = Broadcasting(∇)(∇b1) @@ -47,6 +48,7 @@ b = [ Point(1.0,1.0),Point(1.0,1.0),Point(1.0,1.0),Point(1.0,1.0), 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 = ModalC0Basis{2}(V,order,a,b) +#b2 = ModalC0Basis(Val(2),V,order,a,b) ∇b2 = Broadcasting(∇)(b2) ∇∇b2 = Broadcasting(∇)(∇b2) @@ -61,4 +63,4 @@ H = gradient_type(G,x1) (0.0, 0.5590169943749475, 0.5590169943749475, 1.118033988749895); (0.0, -2.23606797749979, -2.23606797749979, 0.0) ] -end # module \ No newline at end of file +end # module diff --git a/test/PolynomialsTests/MonomialBasesTests.jl b/test/PolynomialsTests/MonomialBasesTests.jl index e087d71b8..2074497d1 100644 --- a/test/PolynomialsTests/MonomialBasesTests.jl +++ b/test/PolynomialsTests/MonomialBasesTests.jl @@ -15,7 +15,7 @@ order = 0 V = Float64 G = gradient_type(V,xi) H = gradient_type(G,xi) -b = MonomialBasis{2}(V,order) +b = MonomialBasis(Val(2),V,order) @test get_order(b) == 0 @test get_orders(b) == (0,0) @@ -35,7 +35,7 @@ order = 1 V = Float64 G = gradient_type(V,xi) H = gradient_type(G,xi) -b = MonomialBasis{2}(V,order) +b = MonomialBasis(Val(2),V,order) v = V[1.0, 2.0, 3.0, 6.0] g = G[(0.0, 0.0), (1.0, 0.0), (0.0, 1.0), (3.0, 2.0)] @@ -52,7 +52,7 @@ test_field_array(b,x[1],bx[1,:],grad=∇bx[1,:],gradgrad=Hbx[1,:]) orders = (1,2) V = Float64 G = gradient_type(V,xi) -b = MonomialBasis{2}(V,orders) +b = MonomialBasis(Val(2),V,orders) v = V[1.0, 2.0, 3.0, 6.0, 9.0, 18.0] g = G[(0.0, 0.0), (1.0, 0.0), (0.0, 1.0), (3.0, 2.0), (0.0, 6.0), (9.0, 12.0)] @@ -68,7 +68,7 @@ order = 1 V = VectorValue{3,Float64} G = gradient_type(V,xi) H = gradient_type(G,xi) -b = MonomialBasis{2}(V,order) +b = MonomialBasis(Val(2),V,order) v = V[[1.0, 0.0, 0.0], [0.0, 1.0, 0.0], [0.0, 0.0, 1.0], [2.0, 0.0, 0.0], [0.0, 2.0, 0.0], [0.0, 0.0, 2.0], @@ -107,7 +107,7 @@ test_field_array(b,x[1],bx[1,:],grad=∇bx[1,:],gradgrad=Hbx[1,:]) orders = (1,2) V = VectorValue{2,Float64} G = gradient_type(V,xi) -b = MonomialBasis{2}(V,orders) +b = MonomialBasis(Val(2),V,orders) v = V[ (1.0, 0.0), (0.0, 1.0), (2.0, 0.0), (0.0, 2.0), @@ -133,7 +133,7 @@ order = 1 V = Float64 G = gradient_type(V,xi) filter = (e,o) -> sum(e) <= o -b = MonomialBasis{2}(V,order,filter) +b = MonomialBasis(Val(2),V,order,filter) v = V[1.0, 2.0, 3.0] g = G[[0.0, 0.0], [1.0, 0.0], [0.0, 1.0]] @@ -149,7 +149,7 @@ order = 1 V = VectorValue{3,Float64} G = gradient_type(V,xi) filter = (e,o) -> sum(e) <= o -b = MonomialBasis{2}(V,order,filter) +b = MonomialBasis(Val(2),V,order,filter) v = V[[1.0; 0.0; 0.0], [0.0; 1.0; 0.0], [0.0; 0.0; 1.0], [2.0; 0.0; 0.0], [0.0; 2.0; 0.0], [0.0; 0.0; 2.0], @@ -172,7 +172,7 @@ order = 1 V = SymTensorValue{2,Float64} G = gradient_type(V,xi) filter = (e,o) -> sum(e) <= o -b = MonomialBasis{2}(V,order,filter) +b = MonomialBasis(Val(2),V,order,filter) v = V[(1.0, 0.0, 0.0), (0.0, 1.0, 0.0), (0.0, 0.0, 1.0), (2.0, 0.0, 0.0), (0.0, 2.0, 0.0), (0.0, 0.0, 2.0), @@ -199,7 +199,7 @@ order = 1 V = SymTracelessTensorValue{2,Float64} G = gradient_type(V,xi) filter = (e,o) -> sum(e) <= o -b = MonomialBasis{2}(V,order,filter) +b = MonomialBasis(Val(2),V,order,filter) v = V[(1.0, 0.0), (0.0, 1.0), (2.0, 0.0), (0.0, 2.0), @@ -219,25 +219,25 @@ test_field_array(b,x[1],bx[1,:],grad=∇bx[1,:]) order = 1 -b = MonomialBasis{1}(Float64,order) +b = MonomialBasis(Val(1),Float64,order) @test evaluate(b,Point{1,Float64}[(0,),(1,)]) == [1.0 0.0; 1.0 1.0] -b = MonomialBasis{0}(VectorValue{2,Float64},order) +b = MonomialBasis(Val(0),VectorValue{2,Float64},order) @test evaluate(b,Point{0,Float64}[(),()]) == VectorValue{2,Float64}[(1.0, 0.0) (0.0, 1.0); (1.0, 0.0) (0.0, 1.0)] -b = MonomialBasis{0}(TensorValue{2,2,Float64},order) +b = MonomialBasis(Val(0),TensorValue{2,2,Float64},order) @test evaluate(b,Point{0,Float64}[(),()]) == TensorValue{2,2,Float64}[ (1.0, 0.0, 0.0, 0.0) (0.0, 1.0, 0.0, 0.0) (0.0, 0.0, 1.0, 0.0) (0.0, 0.0, 0.0, 1.0); (1.0, 0.0, 0.0, 0.0) (0.0, 1.0, 0.0, 0.0) (0.0, 0.0, 1.0, 0.0) (0.0, 0.0, 0.0, 1.0) ] -b = MonomialBasis{0}(SymTensorValue{2,Float64},order) +b = MonomialBasis(Val(0),SymTensorValue{2,Float64},order) @test evaluate(b,Point{0,Float64}[(),()]) == SymTensorValue{2,Float64}[ (1.0, 0.0, 0.0) (0.0, 1.0, 0.0) (0.0, 0.0, 1.0); (1.0, 0.0, 0.0) (0.0, 1.0, 0.0) (0.0, 0.0, 1.0) ] -b = MonomialBasis{0}(SymTracelessTensorValue{2,Float64},order) +b = MonomialBasis(Val(0),SymTracelessTensorValue{2,Float64},order) @test evaluate(b,Point{0,Float64}[(),()]) == SymTracelessTensorValue{2,Float64}[ (1.0, 0.0) (0.0, 1.0); (1.0, 0.0) (0.0, 1.0) ] diff --git a/test/PolynomialsTests/PCurlGradMonomialBasesTests.jl b/test/PolynomialsTests/PCurlGradMonomialBasesTests.jl index f11e9f6c7..d48f69f00 100644 --- a/test/PolynomialsTests/PCurlGradMonomialBasesTests.jl +++ b/test/PolynomialsTests/PCurlGradMonomialBasesTests.jl @@ -16,6 +16,7 @@ T = Float64 V = VectorValue{D,T} G = gradient_type(V,xi) b = PCurlGradMonomialBasis{D}(T,order) +#b = PCurlGradMonomialBasis(Val(D),T,order) v = V[ (1.0, 0.0), (0.0, 1.0), (4.0, 0.0), (0.0, 2.0), (16.0, 0.0), (0.0, 4.0), @@ -47,7 +48,7 @@ g = G[ gvb = evaluate(∇b,xi) @test gvb == g - @test num_terms(b) == 15 + @test length(b) == 15 @test get_order(b) == 2 xi = Point(2,3,5) @@ -60,8 +61,9 @@ g = G[ V = VectorValue{D,T} G = gradient_type(V,xi) b = PCurlGradMonomialBasis{D}(T,order) + #b = PCurlGradMonomialBasis(Val(D),T,order) - @test num_terms(b) == 15 + @test length(b) == 15 @test get_order(b) == 1 end # module diff --git a/test/PolynomialsTests/QCurlGradMonomialBasesTests.jl b/test/PolynomialsTests/QCurlGradMonomialBasesTests.jl index a2c31a787..c226512fc 100644 --- a/test/PolynomialsTests/QCurlGradMonomialBasesTests.jl +++ b/test/PolynomialsTests/QCurlGradMonomialBasesTests.jl @@ -14,10 +14,10 @@ D = 2 T = Float64 V = VectorValue{D,T} G = gradient_type(V,xi) -b = QCurlGradMonomialBasis{D}(T,order) +b = QCurlGradMonomialBasis(Val(D),T,order) -@test num_terms(b) == 4 -@test get_order(b) == 0 +@test length(b) == 4 +@test get_order(b) == 1 xi = Point(2,3,5) np = 5 @@ -28,18 +28,19 @@ D = 3 T = Float64 V = VectorValue{D,T} G = gradient_type(V,xi) -b = QCurlGradMonomialBasis{D}(T,order) +b = QCurlGradMonomialBasis(Val(D),T,order) v = V[ - (1.0, 0.0, 0.0), (0.0, 1.0, 0.0), (0.0, 0.0, 1.0), - (2.0, 0.0, 0.0), (0.0, 3.0, 0.0), (0.0, 0.0, 5.0)] + (1.0, 0.0, 0.0), (2.0, 0.0, 0.0), + (0.0, 1.0, 0.0), (0.0, 3.0, 0.0), + (0.0, 0.0, 1.0), (0.0, 0.0, 5.0)] g = G[ - (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, 0.0, 0.0), (0.0, 0.0, 0.0, 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, 0.0, 0.0, 0.0, 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, 0.0), (0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0)] bx = repeat(permutedims(v),np) @@ -56,20 +57,25 @@ D = 2 T = Float64 V = VectorValue{D,T} G = gradient_type(V,xi) -b = QCurlGradMonomialBasis{D}(T,order) +b = QCurlGradMonomialBasis(Val(D),T,order) v = V[ - (1.0, 0.0), (0.0, 1.0), (2.0, 0.0), (0.0, 3.0), - (4.0, 0.0), (0.0, 9.0), (3.0, 0.0), (0.0, 2.0), - (6.0, 0.0), (0.0, 6.0), (12.0, 0.0), (0.0, 18.0)] + ( 1., 0. ), ( 2., 0. ), ( 4., 0. ), ( 3., 0. ), ( 6., 0. ), (12., 0. ), + ( 0., 1. ), ( 0., 2. ), ( 0., 3. ), ( 0., 6. ), ( 0., 9. ), ( 0., 18.)] g = G[ - (0.0, 0.0, 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, 1.0), - (4.0, 0.0, 0.0, 0.0), (0.0, 0.0, 0.0, 6.0), - (0.0, 1.0, 0.0, 0.0), (0.0, 0.0, 1.0, 0.0), - (3.0, 2.0, 0.0, 0.0), (0.0, 0.0, 3.0, 2.0), - (12.0, 4.0, 0.0, 0.0),(0.0, 0.0, 9.0, 12.0)] + ( 0., 0., 0., 0.), + ( 1., 0., 0., 0.), + ( 4., 0., 0., 0.), + ( 0., 1., 0., 0.), + ( 3., 2., 0., 0.), + (12., 4., 0., 0.), + ( 0., 0., 0., 0.), + ( 0., 0., 1., 0.), + ( 0., 0., 0., 1.), + ( 0., 0., 3., 2.), + ( 0., 0., 0., 6.), + ( 0., 0., 9.,12.)] bx = repeat(permutedims(v),np) ∇bx = repeat(permutedims(g),np) diff --git a/test/PolynomialsTests/QGradMonomialBasesTests.jl b/test/PolynomialsTests/QGradMonomialBasesTests.jl index 90f4cbdd1..42319d0a8 100644 --- a/test/PolynomialsTests/QGradMonomialBasesTests.jl +++ b/test/PolynomialsTests/QGradMonomialBasesTests.jl @@ -15,11 +15,10 @@ D = 2 T = Float64 V = VectorValue{D,T} G = gradient_type(V,xi) -b = QGradMonomialBasis{D}(T,order) +b = QGradMonomialBasis(Val(D),T,order) -@test num_terms(b) == 4 -@test b.order == 0 -@test get_order(b) == 0 +@test length(b) == 4 +@test get_order(b) == 1 xi = Point(2,3,5) np = 5 @@ -30,30 +29,33 @@ D = 3 T = Float64 V = VectorValue{D,T} G = gradient_type(V,xi) -b = QGradMonomialBasis{D}(T,order) +b = QGradMonomialBasis(Val(D),T,order) v = V[ - (1.0, 0.0, 0.0), (0.0, 1.0, 0.0), (0.0, 0.0, 1.0), - (3.0, 0.0, 0.0), (0.0, 5.0, 0.0), (0.0, 0.0, 2.0), - (5.0, 0.0, 0.0), (0.0, 2.0, 0.0), (0.0, 0.0, 3.0), - (15.0, 0.0, 0.0), (0.0, 10.0, 0.0), (0.0, 0.0, 6.0)] + (1.0, 0.0, 0.0), (3.0, 0.0, 0.0), (5.0, 0.0, 0.0), (15.0, 0.0, 0.0), + (0.0, 1.0, 0.0), (0.0, 2.0, 0.0), (0.0, 5.0, 0.0), (0.0, 10.0, 0.0), + (0.0, 0.0, 1.0), (0.0, 0.0, 2.0), (0.0, 0.0, 3.0), (0.0, 0.0, 6.0)] g = G[ - (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, 0.0, 0.0), (0.0, 0.0, 0.0, 0.0, 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, 1.0, 0.0, 0.0, 0.0), - (0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.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, 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, 1.0, 0.0), (0.0, 5.0, 3.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.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, 1.0, 0.0, 0.0, 0.0), (0.0, 0.0, 0.0, 5.0, 0.0, 2.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.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, 1.0, 0.0), (0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 3.0, 2.0, 0.0)] bx = repeat(permutedims(v),np) ∇bx = repeat(permutedims(g),np) + +evaluate(b,x) +evaluate(Broadcasting(∇)(b),x) + test_field_array(b,x,bx,grad=∇bx) test_field_array(b,x[1],bx[1,:],grad=∇bx[1,:]) @@ -66,18 +68,31 @@ D = 2 T = Float64 V = VectorValue{D,T} G = gradient_type(V,xi) -b = QGradMonomialBasis{D}(T,order) +b = QGradMonomialBasis(Val(D),T,order) v = V[ (1.0, 0.0), (0.0, 1.0), (2.0, 0.0), (0.0, 3.0), (3.0, 0.0), (0.0, 2.0), (6.0, 0.0), (0.0, 6.0), (9.0, 0.0), (0.0, 4.0), (18.0, 0.0), (0.0, 12.0)] +v = V[ + (1., 0.), (2., 0.), (3., 0.), (6., 0.), (9., 0.), (18., 0.), + (0., 1.), (0., 2.), (0., 4.), (0., 3.), (0., 6.), (0., 12.)] + + g = G[ - (0.0, 0.0, 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, 1.0), (0.0, 1.0, 0.0, 0.0), (0.0, 0.0, 1.0, 0.0), - (3.0, 2.0, 0.0, 0.0), (0.0, 0.0, 3.0, 2.0), (0.0, 6.0, 0.0, 0.0), - (0.0, 0.0, 4.0, 0.0), (9.0, 12.0, 0.0, 0.0), (0.0, 0.0, 12.0, 4.0)] + (0., 0., 0., 0.), + (1., 0., 0., 0.), + (0., 1., 0., 0.), + (3., 2., 0., 0.), + (0., 6., 0., 0.), + (9.,12., 0., 0.), + (0., 0., 0., 0.), + (0., 0., 1., 0.), + (0., 0., 4., 0.), + (0., 0., 0., 1.), + (0., 0., 3., 2.), + (0., 0.,12., 4.)] bx = repeat(permutedims(v),np) ∇bx = repeat(permutedims(g),np) diff --git a/test/ReferenceFEsTests/BezierRefFEsTests.jl b/test/ReferenceFEsTests/BezierRefFEsTests.jl index 483cdfad8..c92052858 100644 --- a/test/ReferenceFEsTests/BezierRefFEsTests.jl +++ b/test/ReferenceFEsTests/BezierRefFEsTests.jl @@ -20,7 +20,7 @@ p_filter(e,o) = sum(e) ≤ o # 1D p = 2 -prebasis_seg = MonomialBasis{1}(Float64,p,p_filter) +prebasis_seg = MonomialBasis(Val(1),Float64,p,p_filter) C = _berstein_matrix(prebasis_seg,SEGMENT) C12 = [ @@ -41,7 +41,7 @@ Xi = lazy_map( evaluate, Ψ, ξ ) @test Xi == [ Point(0.0,0.0), Point(0.5,0.25), Point(1.0,0.0) ] p = 3 -prebasis_seg = MonomialBasis{1}(Float64,p,p_filter) +prebasis_seg = MonomialBasis(Val(1),Float64,p,p_filter) C = _berstein_matrix(prebasis_seg,SEGMENT) C13 = [ @@ -65,7 +65,7 @@ Xi = lazy_map( evaluate, Ψ, ξ ) # 2D p = 2 -prebasis_tri = MonomialBasis{2}(Float64,p,p_filter) +prebasis_tri = MonomialBasis(Val(2),Float64,p,p_filter) C = _berstein_matrix(prebasis_tri,TRI) C22 = [ @@ -89,7 +89,7 @@ Xi = lazy_map( evaluate, Ψ, ξ ) @test Xi == ξ p = 3 -prebasis_tri = MonomialBasis{2}(Float64,p,p_filter) +prebasis_tri = MonomialBasis(Val(2),Float64,p,p_filter) C = _berstein_matrix(prebasis_tri,TRI) C23 = [ diff --git a/test/ReferenceFEsTests/LagrangianRefFEsTests.jl b/test/ReferenceFEsTests/LagrangianRefFEsTests.jl index 98b4d42f3..75ba8a86a 100644 --- a/test/ReferenceFEsTests/LagrangianRefFEsTests.jl +++ b/test/ReferenceFEsTests/LagrangianRefFEsTests.jl @@ -8,7 +8,7 @@ using Gridap.Fields D = 2 T = Float64 order = 1 -prebasis = MonomialBasis{D}(T,order) +prebasis = MonomialBasis(Val(D),T,order) polytope = QUAD x = get_vertex_coordinates(polytope) diff --git a/test/ReferenceFEsTests/NedelecRefFEsTests.jl b/test/ReferenceFEsTests/NedelecRefFEsTests.jl index 1e0db175c..bd425174a 100644 --- a/test/ReferenceFEsTests/NedelecRefFEsTests.jl +++ b/test/ReferenceFEsTests/NedelecRefFEsTests.jl @@ -15,8 +15,8 @@ order = 0 reffe = NedelecRefFE(et,p,order) test_reference_fe(reffe) -@test num_terms(get_prebasis(reffe)) == 4 -@test get_order(get_prebasis(reffe)) == 0 +@test length(get_prebasis(reffe)) == 4 +@test get_order(get_prebasis(reffe)) == 1 @test num_dofs(reffe) == 4 @test Conformity(reffe) == CurlConformity() @@ -28,9 +28,9 @@ order = 1 reffe = NedelecRefFE(et,p,order) test_reference_fe(reffe) -@test num_terms(get_prebasis(reffe)) == 12 +@test length(get_prebasis(reffe)) == 12 @test num_dofs(reffe) == 12 -@test get_order(get_prebasis(reffe)) == 1 +@test get_order(get_prebasis(reffe)) == 2 prebasis = get_prebasis(reffe) dof_basis = get_dof_basis(reffe) @@ -53,7 +53,7 @@ order = 0 reffe = NedelecRefFE(et,p,order) test_reference_fe(reffe) -@test num_terms(get_prebasis(reffe)) == 6 +@test length(get_prebasis(reffe)) == 6 @test get_order(get_prebasis(reffe)) == 0 @test num_dofs(reffe) == 6 @test Conformity(reffe) == CurlConformity() @@ -94,7 +94,7 @@ order = 0 reffe = NedelecRefFE(et,p,order) test_reference_fe(reffe) -@test num_terms(get_prebasis(reffe)) == 3 +@test length(get_prebasis(reffe)) == 3 @test get_order(get_prebasis(reffe)) == 0 @test num_dofs(reffe) == 3 @test Conformity(reffe) == CurlConformity() @@ -114,14 +114,14 @@ dof_basis = get_dof_basis(reffe) # Factory function reffe = ReferenceFE(QUAD,nedelec,0) -@test num_terms(get_prebasis(reffe)) == 4 -@test get_order(get_prebasis(reffe)) == 0 +@test length(get_prebasis(reffe)) == 4 +@test get_order(get_prebasis(reffe)) == 1 @test num_dofs(reffe) == 4 @test Conformity(reffe) == CurlConformity() reffe = ReferenceFE(QUAD,nedelec,Float64,0) -@test num_terms(get_prebasis(reffe)) == 4 -@test get_order(get_prebasis(reffe)) == 0 +@test length(get_prebasis(reffe)) == 4 +@test get_order(get_prebasis(reffe)) == 1 @test num_dofs(reffe) == 4 @test Conformity(reffe) == CurlConformity() @@ -137,7 +137,7 @@ et = Float64 order = 1 reffe = NedelecRefFE(et,p,order) test_reference_fe(reffe) -@test num_terms(get_prebasis(reffe)) == 20 +@test length(get_prebasis(reffe)) == 20 @test get_order(get_prebasis(reffe)) == 1 @test num_dofs(reffe) == 20 @test Conformity(reffe) == CurlConformity() @@ -145,23 +145,23 @@ dof_basis = get_dof_basis(reffe) face_odofs_L2 = get_face_own_dofs(reffe,L2Conformity()) -@test face_odofs_L2 == [Int64[], Int64[], Int64[], Int64[], - Int64[], Int64[], Int64[], Int64[], Int64[], Int64[], Int64[], Int64[], Int64[], Int64[], +@test face_odofs_L2 == [Int64[], Int64[], Int64[], Int64[], + Int64[], Int64[], Int64[], Int64[], Int64[], Int64[], Int64[], Int64[], Int64[], Int64[], collect(1:20)] face_odofs = get_face_own_dofs(reffe) face_cdofs = get_face_dofs(reffe) -@test face_odofs == [Int64[], Int64[], Int64[], Int64[], - [1, 2], [3, 4], [5, 6], [7, 8], [9, 10], [11, 12], [13, 14], [15, 16], [17, 18], [19, 20], +@test face_odofs == [Int64[], Int64[], Int64[], Int64[], + [1, 2], [3, 4], [5, 6], [7, 8], [9, 10], [11, 12], [13, 14], [15, 16], [17, 18], [19, 20], Int64[]] -@test face_cdofs == [Int64[], Int64[], Int64[], Int64[], - [1, 2], [3, 4], [5, 6], [7, 8], [9, 10], [11, 12], - [1, 2, 3, 4, 5, 6, 13, 14], [1, 2, 7, 8, 9, 10, 15, 16], [3, 4, 7, 8, 11, 12, 17, 18], [5, 6, 9, 10, 11, 12, 19, 20], +@test face_cdofs == [Int64[], Int64[], Int64[], Int64[], + [1, 2], [3, 4], [5, 6], [7, 8], [9, 10], [11, 12], + [1, 2, 3, 4, 5, 6, 13, 14], [1, 2, 7, 8, 9, 10, 15, 16], [3, 4, 7, 8, 11, 12, 17, 18], [5, 6, 9, 10, 11, 12, 19, 20], [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20]] - + #display(face_odofs) using Gridap.Geometry diff --git a/test/ReferenceFEsTests/RaviartThomasRefFEsTests.jl b/test/ReferenceFEsTests/RaviartThomasRefFEsTests.jl index 1bd40ca7b..302513da7 100644 --- a/test/ReferenceFEsTests/RaviartThomasRefFEsTests.jl +++ b/test/ReferenceFEsTests/RaviartThomasRefFEsTests.jl @@ -21,10 +21,12 @@ order = 0 reffe = RaviartThomasRefFE(et,p,order) test_reference_fe(reffe) -@test num_terms(get_prebasis(reffe)) == 4 -@test get_order(get_prebasis(reffe)) == 0 +@test length(get_prebasis(reffe)) == 4 +@test get_order(get_prebasis(reffe)) == 1 @test num_dofs(reffe) == 4 @test Conformity(reffe) == DivConformity() + + p = QUAD D = num_dims(QUAD) et = Float64 @@ -32,9 +34,9 @@ order = 1 reffe = RaviartThomasRefFE(et,p,order) test_reference_fe(reffe) -@test num_terms(get_prebasis(reffe)) == 12 +@test length(get_prebasis(reffe)) == 12 @test num_dofs(reffe) == 12 -@test get_order(get_prebasis(reffe)) == 1 +@test get_order(get_prebasis(reffe)) == 2 prebasis = get_prebasis(reffe) dof_basis = get_dof_basis(reffe) @@ -78,11 +80,12 @@ order = 0 reffe = RaviartThomasRefFE(et,p,order) test_reference_fe(reffe) -@test num_terms(get_prebasis(reffe)) == 4 +@test length(get_prebasis(reffe)) == 4 @test num_dofs(reffe) == 4 @test get_order(get_prebasis(reffe)) == 0 @test Conformity(reffe) == DivConformity() + p = TET D = num_dims(p) et = Float64 @@ -90,7 +93,7 @@ order = 2 reffe = RaviartThomasRefFE(et,p,order) test_reference_fe(reffe) -@test num_terms(get_prebasis(reffe)) == 36 +@test length(get_prebasis(reffe)) == 36 @test num_dofs(reffe) == 36 @test get_order(get_prebasis(reffe)) == 2 @test Conformity(reffe) == DivConformity() @@ -111,14 +114,14 @@ test_dof_array(dof_basis,prebasis,r) # Factory function reffe = ReferenceFE(QUAD,raviart_thomas,0) -@test num_terms(get_prebasis(reffe)) == 4 -@test get_order(get_prebasis(reffe)) == 0 +@test length(get_prebasis(reffe)) == 4 +@test get_order(get_prebasis(reffe)) == 1 @test num_dofs(reffe) == 4 @test Conformity(reffe) == DivConformity() reffe = ReferenceFE(QUAD,raviart_thomas,Float64,0) -@test num_terms(get_prebasis(reffe)) == 4 -@test get_order(get_prebasis(reffe)) == 0 +@test length(get_prebasis(reffe)) == 4 +@test get_order(get_prebasis(reffe)) == 1 @test num_dofs(reffe) == 4 @test Conformity(reffe) == DivConformity() diff --git a/test/ReferenceFEsTests/ReferenceFEInterfacesTests.jl b/test/ReferenceFEsTests/ReferenceFEInterfacesTests.jl index 6830d6835..c1aae9fc3 100644 --- a/test/ReferenceFEsTests/ReferenceFEInterfacesTests.jl +++ b/test/ReferenceFEsTests/ReferenceFEInterfacesTests.jl @@ -8,7 +8,7 @@ using Gridap.ReferenceFEs D = 2 T = Float64 order = 1 -prebasis = MonomialBasis{D}(T,order) +prebasis = MonomialBasis(Val(D),T,order) polytope = QUAD x = get_vertex_coordinates(polytope) From 4b5bde9d7f5c7a20241d0db250bd69bf26bf6bbe Mon Sep 17 00:00:00 2001 From: Antoine Marteau Date: Wed, 18 Dec 2024 16:37:53 +1100 Subject: [PATCH 059/105] Generic return_cache implem for all PolynomialBasis' --- src/Polynomials/CompWiseTensorPolyBases.jl | 66 ++++------------------ src/Polynomials/PolynomialInterfaces.jl | 43 ++++++++++++++ src/Polynomials/TensorPolynomialBases.jl | 40 ------------- 3 files changed, 55 insertions(+), 94 deletions(-) diff --git a/src/Polynomials/CompWiseTensorPolyBases.jl b/src/Polynomials/CompWiseTensorPolyBases.jl index b44127edd..88fe45ea7 100644 --- a/src/Polynomials/CompWiseTensorPolyBases.jl +++ b/src/Polynomials/CompWiseTensorPolyBases.jl @@ -39,7 +39,11 @@ Base.size(a::CompWiseTensorPolyBasis) = ( sum(prod.(eachrow(a.orders .+ 1))), ) get_comp_terms(f::CompWiseTensorPolyBasis) Return a `NTuple{L,CartesianIndices{D}}` containing, for each component -1 ≤ l ≤ `L`, the Cartesian indices of the terms of the tensor product basis. +1 ≤ l ≤ `L`, the Cartesian indices iterator over the terms +in ⟦1,`o`(l,1)+1⟧ × ⟦1,`o`(l,2)+1⟧ × … × ⟦1,`o`(l,D)+1⟧ that define 𝕊ˡ. + +E.g., if `orders=[ 0 1; 1 0]`, then the `comp_terms` are +`( CartesianIndices{2}((1,2)), CartesianIndices{2}((2,1)) )`. """ function get_comp_terms(f::CompWiseTensorPolyBasis{D,V,K,PT,L}) where {D,V,K,PT,L} _terms(l) = CartesianIndices( Tuple(f.orders[l,:] .+ 1) ) @@ -47,20 +51,10 @@ function get_comp_terms(f::CompWiseTensorPolyBasis{D,V,K,PT,L}) where {D,V,K,PT, comp_terms::NTuple{L,CartesianIndices{D}} end -function return_cache( - f::CompWiseTensorPolyBasis{D,V}, - x::AbstractVector{<:Point}) where {D,V} - @check D == length(eltype(x)) "Incorrect number of point components" - T = eltype(V) - np = length(x) - ndof = length(f) - ndof_1d = get_order(f) + 1 - r = CachedArray(zeros(V,(np,ndof))) - v = CachedArray(zeros(V,(ndof,))) - c = CachedArray(zeros(T,(D,ndof_1d))) - (r, v, c) -end +######################## +# Field implementation # +######################## function evaluate!( cache, @@ -85,25 +79,6 @@ function evaluate!( r.array end -function return_cache( - fg::FieldGradientArray{1,<:CompWiseTensorPolyBasis{D,V}}, - x::AbstractVector{<:Point}) where {D,V} - - @check D == length(eltype(x)) "Incorrect number of point components" - T = eltype(V) - f = fg.fa - np = length(x) - ndof = length(f) - ndof_1d = get_order(f) + 1 - xi = testitem(x) - G = gradient_type(V,xi) - r = CachedArray(zeros(G,(np,ndof))) - v = CachedArray(zeros(G,(ndof,))) - c = CachedArray(zeros(T,(D,ndof_1d))) - g = CachedArray(zeros(T,(D,ndof_1d))) - (r, v, c, g) -end - function evaluate!( cache, fg::FieldGradientArray{1,<:CompWiseTensorPolyBasis{D,V,K,PT}}, @@ -129,26 +104,6 @@ function evaluate!( r.array end -function return_cache( - fg::FieldGradientArray{2,<:CompWiseTensorPolyBasis{D,V}}, - x::AbstractVector{<:Point}) where {D,V} - - @check D == length(eltype(x)) "Incorrect number of point components" - T = eltype(V) - f = fg.fa - np = length(x) - ndof = length(f) - ndof_1d = get_order(f) + 1 - xi = testitem(x) - G = gradient_type(V,xi) - r = CachedArray(zeros(G,(np,ndof))) - v = CachedArray(zeros(G,(ndof,))) - c = CachedArray(zeros(T,(D,ndof_1d))) - g = CachedArray(zeros(T,(D,ndof_1d))) - h = CachedArray(zeros(T,(D,ndof_1d))) - (r, v, c, g, h) -end - function evaluate!( cache, fg::FieldGradientArray{2,<:CompWiseTensorPolyBasis{D,V,K,PT}}, @@ -175,7 +130,10 @@ function evaluate!( r.array end -# Helpers + +########### +# Helpers # +########### function _evaluate_nd_cwtpb!( PT::Type{<:Polynomial}, diff --git a/src/Polynomials/PolynomialInterfaces.jl b/src/Polynomials/PolynomialInterfaces.jl index 7af211995..a41faa5c8 100644 --- a/src/Polynomials/PolynomialInterfaces.jl +++ b/src/Polynomials/PolynomialInterfaces.jl @@ -40,6 +40,49 @@ Return the maximum polynomial order in a dimension, or `0` in 0D. get_order(::PolynomialBasis{D,V,K}) where {D,V,K} = K +################################ +# Generic field implementation # +################################ + +""" +TODO +""" +function _return_cache( + f::PolynomialBasis{D}, x,::Type{G},::Val{N_deriv}) where {D,G,N_deriv} + + @assert D == length(eltype(x)) "Incorrect number of point components" + T = eltype(G) + np = length(x) + ndof = length(f) + ndof_1d = get_order(f) + 1 + # Cache for the returned array + r = CachedArray(zeros(G,(np,ndof))) + # Cache for basis functions at one point x[i] + v = CachedArray(zeros(G,(ndof,))) + # Cache for the 1D basis function values in each dimension (to be + # tensor-producted), and of their N_deriv'th 1D derivatives + t = ntuple( _ -> CachedArray(zeros(T,(D,ndof_1d ))), Val(N_deriv+1)) + (r, v, t...) +end + +function return_cache(f::PolynomialBasis{D,V}, x::AbstractVector{<:Point}) where {D,V} + _return_cache(f,x,V,Val(0)) +end + +function return_cache( + fg::FieldGradientArray{N,<:PolynomialBasis{D,V}}, + x::AbstractVector{<:Point}) where {N,D,V} + + f = fg.fa + xi = testitem(x) + G = V + for _ in 1:N + G = gradient_type(G,xi) + end + _return_cache(f,x,G,Val(N)) +end + + ############################### # 1D internal polynomial APIs # ############################### diff --git a/src/Polynomials/TensorPolynomialBases.jl b/src/Polynomials/TensorPolynomialBases.jl index 5fcceed27..26c932fed 100644 --- a/src/Polynomials/TensorPolynomialBases.jl +++ b/src/Polynomials/TensorPolynomialBases.jl @@ -178,26 +178,6 @@ end # Field implementation # ######################## -function _return_cache(f::TensorPolynomialBasis{D},x,::Type{G},N_deriv) where {D,G} - @assert D == length(eltype(x)) "Incorrect number of point components" - T = eltype(G) - np = length(x) - ndof = length(f) - ndof_1d = get_order(f) + 1 - # Cache for the returned array - r = CachedArray(zeros(G,(np,ndof))) - # Cache for basis functions at one point x[i] - v = CachedArray(zeros(G,(ndof,))) - # Cache for the 1D basis function values in each dimension (to be - # tensor-producted), and of their 1D N_deriv'th derivatives - t = ntuple( _ -> CachedArray(zeros(T,(D,ndof_1d ))), N_deriv) - (r, v, t...) -end - -function return_cache(f::TensorPolynomialBasis{D,V},x::AbstractVector{<:Point}) where {D,V} - _return_cache(f,x,V,1) -end - function evaluate!(cache,f::TensorPolynomialBasis{D,V,K,PT},x::AbstractVector{<:Point}) where {D,V,K,PT} r, v, c = cache np = length(x) @@ -218,16 +198,6 @@ function evaluate!(cache,f::TensorPolynomialBasis{D,V,K,PT},x::AbstractVector{<: r.array end -function return_cache( - fg::FieldGradientArray{1,<:TensorPolynomialBasis{D,V}}, - x::AbstractVector{<:Point}) where {D,V} - - f = fg.fa - xi = testitem(x) - G = gradient_type(V,xi) - _return_cache(f,x,G,2) -end - function evaluate!( cache, fg::FieldGradientArray{1,<:TensorPolynomialBasis{D,V,K,PT}}, @@ -255,16 +225,6 @@ function evaluate!( r.array end -function return_cache( - fg::FieldGradientArray{2,<:TensorPolynomialBasis{D,V}}, - x::AbstractVector{<:Point}) where {D,V} - - f = fg.fa - xi = testitem(x) - G = gradient_type(gradient_type(V,xi),xi) - _return_cache(f,x,G,3) -end - function evaluate!( cache, fg::FieldGradientArray{2,<:TensorPolynomialBasis{D,V,K,PT}}, From c7a4bf1b1c315ebcd5ee1e72456fb703bd56080f Mon Sep 17 00:00:00 2001 From: Antoine Marteau Date: Thu, 19 Dec 2024 09:03:50 +1100 Subject: [PATCH 060/105] rename _s_filter and clarify its definition --- src/Polynomials/PCurlGradLegendreBases.jl | 2 +- src/Polynomials/PCurlGradMonomialBases.jl | 2 +- src/Polynomials/TensorPolynomialBases.jl | 7 ++++--- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/src/Polynomials/PCurlGradLegendreBases.jl b/src/Polynomials/PCurlGradLegendreBases.jl index 4cf568e13..a0753a259 100644 --- a/src/Polynomials/PCurlGradLegendreBases.jl +++ b/src/Polynomials/PCurlGradLegendreBases.jl @@ -35,7 +35,7 @@ in this basis is in the P space of degree `order`. function PCurlGradLegendrePolynomialBasis{D}(::Type{T},order::Int) where {D,T} @check T<:Real "T needs to be <:Real since represents the type of the components of the vector value" P_k = MonomialBasis(Val(D),T, order, _p_filter) - S_k = MonomialBasis(Val(D),T, order, _s_filter) + S_k = MonomialBasis(Val(D),T, order, _ps_filter) pterms = P_k.terms sterms = S_k.terms perms = _prepare_perms(D) diff --git a/src/Polynomials/PCurlGradMonomialBases.jl b/src/Polynomials/PCurlGradMonomialBases.jl index 51453ab71..cb5fb3cfe 100644 --- a/src/Polynomials/PCurlGradMonomialBases.jl +++ b/src/Polynomials/PCurlGradMonomialBases.jl @@ -36,7 +36,7 @@ in this basis is in the P space of degree `order`. function PCurlGradMonomialBasis{D}(::Type{T},order::Int) where {D,T} @check T<:Real "T needs to be <:Real since represents the type of the components of the vector value" P_k = MonomialBasis(Val(D), T, order, _p_filter) - S_k = MonomialBasis(Val(D), T, order, _s_filter) + S_k = MonomialBasis(Val(D), T, order, _ps_filter) pterms = P_k.terms sterms = S_k.terms perms = _prepare_perms(D) diff --git a/src/Polynomials/TensorPolynomialBases.jl b/src/Polynomials/TensorPolynomialBases.jl index 26c932fed..0b220386c 100644 --- a/src/Polynomials/TensorPolynomialBases.jl +++ b/src/Polynomials/TensorPolynomialBases.jl @@ -152,9 +152,10 @@ end # Helpers # ########### -_q_filter(e,o) = true -_p_filter(e,order) = (sum(e) <= order) -_s_filter(e,order) = (sum(e) == order) +_q_filter( e,order) = (maximum(e,init=0) <= order) # ℚₙ +_qs_filter(e,order) = (maximum(e,init=0) == order) # ℚₙ\ℚ₍ₙ₋₁₎ +_p_filter( e,order) = (sum(e) <= order) # ℙₙ +_ps_filter(e,order) = (sum(e) == order) # ℙₙ\ℙ₍ₙ₋₁₎ function _p_dim(order,D) dim = 1 From 427b0798d1e57b3a33901b1cd10973794985e49c Mon Sep 17 00:00:00 2001 From: Antoine Marteau Date: Thu, 19 Dec 2024 09:09:16 +1100 Subject: [PATCH 061/105] clean factorised Q[Curl]Grad code --- src/Polynomials/ChebyshevBases.jl | 304 ----------------- src/Polynomials/QCurlGradMonomialBases.jl | 74 ----- .../QGradLegendrePolynomialBases.jl | 306 ------------------ src/Polynomials/QGradMonomialBases.jl | 231 ------------- src/Polynomials/QSpaceBases.jl | 0 5 files changed, 915 deletions(-) delete mode 100644 src/Polynomials/QCurlGradMonomialBases.jl delete mode 100644 src/Polynomials/QGradLegendrePolynomialBases.jl delete mode 100644 src/Polynomials/QGradMonomialBases.jl delete mode 100644 src/Polynomials/QSpaceBases.jl diff --git a/src/Polynomials/ChebyshevBases.jl b/src/Polynomials/ChebyshevBases.jl index 4489d4b01..0b74c4140 100644 --- a/src/Polynomials/ChebyshevBases.jl +++ b/src/Polynomials/ChebyshevBases.jl @@ -87,307 +87,3 @@ function _hessian_1d!( @notimplemented end -############################################################################################ - -""" - struct QGradChebyshevPolynomialBasis{...} <: AbstractVector{Chebyshev{:T}} - -This type implements a multivariate vector-valued polynomial basis -spanning the space needed for Nedelec reference elements on n-cubes. -The type parameters and fields of this `struct` are not public. -This type fully implements the [`Field`](@ref) interface, with up to first order -derivatives. -""" -struct QGradChebyshevPolynomialBasis{D,T} <: AbstractVector{Chebyshev{:T}} - order::Int - terms::CartesianIndices{D} - perms::Matrix{Int} - function QGradChebyshevPolynomialBasis(::Type{T},order::Int,terms::CartesianIndices{D},perms::Matrix{Int}) where {D,T} - new{D,T}(order,terms,perms) - end -end - -Base.size(a::QGradChebyshevPolynomialBasis) = (_ndofs_qgrad_ch(a),) -Base.getindex(a::QGradChebyshevPolynomialBasis,i::Integer) = Chebyshev{:T}() -Base.IndexStyle(::QGradChebyshevPolynomialBasis) = IndexLinear() - -""" - QGradChebyshevPolynomialBasis{D}(::Type{T},order::Int) where {D,T} - -Returns a `QGradChebyshevPolynomialBasis` object. `D` is the dimension -of the coordinate space and `T` is the type of the components in the vector-value. -The `order` argument has the following meaning: the curl of the functions in this basis -is in the Q space of degree `order`. -""" -function QGradChebyshevPolynomialBasis{D}(::Type{T},order::Int) where {D,T} - @check T<:Real "T needs to be <:Real since represents the type of the components of the vector value" - _order = order + 1 - _t = tfill(_order+1,Val{D-1}()) - t = (_order,_t...) - terms = CartesianIndices(t) - perms = _prepare_perms(D) - QGradChebyshevPolynomialBasis(T,order,terms,perms) -end - -""" - num_terms(f::QGradChebyshevPolynomialBasis{D}) where {D} -""" -num_terms(f::QGradChebyshevPolynomialBasis{D}) where {D} = length(f.terms)*D - -get_order(f::QGradChebyshevPolynomialBasis) = f.order - -function return_cache(f::QGradChebyshevPolynomialBasis{D,T},x::AbstractVector{<:Point}) where {D,T} - @check D == length(eltype(x)) "Incorrect number of point components" - np = length(x) - ndof = _ndofs_qgrad_ch(f) - n = 1 + f.order+1 - V = VectorValue{D,T} - r = CachedArray(zeros(V,(np,ndof))) - v = CachedArray(zeros(V,(ndof,))) - c = CachedArray(zeros(T,(D,n))) - (r, v, c) -end - -function evaluate!(cache,f::QGradChebyshevPolynomialBasis{D,T},x::AbstractVector{<:Point}) where {D,T} - r, v, c = cache - np = length(x) - ndof = _ndofs_qgrad_ch(f) - n = 1 + f.order+1 - setsize!(r,(np,ndof)) - setsize!(v,(ndof,)) - setsize!(c,(D,n)) - for i in 1:np - @inbounds xi = x[i] - _evaluate_nd_qgrad_ch!(v,xi,f.order+1,f.terms,f.perms,c) - for j in 1:ndof - @inbounds r[i,j] = v[j] - end - end - r.array -end - -function return_cache( - fg::FieldGradientArray{1,QGradChebyshevPolynomialBasis{D,T}}, - x::AbstractVector{<:Point}) where {D,T} - - f = fg.fa - @check D == length(eltype(x)) "Incorrect number of point components" - np = length(x) - ndof = _ndofs_qgrad_ch(f) - n = 1 + f.order+1 - xi = testitem(x) - V = VectorValue{D,T} - G = gradient_type(V,xi) - r = CachedArray(zeros(G,(np,ndof))) - v = CachedArray(zeros(G,(ndof,))) - c = CachedArray(zeros(T,(D,n))) - g = CachedArray(zeros(T,(D,n))) - (r, v, c, g) -end - -function evaluate!( - cache, - fg::FieldGradientArray{1,QGradChebyshevPolynomialBasis{D,T}}, - x::AbstractVector{<:Point}) where {D,T} - - f = fg.fa - r, v, c, g = cache - np = length(x) - ndof = _ndofs_qgrad_ch(f) - n = 1 + f.order+1 - setsize!(r,(np,ndof)) - setsize!(v,(ndof,)) - setsize!(c,(D,n)) - setsize!(g,(D,n)) - V = VectorValue{D,T} - for i in 1:np - @inbounds xi = x[i] - _gradient_nd_qgrad_ch!(v,xi,f.order+1,f.terms,f.perms,c,g,V) - for j in 1:ndof - @inbounds r[i,j] = v[j] - end - end - r.array -end - -# Helpers - -_ndofs_qgrad_ch(f::QGradChebyshevPolynomialBasis{D}) where D = D*(length(f.terms)) - -function _evaluate_nd_qgrad_ch!( - v::AbstractVector{V}, - x, - order, - terms::CartesianIndices{D}, - perms::Matrix{Int}, - c::AbstractMatrix{T}) where {V,T,D} - - dim = D - for d in 1:dim - K = Val(order) - _evaluate_1d!(Chebyshev{:T},K,c,x,d) - end - - o = one(T) - k = 1 - m = zero(Mutable(V)) - js = eachindex(m) - z = zero(T) - - for ci in terms - - for j in js - - @inbounds for i in js - m[i] = z - end - - s = o - @inbounds for d in 1:dim - s *= c[d,ci[perms[d,j]]] - end - - m[j] = s - v[k] = m - k += 1 - - end - - end - -end - -function _gradient_nd_qgrad_ch!( - v::AbstractVector{G}, - x, - order, - terms::CartesianIndices{D}, - perms::Matrix{Int}, - c::AbstractMatrix{T}, - g::AbstractMatrix{T}, - ::Type{V}) where {G,T,D,V} - - dim = D - for d in 1:dim - K = Val(order) - _derivatives_1d!(Chebyshev{:T},K,(c,g),x,d) - end - - z = zero(Mutable(V)) - m = zero(Mutable(G)) - js = eachindex(z) - mjs = eachindex(m) - o = one(T) - zi = zero(T) - k = 1 - - for ci in terms - - for j in js - - s = z - for i in js - s[i] = o - end - - for q in 1:dim - for d in 1:dim - if d != q - @inbounds s[q] *= c[d,ci[perms[d,j]]] - else - @inbounds s[q] *= g[d,ci[perms[d,j]]] - end - end - end - - @inbounds for i in mjs - m[i] = zi - end - - for i in js - @inbounds m[i,j] = s[i] - end - @inbounds v[k] = m - k += 1 - - end - - end - -end - -############################################################################################ - -""" - struct QCurlGradChebyshevPolynomialBasis{...} <: AbstractArray{Monomial} - -This type implements a multivariate vector-valued polynomial basis -spanning the space needed for Raviart-Thomas reference elements on n-cubes. -The type parameters and fields of this `struct` are not public. -This type fully implements the [`Field`](@ref) interface, with up to first order -derivatives. -""" -struct QCurlGradChebyshevPolynomialBasis{D,T} <: AbstractVector{Chebyshev{:T}} - qgrad::QGradChebyshevPolynomialBasis{D,T} - function QCurlGradChebyshevPolynomialBasis(::Type{T},order::Int,terms::CartesianIndices{D},perms::Matrix{Int}) where {D,T} - qgrad = QGradChebyshevPolynomialBasis(T,order,terms,perms) - new{D,T}(qgrad) - end -end - -Base.size(a::QCurlGradChebyshevPolynomialBasis) = (length(a.qgrad),) -Base.getindex(a::QCurlGradChebyshevPolynomialBasis,i::Integer) = Chebyshev{:T}() -Base.IndexStyle(::QCurlGradChebyshevPolynomialBasis) = IndexLinear() - -""" - QCurlGradChebyshevPolynomialBasis{D}(::Type{T},order::Int) where {D,T} - -Returns a `QCurlGradChebyshevPolynomialBasis` object. `D` is the dimension -of the coordinate space and `T` is the type of the components in the vector-value. -The `order` argument has the following meaning: the divergence of the functions in this basis -is in the Q space of degree `order`. -""" -function QCurlGradChebyshevPolynomialBasis{D}(::Type{T},order::Int) where {D,T} - @check T<:Real "T needs to be <:Real since represents the type of the components of the vector value" - _order = order+1 - _t = tfill(_order,Val{D-1}()) - t = (_order+1,_t...) - terms = CartesianIndices(t) - perms = _prepare_perms(D) - QCurlGradChebyshevPolynomialBasis(T,order,terms,perms) -end - -return_type(::QCurlGradChebyshevPolynomialBasis{D,T}) where {D,T} = T - -function return_cache(f::QCurlGradChebyshevPolynomialBasis,x::AbstractVector{<:Point}) - return_cache(f.qgrad,x) -end - -function evaluate!(cache,f::QCurlGradChebyshevPolynomialBasis,x::AbstractVector{<:Point}) - evaluate!(cache,f.qgrad,x) -end - -function return_cache( - fg::FieldGradientArray{N,<:QCurlGradChebyshevPolynomialBasis}, - x::AbstractVector{<:Point}) where N - - f = fg.fa - return_cache(FieldGradientArray{N}(f.qgrad),x) -end - -function evaluate!( - cache, - fg::FieldGradientArray{N,<:QCurlGradChebyshevPolynomialBasis}, - x::AbstractVector{<:Point}) where N - - f = fg.fa - evaluate!(cache,FieldGradientArray{N}(f.qgrad),x) -end - -""" - num_terms(f::QCurlGradChebyshevPolynomialBasis{D,T}) where {D,T} -""" -num_terms(f::QCurlGradChebyshevPolynomialBasis{D,T}) where {D,T} = length(f.qgrad.terms)*D - -get_order(f::QCurlGradChebyshevPolynomialBasis{D,T}) where {D,T} = get_order(f.qgrad) - diff --git a/src/Polynomials/QCurlGradMonomialBases.jl b/src/Polynomials/QCurlGradMonomialBases.jl deleted file mode 100644 index e4b519dda..000000000 --- a/src/Polynomials/QCurlGradMonomialBases.jl +++ /dev/null @@ -1,74 +0,0 @@ - -""" - struct QCurlGradMonomialBasis{...} <: AbstractArray{Monomial} - -This type implements a multivariate vector-valued polynomial basis -spanning the space needed for Raviart-Thomas reference elements on n-cubes. -The type parameters and fields of this `struct` are not public. -This type fully implements the [`Field`](@ref) interface, with up to first order -derivatives. -""" -struct QCurlGradMonomialBasis{D,T} <: AbstractVector{Monomial} - qgrad::QGradMonomialBasis{D,T} - function QCurlGradMonomialBasis(::Type{T},order::Int,terms::CartesianIndices{D},perms::Matrix{Int}) where {D,T} - qgrad = QGradMonomialBasis(T,order,terms,perms) - new{D,T}(qgrad) - end -end - -Base.size(a::QCurlGradMonomialBasis) = (length(a.qgrad),) -# @santiagobadia : Not sure we want to create the monomial machinery -Base.getindex(a::QCurlGradMonomialBasis,i::Integer) = Monomial() -Base.IndexStyle(::QCurlGradMonomialBasis) = IndexLinear() - -""" - QCurlGradMonomialBasis{D}(::Type{T},order::Int) where {D,T} - -Returns a `QCurlGradMonomialBasis` object. `D` is the dimension -of the coordinate space and `T` is the type of the components in the vector-value. -The `order` argument has the following meaning: the divergence of the functions in this basis -is in the Q space of degree `order`. -""" -function QCurlGradMonomialBasis{D}(::Type{T},order::Int) where {D,T} - @check T<:Real "T needs to be <:Real since represents the type of the components of the vector value" - _order = order+1 - _t = tfill(_order,Val{D-1}()) - t = (_order+1,_t...) - terms = CartesianIndices(t) - perms = _prepare_perms(D) - QCurlGradMonomialBasis(T,order,terms,perms) -end - -return_type(::QCurlGradMonomialBasis{D,T}) where {D,T} = VectorValue{D,T} - -function return_cache(f::QCurlGradMonomialBasis,x::AbstractVector{<:Point}) - return_cache(f.qgrad,x) -end - -function evaluate!(cache,f::QCurlGradMonomialBasis,x::AbstractVector{<:Point}) - evaluate!(cache,f.qgrad,x) -end - -function return_cache( - fg::FieldGradientArray{N,<:QCurlGradMonomialBasis}, - x::AbstractVector{<:Point}) where N - - f = fg.fa - return_cache(FieldGradientArray{N}(f.qgrad),x) -end - -function evaluate!( - cache, - fg::FieldGradientArray{N,<:QCurlGradMonomialBasis}, - x::AbstractVector{<:Point}) where N - - f = fg.fa - evaluate!(cache,FieldGradientArray{N}(f.qgrad),x) -end - -""" - num_terms(f::QCurlGradMonomialBasis{D,T}) where {D,T} -""" -num_terms(f::QCurlGradMonomialBasis{D,T}) where {D,T} = length(f.qgrad.terms)*D - -get_order(f::QCurlGradMonomialBasis{D,T}) where {D,T} = get_order(f.qgrad) diff --git a/src/Polynomials/QGradLegendrePolynomialBases.jl b/src/Polynomials/QGradLegendrePolynomialBases.jl deleted file mode 100644 index 6ef867097..000000000 --- a/src/Polynomials/QGradLegendrePolynomialBases.jl +++ /dev/null @@ -1,306 +0,0 @@ - -""" - struct QGradLegendrePolynomialBasis{...} <: AbstractVector{Monomial} - -This type implements a multivariate vector-valued polynomial basis -spanning the space needed for Nedelec reference elements on n-cubes. -The type parameters and fields of this `struct` are not public. -This type fully implements the [`Field`](@ref) interface, with up to first order -derivatives. -""" -struct QGradLegendrePolynomialBasis{D,T} <: AbstractVector{Legendre} - order::Int - terms::CartesianIndices{D} - perms::Matrix{Int} - function QGradLegendrePolynomialBasis(::Type{T},order::Int,terms::CartesianIndices{D},perms::Matrix{Int}) where {D,T} - new{D,T}(order,terms,perms) - end -end - -Base.size(a::QGradLegendrePolynomialBasis) = (_ndofs_qgrad_leg(a),) -Base.getindex(a::QGradLegendrePolynomialBasis,i::Integer) = Legendre() -Base.IndexStyle(::QGradLegendrePolynomialBasis) = IndexLinear() - -""" - QGradLegendrePolynomialBasis{D}(::Type{T},order::Int) where {D,T} - -Returns a `QGradLegendrePolynomialBasis` object. `D` is the dimension -of the coordinate space and `T` is the type of the components in the vector-value. -The `order` argument has the following meaning: the curl of the functions in this basis -is in the Q space of degree `order`. -""" -function QGradLegendrePolynomialBasis{D}(::Type{T},order::Int) where {D,T} - @check T<:Real "T needs to be <:Real since represents the type of the components of the vector value" - _order = order + 1 - _t = tfill(_order+1,Val{D-1}()) - t = (_order,_t...) - terms = CartesianIndices(t) - perms = _prepare_perms(D) - QGradLegendrePolynomialBasis(T,order,terms,perms) -end - -""" - num_terms(f::QGradLegendrePolynomialBasis{D,T}) where {D,T} -""" -num_terms(f::QGradLegendrePolynomialBasis{D,T}) where {D,T} = length(f.terms)*D - -get_order(f::QGradLegendrePolynomialBasis) = f.order - -return_type(::QGradLegendrePolynomialBasis{D,T}) where {D,T} = VectorValue{D,T} - -function return_cache(f::QGradLegendrePolynomialBasis{D,T},x::AbstractVector{<:Point}) where {D,T} - @check D == length(eltype(x)) "Incorrect number of point components" - np = length(x) - ndof = _ndofs_qgrad_leg(f) - n = 1 + f.order+1 - V = VectorValue{D,T} - r = CachedArray(zeros(V,(np,ndof))) - v = CachedArray(zeros(V,(ndof,))) - c = CachedArray(zeros(T,(D,n))) - (r, v, c) -end - -function evaluate!(cache,f::QGradLegendrePolynomialBasis{D,T},x::AbstractVector{<:Point}) where {D,T} - r, v, c = cache - np = length(x) - ndof = _ndofs_qgrad_leg(f) - n = 1 + f.order+1 - setsize!(r,(np,ndof)) - setsize!(v,(ndof,)) - setsize!(c,(D,n)) - for i in 1:np - @inbounds xi = x[i] - _evaluate_nd_qgrad_leg!(v,xi,f.order+1,f.terms,f.perms,c) - for j in 1:ndof - @inbounds r[i,j] = v[j] - end - end - r.array -end - -function return_cache( - fg::FieldGradientArray{1,QGradLegendrePolynomialBasis{D,T}}, - x::AbstractVector{<:Point}) where {D,T} - - f = fg.fa - @check D == length(eltype(x)) "Incorrect number of point components" - np = length(x) - ndof = _ndofs_qgrad_leg(f) - n = 1 + f.order+1 - xi = testitem(x) - V = VectorValue{D,T} - G = gradient_type(V,xi) - r = CachedArray(zeros(G,(np,ndof))) - v = CachedArray(zeros(G,(ndof,))) - c = CachedArray(zeros(T,(D,n))) - g = CachedArray(zeros(T,(D,n))) - (r, v, c, g) -end - -function evaluate!( - cache, - fg::FieldGradientArray{1,QGradLegendrePolynomialBasis{D,T}}, - x::AbstractVector{<:Point}) where {D,T} - - f = fg.fa - r, v, c, g = cache - np = length(x) - ndof = _ndofs_qgrad_leg(f) - n = 1 + f.order+1 - setsize!(r,(np,ndof)) - setsize!(v,(ndof,)) - setsize!(c,(D,n)) - setsize!(g,(D,n)) - V = VectorValue{D,T} - for i in 1:np - @inbounds xi = x[i] - _gradient_nd_qgrad_leg!(v,xi,f.order+1,f.terms,f.perms,c,g,V) - for j in 1:ndof - @inbounds r[i,j] = v[j] - end - end - r.array -end - -# Helpers - -_ndofs_qgrad_leg(f::QGradLegendrePolynomialBasis{D}) where D = D*(length(f.terms)) - -function _evaluate_nd_qgrad_leg!( - v::AbstractVector{V}, - x, - order, - terms::CartesianIndices{D}, - perms::Matrix{Int}, - c::AbstractMatrix{T}) where {V,T,D} - - dim = D - for d in 1:dim - K = Val(order) - _evaluate_1d!(Legendre,K,c,x,d) - end - - o = one(T) - k = 1 - m = zero(Mutable(V)) - js = eachindex(m) - z = zero(T) - - for ci in terms - - for j in js - - @inbounds for i in js - m[i] = z - end - - s = o - @inbounds for d in 1:dim - s *= c[d,ci[perms[d,j]]] - end - - m[j] = s - v[k] = m - k += 1 - - end - - end - -end - -function _gradient_nd_qgrad_leg!( - v::AbstractVector{G}, - x, - order, - terms::CartesianIndices{D}, - perms::Matrix{Int}, - c::AbstractMatrix{T}, - g::AbstractMatrix{T}, - ::Type{V}) where {G,T,D,V} - - dim = D - for d in 1:dim - K = Val(order) - _derivatives_1d!(Legendre,K,(c,g),x,d) - end - - z = zero(Mutable(V)) - m = zero(Mutable(G)) - js = eachindex(z) - mjs = eachindex(m) - o = one(T) - zi = zero(T) - k = 1 - - for ci in terms - - for j in js - - s = z - for i in js - s[i] = o - end - - for q in 1:dim - for d in 1:dim - if d != q - @inbounds s[q] *= c[d,ci[perms[d,j]]] - else - @inbounds s[q] *= g[d,ci[perms[d,j]]] - end - end - end - - @inbounds for i in mjs - m[i] = zi - end - - for i in js - @inbounds m[i,j] = s[i] - end - @inbounds v[k] = m - k += 1 - - end - - end - -end - -############################################################################################ - -""" - struct QCurlGradLegendrePolynomialBasis{...} <: AbstractArray{Monomial} - -This type implements a multivariate vector-valued polynomial basis -spanning the space needed for Raviart-Thomas reference elements on n-cubes. -The type parameters and fields of this `struct` are not public. -This type fully implements the [`Field`](@ref) interface, with up to first order -derivatives. -""" -struct QCurlGradLegendrePolynomialBasis{D,T} <: AbstractVector{Legendre} - qgrad::QGradLegendrePolynomialBasis{D,T} - function QCurlGradLegendrePolynomialBasis(::Type{T},order::Int,terms::CartesianIndices{D},perms::Matrix{Int}) where {D,T} - qgrad = QGradLegendrePolynomialBasis(T,order,terms,perms) - new{D,T}(qgrad) - end -end - -Base.size(a::QCurlGradLegendrePolynomialBasis) = (length(a.qgrad),) -Base.getindex(a::QCurlGradLegendrePolynomialBasis,i::Integer) = Legendre() -Base.IndexStyle(::QCurlGradLegendrePolynomialBasis) = IndexLinear() - -""" - QCurlGradLegendrePolynomialBasis{D}(::Type{T},order::Int) where {D,T} - -Returns a `QCurlGradLegendrePolynomialBasis` object. `D` is the dimension -of the coordinate space and `T` is the type of the components in the vector-value. -The `order` argument has the following meaning: the divergence of the functions in this basis -is in the Q space of degree `order`. -""" -function QCurlGradLegendrePolynomialBasis{D}(::Type{T},order::Int) where {D,T} - @check T<:Real "T needs to be <:Real since represents the type of the components of the vector value" - _order = order+1 - _t = tfill(_order,Val{D-1}()) - t = (_order+1,_t...) - terms = CartesianIndices(t) - perms = _prepare_perms(D) - QCurlGradLegendrePolynomialBasis(T,order,terms,perms) -end - -return_type(::QCurlGradLegendrePolynomialBasis{D,T}) where {D,T} = VectorValue{D,T} - -function return_cache(f::QCurlGradLegendrePolynomialBasis,x::AbstractVector{<:Point}) - return_cache(f.qgrad,x) -end - -function evaluate!(cache,f::QCurlGradLegendrePolynomialBasis,x::AbstractVector{<:Point}) - evaluate!(cache,f.qgrad,x) -end - -function return_cache( - fg::FieldGradientArray{N,<:QCurlGradLegendrePolynomialBasis}, - x::AbstractVector{<:Point}) where N - - f = fg.fa - return_cache(FieldGradientArray{N}(f.qgrad),x) -end - -function evaluate!( - cache, - fg::FieldGradientArray{N,<:QCurlGradLegendrePolynomialBasis}, - x::AbstractVector{<:Point}) where N - - f = fg.fa - evaluate!(cache,FieldGradientArray{N}(f.qgrad),x) -end - -""" - num_terms(f::QCurlGradLegendrePolynomialBasis{D,T}) where {D,T} -""" -num_terms(f::QCurlGradLegendrePolynomialBasis{D,T}) where {D,T} = length(f.qgrad.terms)*D - -get_order(f::QCurlGradLegendrePolynomialBasis{D,T}) where {D,T} = get_order(f.qgrad) - -############################################################################################ diff --git a/src/Polynomials/QGradMonomialBases.jl b/src/Polynomials/QGradMonomialBases.jl deleted file mode 100644 index 1bf953a1b..000000000 --- a/src/Polynomials/QGradMonomialBases.jl +++ /dev/null @@ -1,231 +0,0 @@ - -""" - struct QGradMonomialBasis{...} <: AbstractVector{Monomial} - -This type implements a multivariate vector-valued polynomial basis -spanning the space needed for Nedelec reference elements on n-cubes. -The type parameters and fields of this `struct` are not public. -This type fully implements the [`Field`](@ref) interface, with up to first order -derivatives. -""" -struct QGradMonomialBasis{D,T} <: AbstractVector{Monomial} - order::Int - terms::CartesianIndices{D} - perms::Matrix{Int} - function QGradMonomialBasis(::Type{T},order::Int,terms::CartesianIndices{D},perms::Matrix{Int}) where {D,T} - new{D,T}(order,terms,perms) - end -end - -Base.size(a::QGradMonomialBasis) = (_ndofs_qgrad(a),) -# @santiagobadia : Not sure we want to create the monomial machinery -Base.getindex(a::QGradMonomialBasis,i::Integer) = Monomial() -Base.IndexStyle(::QGradMonomialBasis) = IndexLinear() - -""" - QGradMonomialBasis{D}(::Type{T},order::Int) where {D,T} - -Returns a `QGradMonomialBasis` object. `D` is the dimension -of the coordinate space and `T` is the type of the components in the vector-value. -The `order` argument has the following meaning: the curl of the functions in this basis -is in the Q space of degree `order`. -""" -function QGradMonomialBasis{D}(::Type{T},order::Int) where {D,T} - @check T<:Real "T needs to be <:Real since represents the type of the components of the vector value" - _order = order + 1 - _t = tfill(_order+1,Val{D-1}()) - t = (_order,_t...) - terms = CartesianIndices(t) - perms = _prepare_perms(D) - QGradMonomialBasis(T,order,terms,perms) -end - -""" - num_terms(f::QGradMonomialBasis{D,T}) where {D,T} -""" -num_terms(f::QGradMonomialBasis{D,T}) where {D,T} = length(f.terms)*D - -get_order(f::QGradMonomialBasis) = f.order - -return_type(::QGradMonomialBasis{D,T}) where {D,T} = VectorValue{D,T} - -function return_cache(f::QGradMonomialBasis{D,T},x::AbstractVector{<:Point}) where {D,T} - @check D == length(eltype(x)) "Incorrect number of point components" - np = length(x) - ndof = _ndofs_qgrad(f) - n = 1 + f.order+1 - V = VectorValue{D,T} - r = CachedArray(zeros(V,(np,ndof))) - v = CachedArray(zeros(V,(ndof,))) - c = CachedArray(zeros(T,(D,n))) - (r, v, c) -end - -function evaluate!(cache,f::QGradMonomialBasis{D,T},x::AbstractVector{<:Point}) where {D,T} - r, v, c = cache - np = length(x) - ndof = _ndofs_qgrad(f) - n = 1 + f.order+1 - setsize!(r,(np,ndof)) - setsize!(v,(ndof,)) - setsize!(c,(D,n)) - for i in 1:np - @inbounds xi = x[i] - _evaluate_nd_qgrad!(v,xi,f.order+1,f.terms,f.perms,c) - for j in 1:ndof - @inbounds r[i,j] = v[j] - end - end - r.array -end - -function return_cache( - fg::FieldGradientArray{1,QGradMonomialBasis{D,T}}, - x::AbstractVector{<:Point}) where {D,T} - - f = fg.fa - @check D == length(eltype(x)) "Incorrect number of point components" - np = length(x) - ndof = _ndofs_qgrad(f) - n = 1 + f.order+1 - xi = testitem(x) - V = VectorValue{D,T} - G = gradient_type(V,xi) - r = CachedArray(zeros(G,(np,ndof))) - v = CachedArray(zeros(G,(ndof,))) - c = CachedArray(zeros(T,(D,n))) - g = CachedArray(zeros(T,(D,n))) - (r, v, c, g) -end - -function evaluate!( - cache, - fg::FieldGradientArray{1,QGradMonomialBasis{D,T}}, - x::AbstractVector{<:Point}) where {D,T} - - f = fg.fa - r, v, c, g = cache - np = length(x) - ndof = _ndofs_qgrad(f) - n = 1 + f.order+1 - setsize!(r,(np,ndof)) - setsize!(v,(ndof,)) - setsize!(c,(D,n)) - setsize!(g,(D,n)) - V = VectorValue{D,T} - for i in 1:np - @inbounds xi = x[i] - _gradient_nd_qgrad!(v,xi,f.order+1,f.terms,f.perms,c,g,V) - for j in 1:ndof - @inbounds r[i,j] = v[j] - end - end - r.array -end - -# Helpers - -_ndofs_qgrad(f::QGradMonomialBasis{D}) where D = D*(length(f.terms)) - -function _evaluate_nd_qgrad!( - v::AbstractVector{V}, - x, - order, - terms::CartesianIndices{D}, - perms::Matrix{Int}, - c::AbstractMatrix{T}) where {V,T,D} - - dim = D - for d in 1:dim - K = Val(order) - _evaluate_1d!(Monomial,K,c,x,d) - end - - o = one(T) - k = 1 - m = zero(Mutable(V)) - js = eachindex(m) - z = zero(T) - - for ci in terms - - for j in js - - @inbounds for i in js - m[i] = z - end - - s = o - @inbounds for d in 1:dim - s *= c[d,ci[perms[d,j]]] - end - - m[j] = s - v[k] = m - k += 1 - - end - - end - -end - -function _gradient_nd_qgrad!( - v::AbstractVector{G}, - x, - order, - terms::CartesianIndices{D}, - perms::Matrix{Int}, - c::AbstractMatrix{T}, - g::AbstractMatrix{T}, - ::Type{V}) where {G,T,D,V} - - dim = D - for d in 1:dim - K = Val(order) - _derivatives_1d!(Monomial,K,(c,g),x,d) - end - - z = zero(Mutable(V)) - m = zero(Mutable(G)) - js = eachindex(z) - mjs = eachindex(m) - o = one(T) - zi = zero(T) - k = 1 - - for ci in terms - - for j in js - - s = z - for i in js - s[i] = o - end - - for q in 1:dim - for d in 1:dim - if d != q - @inbounds s[q] *= c[d,ci[perms[d,j]]] - else - @inbounds s[q] *= g[d,ci[perms[d,j]]] - end - end - end - - @inbounds for i in mjs - m[i] = zi - end - - for i in js - @inbounds m[i,j] = s[i] - end - @inbounds v[k] = m - k += 1 - - end - - end - -end - diff --git a/src/Polynomials/QSpaceBases.jl b/src/Polynomials/QSpaceBases.jl deleted file mode 100644 index e69de29bb..000000000 From e5d4df67487bd7dc837e68ce9cf4c9c5896399bc Mon Sep 17 00:00:00 2001 From: JordiManyer Date: Thu, 19 Dec 2024 15:08:41 +1100 Subject: [PATCH 062/105] More notes on pullbacks --- docs/src/dev-notes/pullbacks.md | 55 ++++++++++++++++++++++++++++++--- 1 file changed, 50 insertions(+), 5 deletions(-) diff --git a/docs/src/dev-notes/pullbacks.md b/docs/src/dev-notes/pullbacks.md index c658ec3a6..467963229 100644 --- a/docs/src/dev-notes/pullbacks.md +++ b/docs/src/dev-notes/pullbacks.md @@ -1,7 +1,9 @@ -# Pullbacks +# FE basis transformations -Consider a reference polytope ``\hat{K}``, mapped to the physical space by a **geometrical map** ``G``, i.e. ``K = G(\hat{K})``. Consider also a linear function space on the reference polytope ``\hat{V}``, and a set of unisolvent degrees of freedom represented by moments in the dual space ``\hat{V}^*``. +## Notation + +Consider a reference polytope ``\hat{K}``, mapped to the physical space by a **geometrical map** ``F``, i.e. ``K = F(\hat{K})``. Consider also a linear function space on the reference polytope ``\hat{V}``, and a set of unisolvent degrees of freedom represented by moments in the dual space ``\hat{V}^*``. Throughout this document, we will use the following notation: @@ -10,12 +12,16 @@ Throughout this document, we will use the following notation: - ``\sigma \in V^*`` is a **physical moment** ``\sigma : V \rightarrow \mathbb{R}``. A basis of ``V^*`` is denoted by ``\Sigma = \{\sigma\}``. - ``\hat{\sigma} \in \hat{V}^*`` is a **reference moment** ``\hat{\sigma} : \hat{V} \rightarrow \mathbb{R}``. A basis of ``\hat{V}^*`` is denoted by ``\hat{\Sigma} = \{\hat{\sigma}\}``. +## Pullbacks and Pushforwards + We define a **pushforward** map as ``F^* : \hat{V} \rightarrow V``, mapping reference fields to physical fields. Given a pushforward ``F^*``, we define: - The **pullback** ``F_* : V^* \rightarrow \hat{V}^*``, mapping physical moments to reference moments. Its action on physical dofs is defined in terms of the pushforward map ``F^*`` as ``\hat{\sigma} = F_*(\sigma) := \sigma \circ F^*``. - The **inverse pushforward** ``(F^*)^{-1} : V \rightarrow \hat{V}``, mapping physical fields to reference fields. - The **inverse pullback** ``(F_*)^{-1} : \hat{V}^* \rightarrow V^*``, mapping reference moments to physical moments. Its action on reference dofs is defined in terms of the inverse pushforward map ``(F^*)^{-1}`` as ``\sigma = (F_*)^{-1}(\hat{\sigma}) := \hat{\sigma} \circ (F^*)^{-1}``. +## Change of basis + In many occasions, we will have that (as a basis) ```math @@ -28,10 +34,49 @@ To maintain conformity and proper scaling in these cases, we define cell-depende \hat{\Sigma} = P F_*(\Sigma), \quad \text{and} \quad \Phi = M F^*(\hat{\Phi}) ``` -An important result from linear algebra is that ``P = M^*`` (or just the transpose if we represent them as real matrices). +An important result from [1, Theorem 3.1] is that ``P = M^T``. + +!!! details + [1, Lemma 2.6]: A key ingredient is that given ``M`` a matrix we have ``\Sigma (M \Phi) = \Sigma (\Phi) M^T`` since + ```math + [\Sigma (M \Phi)]_{ij} = \sigma_i (M_{jk} \varphi_k) = \sigma_i \varphi_k M_{jk} = [\Sigma (\Phi) M^T]_{ij} + ``` + where we have used that moments are linear. + +We then have the following diagram: + +```math +\hat{V}^* \xleftarrow{P} \hat{V}^* \xleftarrow{F_*} V^* \\ +\hat{V} \xrightarrow{F^*} V \xrightarrow{P^T} V +``` + +!!! details + The above diagram is well defined, since we have + ```math + \hat{\Sigma}(\hat{\Phi}) = P F_* (\Sigma)(F^{-*} (P^{-T} \Phi)) = P \Sigma (F^* (F^{-*} P^{-T} \Phi)) = P \Sigma (P^{-T} \Phi) = P \Sigma (\Phi) P^{-1} = Id \\ + \Sigma(\Phi) = F_*^{-1}(P^{-1}\hat{\Sigma})(P^T F^*(\hat{\Phi})) = P^{-1} \hat{\Sigma} (F^{-*}(P^T F^*(\hat{\Phi}))) = P^{-1} \hat{\Sigma} (P^T \hat{\Phi}) = P^{-1} \hat{\Sigma}(\hat{\Phi}) P = Id + ``` + From an implementation point of view, it is more natural to build ``P^{-1}`` and then retrieve all other matrices by transposition/inversion. +## Interpolation + +In each cell ``K`` and for ``C_b^k(K)`` the space of functions defined on ``K`` with at least ``k`` bounded derivatives, we define the interpolation operator ``I_K : C_b^k(K) \rightarrow V`` as + +```math +I_K(g) = \Sigma(g) \Phi \quad, \quad \Sigma(g) = P^{-1} \hat{\Sigma}(F^{-*}(g)) +``` + +## Implementation notes + +!!! note + In [2], Covariant and Contravariant Piola maps preserve exactly (without any sign change) the normal and tangential components of a vector field. + I am quite sure that the discrepancy is coming from the fact that the geometrical information in the reference polytope is globaly oriented. + For instance, the normals ``n`` and ``\hat{n}`` both have the same orientation, i.e ``n = (||\hat{e}||/||e||) (det J) J^{-T} \hat{n}``. Therefore ``\hat{n}`` is not fully local. See [2, Equation 2.11]. + In our case, we will be including the sign change in the transformation matrices, which will include all cell-and-dof-dependent information. + ## References -- [Kirby 2017, A general approach to transforming finite elements.](https://arxiv.org/abs/1706.09017) -- [Aznaran et al. 2021, Transformations for Piola-mapped elements.](https://arxiv.org/abs/2110.13224) +[1] [Kirby 2017, A general approach to transforming finite elements.](https://arxiv.org/abs/1706.09017) + +[2] [Aznaran et al. 2021, Transformations for Piola-mapped elements.](https://arxiv.org/abs/2110.13224) From 69c42c1b7dca54dc968ff6a598ed567f2cd79896 Mon Sep 17 00:00:00 2001 From: Antoine Marteau Date: Thu, 19 Dec 2024 09:09:40 +1100 Subject: [PATCH 063/105] PCurlGrad factorization - new type NonTensorRTPolyBasis, which is a QCurlGradMonomialBasis general for any hierarchical basis <:Polynomial (including Monomial) - removed perms from NonTensorRTPolyBasis (former QCurlGradMonomialBasis), it served no purpose excep changing the order of the basis function. This means that the order of the basis function did change --- src/Polynomials/BernsteinBases.jl | 2 +- src/Polynomials/ChebyshevBases.jl | 4 +- src/Polynomials/CompWiseTensorPolyBases.jl | 16 +- src/Polynomials/LegendreBases.jl | 4 +- src/Polynomials/MonomialBases.jl | 4 +- src/Polynomials/NedelecPrebasisOnSimplex.jl | 31 +- src/Polynomials/NonTensorRTPolyBasis.jl | 259 +++++++++++++ src/Polynomials/OldMonomialHelpers.jl | 344 ------------------ src/Polynomials/PCurlGradLegendreBases.jl | 282 -------------- src/Polynomials/PCurlGradMonomialBases.jl | 284 --------------- src/Polynomials/PolynomialInterfaces.jl | 8 +- src/Polynomials/Polynomials.jl | 11 +- src/Polynomials/TensorPolynomialBases.jl | 2 +- src/ReferenceFEs/RaviartThomasRefFEs.jl | 2 +- .../deprecated/RaviartThomasRefFEs.jl | 6 +- .../PCurlGradMonomialBasesTests.jl | 70 ++-- .../RaviartThomasRefFEsTests.jl | 4 +- 17 files changed, 344 insertions(+), 989 deletions(-) create mode 100644 src/Polynomials/NonTensorRTPolyBasis.jl delete mode 100644 src/Polynomials/PCurlGradLegendreBases.jl delete mode 100644 src/Polynomials/PCurlGradMonomialBases.jl diff --git a/src/Polynomials/BernsteinBases.jl b/src/Polynomials/BernsteinBases.jl index 10881dd76..2681f4379 100644 --- a/src/Polynomials/BernsteinBases.jl +++ b/src/Polynomials/BernsteinBases.jl @@ -5,7 +5,7 @@ Type representing Bernstein polynomials """ struct Bernstein <: Polynomial end -isHierarchical(::Bernstein) = false +isHierarchical(::Type{Bernstein}) = false """ BernsteinBasis{D,V,K} = TensorPolynomialBasis{D,V,K,Bernstein} diff --git a/src/Polynomials/ChebyshevBases.jl b/src/Polynomials/ChebyshevBases.jl index 0b74c4140..c397b5328 100644 --- a/src/Polynomials/ChebyshevBases.jl +++ b/src/Polynomials/ChebyshevBases.jl @@ -6,7 +6,7 @@ where `kind` is either `:T` or `:U` for first and second kind Chebyshev polynomi """ struct Chebyshev{kind} <: Polynomial end -isHierarchical(::Chebyshev) = true +isHierarchical(::Type{<:Chebyshev}) = true """ ChebyshevBasis{D,V,kind,K} = TensorPolynomialBasis{D,V,K,Chebyshev{kind}} @@ -29,7 +29,7 @@ TensorPolynomialBasis{D}(::Type{Chebyshev{:U}}, args...) where D = @notimplement QGradChebyshevBasis(args...; kind=:T) = QGradBasis(Chebyshev{kind}, args...) #PGradChebyshevBasis(args...; kind=:T) = PGradBasis(Chebyshev{kind}, args...) QCurlGradChebyshevBasis(args...; kind=:T) = QCurlGradBasis(Chebyshev{kind}, args...) -#PCurlGradChebyshevBasis(args...; kind=:T) = PCurlGradBasis(Chebyshev{kind}, args...) +PCurlGradChebyshevBasis(args...; kind=:T) = PCurlGradBasis(Chebyshev{kind}, args...) # 1D evaluation implementation diff --git a/src/Polynomials/CompWiseTensorPolyBases.jl b/src/Polynomials/CompWiseTensorPolyBases.jl index 88fe45ea7..7c003caa7 100644 --- a/src/Polynomials/CompWiseTensorPolyBases.jl +++ b/src/Polynomials/CompWiseTensorPolyBases.jl @@ -14,7 +14,8 @@ of 1D ℙ¹ spaces of order oₗₙ for 1 ≤ n ≤ `D`, that is: ⋮\\ 𝕊ᴸ = ℙ¹ₒ(`L`,1) ⊗ … ⊗ ℙ¹ₒ(`L`,`D`) -The `L`×`D` matrix of orders o is given in the constructor, and `K` is the maximum of o. +The `L`×`D` matrix of orders o is given in the constructor, and `K` is the +maximum of o. """ struct CompWiseTensorPolyBasis{D,V,K,PT,L} <: PolynomialBasis{D,V,K,PT} orders::SMatrix{L,D,Int} @@ -234,22 +235,19 @@ end @generated function _comp_wize_set_gradient!( v::AbstractVector{G},s,k,::Val{l},::Type{V}) where {G,l,V} - m = Array{String}(undef, size(G)) N_val_dims = length(size(V)) s_size = size(G)[1:end-N_val_dims] body = "T = eltype(s); z = zero(T);" - #for i in CartesianIndices(m) - # m[i] = "z" - #end + m = Array{String}(undef, size(G)) m .= "z" + for ci in CartesianIndices(s_size) m[ci,l] = "(@inbounds s[$ci])" end body *= "@inbounds v[k] = ($(join(tuple(m...), ", ")));" body = Meta.parse(string("begin ",body," end")) - #println(body) return Expr(:block, body ,:(return k+1)) end @@ -314,8 +312,8 @@ end """ QGradBasis(::Type{PT}, ::Val{D}, ::Type{T}, order::Int) :: PolynomialBasis -Return a basis of ℚ\\_order ⊕ x × (ℚ\\_order \\ ℚ\\_{order-1}), the polynomial space -for Nedelec elements on `D`-dimensonal cubes with scalar type `T`. +Return a basis of (ℚ\\_order)ᴰ ⊕ x × ( ℚ\\_order \\ ℚ\\_{order-1}) )ᴰ, the +polynomial space for Nedelec elements on `D`-dimensonal cubes with scalar type `T`. The `order` argument has the following meaning: the curl of the functions in this basis is in the ℚ space of degree `order`. @@ -349,7 +347,7 @@ end """ QCurlGradBasis(::Type{PT}, ::Val{D}, ::Type{T}, order::Int) :: PolynomialBasis -Return a basis of ℚ\\_order ⊕ x ⋅ (ℚ\\_order \\ ℚ\\_{order-1}), the polynomial space +Return a basis of (ℚ\\_order)ᴰ ⊕ x (ℚ\\_order \\ ℚ\\_{order-1}), the polynomial space for Raviart-Thomas elements on `D`-dimensonal cubes with scalar type `T`. The `order` argument has the following meaning: the divergence of the functions diff --git a/src/Polynomials/LegendreBases.jl b/src/Polynomials/LegendreBases.jl index f42ded5e1..2204145b7 100644 --- a/src/Polynomials/LegendreBases.jl +++ b/src/Polynomials/LegendreBases.jl @@ -5,7 +5,7 @@ Type representing the Legendre polynomials """ struct Legendre <: Polynomial end -isHierarchical(::Legendre) = true +isHierarchical(::Type{Legendre}) = true """ LegendreBasis{D,V,K} = TensorPolynomialBasis{D,V,K,Legendre} @@ -26,7 +26,7 @@ LegendreBasis(args...) = TensorPolynomialBasis(Legendre, args...) QGradLegendreBasis(args...) = QGradBasis(Legendre, args...) #PGradLegendreBasis(args...) = PGradBasis(Legendre, args...) QCurlGradLegendreBasis(args...) = QCurlGradBasis(Legendre, args...) -#PCurlGradLegendreBasis(args...) = PCurlGradBasis(Legendre, args...) +PCurlGradLegendreBasis(args...) = PCurlGradBasis(Legendre, args...) # 1D evaluation implementation diff --git a/src/Polynomials/MonomialBases.jl b/src/Polynomials/MonomialBases.jl index 252cebf22..a0d7f63d5 100644 --- a/src/Polynomials/MonomialBases.jl +++ b/src/Polynomials/MonomialBases.jl @@ -5,7 +5,7 @@ Type representing the monomial polynomials """ struct Monomial <: Polynomial end -isHierarchical(::Monomial) = true +isHierarchical(::Type{Monomial}) = true """ MonomialBasis{D,V,K} = TensorPolynomialBasis{D,V,K,Monomial} @@ -26,7 +26,7 @@ MonomialBasis(args...) = TensorPolynomialBasis(Monomial, args...) QGradMonomialBasis(args...) = QGradBasis(Monomial, args...) #PGradMonomialBasis(args...) = PGradBasis(Monomial, args...) QCurlGradMonomialBasis(args...) = QCurlGradBasis(Monomial, args...) -#PCurlGradMonomialBasis(args...) = PCurlGradBasis(Monomial, args...) +PCurlGradMonomialBasis(args...) = PCurlGradBasis(Monomial, args...) # 1D evaluation implementation diff --git a/src/Polynomials/NedelecPrebasisOnSimplex.jl b/src/Polynomials/NedelecPrebasisOnSimplex.jl index 895c4da1f..0bd420387 100644 --- a/src/Polynomials/NedelecPrebasisOnSimplex.jl +++ b/src/Polynomials/NedelecPrebasisOnSimplex.jl @@ -14,7 +14,6 @@ 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 return_type(::NedelecPrebasisOnSimplex{D}) where {D} = VectorValue{D,Float64} @@ -22,7 +21,7 @@ return_type(::NedelecPrebasisOnSimplex{D}) where {D} = VectorValue{D,Float64} function return_cache( f::NedelecPrebasisOnSimplex{D},x::AbstractVector{<:Point}) where D np = length(x) - ndofs = num_terms(f) + ndofs = length(f) V = eltype(x) a = zeros(V,(np,ndofs)) k = f.order+1 @@ -36,7 +35,7 @@ function evaluate!( ca,cP,P = cache k = f.order+1 np = length(x) - ndofs = num_terms(f) + ndofs = length(f) ndofsP = length(P) setsize!(ca,(np,ndofs)) Px = evaluate!(cP,P,x) @@ -82,7 +81,7 @@ function evaluate!( ca,cP,P = cache k = f.order+1 np = length(x) - ndofs = num_terms(f) + ndofs = length(f) ndofsP = length(P) setsize!(ca,(np,ndofs)) a = ca.array @@ -114,7 +113,7 @@ function return_cache( x::AbstractVector{<:Point}) where D f = g.fa np = length(x) - ndofs = num_terms(f) + ndofs = length(f) xi = testitem(x) V = eltype(x) G = gradient_type(V,xi) @@ -134,7 +133,7 @@ function evaluate!( f = g.fa k = f.order+1 np = length(x) - ndofs = num_terms(f) + ndofs = length(f) setsize!(ca,(np,ndofs)) a = ca.array fill!(a,zero(eltype(a))) @@ -210,7 +209,7 @@ function evaluate!( ca,cP,P = cache k = f.order+1 np = length(x) - ndofs = num_terms(f) + ndofs = length(f) setsize!(ca,(np,ndofs)) a = ca.array fill!(a,zero(eltype(a))) @@ -242,3 +241,21 @@ function evaluate!( a end +""" + PGradBasis(::Type{PT}, ::Val{D}, ::Type{T}, order::Int) :: PolynomialBasis + +Return a basis of ℙ_order ⊕ x × (ℙ_order\\ℙ_{order-1}), the polynomial space +for Nedelec elements on `D`-dimensonal simplices with scalar type `T`. + +The `order` argument has the following meaning: the curl of the functions in +this basis is in the ℙ space of degree `order`. +""" +function PGradBasis(::Type{PT},::Val{D},::Type{T},order::Int) where {PT,D,T} + @notimplemented "only implemented for monomials" +end + +function PGradBasis(::Type{Monomial},::Val{D},::Type{T},order::Int) where {D,T} + @check T<:Real "T needs to be <:Real since represents the type of the components of the vector value" + NedelecPrebasisOnSimplex{D}(order) +end + diff --git a/src/Polynomials/NonTensorRTPolyBasis.jl b/src/Polynomials/NonTensorRTPolyBasis.jl new file mode 100644 index 000000000..1deb80886 --- /dev/null +++ b/src/Polynomials/NonTensorRTPolyBasis.jl @@ -0,0 +1,259 @@ +""" + NonTensorRTPolyBasis{D,V,K,PT} <: PolynomialBasis{D,V,K,PT} + +Basis of ℝ𝕋ᴰₙ = (𝕊ₙ)ᴰ ⊕ x (𝕊ₙ\\𝕊₍ₙ₋₁₎) +where 𝕊ₙ is a multivariate scalar polynomial space of maximum degree n = `K`-1. + +ℝ𝕋ᴰₙ is the space needed for Raviart-Thomas elements with divergence in 𝕊ₙ. +Its maximum degree is n+1 = `K`. `get_order` on it returns `K`. + +The multivariate scalar space 𝕊ₙ, typically ℙₙ, doesn't need to have a tensor +product structure of 1D scalar spaces. Thus, the ℝ𝕋ᴰₙ component's scalar spaces +are not tensor products either. + +𝕊ₙ must admit a basis computable using products of 1D polynomials of the `PT` +type, `PT` thus needs to be hierarchical, see [`isHierarchical`](@ref). + +Indeed, 𝕊ₙ is defined like a scalar valued `TensorPolynomialBasis` via the +`_filter` argument of the constructor, by default `_p_filter`. +""" +struct NonTensorRTPolyBasis{D,V,K,PT} <: PolynomialBasis{D,V,K,PT} + pterms::Vector{CartesianIndex{D}} + sterms::Vector{CartesianIndex{D}} + + """ + NonTensorRTPolyBasis{D}(::Type{PT}, ::Type{T}, order::Int, _filter::Function=_p_filter) + + Where `_filter` defines 𝕊ₙ and `order` = n = K-1 (cf. struct docstring). + """ + function NonTensorRTPolyBasis{D}( + ::Type{PT}, ::Type{T}, order::Int, + _filter::Function=_p_filter + ) where {PT,D,T} + + @check T<:Real "T needs to be <:Real since represents the type of the components of the vector value" + @check D > 1 + @check isHierarchical(PT) "The polynomial basis must be hierarchichal for this space." + + V = VectorValue{D,T} + + # terms defining 𝕊_order + P_k = MonomialBasis(Val(D), T, order, _filter) + pterms = copy(P_k.terms) + msg = "Some term defining `𝕊ₙ` contain a higher index than the maximum, + `order`+1, please fix the `_filter` argument" + @check all( pterm -> (maximum(Tuple(pterm), init=0) <= order+1), pterms) msg + + # terms defining 𝕊_order\𝕊_{order-1} + _minus_one_order_filter = term -> _filter(Tuple(term) .- 1, order-1) # .-1 for index / degree shift + sterms = filter(!_minus_one_order_filter, pterms) + + new{D,V,order+1,PT}(pterms,sterms) + end +end + +Base.size(a::NonTensorRTPolyBasis{D}) where {D} = D*length(a.pterms) + length(a.sterms) +Base.getindex(a::NonTensorRTPolyBasis,i::Integer) = Monomial() +Base.IndexStyle(::NonTensorRTPolyBasis) = IndexLinear() + +function evaluate!( + cache,f::NonTensorRTPolyBasis{D,V,K,PT}, + x::AbstractVector{<:Point}) where {D,V,K,PT} + + r, v, c = cache + np = length(x) + ndof = length(f) + ndof_1d = get_order(f) + 1 # K+1 + setsize!(r,(np,ndof)) + setsize!(v,(ndof,)) + setsize!(c,(D,ndof_1d)) + for i in 1:np + @inbounds xi = x[i] + _evaluate_nd_rt!(PT,v,xi,K,f.pterms,f.sterms,c) + for j in 1:ndof + @inbounds r[i,j] = v[j] + end + end + r.array +end + +function evaluate!(cache, + fg::FieldGradientArray{1,NonTensorRTPolyBasis{D,V,K,PT}}, + x::AbstractVector{<:Point}) where {D,V,K,PT} + + f = fg.fa + r, v, c, g = cache + np = length(x) + ndof = length(f) + ndof_1d = get_order(f) + 1 # K+1 + setsize!(r,(np,ndof)) + setsize!(v,(ndof,)) + setsize!(c,(D,ndof_1d)) + setsize!(g,(D,ndof_1d)) + for i in 1:np + @inbounds xi = x[i] + _gradient_nd_rt!(PT,v,xi,K,f.pterms,f.sterms,c,g,V) + for j in 1:ndof + @inbounds r[i,j] = v[j] + end + end + r.array +end + + +# Helpers + + +function _evaluate_nd_rt!( + ::Type{PT}, + v::AbstractVector{V}, # V = VectorValue{D,T} + x, + order, + pterms::Vector{CartesianIndex{D}}, + sterms::Vector{CartesianIndex{D}}, + c::AbstractMatrix{T}) where {PT,V,T,D} + + for d in 1:D + K = Val(order) + _evaluate_1d!(PT,K,c,x,d) + end + + m = zero(Mutable(V)) + k = 1 + + @inbounds begin + for l in 1:D + for ci in pterms + + s = one(T) + for d in 1:D + s *= c[d,ci[d]] + end + + k = _comp_wize_set_value!(v,s,k,l) + end + end + + for ci in sterms + for i in 1:D + m[i] = zero(T) + end + + for l in 1:D + + s = x[l] + for d in 1:D + s *= c[d,ci[d]] + end + + m[l] = s + end + + v[k] = m + k += 1 + end + end +end + +function _gradient_nd_rt!( + ::Type{PT}, + v::AbstractVector{G}, + x, + order, + pterms::Vector{CartesianIndex{D}}, + sterms::Vector{CartesianIndex{D}}, + c::AbstractMatrix{T}, + g::AbstractMatrix{T}, + ::Type{V}) where {PT,G,T,D,V} + + for d in 1:D + K = Val(order) + _derivatives_1d!(PT,K,(c,g),x,d) + end + + s = zero(Mutable(V)) + m = zero(Mutable(G)) + k = 1 + + @inbounds begin + for l in 1:D + for ci in pterms + + for i in eachindex(s) + s[i] = one(T) + end + + for q in 1:D + for d in 1:D + if d != q + s[q] *= c[d,ci[d]] + else + s[q] *= g[d,ci[d]] + end + end + end + + k = _comp_wize_set_gradient!(v,s,k,Val(l),V) + end + end + + for ci in sterms + + for i in eachindex(m) + m[i] = zero(T) + end + + for l in 1:D + + for i in eachindex(s) + s[i] = x[l] + end + + aux = one(T) + for q in 1:D + aux *= c[q,ci[q]] + for d in 1:D + if d != q + s[q] *= c[d,ci[d]] + else + s[q] *= g[d,ci[d]] + end + end + end + s[l] += aux + + for i in 1:D + m[i,l] = s[i] + end + end + v[k] = m + k += 1 + end + end +end + +""" + PCurlGradBasis(::Type{PT}, ::Val{D}, ::Type{T}, order::Int) :: PolynomialBasis + +Return a basis of ℙ_`order` ⊕ x ⋅ (ℙ_`order`\\ℙ_{`order`-1}), the polynomial +space for Raviart-Thomas elements on `D`-dimensional simplices with scalar type `T`. + +The `order` argument of this function has the following meaning: the divergence +of the functions in this basis is in the ℙ space of degree `order`. + +`PT<:Polynomial` is the choice of scalar 1D polynomial basis, it must be +hierarchichal, see [`isHierarchichal`](@ref). + +Returns a `NonTensorRTPolyBasis{D,VectorValue{D,T},order+1,PT}` object, +or `TensorPolynomialBasis{1,VectorValue{1,T},order+1,PT}` for `D`=1. +""" +function PCurlGradBasis(::Type{PT},::Val{D},::Type{T},order::Int) where {PT,D,T} + NonTensorRTPolyBasis{D}(PT, T, order) +end + +function PCurlGradBasis(::Type{PT},::Val{1},::Type{T},order::Int) where {PT,T} + @check T<:Real "T needs to be <:Real since represents the type of the components of the vector value" + + V = VectorValue{1,T} + TensorPolynomialBasis(PT, Val(1), V, order+1) +end diff --git a/src/Polynomials/OldMonomialHelpers.jl b/src/Polynomials/OldMonomialHelpers.jl index 413b24013..e69de29bb 100644 --- a/src/Polynomials/OldMonomialHelpers.jl +++ b/src/Polynomials/OldMonomialHelpers.jl @@ -1,344 +0,0 @@ - -function _prepare_perms(D) - perms = zeros(Int,D,D) - for j in 1:D - for d in j:D - perms[d,j] = d-j+1 - end - for d in 1:(j-1) - perms[d,j] = d+(D-j)+1 - end - end - perms -end - -function _evaluate_nd!( - v::AbstractVector{V}, - x, - orders, - terms::AbstractVector{CartesianIndex{D}}, - c::AbstractMatrix{T}) where {V,T,D} - - dim = D - for d in 1:dim - Kd = Val(orders[d]) - _evaluate_1d!(Monomial,Kd,c,x,d) - end - - o = one(T) - k = 1 - - for ci in terms - - s = o - for d in 1:dim - @inbounds s *= c[d,ci[d]] - end - - k = _set_value!(v,s,k) - - end - -end - -function _set_value!(v::AbstractVector{V},s::T,k) where {V,T} - ncomp = num_indep_components(V) - z = zero(T) - @inbounds for j in 1:ncomp - v[k] = ntuple(i -> ifelse(i == j, s, z),Val(ncomp)) - k += 1 - end - k -end - -function _set_value!(v::AbstractVector{<:Real},s,k) - @inbounds v[k] = s - k+1 -end - -function _gradient_nd!( - v::AbstractVector{G}, - x, - orders, - terms::AbstractVector{CartesianIndex{D}}, - c::AbstractMatrix{T}, - g::AbstractMatrix{T}, - z::AbstractVector{T}, - ::Type{V}) where {G,T,D,V} - - dim = D - for d in 1:dim - Kd = Val(orders[d]) - _derivatives_1d!(Monomial,Kd,(c,g),x,d) - end - - o = one(T) - k = 1 - - for ci in terms - - 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!(v,s,k,V) - - end - -end - -function _set_gradient!( - v::AbstractVector{G},s,k,::Type{<:Real}) where G - - @inbounds v[k] = s - k+1 -end - -@generated function _set_gradient!( - v::AbstractVector{G},s,k,::Type{V}) where {V,G} - # Git blame me for readable non-generated version - - w = zero(V) - m = Array{String}(undef, size(G)) - N_val_dims = length(size(V)) - s_size = size(G)[1:end-N_val_dims] - - body = "T = eltype(s); z = zero(T);" - for ci in CartesianIndices(s_size) - id = join(Tuple(ci)) - body *= "@inbounds s$id = s[$ci];" - end - - for j in CartesianIndices(w) - for i in CartesianIndices(m) - m[i] = "z" - end - for ci in CartesianIndices(s_size) - id = join(Tuple(ci)) - m[ci,j] = "s$id" - end - body *= "@inbounds v[k] = ($(join(tuple(m...), ", ")));" - body *= "k = k + 1;" - end - - body = Meta.parse(string("begin ",body," end")) - return Expr(:block, body ,:(return k)) -end - -# Specialization for SymTensorValue and SymTracelessTensorValue, -# necessary as long as outer(Point, V<:AbstractSymTensorValue)::G does not -# return a tensor type that implements the appropriate symmetries of the -# gradient (and hessian) -@generated function _set_gradient!( - v::AbstractVector{G},s,k,::Type{V}) where {V<:AbstractSymTensorValue{D},G} where D - # Git blame me for readable non-generated version - - T = eltype(s) - m = Array{String}(undef, size(G)) - s_length = size(G)[1] - - is_traceless = V <: SymTracelessTensorValue - skip_last_diagval = is_traceless ? 1 : 0 # Skid V_DD if traceless - - body = "z = $(zero(T));" - for i in 1:s_length - body *= "@inbounds s$i = s[$i];" - end - - for c in 1:(D-skip_last_diagval) # Go over cols - for r in c:D # Go over lower triangle, current col - for i in eachindex(m) - m[i] = "z" - end - for i in 1:s_length # indices of the Vector s - m[i,r,c] = "s$i" - if (r!=c) - m[i,c,r] = "s$i" - elseif is_traceless # V_rr contributes negatively to V_DD (tracelessness) - m[i,D,D] = "-s$i" - end - end - body *= "@inbounds v[k] = ($(join(tuple(m...), ", ")));" - body *= "k = k + 1;" - end - end - - body = Meta.parse(string("begin ",body," end")) - return Expr(:block, body ,:(return k)) -end - -function _hessian_nd!( - v::AbstractVector{G}, - x, - orders, - terms::AbstractVector{CartesianIndex{D}}, - c::AbstractMatrix{T}, - g::AbstractMatrix{T}, - h::AbstractMatrix{T}, - ::Type{V}) where {G,T,D,V} - - dim = D - for d in 1:dim - Kd = Val(orders[d]) - _derivatives_1d!(Monomial,Kd,(c,g,h),x,d) - end - - z = zero(Mutable(TensorValue{D,D,T})) - o = one(T) - k = 1 - - for ci in terms - - 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!(v,s,k,V) - - end - -end - -#function evaluate!( -# #cache, f::FieldGradientArray{N,<:AbstractVector{Monomial}}, x::VectorValue) where {N} -# cache, f::FieldGradientArray{N,<:AbstractVector{<:Type{Polynomial}}}, x::VectorValue) 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 - -# Field implementation -#function _evaluate!( -# cache, -# fg::FieldGradientArray{1,MonomialBasis{D,V}}, -# x::AbstractVector{<:Point}, -# TisbitsType::Val{true}) where {D,V} -# -# f = fg.fa -# r, v, c, g = cache -# z = zero(Mutable(VectorValue{D,eltype(T)})) -# np = length(x) -# ndof = length(f) -# 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!(v,xi,f.orders,f.terms,c,g,z,T) -# for j in 1:ndof -# @inbounds r[i,j] = v[j] -# end -# end -# r.array -#end -# -#function _evaluate!( -# cache, -# fg::FieldGradientArray{1,MonomialBasis{D,V}}, -# x::AbstractVector{<:Point}, -# TisbitsType::Val{false}) where {D,V} -# -# f = fg.fa -# r, v, c, g, z = cache -# np = length(x) -# ndof = length(f) -# 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!(v,xi,f.orders,f.terms,c,g,z,T) -# for j in 1:ndof -# @inbounds r[i,j] = v[j] -# end -# end -# r.array -#end -# -#function evaluate!( -# cache, -# fg::FieldGradientArray{1,MonomialBasis{D,V}}, -# x::AbstractVector{<:Point}) where {D,V} -# -# r, v, c, g = cache -# TisbitsType = Val(isbitstype(eltype(c))) -# _evaluate!(cache,fg,x,TisbitsType) -#end -# -#function return_cache( -# fg::FieldGradientArray{2,MonomialBasis{D,V}}, -# x::AbstractVector{<:Point}) where {D,V} -# -# f = fg.fa -# @check D == length(eltype(x)) "Incorrect number of point components" -# np = length(x) -# ndof = length(f) -# 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,MonomialBasis{D,V}}, -# x::AbstractVector{<:Point}) where {D,V} -# -# f = fg.fa -# r, v, c, g, h = cache -# np = length(x) -# ndof = length(f) -# 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!(v,xi,f.orders,f.terms,c,g,h,T) -# for j in 1:ndof -# @inbounds r[i,j] = v[j] -# end -# end -# r.array -#end - diff --git a/src/Polynomials/PCurlGradLegendreBases.jl b/src/Polynomials/PCurlGradLegendreBases.jl deleted file mode 100644 index a0753a259..000000000 --- a/src/Polynomials/PCurlGradLegendreBases.jl +++ /dev/null @@ -1,282 +0,0 @@ -""" -struct PCurlGradLegendrePolynomialBasis{...} <: AbstractArray{Legendre} - -This type implements a multivariate vector-valued polynomial basis -spanning the space needed for Raviart-Thomas reference elements on simplices. -The type parameters and fields of this `struct` are not public. -This type fully implements the [`Field`](@ref) interface, with up to first order -derivatives. -""" -struct PCurlGradLegendrePolynomialBasis{D,T} <: AbstractVector{Legendre} - order::Int - pterms::Array{CartesianIndex{D},1} - sterms::Array{CartesianIndex{D},1} - perms::Matrix{Int} - function PCurlGradLegendrePolynomialBasis(::Type{T},order::Int, - pterms::Array{CartesianIndex{D},1},sterms::Array{CartesianIndex{D},1}, - perms::Matrix{Int}) where {D,T} - new{D,T}(order,pterms,sterms,perms) - end -end - -Base.size(a::PCurlGradLegendrePolynomialBasis) = (_ndofs_pgrad(a),) -# @santiagobadia : Not sure we want to create the monomial machinery -Base.getindex(a::PCurlGradLegendrePolynomialBasis,i::Integer) = Legendre() -Base.IndexStyle(::PCurlGradLegendrePolynomialBasis) = IndexLinear() - -""" -PCurlGradLegendrePolynomialBasis{D}(::Type{T},order::Int) where {D,T} - -Returns a `PCurlGradLegendrePolynomialBasis` object. `D` is the dimension -of the coordinate space and `T` is the type of the components in the vector-value. -The `order` argument has the following meaning: the divergence of the functions -in this basis is in the P space of degree `order`. -""" -function PCurlGradLegendrePolynomialBasis{D}(::Type{T},order::Int) where {D,T} - @check T<:Real "T needs to be <:Real since represents the type of the components of the vector value" - P_k = MonomialBasis(Val(D),T, order, _p_filter) - S_k = MonomialBasis(Val(D),T, order, _ps_filter) - pterms = P_k.terms - sterms = S_k.terms - perms = _prepare_perms(D) - PCurlGradLegendrePolynomialBasis(T,order,pterms,sterms,perms) -end - -""" - num_terms(f::PCurlGradLegendrePolynomialBasis{D,T}) where {D,T} -""" -function num_terms(f::PCurlGradLegendrePolynomialBasis{D,T}) where {D,T} - Int(_p_dim(f.order,D)*D + _p_dim(f.order,D-1)) -end - -get_order(f::PCurlGradLegendrePolynomialBasis{D,T}) where {D,T} = f.order - -return_type(::PCurlGradLegendrePolynomialBasis{D,T}) where {D,T} = VectorValue{D,T} - -function return_cache(f::PCurlGradLegendrePolynomialBasis{D,T},x::AbstractVector{<:Point}) where {D,T} - @check D == length(eltype(x)) "Incorrect number of point components" - np = length(x) - ndof = _ndofs_pgrad(f) - n = 1 + f.order+1 - V = VectorValue{D,T} - r = CachedArray(zeros(V,(np,ndof))) - v = CachedArray(zeros(V,(ndof,))) - c = CachedArray(zeros(T,(D,n))) - (r, v, c) -end - -function evaluate!(cache,f::PCurlGradLegendrePolynomialBasis{D,T},x::AbstractVector{<:Point}) where {D,T} - r, v, c = cache - np = length(x) - ndof = _ndofs_pgrad(f) - n = 1 + f.order+1 - setsize!(r,(np,ndof)) - setsize!(v,(ndof,)) - setsize!(c,(D,n)) - for i in 1:np - @inbounds xi = x[i] - _evaluate_nd_pcurlgrad_leg!(v,xi,f.order+1,f.pterms,f.sterms,f.perms,c) - for j in 1:ndof - @inbounds r[i,j] = v[j] - end - end - r.array -end - -function return_cache( - fg::FieldGradientArray{1,PCurlGradLegendrePolynomialBasis{D,T}}, - x::AbstractVector{<:Point}) where {D,T} - - f = fg.fa - @check D == length(eltype(x)) "Incorrect number of point components" - np = length(x) - ndof = _ndofs_pgrad(f) - n = 1 + f.order+1 - xi = testitem(x) - V = VectorValue{D,T} - G = gradient_type(V,xi) - r = CachedArray(zeros(G,(np,ndof))) - v = CachedArray(zeros(G,(ndof,))) - c = CachedArray(zeros(T,(D,n))) - g = CachedArray(zeros(T,(D,n))) - (r, v, c, g) -end - -function evaluate!(cache, - fg::FieldGradientArray{1,PCurlGradLegendrePolynomialBasis{D,T}}, - x::AbstractVector{<:Point}) where {D,T} - - f = fg.fa - r, v, c, g = cache - np = length(x) - ndof = _ndofs_pgrad(f) - n = 1 + f.order+1 - setsize!(r,(np,ndof)) - setsize!(v,(ndof,)) - setsize!(c,(D,n)) - setsize!(g,(D,n)) - V = VectorValue{D,T} - for i in 1:np - @inbounds xi = x[i] - _gradient_nd_pcurlgrad_leg!(v,xi,f.order+1,f.pterms,f.sterms,f.perms,c,g,V) - for j in 1:ndof - @inbounds r[i,j] = v[j] - end - end - r.array -end - - -# Helpers - -_ndofs_pgrad(f::PCurlGradLegendrePolynomialBasis{D}) where D = num_terms(f) - -function _evaluate_nd_pcurlgrad_leg!( - v::AbstractVector{V}, - x, - order, - pterms::Array{CartesianIndex{D},1}, - sterms::Array{CartesianIndex{D},1}, - perms::Matrix{Int}, - c::AbstractMatrix{T}) where {V,T,D} - - dim = D - for d in 1:dim - K = Val(order) - _evaluate_1d!(Legendre,K,c,x,d) - end - - o = one(T) - k = 1 - m = zero(Mutable(V)) - js = eachindex(m) - z = zero(T) - - for ci in pterms - for j in js - - @inbounds for i in js - m[i] = z - end - - s = o - @inbounds for d in 1:dim - s *= c[d,ci[perms[d,j]]] - end - - m[j] = s - v[k] = m - k += 1 - end - end - - for ci in sterms - @inbounds for i in js - m[i] = z - end - for j in js - - s = c[j,2] - @inbounds for d in 1:dim - s *= c[d,ci[d]] - end - - m[j] = s - - end - v[k] = m - k += 1 - end -end - -function _gradient_nd_pcurlgrad_leg!( - v::AbstractVector{G}, - x, - order, - pterms::Array{CartesianIndex{D},1}, - sterms::Array{CartesianIndex{D},1}, - perms::Matrix{Int}, - c::AbstractMatrix{T}, - g::AbstractMatrix{T}, - ::Type{V}) where {G,T,D,V} - - dim = D - for d in 1:dim - K = Val(order) - _derivatives_1d!(Legendre,K,(c,g),x,d) - end - - z = zero(Mutable(V)) - m = zero(Mutable(G)) - js = eachindex(z) - mjs = eachindex(m) - o = one(T) - zi = zero(T) - k = 1 - - for ci in pterms - for j in js - - s = z - for i in js - s[i] = o - end - - for q in 1:dim - for d in 1:dim - if d != q - @inbounds s[q] *= c[d,ci[perms[d,j]]] - else - @inbounds s[q] *= g[d,ci[perms[d,j]]] - end - end - end - - @inbounds for i in mjs - m[i] = zi - end - - for i in js - @inbounds m[i,j] = s[i] - end - @inbounds v[k] = m - k += 1 - end - end - - for ci in sterms - - @inbounds for i in mjs - m[i] = zi - end - - for j in js - - s = z - for i in js - s[i] = c[j,2] - 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 - aux = o - @inbounds for d in 1:dim - aux *= c[d,ci[d]] - end - s[j] += aux - - for i in js - @inbounds m[i,j] = s[i] - end - end - @inbounds v[k] = m - k += 1 - end -end diff --git a/src/Polynomials/PCurlGradMonomialBases.jl b/src/Polynomials/PCurlGradMonomialBases.jl deleted file mode 100644 index cb5fb3cfe..000000000 --- a/src/Polynomials/PCurlGradMonomialBases.jl +++ /dev/null @@ -1,284 +0,0 @@ - -""" -struct PCurlGradMonomialBasis{...} <: AbstractArray{Monomial} - -This type implements a multivariate vector-valued polynomial basis -spanning the space needed for Raviart-Thomas reference elements on simplices. -The type parameters and fields of this `struct` are not public. -This type fully implements the [`Field`](@ref) interface, with up to first order -derivatives. -""" -struct PCurlGradMonomialBasis{D,T} <: AbstractVector{Monomial} - order::Int - pterms::Array{CartesianIndex{D},1} - sterms::Array{CartesianIndex{D},1} - perms::Matrix{Int} - function PCurlGradMonomialBasis(::Type{T},order::Int, - pterms::Array{CartesianIndex{D},1},sterms::Array{CartesianIndex{D},1}, - perms::Matrix{Int}) where {D,T} - new{D,T}(order,pterms,sterms,perms) - end -end - -Base.size(a::PCurlGradMonomialBasis) = (_ndofs_pgrad(a),) -# @santiagobadia : Not sure we want to create the monomial machinery -Base.getindex(a::PCurlGradMonomialBasis,i::Integer) = Monomial() -Base.IndexStyle(::PCurlGradMonomialBasis) = IndexLinear() - -""" -PCurlGradMonomialBasis{D}(::Type{T},order::Int) where {D,T} - -Returns a `PCurlGradMonomialBasis` object. `D` is the dimension -of the coordinate space and `T` is the type of the components in the vector-value. -The `order` argument has the following meaning: the divergence of the functions -in this basis is in the P space of degree `order`. -""" -function PCurlGradMonomialBasis{D}(::Type{T},order::Int) where {D,T} - @check T<:Real "T needs to be <:Real since represents the type of the components of the vector value" - P_k = MonomialBasis(Val(D), T, order, _p_filter) - S_k = MonomialBasis(Val(D), T, order, _ps_filter) - pterms = P_k.terms - sterms = S_k.terms - perms = _prepare_perms(D) - PCurlGradMonomialBasis(T,order,pterms,sterms,perms) -end - -""" - num_terms(f::PCurlGradMonomialBasis{D,T}) where {D,T} -""" -function num_terms(f::PCurlGradMonomialBasis{D,T}) where {D,T} - Int(_p_dim(f.order,D)*D + _p_dim(f.order,D-1)) -end - -get_order(f::PCurlGradMonomialBasis{D,T}) where {D,T} = f.order - -return_type(::PCurlGradMonomialBasis{D,T}) where {D,T} = VectorValue{D,T} - -function return_cache(f::PCurlGradMonomialBasis{D,T},x::AbstractVector{<:Point}) where {D,T} - @check D == length(eltype(x)) "Incorrect number of point components" - np = length(x) - ndof = _ndofs_pgrad(f) - n = 1 + f.order+1 - V = VectorValue{D,T} - r = CachedArray(zeros(V,(np,ndof))) - v = CachedArray(zeros(V,(ndof,))) - c = CachedArray(zeros(T,(D,n))) - (r, v, c) -end - -function evaluate!(cache,f::PCurlGradMonomialBasis{D,T},x::AbstractVector{<:Point}) where {D,T} - r, v, c = cache - np = length(x) - ndof = _ndofs_pgrad(f) - n = 1 + f.order+1 - setsize!(r,(np,ndof)) - setsize!(v,(ndof,)) - setsize!(c,(D,n)) - for i in 1:np - @inbounds xi = x[i] - _evaluate_nd_pcurlgrad!(v,xi,f.order+1,f.pterms,f.sterms,f.perms,c) - for j in 1:ndof - @inbounds r[i,j] = v[j] - end - end - r.array -end - -function return_cache( - fg::FieldGradientArray{1,PCurlGradMonomialBasis{D,T}}, - x::AbstractVector{<:Point}) where {D,T} - - f = fg.fa - @check D == length(eltype(x)) "Incorrect number of point components" - np = length(x) - ndof = _ndofs_pgrad(f) - n = 1 + f.order+1 - xi = testitem(x) - V = VectorValue{D,T} - G = gradient_type(V,xi) - r = CachedArray(zeros(G,(np,ndof))) - v = CachedArray(zeros(G,(ndof,))) - c = CachedArray(zeros(T,(D,n))) - g = CachedArray(zeros(T,(D,n))) - (r, v, c, g) -end - -function evaluate!(cache, - fg::FieldGradientArray{1,PCurlGradMonomialBasis{D,T}}, - x::AbstractVector{<:Point}) where {D,T} - - f = fg.fa - r, v, c, g = cache - np = length(x) - ndof = _ndofs_pgrad(f) - n = 1 + f.order+1 - setsize!(r,(np,ndof)) - setsize!(v,(ndof,)) - setsize!(c,(D,n)) - setsize!(g,(D,n)) - V = VectorValue{D,T} - for i in 1:np - @inbounds xi = x[i] - _gradient_nd_pcurlgrad!(v,xi,f.order+1,f.pterms,f.sterms,f.perms,c,g,V) - for j in 1:ndof - @inbounds r[i,j] = v[j] - end - end - r.array -end - - -# Helpers - -_ndofs_pgrad(f::PCurlGradMonomialBasis{D}) where D = num_terms(f) - - -function _evaluate_nd_pcurlgrad!( - v::AbstractVector{V}, - x, - order, - pterms::Array{CartesianIndex{D},1}, - sterms::Array{CartesianIndex{D},1}, - perms::Matrix{Int}, - c::AbstractMatrix{T}) where {V,T,D} - - dim = D - for d in 1:dim - K = Val(order) - _evaluate_1d!(Monomial,K,c,x,d) - end - - o = one(T) - k = 1 - m = zero(Mutable(V)) - js = eachindex(m) - z = zero(T) - - for ci in pterms - for j in js - - @inbounds for i in js - m[i] = z - end - - s = o - @inbounds for d in 1:dim - s *= c[d,ci[perms[d,j]]] - end - - m[j] = s - v[k] = m - k += 1 - end - end - - for ci in sterms - @inbounds for i in js - m[i] = z - end - for j in js - - s = c[j,2] - @inbounds for d in 1:dim - s *= c[d,ci[d]] - end - - m[j] = s - - end - v[k] = m - k += 1 - end -end - -function _gradient_nd_pcurlgrad!( - v::AbstractVector{G}, - x, - order, - pterms::Array{CartesianIndex{D},1}, - sterms::Array{CartesianIndex{D},1}, - perms::Matrix{Int}, - c::AbstractMatrix{T}, - g::AbstractMatrix{T}, - ::Type{V}) where {G,T,D,V} - - dim = D - for d in 1:dim - K = Val(order) - _derivatives_1d!(Monomial,K,(c,g),x,d) - end - - z = zero(Mutable(V)) - m = zero(Mutable(G)) - js = eachindex(z) - mjs = eachindex(m) - o = one(T) - zi = zero(T) - k = 1 - - for ci in pterms - for j in js - - s = z - for i in js - s[i] = o - end - - for q in 1:dim - for d in 1:dim - if d != q - @inbounds s[q] *= c[d,ci[perms[d,j]]] - else - @inbounds s[q] *= g[d,ci[perms[d,j]]] - end - end - end - - @inbounds for i in mjs - m[i] = zi - end - - for i in js - @inbounds m[i,j] = s[i] - end - @inbounds v[k] = m - k += 1 - end - end - - for ci in sterms - - @inbounds for i in mjs - m[i] = zi - end - - for j in js - - s = z - for i in js - s[i] = c[j,2] - 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 - aux = o - @inbounds for d in 1:dim - aux *= c[d,ci[d]] - end - s[j] += aux - - for i in js - @inbounds m[i,j] = s[i] - end - end - @inbounds v[k] = m - k += 1 - end -end diff --git a/src/Polynomials/PolynomialInterfaces.jl b/src/Polynomials/PolynomialInterfaces.jl index a41faa5c8..e1066ac71 100644 --- a/src/Polynomials/PolynomialInterfaces.jl +++ b/src/Polynomials/PolynomialInterfaces.jl @@ -13,7 +13,7 @@ abstract type Polynomial <: Field end Return true if the basis of order `k` of the given `<:Polynomial` type is the union of the basis of order `k-1` and an other order `k` polynomial. """ -isHierarchical(::Polynomial) = @abstractmethod +isHierarchical(::Type{Polynomial}) = @abstractmethod """ PolynomialBasis{D,V,K,PT<:Polynomial} <: AbstractVector{PT} @@ -27,11 +27,13 @@ The parameters are: """ abstract type PolynomialBasis{D,V,K,PT<:Polynomial} <: AbstractVector{PT} end -@inline Base.size(a::PolynomialBasis{D,V}) where {D,V} = @abstractmethod -@inline Base.getindex(a::PolynomialBasis{D,V,K,PT}, i::Integer) where {D,V,K,PT} = PT() +@inline Base.size(::PolynomialBasis{D,V}) where {D,V} = @abstractmethod +@inline Base.getindex(::PolynomialBasis{D,V,K,PT}, i::Integer) where {D,V,K,PT} = PT() @inline Base.IndexStyle(::PolynomialBasis) = IndexLinear() @inline return_type(::PolynomialBasis{D,V}) where {D,V} = V +@deprecate num_terms(a::PolynomialBasis) length(a) + """ get_order(b::PolynomialBasis{D,V,K) = K diff --git a/src/Polynomials/Polynomials.jl b/src/Polynomials/Polynomials.jl index 9160ac29d..b6f522066 100644 --- a/src/Polynomials/Polynomials.jl +++ b/src/Polynomials/Polynomials.jl @@ -51,12 +51,12 @@ export PCurlGradLegendreBasis include("PolynomialInterfaces.jl") -include("OldMonomialHelpers.jl") - include("TensorPolynomialBases.jl") include("CompWiseTensorPolyBases.jl") +include("NonTensorRTPolyBasis.jl") + include("MonomialBases.jl") include("LegendreBases.jl") @@ -65,14 +65,9 @@ include("LegendreBases.jl") include("ChebyshevBases.jl") -include("ModalC0Bases.jl") - include("BernsteinBases.jl") -#include("newfile.jl") -#include("PSpaceBases.jl") -include("PCurlGradMonomialBases.jl") -include("PCurlGradLegendreBases.jl") +include("ModalC0Bases.jl") include("NedelecPrebasisOnSimplex.jl") diff --git a/src/Polynomials/TensorPolynomialBases.jl b/src/Polynomials/TensorPolynomialBases.jl index 0b220386c..986cbd10d 100644 --- a/src/Polynomials/TensorPolynomialBases.jl +++ b/src/Polynomials/TensorPolynomialBases.jl @@ -476,7 +476,7 @@ function _tensorial_hessian_nd!( end end - k = _set_gradient!(v,s,k,V) + k = _tensorial_set_gradient!(v,s,k,V) end end diff --git a/src/ReferenceFEs/RaviartThomasRefFEs.jl b/src/ReferenceFEs/RaviartThomasRefFEs.jl index b89ff738d..c18b0533a 100644 --- a/src/ReferenceFEs/RaviartThomasRefFEs.jl +++ b/src/ReferenceFEs/RaviartThomasRefFEs.jl @@ -22,7 +22,7 @@ function RaviartThomasRefFE( cb = QGradLegendreBasis(Val(D),T,order-1) # Cell basis fb = LegendreBasis(Val(D-1),T,order,Polynomials._q_filter) # Face basis elseif is_simplex(p) - prebasis = PCurlGradMonomialBasis{D}(T,order) # Prebasis + prebasis = PCurlGradMonomialBasis(Val(D),T,order) # Prebasis cb = LegendreBasis(Val(D),VectorValue{D,T},order-1,Polynomials._p_filter) # Cell basis fb = LegendreBasis(Val(D-1),T,order,Polynomials._p_filter) # Face basis else diff --git a/src/ReferenceFEs/deprecated/RaviartThomasRefFEs.jl b/src/ReferenceFEs/deprecated/RaviartThomasRefFEs.jl index 18eeb8e60..006d92e65 100644 --- a/src/ReferenceFEs/deprecated/RaviartThomasRefFEs.jl +++ b/src/ReferenceFEs/deprecated/RaviartThomasRefFEs.jl @@ -23,13 +23,11 @@ function RaviartThomasRefFE( if is_n_cube(p) && basis_type == :monomial prebasis = QCurlGradMonomialBasis(Val(D),et,order) elseif is_simplex(p) && basis_type == :monomial - prebasis = PCurlGradMonomialBasis{D}(et,order) - #prebasis = PCurlGradMonomialBasis(Val(D),et,order) + prebasis = PCurlGradMonomialBasis(Val(D),et,order) elseif is_n_cube(p) && basis_type == :legendre prebasis = QCurlGradLegendreBasis(Val(D),et,order) elseif is_simplex(p) && basis_type == :legendre - prebasis = PCurlGradLegendreBasis{D}(et,order) - #prebasis = PCurlGradLegendreBasis(Val(D),et,order) + prebasis = PCurlGradLegendreBasis(Val(D),et,order) elseif is_n_cube(p) && basis_type == :chebyshev prebasis = QCurlGradChebyshevBasis(Val(D),et,order) else diff --git a/test/PolynomialsTests/PCurlGradMonomialBasesTests.jl b/test/PolynomialsTests/PCurlGradMonomialBasesTests.jl index d48f69f00..5ddd1ae78 100644 --- a/test/PolynomialsTests/PCurlGradMonomialBasesTests.jl +++ b/test/PolynomialsTests/PCurlGradMonomialBasesTests.jl @@ -15,55 +15,51 @@ D = 2 T = Float64 V = VectorValue{D,T} G = gradient_type(V,xi) -b = PCurlGradMonomialBasis{D}(T,order) -#b = PCurlGradMonomialBasis(Val(D),T,order) +b = PCurlGradMonomialBasis(Val(D),T,order) v = V[ - (1.0, 0.0), (0.0, 1.0), (4.0, 0.0), (0.0, 2.0), (16.0, 0.0), (0.0, 4.0), - (2.0, 0.0), (0.0, 4.0), (8.0, 0.0), (0.0, 8.0), (4.0, 0.0), (0.0, 16.0), - (64.0, 32.0), (32.0, 16.0), (16.0, 8.0)] + (1.0, 0.0), (4.0, 0.0), (16.0, 0.0), (2.0, 0.0), (8.0, 0.0), (4.0, 0.0), # pterm ex + (0.0, 1.0), (0.0, 4.0), (0.0, 16.0), (0.0, 2.0), (0.0, 8.0), (0.0, 4.0), # pterm ey + (64.0,32.0), (32.0,16.0), (16.0, 8.0)] # sterms g = G[ - (0.0, 0.0, 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, 1.0), (8.0, 0.0, 0.0, 0.0), (0.0, 0.0, 0.0, 4.0), - (0.0, 1.0, 0.0, 0.0), (0.0, 0.0, 1.0, 0.0), (2.0, 4.0, 0.0, 0.0), - (0.0, 0.0, 2.0, 4.0), (0.0, 4.0, 0.0, 0.0), (0.0, 0.0, 8.0, 0.0), - (48.0, 0.0, 16.0, 16.0), (16.0, 16.0, 4.0, 16.0), (4.0, 16.0, 0.0, 12.0)] + (0.0, 0.0, 0.0, 0.0), (1.0, 0.0, 0.0, 0.0), (8.0, 0.0, 0.0, 0.0), (0.0, 1.0, 0.0, 0.0), (2.0, 4.0, 0.0, 0.0), (0.0, 4.0, 0.0, 0.0), # pterm ex + (0.0, 0.0, 0.0, 0.0), (0.0, 0.0, 1.0, 0.0), (0.0, 0.0, 8.0, 0.0), (0.0, 0.0, 0.0, 1.0), (0.0, 0.0, 2.0, 4.0), (0.0, 0.0, 0.0, 4.0), # pterm ey + (48.0, 0.0, 16.0, 16.0), (16.0, 16.0, 4.0, 16.0), (4.0, 16.0, 0.0, 12.0)] # sterms - vb = evaluate(b,x) +vb = evaluate(b,x) - for (vi,vbi) in zip(v,vb) - @test vi == vbi - end +for (vi,vbi) in zip(v,vb) + @test vi == vbi +end - vb = evaluate(b,xi) - @test vb == v +vb = evaluate(b,xi) +@test vb == v - ∇b = Broadcasting(gradient)(b) - gvb = evaluate(∇b,x) - for (vi,vbi) in zip(g,gvb) - @test vi == vbi - end +∇b = Broadcasting(gradient)(b) +gvb = evaluate(∇b,x) +for (vi,vbi) in zip(g,gvb) + @test vi == vbi +end - gvb = evaluate(∇b,xi) - @test gvb == g +gvb = evaluate(∇b,xi) +@test gvb == g - @test length(b) == 15 - @test get_order(b) == 2 +@test length(b) == 15 +@test get_order(b) == 3 - xi = Point(2,3,5) - np = 5 - x = fill(xi,np) +xi = Point(2,3,5) +np = 5 +x = fill(xi,np) - order = 1 - D = 3 - T = Float64 - V = VectorValue{D,T} - G = gradient_type(V,xi) - b = PCurlGradMonomialBasis{D}(T,order) - #b = PCurlGradMonomialBasis(Val(D),T,order) +order = 1 +D = 3 +T = Float64 +V = VectorValue{D,T} +G = gradient_type(V,xi) +b = PCurlGradMonomialBasis(Val(D),T,order) - @test length(b) == 15 - @test get_order(b) == 1 +@test length(b) == 15 +@test get_order(b) == 2 end # module diff --git a/test/ReferenceFEsTests/RaviartThomasRefFEsTests.jl b/test/ReferenceFEsTests/RaviartThomasRefFEsTests.jl index 302513da7..92ea581c9 100644 --- a/test/ReferenceFEsTests/RaviartThomasRefFEsTests.jl +++ b/test/ReferenceFEsTests/RaviartThomasRefFEsTests.jl @@ -82,7 +82,7 @@ reffe = RaviartThomasRefFE(et,p,order) test_reference_fe(reffe) @test length(get_prebasis(reffe)) == 4 @test num_dofs(reffe) == 4 -@test get_order(get_prebasis(reffe)) == 0 +@test get_order(get_prebasis(reffe)) == 1 @test Conformity(reffe) == DivConformity() @@ -95,7 +95,7 @@ reffe = RaviartThomasRefFE(et,p,order) test_reference_fe(reffe) @test length(get_prebasis(reffe)) == 36 @test num_dofs(reffe) == 36 -@test get_order(get_prebasis(reffe)) == 2 +@test get_order(get_prebasis(reffe)) == 3 @test Conformity(reffe) == DivConformity() prebasis = get_prebasis(reffe) From a8e7629368fd1f3ac942cfd3b0378694c1797ee9 Mon Sep 17 00:00:00 2001 From: Antoine Marteau Date: Thu, 19 Dec 2024 18:23:43 +1100 Subject: [PATCH 064/105] clean Polynomials folder --- .../{NonTensorRTPolyBasis.jl => NonTensorRTPolyBases.jl} | 0 src/Polynomials/OldMonomialHelpers.jl | 0 src/Polynomials/Polynomials.jl | 2 +- 3 files changed, 1 insertion(+), 1 deletion(-) rename src/Polynomials/{NonTensorRTPolyBasis.jl => NonTensorRTPolyBases.jl} (100%) delete mode 100644 src/Polynomials/OldMonomialHelpers.jl diff --git a/src/Polynomials/NonTensorRTPolyBasis.jl b/src/Polynomials/NonTensorRTPolyBases.jl similarity index 100% rename from src/Polynomials/NonTensorRTPolyBasis.jl rename to src/Polynomials/NonTensorRTPolyBases.jl diff --git a/src/Polynomials/OldMonomialHelpers.jl b/src/Polynomials/OldMonomialHelpers.jl deleted file mode 100644 index e69de29bb..000000000 diff --git a/src/Polynomials/Polynomials.jl b/src/Polynomials/Polynomials.jl index b6f522066..879df0a16 100644 --- a/src/Polynomials/Polynomials.jl +++ b/src/Polynomials/Polynomials.jl @@ -55,7 +55,7 @@ include("TensorPolynomialBases.jl") include("CompWiseTensorPolyBases.jl") -include("NonTensorRTPolyBasis.jl") +include("NonTensorRTPolyBases.jl") include("MonomialBases.jl") From 4823ac2bd6957a7251c69bf2d6f10f745cfeb797 Mon Sep 17 00:00:00 2001 From: JordiManyer Date: Fri, 20 Dec 2024 10:27:31 +1100 Subject: [PATCH 065/105] Minor --- docs/src/dev-notes/pullbacks.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/src/dev-notes/pullbacks.md b/docs/src/dev-notes/pullbacks.md index 467963229..dda070b4d 100644 --- a/docs/src/dev-notes/pullbacks.md +++ b/docs/src/dev-notes/pullbacks.md @@ -39,7 +39,7 @@ An important result from [1, Theorem 3.1] is that ``P = M^T``. !!! details [1, Lemma 2.6]: A key ingredient is that given ``M`` a matrix we have ``\Sigma (M \Phi) = \Sigma (\Phi) M^T`` since ```math - [\Sigma (M \Phi)]_{ij} = \sigma_i (M_{jk} \varphi_k) = \sigma_i \varphi_k M_{jk} = [\Sigma (\Phi) M^T]_{ij} + [\Sigma (M \Phi)]_{ij} = \sigma_i (M_{jk} \varphi_k) = M_{jk} \sigma_i (\varphi_k) = [\Sigma (\Phi) M^T]_{ij} ``` where we have used that moments are linear. From 7e13829d3a13e13e348bbce8a612ec7be0ddf119 Mon Sep 17 00:00:00 2001 From: Antoine Marteau Date: Thu, 19 Dec 2024 18:26:05 +1100 Subject: [PATCH 066/105] evaluate! factorisations, NedelecPrebasis and ModalC0Basis uniformization - factorised evaluate!(PolynomialBasis) using a new generic _evaluate_nd! _gradient_nd! _hessian_nd! interface - made NedelecPreBasisOnSimplex and ModalC0Basis subtype of PolynomialBasis and factorised what was possible --- src/Polynomials/CompWiseTensorPolyBases.jl | 143 ++---- src/Polynomials/ModalC0Bases.jl | 464 +++++++------------ src/Polynomials/NedelecPrebasisOnSimplex.jl | 119 +++-- src/Polynomials/NonTensorRTPolyBases.jl | 98 ++-- src/Polynomials/PolynomialInterfaces.jl | 256 ++++++++-- src/Polynomials/TensorPolynomialBases.jl | 150 ++---- test/ReferenceFEsTests/NedelecRefFEsTests.jl | 6 +- 7 files changed, 536 insertions(+), 700 deletions(-) diff --git a/src/Polynomials/CompWiseTensorPolyBases.jl b/src/Polynomials/CompWiseTensorPolyBases.jl index 7c003caa7..dcbcfdb84 100644 --- a/src/Polynomials/CompWiseTensorPolyBases.jl +++ b/src/Polynomials/CompWiseTensorPolyBases.jl @@ -53,96 +53,18 @@ function get_comp_terms(f::CompWiseTensorPolyBasis{D,V,K,PT,L}) where {D,V,K,PT, end -######################## -# Field implementation # -######################## - -function evaluate!( - cache, - f::CompWiseTensorPolyBasis{D,V,K,PT,L}, - x::AbstractVector{<:Point}) where {D,V,K,PT,L} - - r, v, c = cache - np = length(x) - ndof = length(f) - ndof_1d = get_order(f) + 1 # K + 1 - comp_terms = get_comp_terms(f) - setsize!(r,(np,ndof)) - setsize!(v,(ndof,)) - setsize!(c,(D,ndof_1d)) - for i in 1:np - @inbounds xi = x[i] - _evaluate_nd_cwtpb!(PT,v,xi,f.orders,comp_terms,c) - for j in 1:ndof - @inbounds r[i,j] = v[j] - end - end - r.array -end - -function evaluate!( - cache, - fg::FieldGradientArray{1,<:CompWiseTensorPolyBasis{D,V,K,PT}}, - x::AbstractVector{<:Point}) where {D,V,K,PT} - - f = fg.fa - r, v, c, g = cache - np = length(x) - ndof = length(f) - ndof_1d = get_order(f) + 1 - comp_terms = get_comp_terms(f) - setsize!(r,(np,ndof)) - setsize!(v,(ndof,)) - setsize!(c,(D,ndof_1d)) - setsize!(g,(D,ndof_1d)) - for i in 1:np - @inbounds xi = x[i] - _gradient_nd_cwtpb!(PT,v,xi,f.orders,comp_terms,c,g,V) - for j in 1:ndof - @inbounds r[i,j] = v[j] - end - end - r.array -end - -function evaluate!( - cache, - fg::FieldGradientArray{2,<:CompWiseTensorPolyBasis{D,V,K,PT}}, - x::AbstractVector{<:Point}) where {D,V,K,PT} - - f = fg.fa - r, v, c, g, h = cache - np = length(x) - ndof = length(f) - ndof_1d = get_order(f) + 1 - comp_terms = get_comp_terms(f) - setsize!(r,(np,ndof)) - setsize!(v,(ndof,)) - setsize!(c,(D,ndof_1d)) - setsize!(g,(D,ndof_1d)) - setsize!(h,(D,ndof_1d)) - for i in 1:np - @inbounds xi = x[i] - _hessian_nd_cwtpb!(PT,v,xi,f.orders,comp_terms,c,g,h,V) - for j in 1:ndof - @inbounds r[i,j] = v[j] - end - end - r.array -end - +################################# +# nD evaluations implementation # +################################# -########### -# Helpers # -########### - -function _evaluate_nd_cwtpb!( - PT::Type{<:Polynomial}, +function _evaluate_nd!( + b::CompWiseTensorPolyBasis{D,V,K,PT,L}, x, + r::AbstractMatrix{V}, i, v::AbstractVector{V}, - x, - orders::SMatrix{L,D,Int}, - comp_terms::NTuple{L,CartesianIndices{D}}, - c::AbstractMatrix{T}) where {V,L,D,T} + c::AbstractMatrix{T}) where {D,V,K,PT,L,T} + + orders = b.orders + comp_terms = get_comp_terms(b) for d in 1:D # for each coordinate d, the order at which the basis should be evaluated is @@ -165,6 +87,11 @@ function _evaluate_nd_cwtpb!( k = _comp_wize_set_value!(v,s,k,l) end end + + #r[i] = v + @inbounds for j in 1:length(b) + r[i,j] = v[j] + end end """ @@ -183,15 +110,16 @@ function _comp_wize_set_value!(v::AbstractVector{V},s::T,k,l) where {V,T} return k + 1 end -function _gradient_nd_cwtpb!( - PT::Type{<:Polynomial}, +function _gradient_nd!( + b::CompWiseTensorPolyBasis{D,V,K,PT,L}, x, + r::AbstractMatrix{G}, i, v::AbstractVector{G}, - x, - orders::SMatrix{L,D,Int}, - comp_terms::NTuple{L,CartesianIndices{D}}, c::AbstractMatrix{T}, g::AbstractMatrix{T}, - ::Type{V}) where {G,L,D,T,V} + s::MVector{D,T}) where {D,V,K,PT,L,G,T} + + orders = b.orders + comp_terms = get_comp_terms(b) for d in 1:D # for each spatial coordinate d, the order at which the basis should be @@ -200,7 +128,6 @@ function _gradient_nd_cwtpb!( _derivatives_1d!(PT,Kd,(c,g),x,d) end - s = zero(Mutable(V)) k = 1 for (l,terms) in enumerate(comp_terms) @@ -223,6 +150,11 @@ function _gradient_nd_cwtpb!( k = _comp_wize_set_gradient!(v,s,k,Val(l),V) end end + + #r[i] = v + @inbounds for j in 1:length(b) + r[i,j] = v[j] + end end function _comp_wize_set_gradient!( @@ -257,16 +189,17 @@ end @notimplemented end -function _hessian_nd_cwtpb!( - PT::Type{<:Polynomial}, - v::AbstractVector{G}, - x, - orders::SMatrix{L,D,Int}, - comp_terms::NTuple{L,CartesianIndices{D}}, +function _hessian_nd!( + b::CompWiseTensorPolyBasis{D,V,K,PT,L}, x, + r::AbstractMatrix{H}, i, + v::AbstractVector{H}, c::AbstractMatrix{T}, g::AbstractMatrix{T}, h::AbstractMatrix{T}, - ::Type{V}) where {G,L,D,T,V} + s::MMatrix{D,D,T}) where {D,V,K,PT,L,H,T} + + orders = b.orders + comp_terms = get_comp_terms(b) for d in 1:D # for each spatial coordinate d, the order at which the basis should be @@ -275,7 +208,6 @@ function _hessian_nd_cwtpb!( _derivatives_1d!(PT,Kd,(c,g,h),x,d) end - s = zero(Mutable(V)) k = 1 for (l,terms) in enumerate(comp_terms) @@ -302,6 +234,11 @@ function _hessian_nd_cwtpb!( k = _comp_wize_set_gradient!(v,s,k,l,V) end end + + #r[i] = v + @inbounds for j in 1:length(b) + r[i,j] = v[j] + end end diff --git a/src/Polynomials/ModalC0Bases.jl b/src/Polynomials/ModalC0Bases.jl index 1789a5ac7..f905261f6 100644 --- a/src/Polynomials/ModalC0Bases.jl +++ b/src/Polynomials/ModalC0Bases.jl @@ -1,92 +1,88 @@ -struct ModalC0BasisFunction <: Field end +struct ModalC0 <: Polynomial end -struct ModalC0Basis{D,T,V} <: AbstractVector{ModalC0BasisFunction} +struct ModalC0Basis{D,V,T,K} <: PolynomialBasis{D,V,K,ModalC0} orders::NTuple{D,Int} terms::Vector{CartesianIndex{D}} - a::Vector{Point{D,V}} - b::Vector{Point{D,V}} + a::Vector{Point{D,T}} + b::Vector{Point{D,T}} + function ModalC0Basis{D}( - ::Type{T}, + ::Type{V}, orders::NTuple{D,Int}, terms::Vector{CartesianIndex{D}}, - a::Vector{Point{D,V}}, - b::Vector{Point{D,V}}) where {D,T,V} + a::Vector{Point{D,T}}, + b::Vector{Point{D,T}}) where {D,V,T} + + @check T == eltype(V) "Point and polynomial values should have the same scalar body" + K = maximum(orders, init=0) - new{D,T,V}(orders,terms,a,b) + new{D,V,T,K}(orders,terms,a,b) end end -@inline Base.size(a::ModalC0Basis{D,T,V}) where {D,T,V} = (length(a.terms)*num_indep_components(T),) -@inline Base.getindex(a::ModalC0Basis,i::Integer) = ModalC0BasisFunction() -@inline Base.IndexStyle(::ModalC0Basis) = IndexLinear() - function ModalC0Basis{D}( - ::Type{T}, + ::Type{V}, orders::NTuple{D,Int}, - a::Vector{Point{D,V}}, - b::Vector{Point{D,V}}; + a::Vector{Point{D,T}}, + b::Vector{Point{D,T}}; filter::Function=_q_filter, - sort!::Function=_sort_by_nfaces!) where {D,T,V} + sort!::Function=_sort_by_nfaces!) where {D,V,T} terms = _define_terms_mc0(filter, sort!, orders) - ModalC0Basis{D}(T,orders,terms,a,b) + ModalC0Basis{D}(V,orders,terms,a,b) end function ModalC0Basis{D}( - ::Type{T}, + ::Type{V}, orders::NTuple{D,Int}, - sa::Point{D,V}, - sb::Point{D,V}; + sa::Point{D,T}, + sb::Point{D,T}; filter::Function=_q_filter, - sort!::Function=_sort_by_nfaces!) where {D,T,V} + sort!::Function=_sort_by_nfaces!) where {D,V,T} terms = _define_terms_mc0(filter, sort!, orders) a = fill(sa,length(terms)) b = fill(sb,length(terms)) - ModalC0Basis{D}(T,orders,terms,a,b) + ModalC0Basis{D}(V,orders,terms,a,b) end function ModalC0Basis{D}( - ::Type{T}, + ::Type{V}, orders::NTuple{D,Int}; filter::Function=_q_filter, - sort!::Function=_sort_by_nfaces!) where {D,T} + sort!::Function=_sort_by_nfaces!) where {D,V} - sa = Point{D,eltype(T)}(tfill(zero(eltype(T)),Val{D}())) - sb = Point{D,eltype(T)}(tfill(one(eltype(T)),Val{D}())) - ModalC0Basis{D}(T,orders,sa,sb,filter=filter,sort! = sort!) + T = eltype(V) + sa = Point{D,T}(tfill(zero(T),Val{D}())) + sb = Point{D,T}(tfill( one(T),Val{D}())) + ModalC0Basis{D}(V,orders,sa,sb; filter=filter, sort! =sort!) end function ModalC0Basis{D}( - ::Type{T}, + ::Type{V}, order::Int, - a::Vector{Point{D,V}}, - b::Vector{Point{D,V}}; + a::Vector{Point{D,T}}, + b::Vector{Point{D,T}}; filter::Function=_q_filter, - sort!::Function=_sort_by_nfaces!) where {D,T,V} + sort!::Function=_sort_by_nfaces!) where {D,V,T} orders = tfill(order,Val{D}()) - ModalC0Basis{D}(T,orders,a,b,filter=filter,sort! = sort!) + ModalC0Basis{D}(V,orders,a,b; filter=filter, sort! =sort!) end function ModalC0Basis{D}( - ::Type{T}, + ::Type{V}, order::Int; filter::Function=_q_filter, - sort!::Function=_sort_by_nfaces!) where {D,T} + sort!::Function=_sort_by_nfaces!) where {D,V} orders = tfill(order,Val{D}()) - ModalC0Basis{D}(T,orders,filter=filter,sort! = sort!) + ModalC0Basis{D}(V,orders; filter=filter, sort! =sort!) end # API -""" - get_order(b::ModalC0Basis) -""" -function get_order(b::ModalC0Basis) - maximum(b.orders, init=0) -end +@inline Base.size(a::ModalC0Basis{D,V}) where {D,V} = (length(a.terms)*num_indep_components(V),) """ get_orders(b::ModalC0Basis) @@ -95,173 +91,10 @@ function get_orders(b::ModalC0Basis) b.orders end -return_type(::ModalC0Basis{D,T,V}) where {D,T,V} = T - -# Field implementation - -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) - n = get_order(f) + 1 - 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::ModalC0Basis{D,T,V},x::AbstractVector{<:Point}) where {D,T,V} - r, v, c = cache - np = length(x) - ndof = length(f) - n = get_order(f) + 1 - setsize!(r,(np,ndof)) - setsize!(v,(ndof,)) - setsize!(c,(D,n)) - for i in 1:np - @inbounds xi = x[i] - _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 - end - r.array -end - -function return_cache( - fg::FieldGradientArray{1,ModalC0Basis{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) - xi = testitem(x) - T = gradient_type(V,xi) - n = get_order(f) + 1 - 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,ModalC0Basis{D,T,V}}, - x::AbstractVector{<:Point}) where {D,T,V} - - f = fg.fa - r, v, c, g = cache - np = length(x) - ndof = length(f) - n = get_order(f) + 1 - 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_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 - end - r.array -end - -function return_cache( - fg::FieldGradientArray{2,ModalC0Basis{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) - xi = testitem(x) - T = gradient_type(gradient_type(V,xi),xi) - n = get_order(f) + 1 - 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,ModalC0Basis{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) - n = get_order(f) + 1 - 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_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 - end - r.array -end - -# Optimizing evaluation at a single point - -function return_cache(f::AbstractVector{ModalC0BasisFunction},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{ModalC0BasisFunction},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{ModalC0BasisFunction}}, 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{ModalC0BasisFunction}}, 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 ) -_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. @@ -308,95 +141,43 @@ function _define_terms_mc0(filter,sort!,orders) collect(lazy_map(Reindex(terms),mask)) end -""" -Reference: equation (17) in -Badia, S.; Neiva, E. & Verdugo, F.; (2022); -Robust high-order unfitted finite elements by interpolation-based discrete extension, -Computers & Mathematics with Applications, -https://doi.org/10.1016/j.camwa.2022.09.027 -""" -function _evaluate_1d_mc0!(v::AbstractMatrix{T},x,a,b,order,d) where T - @assert order > 0 - n = order + 1 - o = one(T) - @inbounds v[d,1] = o - 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 +################################# +# nD evaluations implementation # +################################# -function _gradient_1d_mc0!(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_mc0!(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_mc0!( +function _evaluate_nd!( + basis::ModalC0Basis{D,V,T,K}, x, + r::AbstractMatrix{V}, i, 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} + c::AbstractMatrix{T}) where {D,V,T,K} + + terms = basis.terms + orders = basis.orders + a = basis.a + b = basis.b - dim = D - o = one(T) k = 1 l = length(terms) for (i,ci) in enumerate(terms) - for d in 1:dim + for d in 1:D _evaluate_1d_mc0!(c,x,a[i],b[i],orders[d],d) end - s = o - for d in 1:dim + s = one(T) + for d in 1:D @inbounds s *= c[d,ci[d]] end k = _set_value_mc0!(v,s,k,l) - end + #r[i] = v + @inbounds for j in 1:length(basis) + r[i,j] = v[j] + end end @inline function _set_value_mc0!(v::AbstractVector{V},s::T,k,l) where {V,T} @@ -414,36 +195,34 @@ end k+1 end -function _gradient_nd_mc0!( +function _gradient_nd!( + basis::ModalC0Basis{D,V,T,K}, x, + r::AbstractMatrix{G}, i, 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} + s::MVector{D,T}) where {D,V,T,K,G} + + terms = basis.terms + orders = basis.orders + a = basis.a + b = basis.b - 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 + for d in 1: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 for i in eachindex(s) - @inbounds s[i] = o + @inbounds s[i] = one(T) end - for q in 1:dim - for d in 1:dim + for q in 1:D + for d in 1:D if d != q @inbounds s[q] *= c[d,ci[d]] else @@ -453,9 +232,12 @@ function _gradient_nd_mc0!( end k = _set_gradient_mc0!(v,s,k,l,V) - end + #r[i] = v + @inbounds for j in 1:length(basis) + r[i,j] = v[j] + end end @inline function _set_gradient_mc0!( @@ -500,39 +282,37 @@ end return Expr(:block, body ,:(return k+1)) end -function _hessian_nd_mc0!( +function _hessian_nd!( + basis::ModalC0Basis{D,V,T,K}, x, + r::AbstractMatrix{G}, i, 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} + s::MMatrix{D,D,T}) where {D,V,T,K,G} + + terms = basis.terms + orders = basis.orders + a = basis.a + b = basis.b - 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 + for d in 1: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 for i in eachindex(s) - @inbounds s[i] = o + @inbounds s[i] = one(T) end - for r in 1:dim - for q in 1:dim - for d in 1:dim + for r in 1:D + for q in 1:D + for d in 1:D if d != q && d != r @inbounds s[r,q] *= c[d,ci[d]] elseif d == q && d ==r @@ -548,4 +328,74 @@ function _hessian_nd_mc0!( end + #r[i] = v + @inbounds for j in 1:length(basis) + r[i,j] = v[j] + end +end + + +################################# +# 1D evaluations implementation # +################################# + +""" +Reference: equation (17) in + +Badia, S.; Neiva, E. & Verdugo, F.; (2022); +Robust high-order unfitted finite elements by interpolation-based discrete extension, +Computers & Mathematics with Applications, +https://doi.org/10.1016/j.camwa.2022.09.027 +""" +function _evaluate_1d_mc0!(v::AbstractMatrix{T},x,a,b,order,d) where T + @assert order > 0 + n = order + 1 + o = one(T) + @inbounds v[d,1] = o - 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_mc0!(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_mc0!(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 + diff --git a/src/Polynomials/NedelecPrebasisOnSimplex.jl b/src/Polynomials/NedelecPrebasisOnSimplex.jl index 0bd420387..9613f15de 100644 --- a/src/Polynomials/NedelecPrebasisOnSimplex.jl +++ b/src/Polynomials/NedelecPrebasisOnSimplex.jl @@ -1,7 +1,9 @@ -struct NedelecPrebasisOnSimplex{D} <: AbstractVector{Monomial} +struct NedelecPrebasisOnSimplex{D,V,K} <: PolynomialBasis{D,V,K,Monomial} order::Int function NedelecPrebasisOnSimplex{D}(order::Integer) where D - new{D}(Int(order)) + K = Int(order)+1 + V = VectorValue{D,Float64} + new{D,V,K}(Int(order)) end end @@ -11,21 +13,14 @@ function Base.size(a::NedelecPrebasisOnSimplex{D}) where D (n,) end -Base.getindex(a::NedelecPrebasisOnSimplex,i::Integer) = Monomial() -Base.IndexStyle(::Type{<:NedelecPrebasisOnSimplex}) = IndexLinear() - -get_order(f::NedelecPrebasisOnSimplex) = f.order - -return_type(::NedelecPrebasisOnSimplex{D}) where {D} = VectorValue{D,Float64} - function return_cache( f::NedelecPrebasisOnSimplex{D},x::AbstractVector{<:Point}) where D np = length(x) ndofs = length(f) V = eltype(x) a = zeros(V,(np,ndofs)) - k = f.order+1 - P = MonomialBasis(Val(D),VectorValue{D,Float64},k-1,(e,order)->sum(e)<=order) + K = get_order(f) + P = MonomialBasis(Val(D),VectorValue{D,Float64},K-1,(e,order)->sum(e)<=order) cP = return_cache(P,x) CachedArray(a), cP, P end @@ -33,7 +28,7 @@ end function evaluate!( cache,f::NedelecPrebasisOnSimplex{3},x::AbstractVector{<:Point}) ca,cP,P = cache - k = f.order+1 + K = get_order(f) np = length(x) ndofs = length(f) ndofsP = length(P) @@ -51,26 +46,26 @@ function evaluate!( i = ndofsP x1,x2,x3 = x[ip] zp = zero(x1) - for β in 1:k - for α in 1:(k+1-β) + for β in 1:K + for α in 1:(K+1-β) i += 1 a[ip,i] = VectorValue( - -x1^(α-1)*x2^(k-α-β+2)*x3^(β-1), - x1^α*x2^(k-α-β+1)*x3^(β-1), + -x1^(α-1)*x2^(K-α-β+2)*x3^(β-1), + x1^α*x2^(K-α-β+1)*x3^(β-1), zp) i += 1 a[ip,i] = VectorValue( - -x1^(k-α-β+1)*x2^(β-1)*x3^α, + -x1^(K-α-β+1)*x2^(β-1)*x3^α, zp, - x1^(k-α-β+2)*x2^(β-1)*x3^(α-1)) + x1^(K-α-β+2)*x2^(β-1)*x3^(α-1)) end end - for γ in 1:k + for γ in 1:K i += 1 a[ip,i] = VectorValue( zp, - -x2^(γ-1)*x3^(k-γ+1), - x2^γ*x3^(k-γ)) + -x2^(γ-1)*x3^(K-γ+1), + x2^γ*x3^(K-γ)) end end a @@ -79,7 +74,7 @@ end function evaluate!( cache,f::NedelecPrebasisOnSimplex{2},x::AbstractVector{<:Point}) ca,cP,P = cache - k = f.order+1 + K = get_order(f) np = length(x) ndofs = length(f) ndofsP = length(P) @@ -97,9 +92,9 @@ function evaluate!( i = ndofsP x1,x2 = x[ip] zp = zero(x1) - for α in 1:k + for α in 1:K i += 1 - a[ip,i] = VectorValue(-x1^(α-1)*x2^(k-α+1),x1^α*x2^(k-α)) + a[ip,i] = VectorValue(-x1^(α-1)*x2^(K-α+1),x1^α*x2^(K-α)) end #a[ip,1] = VectorValue((u,z)) #a[ip,2] = VectorValue((z,u)) @@ -118,8 +113,8 @@ function return_cache( V = eltype(x) G = gradient_type(V,xi) a = zeros(G,(np,ndofs)) - k = f.order+1 - mb = MonomialBasis(Val(D),VectorValue{D,Float64},k-1,(e,order)->sum(e)<=order) + K = get_order(f) + mb = MonomialBasis(Val(D),VectorValue{D,Float64},K-1,_p_filter) P = Broadcasting(∇)(mb) cP = return_cache(P,x) CachedArray(a), cP, P @@ -131,7 +126,7 @@ function evaluate!( x::AbstractVector{<:Point}) ca,cP,P = cache f = g.fa - k = f.order+1 + K = get_order(f) np = length(x) ndofs = length(f) setsize!(ca,(np,ndofs)) @@ -150,47 +145,47 @@ function evaluate!( i = ndofsP x1,x2,x3 = x[ip] zp = zero(x1) - for β in 1:k - for α in 1:(k+1-β) + for β in 1:K + for α in 1:(K+1-β) i += 1 a[ip,i] = TensorValue( - #-x1^(α-1)*x2^(k-α-β+2)*x3^(β-1), - -(α-1)*_exp(x1,α-2)*x2^(k-α-β+2)*x3^(β-1), - -x1^(α-1)*(k-α-β+2)*_exp(x2,k-α-β+1)*x3^(β-1), - -x1^(α-1)*x2^(k-α-β+2)*(β-1)*_exp(x3,β-2), - #x1^α*x2^(k-α-β+1)*x3^(β-1), - α*_exp(x1,α-1)*x2^(k-α-β+1)*x3^(β-1), - x1^α*(k-α-β+1)*_exp(x2,k-α-β)*x3^(β-1), - x1^α*x2^(k-α-β+1)*(β-1)*_exp(x3,β-2), + #-x1^(α-1)*x2^(K-α-β+2)*x3^(β-1), + -(α-1)*_exp(x1,α-2)*x2^(K-α-β+2)*x3^(β-1), + -x1^(α-1)*(K-α-β+2)*_exp(x2,K-α-β+1)*x3^(β-1), + -x1^(α-1)*x2^(K-α-β+2)*(β-1)*_exp(x3,β-2), + #x1^α*x2^(K-α-β+1)*x3^(β-1), + α*_exp(x1,α-1)*x2^(K-α-β+1)*x3^(β-1), + x1^α*(K-α-β+1)*_exp(x2,K-α-β)*x3^(β-1), + x1^α*x2^(K-α-β+1)*(β-1)*_exp(x3,β-2), #zp, zp,zp,zp) i += 1 a[ip,i] = TensorValue( - #-x1^(k-α-β+1)*x2^(β-1)*x3^α, - -(k-α-β+1)*_exp(x1,k-α-β)*x2^(β-1)*x3^α, - -x1^(k-α-β+1)*(β-1)*_exp(x2,β-2)*x3^α, - -x1^(k-α-β+1)*x2^(β-1)*α*_exp(x3,α-1), + #-x1^(K-α-β+1)*x2^(β-1)*x3^α, + -(K-α-β+1)*_exp(x1,K-α-β)*x2^(β-1)*x3^α, + -x1^(K-α-β+1)*(β-1)*_exp(x2,β-2)*x3^α, + -x1^(K-α-β+1)*x2^(β-1)*α*_exp(x3,α-1), # zp zp,zp,zp, - #x1^(k-α-β+2)*x2^(β-1)*x3^(α-1), - (k-α-β+2)*_exp(x1,k-α-β+1)*x2^(β-1)*x3^(α-1), - x1^(k-α-β+2)*(β-1)*_exp(x2,β-2)*x3^(α-1), - x1^(k-α-β+2)*x2^(β-1)*(α-1)*_exp(x3,α-2)) + #x1^(K-α-β+2)*x2^(β-1)*x3^(α-1), + (K-α-β+2)*_exp(x1,K-α-β+1)*x2^(β-1)*x3^(α-1), + x1^(K-α-β+2)*(β-1)*_exp(x2,β-2)*x3^(α-1), + x1^(K-α-β+2)*x2^(β-1)*(α-1)*_exp(x3,α-2)) end end - for γ in 1:k + for γ in 1:K i += 1 a[ip,i] = TensorValue( #zp zp,zp,zp, - #-x2^(γ-1)*x3^(k-γ+1), - -0*x2^(γ-1)*x3^(k-γ+1), - -(γ-1)*_exp(x2,γ-2)*x3^(k-γ+1), - -x2^(γ-1)*(k-γ+1)*_exp(x3,k-γ), - #x2^γ*x3^(k-γ), - 0*x2^γ*x3^(k-γ), - γ*_exp(x2,γ-1)*x3^(k-γ), - x2^γ*(k-γ)*_exp(x3,k-γ-1)) + #-x2^(γ-1)*x3^(K-γ+1), + -0*x2^(γ-1)*x3^(K-γ+1), + -(γ-1)*_exp(x2,γ-2)*x3^(K-γ+1), + -x2^(γ-1)*(K-γ+1)*_exp(x3,K-γ), + #x2^γ*x3^(K-γ), + 0*x2^γ*x3^(K-γ), + γ*_exp(x2,γ-1)*x3^(K-γ), + x2^γ*(K-γ)*_exp(x3,K-γ-1)) end #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)) @@ -207,7 +202,7 @@ function evaluate!( x::AbstractVector{<:Point}) f = g.fa ca,cP,P = cache - k = f.order+1 + K = get_order(f) np = length(x) ndofs = length(f) setsize!(ca,(np,ndofs)) @@ -226,15 +221,15 @@ function evaluate!( i = ndofsP x1,x2 = x[ip] zp = zero(x1) - for α in 1:k + for α in 1:K i += 1 a[ip,i] = TensorValue( - #-x1^(α-1)*x2^(k-α+1), - -(α-1)*_exp(x1,α-2)*x2^(k-α+1), - -x1^(α-1)*(k-α+1)*_exp(x2,k-α), - #x1^α*x2^(k-α), - α*_exp(x1,α-1)*x2^(k-α), - x1^α*(k-α)*_exp(x2,k-α-1)) + #-x1^(α-1)*x2^(K-α+1), + -(α-1)*_exp(x1,α-2)*x2^(K-α+1), + -x1^(α-1)*(K-α+1)*_exp(x2,K-α), + #x1^α*x2^(K-α), + α*_exp(x1,α-1)*x2^(K-α), + x1^α*(K-α)*_exp(x2,K-α-1)) end #a[ip,3] = TensorValue((z,-u, u,z)) end diff --git a/src/Polynomials/NonTensorRTPolyBases.jl b/src/Polynomials/NonTensorRTPolyBases.jl index 1deb80886..bedc307bc 100644 --- a/src/Polynomials/NonTensorRTPolyBases.jl +++ b/src/Polynomials/NonTensorRTPolyBases.jl @@ -56,66 +56,23 @@ Base.size(a::NonTensorRTPolyBasis{D}) where {D} = D*length(a.pterms) + length(a. Base.getindex(a::NonTensorRTPolyBasis,i::Integer) = Monomial() Base.IndexStyle(::NonTensorRTPolyBasis) = IndexLinear() -function evaluate!( - cache,f::NonTensorRTPolyBasis{D,V,K,PT}, - x::AbstractVector{<:Point}) where {D,V,K,PT} - - r, v, c = cache - np = length(x) - ndof = length(f) - ndof_1d = get_order(f) + 1 # K+1 - setsize!(r,(np,ndof)) - setsize!(v,(ndof,)) - setsize!(c,(D,ndof_1d)) - for i in 1:np - @inbounds xi = x[i] - _evaluate_nd_rt!(PT,v,xi,K,f.pterms,f.sterms,c) - for j in 1:ndof - @inbounds r[i,j] = v[j] - end - end - r.array -end - -function evaluate!(cache, - fg::FieldGradientArray{1,NonTensorRTPolyBasis{D,V,K,PT}}, - x::AbstractVector{<:Point}) where {D,V,K,PT} - - f = fg.fa - r, v, c, g = cache - np = length(x) - ndof = length(f) - ndof_1d = get_order(f) + 1 # K+1 - setsize!(r,(np,ndof)) - setsize!(v,(ndof,)) - setsize!(c,(D,ndof_1d)) - setsize!(g,(D,ndof_1d)) - for i in 1:np - @inbounds xi = x[i] - _gradient_nd_rt!(PT,v,xi,K,f.pterms,f.sterms,c,g,V) - for j in 1:ndof - @inbounds r[i,j] = v[j] - end - end - r.array -end +################################# +# nD evaluations implementation # +################################# -# Helpers +function _evaluate_nd!( + b::NonTensorRTPolyBasis{D,V,K,PT}, x, + r::AbstractMatrix{V}, i, + v::AbstractVector{V}, + c::AbstractMatrix{T}) where {D,V,K,PT,T} - -function _evaluate_nd_rt!( - ::Type{PT}, - v::AbstractVector{V}, # V = VectorValue{D,T} - x, - order, - pterms::Vector{CartesianIndex{D}}, - sterms::Vector{CartesianIndex{D}}, - c::AbstractMatrix{T}) where {PT,V,T,D} + pterms = b.pterms + sterms = b.sterms for d in 1:D - K = Val(order) - _evaluate_1d!(PT,K,c,x,d) + Kv = Val(K) + _evaluate_1d!(PT,Kv,c,x,d) end m = zero(Mutable(V)) @@ -153,25 +110,29 @@ function _evaluate_nd_rt!( k += 1 end end + + #r[i] = v + @inbounds for j in 1:length(b) + r[i,j] = v[j] + end end -function _gradient_nd_rt!( - ::Type{PT}, +function _gradient_nd!( + b::NonTensorRTPolyBasis{D,V,K,PT}, x, + r::AbstractMatrix{G}, i, v::AbstractVector{G}, - x, - order, - pterms::Vector{CartesianIndex{D}}, - sterms::Vector{CartesianIndex{D}}, c::AbstractMatrix{T}, g::AbstractMatrix{T}, - ::Type{V}) where {PT,G,T,D,V} + s::MVector{D,T}) where {D,V,K,PT,G,T} + + pterms = b.pterms + sterms = b.sterms for d in 1:D - K = Val(order) - _derivatives_1d!(PT,K,(c,g),x,d) + Kv = Val(K) + _derivatives_1d!(PT,Kv,(c,g),x,d) end - s = zero(Mutable(V)) m = zero(Mutable(G)) k = 1 @@ -230,6 +191,11 @@ function _gradient_nd_rt!( k += 1 end end + + #r[i] = v + @inbounds for j in 1:length(b) + r[i,j] = v[j] + end end """ @@ -244,7 +210,7 @@ of the functions in this basis is in the ℙ space of degree `order`. `PT<:Polynomial` is the choice of scalar 1D polynomial basis, it must be hierarchichal, see [`isHierarchichal`](@ref). -Returns a `NonTensorRTPolyBasis{D,VectorValue{D,T},order+1,PT}` object, +Returns a `NonTensorRTPolyBasis{D,VectorValue{D,T},order+1,PT}` object for `D`>1, or `TensorPolynomialBasis{1,VectorValue{1,T},order+1,PT}` for `D`=1. """ function PCurlGradBasis(::Type{PT},::Val{D},::Type{T},order::Int) where {PT,D,T} diff --git a/src/Polynomials/PolynomialInterfaces.jl b/src/Polynomials/PolynomialInterfaces.jl index e1066ac71..3f3e2433b 100644 --- a/src/Polynomials/PolynomialInterfaces.jl +++ b/src/Polynomials/PolynomialInterfaces.jl @@ -1,6 +1,6 @@ -################################################# -# Polynomial family/types with their parameters # -################################################# +############################ +# Polynomial family/types # +############################ """ Abstract type for 1- or n-D polynomial family of maximum order k. @@ -15,6 +15,26 @@ of the basis of order `k-1` and an other order `k` polynomial. """ isHierarchical(::Type{Polynomial}) = @abstractmethod + +########################################### +# Polynomial basis abstract type and APIs # +########################################### + +# Notations: +# +# D: spatial / input space dimension +# T: scalar type (Float64, ...) +# V: concrete type of image values (T, VectorValue{D,T} etc.) +# G: concrete MultiValue type holding the gradient or hessian of a function of +# value V, i.e. gradient_type(V,Point{D}) +# or gradient_type(gradient_type(V,p::Point{D}), p) +# +# PT: a concrete `Polynomial` type +# K: integer polynomial order (maximum of any component and in any direction in nD). +# np: number of points at which a basis is evaluated +# ndof: number of basis vectors, num_indep_components(V) × dimension of the polynomial space +# ndof_1d: maximum of 1D basis vector in any spatial dimension + """ PolynomialBasis{D,V,K,PT<:Polynomial} <: AbstractVector{PT} @@ -39,7 +59,7 @@ abstract type PolynomialBasis{D,V,K,PT<:Polynomial} <: AbstractVector{PT} end Return the maximum polynomial order in a dimension, or `0` in 0D. """ -get_order(::PolynomialBasis{D,V,K}) where {D,V,K} = K +@inline get_order(::PolynomialBasis{D,V,K}) where {D,V,K} = K ################################ @@ -84,6 +104,190 @@ function return_cache( _return_cache(f,x,G,Val(N)) end +function evaluate!(cache, + f::PolynomialBasis{D,V,K,PT}, + x::AbstractVector{<:Point}) where {D,V,K,PT} + + r, v, c = cache + np = length(x) + ndof = length(f) + ndof_1d = get_order(f) + 1 # K+1 + setsize!(r,(np,ndof)) + setsize!(v,(ndof,)) + setsize!(c,(D,ndof_1d)) + for i in 1:np + @inbounds xi = x[i] + _evaluate_nd!(f,xi,r,i,v,c) + end + r.array +end + +function evaluate!(cache, + fg::FieldGradientArray{1,<:PolynomialBasis{D,V,K,PT}}, + x::AbstractVector{<:Point}) where {D,V,K,PT} + + f = fg.fa + r, v, c, g = cache + s = zero(Mutable(VectorValue{D,eltype(V)})) + np = length(x) + ndof = length(f) + ndof_1d = get_order(f) + 1 # K+1 + setsize!(r,(np,ndof)) + setsize!(v,(ndof,)) + setsize!(c,(D,ndof_1d)) + setsize!(g,(D,ndof_1d)) + for i in 1:np + @inbounds xi = x[i] + _gradient_nd!(f,xi,r,i,v,c,g,s) + end + r.array +end + +function evaluate!(cache, + fg::FieldGradientArray{2,<:PolynomialBasis{D,V,K,PT}}, + x::AbstractVector{<:Point}) where {D,V,K,PT} + + f = fg.fa + r, v, c, g, h = cache + s = zero(Mutable(TensorValue{D,D,eltype(V)})) + np = length(x) + ndof = length(f) + ndof_1d = get_order(f) + 1 # K+1 + setsize!(r,(np,ndof)) + setsize!(v,(ndof,)) + setsize!(c,(D,ndof_1d)) + setsize!(g,(D,ndof_1d)) + setsize!(h,(D,ndof_1d)) + for i in 1:np + @inbounds xi = x[i] + _hessian_nd!(f,xi,r,i,v,c,g,h,s) + end + r.array +end + + +############################### +# nD internal polynomial APIs # +############################### + +""" + _evaluate_nd!(b,xi,r,i,v,c) + +Compute and assign: r[i] = b(xi) = (b₁(xi), ..., bₙ(xi)) + +where n = length(`b`) (cardinal of the basis), that is the function computes +the basis polynomials at xi and setting the result in the `i`th row of `r`. + +`v` a mutable cache for of `b(xi)` and `c` is a mutable `D`×`K` cache. +""" +function _evaluate_nd!( + b::PolynomialBasis{D,V,K}, + x, + r::AbstractMatrix{V}, + i, + v::AbstractVector{V}, + c::AbstractMatrix{T}) where {D,V,K,T} + + @abstractmethod +end + +""" + _gradient_nd!(b,xi,r,i,v,c,g,s) + +Compute and assign: `r`[`i`] = ∇`b`(`xi`) = (∇`b`₁(`xi`), ..., ∇`b`ₙ(`xi`)) + +where n = length(`b`) (cardinal of the basis), like [`_evaluate_nd!`](@ref) but +for gradients of `b`ₖ(`xi`), and + +- `g` is a mutable `D`×`K` cache (for 1D poly deriv evals). +- `s` is a mutable length `D` cache for ∇`b`ₖ(`xi`). +""" +function _gradient_nd!( + b::PolynomialBasis{D,V,K}, + x, + r::AbstractMatrix{G}, + i, + v::AbstractVector{G}, + c::AbstractMatrix{T}, + g::AbstractMatrix{T}, + s::MVector{D,T}) where {D,V,K,G,T} + + @abstractmethod +end + + +""" + _hessian_nd!(b,xi,r,i,v,c,g,h,s) + +Compute and assign: `r`[`i`] = H`b`(`xi`) = (H`b`₁(`xi`), ..., H`b`ₙ(`xi`)) + +where n = length(`b`) (cardinal of the basis), like [`_evaluate_nd!`](@ref) but +for hessian matrices/tensor of `b`ₖ(`xi`), and + +- `h` is a mutable `D`×`K` cache (for 1D poly second deriv evals). +- `s` is a mutable `D`×`D` cache for H`b`ₖ(`xi`). +""" +function _hessian_nd!( + b::PolynomialBasis{D,V,K}, + x, + r::AbstractMatrix{H}, + i, + v::AbstractVector{H}, + c::AbstractMatrix{T}, + g::AbstractMatrix{T}, + h::AbstractMatrix{T}, + s::MMatrix{D,D,T}) where {D,V,K,H,T} + + @abstractmethod +end + + +############################################## +# Optimizing of evaluation at a single point # +############################################## + +function return_cache(f::PolynomialBasis,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::PolynomialBasis,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,<:PolynomialBasis}, 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,<:PolynomialBasis}, 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 + ############################### # 1D internal polynomial APIs # @@ -150,47 +354,3 @@ function _derivatives_1d!(PT::Type{<:Polynomial},::Val{K},t::NTuple{3},x,d) wher _hessian_1d!( PT, Val(K), t[3], x, d) end -# Optimizing evaluation at a single point - -function return_cache(f::AbstractVector{PT},x::Point) where PT<:Polynomial - 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{PT},x::Point) where PT<:Polynomial - 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{PT}}, x::Point) where {N,PT<:Polynomial} - - 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{PT}}, x::Point) where {N,PT<:Polynomial} - - 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 - diff --git a/src/Polynomials/TensorPolynomialBases.jl b/src/Polynomials/TensorPolynomialBases.jl index 986cbd10d..87027ba86 100644 --- a/src/Polynomials/TensorPolynomialBases.jl +++ b/src/Polynomials/TensorPolynomialBases.jl @@ -2,18 +2,6 @@ # Tensorial nD polynomial bases # ################################# -# Notations: -# D: spatial / input space dimension -# T: scalar type (Float64, ...) -# V: concrete type of image values (T, VectorValue{D,T} etc.) -# G: concrete MultiValue type holding the gradient or hessian of a function of -# value V, i.e. gradient_type(V,Point{D}) or gradient_type(gradient_type(V,p::Point{D}), p) -# -# PT: a concrete `Polynomial` type -# np: number of points at which a basis is evaluated -# ndof: number of basis vectors, num_indep_components(V) × dimension of the polynomial space -# ndof_1d: maximum of 1D basis vector in any spatial dimension - """ struct TensorPolynomialBasis{D,V,K,PT} <: PolynomialBasis{D,V,K,PT} @@ -175,93 +163,18 @@ function _define_terms(filter,orders) end -######################## -# Field implementation # -######################## - -function evaluate!(cache,f::TensorPolynomialBasis{D,V,K,PT},x::AbstractVector{<:Point}) where {D,V,K,PT} - r, v, c = cache - np = length(x) - ndof = length(f) - ndof_1d = get_order(f) + 1 # K+1 - setsize!(r,(np,ndof)) - setsize!(v,(ndof,)) - setsize!(c,(D,ndof_1d)) - for i in 1:np - # TODO Shouldn't we avoid accessing here ? (pass x and i) - @inbounds xi = x[i] - _tensorial_evaluate_nd!(PT,v,xi,f.orders,f.terms,c) - for j in 1:ndof - # TODO Shouldn't we assign in place in _tensorial_evaluate_nd instead of copying everything? - @inbounds r[i,j] = v[j] - end - end - r.array -end - -function evaluate!( - cache, - fg::FieldGradientArray{1,<:TensorPolynomialBasis{D,V,K,PT}}, - x::AbstractVector{<:Point}) where {D,V,K,PT} - - f = fg.fa - r, v, c, g = cache - s = zero(Mutable(VectorValue{D,eltype(V)})) - np = length(x) - ndof = length(f) - ndof_1d = get_order(f) + 1 # K+1 - setsize!(r,(np,ndof)) - setsize!(v,(ndof,)) - setsize!(c,(D,ndof_1d)) - setsize!(g,(D,ndof_1d)) - for i in 1:np - # TODO Shouldn't we avoid accessing here ? (pass x and i) - @inbounds xi = x[i] - _tensorial_gradient_nd!(PT,v,xi,f.orders,f.terms,c,g,s,V) - for j in 1:ndof - # TODO Shouldn't we assign in place in _tensorial_gradient_nd instead of copying everything? - @inbounds r[i,j] = v[j] - end - end - r.array -end - -function evaluate!( - cache, - fg::FieldGradientArray{2,<:TensorPolynomialBasis{D,V,K,PT}}, - x::AbstractVector{<:Point}) where {D,V,K,PT} - - f = fg.fa - r, v, c, g, h = cache - np = length(x) - ndof = length(f) - ndof_1d = get_order(f) + 1 # K+1 - setsize!(r,(np,ndof)) - setsize!(v,(ndof,)) - setsize!(c,(D,ndof_1d)) - setsize!(g,(D,ndof_1d)) - setsize!(h,(D,ndof_1d)) - for i in 1:np - # TODO Shouldn't we avoid accessing here ? (pass x and i) - @inbounds xi = x[i] - _tensorial_hessian_nd!(PT,v,xi,f.orders,f.terms,c,g,h,V) - for j in 1:ndof - # TODO Shouldn't we assign in place in _tensorial_hessian_nd instead of copying everything? - @inbounds r[i,j] = v[j] - end - end - r.array -end - -# Evaluates +################################# +# nD evaluations implementation # +################################# -function _tensorial_evaluate_nd!( - PT::Type{<:Polynomial}, +function _evaluate_nd!( + b::TensorPolynomialBasis{D,V,K,PT}, x, + r::AbstractMatrix{V}, i, v::AbstractVector{V}, - x, - orders, - terms::AbstractVector{CartesianIndex{D}}, - c::AbstractMatrix{T}) where {V,D,T} + c::AbstractMatrix{T}) where {D,V,K,PT,T} + + terms = b.terms + orders = b.orders for d in 1:D Kd = Val(orders[d]) @@ -278,6 +191,11 @@ function _tensorial_evaluate_nd!( k = _tensorial_set_value!(v,s,k) end + + #r[i] = v + @inbounds for j in 1:length(b) + r[i,j] = v[j] + end end """ @@ -316,16 +234,16 @@ function _tensorial_set_value!(v::AbstractVector{V},s::T,k) where {V,T} k end -function _tensorial_gradient_nd!( - PT::Type{<:Polynomial}, +function _gradient_nd!( + b::TensorPolynomialBasis{D,V,K,PT}, x, + r::AbstractMatrix{G}, i, v::AbstractVector{G}, - x, - orders, - terms::AbstractVector{CartesianIndex{D}}, c::AbstractMatrix{T}, g::AbstractMatrix{T}, - s::AbstractVector{T}, - ::Type{V}) where {G,D,T,V} + s::MVector{D,T}) where {D,V,K,PT,G,T} + + terms = b.terms + orders = b.orders for d in 1:D Kd = Val(orders[d]) @@ -351,6 +269,11 @@ function _tensorial_gradient_nd!( k = _tensorial_set_gradient!(v,s,k,V) end + + #r[i] = v + @inbounds for j in 1:length(b) + r[i,j] = v[j] + end end function _tensorial_set_gradient!( @@ -437,23 +360,23 @@ end return Expr(:block, body ,:(return k)) end -function _tensorial_hessian_nd!( - PT::Type{<:Polynomial}, +function _hessian_nd!( + b::TensorPolynomialBasis{D,V,K,PT}, x, + r::AbstractMatrix{G}, i, v::AbstractVector{G}, - x, - orders, - terms::AbstractVector{CartesianIndex{D}}, c::AbstractMatrix{T}, g::AbstractMatrix{T}, h::AbstractMatrix{T}, - ::Type{V}) where {G,D,T,V} + s::MMatrix{D,D,T}) where {D,V,K,PT,G,T} + + terms = b.terms + orders = b.orders for d in 1:D Kd = Val(orders[d]) _derivatives_1d!(PT,Kd,(c,g,h),x,d) end - s = zero(Mutable(TensorValue{D,D,T})) k = 1 for ci in terms @@ -478,5 +401,10 @@ function _tensorial_hessian_nd!( k = _tensorial_set_gradient!(v,s,k,V) end + + #r[i] = v + @inbounds for j in 1:length(b) + r[i,j] = v[j] + end end diff --git a/test/ReferenceFEsTests/NedelecRefFEsTests.jl b/test/ReferenceFEsTests/NedelecRefFEsTests.jl index bd425174a..99ccb68e9 100644 --- a/test/ReferenceFEsTests/NedelecRefFEsTests.jl +++ b/test/ReferenceFEsTests/NedelecRefFEsTests.jl @@ -54,7 +54,7 @@ order = 0 reffe = NedelecRefFE(et,p,order) test_reference_fe(reffe) @test length(get_prebasis(reffe)) == 6 -@test get_order(get_prebasis(reffe)) == 0 +@test get_order(get_prebasis(reffe)) == 1 @test num_dofs(reffe) == 6 @test Conformity(reffe) == CurlConformity() @@ -95,7 +95,7 @@ order = 0 reffe = NedelecRefFE(et,p,order) test_reference_fe(reffe) @test length(get_prebasis(reffe)) == 3 -@test get_order(get_prebasis(reffe)) == 0 +@test get_order(get_prebasis(reffe)) == 1 @test num_dofs(reffe) == 3 @test Conformity(reffe) == CurlConformity() dof_basis = get_dof_basis(reffe) @@ -138,7 +138,7 @@ order = 1 reffe = NedelecRefFE(et,p,order) test_reference_fe(reffe) @test length(get_prebasis(reffe)) == 20 -@test get_order(get_prebasis(reffe)) == 1 +@test get_order(get_prebasis(reffe)) == 2 @test num_dofs(reffe) == 20 @test Conformity(reffe) == CurlConformity() dof_basis = get_dof_basis(reffe) From e9794505ca16d262d12e8aa0c8246308fda212e5 Mon Sep 17 00:00:00 2001 From: Antoine Marteau Date: Fri, 20 Dec 2024 12:13:54 +1100 Subject: [PATCH 067/105] remove the temporary useless cache and associated copies --- src/Polynomials/CompWiseTensorPolyBases.jl | 42 +++------- src/Polynomials/ModalC0Bases.jl | 90 +++++++++------------- src/Polynomials/NonTensorRTPolyBases.jl | 18 +---- src/Polynomials/PolynomialInterfaces.jl | 42 ++++------ src/Polynomials/TensorPolynomialBases.jl | 79 ++++++------------- 5 files changed, 91 insertions(+), 180 deletions(-) diff --git a/src/Polynomials/CompWiseTensorPolyBases.jl b/src/Polynomials/CompWiseTensorPolyBases.jl index dcbcfdb84..45c49ed2e 100644 --- a/src/Polynomials/CompWiseTensorPolyBases.jl +++ b/src/Polynomials/CompWiseTensorPolyBases.jl @@ -60,7 +60,6 @@ end function _evaluate_nd!( b::CompWiseTensorPolyBasis{D,V,K,PT,L}, x, r::AbstractMatrix{V}, i, - v::AbstractVector{V}, c::AbstractMatrix{T}) where {D,V,K,PT,L,T} orders = b.orders @@ -84,36 +83,30 @@ function _evaluate_nd!( s *= c[d,ci[d]] end - k = _comp_wize_set_value!(v,s,k,l) + k = _comp_wize_set_value!(r,i,s,k,l) end end - - #r[i] = v - @inbounds for j in 1:length(b) - r[i,j] = v[j] - end end """ - _comp_wize_set_value!(v::AbstractVector{V},s::T,k,l) + _comp_wize_set_value!(r::AbstractMatrix{V},i,s::T,k,l) ``` -v[k] = V(0, ..., 0, s, 0, ..., 0); return k+1 +r[i,k] = V(0, ..., 0, s, 0, ..., 0); return k+1 ``` where `s` is at position `l` in `V<:MultiValue`. """ -function _comp_wize_set_value!(v::AbstractVector{V},s::T,k,l) where {V,T} +function _comp_wize_set_value!(r::AbstractMatrix{V},i,s::T,k,l) where {V,T} z = zero(T) ncomp = num_indep_components(V) - v[k] = ntuple(i -> ifelse(i == l, s, z),Val(ncomp)) + r[i,k] = ntuple(i -> ifelse(i == l, s, z),Val(ncomp)) return k + 1 end function _gradient_nd!( b::CompWiseTensorPolyBasis{D,V,K,PT,L}, x, r::AbstractMatrix{G}, i, - v::AbstractVector{G}, c::AbstractMatrix{T}, g::AbstractMatrix{T}, s::MVector{D,T}) where {D,V,K,PT,L,G,T} @@ -147,25 +140,20 @@ function _gradient_nd!( end end - k = _comp_wize_set_gradient!(v,s,k,Val(l),V) + k = _comp_wize_set_gradient!(r,i,s,k,Val(l),V) end end - - #r[i] = v - @inbounds for j in 1:length(b) - r[i,j] = v[j] - end end function _comp_wize_set_gradient!( - v::AbstractVector{G},s,k,l,::Type{<:Real}) where G + r::AbstractMatrix{G},i,s,k,l,::Type{<:Real}) where G - @inbounds v[k] = s + @inbounds r[i,k] = s k+1 end @generated function _comp_wize_set_gradient!( - v::AbstractVector{G},s,k,::Val{l},::Type{V}) where {G,l,V} + r::AbstractMatrix{G},i,s,k,::Val{l},::Type{V}) where {G,l,V} N_val_dims = length(size(V)) s_size = size(G)[1:end-N_val_dims] @@ -177,14 +165,14 @@ end for ci in CartesianIndices(s_size) m[ci,l] = "(@inbounds s[$ci])" end - body *= "@inbounds v[k] = ($(join(tuple(m...), ", ")));" + body *= "@inbounds r[i,k] = ($(join(tuple(m...), ", ")));" body = Meta.parse(string("begin ",body," end")) return Expr(:block, body ,:(return k+1)) end @generated function _comp_wize_set_gradient!( - v::AbstractVector{G},s,k,::Type{V}) where {G,V<:AbstractSymTensorValue{D}} where D + r::AbstractMatrix{G},i,s,k,::Type{V}) where {G,V<:AbstractSymTensorValue{D}} where D @notimplemented end @@ -192,7 +180,6 @@ end function _hessian_nd!( b::CompWiseTensorPolyBasis{D,V,K,PT,L}, x, r::AbstractMatrix{H}, i, - v::AbstractVector{H}, c::AbstractMatrix{T}, g::AbstractMatrix{T}, h::AbstractMatrix{T}, @@ -231,14 +218,9 @@ function _hessian_nd!( end end - k = _comp_wize_set_gradient!(v,s,k,l,V) + k = _comp_wize_set_gradient!(r,i,s,k,l,V) end end - - #r[i] = v - @inbounds for j in 1:length(b) - r[i,j] = v[j] - end end diff --git a/src/Polynomials/ModalC0Bases.jl b/src/Polynomials/ModalC0Bases.jl index f905261f6..2456c64f3 100644 --- a/src/Polynomials/ModalC0Bases.jl +++ b/src/Polynomials/ModalC0Bases.jl @@ -149,7 +149,6 @@ end function _evaluate_nd!( basis::ModalC0Basis{D,V,T,K}, x, r::AbstractMatrix{V}, i, - v::AbstractVector{V}, c::AbstractMatrix{T}) where {D,V,T,K} terms = basis.terms @@ -160,10 +159,10 @@ function _evaluate_nd!( k = 1 l = length(terms) - for (i,ci) in enumerate(terms) + for (n,ci) in enumerate(terms) for d in 1:D - _evaluate_1d_mc0!(c,x,a[i],b[i],orders[d],d) + _evaluate_1d_mc0!(c,x,a[n],b[n],orders[d],d) end s = one(T) @@ -171,34 +170,28 @@ function _evaluate_nd!( @inbounds s *= c[d,ci[d]] end - k = _set_value_mc0!(v,s,k,l) - end - - #r[i] = v - @inbounds for j in 1:length(basis) - r[i,j] = v[j] + k = _set_value_mc0!(r,i,s,k,l) end end -@inline function _set_value_mc0!(v::AbstractVector{V},s::T,k,l) where {V,T} +@inline function _set_value_mc0!(r::AbstractMatrix{V},i,s::T,k,l) where {V,T} ncomp = num_indep_components(V) z = zero(T) for j in 1:ncomp m = k+l*(j-1) - @inbounds v[m] = ntuple(i -> ifelse(i == j, s, z),Val(ncomp)) + @inbounds r[i,m] = ntuple(p -> ifelse(p == j, s, z),Val(ncomp)) end k+1 end -@inline function _set_value_mc0!(v::AbstractVector{<:Real},s,k,l) - @inbounds v[k] = s +@inline function _set_value_mc0!(r::AbstractMatrix{<:Real},i,s,k,l) + @inbounds r[i,k] = s k+1 end function _gradient_nd!( basis::ModalC0Basis{D,V,T,K}, x, r::AbstractMatrix{G}, i, - v::AbstractVector{G}, c::AbstractMatrix{T}, g::AbstractMatrix{T}, s::MVector{D,T}) where {D,V,T,K,G} @@ -211,15 +204,15 @@ function _gradient_nd!( k = 1 l = length(terms) - for (i,ci) in enumerate(terms) + for (n,ci) in enumerate(terms) for d in 1: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) + _evaluate_1d_mc0!(c,x,a[n],b[n],orders[d],d) + _gradient_1d_mc0!(g,x,a[n],b[n],orders[d],d) end - for i in eachindex(s) - @inbounds s[i] = one(T) + for j in eachindex(s) + @inbounds s[j] = one(T) end for q in 1:D for d in 1:D @@ -231,19 +224,14 @@ function _gradient_nd!( end end - k = _set_gradient_mc0!(v,s,k,l,V) - end - - #r[i] = v - @inbounds for j in 1:length(basis) - r[i,j] = v[j] + k = _set_gradient_mc0!(r,i,s,k,l,V) end end @inline function _set_gradient_mc0!( - v::AbstractVector{G},s,k,l,::Type{<:Real}) where G + r::AbstractMatrix{G},i,s,k,l,::Type{<:Real}) where G - @inbounds v[k] = s + @inbounds r[i,k] = s k+1 end @@ -251,7 +239,7 @@ end # if the code is optimized for symmetric tensor V valued FESpaces # (if gradient_type(V) returned a symmetric higher order tensor type G) @inline @generated function _set_gradient_mc0!( - v::AbstractVector{G},s,k,l,::Type{V}) where {V,G} + r::AbstractMatrix{G},i1,s,k,l,::Type{V}) where {V,G} # Git blame me for readable non-generated version @notimplementedif num_indep_components(G) != num_components(G) "Not implemented for symmetric Jacobian or Hessian" @@ -275,7 +263,7 @@ end m[ci,j] = "s$id" end body *= "i = k + l*($ij-1);" - body *= "@inbounds v[i] = ($(join(tuple(m...), ", ")));" + body *= "@inbounds r[i1,i] = ($(join(tuple(m...), ", ")));" end body = Meta.parse(string("begin ",body," end")) @@ -285,7 +273,6 @@ end function _hessian_nd!( basis::ModalC0Basis{D,V,T,K}, x, r::AbstractMatrix{G}, i, - v::AbstractVector{G}, c::AbstractMatrix{T}, g::AbstractMatrix{T}, h::AbstractMatrix{T}, @@ -299,16 +286,16 @@ function _hessian_nd!( k = 1 l = length(terms) - for (i,ci) in enumerate(terms) + for (n,ci) in enumerate(terms) for d in 1: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) + _evaluate_1d_mc0!(c,x,a[n],b[n],orders[d],d) + _gradient_1d_mc0!(g,x,a[n],b[n],orders[d],d) + _hessian_1d_mc0!(h,x,a[n],b[n],orders[d],d) end - for i in eachindex(s) - @inbounds s[i] = one(T) + for j in eachindex(s) + @inbounds s[j] = one(T) end for r in 1:D for q in 1:D @@ -324,13 +311,8 @@ function _hessian_nd!( end end - k = _set_gradient_mc0!(v,s,k,l,V) - - end + k = _set_gradient_mc0!(r,i,s,k,l,V) - #r[i] = v - @inbounds for j in 1:length(basis) - r[i,j] = v[j] end end @@ -347,44 +329,44 @@ Robust high-order unfitted finite elements by interpolation-based discrete exten Computers & Mathematics with Applications, https://doi.org/10.1016/j.camwa.2022.09.027 """ -function _evaluate_1d_mc0!(v::AbstractMatrix{T},x,a,b,order,d) where T +function _evaluate_1d_mc0!(c::AbstractMatrix{T},x,a,b,order,d) where T @assert order > 0 n = order + 1 o = one(T) - @inbounds v[d,1] = o - x[d] - @inbounds v[d,2] = x[d] + @inbounds c[d,1] = o - x[d] + @inbounds c[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) + @inbounds c[d,i] = -sqrt(2*i-3)*c[d,1]*c[d,2]*jacobi(ξ,i-3,1,1)/(i-2) end end end -function _gradient_1d_mc0!(v::AbstractMatrix{T},x,a,b,order,d) where T +function _gradient_1d_mc0!(g::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 + @inbounds g[d,1] = -z + @inbounds g[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) + @inbounds g[d,i] = -sqrt(2*i-3)*(g[d,1]*v2*j+v1*g[d,2]*j+v1*v2*(2/(b[d]-a[d]))*dj)/(i-2) end end end -function _hessian_1d_mc0!(v::AbstractMatrix{T},x,a,b,order,d) where T +function _hessian_1d_mc0!(h::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 + @inbounds h[d,1] = y + @inbounds h[d,2] = y if n > 2 ξ = ( 2*x[d] - ( a[d] + b[d] ) ) / ( b[d] - a[d] ) v1 = z - x[d] @@ -394,7 +376,7 @@ function _hessian_1d_mc0!(v::AbstractMatrix{T},x,a,b,order,d) where T 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) + @inbounds h[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 diff --git a/src/Polynomials/NonTensorRTPolyBases.jl b/src/Polynomials/NonTensorRTPolyBases.jl index bedc307bc..e2d8557e5 100644 --- a/src/Polynomials/NonTensorRTPolyBases.jl +++ b/src/Polynomials/NonTensorRTPolyBases.jl @@ -64,7 +64,6 @@ Base.IndexStyle(::NonTensorRTPolyBasis) = IndexLinear() function _evaluate_nd!( b::NonTensorRTPolyBasis{D,V,K,PT}, x, r::AbstractMatrix{V}, i, - v::AbstractVector{V}, c::AbstractMatrix{T}) where {D,V,K,PT,T} pterms = b.pterms @@ -87,7 +86,7 @@ function _evaluate_nd!( s *= c[d,ci[d]] end - k = _comp_wize_set_value!(v,s,k,l) + k = _comp_wize_set_value!(r,i,s,k,l) end end @@ -106,21 +105,16 @@ function _evaluate_nd!( m[l] = s end - v[k] = m + r[i,k] = m k += 1 end - end - #r[i] = v - @inbounds for j in 1:length(b) - r[i,j] = v[j] end end function _gradient_nd!( b::NonTensorRTPolyBasis{D,V,K,PT}, x, r::AbstractMatrix{G}, i, - v::AbstractVector{G}, c::AbstractMatrix{T}, g::AbstractMatrix{T}, s::MVector{D,T}) where {D,V,K,PT,G,T} @@ -154,7 +148,7 @@ function _gradient_nd!( end end - k = _comp_wize_set_gradient!(v,s,k,Val(l),V) + k = _comp_wize_set_gradient!(r,i,s,k,Val(l),V) end end @@ -187,14 +181,10 @@ function _gradient_nd!( m[i,l] = s[i] end end - v[k] = m + r[i,k] = m k += 1 end - end - #r[i] = v - @inbounds for j in 1:length(b) - r[i,j] = v[j] end end diff --git a/src/Polynomials/PolynomialInterfaces.jl b/src/Polynomials/PolynomialInterfaces.jl index 3f3e2433b..913dca47a 100644 --- a/src/Polynomials/PolynomialInterfaces.jl +++ b/src/Polynomials/PolynomialInterfaces.jl @@ -79,12 +79,10 @@ function _return_cache( ndof_1d = get_order(f) + 1 # Cache for the returned array r = CachedArray(zeros(G,(np,ndof))) - # Cache for basis functions at one point x[i] - v = CachedArray(zeros(G,(ndof,))) # Cache for the 1D basis function values in each dimension (to be # tensor-producted), and of their N_deriv'th 1D derivatives t = ntuple( _ -> CachedArray(zeros(T,(D,ndof_1d ))), Val(N_deriv+1)) - (r, v, t...) + (r, t...) end function return_cache(f::PolynomialBasis{D,V}, x::AbstractVector{<:Point}) where {D,V} @@ -108,16 +106,15 @@ function evaluate!(cache, f::PolynomialBasis{D,V,K,PT}, x::AbstractVector{<:Point}) where {D,V,K,PT} - r, v, c = cache + r, c = cache np = length(x) ndof = length(f) ndof_1d = get_order(f) + 1 # K+1 setsize!(r,(np,ndof)) - setsize!(v,(ndof,)) setsize!(c,(D,ndof_1d)) for i in 1:np @inbounds xi = x[i] - _evaluate_nd!(f,xi,r,i,v,c) + _evaluate_nd!(f,xi,r,i,c) end r.array end @@ -127,18 +124,17 @@ function evaluate!(cache, x::AbstractVector{<:Point}) where {D,V,K,PT} f = fg.fa - r, v, c, g = cache + r, c, g = cache s = zero(Mutable(VectorValue{D,eltype(V)})) np = length(x) ndof = length(f) ndof_1d = get_order(f) + 1 # K+1 setsize!(r,(np,ndof)) - setsize!(v,(ndof,)) setsize!(c,(D,ndof_1d)) setsize!(g,(D,ndof_1d)) for i in 1:np @inbounds xi = x[i] - _gradient_nd!(f,xi,r,i,v,c,g,s) + _gradient_nd!(f,xi,r,i,c,g,s) end r.array end @@ -148,19 +144,18 @@ function evaluate!(cache, x::AbstractVector{<:Point}) where {D,V,K,PT} f = fg.fa - r, v, c, g, h = cache + r, c, g, h = cache s = zero(Mutable(TensorValue{D,D,eltype(V)})) np = length(x) ndof = length(f) ndof_1d = get_order(f) + 1 # K+1 setsize!(r,(np,ndof)) - setsize!(v,(ndof,)) setsize!(c,(D,ndof_1d)) setsize!(g,(D,ndof_1d)) setsize!(h,(D,ndof_1d)) for i in 1:np @inbounds xi = x[i] - _hessian_nd!(f,xi,r,i,v,c,g,h,s) + _hessian_nd!(f,xi,r,i,c,g,h,s) end r.array end @@ -171,28 +166,25 @@ end ############################### """ - _evaluate_nd!(b,xi,r,i,v,c) + _evaluate_nd!(b,xi,r,i,c) Compute and assign: r[i] = b(xi) = (b₁(xi), ..., bₙ(xi)) where n = length(`b`) (cardinal of the basis), that is the function computes the basis polynomials at xi and setting the result in the `i`th row of `r`. - -`v` a mutable cache for of `b(xi)` and `c` is a mutable `D`×`K` cache. """ function _evaluate_nd!( b::PolynomialBasis{D,V,K}, x, r::AbstractMatrix{V}, i, - v::AbstractVector{V}, c::AbstractMatrix{T}) where {D,V,K,T} @abstractmethod end """ - _gradient_nd!(b,xi,r,i,v,c,g,s) + _gradient_nd!(b,xi,r,i,c,g,s) Compute and assign: `r`[`i`] = ∇`b`(`xi`) = (∇`b`₁(`xi`), ..., ∇`b`ₙ(`xi`)) @@ -207,7 +199,6 @@ function _gradient_nd!( x, r::AbstractMatrix{G}, i, - v::AbstractVector{G}, c::AbstractMatrix{T}, g::AbstractMatrix{T}, s::MVector{D,T}) where {D,V,K,G,T} @@ -217,7 +208,7 @@ end """ - _hessian_nd!(b,xi,r,i,v,c,g,h,s) + _hessian_nd!(b,xi,r,i,c,g,h,s) Compute and assign: `r`[`i`] = H`b`(`xi`) = (H`b`₁(`xi`), ..., H`b`ₙ(`xi`)) @@ -232,7 +223,6 @@ function _hessian_nd!( x, r::AbstractMatrix{H}, i, - v::AbstractVector{H}, c::AbstractMatrix{T}, g::AbstractMatrix{T}, h::AbstractMatrix{T}, @@ -294,15 +284,15 @@ end ############################### """ - _evaluate_1d!(PT::Type{<:Polynomial},::Val{K},v,x,d) + _evaluate_1d!(PT::Type{<:Polynomial},::Val{K},c,x,d) Evaluates in place the 1D basis polynomials of the given type at one nD point `x` along the given coordinate 1 ≤ `d` ≤ nD. -`v` is an AbstractMatrix of size (at least} `d`×(`K`+1), such that the 1 ≤ i ≤ `k`+1 -values are stored in `v[d,i]`. +`c` is an AbstractMatrix of size (at least} `d`×(`K`+1), such that the 1 ≤ i ≤ `k`+1 +values are stored in `c[d,i]`. """ -function _evaluate_1d!(::Type{<:Polynomial},::Val{K},v::AbstractMatrix{T},x,d) where {K,T<:Number} +function _evaluate_1d!(::Type{<:Polynomial},::Val{K},c::AbstractMatrix{T},x,d) where {K,T<:Number} @abstractmethod end @@ -325,11 +315,11 @@ function _hessian_1d!(::Type{<:Polynomial},::Val{K},h::AbstractMatrix{T},x,d) wh end """ - _derivatives_1d!(PT::Type{<:Polynomial}, ::Val{K}, (v,g,...), x, d) + _derivatives_1d!(PT::Type{<:Polynomial}, ::Val{K}, (c,g,...), x, d) Same as calling ``` -_evaluate_1d!(PT, Val(K), v, x d) +_evaluate_1d!(PT, Val(K), c, x d) _gradient_1d!(PT, Val(K), g, x d) ⋮ ``` diff --git a/src/Polynomials/TensorPolynomialBases.jl b/src/Polynomials/TensorPolynomialBases.jl index 87027ba86..14bf49428 100644 --- a/src/Polynomials/TensorPolynomialBases.jl +++ b/src/Polynomials/TensorPolynomialBases.jl @@ -170,7 +170,6 @@ end function _evaluate_nd!( b::TensorPolynomialBasis{D,V,K,PT}, x, r::AbstractMatrix{V}, i, - v::AbstractVector{V}, c::AbstractMatrix{T}) where {D,V,K,PT,T} terms = b.terms @@ -189,46 +188,26 @@ function _evaluate_nd!( @inbounds s *= c[d,ci[d]] end - k = _tensorial_set_value!(v,s,k) - end - - #r[i] = v - @inbounds for j in 1:length(b) - r[i,j] = v[j] + k = _tensorial_set_value!(r,i,s,k) end end """ - _tensorial_set_value!(v::AbstractVector{<:Real},s,k) + _tensorial_set_value!(r::AbstractMatrix{<:Real},i,s,k) -v[k] = s; return k+1 +r[i,k] = s; return k+1 """ -function _tensorial_set_value!(v::AbstractVector{<:Real},s,k) - @inbounds v[k] = s - k+1 +function _tensorial_set_value!(r::AbstractMatrix{<:Real},i,s,k) + #@inbounds r[i][k] = s + @inbounds r[i,k] = s + k+1 end -""" - _tensorial_set_value!(v::AbstractVector{V},s::T,k) - -``` -v[k] = V(s, 0, ..., 0) -v[k+1] = V(0, s, ..., 0) - ⋮ -v[k+N] = V(0, ..., 0, s) -return k+N -``` - -where `N` is the number of independent components of `V<:MultiValue`. - -This means that the basis has the same polynomial space in each component, so it -is tensorial relative to the `V` components. -""" -function _tensorial_set_value!(v::AbstractVector{V},s::T,k) where {V,T} +function _tensorial_set_value!(r::AbstractMatrix{V},i,s::T,k) where {V,T} ncomp = num_indep_components(V) z = zero(T) @inbounds for j in 1:ncomp - v[k] = ntuple(i -> ifelse(i == j, s, z),Val(ncomp)) + r[i,k] = ntuple(i -> ifelse(i == j, s, z),Val(ncomp)) k += 1 end k @@ -237,7 +216,6 @@ end function _gradient_nd!( b::TensorPolynomialBasis{D,V,K,PT}, x, r::AbstractMatrix{G}, i, - v::AbstractVector{G}, c::AbstractMatrix{T}, g::AbstractMatrix{T}, s::MVector{D,T}) where {D,V,K,PT,G,T} @@ -267,25 +245,20 @@ function _gradient_nd!( end end - k = _tensorial_set_gradient!(v,s,k,V) - end - - #r[i] = v - @inbounds for j in 1:length(b) - r[i,j] = v[j] + k = _tensorial_set_gradient!(r,i,s,k,V) end end function _tensorial_set_gradient!( - v::AbstractVector{G},s,k,::Type{<:Real}) where G + r::AbstractMatrix{G},i,s,k,::Type{<:Real}) where G - @inbounds v[k] = s + @inbounds r[i,k] = s k+1 end # TODO comment @generated function _tensorial_set_gradient!( - v::AbstractVector{G},s,k,::Type{V}) where {G,V} + r::AbstractMatrix{G},i,s,k,::Type{V}) where {G,V} # Git blame me for readable non-generated version w = zero(V) @@ -307,7 +280,7 @@ end id = join(Tuple(ci)) m[ci,j] = "s$id" end - body *= "@inbounds v[k] = ($(join(tuple(m...), ", ")));" + body *= "@inbounds r[i,k] = ($(join(tuple(m...), ", ")));" body *= "k = k + 1;" end @@ -323,7 +296,7 @@ end # This is still (independant-)component tensorial as each independent SymTensor # component holds the same (scalar multivariate) polynomial space. @generated function _tensorial_set_gradient!( - v::AbstractVector{G},s,k,::Type{V}) where {G,V<:AbstractSymTensorValue{D}} where D + r::AbstractMatrix{G},i,s,k,::Type{V}) where {G,V<:AbstractSymTensorValue{D}} where D # Git blame me for readable non-generated version T = eltype(s) @@ -351,7 +324,7 @@ end m[i,D,D] = "-s$i" end end - body *= "@inbounds v[k] = ($(join(tuple(m...), ", ")));" + body *= "@inbounds r[i,k] = ($(join(tuple(m...), ", ")));" body *= "k = k + 1;" end end @@ -363,7 +336,6 @@ end function _hessian_nd!( b::TensorPolynomialBasis{D,V,K,PT}, x, r::AbstractMatrix{G}, i, - v::AbstractVector{G}, c::AbstractMatrix{T}, g::AbstractMatrix{T}, h::AbstractMatrix{T}, @@ -385,26 +357,21 @@ function _hessian_nd!( @inbounds s[i] = one(T) end - for r in 1:D + for t in 1:D for q in 1:D for d in 1:D - 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]] + if d != q && d != t + @inbounds s[t,q] *= c[d,ci[d]] + elseif d == q && d ==t + @inbounds s[t,q] *= h[d,ci[d]] else - @inbounds s[r,q] *= g[d,ci[d]] + @inbounds s[t,q] *= g[d,ci[d]] end end end end - k = _tensorial_set_gradient!(v,s,k,V) - end - - #r[i] = v - @inbounds for j in 1:length(b) - r[i,j] = v[j] + k = _tensorial_set_gradient!(r,i,s,k,V) end end From 2a000afe19a200783ec56b73de314a6a06bea51a Mon Sep 17 00:00:00 2001 From: Antoine Marteau Date: Fri, 20 Dec 2024 12:42:44 +1100 Subject: [PATCH 068/105] cleaned PolynomialInterfaces --- src/Polynomials/ModalC0Bases.jl | 1 - src/Polynomials/PolynomialInterfaces.jl | 83 +++++++++++-------------- src/Polynomials/Polynomials.jl | 10 +-- src/ReferenceFEs/ReferenceFEs.jl | 1 + 4 files changed, 41 insertions(+), 54 deletions(-) diff --git a/src/Polynomials/ModalC0Bases.jl b/src/Polynomials/ModalC0Bases.jl index 2456c64f3..43d9bd052 100644 --- a/src/Polynomials/ModalC0Bases.jl +++ b/src/Polynomials/ModalC0Bases.jl @@ -312,7 +312,6 @@ function _hessian_nd!( end k = _set_gradient_mc0!(r,i,s,k,l,V) - end end diff --git a/src/Polynomials/PolynomialInterfaces.jl b/src/Polynomials/PolynomialInterfaces.jl index 913dca47a..f54c0cc04 100644 --- a/src/Polynomials/PolynomialInterfaces.jl +++ b/src/Polynomials/PolynomialInterfaces.jl @@ -102,16 +102,23 @@ function return_cache( _return_cache(f,x,G,Val(N)) end + +function _setsize!(f::PolynomialBasis{D}, np, r, t...) where D + ndof = length(f) + ndof_1d = get_order(f) + 1 + setsize!(r,(np,ndof)) + for c in t + setsize!(c,(D,ndof_1d)) + end +end + function evaluate!(cache, - f::PolynomialBasis{D,V,K,PT}, - x::AbstractVector{<:Point}) where {D,V,K,PT} + f::PolynomialBasis, + x::AbstractVector{<:Point}) r, c = cache np = length(x) - ndof = length(f) - ndof_1d = get_order(f) + 1 # K+1 - setsize!(r,(np,ndof)) - setsize!(c,(D,ndof_1d)) + _setsize!(f,np,r,c) for i in 1:np @inbounds xi = x[i] _evaluate_nd!(f,xi,r,i,c) @@ -120,18 +127,14 @@ function evaluate!(cache, end function evaluate!(cache, - fg::FieldGradientArray{1,<:PolynomialBasis{D,V,K,PT}}, - x::AbstractVector{<:Point}) where {D,V,K,PT} + fg::FieldGradientArray{1,<:PolynomialBasis{D,V}}, + x::AbstractVector{<:Point}) where {D,V} f = fg.fa r, c, g = cache - s = zero(Mutable(VectorValue{D,eltype(V)})) np = length(x) - ndof = length(f) - ndof_1d = get_order(f) + 1 # K+1 - setsize!(r,(np,ndof)) - setsize!(c,(D,ndof_1d)) - setsize!(g,(D,ndof_1d)) + _setsize!(f,np,r,c,g) + s = zero(Mutable(VectorValue{D,eltype(V)})) for i in 1:np @inbounds xi = x[i] _gradient_nd!(f,xi,r,i,c,g,s) @@ -140,19 +143,14 @@ function evaluate!(cache, end function evaluate!(cache, - fg::FieldGradientArray{2,<:PolynomialBasis{D,V,K,PT}}, - x::AbstractVector{<:Point}) where {D,V,K,PT} + fg::FieldGradientArray{2,<:PolynomialBasis{D,V}}, + x::AbstractVector{<:Point}) where {D,V} f = fg.fa r, c, g, h = cache - s = zero(Mutable(TensorValue{D,D,eltype(V)})) np = length(x) - ndof = length(f) - ndof_1d = get_order(f) + 1 # K+1 - setsize!(r,(np,ndof)) - setsize!(c,(D,ndof_1d)) - setsize!(g,(D,ndof_1d)) - setsize!(h,(D,ndof_1d)) + _setsize!(f,np,r,c,g,h) + s = zero(Mutable(TensorValue{D,D,eltype(V)})) for i in 1:np @inbounds xi = x[i] _hessian_nd!(f,xi,r,i,c,g,h,s) @@ -168,17 +166,16 @@ end """ _evaluate_nd!(b,xi,r,i,c) -Compute and assign: r[i] = b(xi) = (b₁(xi), ..., bₙ(xi)) +Compute and assign: `r`[`i`] = `b`(`xi`) = (`b`₁(`xi`), ..., `b`ₙ(`xi`)) where n = length(`b`) (cardinal of the basis), that is the function computes -the basis polynomials at xi and setting the result in the `i`th row of `r`. +the basis polynomials at a single point `xi` and setting the result in the `i`th +row of `r`. """ function _evaluate_nd!( - b::PolynomialBasis{D,V,K}, - x, - r::AbstractMatrix{V}, - i, - c::AbstractMatrix{T}) where {D,V,K,T} + b::PolynomialBasis, xi, + r::AbstractMatrix, i, + c::AbstractMatrix) @abstractmethod end @@ -195,13 +192,11 @@ for gradients of `b`ₖ(`xi`), and - `s` is a mutable length `D` cache for ∇`b`ₖ(`xi`). """ function _gradient_nd!( - b::PolynomialBasis{D,V,K}, - x, - r::AbstractMatrix{G}, - i, - c::AbstractMatrix{T}, - g::AbstractMatrix{T}, - s::MVector{D,T}) where {D,V,K,G,T} + b::PolynomialBasis, xi, + r::AbstractMatrix, i, + c::AbstractMatrix, + g::AbstractMatrix, + s::MVector) @abstractmethod end @@ -219,14 +214,12 @@ for hessian matrices/tensor of `b`ₖ(`xi`), and - `s` is a mutable `D`×`D` cache for H`b`ₖ(`xi`). """ function _hessian_nd!( - b::PolynomialBasis{D,V,K}, - x, - r::AbstractMatrix{H}, - i, - c::AbstractMatrix{T}, - g::AbstractMatrix{T}, - h::AbstractMatrix{T}, - s::MMatrix{D,D,T}) where {D,V,K,H,T} + b::PolynomialBasis, xi, + r::AbstractMatrix, i, + c::AbstractMatrix, + g::AbstractMatrix, + h::AbstractMatrix, + s::MMatrix) @abstractmethod end diff --git a/src/Polynomials/Polynomials.jl b/src/Polynomials/Polynomials.jl index 879df0a16..c64b34978 100644 --- a/src/Polynomials/Polynomials.jl +++ b/src/Polynomials/Polynomials.jl @@ -23,11 +23,12 @@ import Gridap.Arrays: return_type export Polynomial export isHierarchical + export PolynomialBasis +export get_order export TensorPolynomialBasis export get_exponents -export get_order export get_orders export num_terms @@ -38,15 +39,8 @@ export ModalC0Basis export BernsteinBasis export QGradMonomialBasis -export QGradLegendreBasis -export QGradChebyshevBasis - export QCurlGradMonomialBasis -export QCurlGradLegendreBasis -export QCurlGradChebyshevBasis - export PCurlGradMonomialBasis -export PCurlGradLegendreBasis include("PolynomialInterfaces.jl") diff --git a/src/ReferenceFEs/ReferenceFEs.jl b/src/ReferenceFEs/ReferenceFEs.jl index abfc4766d..e662988c5 100644 --- a/src/ReferenceFEs/ReferenceFEs.jl +++ b/src/ReferenceFEs/ReferenceFEs.jl @@ -34,6 +34,7 @@ import Gridap.Fields: evaluate import Gridap.Fields: lazy_map import Gridap.Fields: linear_combination import Gridap.Polynomials: MonomialBasis +import Gridap.Polynomials: QGradLegendreBasis, QCurlGradLegendreBasis import Gridap.Polynomials: get_order import Gridap.Polynomials: get_orders From 6241cc1deff1d98d0cb56ee4f9809407be86575e Mon Sep 17 00:00:00 2001 From: Antoine Marteau Date: Fri, 20 Dec 2024 13:16:51 +1100 Subject: [PATCH 069/105] add again LegendreBasesTests (former JacobiBasesTests) --- test/PolynomialsTests/LegendreBasesTests.jl | 63 +++++++++++++++++++++ 1 file changed, 63 insertions(+) create mode 100644 test/PolynomialsTests/LegendreBasesTests.jl diff --git a/test/PolynomialsTests/LegendreBasesTests.jl b/test/PolynomialsTests/LegendreBasesTests.jl new file mode 100644 index 000000000..7072855af --- /dev/null +++ b/test/PolynomialsTests/LegendreBasesTests.jl @@ -0,0 +1,63 @@ +module LegendreBasisTests + +using Test +using Gridap.TensorValues +using Gridap.Fields +using Gridap.Fields: Broadcasting +using Gridap.Polynomials + +# 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 = 3 +b1 = LegendreBasis(Val(1),V,order) +∇b1 = Broadcasting(∇)(b1) +∇∇b1 = Broadcasting(∇)(∇b1) + +@test evaluate(b1,[x1,x2,x3,]) ≈ [ 1.0 -1.7320508075688772 2.23606797749979 -2.6457513110645907; + 1.0 0.0 -1.118033988749895 -0.0; + 1.0 1.7320508075688772 2.23606797749979 2.6457513110645907 ] +@test evaluate(∇b1,[x1,x2,x3,]) ≈ G[ (0.0,) (3.4641016151377544,) (-13.416407864998739,) (31.74901573277509,); + (0.0,) (3.4641016151377544,) (0.0,) (-7.937253933193772,); + (0.0,) (3.4641016151377544,) (13.416407864998739,) (31.74901573277509,) ] +@test evaluate(∇∇b1,[x1,x2,x3,]) ≈ H[ (0.0,) (0.0,) (13.416407864998739,) (-79.37253933193772,); + (0.0,) (0.0,) (13.416407864998739,) (0.0,); + (0.0,) (0.0,) (13.416407864998739,) (79.37253933193772,) ] + +x1 = Point(0.0,0.0) +x2 = Point(0.5,0.5) +x3 = Point(1.0,1.0) +b2 = LegendreBasis(Val(2),V,order) +∇b2 = Broadcasting(∇)(b2) +∇∇b2 = Broadcasting(∇)(∇b2) + +G = gradient_type(V,x1) +H = gradient_type(G,x1) + +@test evaluate(b2,[x1,x2,x3,]) ≈ [ 1.0 -1.7320508075688772 2.23606797749979 -2.6457513110645907 #= + =# -1.7320508075688772 2.9999999999999996 -3.872983346207417 #= + =# 4.58257569495584 2.23606797749979 -3.872983346207417 #= + =# 5.000000000000001 -5.916079783099617 -2.6457513110645907 #= + =# 4.58257569495584 -5.916079783099617 7.000000000000001; + 1.0 0.0 -1.118033988749895 -0.0 0.0 0.0 -0.0 -0.0 #= + =# -1.118033988749895 -0.0 1.2500000000000002 0.0 -0.0 -0.0 0.0 0.0; + 1.0 1.7320508075688772 2.23606797749979 2.6457513110645907 #= + =# 1.7320508075688772 2.9999999999999996 3.872983346207417 #= + =# 4.58257569495584 2.23606797749979 3.872983346207417 #= + =# 5.000000000000001 5.916079783099617 2.6457513110645907 #= + =# 4.58257569495584 5.916079783099617 7.000000000000001 ] +@test evaluate(∇b2,[x1,x2,x3,])[:,10] ≈ G[ (7.745966692414834, 23.2379000772445); + (-3.872983346207417, 0.0); + (7.745966692414834, 23.2379000772445) ] +@test evaluate(∇∇b2,[x1,x2,x3,])[:,10] ≈ H[ (0.0, -46.475800154489, -46.475800154489, -23.2379000772445); + (-0.0, 0.0, 0.0, 0.0); + (0.0, 46.475800154489, 46.475800154489, 23.2379000772445) ] + +end # module From 0c0562b8d11ad1b1e77dc1c9ca33da897f333092 Mon Sep 17 00:00:00 2001 From: Antoine Marteau Date: Fri, 20 Dec 2024 16:49:30 +1100 Subject: [PATCH 070/105] further Polynomials doc and code cleaning --- src/Polynomials/BernsteinBases.jl | 10 +- src/Polynomials/ChebyshevBases.jl | 30 +++-- src/Polynomials/CompWiseTensorPolyBases.jl | 57 ++++++--- src/Polynomials/LegendreBases.jl | 15 ++- src/Polynomials/ModalC0Bases.jl | 21 ++-- src/Polynomials/MonomialBases.jl | 12 +- src/Polynomials/NedelecPrebasisOnSimplex.jl | 133 +++++++++++--------- src/Polynomials/NonTensorRTPolyBases.jl | 40 +++--- src/Polynomials/PolynomialInterfaces.jl | 37 +++--- src/Polynomials/TensorPolynomialBases.jl | 10 +- 10 files changed, 208 insertions(+), 157 deletions(-) diff --git a/src/Polynomials/BernsteinBases.jl b/src/Polynomials/BernsteinBases.jl index 2681f4379..fb83de65f 100644 --- a/src/Polynomials/BernsteinBases.jl +++ b/src/Polynomials/BernsteinBases.jl @@ -1,7 +1,7 @@ """ Bernstein <: Polynomial -Type representing Bernstein polynomials +Type representing Bernstein polynomials. """ struct Bernstein <: Polynomial end @@ -10,16 +10,16 @@ isHierarchical(::Type{Bernstein}) = false """ BernsteinBasis{D,V,K} = TensorPolynomialBasis{D,V,K,Bernstein} -Multivariate scalar' or `Multivalue`'d monomial basis, see [`TensorPolynomialBasis`](@ref) +Alias for Bernstein multivariate scalar' or `Multivalue`'d basis, see [`TensorPolynomialBasis`](@ref). """ const BernsteinBasis{D,V,K} = TensorPolynomialBasis{D,V,K,Bernstein} """ BernsteinBasis(::Val{D}, ::Type{V}, order::Int, terms::Vector) - BernsteinBasis(::Val{D}, ::Type{V}, orders::Tuple [, filter::Function]) BernsteinBasis(::Val{D}, ::Type{V}, order::Int [, filter::Function]) + BernsteinBasis(::Val{D}, ::Type{V}, orders::Tuple [, filter::Function]) -Convenience constructors of [`BernsteinBasis`](@ref). +High level constructors of [`BernsteinBasis`](@ref). """ BernsteinBasis(args...) = TensorPolynomialBasis(Bernstein, args...) @@ -34,7 +34,7 @@ QCurlGradBernsteinBasis(args...) = QCurlGradBasis(Bernstein, args...) """ binoms(::Val{K}) -Returns the tuple of binomials ( C₍ₖ₀₎, C₍ₖ₁₎, ..., C₍ₖₖ₎ ) +Returns the tuple of binomials ( C₍ₖ₀₎, C₍ₖ₁₎, ..., C₍ₖₖ₎ ). """ binoms(::Val{K}) where K = ntuple( i -> binomial(K,i-1), Val(K+1)) diff --git a/src/Polynomials/ChebyshevBases.jl b/src/Polynomials/ChebyshevBases.jl index c397b5328..ab2b79759 100644 --- a/src/Polynomials/ChebyshevBases.jl +++ b/src/Polynomials/ChebyshevBases.jl @@ -1,8 +1,8 @@ """ Chebyshev{kind} <: Polynomial -Type representing Chebyshev polynomials of first and second kind -where `kind` is either `:T` or `:U` for first and second kind Chebyshev polynomials respectively. +Type representing Chebyshev polynomials of first and second kind where `kind` is +either `:T` or `:U` for first and second kind Chebyshev polynomials respectively. """ struct Chebyshev{kind} <: Polynomial end @@ -11,16 +11,16 @@ isHierarchical(::Type{<:Chebyshev}) = true """ ChebyshevBasis{D,V,kind,K} = TensorPolynomialBasis{D,V,K,Chebyshev{kind}} -Multivariate scalar' or `Multivalue`'d Chebyshev basis, see [`TensorPolynomialBasis`](@ref) +Alias for Chebyshev multivariate scalar' or `Multivalue`'d basis, see [`TensorPolynomialBasis`](@ref). """ const ChebyshevBasis{D,V,kind,K} = TensorPolynomialBasis{D,V,K,Chebyshev{kind}} """ ChebyshevBasis(::Val{D}, ::Type{V}, order::Int, terms::Vector; kind=:T) - ChebyshevBasis(::Val{D}, ::Type{V}, orders::Tuple [, filter::Function; kind=:T]) ChebyshevBasis(::Val{D}, ::Type{V}, order::Int [, filter::Function; kind=:T]) + ChebyshevBasis(::Val{D}, ::Type{V}, orders::Tuple [, filter::Function; kind=:T]) -Convenience constructors of [`ChebyshevBasis`](@ref). +High level constructors of [`ChebyshevBasis`](@ref). """ ChebyshevBasis(args...; kind=:T) = TensorPolynomialBasis(Chebyshev{kind}, args...) @@ -35,28 +35,28 @@ PCurlGradChebyshevBasis(args...; kind=:T) = PCurlGradBasis(Chebyshev{kind}, args # 1D evaluation implementation function _evaluate_1d!( - ::Type{Chebyshev{:T}},::Val{0},v::AbstractMatrix{T},x,d) where T<:Number + ::Type{<:Chebyshev},::Val{0},c::AbstractMatrix{T},x,d) where T<:Number - @inbounds v[d,1] = one(T) + @inbounds c[d,1] = one(T) end function _evaluate_1d!( - ::Type{Chebyshev{:T}},::Val{K},v::AbstractMatrix{T},x,d) where {K,T<:Number} + ::Type{Chebyshev{:T}},::Val{K},c::AbstractMatrix{T},x,d) where {K,T<:Number} n = K + 1 # n > 1 ξ = (2*x[d] - 1) # ξ ∈ [-1,1] ξ2 = 2*ξ - @inbounds v[d,1] = one(T) - @inbounds v[d,2] = ξ + @inbounds c[d,1] = one(T) + @inbounds c[d,2] = ξ for i in 3:n - @inbounds v[d,i] = v[d,i-1]*ξ2 - v[d,i-2] + @inbounds c[d,i] = c[d,i-1]*ξ2 - c[d,i-2] end end function _gradient_1d!( - ::Type{Chebyshev{:T}},::Val{0},g::AbstractMatrix{T},x,d) where T<:Number + ::Type{<:Chebyshev},::Val{0},g::AbstractMatrix{T},x,d) where T<:Number @inbounds g[d,1] = zero(T) end @@ -82,8 +82,12 @@ end function _hessian_1d!( - ::Type{Chebyshev{:T}},::Val{K},g::AbstractMatrix{T},x,d) where {K,T<:Number} + ::Type{Chebyshev{:T}},::Val{K},h::AbstractMatrix{T},x,d) where {K,T<:Number} @notimplemented end + +_evaluate_1d!(::Type{Chebyshev{:U}},::Val{K},c::AbstractMatrix{T},x,d) where {K,T<:Number} = @notimplemented +_gradient_1d!(::Type{Chebyshev{:U}},::Val{K},g::AbstractMatrix{T},x,d) where {K,T<:Number} = @notimplemented +_hessian_1d!( ::Type{Chebyshev{:U}},::Val{K},h::AbstractMatrix{T},x,d) where {K,T<:Number} = @notimplemented diff --git a/src/Polynomials/CompWiseTensorPolyBases.jl b/src/Polynomials/CompWiseTensorPolyBases.jl index 45c49ed2e..b7a43a485 100644 --- a/src/Polynomials/CompWiseTensorPolyBases.jl +++ b/src/Polynomials/CompWiseTensorPolyBases.jl @@ -1,21 +1,24 @@ """ CompWiseTensorPolyBasis{D,V,K,PT,L} <: PolynomialBasis{D,V,K,PT} -"Polynomial basis of component wise tensor product spaces" +"Polynomial basis of component wise tensor product polynomial spaces" -Polynomial basis for a multivariate `MultiValue`'d polynomial space -`V`(𝕊¹, 𝕊², ..., 𝕊ᴸ) with `L`>1, where the scalar multivariate spaces 𝕊ˡ -(for 1 ≤ l ≤ `L`) of each (independent) component of `V` is the tensor product -of 1D ℙ¹ spaces of order oₗₙ for 1 ≤ n ≤ `D`, that is: +Polynomial basis for a multivariate `MultiValue`'d polynomial space: -𝕊¹ = ℙ¹ₒ(1,1) ⊗ … ⊗ ℙ¹ₒ(1,`D`)\\ +`V`(𝕊¹, 𝕊², ..., 𝕊ᴸ) + +with `L`>1, where the scalar multivariate spaces 𝕊ˡ (for 1 ≤ l ≤ `L`) of each +(independent) component of `V` is the tensor product of 1D ℙ spaces of order +α(l,n) for 1 ≤ n ≤ `D`, that is: + +𝕊¹ = ℙα(1,1) ⊗ … ⊗ ℙα(1,`D`)\\ ⋮\\ -𝕊ˡ = ⊗ₙ ℙ¹ₒ₍ₗ,ₙ₎\\ +𝕊ˡ = ⊗ₙ ℙα(l,n)\\ ⋮\\ -𝕊ᴸ = ℙ¹ₒ(`L`,1) ⊗ … ⊗ ℙ¹ₒ(`L`,`D`) +𝕊ᴸ = ℙα(`L`,1) ⊗ … ⊗ ℙα(`L`,`D`) -The `L`×`D` matrix of orders o is given in the constructor, and `K` is the -maximum of o. +The `L`×`D` matrix of orders α is given in the constructor, and `K` is the +maximum of α. Any 1D polynomial basis `PT` is usable. """ struct CompWiseTensorPolyBasis{D,V,K,PT,L} <: PolynomialBasis{D,V,K,PT} orders::SMatrix{L,D,Int} @@ -231,14 +234,21 @@ end """ QGradBasis(::Type{PT}, ::Val{D}, ::Type{T}, order::Int) :: PolynomialBasis -Return a basis of (ℚ\\_order)ᴰ ⊕ x × ( ℚ\\_order \\ ℚ\\_{order-1}) )ᴰ, the -polynomial space for Nedelec elements on `D`-dimensonal cubes with scalar type `T`. +Return a basis of ℕ𝔻ᴰₙ(□) = (ℚₙ)ᴰ ⊕ x × (ℚₙ \\ ℚₙ₋₁)ᴰ with n=`order`, the +polynomial space for Nedelec elements on `D`-dimensional cubes with scalar type `T`. -The `order` argument has the following meaning: the curl of the functions in -this basis is in the ℚ space of degree `order`. +The `order`=n argument has the following meaning: the curl of the functions in +this basis is in ℚₙ. `PT<:Polynomial` is the choice of scalar 1D polynomial basis. +# Example: + +```jldoctest +# a basis for Nedelec on hexahedra with divergence in ℚ₂ +b = PCurlGradBasis(Monomial, Val(3), Float64, 2) +``` + For more details, see [`CompWiseTensorPolyBasis`](@ref), as `QGradBasis` returns an instance of `CompWiseTensorPolyBasis{D,VectorValue{D,T},order+1,PT,D}`. """ @@ -266,16 +276,25 @@ end """ QCurlGradBasis(::Type{PT}, ::Val{D}, ::Type{T}, order::Int) :: PolynomialBasis -Return a basis of (ℚ\\_order)ᴰ ⊕ x (ℚ\\_order \\ ℚ\\_{order-1}), the polynomial space -for Raviart-Thomas elements on `D`-dimensonal cubes with scalar type `T`. +Return a basis of ℝ𝕋ᴰₙ(□) = (ℚₙ)ᴰ ⊕ x (ℚₙ \\ ℚₙ₋₁) with n=`order`, the polynomial +space for Raviart-Thomas elements on `D`-dimensional cubes with scalar type `T`. -The `order` argument has the following meaning: the divergence of the functions -in this basis is in the ℚ space of degree `order`. +The `order`=n argument has the following meaning: the divergence of the functions +in this basis is in ℚₙ. `PT<:Polynomial` is the choice of scalar 1D polynomial basis. +# Example: + +```jldoctest +# a basis for Raviart-Thomas on rectangles with divergence in ℚ₃ +b = QCurlGradBasis(Bernstein, Val(2), Float64, 3) +``` + For more details, see [`CompWiseTensorPolyBasis`](@ref), as `QCurlGradBasis` returns -an instance of `CompWiseTensorPolyBasis{D,VectorValue{D,T},order+1,PT,D}`. +an instance of\\ +`CompWiseTensorPolyBasis{D, VectorValue{D,T}, order+1, PT, D}` for `D`>1, or\\ +`TensorPolynomialBasis{1, VectorValue{1,T}, order+1, PT, D}` for `D`=1. """ function QCurlGradBasis(::Type{PT},::Val{D},::Type{T},order::Int) where {PT,D,T} @check T<:Real "T needs to be <:Real since represents the type of the components of the vector value" diff --git a/src/Polynomials/LegendreBases.jl b/src/Polynomials/LegendreBases.jl index 2204145b7..3aa74b864 100644 --- a/src/Polynomials/LegendreBases.jl +++ b/src/Polynomials/LegendreBases.jl @@ -1,7 +1,7 @@ """ Legendre <: Polynomial -Type representing the Legendre polynomials +Type representing the Legendre polynomials. """ struct Legendre <: Polynomial end @@ -10,16 +10,16 @@ isHierarchical(::Type{Legendre}) = true """ LegendreBasis{D,V,K} = TensorPolynomialBasis{D,V,K,Legendre} -Multivariate scalar' or `Multivalue`'d Legendre basis, see [`TensorPolynomialBasis`](@ref) +Alias for Legendre multivariate scalar' or `Multivalue`'d basis, see [`TensorPolynomialBasis`](@ref). """ const LegendreBasis{D,V,K} = TensorPolynomialBasis{D,V,K,Legendre} """ LegendreBasis(::Val{D}, ::Type{V}, order::Int, terms::Vector) - LegendreBasis(::Val{D}, ::Type{V}, orders::Tuple [, filter::Function]) LegendreBasis(::Val{D}, ::Type{V}, order::Int [, filter::Function]) + LegendreBasis(::Val{D}, ::Type{V}, orders::Tuple [, filter::Function]) -Convenience constructors of [`LegendreBasis`](@ref). +High level constructors of [`LegendreBasis`](@ref). """ LegendreBasis(args...) = TensorPolynomialBasis(Legendre, args...) @@ -33,10 +33,9 @@ PCurlGradLegendreBasis(args...) = PCurlGradBasis(Legendre, args...) # TODO optimize evaluation by using the iterative formula explicitely -function _evaluate_1d!(::Type{Legendre},::Val{K},v::AbstractMatrix{T},x,d) where {K,T<:Number} +function _evaluate_1d!(::Type{Legendre},::Val{K},c::AbstractMatrix{T},x,d) where {K,T<:Number} n = K + 1 - o = one(T) - @inbounds v[d,1] = o + @inbounds c[d,1] = one(T) if n > 1 ξ = ( 2*x[d] - 1 ) for i in 2:n @@ -44,7 +43,7 @@ function _evaluate_1d!(::Type{Legendre},::Val{K},v::AbstractMatrix{T},x,d) where # product on ξ∈[0,1], indeed: # ∫[0,1] Pn(2ξ-1)^2 dξ = 1/2 ∫[-1,1] Pn(t)^2 dt = 1/(2n+1) # C.f. Eq. (1.25) in Section 1.1.5 in Ern & Guermond book (2013). - @inbounds v[d,i] = sqrt(2*i-1)*jacobi(ξ,i-1,0,0) + @inbounds c[d,i] = sqrt(2*i-1)*jacobi(ξ,i-1,0,0) end end end diff --git a/src/Polynomials/ModalC0Bases.jl b/src/Polynomials/ModalC0Bases.jl index 43d9bd052..798f3937e 100644 --- a/src/Polynomials/ModalC0Bases.jl +++ b/src/Polynomials/ModalC0Bases.jl @@ -1,3 +1,10 @@ +""" + ModalC0 <: Polynomial + +Type representing ModalC0 polynomial. + +Reference: Eq. (17) in https://doi.org/10.1016/j.camwa.2022.09.027 +""" struct ModalC0 <: Polynomial end struct ModalC0Basis{D,V,T,K} <: PolynomialBasis{D,V,K,ModalC0} @@ -320,14 +327,12 @@ end # 1D evaluations implementation # ################################# -""" -Reference: equation (17) in - -Badia, S.; Neiva, E. & Verdugo, F.; (2022); -Robust high-order unfitted finite elements by interpolation-based discrete extension, -Computers & Mathematics with Applications, -https://doi.org/10.1016/j.camwa.2022.09.027 -""" +# Reference: equation (17) in +# +# Badia, S.; Neiva, E. & Verdugo, F.; (2022); +# Robust high-order unfitted finite elements by interpolation-based discrete extension, +# Computers & Mathematics with Applications, +# https://doi.org/10.1016/j.camwa.2022.09.027 function _evaluate_1d_mc0!(c::AbstractMatrix{T},x,a,b,order,d) where T @assert order > 0 n = order + 1 diff --git a/src/Polynomials/MonomialBases.jl b/src/Polynomials/MonomialBases.jl index a0d7f63d5..5f0fd033b 100644 --- a/src/Polynomials/MonomialBases.jl +++ b/src/Polynomials/MonomialBases.jl @@ -1,7 +1,7 @@ """ Monomial <: Polynomial -Type representing the monomial polynomials +Type representing the monomial polynomials. """ struct Monomial <: Polynomial end @@ -10,16 +10,16 @@ isHierarchical(::Type{Monomial}) = true """ MonomialBasis{D,V,K} = TensorPolynomialBasis{D,V,K,Monomial} -Multivariate scalar' or `Multivalue`'d monomial basis, see [`TensorPolynomialBasis`](@ref) +Alias for monomial Multivariate scalar' or `Multivalue`'d basis, see [`TensorPolynomialBasis`](@ref). """ const MonomialBasis{D,V,K} = TensorPolynomialBasis{D,V,K,Monomial} """ MonomialBasis(::Val{D}, ::Type{V}, order::Int, terms::Vector) - MonomialBasis(::Val{D}, ::Type{V}, orders::Tuple [, filter::Function]) MonomialBasis(::Val{D}, ::Type{V}, order::Int [, filter::Function]) + MonomialBasis(::Val{D}, ::Type{V}, orders::Tuple [, filter::Function]) -Convenience constructors of [`MonomialBasis`](@ref). +High level constructors of [`MonomialBasis`](@ref). """ MonomialBasis(args...) = TensorPolynomialBasis(Monomial, args...) @@ -31,13 +31,13 @@ PCurlGradMonomialBasis(args...) = PCurlGradBasis(Monomial, args...) # 1D evaluation implementation -function _evaluate_1d!(::Type{Monomial},::Val{K},v::AbstractMatrix{T},x,d) where {K,T<:Number} +function _evaluate_1d!(::Type{Monomial},::Val{K},c::AbstractMatrix{T},x,d) where {K,T<:Number} n = K + 1 xn = one(T) @inbounds xd = x[d] for i in 1:n - @inbounds v[d,i] = xn + @inbounds c[d,i] = xn xn *= xd end end diff --git a/src/Polynomials/NedelecPrebasisOnSimplex.jl b/src/Polynomials/NedelecPrebasisOnSimplex.jl index 9613f15de..1872b082e 100644 --- a/src/Polynomials/NedelecPrebasisOnSimplex.jl +++ b/src/Polynomials/NedelecPrebasisOnSimplex.jl @@ -38,32 +38,32 @@ function evaluate!( V = eltype(x) T = eltype(V) z = zero(T) - u = one(T) - for (ip,p) in enumerate(x) + for (i,xi) in enumerate(x) + # terms for (ℙₖ)³ for j in 1:ndofsP - a[ip,j] = Px[ip,j] + a[i,j] = Px[i,j] end - i = ndofsP - x1,x2,x3 = x[ip] - zp = zero(x1) + # terms for x × (ℙₖ\ℙₖ₋₁)³ + j = ndofsP + x1,x2,x3 = x[i] for β in 1:K for α in 1:(K+1-β) - i += 1 - a[ip,i] = VectorValue( + j += 1 + a[i,j] = VectorValue( -x1^(α-1)*x2^(K-α-β+2)*x3^(β-1), x1^α*x2^(K-α-β+1)*x3^(β-1), - zp) - i += 1 - a[ip,i] = VectorValue( + z) + j += 1 + a[i,j] = VectorValue( -x1^(K-α-β+1)*x2^(β-1)*x3^α, - zp, + z, x1^(K-α-β+2)*x2^(β-1)*x3^(α-1)) end end for γ in 1:K - i += 1 - a[ip,i] = VectorValue( - zp, + j += 1 + a[i,j] = VectorValue( + z, -x2^(γ-1)*x3^(K-γ+1), x2^γ*x3^(K-γ)) end @@ -83,22 +83,23 @@ function evaluate!( V = eltype(x) T = eltype(V) z = zero(T) - u = one(T) Px = evaluate!(cP,P,x) - for (ip,p) in enumerate(x) + for (i,xi) in enumerate(x) + # terms for (ℙₖ)² for j in 1:ndofsP - a[ip,j] = Px[ip,j] + a[i,j] = Px[i,j] end - i = ndofsP - x1,x2 = x[ip] - zp = zero(x1) + # terms for x × (ℙₖ\ℙₖ₋₁)² + j = ndofsP + x1,x2 = xi for α in 1:K - i += 1 - a[ip,i] = VectorValue(-x1^(α-1)*x2^(K-α+1),x1^α*x2^(K-α)) + j += 1 + a[i,j] = VectorValue(-x1^(α-1)*x2^(K-α+1),x1^α*x2^(K-α)) end - #a[ip,1] = VectorValue((u,z)) - #a[ip,2] = VectorValue((z,u)) - #a[ip,3] = VectorValue((-p[2],p[1])) + #u = one(T) + #a[i,1] = VectorValue((u,z)) + #a[i,2] = VectorValue((z,u)) + #a[i,3] = VectorValue((-xi[2],xi[1])) end a end @@ -137,18 +138,18 @@ function evaluate!( V = eltype(x) T = eltype(V) z = zero(T) - u = one(T) - for (ip,p) in enumerate(x) + for (i,xi) in enumerate(x) + # terms for ∇((ℙₖ)³) for j in 1:ndofsP - a[ip,j] = Px[ip,j] + a[i,j] = Px[i,j] end - i = ndofsP - x1,x2,x3 = x[ip] - zp = zero(x1) + # terms for ∇(x × (ℙₖ\ℙₖ₋₁)³) + j = ndofsP + x1,x2,x3 = x[i] for β in 1:K for α in 1:(K+1-β) - i += 1 - a[ip,i] = TensorValue( + j += 1 + a[i,j] = TensorValue( #-x1^(α-1)*x2^(K-α-β+2)*x3^(β-1), -(α-1)*_exp(x1,α-2)*x2^(K-α-β+2)*x3^(β-1), -x1^(α-1)*(K-α-β+2)*_exp(x2,K-α-β+1)*x3^(β-1), @@ -157,16 +158,14 @@ function evaluate!( α*_exp(x1,α-1)*x2^(K-α-β+1)*x3^(β-1), x1^α*(K-α-β+1)*_exp(x2,K-α-β)*x3^(β-1), x1^α*x2^(K-α-β+1)*(β-1)*_exp(x3,β-2), - #zp, - zp,zp,zp) - i += 1 - a[ip,i] = TensorValue( + z,z,z) + j += 1 + a[i,j] = TensorValue( #-x1^(K-α-β+1)*x2^(β-1)*x3^α, -(K-α-β+1)*_exp(x1,K-α-β)*x2^(β-1)*x3^α, -x1^(K-α-β+1)*(β-1)*_exp(x2,β-2)*x3^α, -x1^(K-α-β+1)*x2^(β-1)*α*_exp(x3,α-1), - # zp - zp,zp,zp, + z,z,z, #x1^(K-α-β+2)*x2^(β-1)*x3^(α-1), (K-α-β+2)*_exp(x1,K-α-β+1)*x2^(β-1)*x3^(α-1), x1^(K-α-β+2)*(β-1)*_exp(x2,β-2)*x3^(α-1), @@ -174,10 +173,9 @@ function evaluate!( end end for γ in 1:K - i += 1 - a[ip,i] = TensorValue( - #zp - zp,zp,zp, + j += 1 + a[i,j] = TensorValue( + z,z,z, #-x2^(γ-1)*x3^(K-γ+1), -0*x2^(γ-1)*x3^(K-γ+1), -(γ-1)*_exp(x2,γ-2)*x3^(K-γ+1), @@ -187,9 +185,10 @@ function evaluate!( γ*_exp(x2,γ-1)*x3^(K-γ), x2^γ*(K-γ)*_exp(x3,K-γ-1)) end - #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)) + #u = one(T) + #a[i,4] = TensorValue((z,-u,z, u,z,z, z,z,z)) + #a[i,5] = TensorValue((z,z,-u, z,z,z, u,z,z)) + #a[i,6] = TensorValue((z,z,z, z,z,-u, z,u,z)) end a end @@ -211,19 +210,19 @@ function evaluate!( V = eltype(x) T = eltype(V) z = zero(T) - u = one(T) ndofsP = length(P) Px = evaluate!(cP,P,x) - for (ip,p) in enumerate(x) + for (i,xi) in enumerate(x) + # terms for ∇((ℙₖ)²) for j in 1:ndofsP - a[ip,j] = Px[ip,j] + a[i,j] = Px[i,j] end - i = ndofsP - x1,x2 = x[ip] - zp = zero(x1) + # terms for ∇(x × (ℙₖ\ℙₖ₋₁)²) + j = ndofsP + x1,x2 = x[i] for α in 1:K - i += 1 - a[ip,i] = TensorValue( + j += 1 + a[i,j] = TensorValue( #-x1^(α-1)*x2^(K-α+1), -(α-1)*_exp(x1,α-2)*x2^(K-α+1), -x1^(α-1)*(K-α+1)*_exp(x2,K-α), @@ -231,19 +230,31 @@ function evaluate!( α*_exp(x1,α-1)*x2^(K-α), x1^α*(K-α)*_exp(x2,K-α-1)) end - #a[ip,3] = TensorValue((z,-u, u,z)) + #u = one(T) + #a[i,3] = TensorValue((z,-u, u,z)) end a end +#################################### +# Basis for Nedelec on D-simplices # +#################################### + """ - PGradBasis(::Type{PT}, ::Val{D}, ::Type{T}, order::Int) :: PolynomialBasis + PGradBasis(::Type{Monomial}, ::Val{D}, ::Type{T}, order::Int) :: PolynomialBasis + +Return a basis of ℕ𝔻ᴰₙ(△) = (ℙₙ)ᴰ ⊕ x × (ℙₙ \\ ℙₙ₋₁)ᴰ with n=`order`, the polynomial +space for Nedelec elements on `D`-dimensional simplices with scalar type `T`. + +The `order`=n argument has the following meaning: the curl of the functions in +this basis is in ℙₙ. -Return a basis of ℙ_order ⊕ x × (ℙ_order\\ℙ_{order-1}), the polynomial space -for Nedelec elements on `D`-dimensonal simplices with scalar type `T`. +# Example: -The `order` argument has the following meaning: the curl of the functions in -this basis is in the ℙ space of degree `order`. +```jldoctest +# a basis for Nedelec on tetrahedra with curl in ℙ₂ +b = PGradBasis(Monomial, Val(3), Float64, 2) +``` """ function PGradBasis(::Type{PT},::Val{D},::Type{T},order::Int) where {PT,D,T} @notimplemented "only implemented for monomials" diff --git a/src/Polynomials/NonTensorRTPolyBases.jl b/src/Polynomials/NonTensorRTPolyBases.jl index e2d8557e5..1aaa210cd 100644 --- a/src/Polynomials/NonTensorRTPolyBases.jl +++ b/src/Polynomials/NonTensorRTPolyBases.jl @@ -4,7 +4,7 @@ Basis of ℝ𝕋ᴰₙ = (𝕊ₙ)ᴰ ⊕ x (𝕊ₙ\\𝕊₍ₙ₋₁₎) where 𝕊ₙ is a multivariate scalar polynomial space of maximum degree n = `K`-1. -ℝ𝕋ᴰₙ is the space needed for Raviart-Thomas elements with divergence in 𝕊ₙ. +This ℝ𝕋ᴰₙ is the polynomial space for Raviart-Thomas elements with divergence in 𝕊ₙ. Its maximum degree is n+1 = `K`. `get_order` on it returns `K`. The multivariate scalar space 𝕊ₙ, typically ℙₙ, doesn't need to have a tensor @@ -14,8 +14,8 @@ are not tensor products either. 𝕊ₙ must admit a basis computable using products of 1D polynomials of the `PT` type, `PT` thus needs to be hierarchical, see [`isHierarchical`](@ref). -Indeed, 𝕊ₙ is defined like a scalar valued `TensorPolynomialBasis` via the -`_filter` argument of the constructor, by default `_p_filter`. +Indeed, 𝕊ₙ is defined like a scalar valued [`TensorPolynomialBasis`](@ref) via +the `_filter` argument of the constructor, by default `_p_filter`. """ struct NonTensorRTPolyBasis{D,V,K,PT} <: PolynomialBasis{D,V,K,PT} pterms::Vector{CartesianIndex{D}} @@ -36,25 +36,24 @@ struct NonTensorRTPolyBasis{D,V,K,PT} <: PolynomialBasis{D,V,K,PT} @check isHierarchical(PT) "The polynomial basis must be hierarchichal for this space." V = VectorValue{D,T} + indexbase = 1 - # terms defining 𝕊_order + # terms defining 𝕊ₙ P_k = MonomialBasis(Val(D), T, order, _filter) - pterms = copy(P_k.terms) + pterms = P_k.terms msg = "Some term defining `𝕊ₙ` contain a higher index than the maximum, `order`+1, please fix the `_filter` argument" - @check all( pterm -> (maximum(Tuple(pterm), init=0) <= order+1), pterms) msg + @check all( pterm -> (maximum(Tuple(pterm) .- indexbase, init=0) <= order), pterms) msg - # terms defining 𝕊_order\𝕊_{order-1} - _minus_one_order_filter = term -> _filter(Tuple(term) .- 1, order-1) # .-1 for index / degree shift + # terms defining 𝕊ₙ\𝕊ₙ₋₁ + _minus_one_order_filter = term -> _filter(Tuple(term) .- indexbase, order-1) sterms = filter(!_minus_one_order_filter, pterms) new{D,V,order+1,PT}(pterms,sterms) end end -Base.size(a::NonTensorRTPolyBasis{D}) where {D} = D*length(a.pterms) + length(a.sterms) -Base.getindex(a::NonTensorRTPolyBasis,i::Integer) = Monomial() -Base.IndexStyle(::NonTensorRTPolyBasis) = IndexLinear() +Base.size(a::NonTensorRTPolyBasis{D}) where {D} = (D*length(a.pterms) + length(a.sterms), ) ################################# @@ -191,17 +190,26 @@ end """ PCurlGradBasis(::Type{PT}, ::Val{D}, ::Type{T}, order::Int) :: PolynomialBasis -Return a basis of ℙ_`order` ⊕ x ⋅ (ℙ_`order`\\ℙ_{`order`-1}), the polynomial +Return a basis of ℝ𝕋ᴰₙ(△) = (ℙₙ)ᴰ ⊕ x (ℙₙ \\ ℙₙ₋₁) with n=`order`, the polynomial space for Raviart-Thomas elements on `D`-dimensional simplices with scalar type `T`. -The `order` argument of this function has the following meaning: the divergence -of the functions in this basis is in the ℙ space of degree `order`. +The `order`=n argument of this function has the following meaning: the divergence +of the functions in this basis is in ℙₙ. `PT<:Polynomial` is the choice of scalar 1D polynomial basis, it must be hierarchichal, see [`isHierarchichal`](@ref). -Returns a `NonTensorRTPolyBasis{D,VectorValue{D,T},order+1,PT}` object for `D`>1, -or `TensorPolynomialBasis{1,VectorValue{1,T},order+1,PT}` for `D`=1. +# Example: + +```jldoctest +# a basis for Raviart-Thomas on tetrahedra with divergence in ℙ₂ +b = PCurlGradBasis(Monomial, Val(3), Float64, 2) +``` + +For more details, see [`NonTensorRTPolyBasis`](@ref), as `PCurlGradBasis` returns +an instance of\\ +`NonTensorRTPolyBasis{D,VectorValue{D,T},order+1,PT}` for `D`=2,3, or\\ +`TensorPolynomialBasis{1,VectorValue{1,T},order+1,PT}` for `D`=1. """ function PCurlGradBasis(::Type{PT},::Val{D},::Type{T},order::Int) where {PT,D,T} NonTensorRTPolyBasis{D}(PT, T, order) diff --git a/src/Polynomials/PolynomialInterfaces.jl b/src/Polynomials/PolynomialInterfaces.jl index f54c0cc04..34b28b670 100644 --- a/src/Polynomials/PolynomialInterfaces.jl +++ b/src/Polynomials/PolynomialInterfaces.jl @@ -3,15 +3,23 @@ ############################ """ -Abstract type for 1- or n-D polynomial family of maximum order k. + Polynomial <: Field -Implements [`isHierarchical`](@ref). +Abstract type for polynomial bases families/types. It has trait +[`isHierarchical`](@ref). + +The currently implemented families are [Monomial](@ref), [Legendre](@ref), +[Chebyshev](@ref), [ModalC0](@ref) and [Bernstein](@ref). Only Bernstein is not +hierarchical. """ abstract type Polynomial <: Field end """ -Return true if the basis of order `k` of the given `<:Polynomial` type is the union -of the basis of order `k-1` and an other order `k` polynomial. + isHierarchical(::Type{Polynomial}) + +Return true if the 1D basis of order `K` of the given [`Polynomial`](@ref) type +is the union of the basis of order `K-1` and an other order `K` polynomial. +Equivalently, if the iᵗʰ basis polynomial is of order i-1. """ isHierarchical(::Type{Polynomial}) = @abstractmethod @@ -29,11 +37,11 @@ isHierarchical(::Type{Polynomial}) = @abstractmethod # value V, i.e. gradient_type(V,Point{D}) # or gradient_type(gradient_type(V,p::Point{D}), p) # -# PT: a concrete `Polynomial` type -# K: integer polynomial order (maximum of any component and in any direction in nD). -# np: number of points at which a basis is evaluated -# ndof: number of basis vectors, num_indep_components(V) × dimension of the polynomial space -# ndof_1d: maximum of 1D basis vector in any spatial dimension +# PT: a concrete `Polynomial` type +# K: integer polynomial order (maximum order of any component and in any direction in nD). +# np: number of points at which a basis is evaluated +# ndof: number of basis polynomials +# ndof_1d: maximum of 1D polynomial vector in any spatial dimension """ PolynomialBasis{D,V,K,PT<:Polynomial} <: AbstractVector{PT} @@ -43,7 +51,7 @@ The parameters are: - `D`: the spatial dimension - `V`: the image values type, of type `<:Real` or `<:MultiValue` - `K`: the maximum order of a basis polynomial in a spatial component -- `PT <: Polynomial`: the polynomial family (must be a concrete type) +- `PT <: Polynomial`: the polynomial family (must be a concrete type). """ abstract type PolynomialBasis{D,V,K,PT<:Polynomial} <: AbstractVector{PT} end @@ -55,9 +63,9 @@ abstract type PolynomialBasis{D,V,K,PT<:Polynomial} <: AbstractVector{PT} end @deprecate num_terms(a::PolynomialBasis) length(a) """ - get_order(b::PolynomialBasis{D,V,K) = K + get_order(b::PolynomialBasis{D,V,K}) = K -Return the maximum polynomial order in a dimension, or `0` in 0D. +Return the maximum polynomial order in a dimension, is should be `0` in 0D. """ @inline get_order(::PolynomialBasis{D,V,K}) where {D,V,K} = K @@ -66,9 +74,6 @@ Return the maximum polynomial order in a dimension, or `0` in 0D. # Generic field implementation # ################################ -""" -TODO -""" function _return_cache( f::PolynomialBasis{D}, x,::Type{G},::Val{N_deriv}) where {D,G,N_deriv} @@ -282,7 +287,7 @@ end Evaluates in place the 1D basis polynomials of the given type at one nD point `x` along the given coordinate 1 ≤ `d` ≤ nD. -`c` is an AbstractMatrix of size (at least} `d`×(`K`+1), such that the 1 ≤ i ≤ `k`+1 +`c` is an AbstractMatrix of size (at least) `d`×(`K`+1), such that the 1 ≤ i ≤ `k`+1 values are stored in `c[d,i]`. """ function _evaluate_1d!(::Type{<:Polynomial},::Val{K},c::AbstractMatrix{T},x,d) where {K,T<:Number} diff --git a/src/Polynomials/TensorPolynomialBases.jl b/src/Polynomials/TensorPolynomialBases.jl index 14bf49428..82047ae63 100644 --- a/src/Polynomials/TensorPolynomialBases.jl +++ b/src/Polynomials/TensorPolynomialBases.jl @@ -56,8 +56,8 @@ end TensorPolynomialBasis(::Type{PT}, ::Val{D}, ::Type{V}, orders::Tuple [, filter::Function]) This version of the constructor allows to pass a tuple `orders` containing the -polynomial order to be used in each of the `D` dimensions in order to construct -an anisotropic tensor-product space. +polynomial order to be used in each of the `D` dimensions in order to construct +an tensor-product multivariate space. """ function TensorPolynomialBasis( ::Type{PT}, ::Val{D}, ::Type{V}, orders::NTuple{D,Int}, filter::Function=_q_filter @@ -87,8 +87,8 @@ The signature of the filter function is where `e` is a tuple of `D` integers containing the exponents of a multivariate monomial. The following filters are used to select well known polynomial spaces -- Q space: `(e,order) -> true` -- P space: `(e,order) -> sum(e) <= order` +- ℚ space: `(e,order) -> true` +- ℙ space: `(e,order) -> sum(e) <= order` - "Serendipity" space: `(e,order) -> sum( [ i for i in e if i>1 ] ) <= order` """ @@ -106,7 +106,7 @@ end Get a vector of tuples with the exponents of all the terms in the basis. -# Examples +# Example ```jldoctest using Gridap.Polynomials From 4a2cd6b6a9be29fc1365b5bbe5fdf7f32899e7b6 Mon Sep 17 00:00:00 2001 From: JordiManyer Date: Thu, 2 Jan 2025 15:52:00 +1100 Subject: [PATCH 071/105] Started adding change of basis --- docs/src/dev-notes/pullbacks.md | 2 +- src/Arrays/Arrays.jl | 3 +- src/FESpaces/Pullbacks.jl | 43 ++++++++------- src/ReferenceFEs/Dofs.jl | 53 +++++++++++++++++++ .../LinearCombinationDofVectors.jl | 46 ---------------- src/ReferenceFEs/Pullbacks.jl | 38 +++++++------ src/ReferenceFEs/ReferenceFEs.jl | 2 - 7 files changed, 100 insertions(+), 87 deletions(-) delete mode 100644 src/ReferenceFEs/LinearCombinationDofVectors.jl diff --git a/docs/src/dev-notes/pullbacks.md b/docs/src/dev-notes/pullbacks.md index dda070b4d..2a2a98d13 100644 --- a/docs/src/dev-notes/pullbacks.md +++ b/docs/src/dev-notes/pullbacks.md @@ -71,7 +71,7 @@ I_K(g) = \Sigma(g) \Phi \quad, \quad \Sigma(g) = P^{-1} \hat{\Sigma}(F^{-*}(g)) !!! note In [2], Covariant and Contravariant Piola maps preserve exactly (without any sign change) the normal and tangential components of a vector field. - I am quite sure that the discrepancy is coming from the fact that the geometrical information in the reference polytope is globaly oriented. + I am quite sure that the discrepancy is coming from the fact that the geometrical information in the reference polytope is globally oriented. For instance, the normals ``n`` and ``\hat{n}`` both have the same orientation, i.e ``n = (||\hat{e}||/||e||) (det J) J^{-T} \hat{n}``. Therefore ``\hat{n}`` is not fully local. See [2, Equation 2.11]. In our case, we will be including the sign change in the transformation matrices, which will include all cell-and-dof-dependent information. diff --git a/src/Arrays/Arrays.jl b/src/Arrays/Arrays.jl index ce7452453..54f39222a 100644 --- a/src/Arrays/Arrays.jl +++ b/src/Arrays/Arrays.jl @@ -47,9 +47,8 @@ export testargs export inverse_map export Broadcasting - export Operation - +export InverseMap # LazyArray diff --git a/src/FESpaces/Pullbacks.jl b/src/FESpaces/Pullbacks.jl index 0f3bce9cd..8e006a6f2 100644 --- a/src/FESpaces/Pullbacks.jl +++ b/src/FESpaces/Pullbacks.jl @@ -1,27 +1,31 @@ # TODO: We probably want to export these from Gridap.ReferenceFEs -using Gridap.ReferenceFEs: PushforwardRefFE, Pushforward, InversePullback +using Gridap.ReferenceFEs: PushforwardRefFE, Pushforward, Pullback using Gridap.ReferenceFEs: ContraVariantPiolaMap, CoVariantPiolaMap function get_cell_dof_basis( model::DiscreteModel, cell_reffe::AbstractArray{<:GenericRefFE{T}}, conformity::Conformity ) where T <: PushforwardRefFE - pushforward = Pushforward(T) - cell_args = get_cell_pushforward_arguments(pushforward, model, cell_reffe, conformity) - cell_dofs = lazy_map(get_dof_basis, cell_reffe) - return lazy_map(InversePullback(pushforward), cell_dofs, cell_args...) + pushforward, cell_change, cell_args = get_cell_pushforward( + Pushforward(T), model, cell_reffe, conformity + ) + cell_ref_dofs = lazy_map(get_dof_basis, cell_reffe) + cell_phy_dofs = lazy_map(inverse_map(Pullback(pushforward)), cell_ref_dofs, cell_args...) + return lazy_map(linear_combination, cell_change, cell_phy_dofs) # TODO: Inverse end function get_cell_shapefuns( model::DiscreteModel, cell_reffe::AbstractArray{<:GenericRefFE{T}}, conformity::Conformity ) where T <: PushforwardRefFE - pushforward = Pushforward(T) - cell_args = get_cell_pushforward_arguments(pushforward, model, cell_reffe, conformity) - cell_dofs = lazy_map(get_shapefuns, cell_reffe) - return lazy_map(pushforward, cell_dofs, cell_args...) + pushforward, cell_change, cell_args = get_cell_pushforward( + Pushforward(T), model, cell_reffe, conformity + ) + cell_ref_fields = lazy_map(get_shapefuns, cell_reffe) + cell_phy_fields = lazy_map(pushforward, cell_ref_fields, cell_args...) + return lazy_map(linear_combination, cell_change, cell_phy_fields) end -function get_cell_pushforward_arguments( +function get_cell_pushforward( ::Pushforward, model::DiscreteModel, cell_reffe, conformity ) @abstractmethod @@ -29,23 +33,24 @@ end # ContraVariantPiolaMap -function get_cell_pushforward_arguments( +function get_cell_pushforward( ::ContraVariantPiolaMap, model::DiscreteModel, cell_reffe, conformity ) cell_map = get_cell_map(get_grid(model)) Jt = lazy_map(Broadcasting(∇),cell_map) - sign_flip = lazy_map(Broadcasting(constant_field),get_sign_flip(model, cell_reffe)) - return Jt, sign_flip + change = get_sign_flip(model, cell_reffe) + return ContraVariantPiolaMap(), change, (Jt,) end # CoVariantPiolaMap -function get_cell_pushforward_arguments( +function get_cell_pushforward( ::CoVariantPiolaMap, model::DiscreteModel, cell_reffe, conformity ) cell_map = get_cell_map(get_grid(model)) Jt = lazy_map(Broadcasting(∇),cell_map) - return Jt + change = lazy_map(r -> Diagonal(ones(num_dofs(r))), cell_reffe) # TODO: Replace by edge-signs + return CoVariantPiolaMap(), change, (Jt,) end # SignFlipMap @@ -72,7 +77,7 @@ function return_cache(k::SignFlipMap,reffe,facet_own_dofs,cell) cell_facets = get_faces(topo, Dc, Dc-1) cell_facets_cache = array_cache(cell_facets) - return cell_facets, cell_facets_cache, CachedVector(Bool) + return cell_facets, cell_facets_cache, CachedVector(Float64) end function evaluate!(cache,k::SignFlipMap,reffe,facet_own_dofs,cell) @@ -81,19 +86,19 @@ function evaluate!(cache,k::SignFlipMap,reffe,facet_own_dofs,cell) setsize!(sign_flip_cache, (num_dofs(reffe),)) sign_flip = sign_flip_cache.array - sign_flip .= false + fill!(sign_flip, one(eltype(sign_flip))) facets = getindex!(cell_facets_cache,cell_facets,cell) for (lfacet,facet) in enumerate(facets) owner = facet_owners[facet] if owner != cell for dof in facet_own_dofs[lfacet] - sign_flip[dof] = true + sign_flip[dof] = -1.0 end end end - return sign_flip + return Diagonal(sign_flip) end function get_sign_flip(model::DiscreteModel{Dc}, cell_reffe) where Dc diff --git a/src/ReferenceFEs/Dofs.jl b/src/ReferenceFEs/Dofs.jl index cee46b14e..42d913929 100644 --- a/src/ReferenceFEs/Dofs.jl +++ b/src/ReferenceFEs/Dofs.jl @@ -1,5 +1,58 @@ abstract type Dof <: Map end +""" + struct LinearCombinationDofVector{T} <: AbstractVector{Dof} + values::Matrix{T} + dofs::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 `dofs`) and the +dof values are next mapped to dof basis (a) applying a change of basis (field +`values`). + +Fields: + +- `values::Matrix{T}` the matrix of the change from dof basis (b) to (a) +- `dofs::AbstractVector{<:Dof}` A type representing dof basis (b) +""" +struct LinearCombinationDofVector{T,V,F} <: AbstractVector{T} + values::V + dofs::F + function LinearCombinationDofVector( + values::AbstractMatrix{<:Number}, + dofs::AbstractVector{<:Dof} + ) + T = eltype(dofs) + V = typeof(values) + F = typeof(dofs) + new{T,V,F}(values,dofs) + end +end + +@inline Base.size(a::LinearCombinationDofVector) = size(a.values,2) +@inline Base.IndexStyle(::LinearCombinationDofVector) = IndexLinear() +@inline Base.getindex(::LinearCombinationDofVector{T},::Integer) where T = T() + +function linear_combination(a::AbstractMatrix{<:Number}, b::AbstractVector{<:Dof}) + LinearCombinationDofVector(a,b) +end + +function return_cache(b::LinearCombinationDofVector,field) + k = LinearCombinationMap(:) + cf = return_cache(b.dofs,field) + fx = evaluate!(cf,b.dofs,field) + ck = return_cache(k,b.values,fx) + return cf, ck +end + +function evaluate!(cache,b::LinearCombinationDofVector,field) + cf, ck = cache + k = LinearCombinationMap(:) + fx = evaluate!(cf,b.dofs,field) + return evaluate!(ck,k,b.values,fx) +end + """ test_dof(dof,field,v;cmp::Function=(==)) diff --git a/src/ReferenceFEs/LinearCombinationDofVectors.jl b/src/ReferenceFEs/LinearCombinationDofVectors.jl deleted file mode 100644 index 507a34724..000000000 --- a/src/ReferenceFEs/LinearCombinationDofVectors.jl +++ /dev/null @@ -1,46 +0,0 @@ -""" - 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/Pullbacks.jl b/src/ReferenceFEs/Pullbacks.jl index 5ab1f03cf..02ef0a13a 100644 --- a/src/ReferenceFEs/Pullbacks.jl +++ b/src/ReferenceFEs/Pullbacks.jl @@ -46,10 +46,9 @@ function Arrays.evaluate!( ::Broadcasting{typeof(∇)}, a::Fields.BroadcastOpFieldArray{<:Pushforward} ) - v, Jt, sign_flip = a.args - ∇v = Broadcasting(∇)(v) - k = ContraVariantPiolaMap() - Broadcasting(Operation(k))(∇v,Jt,sign_flip) + v, pf_args... = a.args + grad_v = Broadcasting(∇)(v) + Broadcasting(Operation(a.op))(grad_v,pf_args...) end # InversePushforward @@ -106,6 +105,12 @@ function evaluate!( Broadcasting(Operation(k))(f_phys,args...) end +function evaluate!( + cache, k::InversePushforward, f_phys::Field, args... +) + Operation(k)(f_phys,args...) +end + # MappedDofBasis """ @@ -208,7 +213,6 @@ const InversePullback{PB} = InverseMap{PB} where PB <: Pullback function Arrays.lazy_map( ::typeof(evaluate), k::LazyArray{<:Fill{<:InversePullback}}, phys_cell_fields::AbstractArray ) - println("InversePullback dispatch") pb = inverse_map(k.maps.value) ref_cell_dofs, pf_args... = k.args ref_cell_fields = lazy_map(inverse_map(pb.pushforward), phys_cell_fields, pf_args...) @@ -227,25 +231,23 @@ end struct ContraVariantPiolaMap <: Pushforward end function evaluate!( - cache, ::ContraVariantPiolaMap, v_ref::Number, Jt::Number, sign_flip::Bool + cache, ::ContraVariantPiolaMap, v_ref::Number, Jt::Number ) - sign = (-1)^sign_flip idetJ = 1. / meas(Jt) - return (sign * v_ref) ⋅(idetJ * Jt) + return v_ref ⋅ (idetJ * Jt) end function return_cache( - ::InversePushforward{ContraVariantPiolaMap}, v_phys::Number, Jt::Number, sign_flip::Bool + ::InversePushforward{ContraVariantPiolaMap}, v_phys::Number, Jt::Number ) nothing end function evaluate!( - cache, ::InversePushforward{ContraVariantPiolaMap}, v_phys::Number, Jt::Number, sign_flip::Bool + cache, ::InversePushforward{ContraVariantPiolaMap}, v_phys::Number, Jt::Number ) - sign = (-1)^sign_flip detJ = meas(Jt) - return (sign * v_phys) ⋅ (detJ * pinvJt(Jt)) + return v_phys ⋅ (detJ * pinvJt(Jt)) end # TODO: Should this be here? Probably not... @@ -263,11 +265,13 @@ function Fields.DIV(a::LazyArray{<:Fill{typeof(linear_combination)}}) end function Fields.DIV(f::LazyArray{<:Fill{Broadcasting{Operation{ContraVariantPiolaMap}}}}) - ϕrgₖ = f.args[1] - fsign_flip = f.args[3] - div_ϕrgₖ = lazy_map(Broadcasting(divergence),ϕrgₖ) - fsign_flip = lazy_map(Broadcasting(Operation(x->(-1)^x)), fsign_flip) - lazy_map(Broadcasting(Operation(*)),fsign_flip,div_ϕrgₖ) + ϕrgₖ = f.args[1] + return lazy_map(Broadcasting(divergence),ϕrgₖ) +end + +function Fields.DIV(f::Fill{<:Fields.BroadcastOpFieldArray{ContraVariantPiolaMap}}) + ϕrgₖ = f.value.args[1] + return Fill(Broadcasting(divergence)(ϕrgₖ),length(f)) end # CoVariantPiolaMap diff --git a/src/ReferenceFEs/ReferenceFEs.jl b/src/ReferenceFEs/ReferenceFEs.jl index 12a531f63..a433e49ba 100644 --- a/src/ReferenceFEs/ReferenceFEs.jl +++ b/src/ReferenceFEs/ReferenceFEs.jl @@ -265,6 +265,4 @@ include("BezierRefFEs.jl") include("ModalC0RefFEs.jl") -include("LinearCombinationDofVectors.jl") - end # module From 6f50ebb3f671f2cac20dc49d480ef663af38ec8c Mon Sep 17 00:00:00 2001 From: JordiManyer Date: Thu, 2 Jan 2025 16:31:52 +1100 Subject: [PATCH 072/105] Minor bugfixes --- src/FESpaces/Pullbacks.jl | 2 +- src/ReferenceFEs/Dofs.jl | 54 ++++++++++++++++++++++++++++++-- src/ReferenceFEs/Pullbacks.jl | 58 ++--------------------------------- 3 files changed, 56 insertions(+), 58 deletions(-) diff --git a/src/FESpaces/Pullbacks.jl b/src/FESpaces/Pullbacks.jl index 8e006a6f2..0b05d2403 100644 --- a/src/FESpaces/Pullbacks.jl +++ b/src/FESpaces/Pullbacks.jl @@ -66,7 +66,7 @@ function SignFlipMap(model) end function return_value(k::SignFlipMap,reffe,facet_own_dofs,cell) - fill(false, num_dofs(reffe)) + Diagonal(fill(one(Float64), num_dofs(reffe))) end function return_cache(k::SignFlipMap,reffe,facet_own_dofs,cell) diff --git a/src/ReferenceFEs/Dofs.jl b/src/ReferenceFEs/Dofs.jl index 42d913929..1a9fb3025 100644 --- a/src/ReferenceFEs/Dofs.jl +++ b/src/ReferenceFEs/Dofs.jl @@ -39,7 +39,7 @@ function linear_combination(a::AbstractMatrix{<:Number}, b::AbstractVector{<:Dof end function return_cache(b::LinearCombinationDofVector,field) - k = LinearCombinationMap(:) + k = Fields.LinearCombinationMap(:) cf = return_cache(b.dofs,field) fx = evaluate!(cf,b.dofs,field) ck = return_cache(k,b.values,fx) @@ -48,11 +48,61 @@ end function evaluate!(cache,b::LinearCombinationDofVector,field) cf, ck = cache - k = LinearCombinationMap(:) + k = Fields.LinearCombinationMap(:) fx = evaluate!(cf,b.dofs,field) return evaluate!(ck,k,b.values,fx) end +""" + struct MappedDofBasis{T<:Dof,MT,BT} <: AbstractVector{T} + F :: MT + σ :: BT + args + end + +Represents η = σ∘F, evaluated as η(φ) = σ(F(φ,args...)) + + - σ : V* -> R is a dof basis + - F : W -> V is a map between function spaces + +Intended combinations would be: + +- σ : V* -> R dof basis in the physical domain and F* : V̂ -> V is a pushforward map. +- ̂σ : V̂* -> R dof basis in the reference domain and (F*)^-1 : V -> V̂ is an inverse pushforward map. + +""" +struct MappedDofBasis{T<:Dof,MT,BT,A} <: AbstractVector{T} + F :: MT + dofs :: BT + args :: A + + function MappedDofBasis(F::Map, dofs::AbstractVector{<:Dof}, args...) + T = eltype(dofs) + MT = typeof(F) + BT = typeof(dofs) + A = typeof(args) + new{T,MT,BT,A}(F,dofs,args) + end +end + +Base.size(b::MappedDofBasis) = size(b.dofs) + +# Technically wrong, but the individual dofs are fake anyway +Base.getindex(b::MappedDofBasis, i) = getindex(b.dofs,i) + +function Arrays.return_cache(b::MappedDofBasis, fields) + f_cache = return_cache(b.F,fields,b.args...) + ffields = evaluate!(f_cache,b.F,fields,b.args...) + dofs_cache = return_cache(b.dofs,ffields) + return f_cache, dofs_cache +end + +function Arrays.evaluate!(cache, b::MappedDofBasis, fields) + f_cache, dofs_cache = cache + ffields = evaluate!(f_cache,b.F,fields,b.args...) + evaluate!(dofs_cache,b.dofs,ffields) +end + """ test_dof(dof,field,v;cmp::Function=(==)) diff --git a/src/ReferenceFEs/Pullbacks.jl b/src/ReferenceFEs/Pullbacks.jl index 02ef0a13a..46602b495 100644 --- a/src/ReferenceFEs/Pullbacks.jl +++ b/src/ReferenceFEs/Pullbacks.jl @@ -43,7 +43,7 @@ end function Arrays.evaluate!( cache, - ::Broadcasting{typeof(∇)}, + ::Broadcasting{typeof(gradient)}, a::Fields.BroadcastOpFieldArray{<:Pushforward} ) v, pf_args... = a.args @@ -111,58 +111,6 @@ function evaluate!( Operation(k)(f_phys,args...) end -# MappedDofBasis - -""" - struct MappedDofBasis{T<:Dof,MT,BT} <: AbstractVector{T} - F :: MT - σ :: BT - args - end - -Represents η = σ∘F, evaluated as η(φ) = σ(F(φ,args...)) - - - σ : V* -> R is a dof basis - - F : W -> V is a map between function spaces - -Intended combinations would be: - -- σ : V* -> R dof basis in the physical domain and F* : V̂ -> V is a pushforward map. -- ̂σ : V̂* -> R dof basis in the reference domain and (F*)^-1 : V -> V̂ is an inverse pushforward map. - -""" -struct MappedDofBasis{T<:Dof,MT,BT,A} <: AbstractVector{T} - F :: MT - dofs :: BT - args :: A - - function MappedDofBasis(F::Map, dofs::AbstractVector{<:Dof}, args...) - T = eltype(dofs) - MT = typeof(F) - BT = typeof(dofs) - A = typeof(args) - new{T,MT,BT,A}(F,dofs,args) - end -end - -Base.size(b::MappedDofBasis) = size(b.dofs) - -# Technically wrong, but the individual dofs are fake anyway -Base.getindex(b::MappedDofBasis, i) = getindex(b.dofs,i) - -function Arrays.return_cache(b::MappedDofBasis, fields) - f_cache = return_cache(b.F,fields,b.args...) - ffields = evaluate!(f_cache,b.F,fields,b.args...) - dofs_cache = return_cache(b.dofs,ffields) - return f_cache, dofs_cache -end - -function Arrays.evaluate!(cache, b::MappedDofBasis, fields) - f_cache, dofs_cache = cache - ffields = evaluate!(f_cache,b.F,fields,b.args...) - evaluate!(dofs_cache,b.dofs,ffields) -end - # Pullback """ @@ -282,7 +230,7 @@ function evaluate!( cache, ::CoVariantPiolaMap, v_ref::Number, Jt::Number ) # we right-multiply to compute the gradient correctly - return v_ref⋅transpose(inv(Jt)) + return v_ref ⋅ transpose(inv(Jt)) end function return_cache( @@ -294,5 +242,5 @@ end function evaluate!( cache, ::InversePushforward{CoVariantPiolaMap}, v_phys::Number, Jt::Number ) - return transpose(Jt)⋅v_phys + return v_phys ⋅ transpose(Jt) end From 7d40d1b9dba0f068c7b51b6d59fed23ef2b04cec Mon Sep 17 00:00:00 2001 From: JordiManyer Date: Thu, 2 Jan 2025 17:02:20 +1100 Subject: [PATCH 073/105] Minor --- src/FESpaces/Pullbacks.jl | 2 +- src/ReferenceFEs/Pullbacks.jl | 1 - src/ReferenceFEs/ReferenceFEInterfaces.jl | 4 ++-- 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/src/FESpaces/Pullbacks.jl b/src/FESpaces/Pullbacks.jl index 0b05d2403..8afd972a6 100644 --- a/src/FESpaces/Pullbacks.jl +++ b/src/FESpaces/Pullbacks.jl @@ -11,7 +11,7 @@ function get_cell_dof_basis( ) cell_ref_dofs = lazy_map(get_dof_basis, cell_reffe) cell_phy_dofs = lazy_map(inverse_map(Pullback(pushforward)), cell_ref_dofs, cell_args...) - return lazy_map(linear_combination, cell_change, cell_phy_dofs) # TODO: Inverse + return lazy_map(linear_combination, cell_change, cell_phy_dofs) # TODO: Inverse and transpose end function get_cell_shapefuns( diff --git a/src/ReferenceFEs/Pullbacks.jl b/src/ReferenceFEs/Pullbacks.jl index 46602b495..bbdf1ddda 100644 --- a/src/ReferenceFEs/Pullbacks.jl +++ b/src/ReferenceFEs/Pullbacks.jl @@ -229,7 +229,6 @@ struct CoVariantPiolaMap <: Pushforward end function evaluate!( cache, ::CoVariantPiolaMap, v_ref::Number, Jt::Number ) - # we right-multiply to compute the gradient correctly return v_ref ⋅ transpose(inv(Jt)) end diff --git a/src/ReferenceFEs/ReferenceFEInterfaces.jl b/src/ReferenceFEs/ReferenceFEInterfaces.jl index ead616c1f..a4b1c0b82 100644 --- a/src/ReferenceFEs/ReferenceFEInterfaces.jl +++ b/src/ReferenceFEs/ReferenceFEInterfaces.jl @@ -390,11 +390,11 @@ associated with the dof basis `predofs` and the basis `shapefuns`. It is equivalent to - change = inv(evaluate(predofs,shapefuns)) + change = transpose(inv(evaluate(predofs,shapefuns))) linear_combination(change,predofs) # i.e. transpose(change)*predofs """ function compute_dofs(predofs,shapefuns) - change = inv(evaluate(predofs,shapefuns)) + change = transpose(inv(evaluate(predofs,shapefuns))) linear_combination(change,predofs) end From 7b6bfb00868e72aa4e5d1a1548485ecf93d0befd Mon Sep 17 00:00:00 2001 From: JordiManyer Date: Thu, 2 Jan 2025 17:21:20 +1100 Subject: [PATCH 074/105] Minor --- docs/src/Adaptivity.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/docs/src/Adaptivity.md b/docs/src/Adaptivity.md index 5a698528a..04e0b3d62 100644 --- a/docs/src/Adaptivity.md +++ b/docs/src/Adaptivity.md @@ -156,6 +156,4 @@ When a cell is refined, we need to be able to evaluate the fields defined on the ```@docs FineToCoarseField -FineToCoarseDofBasis -FineToCoarseRefFE ``` From 00620d94ef538605f4257cce8c55eba36ba94028 Mon Sep 17 00:00:00 2001 From: JordiManyer Date: Fri, 3 Jan 2025 08:53:21 +1100 Subject: [PATCH 075/105] Minor --- src/ReferenceFEs/Dofs.jl | 21 +++++++++---------- .../CartesianRefinementTests.jl | 4 ---- 2 files changed, 10 insertions(+), 15 deletions(-) diff --git a/src/ReferenceFEs/Dofs.jl b/src/ReferenceFEs/Dofs.jl index 1a9fb3025..2e8d42b07 100644 --- a/src/ReferenceFEs/Dofs.jl +++ b/src/ReferenceFEs/Dofs.jl @@ -1,9 +1,9 @@ abstract type Dof <: Map end """ - struct LinearCombinationDofVector{T} <: AbstractVector{Dof} - values::Matrix{T} - dofs::AbstractVector{<:Dof} + struct LinearCombinationDofVector{T<:Dof,V,F} <: AbstractVector{T} + values :: V + dofs :: F end Type that implements a dof basis (a) as the linear combination of a dof basis @@ -13,8 +13,8 @@ dof values are next mapped to dof basis (a) applying a change of basis (field Fields: -- `values::Matrix{T}` the matrix of the change from dof basis (b) to (a) -- `dofs::AbstractVector{<:Dof}` A type representing dof basis (b) +- `values::AbstractMatrix{<:Number}` the matrix of the change from dof basis (b) to (a) +- `dofs::AbstractVector{T}` A type representing dof basis (b), with `T<:Dof` """ struct LinearCombinationDofVector{T,V,F} <: AbstractVector{T} values::V @@ -30,9 +30,9 @@ struct LinearCombinationDofVector{T,V,F} <: AbstractVector{T} end end -@inline Base.size(a::LinearCombinationDofVector) = size(a.values,2) -@inline Base.IndexStyle(::LinearCombinationDofVector) = IndexLinear() -@inline Base.getindex(::LinearCombinationDofVector{T},::Integer) where T = T() +Base.size(a::LinearCombinationDofVector) = size(a.values,2) +Base.IndexStyle(::LinearCombinationDofVector) = IndexLinear() +Base.getindex(::LinearCombinationDofVector{T},::Integer) where T = T() function linear_combination(a::AbstractMatrix{<:Number}, b::AbstractVector{<:Dof}) LinearCombinationDofVector(a,b) @@ -86,9 +86,8 @@ struct MappedDofBasis{T<:Dof,MT,BT,A} <: AbstractVector{T} end Base.size(b::MappedDofBasis) = size(b.dofs) - -# Technically wrong, but the individual dofs are fake anyway -Base.getindex(b::MappedDofBasis, i) = getindex(b.dofs,i) +Base.IndexStyle(::MappedDofBasis) = IndexLinear() +Base.getindex(b::MappedDofBasis, i) = T() function Arrays.return_cache(b::MappedDofBasis, fields) f_cache = return_cache(b.F,fields,b.args...) diff --git a/test/AdaptivityTests/CartesianRefinementTests.jl b/test/AdaptivityTests/CartesianRefinementTests.jl index 1f91dffce..8c9b6675f 100644 --- a/test/AdaptivityTests/CartesianRefinementTests.jl +++ b/test/AdaptivityTests/CartesianRefinementTests.jl @@ -40,8 +40,6 @@ function test_grid_transfers(D,model,parent,order) U_f = TrialFESpace(V_f,sol) V_c = TestFESpace(parent,reffe;dirichlet_tags="boundary") U_c = TrialFESpace(V_c,sol) - V_c_fast = TestFESpace(parent,rrules,reffe;dirichlet_tags="boundary") - U_c_fast = TrialFESpace(V_c_fast,sol) # CellField: Coarse -> Fine cf_c_phy = CellField(sol,ctrian) @@ -80,14 +78,12 @@ function test_grid_transfers(D,model,parent,order) uh_c_inter = interpolate(uh_f,U_c) uh_c_inter2 = interpolate_everywhere(uh_f,U_c) uh_c_inter3 = interpolate_dirichlet(uh_f,U_c) - uh_c_inter4 = interpolate(uh_f,U_c_fast) @test l2_error(uh_f,uh_f_inter,dΩ_f) < 1.e-8 @test l2_error(uh_f,uh_f_inter2,dΩ_f) < 1.e-8 @test l2_error(uh_c,uh_c_inter,dΩ_c) < 1.e-8 @test l2_error(uh_c,uh_c_inter2,dΩ_c) < 1.e-8 - @test l2_error(uh_c,uh_c_inter4,dΩ_c) < 1.e-8 # Coarse FEFunction -> Fine FEFunction, by projection af(u,v) = ∫(v⋅u)*dΩ_f From d8ce6837326a14dcb64b32be68b81fe98b2cb48f Mon Sep 17 00:00:00 2001 From: JordiManyer Date: Fri, 3 Jan 2025 08:55:01 +1100 Subject: [PATCH 076/105] Added deprecated code --- .../deprecated/FineToCoarseReferenceFEs.jl | 132 ++++++++++++++++++ 1 file changed, 132 insertions(+) create mode 100644 src/Adaptivity/deprecated/FineToCoarseReferenceFEs.jl diff --git a/src/Adaptivity/deprecated/FineToCoarseReferenceFEs.jl b/src/Adaptivity/deprecated/FineToCoarseReferenceFEs.jl new file mode 100644 index 000000000..f36ad5793 --- /dev/null +++ b/src/Adaptivity/deprecated/FineToCoarseReferenceFEs.jl @@ -0,0 +1,132 @@ +""" +""" +struct FineToCoarseDofBasis{T,A,B,C} <: AbstractVector{T} + dof_basis :: A + rrule :: B + child_ids :: C + + function FineToCoarseDofBasis(dof_basis::AbstractVector{T},rrule::RefinementRule) where {T<:Dof} + nodes = get_nodes(dof_basis) + child_ids = map(x -> x_to_cell(rrule,x),nodes) + + A = typeof(dof_basis) + B = typeof(rrule) + C = typeof(child_ids) + new{T,A,B,C}(dof_basis,rrule,child_ids) + end +end + +Base.size(a::FineToCoarseDofBasis) = size(a.dof_basis) +Base.axes(a::FineToCoarseDofBasis) = axes(a.dof_basis) +Base.getindex(a::FineToCoarseDofBasis,i::Integer) = getindex(a.dof_basis,i) +Base.IndexStyle(a::FineToCoarseDofBasis) = IndexStyle(a.dof_basis) + +ReferenceFEs.get_nodes(a::FineToCoarseDofBasis) = get_nodes(a.dof_basis) + +# Default behaviour +Arrays.return_cache(b::FineToCoarseDofBasis,field) = return_cache(b.dof_basis,field) +Arrays.evaluate!(cache,b::FineToCoarseDofBasis,field) = evaluate!(cache,b.dof_basis,field) + +# Spetialized behaviour +function Arrays.return_cache(s::FineToCoarseDofBasis{T,<:LagrangianDofBasis},field::FineToCoarseField) where T + b = s.dof_basis + cf = return_cache(field,b.nodes,s.child_ids) + vals = evaluate!(cf,field,b.nodes,s.child_ids) + ndofs = length(b.dof_to_node) + r = ReferenceFEs._lagr_dof_cache(vals,ndofs) + c = CachedArray(r) + return (c, cf) +end + +function Arrays.evaluate!(cache,s::FineToCoarseDofBasis{T,<:LagrangianDofBasis},field::FineToCoarseField) where T + c, cf = cache + b = s.dof_basis + vals = evaluate!(cf,field,b.nodes,s.child_ids) + ndofs = length(b.dof_to_node) + T2 = eltype(vals) + ncomps = num_indep_components(T2) + @check ncomps == num_indep_components(eltype(b.node_and_comp_to_dof)) """\n + Unable to evaluate LagrangianDofBasis. The number of components of the + given Field does not match with the LagrangianDofBasis. + If you are trying to interpolate a function on a FESpace make sure that + both objects have the same value type. + For instance, trying to interpolate a vector-valued function on a scalar-valued FE space + would raise this error. + """ + ReferenceFEs._evaluate_lagr_dof!(c,vals,b.node_and_comp_to_dof,ndofs,ncomps) +end + +function Arrays.return_cache(s::FineToCoarseDofBasis{T,<:MomentBasedDofBasis},field::FineToCoarseField) where T + b = s.dof_basis + cf = return_cache(field,b.nodes,s.child_ids) + vals = evaluate!(cf,field,b.nodes,s.child_ids) + ndofs = num_dofs(b) + r = ReferenceFEs._moment_dof_basis_cache(vals,ndofs) + c = CachedArray(r) + return (c, cf) +end + +function Arrays.evaluate!(cache,s::FineToCoarseDofBasis{T,<:MomentBasedDofBasis},field::FineToCoarseField) where T + c, cf = cache + b = s.dof_basis + vals = evaluate!(cf,field,b.nodes,s.child_ids) + dofs = c.array + ReferenceFEs._eval_moment_dof_basis!(dofs,vals,b) + dofs +end + + +""" + Wrapper for a ReferenceFE which is specialised for + efficiently evaluating FineToCoarseFields. +""" +struct FineToCoarseRefFE{T,D,A} <: ReferenceFE{D} + reffe :: T + dof_basis :: A + + function FineToCoarseRefFE(reffe::ReferenceFE{D},dof_basis::FineToCoarseDofBasis) where D + T = typeof(reffe) + A = typeof(dof_basis) + new{T,D,A}(reffe,dof_basis) + end +end + +ReferenceFEs.num_dofs(reffe::FineToCoarseRefFE) = num_dofs(reffe.reffe) +ReferenceFEs.get_polytope(reffe::FineToCoarseRefFE) = get_polytope(reffe.reffe) +ReferenceFEs.get_prebasis(reffe::FineToCoarseRefFE) = get_prebasis(reffe.reffe) +ReferenceFEs.get_dof_basis(reffe::FineToCoarseRefFE) = reffe.dof_basis +ReferenceFEs.Conformity(reffe::FineToCoarseRefFE) = Conformity(reffe.reffe) +ReferenceFEs.get_face_dofs(reffe::FineToCoarseRefFE) = get_face_dofs(reffe.reffe) +ReferenceFEs.get_shapefuns(reffe::FineToCoarseRefFE) = get_shapefuns(reffe.reffe) +ReferenceFEs.get_metadata(reffe::FineToCoarseRefFE) = get_metadata(reffe.reffe) +ReferenceFEs.get_orders(reffe::FineToCoarseRefFE) = get_orders(reffe.reffe) +ReferenceFEs.get_order(reffe::FineToCoarseRefFE) = get_order(reffe.reffe) + +ReferenceFEs.Conformity(reffe::FineToCoarseRefFE,sym::Symbol) = Conformity(reffe.reffe,sym) +ReferenceFEs.get_face_own_dofs(reffe::FineToCoarseRefFE,conf::Conformity) = get_face_own_dofs(reffe.reffe,conf) + + +function ReferenceFEs.ReferenceFE(p::Polytope,rrule::RefinementRule,name::ReferenceFEName,order) + FineToCoarseRefFE(p,rrule,name,Float64,order) +end + +function ReferenceFEs.ReferenceFE(p::Polytope,rrule::RefinementRule,name::ReferenceFEName,::Type{T},order) where T + FineToCoarseRefFE(p,rrule,name,T,order) +end + +function FineToCoarseRefFE(p::Polytope,rrule::RefinementRule,name::ReferenceFEName,::Type{T},order) where T + @check p == get_polytope(rrule) + reffe = ReferenceFE(p,name,T,order) + dof_basis = FineToCoarseDofBasis(get_dof_basis(reffe),rrule) + return FineToCoarseRefFE(reffe,dof_basis) +end + +# FESpaces constructors + +function FESpaces.TestFESpace(model::DiscreteModel,rrules::AbstractVector{<:RefinementRule},reffe::Tuple{<:ReferenceFEName,Any,Any};kwargs...) + @check num_cells(model) == length(rrules) + @check all(CompressedArray(get_polytopes(model),get_cell_type(model)) .== lazy_map(get_polytope,rrules)) + basis, reffe_args, reffe_kwargs = reffe + reffes = lazy_map(rr -> ReferenceFE(get_polytope(rr),rr,basis,reffe_args...;reffe_kwargs...),rrules) + return TestFESpace(model,reffes;kwargs...) +end From b3002db1e40361aee61821361bfa06570d66b7d7 Mon Sep 17 00:00:00 2001 From: JordiManyer Date: Fri, 3 Jan 2025 09:05:57 +1100 Subject: [PATCH 077/105] Optimised linear_combination for Diagonal matrices --- src/Fields/FieldArrays.jl | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/src/Fields/FieldArrays.jl b/src/Fields/FieldArrays.jl index 4eba60280..485de481b 100644 --- a/src/Fields/FieldArrays.jl +++ b/src/Fields/FieldArrays.jl @@ -368,6 +368,16 @@ function evaluate!(cache,k::LinearCombinationMap{Colon},v::AbstractMatrix,fx::Ab r end +function evaluate!(cache,k::LinearCombinationMap{Colon},v::LinearAlgebra.Diagonal,fx::AbstractVector) + @check length(fx) == size(v,1) + setsize!(cache,(size(v,2),)) + r = cache.array + @inbounds for j in eachindex(fx) + r[j] = outer(fx[j],v.diag[j]) + end + r +end + function return_cache(k::LinearCombinationMap{Colon},v::AbstractMatrix,fx::AbstractMatrix) vf = testitem(fx) vv = testitem(v) @@ -392,6 +402,18 @@ function evaluate!(cache,k::LinearCombinationMap{Colon},v::AbstractMatrix,fx::Ab r end +function evaluate!(cache,k::LinearCombinationMap{Colon},v::LinearAlgebra.Diagonal,fx::AbstractMatrix) + @check size(fx,2) == size(v,1) + setsize!(cache,(size(fx,1),size(v,2))) + r = cache.array + @inbounds for p in 1:size(fx,1) + for j in 1:size(fx,2) + r[p,j] = outer(fx[p,j],v.diag[j]) + end + end + r +end + # Optimizing transpose testitem(a::Transpose{<:Field}) = testitem(a.parent) evaluate!(cache,k::Broadcasting{typeof(∇)},a::Transpose{<:Field}) = transpose(k(a.parent)) From b741a9a4f169212c785fe494dbaceb6809baa296 Mon Sep 17 00:00:00 2001 From: JordiManyer Date: Fri, 3 Jan 2025 09:41:50 +1100 Subject: [PATCH 078/105] Minor --- src/FESpaces/Pullbacks.jl | 33 +++++++++++-------- .../EdgeBasedRefinementTests.jl | 4 --- 2 files changed, 19 insertions(+), 18 deletions(-) diff --git a/src/FESpaces/Pullbacks.jl b/src/FESpaces/Pullbacks.jl index 8afd972a6..26386fc1d 100644 --- a/src/FESpaces/Pullbacks.jl +++ b/src/FESpaces/Pullbacks.jl @@ -53,23 +53,28 @@ function get_cell_pushforward( return CoVariantPiolaMap(), change, (Jt,) end -# SignFlipMap +# NormalSignMap -struct SignFlipMap{T} <: Map +""" + struct NormalSignMap <: Map + ... + end +""" +struct NormalSignMap{T} <: Map model::T facet_owners::Vector{Int32} end -function SignFlipMap(model) +function NormalSignMap(model) facet_owners = compute_facet_owners(model) - SignFlipMap(model,facet_owners) + NormalSignMap(model,facet_owners) end -function return_value(k::SignFlipMap,reffe,facet_own_dofs,cell) +function return_value(k::NormalSignMap,reffe,facet_own_dofs,cell) Diagonal(fill(one(Float64), num_dofs(reffe))) end -function return_cache(k::SignFlipMap,reffe,facet_own_dofs,cell) +function return_cache(k::NormalSignMap,reffe,facet_own_dofs,cell) model = k.model Dc = num_cell_dims(model) topo = get_grid_topology(model) @@ -80,25 +85,25 @@ function return_cache(k::SignFlipMap,reffe,facet_own_dofs,cell) return cell_facets, cell_facets_cache, CachedVector(Float64) end -function evaluate!(cache,k::SignFlipMap,reffe,facet_own_dofs,cell) - cell_facets,cell_facets_cache,sign_flip_cache = cache +function evaluate!(cache,k::NormalSignMap,reffe,facet_own_dofs,cell) + cell_facets,cell_facets_cache,dof_sign_cache = cache facet_owners = k.facet_owners - setsize!(sign_flip_cache, (num_dofs(reffe),)) - sign_flip = sign_flip_cache.array - fill!(sign_flip, one(eltype(sign_flip))) + setsize!(dof_sign_cache, (num_dofs(reffe),)) + dof_sign = dof_sign_cache.array + fill!(dof_sign, one(eltype(dof_sign))) facets = getindex!(cell_facets_cache,cell_facets,cell) for (lfacet,facet) in enumerate(facets) owner = facet_owners[facet] if owner != cell for dof in facet_own_dofs[lfacet] - sign_flip[dof] = -1.0 + dof_sign[dof] = -1.0 end end end - return Diagonal(sign_flip) + return Diagonal(dof_sign) end function get_sign_flip(model::DiscreteModel{Dc}, cell_reffe) where Dc @@ -106,7 +111,7 @@ function get_sign_flip(model::DiscreteModel{Dc}, cell_reffe) where Dc get_facet_own_dofs(reffe) = view(get_face_own_dofs(reffe),get_dimrange(get_polytope(reffe),Dc-1)) cell_facet_own_dofs = lazy_map(get_facet_own_dofs, cell_reffe) cell_ids = IdentityVector(Int32(num_cells(model))) - return lazy_map(SignFlipMap(model), cell_reffe, cell_facet_own_dofs, cell_ids) + return lazy_map(NormalSignMap(model), cell_reffe, cell_facet_own_dofs, cell_ids) end function compute_facet_owners(model::DiscreteModel{Dc}) where {Dc} diff --git a/test/AdaptivityTests/EdgeBasedRefinementTests.jl b/test/AdaptivityTests/EdgeBasedRefinementTests.jl index d68ec1cad..929b26556 100644 --- a/test/AdaptivityTests/EdgeBasedRefinementTests.jl +++ b/test/AdaptivityTests/EdgeBasedRefinementTests.jl @@ -38,8 +38,6 @@ function test_grid_transfers(parent,model,order) U_f = TrialFESpace(V_f,sol) V_c = TestFESpace(parent,reffe;dirichlet_tags="boundary") U_c = TrialFESpace(V_c,sol) - V_c_fast = TestFESpace(parent,rrules,reffe;dirichlet_tags="boundary") - U_c_fast = TrialFESpace(V_c_fast,sol) # CellField: Coarse -> Fine cf_c_phy = CellField(sol,ctrian) @@ -78,14 +76,12 @@ function test_grid_transfers(parent,model,order) uh_c_inter = interpolate(uh_f,U_c) uh_c_inter2 = interpolate_everywhere(uh_f,U_c) uh_c_inter3 = interpolate_dirichlet(uh_f,U_c) - uh_c_inter4 = interpolate(uh_f,U_c_fast) @test l2_error(uh_f,uh_f_inter,dΩ_f) < 1.e-8 @test l2_error(uh_f,uh_f_inter2,dΩ_f) < 1.e-8 @test l2_error(uh_c,uh_c_inter,dΩ_c) < 1.e-8 @test l2_error(uh_c,uh_c_inter2,dΩ_c) < 1.e-8 - @test l2_error(uh_c,uh_c_inter4,dΩ_c) < 1.e-8 # Coarse FEFunction -> Fine FEFunction, by projection af(u,v) = ∫(v⋅u)*dΩ_f From ba8261ac153e2ad9120a9c1c3bb82f3e93c9d762 Mon Sep 17 00:00:00 2001 From: JordiManyer Date: Fri, 3 Jan 2025 10:29:10 +1100 Subject: [PATCH 079/105] HHJ reference FEs --- src/ReferenceFEs/AWRefFEs.jl | 4 +- src/ReferenceFEs/HHJRefFEs.jl | 70 ++++++++++++++++++++++++++++++++ src/ReferenceFEs/Pullbacks.jl | 76 ++++++++++++++++------------------- 3 files changed, 107 insertions(+), 43 deletions(-) create mode 100644 src/ReferenceFEs/HHJRefFEs.jl diff --git a/src/ReferenceFEs/AWRefFEs.jl b/src/ReferenceFEs/AWRefFEs.jl index a2c4810e8..72a9a527f 100644 --- a/src/ReferenceFEs/AWRefFEs.jl +++ b/src/ReferenceFEs/AWRefFEs.jl @@ -57,11 +57,11 @@ function ArnoldWintherRefFE(::Type{T},p::Polytope,order::Integer) where T end function ReferenceFE(p::Polytope,::ArnoldWinther, order) - BDMRefFE(Float64,p,order) + ArnoldWintherRefFE(Float64,p,order) end function ReferenceFE(p::Polytope,::ArnoldWinther,::Type{T}, order) where T - BDMRefFE(T,p,order) + ArnoldWintherRefFE(T,p,order) end function Conformity(reffe::GenericRefFE{ArnoldWinther},sym::Symbol) diff --git a/src/ReferenceFEs/HHJRefFEs.jl b/src/ReferenceFEs/HHJRefFEs.jl new file mode 100644 index 000000000..9c0c691e5 --- /dev/null +++ b/src/ReferenceFEs/HHJRefFEs.jl @@ -0,0 +1,70 @@ + +struct HellanHerrmannJhonson <: PushforwardRefFE end + +const hhj = HellanHerrmannJhonson() + +Pushforward(::Type{<:HellanHerrmannJhonson}) = DoubleContraVariantPiolaMap() + +""" + struct HellanHerrmannJhonson <: PushforwardRefFE end + HellanHerrmannJhonsonRefFE(::Type{T},p::Polytope,order::Integer) where T + +Hellan-Herrmann-Jhonson reference finite element. + +References: + +- `The Hellan-Herrmann-Johnson method with curved elements`, Arnold and Walker (2020) + +""" +function HellanHerrmannJhonsonRefFE(::Type{T},p::Polytope,order::Integer) where T + @assert p == TRI "HellanHerrmannJhonson Reference FE only defined for TRIangles" + + VT = SymTensorValue{2,T} + prebasis = MonomialBasis{2}(VT,order,Polynomials._p_filter) + fb = MonomialBasis{D-1}(T,order,Polynomials._p_filter) + cb = MonomialBasis{2}(VT,order-1,Polynomials._p_filter) + + function cmom(φ,μ,ds) # Cell and Node moment function: σ_K(φ,μ) = ∫(φ:μ)dK + Broadcasting(Operation(⊙))(φ,μ) + end + function fmom(φ,μ,ds) # Face moment function (normal) : σ_F(φ,μ) = ∫((n·φ·n)*μ)dF + n = get_facet_normal(ds) + φn = Broadcasting(Operation(⋅))(φ,n) + nφn = Broadcasting(Operation(⋅))(n,φn) + Broadcasting(Operation(*))(nφn,μ) + end + + moments = Tuple[ + (get_dimrange(p,1),fmom,fb), # Face moments + (get_dimrange(p,2),cmom,cb) # Cell moments + ] + + return MomentBasedReferenceFE(HellanHerrmannJhonson(),p,prebasis,moments,DivConformity()) +end + +function ReferenceFE(p::Polytope,::HellanHerrmannJhonson, order) + HellanHerrmannJhonsonRefFE(Float64,p,order) +end + +function ReferenceFE(p::Polytope,::HellanHerrmannJhonson,::Type{T}, order) where T + HellanHerrmannJhonsonRefFE(T,p,order) +end + +function Conformity(reffe::GenericRefFE{HellanHerrmannJhonson},sym::Symbol) + hdiv = (:Hdiv,:HDiv) + if sym == :L2 + L2Conformity() + elseif sym in hdiv + DivConformity() + else + @unreachable """\n + It is not possible to use conformity = $sym on a HellanHerrmannJhonson reference FE. + + Possible values of conformity for this reference fe are $((:L2, hdiv...)). + """ + end +end + +function get_face_own_dofs(reffe::GenericRefFE{HellanHerrmannJhonson}, conf::DivConformity) + get_face_dofs(reffe) +end diff --git a/src/ReferenceFEs/Pullbacks.jl b/src/ReferenceFEs/Pullbacks.jl index bbdf1ddda..1a825b460 100644 --- a/src/ReferenceFEs/Pullbacks.jl +++ b/src/ReferenceFEs/Pullbacks.jl @@ -61,18 +61,6 @@ Represents the inverse of a pushforward map F*, defined as where - V̂ is a function space on the reference cell K̂ and - V is a function space on the physical cell K. - -# Note: - -Given a pushforward F*, we provide a default implementation for the inverse (F*)^-1 that -is not optimal (and I think wrong for nonlinear geometries???). - -For better performance, the user should overload the following methods for each -specific pushforward type: - -- `return_cache(k::InversePushforward{PF}, v_phys::Number, args...)` -- `evaluate!(cache, k::InversePushforward{PF}, v_phys::Number, args...)` - """ const InversePushforward{PF} = InverseMap{PF} where PF <: Pushforward @@ -82,23 +70,6 @@ function Arrays.lazy_map( lazy_map(Broadcasting(Operation(k)), phys_cell_fields, pf_args...) end -function Arrays.return_cache( - k::InversePushforward, v_phys::Number, args... -) - mock_basis(::VectorValue{D,T}) where {D,T} = one(TensorValue{D,D,T}) - v_ref_basis = mock_basis(v_phys) - pf_cache = return_cache(inverse_map(k),v_ref_basis,args...) - return v_ref_basis, pf_cache -end - -function Arrays.evaluate!( - cache, k::InversePushforward, v_phys::Number, args... -) - v_ref_basis, pf_cache = cache - change = evaluate!(pf_cache,inverse_map(k),v_ref_basis,args...) - return v_phys⋅inv(change) -end - function evaluate!( cache, k::InversePushforward, f_phys::AbstractVector{<:Field}, args... ) @@ -185,12 +156,6 @@ function evaluate!( return v_ref ⋅ (idetJ * Jt) end -function return_cache( - ::InversePushforward{ContraVariantPiolaMap}, v_phys::Number, Jt::Number -) - nothing -end - function evaluate!( cache, ::InversePushforward{ContraVariantPiolaMap}, v_phys::Number, Jt::Number ) @@ -229,17 +194,46 @@ struct CoVariantPiolaMap <: Pushforward end function evaluate!( cache, ::CoVariantPiolaMap, v_ref::Number, Jt::Number ) - return v_ref ⋅ transpose(inv(Jt)) + return v_ref ⋅ transpose(pinvJt(Jt)) +end + +function evaluate!( + cache, ::InversePushforward{CoVariantPiolaMap}, v_phys::Number, Jt::Number +) + return v_phys ⋅ transpose(Jt) end -function return_cache( - ::InversePushforward{CoVariantPiolaMap}, v_phys::Number, Jt::Number +# DoubleContraVariantPiolaMap + +struct DoubleContraVariantPiolaMap <: Pushforward end + +function evaluate!( + cache, ::DoubleContraVariantPiolaMap, v_ref::Number, Jt::Number ) - return nothing + _Jt = meas(Jt) * Jt + return transpose(_Jt) ⋅ v_ref ⋅ _Jt end function evaluate!( - cache, ::InversePushforward{CoVariantPiolaMap}, v_phys::Number, Jt::Number + cache, ::InversePushforward{DoubleContraVariantPiolaMap}, v_phys::Number, Jt::Number ) - return v_phys ⋅ transpose(Jt) + iJt = meas(Jt) * pinvJt(Jt) + return transpose(iJt) ⋅ v_phys ⋅ iJt +end + +# DoubleCoVariantPiolaMap + +struct DoubleCoVariantPiolaMap <: Pushforward end + +function evaluate!( + cache, ::DoubleCoVariantPiolaMap, v_ref::Number, Jt::Number +) + iJt = pinvJt(Jt) + return iJt ⋅ v_ref ⋅ transpose(iJt) +end + +function evaluate!( + cache, ::InversePushforward{DoubleCoVariantPiolaMap}, v_phys::Number, Jt::Number +) + return Jt ⋅ v_phys ⋅ transpose(Jt) end From f4c35eac65d3a2af30d4903250c6d158815053d9 Mon Sep 17 00:00:00 2001 From: JordiManyer Date: Fri, 3 Jan 2025 11:53:57 +1100 Subject: [PATCH 080/105] Minor display bugfix --- src/ReferenceFEs/Dofs.jl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/ReferenceFEs/Dofs.jl b/src/ReferenceFEs/Dofs.jl index 2e8d42b07..546afc1aa 100644 --- a/src/ReferenceFEs/Dofs.jl +++ b/src/ReferenceFEs/Dofs.jl @@ -30,7 +30,7 @@ struct LinearCombinationDofVector{T,V,F} <: AbstractVector{T} end end -Base.size(a::LinearCombinationDofVector) = size(a.values,2) +Base.size(a::LinearCombinationDofVector) = (size(a.values,2),) Base.IndexStyle(::LinearCombinationDofVector) = IndexLinear() Base.getindex(::LinearCombinationDofVector{T},::Integer) where T = T() @@ -87,7 +87,7 @@ end Base.size(b::MappedDofBasis) = size(b.dofs) Base.IndexStyle(::MappedDofBasis) = IndexLinear() -Base.getindex(b::MappedDofBasis, i) = T() +Base.getindex(::MappedDofBasis{T}, ::Integer) where T = T() function Arrays.return_cache(b::MappedDofBasis, fields) f_cache = return_cache(b.F,fields,b.args...) From ba4b33c281da282c8a975578ce530eb2cba816e5 Mon Sep 17 00:00:00 2001 From: Antoine Marteau Date: Fri, 3 Jan 2025 15:43:42 +1100 Subject: [PATCH 081/105] various Polynomials improvements - homogenized names and updated exported names - updated news.md - handled deprecated APIs - added some docstrings - updated tests with new APIs --- NEWS.md | 32 +++- src/Polynomials/BernsteinBases.jl | 11 +- src/Polynomials/ChebyshevBases.jl | 15 +- src/Polynomials/CompWiseTensorPolyBases.jl | 48 ++++-- src/Polynomials/Deprecated.jl | 51 +++++++ src/Polynomials/LegendreBases.jl | 13 +- src/Polynomials/ModalC0Bases.jl | 8 +- src/Polynomials/MonomialBases.jl | 16 +- ...ebasisOnSimplex.jl => NedelecPolyBases.jl} | 50 +++---- src/Polynomials/PolynomialInterfaces.jl | 2 - src/Polynomials/Polynomials.jl | 32 +++- ...PolyBases.jl => RaviartThomasPolyBases.jl} | 30 ++-- ...PolynomialBases.jl => UniformPolyBases.jl} | 140 +++++++++++------- src/ReferenceFEs/BDMRefFEs.jl | 2 +- src/ReferenceFEs/NedelecRefFEs.jl | 12 +- src/ReferenceFEs/RaviartThomasRefFEs.jl | 6 +- src/ReferenceFEs/ReferenceFEs.jl | 4 - src/ReferenceFEs/deprecated/BDMRefFEs.jl | 4 +- ...alBasesTests.jl => PCurlGradBasesTests.jl} | 6 +- test/PolynomialsTests/PGradBasesTests.jl | 49 ++++++ ...alBasesTests.jl => QCurlGradBasesTests.jl} | 8 +- ...nomialBasesTests.jl => QGradBasesTests.jl} | 43 +----- test/PolynomialsTests/runtests.jl | 8 +- 23 files changed, 363 insertions(+), 227 deletions(-) create mode 100644 src/Polynomials/Deprecated.jl rename src/Polynomials/{NedelecPrebasisOnSimplex.jl => NedelecPolyBases.jl} (81%) rename src/Polynomials/{NonTensorRTPolyBases.jl => RaviartThomasPolyBases.jl} (83%) rename src/Polynomials/{TensorPolynomialBases.jl => UniformPolyBases.jl} (66%) rename test/PolynomialsTests/{PCurlGradMonomialBasesTests.jl => PCurlGradBasesTests.jl} (91%) create mode 100644 test/PolynomialsTests/PGradBasesTests.jl rename test/PolynomialsTests/{QCurlGradMonomialBasesTests.jl => QCurlGradBasesTests.jl} (90%) rename test/PolynomialsTests/{QGradMonomialBasesTests.jl => QGradBasesTests.jl} (68%) diff --git a/NEWS.md b/NEWS.md index a3e6ace1f..7045d6aac 100644 --- a/NEWS.md +++ b/NEWS.md @@ -11,10 +11,36 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Added AMR-related methods `mark` and `estimate` to `Adaptivity` module. Implemented Dorfler marking strategy. Since PR[#1063](https://github.com/gridap/Gridap.jl/pull/1063). +- Documentation and refactoring of Gridap.Polynomials. Since PR[#TODO](https://github.com/gridap/Gridap.jl/pull/#TODO). +- Three new families of polynomial bases in addition to `Monomial`, `Legendre` (former `Jacobi`) and `ModalC0`: `Chebyshev`, `Bernstein` and `Jacobi` +- `MonomialBasis` and `Q[Curl]GradMonomialBasis` have been generalized to `Legendre`, `Chebyshev`, `Bernstein` and `Jacobi` using the new `UniformPolyBasis` and `CompWiseTensorPolyBasis` respectively. +- `PCurlGradMonomialBasis` has been generalized to `Legendre`, `Chebyshev` and `Jacobi` using the new `RaviartThomasPolyBasis`. +- New aliases and high level constructor for `UniformPolyBasis` (former MonomialBasis): `MonomialBasis`, `LegendreBasis`, `ChebyshevBasis`, `BernsteinBasis`, `JacobiBasis`. +- New high level constructors for Nedelec and Raviart-Thomas polynomial bases: + - Nedelec on simplex `PGradBasis(PT<:Polynomial, Val(D), order)` + - Nedelec on n-cubes `QGradBasis(PT<:Polynomial, Val(D), order)` + - Raviart on simplex `PCurlGradBasis(PT<:Polynomial, Val(D), order)` + - Raviart on n-cubes `QCurlGradBasis(PT<:Polynomial, Val(D), order)` + ### Changed -- Existing Jacobi polynomial bases/spaces were renamed to Legendre (which they are). TODO -- `num_terms(b::Q/P(Curl)Grad...)` is deprecated, use `length(b)` instead -- `get_order(b::Q/P(Curl)Grad...)`, now returns the order of the basis, +1 that used to create it. + +- Existing Jacobi polynomial bases/spaces were renamed to Legendre (which they were). +- `Monomial` is now subtype of the new abstract type`Polynomial <: Field` +- `MonomialBasis` is now an alias for `UniformPolyBasis{...,Monomial}` +- All polynomial bases are now subtypes of the new abstract type `PolynomialBasis <: AbstractVector{<:Polynomial}` +- `get_order(b::(Q/P)[Curl]Grad...)`, now returns the order of the basis, +1 than the order parameter passed to the constructor. +- `NedelecPreBasisOnSimplex` is renamed `NedelecPolyBasisOnSimplex` +- `JacobiPolynomial` is renamed `Legendre` and subtypes `Polynomial` +- `JacobiPolynomialBasis` is renamed `LegendreBasis` +- `ModalC0BasisFunction` is renamed `ModalC0` and subtypes `Polynomial` + +### Deprecated + +- `num_terms(f::AbstractVector{<:Field})` in favor of `length(f::PolynomialBasis)` +- `MonomialBasis{D}(args...)` in favor of `MonomialBasis(Val(D), args...)` +- `[P/Q][Curl]GradMonomialBasis{D}(args...)` in favor of `[...]GradBasis(Monomial, Val(D), args...)` +- `NedelecPreBasisOnSimplex{D}(args...)` in favor of `NedelecPolyBasisOnSimplex(Val(D), args...)` +- `JacobiPolynomialBasis{D}(args...)` in favor of `LegendreBasis(Val(D), args...)` ## [0.18.8] - 2024-12-2 diff --git a/src/Polynomials/BernsteinBases.jl b/src/Polynomials/BernsteinBases.jl index fb83de65f..fba1b0b21 100644 --- a/src/Polynomials/BernsteinBases.jl +++ b/src/Polynomials/BernsteinBases.jl @@ -8,11 +8,11 @@ struct Bernstein <: Polynomial end isHierarchical(::Type{Bernstein}) = false """ - BernsteinBasis{D,V,K} = TensorPolynomialBasis{D,V,K,Bernstein} + BernsteinBasis{D,V,K} = UniformPolyBasis{D,V,K,Bernstein} -Alias for Bernstein multivariate scalar' or `Multivalue`'d basis, see [`TensorPolynomialBasis`](@ref). +Alias for Bernstein multivariate scalar' or `Multivalue`'d basis, see [`UniformPolyBasis`](@ref). """ -const BernsteinBasis{D,V,K} = TensorPolynomialBasis{D,V,K,Bernstein} +const BernsteinBasis{D,V,K} = UniformPolyBasis{D,V,K,Bernstein} """ BernsteinBasis(::Val{D}, ::Type{V}, order::Int, terms::Vector) @@ -21,10 +21,7 @@ const BernsteinBasis{D,V,K} = TensorPolynomialBasis{D,V,K,Bernstein} High level constructors of [`BernsteinBasis`](@ref). """ -BernsteinBasis(args...) = TensorPolynomialBasis(Bernstein, args...) - -QGradBernsteinBasis(args...) = QGradBasis(Bernstein, args...) -QCurlGradBernsteinBasis(args...) = QCurlGradBasis(Bernstein, args...) +BernsteinBasis(args...) = UniformPolyBasis(Bernstein, args...) # 1D evaluation implementation diff --git a/src/Polynomials/ChebyshevBases.jl b/src/Polynomials/ChebyshevBases.jl index ab2b79759..729f4f7a0 100644 --- a/src/Polynomials/ChebyshevBases.jl +++ b/src/Polynomials/ChebyshevBases.jl @@ -9,11 +9,11 @@ struct Chebyshev{kind} <: Polynomial end isHierarchical(::Type{<:Chebyshev}) = true """ - ChebyshevBasis{D,V,kind,K} = TensorPolynomialBasis{D,V,K,Chebyshev{kind}} + ChebyshevBasis{D,V,kind,K} = UniformPolyBasis{D,V,K,Chebyshev{kind}} -Alias for Chebyshev multivariate scalar' or `Multivalue`'d basis, see [`TensorPolynomialBasis`](@ref). +Alias for Chebyshev multivariate scalar' or `Multivalue`'d basis, see [`UniformPolyBasis`](@ref). """ -const ChebyshevBasis{D,V,kind,K} = TensorPolynomialBasis{D,V,K,Chebyshev{kind}} +const ChebyshevBasis{D,V,kind,K} = UniformPolyBasis{D,V,K,Chebyshev{kind}} """ ChebyshevBasis(::Val{D}, ::Type{V}, order::Int, terms::Vector; kind=:T) @@ -22,14 +22,9 @@ const ChebyshevBasis{D,V,kind,K} = TensorPolynomialBasis{D,V,K,Chebyshev{kind}} High level constructors of [`ChebyshevBasis`](@ref). """ -ChebyshevBasis(args...; kind=:T) = TensorPolynomialBasis(Chebyshev{kind}, args...) +ChebyshevBasis(args...; kind=:T) = UniformPolyBasis(Chebyshev{kind}, args...) -TensorPolynomialBasis{D}(::Type{Chebyshev{:U}}, args...) where D = @notimplemented "1D evaluation for second kind needed here" - -QGradChebyshevBasis(args...; kind=:T) = QGradBasis(Chebyshev{kind}, args...) -#PGradChebyshevBasis(args...; kind=:T) = PGradBasis(Chebyshev{kind}, args...) -QCurlGradChebyshevBasis(args...; kind=:T) = QCurlGradBasis(Chebyshev{kind}, args...) -PCurlGradChebyshevBasis(args...; kind=:T) = PCurlGradBasis(Chebyshev{kind}, args...) +UniformPolyBasis{D}(::Type{Chebyshev{:U}}, args...) where D = @notimplemented "1D evaluation for second kind needed here" # 1D evaluation implementation diff --git a/src/Polynomials/CompWiseTensorPolyBases.jl b/src/Polynomials/CompWiseTensorPolyBases.jl index b7a43a485..93b72f27a 100644 --- a/src/Polynomials/CompWiseTensorPolyBases.jl +++ b/src/Polynomials/CompWiseTensorPolyBases.jl @@ -28,7 +28,7 @@ struct CompWiseTensorPolyBasis{D,V,K,PT,L} <: PolynomialBasis{D,V,K,PT} msg1 = "The orders matrix rows number must match the number of independent components of V" @check L == num_indep_components(V) msg1 - msg2 = "The Component Wise construction is useless for one component, use TensorPolynomialBasis instead" + msg2 = "The Component Wise construction is useless for one component, use UniformPolyBasis instead" @check L > 1 msg2 @check D > 0 K = maximum(orders) @@ -143,19 +143,40 @@ function _gradient_nd!( end end - k = _comp_wize_set_gradient!(r,i,s,k,Val(l),V) + k = _comp_wize_set_derivative!(r,i,s,k,Val(l),V) end end end -function _comp_wize_set_gradient!( - r::AbstractMatrix{G},i,s,k,l,::Type{<:Real}) where G +""" + _uniform_set_derivative!(r::AbstractMatrix{G},i,s,k,::Type{V}) + +``` +r[i,k] = G(s…) = (Dbᵏ)(xi) +return k+1 +``` + +""" +function _comp_wize_set_derivative!( + r::AbstractMatrix{G},i,s,k,vall,::Type{<:Real}) where G @inbounds r[i,k] = s k+1 end -@generated function _comp_wize_set_gradient!( +""" + _uniform_set_derivative!(r::AbstractMatrix{G},i,s,k,::Type{V}) + +``` +z = zero(s) +r[i,k] = G(z…, ..., z…, s…, z…, ..., z…) = (Dbᵏ)(xi) +return k+1 +``` + +where `s…` is the `l`ᵗʰ set of components. This is the gradient or hessian of +the `k`ᵗʰ basis polynomial, whose nonzero component in `V` is the `l`ᵗʰ. +""" +@generated function _comp_wize_set_derivative!( r::AbstractMatrix{G},i,s,k,::Val{l},::Type{V}) where {G,l,V} N_val_dims = length(size(V)) @@ -174,7 +195,8 @@ end return Expr(:block, body ,:(return k+1)) end -@generated function _comp_wize_set_gradient!( +# See _uniform_set_derivative!(r::AbstractMatrix{G},i,s,k,::Type{V}) where {G,V<:AbstractSymTensorValue{D}} where D +@generated function _comp_wize_set_derivative!( r::AbstractMatrix{G},i,s,k,::Type{V}) where {G,V<:AbstractSymTensorValue{D}} where D @notimplemented @@ -221,7 +243,7 @@ function _hessian_nd!( end end - k = _comp_wize_set_gradient!(r,i,s,k,l,V) + k = _comp_wize_set_derivative!(r,i,s,k,l,V) end end end @@ -250,7 +272,9 @@ b = PCurlGradBasis(Monomial, Val(3), Float64, 2) ``` For more details, see [`CompWiseTensorPolyBasis`](@ref), as `QGradBasis` returns -an instance of `CompWiseTensorPolyBasis{D,VectorValue{D,T},order+1,PT,D}`. +an instance of\\ +`CompWiseTensorPolyBasis{D, VectorValue{D,T}, order+1, PT}` for `D`>1, or\\ +`UniformPolyBasis{1, VectorValue{1,T}, order+1, PT}` for `D`=1. """ function QGradBasis(::Type{PT},::Val{D},::Type{T},order::Int) where {PT,D,T} @check T<:Real "T needs to be <:Real since represents the type of the components of the vector value" @@ -265,7 +289,7 @@ function QGradBasis(::Type{PT},::Val{1},::Type{T},order::Int) where {PT,T} @check T<:Real "T needs to be <:Real since represents the type of the components of the vector value" V = VectorValue{1,T} - TensorPolynomialBasis(PT, Val(1), V, order+1) + UniformPolyBasis(PT, Val(1), V, order+1) end @@ -293,8 +317,8 @@ b = QCurlGradBasis(Bernstein, Val(2), Float64, 3) For more details, see [`CompWiseTensorPolyBasis`](@ref), as `QCurlGradBasis` returns an instance of\\ -`CompWiseTensorPolyBasis{D, VectorValue{D,T}, order+1, PT, D}` for `D`>1, or\\ -`TensorPolynomialBasis{1, VectorValue{1,T}, order+1, PT, D}` for `D`=1. +`CompWiseTensorPolyBasis{D, VectorValue{D,T}, order+1, PT}` for `D`>1, or\\ +`UniformPolyBasis{1, VectorValue{1,T}, order+1, PT}` for `D`=1. """ function QCurlGradBasis(::Type{PT},::Val{D},::Type{T},order::Int) where {PT,D,T} @check T<:Real "T needs to be <:Real since represents the type of the components of the vector value" @@ -309,5 +333,5 @@ function QCurlGradBasis(::Type{PT},::Val{1},::Type{T},order::Int) where {PT,T} @check T<:Real "T needs to be <:Real since represents the type of the components of the vector value" V = VectorValue{1,T} - TensorPolynomialBasis(PT, Val(1), V, order+1) + UniformPolyBasis(PT, Val(1), V, order+1) end diff --git a/src/Polynomials/Deprecated.jl b/src/Polynomials/Deprecated.jl new file mode 100644 index 000000000..edca51e75 --- /dev/null +++ b/src/Polynomials/Deprecated.jl @@ -0,0 +1,51 @@ +@deprecate num_terms(a::PolynomialBasis) length(a) + +@deprecate MonomialBasis{D}(args...) where D MonomialBasis(Val(D), args...) + +struct PGradMonomialBasis{D} + function PGradMonomialBasis() + @unreachable + new{0}() + end +end +@deprecate PGradMonomialBasis{D}(args...) where D PGradBasis(Monomial, Val(D), args...) + +struct PCurlGradMonomialBasis{D} + function PCurlGradMonomialBasis() + @unreachable + new{0}() + end +end +@deprecate PCurlGradMonomialBasis{D}(args...) where D PCurlGradBasis(Monomial, Val(D), args...) + +struct QGradMonomialBasis{D} + function QGradMonomialBasis() + @unreachable + new{0}() + end +end +@deprecate QGradMonomialBasis{D}(args...) where D QGradBasis(Monomial, Val(D), args...) + +struct QCurlGradMonomialBasis{D} + function QCurlGradMonomialBasis() + @unreachable + new{0}() + end +end +@deprecate QCurlGradMonomialBasis{D}(args...) where D QCurlGradBasis(Monomial, Val(D), args...) + +struct NedelecPreBasisOnSimplex{D} + function NedelecPreBasisOnSimplex() + @unreachable + new{0}() + end +end +@deprecate NedelecPreBasisOnSimplex{D}(args...) where D NedelecPolyBasisOnSimplex{D}(args...) + +struct JacobiPolynomialBasis{D} + function JacobiPolynomialBasis() + @unreachable + new{0}() + end +end +@deprecate JacobiPolynomialBasis{D}(args...) where D LegendreBasis(Val(D), args...) diff --git a/src/Polynomials/LegendreBases.jl b/src/Polynomials/LegendreBases.jl index 3aa74b864..491dbd9c0 100644 --- a/src/Polynomials/LegendreBases.jl +++ b/src/Polynomials/LegendreBases.jl @@ -8,11 +8,11 @@ struct Legendre <: Polynomial end isHierarchical(::Type{Legendre}) = true """ - LegendreBasis{D,V,K} = TensorPolynomialBasis{D,V,K,Legendre} + LegendreBasis{D,V,K} = UniformPolyBasis{D,V,K,Legendre} -Alias for Legendre multivariate scalar' or `Multivalue`'d basis, see [`TensorPolynomialBasis`](@ref). +Alias for Legendre multivariate scalar' or `Multivalue`'d basis, see [`UniformPolyBasis`](@ref). """ -const LegendreBasis{D,V,K} = TensorPolynomialBasis{D,V,K,Legendre} +const LegendreBasis{D,V,K} = UniformPolyBasis{D,V,K,Legendre} """ LegendreBasis(::Val{D}, ::Type{V}, order::Int, terms::Vector) @@ -21,12 +21,7 @@ const LegendreBasis{D,V,K} = TensorPolynomialBasis{D,V,K,Legendre} High level constructors of [`LegendreBasis`](@ref). """ -LegendreBasis(args...) = TensorPolynomialBasis(Legendre, args...) - -QGradLegendreBasis(args...) = QGradBasis(Legendre, args...) -#PGradLegendreBasis(args...) = PGradBasis(Legendre, args...) -QCurlGradLegendreBasis(args...) = QCurlGradBasis(Legendre, args...) -PCurlGradLegendreBasis(args...) = PCurlGradBasis(Legendre, args...) +LegendreBasis(args...) = UniformPolyBasis(Legendre, args...) # 1D evaluation implementation diff --git a/src/Polynomials/ModalC0Bases.jl b/src/Polynomials/ModalC0Bases.jl index 798f3937e..f0bbfed89 100644 --- a/src/Polynomials/ModalC0Bases.jl +++ b/src/Polynomials/ModalC0Bases.jl @@ -231,11 +231,11 @@ function _gradient_nd!( end end - k = _set_gradient_mc0!(r,i,s,k,l,V) + k = _set_derivative_mc0!(r,i,s,k,l,V) end end -@inline function _set_gradient_mc0!( +@inline function _set_derivative_mc0!( r::AbstractMatrix{G},i,s,k,l,::Type{<:Real}) where G @inbounds r[i,k] = s @@ -245,7 +245,7 @@ end # Indexing and m definition should be fixed if G contains symmetries, that is # if the code is optimized for symmetric tensor V valued FESpaces # (if gradient_type(V) returned a symmetric higher order tensor type G) -@inline @generated function _set_gradient_mc0!( +@inline @generated function _set_derivative_mc0!( r::AbstractMatrix{G},i1,s,k,l,::Type{V}) where {V,G} # Git blame me for readable non-generated version @notimplementedif num_indep_components(G) != num_components(G) "Not implemented for symmetric Jacobian or Hessian" @@ -318,7 +318,7 @@ function _hessian_nd!( end end - k = _set_gradient_mc0!(r,i,s,k,l,V) + k = _set_derivative_mc0!(r,i,s,k,l,V) end end diff --git a/src/Polynomials/MonomialBases.jl b/src/Polynomials/MonomialBases.jl index 5f0fd033b..32ce2fbf0 100644 --- a/src/Polynomials/MonomialBases.jl +++ b/src/Polynomials/MonomialBases.jl @@ -8,11 +8,11 @@ struct Monomial <: Polynomial end isHierarchical(::Type{Monomial}) = true """ - MonomialBasis{D,V,K} = TensorPolynomialBasis{D,V,K,Monomial} + MonomialBasis{D,V,K} = UniformPolyBasis{D,V,K,Monomial} -Alias for monomial Multivariate scalar' or `Multivalue`'d basis, see [`TensorPolynomialBasis`](@ref). +Alias for monomial Multivariate scalar' or `Multivalue`'d basis, see [`UniformPolyBasis`](@ref). """ -const MonomialBasis{D,V,K} = TensorPolynomialBasis{D,V,K,Monomial} +const MonomialBasis{D,V,K} = UniformPolyBasis{D,V,K,Monomial} """ MonomialBasis(::Val{D}, ::Type{V}, order::Int, terms::Vector) @@ -21,13 +21,11 @@ const MonomialBasis{D,V,K} = TensorPolynomialBasis{D,V,K,Monomial} High level constructors of [`MonomialBasis`](@ref). """ -MonomialBasis(args...) = TensorPolynomialBasis(Monomial, args...) - -QGradMonomialBasis(args...) = QGradBasis(Monomial, args...) -#PGradMonomialBasis(args...) = PGradBasis(Monomial, args...) -QCurlGradMonomialBasis(args...) = QCurlGradBasis(Monomial, args...) -PCurlGradMonomialBasis(args...) = PCurlGradBasis(Monomial, args...) +MonomialBasis(args...) = UniformPolyBasis(Monomial, args...) +function PGradBasis(::Type{Monomial},::Val{D},::Type{T},order::Int) where {D,T} + NedelecPolyBasisOnSimplex{D}(Monomial,T,order) +end # 1D evaluation implementation diff --git a/src/Polynomials/NedelecPrebasisOnSimplex.jl b/src/Polynomials/NedelecPolyBases.jl similarity index 81% rename from src/Polynomials/NedelecPrebasisOnSimplex.jl rename to src/Polynomials/NedelecPolyBases.jl index 1872b082e..97fcf0f22 100644 --- a/src/Polynomials/NedelecPrebasisOnSimplex.jl +++ b/src/Polynomials/NedelecPolyBases.jl @@ -1,32 +1,32 @@ -struct NedelecPrebasisOnSimplex{D,V,K} <: PolynomialBasis{D,V,K,Monomial} +struct NedelecPolyBasisOnSimplex{D,V,K,PT} <: PolynomialBasis{D,V,K,PT} order::Int - function NedelecPrebasisOnSimplex{D}(order::Integer) where D + function NedelecPolyBasisOnSimplex{D}(::Type{PT},::Type{T},order::Integer) where {D,PT,T} + @check T<:Real "T needs to be <:Real since represents the type of the components of the vector value" K = Int(order)+1 - V = VectorValue{D,Float64} - new{D,V,K}(Int(order)) + V = VectorValue{D,T} + new{D,V,K,PT}(Int(order)) end end -function Base.size(a::NedelecPrebasisOnSimplex{D}) where D +function Base.size(a::NedelecPolyBasisOnSimplex{D}) where D k = a.order+1 n = div(k*prod(i->(k+i),2:D),factorial(D-1)) (n,) end function return_cache( - f::NedelecPrebasisOnSimplex{D},x::AbstractVector{<:Point}) where D + f::NedelecPolyBasisOnSimplex{D,V,K,PT},x::AbstractVector{<:Point}) where {D,V,K,PT} + np = length(x) ndofs = length(f) - V = eltype(x) a = zeros(V,(np,ndofs)) - K = get_order(f) - P = MonomialBasis(Val(D),VectorValue{D,Float64},K-1,(e,order)->sum(e)<=order) + P = UniformPolyBasis(PT,Val(D),V,K-1,_p_filter) cP = return_cache(P,x) CachedArray(a), cP, P end function evaluate!( - cache,f::NedelecPrebasisOnSimplex{3},x::AbstractVector{<:Point}) + cache,f::NedelecPolyBasisOnSimplex{3,V},x::AbstractVector{<:Point}) where V ca,cP,P = cache K = get_order(f) np = length(x) @@ -35,7 +35,6 @@ function evaluate!( setsize!(ca,(np,ndofs)) Px = evaluate!(cP,P,x) a = ca.array - V = eltype(x) T = eltype(V) z = zero(T) for (i,xi) in enumerate(x) @@ -72,7 +71,7 @@ function evaluate!( end function evaluate!( - cache,f::NedelecPrebasisOnSimplex{2},x::AbstractVector{<:Point}) + cache,f::NedelecPolyBasisOnSimplex{2,V},x::AbstractVector{<:Point}) where V ca,cP,P = cache K = get_order(f) np = length(x) @@ -80,7 +79,6 @@ function evaluate!( ndofsP = length(P) setsize!(ca,(np,ndofs)) a = ca.array - V = eltype(x) T = eltype(V) z = zero(T) Px = evaluate!(cP,P,x) @@ -105,17 +103,15 @@ function evaluate!( end function return_cache( - g::FieldGradientArray{1,<:NedelecPrebasisOnSimplex{D}}, - x::AbstractVector{<:Point}) where D + g::FieldGradientArray{1,<:NedelecPolyBasisOnSimplex{D,V,K,PT}}, + x::AbstractVector{<:Point}) where {D,V,K,PT} f = g.fa np = length(x) ndofs = length(f) xi = testitem(x) - V = eltype(x) G = gradient_type(V,xi) a = zeros(G,(np,ndofs)) - K = get_order(f) - mb = MonomialBasis(Val(D),VectorValue{D,Float64},K-1,_p_filter) + mb = UniformPolyBasis(PT,Val(D),V,K-1,_p_filter) P = Broadcasting(∇)(mb) cP = return_cache(P,x) CachedArray(a), cP, P @@ -123,8 +119,8 @@ end function evaluate!( cache, - g::FieldGradientArray{1,<:NedelecPrebasisOnSimplex{3}}, - x::AbstractVector{<:Point}) + g::FieldGradientArray{1,<:NedelecPolyBasisOnSimplex{3,V}}, + x::AbstractVector{<:Point}) where V ca,cP,P = cache f = g.fa K = get_order(f) @@ -135,7 +131,6 @@ function evaluate!( fill!(a,zero(eltype(a))) ndofsP = length(P) Px = evaluate!(cP,P,x) - V = eltype(x) T = eltype(V) z = zero(T) for (i,xi) in enumerate(x) @@ -197,8 +192,8 @@ _exp(a,y) = y>0 ? a^y : one(a) function evaluate!( cache, - g::FieldGradientArray{1,<:NedelecPrebasisOnSimplex{2}}, - x::AbstractVector{<:Point}) + g::FieldGradientArray{1,<:NedelecPolyBasisOnSimplex{2,V}}, + x::AbstractVector{<:Point}) where V f = g.fa ca,cP,P = cache K = get_order(f) @@ -207,7 +202,6 @@ function evaluate!( setsize!(ca,(np,ndofs)) a = ca.array fill!(a,zero(eltype(a))) - V = eltype(x) T = eltype(V) z = zero(T) ndofsP = length(P) @@ -257,11 +251,11 @@ b = PGradBasis(Monomial, Val(3), Float64, 2) ``` """ function PGradBasis(::Type{PT},::Val{D},::Type{T},order::Int) where {PT,D,T} + # Although NedelecPolyBasisOnSimplex can be constructed with any PT Bool -where `e` is a tuple of `D` integers containing the exponents of a multivariate monomial. The following filters -are used to select well known polynomial spaces +where `e` is a tuple of `D` integers containing the exponents of a multivariate +monomial. The following filters are used to select well known polynomial spaces -- ℚ space: `(e,order) -> true` +- ℚ space: `(e,order) -> maximum(e) <= order` - ℙ space: `(e,order) -> sum(e) <= order` - "Serendipity" space: `(e,order) -> sum( [ i for i in e if i>1 ] ) <= order` """ -function TensorPolynomialBasis( +function UniformPolyBasis( ::Type{PT}, VD::Val{D}, ::Type{V}, order::Int, filter::Function=_q_filter) where {PT,D,V} orders = tfill(order,VD) - TensorPolynomialBasis(PT,Val(D),V,orders,filter) + UniformPolyBasis(PT,Val(D),V,orders,filter) end # API """ - get_exponents(b::TensorPolynomialBasis) + get_exponents(b::UniformPolyBasis) -Get a vector of tuples with the exponents of all the terms in the basis. +Get a vector of tuples with the exponents of all the terms in the basis of 𝕊. # Example @@ -121,17 +120,17 @@ println(exponents) Tuple{Int,Int}[(0, 0), (1, 0), (2, 0), (0, 1), (1, 1), (2, 1), (0, 2), (1, 2), (2, 2)] ``` """ -function get_exponents(b::TensorPolynomialBasis) +function get_exponents(b::UniformPolyBasis) indexbase = 1 [Tuple(t) .- indexbase for t in b.terms] end """ - get_orders(b::TensorPolynomialBasis) + get_orders(b::UniformPolyBasis) Return the D-tuple of polynomial orders in each dimension """ -function get_orders(b::TensorPolynomialBasis) +function get_orders(b::UniformPolyBasis) b.orders end @@ -168,7 +167,7 @@ end ################################# function _evaluate_nd!( - b::TensorPolynomialBasis{D,V,K,PT}, x, + b::UniformPolyBasis{D,V,K,PT}, x, r::AbstractMatrix{V}, i, c::AbstractMatrix{T}) where {D,V,K,PT,T} @@ -188,22 +187,34 @@ function _evaluate_nd!( @inbounds s *= c[d,ci[d]] end - k = _tensorial_set_value!(r,i,s,k) + k = _uniform_set_value!(r,i,s,k) end end """ - _tensorial_set_value!(r::AbstractMatrix{<:Real},i,s,k) + _uniform_set_value!(r::AbstractMatrix{<:Real},i,s,k) -r[i,k] = s; return k+1 +r[i,k] = s; return k+1 """ -function _tensorial_set_value!(r::AbstractMatrix{<:Real},i,s,k) - #@inbounds r[i][k] = s +function _uniform_set_value!(r::AbstractMatrix{<:Real},i,s,k) @inbounds r[i,k] = s k+1 end -function _tensorial_set_value!(r::AbstractMatrix{V},i,s::T,k) where {V,T} +""" + _uniform_set_value!(r::AbstractMatrix{V},i,s::T,k,l) + +``` +r[i,k] = V(s, 0, ..., 0) +r[i,k+1] = V(0, s, 0, ..., 0) +⋮ +r[i,k+N-1] = V(0, ..., 0, s) +return k+N +``` + +where `N = num_indep_components(V)`. +""" +function _uniform_set_value!(r::AbstractMatrix{V},i,s::T,k) where {V,T} ncomp = num_indep_components(V) z = zero(T) @inbounds for j in 1:ncomp @@ -214,7 +225,7 @@ function _tensorial_set_value!(r::AbstractMatrix{V},i,s::T,k) where {V,T} end function _gradient_nd!( - b::TensorPolynomialBasis{D,V,K,PT}, x, + b::UniformPolyBasis{D,V,K,PT}, x, r::AbstractMatrix{G}, i, c::AbstractMatrix{T}, g::AbstractMatrix{T}, @@ -245,19 +256,43 @@ function _gradient_nd!( end end - k = _tensorial_set_gradient!(r,i,s,k,V) + k = _uniform_set_derivative!(r,i,s,k,V) end end -function _tensorial_set_gradient!( +""" + _uniform_set_derivative!(r::AbstractMatrix{G},i,s,k,::Type{<:Real}) + +``` +r[i,k] = s = (∇bᵏ)(xi); return k+1 +``` + +where bᵏ is the kᵗʰ basis polynomial. Note that `r[i,k]` is a `VectorValue` or +`TensorValue` and `s` a `MVector` or `MMatrix` respectively, of same size. +""" +function _uniform_set_derivative!( r::AbstractMatrix{G},i,s,k,::Type{<:Real}) where G @inbounds r[i,k] = s k+1 end -# TODO comment -@generated function _tensorial_set_gradient!( +""" + _uniform_set_derivative!(r::AbstractMatrix{G},i,s,k,::Type{V}) + +``` +z = zero(s) +r[i,k] = G(s…, z…, ..., z…) = (Dbᵏ )(xi) +r[i,k+1] = G(z…, s…, z…, ..., z…) = (Dbᵏ⁺¹ )(xi) +⋮ +r[i,k+n-1] = G(z…, ..., z…, s…) = (Dbᵏ⁺ⁿ⁻¹)(xi) +return k+n +``` + +Note that `r[i,k]` is a `TensorValue` or `ThirdOrderTensorValue` and `s` a +`MVector` or `MMatrix`. +""" +@generated function _uniform_set_derivative!( r::AbstractMatrix{G},i,s,k,::Type{V}) where {G,V} # Git blame me for readable non-generated version @@ -292,10 +327,7 @@ end # necessary as long as outer(Point, V<:AbstractSymTensorValue)::G does not # return a tensor type G that implements the appropriate symmetries of the # gradient (and hessian) -# -# This is still (independant-)component tensorial as each independent SymTensor -# component holds the same (scalar multivariate) polynomial space. -@generated function _tensorial_set_gradient!( +@generated function _uniform_set_derivative!( r::AbstractMatrix{G},i,s,k,::Type{V}) where {G,V<:AbstractSymTensorValue{D}} where D # Git blame me for readable non-generated version @@ -334,7 +366,7 @@ end end function _hessian_nd!( - b::TensorPolynomialBasis{D,V,K,PT}, x, + b::UniformPolyBasis{D,V,K,PT}, x, r::AbstractMatrix{G}, i, c::AbstractMatrix{T}, g::AbstractMatrix{T}, @@ -371,7 +403,7 @@ function _hessian_nd!( end end - k = _tensorial_set_gradient!(r,i,s,k,V) + k = _uniform_set_derivative!(r,i,s,k,V) end end diff --git a/src/ReferenceFEs/BDMRefFEs.jl b/src/ReferenceFEs/BDMRefFEs.jl index a308b3076..3b0042563 100644 --- a/src/ReferenceFEs/BDMRefFEs.jl +++ b/src/ReferenceFEs/BDMRefFEs.jl @@ -15,7 +15,7 @@ function BDMRefFE(::Type{T},p::Polytope,order::Integer) where T if is_simplex(p) prebasis = MonomialBasis(Val(D),VectorValue{D,T},order,Polynomials._p_filter) fb = MonomialBasis(Val(D-1),T,order,Polynomials._p_filter) - cb = Polynomials.NedelecPrebasisOnSimplex{D}(order-2) + cb = PGradBasis(Monomial,Val(D),T,order-2) else @notimplemented "BDM Reference FE only available for simplices" end diff --git a/src/ReferenceFEs/NedelecRefFEs.jl b/src/ReferenceFEs/NedelecRefFEs.jl index 5243b6e1a..a3b7fb679 100644 --- a/src/ReferenceFEs/NedelecRefFEs.jl +++ b/src/ReferenceFEs/NedelecRefFEs.jl @@ -14,13 +14,13 @@ function NedelecRefFE(::Type{et},p::Polytope,order::Integer) where et D = num_dims(p) if is_n_cube(p) - prebasis = QGradMonomialBasis(Val(D),et,order) # Prebasis - eb = MonomialBasis(Val(1),et,order) # Edge basis - fb = QGradMonomialBasis(Val(D-1),et,order-1) # Face basis - cb = QCurlGradMonomialBasis(Val(D),et,order-1) # Cell basis + prebasis = QGradBasis(Monomial,Val(D),et,order) # Prebasis + eb = MonomialBasis(Val(1),et,order) # Edge basis + fb = QGradBasis(Monomial,Val(D-1),et,order-1) # Face basis + cb = QCurlGradBasis(Monomial,Val(D),et,order-1) # Cell basis elseif is_simplex(p) - prebasis = Polynomials.NedelecPrebasisOnSimplex{D}(order) # Prebasis - eb = MonomialBasis(Val(1),et,order) # Edge basis + prebasis = PGradBasis(Monomial,Val(D),et,order) # Prebasis + eb = MonomialBasis(Val(1),et,order) # Edge basis fb = MonomialBasis(Val(D-1),VectorValue{D-1,et},order-1,Polynomials._p_filter) # Face basis cb = MonomialBasis(Val(D),VectorValue{D,et},order-D+1,Polynomials._p_filter) # Cell basis else diff --git a/src/ReferenceFEs/RaviartThomasRefFEs.jl b/src/ReferenceFEs/RaviartThomasRefFEs.jl index c18b0533a..3b1afe1f9 100644 --- a/src/ReferenceFEs/RaviartThomasRefFEs.jl +++ b/src/ReferenceFEs/RaviartThomasRefFEs.jl @@ -18,11 +18,11 @@ function RaviartThomasRefFE( ) where {T,D} if is_n_cube(p) - prebasis = QCurlGradLegendreBasis(Val(D),T,order) # Prebasis - cb = QGradLegendreBasis(Val(D),T,order-1) # Cell basis + prebasis = QCurlGradBasis(Legendre,Val(D),T,order) # Prebasis + cb = QGradBasis(Legendre,Val(D),T,order-1) # Cell basis fb = LegendreBasis(Val(D-1),T,order,Polynomials._q_filter) # Face basis elseif is_simplex(p) - prebasis = PCurlGradMonomialBasis(Val(D),T,order) # Prebasis + prebasis = PCurlGradBasis(Monomial,Val(D),T,order) # Prebasis cb = LegendreBasis(Val(D),VectorValue{D,T},order-1,Polynomials._p_filter) # Cell basis fb = LegendreBasis(Val(D-1),T,order,Polynomials._p_filter) # Face basis else diff --git a/src/ReferenceFEs/ReferenceFEs.jl b/src/ReferenceFEs/ReferenceFEs.jl index e662988c5..5989075b7 100644 --- a/src/ReferenceFEs/ReferenceFEs.jl +++ b/src/ReferenceFEs/ReferenceFEs.jl @@ -33,11 +33,7 @@ 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: QGradLegendreBasis, QCurlGradLegendreBasis -import Gridap.Polynomials: get_order -import Gridap.Polynomials: get_orders import Gridap.Polynomials: _compute_filter_mask import Gridap.Polynomials: _define_terms, _sort_by_nfaces! diff --git a/src/ReferenceFEs/deprecated/BDMRefFEs.jl b/src/ReferenceFEs/deprecated/BDMRefFEs.jl index 001eb1d15..0a425b247 100644 --- a/src/ReferenceFEs/deprecated/BDMRefFEs.jl +++ b/src/ReferenceFEs/deprecated/BDMRefFEs.jl @@ -169,9 +169,7 @@ function Conformity(reffe::GenericRefFE{BDM},sym::Symbol) # Cell moments, i.e., M(C)_{ab} = q_C^a(xgp_C^b) w_C^b ⋅ () if is_simplex(p) - T = VectorValue{num_dims(p),et} - # cbasis = GradMonomialBasis(Val(num_dims(p)),T,order-1) - cbasis = Polynomials.NedelecPrebasisOnSimplex{num_dims(p)}(order-2) + cbasis = PGradBasis(Monomial,Val(num_dims(p)),et,order-2) else @notimplemented end diff --git a/test/PolynomialsTests/PCurlGradMonomialBasesTests.jl b/test/PolynomialsTests/PCurlGradBasesTests.jl similarity index 91% rename from test/PolynomialsTests/PCurlGradMonomialBasesTests.jl rename to test/PolynomialsTests/PCurlGradBasesTests.jl index 5ddd1ae78..e446296d3 100644 --- a/test/PolynomialsTests/PCurlGradMonomialBasesTests.jl +++ b/test/PolynomialsTests/PCurlGradBasesTests.jl @@ -1,4 +1,4 @@ -module PCurlGradMonomialBasesTests +module PCurlGradBasesTests using Test using Gridap.TensorValues @@ -15,7 +15,7 @@ D = 2 T = Float64 V = VectorValue{D,T} G = gradient_type(V,xi) -b = PCurlGradMonomialBasis(Val(D),T,order) +b = PCurlGradBasis(Monomial, Val(D),T,order) v = V[ (1.0, 0.0), (4.0, 0.0), (16.0, 0.0), (2.0, 0.0), (8.0, 0.0), (4.0, 0.0), # pterm ex @@ -57,7 +57,7 @@ D = 3 T = Float64 V = VectorValue{D,T} G = gradient_type(V,xi) -b = PCurlGradMonomialBasis(Val(D),T,order) +b = PCurlGradBasis(Monomial, Val(D),T,order) @test length(b) == 15 @test get_order(b) == 2 diff --git a/test/PolynomialsTests/PGradBasesTests.jl b/test/PolynomialsTests/PGradBasesTests.jl new file mode 100644 index 000000000..5e2de9e21 --- /dev/null +++ b/test/PolynomialsTests/PGradBasesTests.jl @@ -0,0 +1,49 @@ +module PGradBasesTests + +using Test +using Gridap.TensorValues +using Gridap.Fields +using Gridap.Arrays +using Gridap.Polynomials + +xi = Point(2.,3.,5.) +np = 3 +x = fill(xi,np) +T = Float64 + + +order = 0 +D = 3 +b = NedelecPolyBasisOnSimplex{D}(Monomial, T, order) + +V = VectorValue{D, Float64} +v = V[(1,0,0),(0,1,0),(0,0,1),(-3,2,0),(-5,0,2),(0,-5,3)] + +G = gradient_type(V,xi) +g = G[ + (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,-1,0, 1,0,0, 0,0,0),(0,0,-1, 0,0,0, 1,0,0),(0,0,0, 0,0,-1, 0,1,0)] + +bx = repeat(permutedims(v),np) +∇bx = repeat(permutedims(g),np) +test_field_array(b,x,bx,grad=∇bx) +test_field_array(b,x[1],bx[1,:],grad=∇bx[1,:]) + +xi = Point(2.,3.) +np = 4 +x = fill(xi,np) + +order = 0 +D = 2 +b = NedelecPolyBasisOnSimplex{D}(Monomial, T, order) +V = VectorValue{D, Float64} +v = V[(1,0),(0,1),(-3,2)] +G = gradient_type(V,xi) +g = G[(0,0, 0,0), (0,0, 0,0), (0,-1, 1,0)] +bx = repeat(permutedims(v),np) +∇bx = repeat(permutedims(g),np) +test_field_array(b,x,bx,grad=∇bx) +test_field_array(b,x[1],bx[1,:],grad=∇bx[1,:]) + + +end # module diff --git a/test/PolynomialsTests/QCurlGradMonomialBasesTests.jl b/test/PolynomialsTests/QCurlGradBasesTests.jl similarity index 90% rename from test/PolynomialsTests/QCurlGradMonomialBasesTests.jl rename to test/PolynomialsTests/QCurlGradBasesTests.jl index c226512fc..6c345aadf 100644 --- a/test/PolynomialsTests/QCurlGradMonomialBasesTests.jl +++ b/test/PolynomialsTests/QCurlGradBasesTests.jl @@ -1,4 +1,4 @@ -module QCurlGradMonomialBasesTests +module QCurlGradBasesTests using Test using Gridap.TensorValues @@ -14,7 +14,7 @@ D = 2 T = Float64 V = VectorValue{D,T} G = gradient_type(V,xi) -b = QCurlGradMonomialBasis(Val(D),T,order) +b = QCurlGradBasis(Monomial, Val(D),T,order) @test length(b) == 4 @test get_order(b) == 1 @@ -28,7 +28,7 @@ D = 3 T = Float64 V = VectorValue{D,T} G = gradient_type(V,xi) -b = QCurlGradMonomialBasis(Val(D),T,order) +b = QCurlGradBasis(Monomial, Val(D),T,order) v = V[ (1.0, 0.0, 0.0), (2.0, 0.0, 0.0), @@ -57,7 +57,7 @@ D = 2 T = Float64 V = VectorValue{D,T} G = gradient_type(V,xi) -b = QCurlGradMonomialBasis(Val(D),T,order) +b = QCurlGradBasis(Monomial, Val(D),T,order) v = V[ ( 1., 0. ), ( 2., 0. ), ( 4., 0. ), ( 3., 0. ), ( 6., 0. ), (12., 0. ), diff --git a/test/PolynomialsTests/QGradMonomialBasesTests.jl b/test/PolynomialsTests/QGradBasesTests.jl similarity index 68% rename from test/PolynomialsTests/QGradMonomialBasesTests.jl rename to test/PolynomialsTests/QGradBasesTests.jl index 42319d0a8..9a189ca30 100644 --- a/test/PolynomialsTests/QGradMonomialBasesTests.jl +++ b/test/PolynomialsTests/QGradBasesTests.jl @@ -15,7 +15,7 @@ D = 2 T = Float64 V = VectorValue{D,T} G = gradient_type(V,xi) -b = QGradMonomialBasis(Val(D),T,order) +b = QGradBasis(Monomial, Val(D),T,order) @test length(b) == 4 @test get_order(b) == 1 @@ -29,7 +29,7 @@ D = 3 T = Float64 V = VectorValue{D,T} G = gradient_type(V,xi) -b = QGradMonomialBasis(Val(D),T,order) +b = QGradBasis(Monomial, Val(D),T,order) v = V[ (1.0, 0.0, 0.0), (3.0, 0.0, 0.0), (5.0, 0.0, 0.0), (15.0, 0.0, 0.0), @@ -68,7 +68,7 @@ D = 2 T = Float64 V = VectorValue{D,T} G = gradient_type(V,xi) -b = QGradMonomialBasis(Val(D),T,order) +b = QGradBasis(Monomial, Val(D),T,order) v = V[ (1.0, 0.0), (0.0, 1.0), (2.0, 0.0), (0.0, 3.0), @@ -99,42 +99,5 @@ bx = repeat(permutedims(v),np) test_field_array(b,x,bx,grad=∇bx) test_field_array(b,x[1],bx[1,:],grad=∇bx[1,:]) -xi = Point(2.,3.,5.) -np = 3 -x = fill(xi,np) - -order = 0 -D = 3 -b = Polynomials.NedelecPrebasisOnSimplex{D}(order) - -V = VectorValue{D, Float64} -v = V[(1,0,0),(0,1,0),(0,0,1),(-3,2,0),(-5,0,2),(0,-5,3)] - -G = gradient_type(V,xi) -g = G[ - (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,-1,0, 1,0,0, 0,0,0),(0,0,-1, 0,0,0, 1,0,0),(0,0,0, 0,0,-1, 0,1,0)] - -bx = repeat(permutedims(v),np) -∇bx = repeat(permutedims(g),np) -test_field_array(b,x,bx,grad=∇bx) -test_field_array(b,x[1],bx[1,:],grad=∇bx[1,:]) - -xi = Point(2.,3.) -np = 4 -x = fill(xi,np) - -order = 0 -D = 2 -b = Polynomials.NedelecPrebasisOnSimplex{D}(order) -V = VectorValue{D, Float64} -v = V[(1,0),(0,1),(-3,2)] -G = gradient_type(V,xi) -g = G[(0,0, 0,0), (0,0, 0,0), (0,-1, 1,0)] -bx = repeat(permutedims(v),np) -∇bx = repeat(permutedims(g),np) -test_field_array(b,x,bx,grad=∇bx) -test_field_array(b,x[1],bx[1,:],grad=∇bx[1,:]) - end # module diff --git a/test/PolynomialsTests/runtests.jl b/test/PolynomialsTests/runtests.jl index 5788a2571..716a6344b 100644 --- a/test/PolynomialsTests/runtests.jl +++ b/test/PolynomialsTests/runtests.jl @@ -4,11 +4,13 @@ using Test @testset "MonomialBases" begin include("MonomialBasesTests.jl") end -@testset "QGradMonomialBases" begin include("QGradMonomialBasesTests.jl") end +@testset "QGradBases" begin include("QGradBasesTests.jl") end -@testset "QCurlGradMonomialBases" begin include("QCurlGradMonomialBasesTests.jl") end +@testset "QCurlGradBases" begin include("QCurlGradBasesTests.jl") end -@testset "PCurlGradMonomialBases" begin include("PCurlGradMonomialBasesTests.jl") end +@testset "PGradBases" begin include("PGradBasesTests.jl") end + +@testset "PCurlGradBases" begin include("PCurlGradBasesTests.jl") end @testset "ModalC0Bases" begin include("ModalC0BasesTests.jl") end From f7a6ad001a262acdef79f8724c75da1a27c97f8d Mon Sep 17 00:00:00 2001 From: Antoine Marteau Date: Fri, 3 Jan 2025 15:46:21 +1100 Subject: [PATCH 082/105] WIP Polynomials doc, added UML diagrams --- docs/Project.toml | 1 + docs/generate_diagrams.jl | 85 ++++++++++++++++++++++++++++++++++++++ docs/src/Polynomials.md | 5 +++ docs/src/assets/poly_1.svg | 1 + docs/src/assets/poly_2.svg | 1 + 5 files changed, 93 insertions(+) create mode 100644 docs/generate_diagrams.jl create mode 100644 docs/src/assets/poly_1.svg create mode 100644 docs/src/assets/poly_2.svg diff --git a/docs/Project.toml b/docs/Project.toml index 44ea2eafb..96ff808c5 100644 --- a/docs/Project.toml +++ b/docs/Project.toml @@ -1,6 +1,7 @@ [deps] Documenter = "e30172f5-a6a5-5a46-863b-614d45cd2de4" Gridap = "56d4f2e9-7ea1-5844-9cf6-b9c51ca7ce8e" +Kroki = "b3565e16-c1f2-4fe9-b4ab-221c88942068" [compat] Documenter = "1.0" diff --git a/docs/generate_diagrams.jl b/docs/generate_diagrams.jl new file mode 100644 index 000000000..ba0e7fc3f --- /dev/null +++ b/docs/generate_diagrams.jl @@ -0,0 +1,85 @@ +using Kroki + +macro export_kroki(name, str) + return quote + write("src/assets/"*string($name)*".svg", render($str, "svg")) + end +end + +# Plantuml uml diagrams syntax: https://plantuml.com/class-diagram#49b7759afaffc066 + +@export_kroki :poly_1 plantuml""" + @startuml + skinparam groupInheritance 2 + hide empty members + + together { + abstract Field + abstract Polynomial { + +isHierarchical + } + } + Field <|-left- Polynomial + + together { + struct Monomial + struct Legendre + struct Chebyshev + struct ModalC0 + struct Bernstein + } + + Polynomial <|-- Monomial + Polynomial <|-- Legendre + Polynomial <|-- Chebyshev + Polynomial <|-- ModalC0 + Polynomial <|-- Bernstein + + @enduml + """ + +@export_kroki :poly_2 plantuml""" + @startuml + skinparam groupInheritance 2 + hide empty members + + together { + abstract "AbstractArray{<:Polynomial}" as a1 + abstract PolynomialBasis { + +get_order + +return_type + } + } + a1 <|-left- PolynomialBasis + + together { + struct UniformPolyBasis { + +get_exponents + +get_orders + } + struct CompWiseTensorPolyBasis + struct RaviartThomasPolyBasis + struct NedelecPolyBasisOnSimplex + struct ModalC0Basis { + +get_orders + } + } + + PolynomialBasis <|-- UniformPolyBasis + PolynomialBasis <|-- CompWiseTensorPolyBasis + PolynomialBasis <|-- RaviartThomasPolyBasis + PolynomialBasis <|-- NedelecPolyBasisOnSimplex + PolynomialBasis <|-- ModalC0Basis + + object "(<:Polynomial)Basis" as m1 + object "QGrad[<:Polynomial]Basis\nQCurlGrad[<:Polynomial]Basis" as m2 + object "PCurlGrad[<:Polynomial]Basis" as m4 + object "PGradMonomialBasis" as m5 + UniformPolyBasis <-down- m1 + CompWiseTensorPolyBasis <-down- m2 + RaviartThomasPolyBasis <-down- m4 + NedelecPolyBasisOnSimplex <-down- m5 + + @enduml + """ + diff --git a/docs/src/Polynomials.md b/docs/src/Polynomials.md index 33a3e658c..85d30d782 100644 --- a/docs/src/Polynomials.md +++ b/docs/src/Polynomials.md @@ -3,8 +3,13 @@ CurrentModule = Gridap.Polynomials ``` + # Gridap.Polynomials +![](./assets/poly_1.svg) + +![](./assets/poly_2.svg) + ```@autodocs Modules = [Polynomials,] ``` diff --git a/docs/src/assets/poly_1.svg b/docs/src/assets/poly_1.svg new file mode 100644 index 000000000..2e3ffcf55 --- /dev/null +++ b/docs/src/assets/poly_1.svg @@ -0,0 +1 @@ +FieldPolynomialisHierarchicalMonomialLegendreChebyshevModalC0Bernstein \ No newline at end of file diff --git a/docs/src/assets/poly_2.svg b/docs/src/assets/poly_2.svg new file mode 100644 index 000000000..0299b85dc --- /dev/null +++ b/docs/src/assets/poly_2.svg @@ -0,0 +1 @@ +AbstractArray{<:Polynomial}PolynomialBasisget_orderreturn_typeUniformPolyBasisget_exponentsget_ordersCompWiseTensorPolyBasisRaviartThomasPolyBasisNedelecPolyBasisOnSimplexModalC0Basisget_orders(<:Polynomial)BasisQGrad[<:Polynomial]BasisQCurlGrad[<:Polynomial]BasisPCurlGrad[<:Polynomial]BasisPGradMonomialBasis \ No newline at end of file From 1d3a5715a0d0f5d81826da0948f4fc5a519e9c61 Mon Sep 17 00:00:00 2001 From: Antoine Marteau Date: Fri, 3 Jan 2025 16:02:42 +1100 Subject: [PATCH 083/105] revert import mistake and fix MonomialBasis name conflict --- src/ReferenceFEs/CDLagrangianRefFEs.jl | 2 +- src/ReferenceFEs/CLagrangianRefFEs.jl | 10 +++++----- src/ReferenceFEs/ReferenceFEs.jl | 2 ++ src/ReferenceFEs/deprecated/NedelecRefFEs.jl | 2 +- test/ReferenceFEsTests/CLagrangianRefFEsTests.jl | 12 +++++++----- 5 files changed, 16 insertions(+), 12 deletions(-) diff --git a/src/ReferenceFEs/CDLagrangianRefFEs.jl b/src/ReferenceFEs/CDLagrangianRefFEs.jl index f4b071e08..f456ec9d9 100644 --- a/src/ReferenceFEs/CDLagrangianRefFEs.jl +++ b/src/ReferenceFEs/CDLagrangianRefFEs.jl @@ -136,7 +136,7 @@ end if active_faces[offset+iface] face = Polytope{d}(p,iface) face_ref_x = get_vertex_coordinates(face) - face_prebasis = MonomialBasis(Float64,face,1) + face_prebasis = monomial_basis(Float64,face,1) change = inv(evaluate(face_prebasis,face_ref_x)) face_shapefuns = linear_combination(change,face_prebasis) face_vertex_ids = get_faces(p,d,0)[iface] diff --git a/src/ReferenceFEs/CLagrangianRefFEs.jl b/src/ReferenceFEs/CLagrangianRefFEs.jl index 56d1ce99b..0cd4e5fc0 100644 --- a/src/ReferenceFEs/CLagrangianRefFEs.jl +++ b/src/ReferenceFEs/CLagrangianRefFEs.jl @@ -286,7 +286,7 @@ function _lagrangian_ref_fe(::Type{T},p::Polytope{D},orders) where {T,D} end -function MonomialBasis(::Type{T},p::Polytope,orders) where T +function monomial_basis(::Type{T},p::Polytope,orders) where T compute_monomial_basis(T,p,orders) end @@ -369,9 +369,9 @@ function LagrangianRefFE(::Type{T},p::Polytope{D},order::Int;space::Symbol=_defa LagrangianRefFE(T,p,orders;space=space) end -function MonomialBasis(::Type{T},p::Polytope{D},order::Int) where {D,T} +function monomial_basis(::Type{T},p::Polytope{D},order::Int) where {D,T} orders = tfill(order,Val{D}()) - MonomialBasis(T,p,orders) + monomial_basis(T,p,orders) end function LagrangianDofBasis(::Type{T},p::Polytope{D},order::Int) where {T,D} @@ -508,7 +508,7 @@ end for iface in 1:num_faces(p,d) face = Polytope{d}(p,iface) face_ref_x = get_vertex_coordinates(face) - face_prebasis = MonomialBasis(Float64,face,1) + face_prebasis = monomial_basis(Float64,face,1) change = inv(evaluate(face_prebasis,face_ref_x)) face_shapefuns = linear_combination(change,face_prebasis) face_vertex_ids = get_faces(p,d,0)[iface] @@ -538,7 +538,7 @@ _compute_node_permutations(::Polytope{0}, interior_nodes) = [[1]] function _compute_node_permutations(p, interior_nodes) vertex_to_coord = get_vertex_coordinates(p) - lbasis = MonomialBasis(Float64,p,1) + lbasis = monomial_basis(Float64,p,1) change = inv(evaluate(lbasis,vertex_to_coord)) lshapefuns = linear_combination(change,lbasis) perms = get_vertex_permutations(p) diff --git a/src/ReferenceFEs/ReferenceFEs.jl b/src/ReferenceFEs/ReferenceFEs.jl index 5989075b7..1f6f9b0f8 100644 --- a/src/ReferenceFEs/ReferenceFEs.jl +++ b/src/ReferenceFEs/ReferenceFEs.jl @@ -34,6 +34,8 @@ import Gridap.Fields: evaluate import Gridap.Fields: lazy_map import Gridap.Fields: linear_combination +import Gridap.Polynomials: get_order +import Gridap.Polynomials: get_orders import Gridap.Polynomials: _compute_filter_mask import Gridap.Polynomials: _define_terms, _sort_by_nfaces! diff --git a/src/ReferenceFEs/deprecated/NedelecRefFEs.jl b/src/ReferenceFEs/deprecated/NedelecRefFEs.jl index fb28eac7a..753354db7 100644 --- a/src/ReferenceFEs/deprecated/NedelecRefFEs.jl +++ b/src/ReferenceFEs/deprecated/NedelecRefFEs.jl @@ -168,7 +168,7 @@ function _Nedelec_edge_values(p,et,order) c_eips, ecips, ewips = _nfaces_evaluation_points_weights(p, egeomap, cips, wips) # Edge moments, i.e., M(Ei)_{ab} = q_RE^a(xgp_REi^b) w_Fi^b t_Ei ⋅ () - eshfs = MonomialBasis(et,ep,order) + eshfs = monomial_basis(et,ep,order) emoments = _Nedelec_edge_moments(p, eshfs, c_eips, ecips, ewips) return ecips, emoments diff --git a/test/ReferenceFEsTests/CLagrangianRefFEsTests.jl b/test/ReferenceFEsTests/CLagrangianRefFEsTests.jl index f2c6204f0..4dc8dee38 100644 --- a/test/ReferenceFEsTests/CLagrangianRefFEsTests.jl +++ b/test/ReferenceFEsTests/CLagrangianRefFEsTests.jl @@ -8,23 +8,25 @@ using Gridap.Polynomials using Gridap.ReferenceFEs using JSON +using Gridap.ReferenceFEs: monomial_basis + orders = (2,3) -b = MonomialBasis(Float64,QUAD,orders) +b = monomial_basis(Float64,QUAD,orders) r = [(0,0), (1,0), (2,0), (0,1), (1,1), (2,1), (0,2), (1,2), (2,2), (0,3), (1,3), (2,3)] @test get_exponents(b) == r orders = (1,1,2) -b = MonomialBasis(Float64,WEDGE,orders) +b = monomial_basis(Float64,WEDGE,orders) r = [(0,0,0), (1,0,0), (0,1,0), (0,0,1), (1,0,1), (0,1,1), (0,0,2), (1,0,2), (0,1,2)] @test get_exponents(b) == r orders = (1,1,1) -b = MonomialBasis(Float64,PYRAMID,orders) +b = monomial_basis(Float64,PYRAMID,orders) r = [(0,0,0), (1,0,0), (0,1,0), (1,1, 0), (0,0,1)] @test get_exponents(b) == r orders = (1,1,1) -b = MonomialBasis(Float64,TET,orders) +b = monomial_basis(Float64,TET,orders) r = [(0,0,0), (1,0,0), (0,1,0), (0,0,1)] @test get_exponents(b) == r @@ -71,7 +73,7 @@ dofs = LagrangianDofBasis(SymTensorValue{2,Int},VERTEX,()) dofs = LagrangianDofBasis(SymTracelessTensorValue{2,Int},VERTEX,()) @test dofs.node_and_comp_to_dof == SymTracelessTensorValue{2,Int}[(1,2)] -b = MonomialBasis(VectorValue{2,Int},VERTEX,()) +b = monomial_basis(VectorValue{2,Int},VERTEX,()) @test length(b) == 2 @test evaluate(b,Point{0,Int}[(),()]) == VectorValue{2,Int}[(1, 0) (0, 1); (1, 0) (0, 1)] From 6c821e825acf66e441febbb1079407b39c7b8c7e Mon Sep 17 00:00:00 2001 From: Antoine Marteau Date: Fri, 3 Jan 2025 16:25:55 +1100 Subject: [PATCH 084/105] marked broken tests broken --- test/FESpacesTests/CurlConformingFESpacesTests.jl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/FESpacesTests/CurlConformingFESpacesTests.jl b/test/FESpacesTests/CurlConformingFESpacesTests.jl index bd46c3383..13bf7e845 100644 --- a/test/FESpacesTests/CurlConformingFESpacesTests.jl +++ b/test/FESpacesTests/CurlConformingFESpacesTests.jl @@ -90,7 +90,7 @@ U = TrialFESpace(V,u) uh = interpolate(u,U) e = u - uh el2 = sqrt(sum( ∫( e⋅e )*dΩ )) -@test el2 < 1.0e-10 +@test el2 < 1.0e-10 broken=true order = 2 reffe = ReferenceFE(nedelec,order) V = TestFESpace(model,reffe,dirichlet_tags = "boundary") @@ -99,7 +99,7 @@ U = TrialFESpace(V,u) uh = interpolate(u,U) e = u - uh el2 = sqrt(sum( ∫( e⋅e )*dΩ )) -@test el2 < 1.0e-10 +@test el2 < 1.0e-10 broken=true domain =(0,1,0,1,0,1) partition = (3,3,3) From 494e1811cf1e62f12f0a678cd0396e65c53bc70f Mon Sep 17 00:00:00 2001 From: Antoine Marteau Date: Fri, 3 Jan 2025 17:45:44 +1100 Subject: [PATCH 085/105] better Polynomials docstrings --- src/Polynomials/CompWiseTensorPolyBases.jl | 30 ++++++++++-------- src/Polynomials/ModalC0Bases.jl | 5 +-- src/Polynomials/NedelecPolyBases.jl | 8 +++-- src/Polynomials/PolynomialInterfaces.jl | 30 +++++++++--------- src/Polynomials/RaviartThomasPolyBases.jl | 35 ++++++++++++--------- src/Polynomials/UniformPolyBases.jl | 36 +++++++++++----------- 6 files changed, 78 insertions(+), 66 deletions(-) diff --git a/src/Polynomials/CompWiseTensorPolyBases.jl b/src/Polynomials/CompWiseTensorPolyBases.jl index 93b72f27a..1d17e0e43 100644 --- a/src/Polynomials/CompWiseTensorPolyBases.jl +++ b/src/Polynomials/CompWiseTensorPolyBases.jl @@ -18,7 +18,7 @@ with `L`>1, where the scalar multivariate spaces 𝕊ˡ (for 1 ≤ l ≤ `L`) of 𝕊ᴸ = ℙα(`L`,1) ⊗ … ⊗ ℙα(`L`,`D`) The `L`×`D` matrix of orders α is given in the constructor, and `K` is the -maximum of α. Any 1D polynomial basis `PT` is usable. +maximum of α. Any 1D polynomial family `PT<:Polynomial` is usable. """ struct CompWiseTensorPolyBasis{D,V,K,PT,L} <: PolynomialBasis{D,V,K,PT} orders::SMatrix{L,D,Int} @@ -40,11 +40,11 @@ end Base.size(a::CompWiseTensorPolyBasis) = ( sum(prod.(eachrow(a.orders .+ 1))), ) """ - get_comp_terms(f::CompWiseTensorPolyBasis) + get_comp_terms(f::CompWiseTensorPolyBasis{D,V}) -Return a `NTuple{L,CartesianIndices{D}}` containing, for each component -1 ≤ l ≤ `L`, the Cartesian indices iterator over the terms -in ⟦1,`o`(l,1)+1⟧ × ⟦1,`o`(l,2)+1⟧ × … × ⟦1,`o`(l,D)+1⟧ that define 𝕊ˡ. +Return a tuple (terms\\_1, ..., terms\\_l, ..., terms\\_L) containing, for each +component of V, the Cartesian indices iterator over the terms that define 𝕊ˡ, +that is all elements of ⟦1,`o`(l,1)+1⟧ × ⟦1,`o`(l,2)+1⟧ × … × ⟦1,`o`(l,D)+1⟧. E.g., if `orders=[ 0 1; 1 0]`, then the `comp_terms` are `( CartesianIndices{2}((1,2)), CartesianIndices{2}((2,1)) )`. @@ -149,7 +149,7 @@ function _gradient_nd!( end """ - _uniform_set_derivative!(r::AbstractMatrix{G},i,s,k,::Type{V}) + _comp_wize_set_derivative!(r::AbstractMatrix{G},i,s,k,::Type{<:Real}) ``` r[i,k] = G(s…) = (Dbᵏ)(xi) @@ -165,7 +165,7 @@ function _comp_wize_set_derivative!( end """ - _uniform_set_derivative!(r::AbstractMatrix{G},i,s,k,::Type{V}) + _comp_wize_set_derivative!(r::AbstractMatrix{G},i,s,k,::Type{V}) ``` z = zero(s) @@ -262,7 +262,7 @@ polynomial space for Nedelec elements on `D`-dimensional cubes with scalar type The `order`=n argument has the following meaning: the curl of the functions in this basis is in ℚₙ. -`PT<:Polynomial` is the choice of scalar 1D polynomial basis. +`PT<:Polynomial` is the choice of the family of the scalar 1D basis polynomials. # Example: @@ -300,13 +300,17 @@ end """ QCurlGradBasis(::Type{PT}, ::Val{D}, ::Type{T}, order::Int) :: PolynomialBasis -Return a basis of ℝ𝕋ᴰₙ(□) = (ℚₙ)ᴰ ⊕ x (ℚₙ \\ ℚₙ₋₁) with n=`order`, the polynomial -space for Raviart-Thomas elements on `D`-dimensional cubes with scalar type `T`. +Return a basis of + +ℝ𝕋ᴰₙ(□) = (ℚₙ)ᴰ ⊕ x (ℚₙ \\ ℚₙ₋₁) + +with n=`order`, the polynomial space for Raviart-Thomas elements on +`D`-dimensional cubes with scalar type `T`. The `order`=n argument has the following meaning: the divergence of the functions in this basis is in ℚₙ. -`PT<:Polynomial` is the choice of scalar 1D polynomial basis. +`PT<:Polynomial` is the choice of the family of the scalar 1D basis polynomials. # Example: @@ -315,8 +319,8 @@ in this basis is in ℚₙ. b = QCurlGradBasis(Bernstein, Val(2), Float64, 3) ``` -For more details, see [`CompWiseTensorPolyBasis`](@ref), as `QCurlGradBasis` returns -an instance of\\ +For more details, see [`CompWiseTensorPolyBasis`](@ref), as `QCurlGradBasis` +returns an instance of\\ `CompWiseTensorPolyBasis{D, VectorValue{D,T}, order+1, PT}` for `D`>1, or\\ `UniformPolyBasis{1, VectorValue{1,T}, order+1, PT}` for `D`=1. """ diff --git a/src/Polynomials/ModalC0Bases.jl b/src/Polynomials/ModalC0Bases.jl index f0bbfed89..b539746a9 100644 --- a/src/Polynomials/ModalC0Bases.jl +++ b/src/Polynomials/ModalC0Bases.jl @@ -1,7 +1,7 @@ """ ModalC0 <: Polynomial -Type representing ModalC0 polynomial. +Type representing ModalC0 polynomials. Reference: Eq. (17) in https://doi.org/10.1016/j.camwa.2022.09.027 """ @@ -91,9 +91,6 @@ end @inline Base.size(a::ModalC0Basis{D,V}) where {D,V} = (length(a.terms)*num_indep_components(V),) -""" - get_orders(b::ModalC0Basis) -""" function get_orders(b::ModalC0Basis) b.orders end diff --git a/src/Polynomials/NedelecPolyBases.jl b/src/Polynomials/NedelecPolyBases.jl index 97fcf0f22..ebd65130b 100644 --- a/src/Polynomials/NedelecPolyBases.jl +++ b/src/Polynomials/NedelecPolyBases.jl @@ -237,8 +237,12 @@ end """ PGradBasis(::Type{Monomial}, ::Val{D}, ::Type{T}, order::Int) :: PolynomialBasis -Return a basis of ℕ𝔻ᴰₙ(△) = (ℙₙ)ᴰ ⊕ x × (ℙₙ \\ ℙₙ₋₁)ᴰ with n=`order`, the polynomial -space for Nedelec elements on `D`-dimensional simplices with scalar type `T`. +Return a basis of + +ℕ𝔻ᴰₙ(△) = (ℙₙ)ᴰ ⊕ x × (ℙₙ \\ ℙₙ₋₁)ᴰ + +with n=`order`, the polynomial space for Nedelec elements on `D`-dimensional +simplices with scalar type `T`. The `order`=n argument has the following meaning: the curl of the functions in this basis is in ℙₙ. diff --git a/src/Polynomials/PolynomialInterfaces.jl b/src/Polynomials/PolynomialInterfaces.jl index 4f7a3c467..26509f444 100644 --- a/src/Polynomials/PolynomialInterfaces.jl +++ b/src/Polynomials/PolynomialInterfaces.jl @@ -15,11 +15,11 @@ hierarchical. abstract type Polynomial <: Field end """ - isHierarchical(::Type{Polynomial}) + isHierarchical(::Type{Polynomial})::Bool -Return true if the 1D basis of order `K` of the given [`Polynomial`](@ref) type -is the union of the basis of order `K-1` and an other order `K` polynomial. -Equivalently, if the iᵗʰ basis polynomial is of order i-1. +Return `true` if the 1D basis of order `K` of the given [`Polynomial`](@ref) +basis family is the union of the basis of order `K-1` and an other order `K` +polynomial. Equivalently, if the iᵗʰ basis polynomial is of order i-1. """ isHierarchical(::Type{Polynomial}) = @abstractmethod @@ -49,9 +49,9 @@ isHierarchical(::Type{Polynomial}) = @abstractmethod Abstract type representing a generic multivariate polynomial basis. The parameters are: - `D`: the spatial dimension -- `V`: the image values type, of type `<:Real` or `<:MultiValue` +- `V`: the image values type, a concrete type `<:Real` or `<:MultiValue` - `K`: the maximum order of a basis polynomial in a spatial component -- `PT <: Polynomial`: the polynomial family (must be a concrete type). +- `PT <: Polynomial`: the family of the basis polynomials (must be a concrete type). """ abstract type PolynomialBasis{D,V,K,PT<:Polynomial} <: AbstractVector{PT} end @@ -191,7 +191,7 @@ Compute and assign: `r`[`i`] = ∇`b`(`xi`) = (∇`b`₁(`xi`), ..., ∇`b`ₙ(` where n = length(`b`) (cardinal of the basis), like [`_evaluate_nd!`](@ref) but for gradients of `b`ₖ(`xi`), and -- `g` is a mutable `D`×`K` cache (for 1D poly deriv evals). +- `g` is a mutable `D`×`K` cache (for the 1D polynomials first derivatives). - `s` is a mutable length `D` cache for ∇`b`ₖ(`xi`). """ function _gradient_nd!( @@ -213,7 +213,7 @@ Compute and assign: `r`[`i`] = H`b`(`xi`) = (H`b`₁(`xi`), ..., H`b`ₙ(`xi`)) where n = length(`b`) (cardinal of the basis), like [`_evaluate_nd!`](@ref) but for hessian matrices/tensor of `b`ₖ(`xi`), and -- `h` is a mutable `D`×`K` cache (for 1D poly second deriv evals). +- `h` is a mutable `D`×`K` cache (for the 1D polynomials second derivatives). - `s` is a mutable `D`×`D` cache for H`b`ₖ(`xi`). """ function _hessian_nd!( @@ -282,11 +282,11 @@ end """ _evaluate_1d!(PT::Type{<:Polynomial},::Val{K},c,x,d) -Evaluates in place the 1D basis polynomials of the given type at one nD point `x` -along the given coordinate 1 ≤ `d` ≤ nD. +Evaluates in place the 1D basis polynomials of the family `PT` at one D-dim. +point `x` along the given coordinate 1 ≤ `d` ≤ D. -`c` is an AbstractMatrix of size (at least) `d`×(`K`+1), such that the 1 ≤ i ≤ `k`+1 -values are stored in `c[d,i]`. +`c` is an AbstractMatrix of size (at least) `d`×(`K`+1), such that the +1 ≤ i ≤ `k`+1 values are stored in `c[d,i]`. """ function _evaluate_1d!(::Type{<:Polynomial},::Val{K},c::AbstractMatrix{T},x,d) where {K,T<:Number} @abstractmethod @@ -295,7 +295,8 @@ end """ _gradient_1d!(PT::Type{<:Polynomial},::Val{K},g,x,d) -Like [`_evaluate_1d!`](@ref), but computes the first derivative of the basis functions. +Like [`_evaluate_1d!`](@ref), but computes the first derivative of the basis +polynomials. """ function _gradient_1d!(::Type{<:Polynomial},::Val{K},g::AbstractMatrix{T},x,d) where {K,T<:Number} @abstractmethod @@ -304,7 +305,8 @@ end """ _hessian_1d!(PT::Type{<:Polynomial},::Val{K},g,x,d) -Like [`_evaluate_1d!`](@ref), but computes the second derivative of the basis functions. +Like [`_evaluate_1d!`](@ref), but computes the second derivative of the basis +polynomials. """ function _hessian_1d!(::Type{<:Polynomial},::Val{K},h::AbstractMatrix{T},x,d) where {K,T<:Number} @abstractmethod diff --git a/src/Polynomials/RaviartThomasPolyBases.jl b/src/Polynomials/RaviartThomasPolyBases.jl index 5afb50296..9b0c99fcd 100644 --- a/src/Polynomials/RaviartThomasPolyBases.jl +++ b/src/Polynomials/RaviartThomasPolyBases.jl @@ -1,21 +1,22 @@ """ RaviartThomasPolyBasis{D,V,K,PT} <: PolynomialBasis{D,V,K,PT} -Basis of ℝ𝕋ᴰₙ = (𝕊ₙ)ᴰ ⊕ x (𝕊ₙ\\𝕊₍ₙ₋₁₎) +Basis of the vector valued (`V<:VectorValue{D}`) space + +ℝ𝕋ᴰₙ = (𝕊ₙ)ᴰ ⊕ x (𝕊ₙ\\𝕊₍ₙ₋₁₎) + where 𝕊ₙ is a multivariate scalar polynomial space of maximum degree n = `K`-1. This ℝ𝕋ᴰₙ is the polynomial space for Raviart-Thomas elements with divergence in 𝕊ₙ. Its maximum degree is n+1 = `K`. `get_order` on it returns `K`. -The multivariate scalar space 𝕊ₙ, typically ℙₙ, doesn't need to have a tensor -product structure of 1D scalar spaces. Thus, the ℝ𝕋ᴰₙ component's scalar spaces -are not tensor products either. - -𝕊ₙ must admit a basis computable using products of 1D polynomials of the `PT` -type, `PT` thus needs to be hierarchical, see [`isHierarchical`](@ref). +The multivariate scalar space 𝕊ₙ, typically ℙₙ or ℚₙ, does not need to have a +tensor product structure of 1D scalar spaces. Thus, the ℝ𝕋ᴰₙ component's scalar +spaces are not tensor products either. -Indeed, 𝕊ₙ is defined like a scalar valued [`UniformPolyBasis`](@ref) via -the `_filter` argument of the constructor, by default [`_p_filter`](@ref). +𝕊ₙ is defined like a scalar valued [`UniformPolyBasis`](@ref) via the `_filter` +argument of the constructor, by default [`_p_filter`](@ref) for ℙₙ. +As a consequence, `PT` must be hierarchical, see [`isHierarchical`](@ref). """ struct RaviartThomasPolyBasis{D,V,K,PT} <: PolynomialBasis{D,V,K,PT} pterms::Vector{CartesianIndex{D}} @@ -190,14 +191,18 @@ end """ PCurlGradBasis(::Type{PT}, ::Val{D}, ::Type{T}, order::Int) :: PolynomialBasis -Return a basis of ℝ𝕋ᴰₙ(△) = (ℙₙ)ᴰ ⊕ x (ℙₙ \\ ℙₙ₋₁) with n=`order`, the polynomial -space for Raviart-Thomas elements on `D`-dimensional simplices with scalar type `T`. +Return a basis of + +ℝ𝕋ᴰₙ(△) = (ℙₙ)ᴰ ⊕ x (ℙₙ \\ ℙₙ₋₁) + +with n=`order`, the polynomial space for Raviart-Thomas elements on +`D`-dimensional simplices with scalar type `T`. The `order`=n argument of this function has the following meaning: the divergence of the functions in this basis is in ℙₙ. -`PT<:Polynomial` is the choice of scalar 1D polynomial basis, it must be -hierarchichal, see [`isHierarchichal`](@ref). +`PT<:Polynomial` is the choice of the family of the scalar 1D basis polynomials, +it must be hierarchichal, see [`isHierarchichal`](@ref). # Example: @@ -208,8 +213,8 @@ b = PCurlGradBasis(Monomial, Val(3), Float64, 2) For more details, see [`RaviartThomasPolyBasis`](@ref), as `PCurlGradBasis` returns an instance of\\ -`RaviartThomasPolyBasis{D,VectorValue{D,T},order+1,PT}` for `D`=2,3, or\\ -`UniformPolyBasis{1,VectorValue{1,T},order+1,PT}` for `D`=1. +`RaviartThomasPolyBasis{D, VectorValue{D,T}, order+1, PT}` for `D`>1, or\\ +`UniformPolyBasis{1, VectorValue{1,T}, order+1, PT}` for `D`=1. """ function PCurlGradBasis(::Type{PT},::Val{D},::Type{T},order::Int) where {PT,D,T} RaviartThomasPolyBasis{D}(PT, T, order) diff --git a/src/Polynomials/UniformPolyBases.jl b/src/Polynomials/UniformPolyBases.jl index bb7fd6b7e..1e39c6052 100644 --- a/src/Polynomials/UniformPolyBases.jl +++ b/src/Polynomials/UniformPolyBases.jl @@ -6,8 +6,12 @@ struct UniformPolyBasis{D,V,K,PT} <: PolynomialBasis{D,V,K,PT} Type representing a uniform basis of (an)isotropic `D`-multivariate `V`-valued -polynomial space `V`(𝕊, 𝕊, ..., 𝕊), where 𝕊 is a scalar polynomial space. So each -independant component of `V` holds the same space, which is here called 'uniform'. +polynomial space + +`V`(𝕊, 𝕊, ..., 𝕊) + +where 𝕊 is a scalar polynomial space. So each (independant) component of `V` +holds the same space, which is here called 'uniform'. The scalar polynomial basis spanning 𝕊 is defined as @@ -52,11 +56,11 @@ function UniformPolyBasis( end """ - UniformPolyBasis(::Type{PT}, ::Val{D}, ::Type{V}, orders::Tuple [, filter::Function]) + UniformPolyBasis(::Type{PT}, ::Val{D}, ::Type{V}, orders::Tuple [, filter=_q_filter]) -This version of the constructor allows to pass a tuple `orders` containing the -polynomial order to be used in each of the `D` dimensions in order to construct -a tensorial anisotropic multivariate space 𝕊. +This constructor allows to pass a tuple `orders` containing the polynomial order +to be used in each of the `D` spatial dimensions in order to construct a +tensorial anisotropic multivariate space 𝕊. """ function UniformPolyBasis( ::Type{PT}, ::Val{D}, ::Type{V}, orders::NTuple{D,Int}, filter::Function=_q_filter @@ -67,29 +71,25 @@ function UniformPolyBasis( end """ - UniformPolyBasis(::Type{V}, ::Val{D}, order::Int [, filter::Function]) where {D,V} + UniformPolyBasis(::Type{PT}, ::Type{V}, ::Val{D}, order::Int [, filter=_q_filter]) where {D,V} -Returns an instance of `UniformPolyBasis` representing a multivariate polynomial -basis in `D` dimensions, of polynomial degree `order`, whose value is represented -by the type `V`. The type `V` is typically `<:Number`, e.g., `Float64` for -scalar-valued functions and `VectorValue{D,Float64}` for vector-valued ones. +Returns an instance of `UniformPolyBasis{D,V,order,PT}`. # Filter function The `filter` function is used to select which terms of the tensor product space -of order `order` in `D` dimensions are to be used. If the filter is not provided, -the full tensor-product space is used by default leading to a multivariate -polynomial space of type ℚ. The signature of the filter function is +of order `order` in `D` spatial dimensions are to be used. If the filter is not +provided, the full tensor-product space is used by default leading to a +multivariate polynomial space of type ℚ. The signature of the filter function is (e,order) -> Bool where `e` is a tuple of `D` integers containing the exponents of a multivariate monomial. The following filters are used to select well known polynomial spaces -- ℚ space: `(e,order) -> maximum(e) <= order` -- ℙ space: `(e,order) -> sum(e) <= order` +- ℚ space: `_p_filter = (e,order) -> maximum(e) <= order` +- ℙ space: `_q_filter = (e,order) -> sum(e) <= order` - "Serendipity" space: `(e,order) -> sum( [ i for i in e if i>1 ] ) <= order` - """ function UniformPolyBasis( ::Type{PT}, VD::Val{D}, ::Type{V}, order::Int, filter::Function=_q_filter) where {PT,D,V} @@ -128,7 +128,7 @@ end """ get_orders(b::UniformPolyBasis) -Return the D-tuple of polynomial orders in each dimension +Return the D-tuple of polynomial orders in each spatial dimension """ function get_orders(b::UniformPolyBasis) b.orders From a48c09bd1f6df0b09ab9130d11c9295fc8d27c8c Mon Sep 17 00:00:00 2001 From: Antoine Marteau Date: Fri, 3 Jan 2025 18:04:13 +1100 Subject: [PATCH 086/105] minor --- src/Polynomials/CompWiseTensorPolyBases.jl | 10 +++++++--- src/Polynomials/Deprecated.jl | 4 ++-- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/src/Polynomials/CompWiseTensorPolyBases.jl b/src/Polynomials/CompWiseTensorPolyBases.jl index 1d17e0e43..6b647ab19 100644 --- a/src/Polynomials/CompWiseTensorPolyBases.jl +++ b/src/Polynomials/CompWiseTensorPolyBases.jl @@ -256,8 +256,12 @@ end """ QGradBasis(::Type{PT}, ::Val{D}, ::Type{T}, order::Int) :: PolynomialBasis -Return a basis of ℕ𝔻ᴰₙ(□) = (ℚₙ)ᴰ ⊕ x × (ℚₙ \\ ℚₙ₋₁)ᴰ with n=`order`, the -polynomial space for Nedelec elements on `D`-dimensional cubes with scalar type `T`. +Return a basis of + +ℕ𝔻ᴰₙ(□) = (ℚₙ)ᴰ ⊕ x × (ℚₙ \\ ℚₙ₋₁)ᴰ + +with n=`order`, the polynomial space for Nedelec elements on `D`-dimensional +cubes with scalar type `T`. The `order`=n argument has the following meaning: the curl of the functions in this basis is in ℚₙ. @@ -268,7 +272,7 @@ this basis is in ℚₙ. ```jldoctest # a basis for Nedelec on hexahedra with divergence in ℚ₂ -b = PCurlGradBasis(Monomial, Val(3), Float64, 2) +b = QGradBasis(Monomial, Val(3), Float64, 2) ``` For more details, see [`CompWiseTensorPolyBasis`](@ref), as `QGradBasis` returns diff --git a/src/Polynomials/Deprecated.jl b/src/Polynomials/Deprecated.jl index edca51e75..a36b96fbe 100644 --- a/src/Polynomials/Deprecated.jl +++ b/src/Polynomials/Deprecated.jl @@ -8,7 +8,7 @@ struct PGradMonomialBasis{D} new{0}() end end -@deprecate PGradMonomialBasis{D}(args...) where D PGradBasis(Monomial, Val(D), args...) +@deprecate PGradMonomialBasis{D}(args...) where D PGradBasis(Monomial, Val(D), args...) false struct PCurlGradMonomialBasis{D} function PCurlGradMonomialBasis() @@ -40,7 +40,7 @@ struct NedelecPreBasisOnSimplex{D} new{0}() end end -@deprecate NedelecPreBasisOnSimplex{D}(args...) where D NedelecPolyBasisOnSimplex{D}(args...) +@deprecate NedelecPreBasisOnSimplex{D}(args...) where D NedelecPolyBasisOnSimplex{D}(args...) false struct JacobiPolynomialBasis{D} function JacobiPolynomialBasis() From 15fee116d697cf1af547ff8818cfaeea6b79b197 Mon Sep 17 00:00:00 2001 From: Antoine Marteau Date: Fri, 3 Jan 2025 18:08:04 +1100 Subject: [PATCH 087/105] minor --- test/FESpacesTests/CurlConformingFESpacesTests.jl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/FESpacesTests/CurlConformingFESpacesTests.jl b/test/FESpacesTests/CurlConformingFESpacesTests.jl index 42d14c91f..d5e983822 100644 --- a/test/FESpacesTests/CurlConformingFESpacesTests.jl +++ b/test/FESpacesTests/CurlConformingFESpacesTests.jl @@ -90,7 +90,7 @@ U = TrialFESpace(V,u) uh = interpolate(u,U) e = u - uh el2 = sqrt(sum( ∫( e⋅e )*dΩ )) -@test el2 < 1.0e-10 broken=true +@test el2 < 1.0e-10 order = 2 reffe = ReferenceFE(nedelec,order) V = TestFESpace(model,reffe,dirichlet_tags = "boundary") @@ -99,7 +99,7 @@ U = TrialFESpace(V,u) uh = interpolate(u,U) e = u - uh el2 = sqrt(sum( ∫( e⋅e )*dΩ )) -@test el2 < 1.0e-10 broken=true +@test el2 < 1.0e-10 domain = (0,1,0,1,0,1) partition = (3,3,3) From 5c401004ee9c577d6cd15de4a651e9352080d458 Mon Sep 17 00:00:00 2001 From: Antoine Marteau Date: Mon, 6 Jan 2025 11:08:23 +1100 Subject: [PATCH 088/105] fix UniformPolyBases test coverage --- src/Polynomials/UniformPolyBases.jl | 8 ------ test/PolynomialsTests/MonomialBasesTests.jl | 29 +++++++++++++++++++++ 2 files changed, 29 insertions(+), 8 deletions(-) diff --git a/src/Polynomials/UniformPolyBases.jl b/src/Polynomials/UniformPolyBases.jl index 1e39c6052..7130144b1 100644 --- a/src/Polynomials/UniformPolyBases.jl +++ b/src/Polynomials/UniformPolyBases.jl @@ -144,14 +144,6 @@ _qs_filter(e,order) = (maximum(e,init=0) == order) # ℚₙ\ℚ₍ₙ₋₁₎ _p_filter( e,order) = (sum(e) <= order) # ℙₙ _ps_filter(e,order) = (sum(e) == order) # ℙₙ\ℙ₍ₙ₋₁₎ -function _p_dim(order,D) - dim = 1 - for d in 1:D - dim *= order+d - end - dim/factorial(D) -end - function _define_terms(filter,orders) t = orders .+ 1 g = (0 .* orders) .+ 1 diff --git a/test/PolynomialsTests/MonomialBasesTests.jl b/test/PolynomialsTests/MonomialBasesTests.jl index 2074497d1..1114f5656 100644 --- a/test/PolynomialsTests/MonomialBasesTests.jl +++ b/test/PolynomialsTests/MonomialBasesTests.jl @@ -5,6 +5,8 @@ using Gridap.TensorValues using Gridap.Fields using Gridap.Polynomials +using Gridap.Polynomials: _q_filter, _qs_filter, _p_filter, _ps_filter + xi = Point(2,3) np = 5 x = fill(xi,np) @@ -242,4 +244,31 @@ b = MonomialBasis(Val(0),SymTracelessTensorValue{2,Float64},order) (1.0, 0.0) (0.0, 1.0); (1.0, 0.0) (0.0, 1.0) ] +order = 2 + +@test _q_filter( (1,2) ,order) == true +@test _q_filter( (2,0) ,order) == true +@test _q_filter( (2,2) ,order) == true +@test _q_filter( (1,1) ,order) == true +@test _q_filter( (3,1) ,order) == false + +@test _qs_filter( (1,2) ,order) == true +@test _qs_filter( (2,0) ,order) == true +@test _qs_filter( (2,2) ,order) == true +@test _qs_filter( (1,1) ,order) == false +@test _qs_filter( (3,1) ,order) == false + +@test _p_filter( (1,2) ,order) == false +@test _p_filter( (2,0) ,order) == true +@test _p_filter( (2,2) ,order) == false +@test _p_filter( (1,1) ,order) == true +@test _p_filter( (3,1) ,order) == false +@test _p_filter( (0,1) ,order) == true + +@test _ps_filter( (1,2) ,order) == false +@test _ps_filter( (2,0) ,order) == true +@test _ps_filter( (2,2) ,order) == false +@test _ps_filter( (1,1) ,order) == true +@test _ps_filter( (3,1) ,order) == false + end # module From 6d8ec255063dc64546e07cca79c87c578e23f383 Mon Sep 17 00:00:00 2001 From: Antoine Marteau Date: Mon, 6 Jan 2025 11:11:00 +1100 Subject: [PATCH 089/105] fix CompWiseTensorPolyBases test coverage and Hessian --- src/Polynomials/CompWiseTensorPolyBases.jl | 18 +-- test/PolynomialsTests/PGradBasesTests.jl | 20 ++- test/PolynomialsTests/QCurlGradBasesTests.jl | 24 ++- test/PolynomialsTests/QGradBasesTests.jl | 146 ++++++++++++++----- 4 files changed, 147 insertions(+), 61 deletions(-) diff --git a/src/Polynomials/CompWiseTensorPolyBases.jl b/src/Polynomials/CompWiseTensorPolyBases.jl index 6b647ab19..d0d1a2283 100644 --- a/src/Polynomials/CompWiseTensorPolyBases.jl +++ b/src/Polynomials/CompWiseTensorPolyBases.jl @@ -148,22 +148,6 @@ function _gradient_nd!( end end -""" - _comp_wize_set_derivative!(r::AbstractMatrix{G},i,s,k,::Type{<:Real}) - -``` -r[i,k] = G(s…) = (Dbᵏ)(xi) -return k+1 -``` - -""" -function _comp_wize_set_derivative!( - r::AbstractMatrix{G},i,s,k,vall,::Type{<:Real}) where G - - @inbounds r[i,k] = s - k+1 -end - """ _comp_wize_set_derivative!(r::AbstractMatrix{G},i,s,k,::Type{V}) @@ -243,7 +227,7 @@ function _hessian_nd!( end end - k = _comp_wize_set_derivative!(r,i,s,k,l,V) + k = _comp_wize_set_derivative!(r,i,s,k,Val(l),V) end end end diff --git a/test/PolynomialsTests/PGradBasesTests.jl b/test/PolynomialsTests/PGradBasesTests.jl index 5e2de9e21..d09cfb940 100644 --- a/test/PolynomialsTests/PGradBasesTests.jl +++ b/test/PolynomialsTests/PGradBasesTests.jl @@ -11,10 +11,11 @@ np = 3 x = fill(xi,np) T = Float64 +PT = Monomial order = 0 D = 3 -b = NedelecPolyBasisOnSimplex{D}(Monomial, T, order) +b = NedelecPolyBasisOnSimplex{D}(PT, T, order) V = VectorValue{D, Float64} v = V[(1,0,0),(0,1,0),(0,0,1),(-3,2,0),(-5,0,2),(0,-5,3)] @@ -35,7 +36,7 @@ x = fill(xi,np) order = 0 D = 2 -b = NedelecPolyBasisOnSimplex{D}(Monomial, T, order) +b = NedelecPolyBasisOnSimplex{D}(PT, T, order) V = VectorValue{D, Float64} v = V[(1,0),(0,1),(-3,2)] G = gradient_type(V,xi) @@ -45,5 +46,20 @@ bx = repeat(permutedims(v),np) test_field_array(b,x,bx,grad=∇bx) test_field_array(b,x[1],bx[1,:],grad=∇bx[1,:]) +# 1D + +order = 0 +D = 1 +T = Float64 +V = VectorValue{D,T} +b = QGradBasis(PT,Val(D),T,order) + +@test b isa UniformPolyBasis{D,V,order+1,PT} + +@test_throws AssertionError QGradBasis(PT,Val(D),V,order) + +# D > 3 not implemented + +@test_throws ErrorException NedelecPolyBasisOnSimplex{4}(PT, T, order) end # module diff --git a/test/PolynomialsTests/QCurlGradBasesTests.jl b/test/PolynomialsTests/QCurlGradBasesTests.jl index 6c345aadf..30384f53f 100644 --- a/test/PolynomialsTests/QCurlGradBasesTests.jl +++ b/test/PolynomialsTests/QCurlGradBasesTests.jl @@ -9,16 +9,20 @@ xi = Point(2,3) np = 5 x = fill(xi,np) +PT = Monomial + order = 0 D = 2 T = Float64 V = VectorValue{D,T} G = gradient_type(V,xi) -b = QCurlGradBasis(Monomial, Val(D),T,order) +b = QCurlGradBasis(PT,Val(D),T,order) @test length(b) == 4 @test get_order(b) == 1 +@test_throws AssertionError QCurlGradBasis(PT,Val(D),V,order) + xi = Point(2,3,5) np = 5 x = fill(xi,np) @@ -28,7 +32,7 @@ D = 3 T = Float64 V = VectorValue{D,T} G = gradient_type(V,xi) -b = QCurlGradBasis(Monomial, Val(D),T,order) +b = QCurlGradBasis(PT, Val(D),T,order) v = V[ (1.0, 0.0, 0.0), (2.0, 0.0, 0.0), @@ -57,7 +61,7 @@ D = 2 T = Float64 V = VectorValue{D,T} G = gradient_type(V,xi) -b = QCurlGradBasis(Monomial, Val(D),T,order) +b = QCurlGradBasis(PT, Val(D),T,order) v = V[ ( 1., 0. ), ( 2., 0. ), ( 4., 0. ), ( 3., 0. ), ( 6., 0. ), (12., 0. ), @@ -82,4 +86,18 @@ bx = repeat(permutedims(v),np) test_field_array(b,x,bx,grad=∇bx) test_field_array(b,x[1],bx[1,:],grad=∇bx[1,:]) +# 1D + +order = 0 +D = 1 +T = Float64 +V = VectorValue{D,T} +G = gradient_type(V,xi) +b = QCurlGradBasis(PT,Val(D),T,order) + +@test b isa UniformPolyBasis{D,V,order+1,PT} + +@test_throws AssertionError QCurlGradBasis(PT,Val(D),V,order) + + end # module diff --git a/test/PolynomialsTests/QGradBasesTests.jl b/test/PolynomialsTests/QGradBasesTests.jl index 9a189ca30..1a7d0efd1 100644 --- a/test/PolynomialsTests/QGradBasesTests.jl +++ b/test/PolynomialsTests/QGradBasesTests.jl @@ -9,17 +9,21 @@ using Gridap.Polynomials xi = Point(2,3) np = 5 x = fill(xi,np) +PT = Monomial +# 3D order = 0 D = 2 T = Float64 V = VectorValue{D,T} G = gradient_type(V,xi) -b = QGradBasis(Monomial, Val(D),T,order) +b = QGradBasis(PT, Val(D),T,order) @test length(b) == 4 @test get_order(b) == 1 +@test_throws AssertionError QGradBasis(PT,Val(D),V,order) + xi = Point(2,3,5) np = 5 x = fill(xi,np) @@ -29,35 +33,57 @@ D = 3 T = Float64 V = VectorValue{D,T} G = gradient_type(V,xi) -b = QGradBasis(Monomial, Val(D),T,order) +H = gradient_type(G,xi) +b = QGradBasis(PT, Val(D),T,order) v = V[ +# (1.0, 0.0, 0.0), ( y , 0.0, 0.0), ( z , 0.0, 0.0), ( yz , 0.0, 0.0), +# (0.0, 1.0, 0.0), (0.0, x , 0.0), (0.0, z , 0.0), (0.0, xz , 0.0), +# (0.0, 0.0, 1.0), (0.0, 0.0, x ), (0.0, 0.0, y ), (0.0, 0.0, xy)] (1.0, 0.0, 0.0), (3.0, 0.0, 0.0), (5.0, 0.0, 0.0), (15.0, 0.0, 0.0), (0.0, 1.0, 0.0), (0.0, 2.0, 0.0), (0.0, 5.0, 0.0), (0.0, 10.0, 0.0), (0.0, 0.0, 1.0), (0.0, 0.0, 2.0), (0.0, 0.0, 3.0), (0.0, 0.0, 6.0)] g = G[ - (0.0, 0.0, 0.0, 0.0, 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, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0), - (0.0, 5.0, 3.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.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, 1.0, 0.0, 0.0, 0.0), - (0.0, 0.0, 0.0, 5.0, 0.0, 2.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.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, 1.0, 0.0), - (0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 3.0, 2.0, 0.0)] - -bx = repeat(permutedims(v),np) + (0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0), # ( 1 , 0 , 0 ) + (0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0), # ( y , 0 , 0 ) + (0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0), # ( z , 0 , 0 ) + (0.0, 5.0, 3.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0), # ( yz , 0 , 0 ) + (0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0), # ( 0 , 1 , 0 ) + (0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0), # ( 0 , x , 0 ) + (0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0), # ( 0 , z , 0 ) + (0.0, 0.0, 0.0, 5.0, 0.0, 2.0, 0.0, 0.0, 0.0), # ( 0 , xz, 0 ) + (0.0, 0.0, 0.0, 0.0, 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, 1.0, 0.0, 0.0), # ( 0 , 0 , x ) + (0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0), # ( 0 , 0 , y ) + (0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 3.0, 2.0, 0.0)] # ( 0 , 0 , xy ) + +h = H[ + (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, 0.0, 0.0, 0.0, 0.0, 0.0, 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.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), # ( y , 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, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0), # ( z , 0 , 0 ) + (0.0, 0.0, 0.0, 0.0, 0.0, 1. , 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.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0), # ( yz , 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, 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 , 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.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0), # ( 0 , x , 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.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0), # ( 0 , z , 0 ) + (0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1. , 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 , xz, 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.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 , 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, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0), # ( 0 , 0 , x ) + (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, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0), # ( 0 , 0 , y ) + (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, 0.0, 0.0, 0.0, 1. , 0.0, 1. , 0.0, 0.0, 0.0, 0.0, 0.0)] # ( 0 , 0 , xy ) + +bx = repeat(permutedims(v),np) ∇bx = repeat(permutedims(g),np) +Hbx = repeat(permutedims(h),np) evaluate(b,x) evaluate(Broadcasting(∇)(b),x) +evaluate(Broadcasting(∇∇)(b),x) -test_field_array(b,x,bx,grad=∇bx) -test_field_array(b,x[1],bx[1,:],grad=∇bx[1,:]) +test_field_array(b,x,bx,grad=∇bx,gradgrad=Hbx) +test_field_array(b,x[1],bx[1,:],grad=∇bx[1,:],gradgrad=Hbx[1,:]) + +# 2D xi = Point(2,3) np = 5 @@ -68,36 +94,78 @@ D = 2 T = Float64 V = VectorValue{D,T} G = gradient_type(V,xi) -b = QGradBasis(Monomial, Val(D),T,order) - -v = V[ - (1.0, 0.0), (0.0, 1.0), (2.0, 0.0), (0.0, 3.0), - (3.0, 0.0), (0.0, 2.0), (6.0, 0.0), (0.0, 6.0), - (9.0, 0.0), (0.0, 4.0), (18.0, 0.0), (0.0, 12.0)] +H = gradient_type(G,xi) +b = QGradBasis(PT, Val(D),T,order) v = V[ +# (1., 0.), (x , 0.), (y , 0.), (xy, 0.), (y², 0.), (xy², 0.), +# (0., 1.), (0., x ), (0., x²), (0., y ), (0., xy), (0., x²y)] (1., 0.), (2., 0.), (3., 0.), (6., 0.), (9., 0.), (18., 0.), (0., 1.), (0., 2.), (0., 4.), (0., 3.), (0., 6.), (0., 12.)] - g = G[ - (0., 0., 0., 0.), - (1., 0., 0., 0.), - (0., 1., 0., 0.), - (3., 2., 0., 0.), - (0., 6., 0., 0.), - (9.,12., 0., 0.), - (0., 0., 0., 0.), - (0., 0., 1., 0.), - (0., 0., 4., 0.), - (0., 0., 0., 1.), - (0., 0., 3., 2.), - (0., 0.,12., 4.)] + # # ∂xV₁ ∂yV₁ ∂ₓV₂ ∂xV₂ # (V₁, V₂ ) + (0., 0., 0., 0.), # (0 , 0 , 0 , 0 ) # (1 , 0 ) + (1., 0., 0., 0.), # (1 , 0 , 0 , 0 ) # (x , 0 ) + (0., 1., 0., 0.), # (0 , 1 , 0 , 0 ) # (y , 0 ) + (3., 2., 0., 0.), # (y , x, 0 , 0 ) # (xy, 0 ) + (0., 6., 0., 0.), # (0., 2y, 0 , 0 ) # (y², 0 ) + (9., 12., 0., 0.), # (y², 2xy, 0 , 0 ) # (xy²,0 ) + (0., 0., 0., 0.), # (0 , 0 , 0 , 0 ) # (0 , 1 ) + (0., 0., 1., 0.), # (0 , 0 , 1 , 0 ) # (0 , x ) + (0., 0., 4., 0.), # (0 , 0 , 2x, 0 ) # (0 , x² ) + (0., 0., 0., 1.), # (0 , 0 , 0 , 1 ) # (0 , y ) + (0., 0., 3., 2.), # (0 , 0 , y, x ) # (0 , xy ) + (0., 0., 12., 4.)] # (0 , 0 , 2xy, x²) # (0 , x²y) + +h = H[ + # ∂xxV₁ ∂yxV₁ ∂xyV₁ ∂yyV₁ ∂xxV₂ ∂yxV₂ ∂xxV₂ ∂yxV₂ # (V₁, V₂ ) + (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 ), # (x , 0. ) # (0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ) + (0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ), # (y , 0. ) # (0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ) + (0 , 1 , 1 , 0 , 0 , 0 , 0 , 0 ), # (xy, 0. ) # (0 , 1 , 1, 0, 0 , 0 , 0 , 0 ) + (0 , 0., 0 , 2 , 0 , 0 , 0 , 0 ), # (y², 0. ) # (0 , 0., 0, 2, 0 , 0 , 0 , 0 ) + (0 , 6., 6., 4., 0 , 0 , 0 , 0 ), # (xy², 0.) # (0 , 2y, 2y, 2x, 0 , 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., x ) # (0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ) + (0 , 0 , 0 , 0 , 2 , 0 , 0 , 0 ), # (0., x² ) # (0 , 0 , 0 , 0 , 2 , 0 , 0 , 0 ) + (0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ), # (0., y ) # (0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ) + (0 , 0 , 0 , 0 , 0 , 1 , 1 , 0 ), # (0., xy ) # (0 , 0 , 0 , 0 , 0 , 1 , 1 , 0 ) + (0 , 0 , 0 , 0 , 6., 4., 4., 0 )] # (0., x²y) # (0 , 0 , 0 , 0 , 2y, 2x, 2x, 0 ) bx = repeat(permutedims(v),np) ∇bx = repeat(permutedims(g),np) -test_field_array(b,x,bx,grad=∇bx) -test_field_array(b,x[1],bx[1,:],grad=∇bx[1,:]) +Hbx = repeat(permutedims(h),np) +test_field_array(b,x,bx,grad=∇bx,gradgrad=Hbx) +test_field_array(b,x[1],bx[1,:],grad=∇bx[1,:],gradgrad=Hbx[1,:]) + + +# 1D + +order = 0 +D = 1 +T = Float64 +V = VectorValue{D,T} +b = QGradBasis(PT,Val(D),T,order) + +@test b isa UniformPolyBasis{D,V,order+1,PT} + +@test_throws AssertionError QGradBasis(PT,Val(D),V,order) + +# Misc + +# Derivatives not implemented for symetric tensor types +T = Float64 +V = SymTensorValue{D,T} +G = gradient_type(V,xi) +r = zeros(G, (1,1)) +@test_throws ErrorException Polynomials._comp_wize_set_derivative!(r,0,0,0,V) + +T = Float64 +V = SymTracelessTensorValue{D,T} +G = gradient_type(V,xi) +r = zeros(G, (1,1)) +@test_throws ErrorException Polynomials._comp_wize_set_derivative!(r,0,0,0,V) end # module From d2e94acef64238875857316ef59408c91c3cb1ba Mon Sep 17 00:00:00 2001 From: Antoine Marteau Date: Mon, 6 Jan 2025 12:12:50 +1100 Subject: [PATCH 090/105] fix PolynomialInterfaces test coverage and isHierarchical --- src/Polynomials/PolynomialInterfaces.jl | 2 +- .../PolynomialInterfacesTests.jl | 81 +++++++++++++++++++ test/PolynomialsTests/runtests.jl | 2 + 3 files changed, 84 insertions(+), 1 deletion(-) create mode 100644 test/PolynomialsTests/PolynomialInterfacesTests.jl diff --git a/src/Polynomials/PolynomialInterfaces.jl b/src/Polynomials/PolynomialInterfaces.jl index 26509f444..2dcf212c3 100644 --- a/src/Polynomials/PolynomialInterfaces.jl +++ b/src/Polynomials/PolynomialInterfaces.jl @@ -21,7 +21,7 @@ Return `true` if the 1D basis of order `K` of the given [`Polynomial`](@ref) basis family is the union of the basis of order `K-1` and an other order `K` polynomial. Equivalently, if the iᵗʰ basis polynomial is of order i-1. """ -isHierarchical(::Type{Polynomial}) = @abstractmethod +isHierarchical(::Type{<:Polynomial}) = @abstractmethod ########################################### diff --git a/test/PolynomialsTests/PolynomialInterfacesTests.jl b/test/PolynomialsTests/PolynomialInterfacesTests.jl new file mode 100644 index 000000000..7d4660761 --- /dev/null +++ b/test/PolynomialsTests/PolynomialInterfacesTests.jl @@ -0,0 +1,81 @@ +module ChangeBasisTests + +using Test +using Gridap.Fields +using Gridap.Polynomials +using StaticArrays + +xi = Point(0.) +np = 5 +x = fill(xi,np) + +###################### +# Polynomial interface +###################### + +struct MockPolynomial <: Polynomial end +@test_throws ErrorException isHierarchical(Polynomial) + +# Interfaces to implement +@test_throws ErrorException isHierarchical(MockPolynomial) + +D = 1 +K = 0 +c = zero(MMatrix{D,K+1}) + +@test_throws ErrorException Polynomials._evaluate_1d!(MockPolynomial, Val(1), c, xi, 1) +@test_throws ErrorException Polynomials._gradient_1d!(MockPolynomial, Val(1), c, xi, 1) +@test_throws ErrorException Polynomials._hessian_1d!( MockPolynomial, Val(1), c, xi, 1) +@test_throws ErrorException Polynomials._derivatives_1d!(MockPolynomial, Val(1), (nothing,nothing,nothing,nothing), xi, 1) + +function Polynomials._evaluate_1d!( + ::Type{MockPolynomial},::Val{K}, cc::AbstractMatrix{T}, xi, d) where {K,T<:Number} + + cc[1,1] = 1. +end +Polynomials._derivatives_1d!(MockPolynomial, Val(1), (c,), xi, 1) +@test c[1][1] == 1. + +########################### +# PolynomialBasis interface +########################### + +T = Float64 +D = 1 +struct MockPolyBasis <: PolynomialBasis{D,T,0,MockPolynomial} end + +mb = MockPolyBasis() + + +# Implemented interfaces +@test IndexStyle(mb) == IndexLinear() +@test return_type(mb) == T +@test get_order(mb) == 0 +@test mb[1] == MockPolynomial() + + +# Interfaces to implement +@test_throws ErrorException size(mb) +import Base.size +Base.size(::MockPolyBasis) = (1,) +@test length(mb) == 1 + + +r, c = return_cache(mb,x) + +@test_throws ErrorException Polynomials._evaluate_nd!(mb, xi, r, 1, c) + +∇mb = FieldGradientArray{1}(mb) +r, c, g = return_cache(∇mb,x) +s = MVector{D,T}(0.) + +@test_throws ErrorException Polynomials._gradient_nd!(mb, xi, r, 1, c, g, s) + +Hmb = FieldGradientArray{2}(mb) +r, c, g, h = return_cache(Hmb,x) +s = MMatrix{D,D,T}(0.) + +@test_throws ErrorException Polynomials._hessian_nd!(mb, xi, r, 1, c, g, h, s) + + +end diff --git a/test/PolynomialsTests/runtests.jl b/test/PolynomialsTests/runtests.jl index 716a6344b..a21785bf1 100644 --- a/test/PolynomialsTests/runtests.jl +++ b/test/PolynomialsTests/runtests.jl @@ -2,6 +2,8 @@ module PolynomialsTests using Test +@testset "PolynomialInterfaces" begin include("PolynomialInterfacesTests.jl") end + @testset "MonomialBases" begin include("MonomialBasesTests.jl") end @testset "QGradBases" begin include("QGradBasesTests.jl") end From 6ecc30960d9c0fe2418e86623990755d019da000 Mon Sep 17 00:00:00 2001 From: Antoine Marteau Date: Mon, 6 Jan 2025 12:37:40 +1100 Subject: [PATCH 091/105] fix ModalC0Bases coverage tests and 1 bug --- src/Polynomials/ModalC0Bases.jl | 2 +- test/PolynomialsTests/ModalC0BasesTests.jl | 18 ++++++++++++++++++ 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/src/Polynomials/ModalC0Bases.jl b/src/Polynomials/ModalC0Bases.jl index b539746a9..6e89b1f22 100644 --- a/src/Polynomials/ModalC0Bases.jl +++ b/src/Polynomials/ModalC0Bases.jl @@ -245,7 +245,7 @@ end @inline @generated function _set_derivative_mc0!( r::AbstractMatrix{G},i1,s,k,l,::Type{V}) where {V,G} # Git blame me for readable non-generated version - @notimplementedif num_indep_components(G) != num_components(G) "Not implemented for symmetric Jacobian or Hessian" + @notimplementedif num_indep_components(V) != num_components(V) "Not implemented for symmetric Jacobian or Hessian" m = Array{String}(undef, size(G)) N_val_dims = length(size(V)) diff --git a/test/PolynomialsTests/ModalC0BasesTests.jl b/test/PolynomialsTests/ModalC0BasesTests.jl index 7b1b14c1d..fd24eac70 100644 --- a/test/PolynomialsTests/ModalC0BasesTests.jl +++ b/test/PolynomialsTests/ModalC0BasesTests.jl @@ -4,6 +4,7 @@ using Test using Gridap.TensorValues using Gridap.Fields using Gridap.Polynomials +using StaticArrays # using BenchmarkTools import Gridap.Fields: Broadcasting @@ -62,5 +63,22 @@ H = gradient_type(G,x1) @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) ] +# Misc + +# Derivatives not implemented for symetric tensor types + +D = 2 +T = Float64 +V = SymTensorValue{D,T} +G = gradient_type(V,x1) +s = MVector(0.,0.) +r = zeros(G, (1,1)) +@test_throws ErrorException Polynomials._set_derivative_mc0!(r,1,s,0,0,V) + +V = SymTracelessTensorValue{D,T} +G = gradient_type(V,x1) +r = zeros(G, (1,1)) +@test_throws ErrorException Polynomials._set_derivative_mc0!(r,1,s,0,0,V) + end # module From 9555fe9413b1a2010acce9bc37d16c4c99c2174e Mon Sep 17 00:00:00 2001 From: Antoine Marteau Date: Mon, 6 Jan 2025 13:00:10 +1100 Subject: [PATCH 092/105] fix P[Curl]Grad coverage and API for 1D + isHierarchical cov --- src/Polynomials/MonomialBases.jl | 5 +++++ src/Polynomials/NedelecPolyBases.jl | 4 ++-- test/PolynomialsTests/BernsteinBasesTests.jl | 6 ++++++ test/PolynomialsTests/LegendreBasesTests.jl | 2 ++ test/PolynomialsTests/MonomialBasesTests.jl | 2 ++ test/PolynomialsTests/PCurlGradBasesTests.jl | 20 ++++++++++++++++++-- test/PolynomialsTests/PGradBasesTests.jl | 11 +++++++++-- test/PolynomialsTests/QCurlGradBasesTests.jl | 1 - 8 files changed, 44 insertions(+), 7 deletions(-) diff --git a/src/Polynomials/MonomialBases.jl b/src/Polynomials/MonomialBases.jl index 32ce2fbf0..58a6282d7 100644 --- a/src/Polynomials/MonomialBases.jl +++ b/src/Polynomials/MonomialBases.jl @@ -26,6 +26,11 @@ MonomialBasis(args...) = UniformPolyBasis(Monomial, args...) function PGradBasis(::Type{Monomial},::Val{D},::Type{T},order::Int) where {D,T} NedelecPolyBasisOnSimplex{D}(Monomial,T,order) end +function PGradBasis(::Type{Monomial},::Val{1},::Type{T},order::Int) where T + @check T<:Real "T needs to be <:Real since represents the type of the components of the vector value" + V = VectorValue{1,T} + UniformPolyBasis(Monomial, Val(1), V, order+1) +end # 1D evaluation implementation diff --git a/src/Polynomials/NedelecPolyBases.jl b/src/Polynomials/NedelecPolyBases.jl index ebd65130b..807c2d4a8 100644 --- a/src/Polynomials/NedelecPolyBases.jl +++ b/src/Polynomials/NedelecPolyBases.jl @@ -2,6 +2,7 @@ struct NedelecPolyBasisOnSimplex{D,V,K,PT} <: PolynomialBasis{D,V,K,PT} order::Int function NedelecPolyBasisOnSimplex{D}(::Type{PT},::Type{T},order::Integer) where {D,PT,T} @check T<:Real "T needs to be <:Real since represents the type of the components of the vector value" + @notimplementedif !(D in (2,3)) K = Int(order)+1 V = VectorValue{D,T} new{D,V,K,PT}(Int(order)) @@ -242,7 +243,7 @@ Return a basis of ℕ𝔻ᴰₙ(△) = (ℙₙ)ᴰ ⊕ x × (ℙₙ \\ ℙₙ₋₁)ᴰ with n=`order`, the polynomial space for Nedelec elements on `D`-dimensional -simplices with scalar type `T`. +simplices with scalar type `T`. `D` must be 1, 2 or 3. The `order`=n argument has the following meaning: the curl of the functions in this basis is in ℙₙ. @@ -262,4 +263,3 @@ function PGradBasis(::Type{PT},::Val{D},::Type{T},order::Int) where {PT,D,T} @notimplemented "only implemented for monomials" end - diff --git a/test/PolynomialsTests/BernsteinBasesTests.jl b/test/PolynomialsTests/BernsteinBasesTests.jl index 33f007b4c..17ffc99ec 100644 --- a/test/PolynomialsTests/BernsteinBasesTests.jl +++ b/test/PolynomialsTests/BernsteinBasesTests.jl @@ -5,6 +5,8 @@ using Gridap.TensorValues using Gridap.Fields using Gridap.Polynomials +@test isHierarchical(Bernstein) == false + np = 3 x = [Point(0.),Point(1.),Point(.4)] xi = x[1] @@ -15,6 +17,7 @@ V = Float64 G = gradient_type(V,xi) H = gradient_type(G,xi) + # order 0 degenerated case order = 0 @@ -32,6 +35,7 @@ Hbx = repeat(permutedims(h),np) test_field_array(b,x,bx,grad=∇bx,gradgrad=Hbx) test_field_array(b,x[1],bx[1,:],grad=∇bx[1,:],gradgrad=Hbx[1,:]) + # Order 1 order = 1 @@ -52,6 +56,7 @@ Hbx = H[ 0. 0. test_field_array(b,x,bx,grad=∇bx,gradgrad=Hbx) test_field_array(b,x[1],bx[1,:],grad=∇bx[1,:],gradgrad=Hbx[1,:]) + # Order 2 order = 2 @@ -73,6 +78,7 @@ Hbx = H[ 2. -4. 2. test_field_array(b,x,bx,≈, grad=∇bx,gradgrad=Hbx) test_field_array(b,x[1],bx[1,:],grad=∇bx[1,:],gradgrad=Hbx[1,:]) + # Order 3 order = 3 diff --git a/test/PolynomialsTests/LegendreBasesTests.jl b/test/PolynomialsTests/LegendreBasesTests.jl index 7072855af..404492b77 100644 --- a/test/PolynomialsTests/LegendreBasesTests.jl +++ b/test/PolynomialsTests/LegendreBasesTests.jl @@ -6,6 +6,8 @@ using Gridap.Fields using Gridap.Fields: Broadcasting using Gridap.Polynomials +@test isHierarchical(Legendre) == true + # Real-valued Q space with isotropic order x1 = Point(0.0) diff --git a/test/PolynomialsTests/MonomialBasesTests.jl b/test/PolynomialsTests/MonomialBasesTests.jl index 1114f5656..d3a734982 100644 --- a/test/PolynomialsTests/MonomialBasesTests.jl +++ b/test/PolynomialsTests/MonomialBasesTests.jl @@ -7,6 +7,8 @@ using Gridap.Polynomials using Gridap.Polynomials: _q_filter, _qs_filter, _p_filter, _ps_filter +@test isHierarchical(Monomial) == true + xi = Point(2,3) np = 5 x = fill(xi,np) diff --git a/test/PolynomialsTests/PCurlGradBasesTests.jl b/test/PolynomialsTests/PCurlGradBasesTests.jl index e446296d3..07d171796 100644 --- a/test/PolynomialsTests/PCurlGradBasesTests.jl +++ b/test/PolynomialsTests/PCurlGradBasesTests.jl @@ -10,12 +10,14 @@ xi = Point(4,2) np = 1 x = fill(xi,np) +PT = Monomial + order = 2 D = 2 T = Float64 V = VectorValue{D,T} G = gradient_type(V,xi) -b = PCurlGradBasis(Monomial, Val(D),T,order) +b = PCurlGradBasis(PT, Val(D),T,order) v = V[ (1.0, 0.0), (4.0, 0.0), (16.0, 0.0), (2.0, 0.0), (8.0, 0.0), (4.0, 0.0), # pterm ex @@ -57,9 +59,23 @@ D = 3 T = Float64 V = VectorValue{D,T} G = gradient_type(V,xi) -b = PCurlGradBasis(Monomial, Val(D),T,order) +b = PCurlGradBasis(PT, Val(D),T,order) @test length(b) == 15 @test get_order(b) == 2 + +# 1D + +order = 0 +D = 1 +T = Float64 +V = VectorValue{D,T} +b = PCurlGradBasis(PT,Val(D),T,order) + +@test b isa UniformPolyBasis{D,V,order+1,PT} + +@test_throws AssertionError PCurlGradBasis(PT,Val(D),V,order) + + end # module diff --git a/test/PolynomialsTests/PGradBasesTests.jl b/test/PolynomialsTests/PGradBasesTests.jl index d09cfb940..01138775d 100644 --- a/test/PolynomialsTests/PGradBasesTests.jl +++ b/test/PolynomialsTests/PGradBasesTests.jl @@ -11,6 +11,11 @@ np = 3 x = fill(xi,np) T = Float64 +D = 2 +for PT in (Legendre, Chebyshev, ModalC0, Bernstein) + @test_throws ErrorException PGradBasis(PT,Val(D),T,0) +end + PT = Monomial order = 0 @@ -46,20 +51,22 @@ bx = repeat(permutedims(v),np) test_field_array(b,x,bx,grad=∇bx) test_field_array(b,x[1],bx[1,:],grad=∇bx[1,:]) + # 1D order = 0 D = 1 T = Float64 V = VectorValue{D,T} -b = QGradBasis(PT,Val(D),T,order) +b = PGradBasis(PT,Val(D),T,order) @test b isa UniformPolyBasis{D,V,order+1,PT} -@test_throws AssertionError QGradBasis(PT,Val(D),V,order) +@test_throws AssertionError PGradBasis(PT,Val(D),V,order) # D > 3 not implemented @test_throws ErrorException NedelecPolyBasisOnSimplex{4}(PT, T, order) + end # module diff --git a/test/PolynomialsTests/QCurlGradBasesTests.jl b/test/PolynomialsTests/QCurlGradBasesTests.jl index 30384f53f..30a7d3207 100644 --- a/test/PolynomialsTests/QCurlGradBasesTests.jl +++ b/test/PolynomialsTests/QCurlGradBasesTests.jl @@ -92,7 +92,6 @@ order = 0 D = 1 T = Float64 V = VectorValue{D,T} -G = gradient_type(V,xi) b = QCurlGradBasis(PT,Val(D),T,order) @test b isa UniformPolyBasis{D,V,order+1,PT} From 6b07a81a6f1c3be47499441f58de6769e90d05a9 Mon Sep 17 00:00:00 2001 From: Antoine Marteau Date: Mon, 6 Jan 2025 15:09:41 +1100 Subject: [PATCH 093/105] fix Bernstein test coverage --- test/PolynomialsTests/BernsteinBasesTests.jl | 35 +++++++++++++++----- 1 file changed, 26 insertions(+), 9 deletions(-) diff --git a/test/PolynomialsTests/BernsteinBasesTests.jl b/test/PolynomialsTests/BernsteinBasesTests.jl index 17ffc99ec..1a06398f1 100644 --- a/test/PolynomialsTests/BernsteinBasesTests.jl +++ b/test/PolynomialsTests/BernsteinBasesTests.jl @@ -4,6 +4,8 @@ using Test using Gridap.TensorValues using Gridap.Fields using Gridap.Polynomials +using Gridap.Polynomials: binoms +using ForwardDiff @test isHierarchical(Bernstein) == false @@ -81,26 +83,41 @@ test_field_array(b,x[1],bx[1,:],grad=∇bx[1,:],gradgrad=Hbx[1,:]) # Order 3 +function bernstein(K,N) + b = binoms(Val(K)) + t -> b[N+1]*(t^N)*((1-t)^(K-N)) +end +_∇(b) = t -> ForwardDiff.derivative(b, t) +_H(b) = t -> ForwardDiff.derivative(y -> ForwardDiff.derivative(b, y), t) + order = 3 b = BernsteinBasis(Val(1),V,order) # x=x^1; x2 = x^2; x3 = x^3 # -x3+3x2-3x+1 3x3-6x2+3x -3x3+3x2 x3 -bx = [ 1.0 0.0 0.0 0.0 - 0.0 0.0 0.0 1.0 - .216 .432 .288 .064] +bx = [ bernstein(order,n)( xi[1]) for xi in x, n in 0:order] # -3x2+6x-3 9x2-12x+3 -9x2+6x 3x2 -∇bx = G[ -3. 3. 0. 0. - 0. 0. -3. 3. - -1.08 -.36 .96 .48] +∇bx = [ G(_∇(bernstein(order,n))(xi[1])) for xi in x, n in 0:order] # -6x+6 18x-12 -18x+6 6x -Hbx = H[ 6. -12. 6. 0. - 0. 6. -12. 6. - 3.6 -4.8 -1.2 2.4] +Hbx = [ H(_H(bernstein(order,n))(xi[1])) for xi in x, n in 0:order] + +test_field_array(b,x,bx,≈, grad=∇bx,gradgrad=Hbx) +test_field_array(b,x[1],bx[1,:],grad=∇bx[1,:],gradgrad=Hbx[1,:]) + + +# Order 4 + +order = 4 +b = BernsteinBasis(Val(1),V,order) + +bx = [ bernstein(order,n)( xi[1]) for xi in x, n in 0:order] +∇bx = [ G(_∇(bernstein(order,n))(xi[1])) for xi in x, n in 0:order] +Hbx = [ H(_H(bernstein(order,n))(xi[1])) for xi in x, n in 0:order] test_field_array(b,x,bx,≈, grad=∇bx,gradgrad=Hbx) test_field_array(b,x[1],bx[1,:],grad=∇bx[1,:],gradgrad=Hbx[1,:]) + end # module From 439ea2e73f84c8e6343e10bf48aa72eac2c418e6 Mon Sep 17 00:00:00 2001 From: Antoine Marteau Date: Mon, 6 Jan 2025 17:53:26 +1100 Subject: [PATCH 094/105] Chebyshev test coverage and method ambiguities --- src/Polynomials/ChebyshevBases.jl | 26 ++-- test/PolynomialsTests/ChebyshevBasesTests.jl | 119 +++++++++++++++++++ test/PolynomialsTests/runtests.jl | 2 + 3 files changed, 135 insertions(+), 12 deletions(-) create mode 100644 test/PolynomialsTests/ChebyshevBasesTests.jl diff --git a/src/Polynomials/ChebyshevBases.jl b/src/Polynomials/ChebyshevBases.jl index 729f4f7a0..9e7878512 100644 --- a/src/Polynomials/ChebyshevBases.jl +++ b/src/Polynomials/ChebyshevBases.jl @@ -24,34 +24,37 @@ High level constructors of [`ChebyshevBasis`](@ref). """ ChebyshevBasis(args...; kind=:T) = UniformPolyBasis(Chebyshev{kind}, args...) -UniformPolyBasis{D}(::Type{Chebyshev{:U}}, args...) where D = @notimplemented "1D evaluation for second kind needed here" +function UniformPolyBasis( + ::Type{Chebyshev{:U}}, ::Val{D}, ::Type{V}, ::Int64) where {D, V} + + @notimplemented "1D evaluation for second kind need to be implemented here" +end # 1D evaluation implementation function _evaluate_1d!( - ::Type{<:Chebyshev},::Val{0},c::AbstractMatrix{T},x,d) where T<:Number + ::Type{Chebyshev{kind}},::Val{0},c::AbstractMatrix{T},x,d) where {kind,T<:Number} @inbounds c[d,1] = one(T) end function _evaluate_1d!( - ::Type{Chebyshev{:T}},::Val{K},c::AbstractMatrix{T},x,d) where {K,T<:Number} + ::Type{Chebyshev{kind}},::Val{K},c::AbstractMatrix{T},x,d) where {kind,K,T<:Number} n = K + 1 # n > 1 ξ = (2*x[d] - 1) # ξ ∈ [-1,1] ξ2 = 2*ξ @inbounds c[d,1] = one(T) - @inbounds c[d,2] = ξ + @inbounds c[d,2] = (kind == :T) ? ξ : ξ2 for i in 3:n @inbounds c[d,i] = c[d,i-1]*ξ2 - c[d,i-2] end end - function _gradient_1d!( - ::Type{<:Chebyshev},::Val{0},g::AbstractMatrix{T},x,d) where T<:Number + ::Type{Chebyshev{:T}},::Val{0},g::AbstractMatrix{T},x,d) where T<:Number @inbounds g[d,1] = zero(T) end @@ -76,13 +79,12 @@ function _gradient_1d!( end -function _hessian_1d!( - ::Type{Chebyshev{:T}},::Val{K},h::AbstractMatrix{T},x,d) where {K,T<:Number} +function _gradient_1d!( + ::Type{Chebyshev{:U}},::Val{0},g::AbstractMatrix{T},x,d) where T<:Number - @notimplemented + @inbounds g[d,1] = zero(T) end - -_evaluate_1d!(::Type{Chebyshev{:U}},::Val{K},c::AbstractMatrix{T},x,d) where {K,T<:Number} = @notimplemented -_gradient_1d!(::Type{Chebyshev{:U}},::Val{K},g::AbstractMatrix{T},x,d) where {K,T<:Number} = @notimplemented +_gradient_1d!(::Type{Chebyshev{:U}},::Val{K},h::AbstractMatrix{T},x,d) where {K,T<:Number} = @notimplemented _hessian_1d!( ::Type{Chebyshev{:U}},::Val{K},h::AbstractMatrix{T},x,d) where {K,T<:Number} = @notimplemented + diff --git a/test/PolynomialsTests/ChebyshevBasesTests.jl b/test/PolynomialsTests/ChebyshevBasesTests.jl new file mode 100644 index 000000000..b32ddfd90 --- /dev/null +++ b/test/PolynomialsTests/ChebyshevBasesTests.jl @@ -0,0 +1,119 @@ +module ChebyshevBasisTests + +using Test +using Gridap.TensorValues +using Gridap.Fields +using Gridap.Polynomials +using Gridap.Polynomials: binoms +using ForwardDiff + +@test isHierarchical(Chebyshev) == true + +np = 3 +x = [Point(2.),Point(3.),Point(4.)] +x1 = x[1] + + +# Only test 1D evaluations as tensor product structure is tested in monomial tests + +V = Float64 +G = gradient_type(V,x1) +H = gradient_type(G,x1) + +chebyshev_T(N) = t -> begin + ξ = 2t-1 + sq = sqrt(ξ*ξ-1) + .5*( (ξ - sq)^N + (ξ + sq)^N ) +end +chebyshev_U(N) = t -> begin + ξ = 2t-1 + sq = sqrt(ξ*ξ-1) + .5*( (ξ + sq)^(N+1) - (ξ - sq)^(N+1) )/sq +end +_∇(b) = t -> ForwardDiff.derivative(b, t) +_H(b) = t -> ForwardDiff.derivative(y -> ForwardDiff.derivative(b, y), t) + +###################################### +# First Kind Chebyshev ( Hessian TODO) +###################################### + +# order 0 degenerated case +order = 0 +b = ChebyshevBasis(Val(1),V,order) +@test get_order(b) == 0 +@test get_orders(b) == (0,) + +bx = [ chebyshev_T(n)( xi[1]) for xi in x, n in 0:order] +∇bx = [ G(_∇(chebyshev_T(n))(xi[1])) for xi in x, n in 0:order] +Hbx = [ H(_H(chebyshev_T(n))(xi[1])) for xi in x, n in 0:order] + +test_field_array(b,x,bx,≈, grad=∇bx)#,gradgrad=Hbx) +test_field_array(b,x[1],bx[1,:],≈,grad=∇bx[1,:])#,gradgrad=Hbx[1,:]) + + +# Order 1 +order = 1 +b = ChebyshevBasis(Val(1),V,order) + +bx = [ chebyshev_T(n)( xi[1]) for xi in x, n in 0:order] +∇bx = [ G(_∇(chebyshev_T(n))(xi[1])) for xi in x, n in 0:order] +Hbx = [ H(_H(chebyshev_T(n))(xi[1])) for xi in x, n in 0:order] + +test_field_array(b,x,bx,≈,grad=∇bx)#,gradgrad=Hbx) +test_field_array(b,x[1],bx[1,:],≈,grad=∇bx[1,:])#,gradgrad=Hbx[1,:]) + + +# Order 2 +order = 2 +b = ChebyshevBasis(Val(1),V,order) + + +bx = [ chebyshev_T(n)( xi[1]) for xi in x, n in 0:order] +∇bx = [ G(_∇(chebyshev_T(n))(xi[1])) for xi in x, n in 0:order] +Hbx = [ H(_H(chebyshev_T(n))(xi[1])) for xi in x, n in 0:order] + +test_field_array(b,x,bx,≈,grad=∇bx)#,gradgrad=Hbx) +test_field_array(b,x[1],bx[1,:],≈,grad=∇bx[1,:])#,gradgrad=Hbx[1,:]) + + +# Order 3 +order = 3 +b = ChebyshevBasis(Val(1),V,order) + +bx = [ chebyshev_T(n)( xi[1]) for xi in x, n in 0:order] +∇bx = [ G(_∇(chebyshev_T(n))(xi[1])) for xi in x, n in 0:order] +Hbx = [ H(_H(chebyshev_T(n))(xi[1])) for xi in x, n in 0:order] + +test_field_array(b,x,bx,≈, grad=∇bx)#,gradgrad=Hbx) +test_field_array(b,x[1],bx[1,:],≈,grad=∇bx[1,:])#,gradgrad=Hbx[1,:]) + + +# Order 4 +order = 4 +b = ChebyshevBasis(Val(1),V,order) + +bx = [ chebyshev_T(n)( xi[1]) for xi in x, n in 0:order] +∇bx = [ G(_∇(chebyshev_T(n))(xi[1])) for xi in x, n in 0:order] +Hbx = [ H(_H(chebyshev_T(n))(xi[1])) for xi in x, n in 0:order] + +test_field_array(b,x,bx,≈,grad=∇bx)#,gradgrad=Hbx) +test_field_array(b,x[1],bx[1,:],≈,grad=∇bx[1,:])#,gradgrad=Hbx[1,:]) + + +############################ +# Second Kind Chebyshev TODO +############################ + +@test_throws ErrorException ChebyshevBasis(Val(1),Float64,0;kind=:U) + +order = 1 +d = 1 +b = ChebyshevBasis(Val(1),V,order) +Hb = FieldGradientArray{2}(b) +r, c, g, h = return_cache(Hb,x) + +@test_throws ErrorException Polynomials._gradient_1d!(Chebyshev{:U},Val(order),g,x1,d) +@test_throws ErrorException Polynomials._hessian_1d!( Chebyshev{:U},Val(order),h,x1,d) + + +end # module diff --git a/test/PolynomialsTests/runtests.jl b/test/PolynomialsTests/runtests.jl index a21785bf1..2c25b3ae4 100644 --- a/test/PolynomialsTests/runtests.jl +++ b/test/PolynomialsTests/runtests.jl @@ -18,6 +18,8 @@ using Test @testset "LegendreBases" begin include("LegendreBasesTests.jl") end +@testset "ChebyshevBases" begin include("ChebyshevBasesTests.jl") end + @testset "BernsteinBases" begin include("BernsteinBasesTests.jl") end #@testset "ChangeBasis" begin include("ChangeBasisTests.jl") end From 038cc885bf2ba597c836c81e9a990195293f5cdc Mon Sep 17 00:00:00 2001 From: Antoine Marteau Date: Mon, 6 Jan 2025 18:06:37 +1100 Subject: [PATCH 095/105] postbone Jacobi --- NEWS.md | 8 ++++---- src/Polynomials/Polynomials.jl | 2 -- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/NEWS.md b/NEWS.md index 877a35139..83b1c74d8 100644 --- a/NEWS.md +++ b/NEWS.md @@ -12,10 +12,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Added AMR-related methods `mark` and `estimate` to `Adaptivity` module. Implemented Dorfler marking strategy. Since PR[#1063](https://github.com/gridap/Gridap.jl/pull/1063). - Documentation and refactoring of Gridap.Polynomials. Since PR[#TODO](https://github.com/gridap/Gridap.jl/pull/#TODO). -- Three new families of polynomial bases in addition to `Monomial`, `Legendre` (former `Jacobi`) and `ModalC0`: `Chebyshev`, `Bernstein` and `Jacobi` -- `MonomialBasis` and `Q[Curl]GradMonomialBasis` have been generalized to `Legendre`, `Chebyshev`, `Bernstein` and `Jacobi` using the new `UniformPolyBasis` and `CompWiseTensorPolyBasis` respectively. -- `PCurlGradMonomialBasis` has been generalized to `Legendre`, `Chebyshev` and `Jacobi` using the new `RaviartThomasPolyBasis`. -- New aliases and high level constructor for `UniformPolyBasis` (former MonomialBasis): `MonomialBasis`, `LegendreBasis`, `ChebyshevBasis`, `BernsteinBasis`, `JacobiBasis`. +- Two new families of polynomial bases in addition to `Monomial`, `Legendre` (former `Jacobi`) and `ModalC0`: `Chebyshev` and `Bernstein` +- `MonomialBasis` and `Q[Curl]GradMonomialBasis` have been generalized to `Legendre`, `Chebyshev` and `Bernstein` using the new `UniformPolyBasis` and `CompWiseTensorPolyBasis` respectively. +- `PCurlGradMonomialBasis` has been generalized to `Legendre` and `Chebyshev` using the new `RaviartThomasPolyBasis`. +- New aliases and high level constructor for `UniformPolyBasis` (former MonomialBasis): `MonomialBasis`, `LegendreBasis`, `ChebyshevBasis` and `BernsteinBasis`. - New high level constructors for Nedelec and Raviart-Thomas polynomial bases: - Nedelec on simplex `PGradBasis(PT<:Polynomial, Val(D), order)` - Nedelec on n-cubes `QGradBasis(PT<:Polynomial, Val(D), order)` diff --git a/src/Polynomials/Polynomials.jl b/src/Polynomials/Polynomials.jl index 38e0cbc28..24f1c61fb 100644 --- a/src/Polynomials/Polynomials.jl +++ b/src/Polynomials/Polynomials.jl @@ -73,8 +73,6 @@ include("MonomialBases.jl") include("LegendreBases.jl") -#include("JacobiBases.jl") - include("ChebyshevBases.jl") include("BernsteinBases.jl") From a351aa3ac3d48a919268d99f174193b8ed6fcc8d Mon Sep 17 00:00:00 2001 From: Antoine Marteau Date: Tue, 7 Jan 2025 08:50:28 +1100 Subject: [PATCH 096/105] minor coverage fix --- src/Polynomials/ChebyshevBases.jl | 7 ------- test/PolynomialsTests/ModalC0BasesTests.jl | 4 +++- 2 files changed, 3 insertions(+), 8 deletions(-) diff --git a/src/Polynomials/ChebyshevBases.jl b/src/Polynomials/ChebyshevBases.jl index 9e7878512..8ca6da07e 100644 --- a/src/Polynomials/ChebyshevBases.jl +++ b/src/Polynomials/ChebyshevBases.jl @@ -78,13 +78,6 @@ function _gradient_1d!( end end - -function _gradient_1d!( - ::Type{Chebyshev{:U}},::Val{0},g::AbstractMatrix{T},x,d) where T<:Number - - @inbounds g[d,1] = zero(T) -end - _gradient_1d!(::Type{Chebyshev{:U}},::Val{K},h::AbstractMatrix{T},x,d) where {K,T<:Number} = @notimplemented _hessian_1d!( ::Type{Chebyshev{:U}},::Val{K},h::AbstractMatrix{T},x,d) where {K,T<:Number} = @notimplemented diff --git a/test/PolynomialsTests/ModalC0BasesTests.jl b/test/PolynomialsTests/ModalC0BasesTests.jl index fd24eac70..de293238e 100644 --- a/test/PolynomialsTests/ModalC0BasesTests.jl +++ b/test/PolynomialsTests/ModalC0BasesTests.jl @@ -23,7 +23,9 @@ order = 3 a = fill(Point(-0.5),order+1) b = fill(Point(2.5),order+1) b1 = ModalC0Basis{1}(V,order,a,b) -#b1 = ModalC0Basis(Val(1),V,order,a,b) + +@test IndexStyle(b1) == IndexLinear() + ∇b1 = Broadcasting(∇)(b1) ∇∇b1 = Broadcasting(∇)(∇b1) From 1971ea594f0b2e62aadf7244ebbe573f13b6a42d Mon Sep 17 00:00:00 2001 From: Antoine Marteau Date: Tue, 7 Jan 2025 13:04:01 +1100 Subject: [PATCH 097/105] optimize and test Bernstein in place De Casteljau combine De Casteljau's for _derivatives_1d add test --- src/Polynomials/BernsteinBases.jl | 193 ++++++++++++++----- src/Polynomials/PolynomialInterfaces.jl | 26 +-- test/PolynomialsTests/BernsteinBasesTests.jl | 42 +++- 3 files changed, 196 insertions(+), 65 deletions(-) diff --git a/src/Polynomials/BernsteinBases.jl b/src/Polynomials/BernsteinBases.jl index fba1b0b21..40489e62b 100644 --- a/src/Polynomials/BernsteinBases.jl +++ b/src/Polynomials/BernsteinBases.jl @@ -26,8 +26,6 @@ BernsteinBasis(args...) = UniformPolyBasis(Bernstein, args...) # 1D evaluation implementation -# TODO Optimize with in-place De Casteljau - """ binoms(::Val{K}) @@ -36,22 +34,53 @@ Returns the tuple of binomials ( C₍ₖ₀₎, C₍ₖ₁₎, ..., C₍ₖₖ binoms(::Val{K}) where K = ntuple( i -> binomial(K,i-1), Val(K+1)) -# jth Bernstein poly of order K at x: -# Bᵏⱼ(x) = binom(K,j) * x^j * (1-x)^(K-j) -function _evaluate_1d!(::Type{Bernstein},::Val{K}, v::AbstractMatrix{T},x,d) where {K,T<:Number} - b = binoms(Val(K)) - n = K + 1 - @inbounds λ1 = x[d] - λ2 = one(T) - λ1 - - for i in 1:n - j = i-1 - λ1_j = λ1^(j) - λ2_j = λ2^(K-j) - @inbounds v[d,i] = b[i] * λ1_j * λ2_j # order K +function _evaluate_1d!(::Type{Bernstein},::Val{0},v::AbstractMatrix{T},x,d) where {T<:Number} + @inbounds v[d,1] = one(T) +end + +@inline function _De_Casteljau_step_1D!(v,d,i,λ1,λ2) + # i = k+1 + + # vₖ <- xvₖ₋₁ # Bᵏₖ(x) = x*Bᵏ⁻¹ₖ₋₁(x) + v[d,i] = λ2*v[d,i-1] + # vⱼ <- xvⱼ₋₁ + (1-x)vⱼ # Bᵏⱼ(x) = x*Bᵏ⁻¹ⱼ₋₁(x) + (1-x)*Bᵏ⁻¹ⱼ(x) for j = k-1, k-2, ..., 1 + for l in i-1:-1:2 + v[d,l] = λ2*v[d,l-1] + λ1*v[d,l] end + # v₀ <- (1-x)v₀ # Bᵏ₀(x) = (1-x)*Bᵏ⁻¹₀(x) + v[d,1] = λ1*v[d,1] end +# jth Bernstein poly of order K at x: +# Bᵏⱼ(x) = binom(K,j) * x^j * (1-x)^(K-j) = x*Bᵏ⁻¹ⱼ₋₁(x) + (1-x)*Bᵏ⁻¹ⱼ(x) +function _evaluate_1d!(::Type{Bernstein},::Val{K},v::AbstractMatrix{T},x,d) where {K,T<:Number} + @inbounds begin + n = K + 1 # n > 1 + λ2 = x[d] + λ1 = one(T) - λ2 + + # In place De Casteljau: init with B¹₀(x)=x and B¹₁(x)=1-x + v[d,1] = λ1 + v[d,2] = λ2 + + for i in 3:n + _De_Casteljau_step_1D!(v,d,i,λ1,λ2) + ## vₖ <- xvₖ₋₁ # Bᵏₖ(x) = x*Bᵏ⁻¹ₖ₋₁(x) + #v[d,i] = λ2*v[d,i-1] + ## vⱼ <- xvⱼ₋₁ + (1-x)vⱼ # Bᵏⱼ(x) = x*Bᵏ⁻¹ⱼ₋₁(x) + (1-x)*Bᵏ⁻¹ⱼ(x) for j = k-1, k-2, ..., 1 + #for l in i-1:-1:2 + # v[d,l] = λ2*v[d,l-1] + λ1*v[d,l] + #end + ## v₀ <- (1-x)v₀ # Bᵏ₀(x) = (1-x)*Bᵏ⁻¹₀(x) + #v[d,1] = λ1*v[d,1] + end + end + # still optimisable for K > 2/3: + # - compute bj = binoms(k,j) at compile time (binoms(Val(K)) function) + # - compute vj = xʲ*(1-x)ᴷ⁻ʲ recursively in place like De Casteljau (saving half the redundant multiplications) + # - do it in a stack allocated cache (MVector, Bumber.jl) + # - @simd affect bj * vj in v[d,i] for all j +end function _gradient_1d!(::Type{Bernstein},::Val{0},g::AbstractMatrix{T},x,d) where {T<:Number} @inbounds g[d,1] = zero(T) @@ -66,19 +95,20 @@ end # (Bᵏⱼ)'(x) = K * ( Bᵏ⁻¹ⱼ₋₁(x) - Bᵏ⁻¹ⱼ(x) ) # = K * x^(j-1) * (1-x)^(K-j-1) * ((1-x)*binom(K-1,j-1) - x*binom(K-1,j)) function _gradient_1d!(::Type{Bernstein},::Val{K}, g::AbstractMatrix{T},x,d) where {K,T<:Number} - b = binoms(Val(K-1)) - n = K + 1 - - @inbounds λ1 = x[d] - λ2 = one(T) - λ1 - - @inbounds g[1] =-K * λ2^(K-1) - @inbounds g[n] = K * λ1^(K-1) - for i in 2:n-1 - j = i-1 - λ1_j = λ1^(j-1) - λ2_j = λ2^(K-j-1) - @inbounds g[d,i] = K * λ1_j * λ2_j *(λ2*b[i-1] - λ1*b[i]) # order K-1 + @inbounds begin + n = K + 1 # n > 2 + + # De Casteljau for Bᵏ⁻¹ⱼ for j = k-1, k-2, ..., 1 + _evaluate_1d!(Bernstein,Val(K-1),g,x,d) + + # gₖ <- K*gₖ₋₁ # ∂ₓBᵏₖ(x) = K*Bᵏ⁻¹ₖ₋₁(x) + g[d,n] = K*g[d,n-1] + # gⱼ <- K(gⱼ₋₁ + gⱼ) # ∂ₓBᵏⱼ(x) = K(Bᵏ⁻¹ⱼ₋₁(x) - Bᵏ⁻¹ⱼ(x)) for j = k-1, k-2, ..., 1 + for l in n-1:-1:2 + g[d,l] = K*(g[d,l-1] - g[d,l]) + end + # g₀ <- K*g₀ # ∂ₓBᵏ₀(x) = -K*Bᵏ⁻¹₀(x) + g[d,1] = -K*g[d,1] end end @@ -103,21 +133,96 @@ end # - 2x*(1-x)*binom(K-2,j-1) + (x)^2*binom(K-2,j) # ) function _hessian_1d!(::Type{Bernstein},::Val{K},h::AbstractMatrix{T},x,d) where {K,T<:Number} - b = binoms(Val(K-2)) - n = K + 1 - C = K*(K-1) - - @inbounds λ1 = x[d] - λ2 = one(T) - λ1 - - @inbounds h[1] = C * λ2^(K-2) - @inbounds h[2] = C * (-2λ2^(K-2) + (K-2)*λ2^(K-3)*λ1) - @inbounds h[n-1]=C * (-2λ1^(K-2) + (K-2)*λ1^(K-3)*λ2) - @inbounds h[n] = C * λ1^(K-2) - for i in 3:n-2 - j = i-1 - λ1_j = λ1^(j-2) - λ2_j = λ2^(K-j-2) - @inbounds h[d,i] = C * λ1_j * λ2_j *(λ2*λ2*b[i-2] -2λ2*λ1*b[i-1] + λ1*λ1*b[i]) # order K-2 + @inbounds begin + n = K + 1 # n > 3 + KK = K*(K-1) + + # De Casteljau for Bᵏ⁻²ⱼ for j = k-2, k-3, ..., 1 + _evaluate_1d!(Bernstein,Val(K-2),h,x,d) + + # hₖ <- K(K-1)*hₖ₋₂ + h[d,n] = KK*h[d,n-2] + # hₖ₋₁ <- K(K-1)*(-2*hₖ₋₁ + hₖ₋₂) + h[d,n-1] = KK*( h[d,n-3] -2*h[d,n-2] ) + + # hⱼ <- K(K-1)(hⱼ₋₂ -2hⱼ₋₁ + hⱼ) + for l in n-2:-1:3 + h[d,l] = KK*( h[d,l-2] -2*h[d,l-1] + h[d,l] ) + end + + # h₁ <- K(K-1)*(-2h₀ + h₁) + h[d,2] = KK*( -2*h[d,1] + h[d,2] ) + # h₀ <- K(K-1)*h₀ + h[d,1] = KK*h[d,1] + end +end + +function _derivatives_1d!(::Type{Bernstein},v::Val_01,t::NTuple{2},x,d) + @inline _evaluate_1d!(Bernstein, v, t[1], x, d) + @inline _gradient_1d!(Bernstein, v, t[2], x, d) +end + +function _derivatives_1d!(::Type{Bernstein},::Val{K},t::NTuple{2},x,d) where K + @inbounds begin + n = K + 1 # n > 2 + v, g = t + + λ2 = x[d] + λ1 = one(eltype(v)) - λ2 + + # De Casteljau for Bᵏ⁻¹ⱼ for j = k-1, k-2, ..., 1 + _evaluate_1d!(Bernstein,Val(K-1),v,x,d) + + # Compute gradients as _gradient_1d! + g[d,n] = K*v[d,n-1] + @simd for l in n-1:-1:2 + g[d,l] = K*(v[d,l-1] - v[d,l]) + end + g[d,1] = -K*v[d,1] + + # Last step of De Casteljau for _evaluate_1d! + _De_Casteljau_step_1D!(v,d,n,λ1,λ2) + end +end + +function _derivatives_1d!(::Type{Bernstein},v::Val_012,t::NTuple{3},x,d) + @inline _evaluate_1d!(Bernstein, v, t[1], x, d) + @inline _gradient_1d!(Bernstein, v, t[2], x, d) + @inline _hessian_1d!( Bernstein, v, t[3], x, d) +end + +function _derivatives_1d!(::Type{Bernstein},::Val{K},t::NTuple{3},x,d) where K + @inbounds begin + n = K + 1 # n > 3 + v, g, h = t + + KK = K*(K-1) + λ2 = x[d] + λ1 = one(eltype(v)) - λ2 + + # De Casteljau until Bᵏ⁻²ⱼ ∀j + _evaluate_1d!(Bernstein,Val(K-2),v,x,d) + + # Compute hessians as _hessian_1d! + h[d,n] = KK*v[d,n-2] + h[d,n-1] = KK*( v[d,n-3] -2*v[d,n-2] ) + @simd for l in n-2:-1:3 + h[d,l] = KK*( v[d,l-2] -2*v[d,l-1] + v[d,l] ) + end + h[d,2] = KK*( -2*v[d,1] + v[d,2] ) + h[d,1] = KK*v[d,1] + + # One step of De Casteljau to get Bᵏ⁻¹ⱼ ∀j + _De_Casteljau_step_1D!(v,d,n-1,λ1,λ2) + + # Compute gradients as _gradient_1d! + g[d,n] = K*v[d,n-1] + @simd for l in n-1:-1:2 + g[d,l] = K*(v[d,l-1] - v[d,l]) + end + g[d,1] = -K*v[d,1] + + # Last step of De Casteljau for _evaluate_1d! + _De_Casteljau_step_1D!(v,d,n,λ1,λ2) end end diff --git a/src/Polynomials/PolynomialInterfaces.jl b/src/Polynomials/PolynomialInterfaces.jl index 2dcf212c3..8008cd1a6 100644 --- a/src/Polynomials/PolynomialInterfaces.jl +++ b/src/Polynomials/PolynomialInterfaces.jl @@ -204,7 +204,6 @@ function _gradient_nd!( @abstractmethod end - """ _hessian_nd!(b,xi,r,i,c,g,h,s) @@ -312,6 +311,10 @@ function _hessian_1d!(::Type{<:Polynomial},::Val{K},h::AbstractMatrix{T},x,d) wh @abstractmethod end +# Dispatch helpers for base cases +const Val_01 = Union{Val{0},Val{1}} +const Val_012 = Union{Val{0},Val{1},Val{2}} + """ _derivatives_1d!(PT::Type{<:Polynomial}, ::Val{K}, (c,g,...), x, d) @@ -323,22 +326,21 @@ _gradient_1d!(PT, Val(K), g, x d) ``` but with possible performance optimization. """ -function _derivatives_1d!( ::Type{<:Polynomial},::Val{K},t::NTuple{N},x,d) where {K,N} +function _derivatives_1d!( ::Type{<:Polynomial},v::Val,t::NTuple{N},x,d) where N @abstractmethod end -function _derivatives_1d!(PT::Type{<:Polynomial},::Val{K},t::NTuple{1},x,d) where K - _evaluate_1d!(PT, Val(K), t[1], x, d) +function _derivatives_1d!(PT::Type{<:Polynomial},v::Val,t::NTuple{1},x,d) + @inline _evaluate_1d!(PT, v, t[1], x, d) end -function _derivatives_1d!(PT::Type{<:Polynomial},::Val{K},t::NTuple{2},x,d) where K - _evaluate_1d!(PT, Val(K), t[1], x, d) - _gradient_1d!(PT, Val(K), t[2], x, d) +function _derivatives_1d!(PT::Type{<:Polynomial},v::Val,t::NTuple{2},x,d) + @inline _evaluate_1d!(PT, v, t[1], x, d) + @inline _gradient_1d!(PT, v, t[2], x, d) end -function _derivatives_1d!(PT::Type{<:Polynomial},::Val{K},t::NTuple{3},x,d) where K - _evaluate_1d!(PT, Val(K), t[1], x, d) - _gradient_1d!(PT, Val(K), t[2], x, d) - _hessian_1d!( PT, Val(K), t[3], x, d) +function _derivatives_1d!(PT::Type{<:Polynomial},v::Val,t::NTuple{3},x,d) + @inline _evaluate_1d!(PT, v, t[1], x, d) + @inline _gradient_1d!(PT, v, t[2], x, d) + @inline _hessian_1d!( PT, v, t[3], x, d) end - diff --git a/test/PolynomialsTests/BernsteinBasesTests.jl b/test/PolynomialsTests/BernsteinBasesTests.jl index 1a06398f1..60abe1ccd 100644 --- a/test/PolynomialsTests/BernsteinBasesTests.jl +++ b/test/PolynomialsTests/BernsteinBasesTests.jl @@ -11,13 +11,30 @@ using ForwardDiff np = 3 x = [Point(0.),Point(1.),Point(.4)] -xi = x[1] +x1 = x[1] # Only test 1D evaluations as tensor product structure is tested in monomial tests # V = Float64 -G = gradient_type(V,xi) -H = gradient_type(G,xi) +G = gradient_type(V,x1) +H = gradient_type(G,x1) + +function test_internals(order,x,bx,∇bg,Hbx) + sz = (1,order+1) + for (i,xi) in enumerate(x) + v2 = zeros(sz) + Polynomials._evaluate_1d!(Bernstein,Val(order),v2,xi,1) + @test all( [ bxi[1]≈vxi[1] for (bxi,vxi) in zip(bx[i,:],v2[:,1]) ] ) + + g2 = zeros(sz) + Polynomials._gradient_1d!(Bernstein,Val(order),g2,xi,1) + @test all( [ bxi[1]≈vxi[1] for (bxi,vxi) in zip(∇bx[i,:],g2[:,1]) ] ) + + h2 = zeros(sz) + Polynomials._hessian_1d!(Bernstein,Val(order),h2,xi,1) + @test all( [ bxi[1]≈vxi[1] for (bxi,vxi) in zip(Hbx[i,:],h2[:,1]) ] ) + end +end # order 0 degenerated case @@ -27,13 +44,12 @@ b = BernsteinBasis(Val(1),V,order) @test get_order(b) == 0 @test get_orders(b) == (0,) -v = V[1.0,] -g = G[(0.0)] -h = H[(0.0)] +bx = [ 1.; 1.; 1.;; ] +∇bx = G[ 0.; 0.; 0.;; ] +Hbx = H[ 0.; 0.; 0.;; ] + +test_internals(order,x,bx,∇bx,Hbx) -bx = repeat(permutedims(v),np) -∇bx = repeat(permutedims(g),np) -Hbx = repeat(permutedims(h),np) test_field_array(b,x,bx,grad=∇bx,gradgrad=Hbx) test_field_array(b,x[1],bx[1,:],grad=∇bx[1,:],gradgrad=Hbx[1,:]) @@ -55,6 +71,8 @@ Hbx = H[ 0. 0. 0. 0. 0. 0. ] +test_internals(order,x,bx,∇bx,Hbx) + test_field_array(b,x,bx,grad=∇bx,gradgrad=Hbx) test_field_array(b,x[1],bx[1,:],grad=∇bx[1,:],gradgrad=Hbx[1,:]) @@ -77,6 +95,8 @@ Hbx = H[ 2. -4. 2. 2. -4. 2. 2. -4. 2. ] +test_internals(order,x,bx,∇bx,Hbx) + test_field_array(b,x,bx,≈, grad=∇bx,gradgrad=Hbx) test_field_array(b,x[1],bx[1,:],grad=∇bx[1,:],gradgrad=Hbx[1,:]) @@ -103,6 +123,8 @@ bx = [ bernstein(order,n)( xi[1]) for xi in x, n in 0:order] # -6x+6 18x-12 -18x+6 6x Hbx = [ H(_H(bernstein(order,n))(xi[1])) for xi in x, n in 0:order] +test_internals(order,x,bx,∇bx,Hbx) + test_field_array(b,x,bx,≈, grad=∇bx,gradgrad=Hbx) test_field_array(b,x[1],bx[1,:],grad=∇bx[1,:],gradgrad=Hbx[1,:]) @@ -116,6 +138,8 @@ bx = [ bernstein(order,n)( xi[1]) for xi in x, n in 0:order] ∇bx = [ G(_∇(bernstein(order,n))(xi[1])) for xi in x, n in 0:order] Hbx = [ H(_H(bernstein(order,n))(xi[1])) for xi in x, n in 0:order] +test_internals(order,x,bx,∇bx,Hbx) + test_field_array(b,x,bx,≈, grad=∇bx,gradgrad=Hbx) test_field_array(b,x[1],bx[1,:],grad=∇bx[1,:],gradgrad=Hbx[1,:]) From b8203fc7dcc84915b9ba7526341adbd93bcbe738 Mon Sep 17 00:00:00 2001 From: Antoine Marteau Date: Wed, 8 Jan 2025 10:30:55 +1100 Subject: [PATCH 098/105] better public name display in module docstrings --- src/Adaptivity/Adaptivity.jl | 2 ++ src/Algebra/Algebra.jl | 3 +-- src/Arrays/Arrays.jl | 2 +- src/CellData/CellData.jl | 5 ++--- src/FESpaces/FESpaces.jl | 3 +-- src/Fields/Fields.jl | 5 +++++ src/Geometry/Geometry.jl | 3 +-- src/Helpers/HelperFunctions.jl | 26 ++++++++++++++++++++++++++ src/Helpers/Helpers.jl | 6 ++---- src/Io/Io.jl | 3 +-- src/MultiField/MultiField.jl | 3 +-- src/ODEs/ODEs.jl | 3 +-- src/Polynomials/Polynomials.jl | 4 ++-- src/ReferenceFEs/ReferenceFEs.jl | 3 +-- src/TensorValues/TensorValues.jl | 2 ++ src/Visualization/Visualization.jl | 3 +-- 16 files changed, 50 insertions(+), 26 deletions(-) diff --git a/src/Adaptivity/Adaptivity.jl b/src/Adaptivity/Adaptivity.jl index 8811c68dc..140f4ab7b 100644 --- a/src/Adaptivity/Adaptivity.jl +++ b/src/Adaptivity/Adaptivity.jl @@ -1,5 +1,7 @@ """ Mesh Adaptivity for Gridap + +$(public_names_in_md(@__MODULE__)) """ module Adaptivity diff --git a/src/Algebra/Algebra.jl b/src/Algebra/Algebra.jl index 46a520f35..87961270d 100644 --- a/src/Algebra/Algebra.jl +++ b/src/Algebra/Algebra.jl @@ -1,7 +1,6 @@ """ -The exported names are -$(EXPORTS) +$(public_names_in_md(@__MODULE__)) """ module Algebra diff --git a/src/Arrays/Arrays.jl b/src/Arrays/Arrays.jl index 54f39222a..9a790df6a 100644 --- a/src/Arrays/Arrays.jl +++ b/src/Arrays/Arrays.jl @@ -6,7 +6,7 @@ This module provides: The exported names in this module are: -$(EXPORTS) +$(public_names_in_md(@__MODULE__)) """ module Arrays diff --git a/src/CellData/CellData.jl b/src/CellData/CellData.jl index d96ecca48..a3317d1c1 100644 --- a/src/CellData/CellData.jl +++ b/src/CellData/CellData.jl @@ -1,7 +1,6 @@ """ -The exported names are -$(EXPORTS) +$(public_names_in_md(@__MODULE__)) """ module CellData @@ -86,7 +85,7 @@ export update_state! export DiracDelta -export SkeletonCellFieldPair +export SkeletonCellFieldPair include("CellDataInterface.jl") diff --git a/src/FESpaces/FESpaces.jl b/src/FESpaces/FESpaces.jl index 5537ed784..6302932ce 100644 --- a/src/FESpaces/FESpaces.jl +++ b/src/FESpaces/FESpaces.jl @@ -1,7 +1,6 @@ """ -The exported names are -$(EXPORTS) +$(public_names_in_md(@__MODULE__)) """ module FESpaces diff --git a/src/Fields/Fields.jl b/src/Fields/Fields.jl index 0dd060b4c..bb01fc091 100644 --- a/src/Fields/Fields.jl +++ b/src/Fields/Fields.jl @@ -1,3 +1,7 @@ +""" + +$(public_names_in_md(@__MODULE__)) +""" module Fields using Gridap.Arrays @@ -6,6 +10,7 @@ import Gridap.Arrays: inverse_map import Gridap.Arrays: get_children import Gridap.Arrays: testitem +using Gridap.Helpers using Gridap.Helpers: @abstractmethod, @notimplemented using Gridap.Helpers: @notimplementedif, @unreachable, @check using Gridap.Helpers: tfill diff --git a/src/Geometry/Geometry.jl b/src/Geometry/Geometry.jl index 471b420ff..6adc931c5 100644 --- a/src/Geometry/Geometry.jl +++ b/src/Geometry/Geometry.jl @@ -1,7 +1,6 @@ """ -Exported names are -$(EXPORTS) +$(public_names_in_md(@__MODULE__)) """ module Geometry diff --git a/src/Helpers/HelperFunctions.jl b/src/Helpers/HelperFunctions.jl index 5fb5cda3d..3a8e5fba5 100644 --- a/src/Helpers/HelperFunctions.jl +++ b/src/Helpers/HelperFunctions.jl @@ -38,3 +38,29 @@ function first_and_tail(a::Tuple) first(a), Base.tail(a) end +""" + public_names_in_md(m::Module) + +Return a string displaying exported and other public names of the module for +printing in markdown. +""" +function public_names_in_md(m::Module) + publics = filter(!=(nameof(m)), names(m)) + exported = filter(n->Base.isexported(m,n), publics) + non_exported_publics = filter(∉(exported), publics) + + isempty(exported) && return "" + + s = """ + ### Exported names + `$( join(exported, "`, `") )` + """ + + isempty(non_exported_publics) && return s + + s * """ + + ### Other public names + `$(join(non_exported_publics,"`, `"))` + """ +end diff --git a/src/Helpers/Helpers.jl b/src/Helpers/Helpers.jl index e2e7f3d3d..15e26a1de 100644 --- a/src/Helpers/Helpers.jl +++ b/src/Helpers/Helpers.jl @@ -1,10 +1,7 @@ """ This module provides a set of helper macros and helper functions -The exported macros are: - -$(EXPORTS) - +$(public_names_in_md(@__MODULE__)) """ module Helpers using DocStringExtensions @@ -26,6 +23,7 @@ export get_val_parameter export first_and_tail export GridapType export set_debug_mode, set_performance_mode +export public_names_in_md #export operate include("Preferences.jl") diff --git a/src/Io/Io.jl b/src/Io/Io.jl index f8ed74aa1..5f6febd2d 100644 --- a/src/Io/Io.jl +++ b/src/Io/Io.jl @@ -1,7 +1,6 @@ """ -The exported names in this module are: -$(EXPORTS) +$(public_names_in_md(@__MODULE__)) """ module Io diff --git a/src/MultiField/MultiField.jl b/src/MultiField/MultiField.jl index bd2697232..23f76f5af 100644 --- a/src/MultiField/MultiField.jl +++ b/src/MultiField/MultiField.jl @@ -1,7 +1,6 @@ """ -The exported names are -$(EXPORTS) +$(public_names_in_md(@__MODULE__)) """ module MultiField diff --git a/src/ODEs/ODEs.jl b/src/ODEs/ODEs.jl index 30f294d5d..2be3e347d 100644 --- a/src/ODEs/ODEs.jl +++ b/src/ODEs/ODEs.jl @@ -1,7 +1,6 @@ """ -The exported names are -$(EXPORTS) +$(public_names_in_md(@__MODULE__)) """ module ODEs diff --git a/src/Polynomials/Polynomials.jl b/src/Polynomials/Polynomials.jl index 24f1c61fb..66364d929 100644 --- a/src/Polynomials/Polynomials.jl +++ b/src/Polynomials/Polynomials.jl @@ -1,9 +1,9 @@ """ This module provides a collection of multivariate polynomial bases. -The exported names are: -$(EXPORTS) + +$(public_names_in_md(@__MODULE__)) """ module Polynomials diff --git a/src/ReferenceFEs/ReferenceFEs.jl b/src/ReferenceFEs/ReferenceFEs.jl index 31353b77f..ea663c9b2 100644 --- a/src/ReferenceFEs/ReferenceFEs.jl +++ b/src/ReferenceFEs/ReferenceFEs.jl @@ -1,7 +1,6 @@ """ -The exported names are -$(EXPORTS) +$(public_names_in_md(@__MODULE__)) """ module ReferenceFEs diff --git a/src/TensorValues/TensorValues.jl b/src/TensorValues/TensorValues.jl index 5d491f997..b46285e71 100644 --- a/src/TensorValues/TensorValues.jl +++ b/src/TensorValues/TensorValues.jl @@ -33,6 +33,8 @@ SymTensorValue( SMatrix{2}(1,2,3,4) ) # SymTensorValue{2, Int64, 3}(1, 3, 4) ``` See the official documentation for more details. + +$(public_names_in_md(@__MODULE__)) """ module TensorValues diff --git a/src/Visualization/Visualization.jl b/src/Visualization/Visualization.jl index c20fb41ea..614ce8ee0 100644 --- a/src/Visualization/Visualization.jl +++ b/src/Visualization/Visualization.jl @@ -1,7 +1,6 @@ """ -The exported names are -$(EXPORTS) +$(public_names_in_md(@__MODULE__)) """ module Visualization From facb8eb60cba522f2860710af96688f3c6416bcf Mon Sep 17 00:00:00 2001 From: Antoine Marteau Date: Wed, 8 Jan 2025 12:14:31 +1100 Subject: [PATCH 099/105] better errors in wrong PolyBasis construction --- src/Polynomials/CompWiseTensorPolyBases.jl | 1 + src/Polynomials/NedelecPolyBases.jl | 5 +++-- src/Polynomials/RaviartThomasPolyBases.jl | 3 ++- src/Polynomials/UniformPolyBases.jl | 2 ++ 4 files changed, 8 insertions(+), 3 deletions(-) diff --git a/src/Polynomials/CompWiseTensorPolyBases.jl b/src/Polynomials/CompWiseTensorPolyBases.jl index d0d1a2283..536c5e89e 100644 --- a/src/Polynomials/CompWiseTensorPolyBases.jl +++ b/src/Polynomials/CompWiseTensorPolyBases.jl @@ -31,6 +31,7 @@ struct CompWiseTensorPolyBasis{D,V,K,PT,L} <: PolynomialBasis{D,V,K,PT} msg2 = "The Component Wise construction is useless for one component, use UniformPolyBasis instead" @check L > 1 msg2 @check D > 0 + @check isconcretetype(PT) "PT needs to be a concrete <:Polynomial type" K = maximum(orders) new{D,V,K,PT,L}(orders) diff --git a/src/Polynomials/NedelecPolyBases.jl b/src/Polynomials/NedelecPolyBases.jl index 807c2d4a8..7c96be8a1 100644 --- a/src/Polynomials/NedelecPolyBases.jl +++ b/src/Polynomials/NedelecPolyBases.jl @@ -1,7 +1,8 @@ struct NedelecPolyBasisOnSimplex{D,V,K,PT} <: PolynomialBasis{D,V,K,PT} order::Int - function NedelecPolyBasisOnSimplex{D}(::Type{PT},::Type{T},order::Integer) where {D,PT,T} + function NedelecPolyBasisOnSimplex{D}(::Type{PT},::Type{T},order::Integer) where {D,PT<:Polynomial,T} @check T<:Real "T needs to be <:Real since represents the type of the components of the vector value" + @check isconcretetype(PT) "PT needs to be a concrete <:Polynomial type" @notimplementedif !(D in (2,3)) K = Int(order)+1 V = VectorValue{D,T} @@ -260,6 +261,6 @@ function PGradBasis(::Type{PT},::Val{D},::Type{T},order::Int) where {PT,D,T} # the code explicitely uses monomials for the terms of x×(ℙₙ \\ ℙₙ₋₁)ᴰ, so I # disable them here. # But one can use NedelecPolyBasisOnSimplex{D}(PT,T,order) if they wish. - @notimplemented "only implemented for monomials" + @notimplemented "Nedelec on simplices is only implemented for monomials" end diff --git a/src/Polynomials/RaviartThomasPolyBases.jl b/src/Polynomials/RaviartThomasPolyBases.jl index 9b0c99fcd..8cc689194 100644 --- a/src/Polynomials/RaviartThomasPolyBases.jl +++ b/src/Polynomials/RaviartThomasPolyBases.jl @@ -30,10 +30,11 @@ struct RaviartThomasPolyBasis{D,V,K,PT} <: PolynomialBasis{D,V,K,PT} function RaviartThomasPolyBasis{D}( ::Type{PT}, ::Type{T}, order::Int, _filter::Function=_p_filter - ) where {PT,D,T} + ) where {PT<:Polynomial,D,T} @check T<:Real "T needs to be <:Real since represents the type of the components of the vector value" @check D > 1 + @check isconcretetype(PT) "PT needs to be a concrete <:Polynomial type" @check isHierarchical(PT) "The polynomial basis must be hierarchichal for this space." V = VectorValue{D,T} diff --git a/src/Polynomials/UniformPolyBases.jl b/src/Polynomials/UniformPolyBases.jl index 7130144b1..982c983ea 100644 --- a/src/Polynomials/UniformPolyBases.jl +++ b/src/Polynomials/UniformPolyBases.jl @@ -36,6 +36,8 @@ struct UniformPolyBasis{D,V,K,PT} <: PolynomialBasis{D,V,K,PT} orders::NTuple{D,Int}, terms::Vector{CartesianIndex{D}}) where {D,V,PT<:Polynomial} + @check isconcretetype(PT) "PT needs to be a concrete <:Polynomial type" + K = maximum(orders; init=0) msg = "Some term contain a higher index than the maximum degree + 1." @check all( term -> (maximum(Tuple(term), init=0) <= K+1), terms) msg From 10bab17f420f9ea3d88229e7050c7446fa22f9f0 Mon Sep 17 00:00:00 2001 From: Antoine Marteau Date: Wed, 8 Jan 2025 12:24:55 +1100 Subject: [PATCH 100/105] wrote Gridap.Polynomials' docstring --- src/Polynomials/Polynomials.jl | 76 ++++++++++++++++++++++++++++++++-- 1 file changed, 73 insertions(+), 3 deletions(-) diff --git a/src/Polynomials/Polynomials.jl b/src/Polynomials/Polynomials.jl index 66364d929..450fd4084 100644 --- a/src/Polynomials/Polynomials.jl +++ b/src/Polynomials/Polynomials.jl @@ -1,7 +1,77 @@ """ -This module provides a collection of multivariate polynomial bases. - - +This module provides a collection of uni- and multi-variate scalar- and multi-value'd polynomial bases. + +Most of the basis polynomials are composed using products of 1D polynomials, +represented by the type [`Polynomial`](@ref). +Five `Polynomial` families are currently implemented: [`Monomial`](@ref), +[`Legendre`](@ref), [`Chebyshev`](@ref), [`Bernstein`](@ref) and [`ModalC0`](@ref). + +The polynomial bases all subtype [`PolynomialBasis`](@ref), which subtypes +`AbstractVector{<:Field}`, so they implement the `Field` interface up to first +or second derivatives. + +Constructors for commonly used bases (see the documentation for the spaces definitions): +- ℚ spaces: `[Polynomial]Basis(Val(D), V, order)` +- ℙ spaces: `[Polynomial]Basis(..., Polynomials._p_filter)` +- ℚₙ\\ℚₙ₋₁: `[Polynomial]Basis(..., Polynomials._qs_filter)` +- ℙₙ\\ℙₙ₋₁: `[Polynomial]Basis(..., Polynomials._ps_filter)` +- ℕ𝔻(△): `PGradBasis(Val(D), T, order)` +- ℕ𝔻(□): `QGradBasis(...)` +- ℝ𝕋(△): `PCurlGradBasis(...)` +- ℝ𝕋(□): `QCurlGradBasis(...)` + +### Examples + +```julia +using Gridap +using Gridap.Polynomials +using Gridap.Fields: return_type + +# Basis of ℚ¹₂ of Float64 value type based on Bernstein polynomials: +# {(1-x)², 2x(1-x), x²} +D = 1; n = 2 # spatial dimension and order +b = BernsteinBasis(Val(D), Float64, n) + +# APIs +length(b) # 3 +return_type(b) # Float64 +get_order(b) # 2 + +xi =Point(0.1) +evaluate(b, xi) +evaluate(Broadcasting(∇)(b), xi) # gradients +evaluate(Broadcasting(∇∇)(b), xi) # hessians, not all basis support hessians +evaluate(b, [xi, xi]) # evaluation on arrays of points + +# Basis of ℚ²₂ of Float64 value type based on Legendre polynomials, our 1D Legendre +# polynomials are normalized for L2 scalar product and moved from [-1,1] to [0,1] using x -> 2x-1 +# { 1, √3(2x-1), √5(6x²-6x+2), +# √3(2y-1), √3(2x-1)√3(2y-1), √5(6x²-6x+2)√3(2y-1), +# √5(6y²-6y+2), √3(2x-1)√5(6x²-6x+2), √5(6x²-6x+2)√5(6y²-6y+2) } +D = 2; n = 2 # spatial dimension and order +b = LegendreBasis(Val(D), Float64, n) + +# Basis of (ℙ³₁)³ of VectorValue{3,Float64} value type, based on monomials: +# {(1,0,0), (0,1,0), (0,0,1) +# (x,0,0), (0,x,0), (0,0,x) +# (y,0,0), (0,y,0), (0,0,y) +# (z,0,0), (0,z,0), (0,0,z)} +D = 3; n = 1 # spatial dimension and order +b = MonomialBasis(Val(D), VectorValue{D,Float64}, n, Polynomials._p_filter) +evaluate(b, Point(.1, .2, .3) + +# a basis for Nedelec on tetrahedra with curl in ℙ₂ +b = PGradBasis(Monomial, Val(3), Float64, 2) # basis of order 3 + +# a basis for Nedelec on hexahedra with divergence in ℚ₂ +b = QGradBasis(Bernstein, Val(3), Float64, 2) # basis of order 3 + +# a basis for Raviart-Thomas on tetrahedra with divergence in ℙ₂ +b = PCurlGradBasis(Chebyshev{:T}, Val(3), Float64, 2) # basis of order 3 + +# a basis for Raviart-Thomas on rectangles with divergence in ℚ₃ +b = QCurlGradBasis(Bernstein, Val(2), Float64, 3) # basis of order 4 +``` $(public_names_in_md(@__MODULE__)) """ From 5eaf0766754538a26603b451091eeb6ec9386e0b Mon Sep 17 00:00:00 2001 From: Antoine Marteau Date: Wed, 8 Jan 2025 15:40:59 +1100 Subject: [PATCH 101/105] change Int64 to Int where possible, fix dispatch test --- src/Polynomials/ChebyshevBases.jl | 2 +- test/TensorValuesTests/TypesTests.jl | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/Polynomials/ChebyshevBases.jl b/src/Polynomials/ChebyshevBases.jl index 8ca6da07e..71f9beb57 100644 --- a/src/Polynomials/ChebyshevBases.jl +++ b/src/Polynomials/ChebyshevBases.jl @@ -25,7 +25,7 @@ High level constructors of [`ChebyshevBasis`](@ref). ChebyshevBasis(args...; kind=:T) = UniformPolyBasis(Chebyshev{kind}, args...) function UniformPolyBasis( - ::Type{Chebyshev{:U}}, ::Val{D}, ::Type{V}, ::Int64) where {D, V} + ::Type{Chebyshev{:U}}, ::Val{D}, ::Type{V}, ::Int) where {D, V} @notimplemented "1D evaluation for second kind need to be implemented here" end diff --git a/test/TensorValuesTests/TypesTests.jl b/test/TensorValuesTests/TypesTests.jl index bb1dfd073..44845c697 100644 --- a/test/TensorValuesTests/TypesTests.jl +++ b/test/TensorValuesTests/TypesTests.jl @@ -117,7 +117,7 @@ a = [11.0 21.0; NaN 22.0] @test convert(SymTensorValue{2,Float64},a) == SymTensorValue{2,Float64,3}(11.0, 21.0, 22.0) # Constructors (SymTracelessTensorValue) -q_none = SymTracelessTensorValue{0, Int64, 0}() +q_none = SymTracelessTensorValue{0, Int, 0}() q = SymTracelessTensorValue() @test q == q_none q = SymTracelessTensorValue{0}() @@ -681,13 +681,13 @@ a = SymTensorValue(11,21,22) @test change_eltype(SymTensorValue{2,Float64},Int) == SymTensorValue{2,Int} @test isa(Tuple(a),Tuple) @test Tuple(a) == a.data -b = Matrix{Int64}(undef,2,2) +b = Matrix{Int}(undef,2,2) b[1,1] = a[1,1] b[1,2] = a[1,2] b[2,1] = a[2,1] b[2,2] = a[2,2] a = SymTensorValue(11,21,22) -bt = SymTensorValue{2,Int64}(b) +bt = SymTensorValue{2,Int}(b) @test all(bt .== a) a = SymTracelessTensorValue(11,21) @@ -695,12 +695,12 @@ a = SymTracelessTensorValue(11,21) @test change_eltype(SymTracelessTensorValue{2,Float64},Int) == SymTracelessTensorValue{2,Int} @test isa(Tuple(a),Tuple) @test Tuple(a) == a.data -b = Matrix{Int64}(undef,2,2) +b = Matrix{Int}(undef,2,2) b[1,1] = a[1,1] b[1,2] = a[1,2] b[2,1] = a[2,1] b[2,2] = a[2,2] -bt = SymTracelessTensorValue{2,Int64}(b) +bt = SymTracelessTensorValue{2,Int}(b) @test all(bt .== a) a = SymFourthOrderTensorValue(1111,1121,1122, 2111,2121,2122, 2211,2221,2222) From f39869621b999ac68366e1553ec1419f8c089efb Mon Sep 17 00:00:00 2001 From: Antoine Marteau Date: Thu, 9 Jan 2025 09:12:42 +1100 Subject: [PATCH 102/105] documentation of Gridap.Polynomials --- docs/make.jl | 3 +- docs/src/Polynomials.md | 290 ++++++++++++++++++++- docs/src/TensorValues.md | 2 +- src/Helpers/HelperFunctions.jl | 4 +- src/Polynomials/BernsteinBases.jl | 4 +- src/Polynomials/ChebyshevBases.jl | 8 +- src/Polynomials/CompWiseTensorPolyBases.jl | 12 +- src/Polynomials/Deprecated.jl | 38 +++ src/Polynomials/LegendreBases.jl | 4 +- src/Polynomials/ModalC0Bases.jl | 9 +- src/Polynomials/MonomialBases.jl | 4 +- src/Polynomials/NedelecPolyBases.jl | 14 +- src/Polynomials/Polynomials.jl | 33 +-- src/Polynomials/RaviartThomasPolyBases.jl | 18 +- src/Polynomials/UniformPolyBases.jl | 34 +-- 15 files changed, 405 insertions(+), 72 deletions(-) diff --git a/docs/make.jl b/docs/make.jl index b05b679a5..cf0083e80 100644 --- a/docs/make.jl +++ b/docs/make.jl @@ -30,7 +30,8 @@ pages = [ makedocs( sitename = "Gridap.jl", format = Documenter.HTML( - size_threshold=nothing + size_threshold=nothing, + size_threshold_warn=300 * 2^10 # 300KiB ), modules = [Gridap], pages = pages, diff --git a/docs/src/Polynomials.md b/docs/src/Polynomials.md index 85d30d782..58143c701 100644 --- a/docs/src/Polynomials.md +++ b/docs/src/Polynomials.md @@ -1,15 +1,299 @@ +# Gridap.Polynomials ```@meta CurrentModule = Gridap.Polynomials ``` +The module documentation is organised as follows: +- Summary of the main features +- Mathematical definitions of the families of [polynomial bases](@ref "Families + of polynomial bases") and spanned polynomial [spaces](@ref "Polynomial spaces + in FEM") +- Docstrings of implemented [families](@ref "Types for polynomial families") + and [bases](@ref "Polynomial bases") of polynomials +- [Low level](@ref "Low level APIs and internals") APIs, internals and + [deprecated](@ref "Deprecated APIs") methods -# Gridap.Polynomials +### Summary + +```@docs +Polynomials +``` + +## Mathematical definitions + +### Families of polynomial bases + +#### Monomials + +The [`Monomial`](@ref)s are the standard basis polynomials of general +polynomial spaces. The order ``K`` 1D monomial is +```math +x \rightarrow x^K, +``` +and the order ``\boldsymbol{K}=(K_1, K_2, \dots, K_D)`` D-dimensional monomial is defined by +```math +\boldsymbol{x} = (x_1, x_2, \dots, x_D) \longrightarrow +\boldsymbol{x}^{\boldsymbol{K}} = x_1^{K_1}x_2^{K_2}...x_D^{K_D} = \Pi_{i=1}^D +x_i^{K_i}. +``` + +#### Legendre polynomials + +The Legendre polynomials are the orthogonal 1D basis recursively defined by +```math +P_0(x) = 1,\qquad +P_1(x) = x,\qquad +P_{n+1}(x) = \frac{1}{(n+1)}( (2n+1)x P_{n}(x)-n P_{n-1}(x) ), +``` +the orthogonality is for the ``L^2`` scalar product on ``[-1,1]``. + +This module implements the normalized shifted [`Legendre`](@ref) polynomials, +shifted to be orthogonal on ``[0,1]`` using the change of variable ``x +\rightarrow 2x-1``, leading to +```math +P^*_{n}(x)=\frac{1}{\sqrt{2n+1}}P_n(2x-1)=\frac{1}{\sqrt{2n+1}}(-1)^{n}\sum _{i=0}^{n}{\binom{n}{i}}{\binom{n+i}{i}}(-x)^{i}. +``` + +#### Chebyshev polynomials + +The first kind Chebyshev polynomials ``T_n`` and second kind Chebyshev +polynomials ``U_n`` can be recursively defined by +```math + T_{0}(x)=1,\qquad T_{1}(x)=\ \,x,\qquad T_{n+1}(x)=2x\,T_{n}(x)-T_{n-1}(x).\\ + U_{0}(x)=1,\qquad U_{1}(x)=2x,\qquad U_{n+1}(x)=2x\,U_{n}(x)-U_{n-1}(x). +``` +or explicitly defined by +```math +T_{n}(x)=\sum _{i=0}^{\left\lfloor {n}/{2}\right\rfloor }{\binom + {n}{2i}}\left(x^{2}-1\right)^{i}x^{n-2i},\qquad +U_{n}(x)=\sum _{i=0}^{\left\lfloor {n}/{2}\right\rfloor }{\binom + {n+1}{2i+1}}\left(x^{2}-1\right)^{i}x^{n-2i}, +``` +where ``\left\lfloor {n}/2\right\rfloor`` is `floor(n/2)`. + +Similarly to Legendre above, this module implements the shifted first kind +Chebyshev by [`Chebyshev{:T}`](@ref), defined by +```math +T^*_n(x) = T_n(2x-1). +``` +The analog second kind shifted Chebyshev polynomials can be implemented by +[`Chebyshev{:U}`](@ref) in the future. + +#### Bernstein polynomials + +The [`Bernstein`](@ref) polynomials forming a basis of ``\mathbb{P}_K`` are +defined by +```math +B^K_{n}(x) = \binom{K}{n} x^n (1-x)^{K-n}\qquad\text{ for } 0\leq n\leq K. +``` +They are positive on ``[0,1]`` and sum to ``1``. + +#### ModalC0 polynomials + +The [`ModalC0`](@ref) polynomials are 1D hierarchical and orthogonal +polynomials ``\phi_K`` for which ``\phi_K(0) = \delta_{0K}`` and ``\phi_K(1) = +\delta_{1K}``. This module implements the generalised version introduced in Eq. +17 of [Badia-Neiva-Verdugo 2022](https://doi.org/10.1016/j.camwa.2022.09.027). + + +### Polynomial spaces in FEM + +#### P and Q spaces + +```math +\mathbb{P}_K(x) = \text{Span}\big\{\quad x\rightarrow x^i \quad\big|\quad 0\leq i\leq K \quad\big\} +``` + +```math +\mathbb{Q}^D_K = \text{Span}\big\{\quad \bm{x}\rightarrow\bm{x}^\alpha \quad\big|\quad 0\leq + \alpha_1, \alpha_2, \dots, \alpha_D \leq K \quad\big\} +``` + +```math +\mathbb{P}^D_K = \text{Span}\big\{\quad \bm{x}\rightarrow\bm{x}^\alpha \quad\big|\quad 0\leq + \alpha_1, \alpha_2, \dots, \alpha_D \leq K;\quad \sum_{d=1}^D \alpha_d \leq + K \quad\big\} +``` + +```math +\mathbb{P}_K = \mathbb{P}^1_K = \mathbb{Q}^1_K +``` + +#### Serendipity space Sr + +The serendipity space, commonly used for serendipity finite elements on n-cubes, +are defined by +```math +\mathbb{S}r^D_K = \text{Span}\big\{\quad \bm{x}\rightarrow\bm{x}^\alpha \quad\big|\quad 0\leq + \alpha_1, \alpha_2, \dots, \alpha_D \leq K;\quad + \sum_{d=1}^D \alpha_d\;\mathbb{1}_{[2,K]}(\alpha_d) \leq K \quad\big\} +``` +where ``\mathbb{1}_{[2,K]}(\alpha_d)`` is ``1`` if ``\alpha_d\geq 2`` or else +``0``. + +#### Homogeneous P and Q spaces +It will later be useful to define the homogeneous Q spaces +```math +\tilde{\mathbb{Q}}^D_K = \mathbb{Q}^D_K\backslash\mathbb{Q}^D_{K-1} = + \text{Span}\big\{\quad \bm{x}\rightarrow\bm{x}^\alpha \quad\big|\quad 0\leq \alpha_1, + \alpha_2, \dots, \alpha_D \leq K; \quad \text{max}(\alpha) = K \quad\big\}, +``` +and homogeneous P spaces +```math +\tilde{\mathbb{P}}^D_K = \mathbb{P}^D_K\backslash \mathbb{P}^D_{K-1} = + \text{Span}\big\{\quad \bm{x}\rightarrow\bm{x}^\alpha \quad\big|\quad 0\leq \alpha_1, + \alpha_2, \dots, \alpha_D \leq K;\quad \sum_{d=1}^D \alpha_d = K \quad\big\}. +``` + + +#### Nédélec spaces + +The Kᵗʰ Nédélec polynomial spaces on respectively rectangles and +triangles are defined by +```math +\mathbb{ND}^2_K(\square) = \left(\mathbb{Q}^2_K\right)^2 \oplus + \left(\begin{array}{c} y^{K+1}\,\mathbb{P}_K(x)\\ x^{K+1}\,\mathbb{P}_K(y) \end{array}\right) +,\qquad +\mathbb{ND}^2_K(\bigtriangleup) =\left(\mathbb{P}^2_K\right)^2 \oplus\bm{x}\times(\tilde{\mathbb{P}}^2_K)^2, +``` +where ``\times`` here means ``\left(\begin{array}{c} x\\ y +\end{array}\right)\times\left(\begin{array}{c} p(\bm{x})\\ q(\bm{x}) +\end{array}\right) = \left(\begin{array}{c} y p(\bm{x})\\ -x q(\bm{x}) +\end{array}\right)`` and ``\oplus`` is the direct sum of vector spaces. + +Then, the Kᵗʰ Nédélec polynomial spaces on respectively hexahedra and +tetrahedra are defined by +```math +\mathbb{ND}^3_K(\square) = \left(\mathbb{Q}^3_K\right)^3 \oplus \bm{x}\times(\tilde{\mathbb{Q}}^3_K)^3,\qquad +\mathbb{ND}^3_K(\bigtriangleup) =\left(\mathbb{P}^3_K\right)^3 \oplus \bm{x}\times(\tilde{\mathbb{P}}^3_K)^3. +``` + +``\mathbb{ND}^D_K(\square)`` and ``\mathbb{ND}^D_K(\bigtriangleup)`` are of +order K+1 and the curl of their elements are in ``(\mathbb{Q}^D_K)^D`` +and ``(\mathbb{P}^D_K)^D`` respectively. + +#### Raviart-Thomas spaces + +The Kᵗʰ Raviart-Thomas polynomial spaces on respectively D-cubes and +D-simplices are defined by +```math +\mathbb{ND}^D_K(\square) = \left(\mathbb{Q}^D_K\right)^D \oplus \bm{x}\;\tilde{\mathbb{Q}}^D_K, \qquad +\mathbb{ND}^D_K(\bigtriangleup) = \left(\mathbb{P}^D_K\right)^D \oplus \bm{x}\;\tilde{\mathbb{P}}^D_K, +``` +these bases are of dimension K+1 and the divergence of their elements are in +``\mathbb{Q}^D_K`` and ``\mathbb{P}^D_K`` respectively. + + +#### Filter functions + +Some `filter` functions are used to select which terms of a `D`-dimensional +tensor product space of 1D polynomial bases are to be used to create a +`D`-multivariate basis. When a filter can be chosen, the default filter is +always the trivial filter for space of type ℚ, yielding the full tensor-product +space. + +The signature of the filter functions should be + + (term,order) -> Bool + +where `term` is a tuple of `D` integers containing the exponents of a +multivariate monomial, that correspond to the multi-index ``\alpha`` previously +used in the P/Q spaces definitions. + +When using [`hierarchical`](@ref isHierarchical) 1D bases, the following +filters can be used to define associated polynomial spaces: + +| space | filter | +| :-----------| :--------------------------------------------------------------- | +| ℚᴰ | `_q_filter(e,order) = maximum(e) <= order` | +| ℚᴰₙ\\ℚᴰₙ₋₁ | `_qs_filter(e,order) = maximum(e) == order` | +| ℙᴰ | `_p_filter(e,order) = sum(e) <= order` | +| ℙᴰₙ\\ℙᴰₙ₋₁ | `_ps_filter(e,order) = sum(e) == order` | +| 𝕊rᴰₙ | `_ser_filter(e,order) = sum( [ i for i in e if i>1 ] ) <= order` | + + +## Types for polynomial families + +The following types represent particular polynomial bases 'families' or 'types', later +shortened as `PT` in type parameters. + +```@docs +Polynomial +``` ![](./assets/poly_1.svg) +!!! warning + [`Polynomial`](@ref)s do not implement the `Field` interface, only the + [`PolynomialBasis`](@ref) can be evaluated. + + +```@docs +isHierarchical +Monomial +Legendre +Chebyshev +``` +!!! todo + Second kind `Chebyshev{:U}` are not implemented yet. + +```@docs +Bernstein +ModalC0 +``` + +## Polynomial bases + +```@docs +PolynomialBasis +get_order +MonomialBasis(args...) +MonomialBasis +LegendreBasis(args...) +LegendreBasis +ChebyshevBasis(args...) +ChebyshevBasis +BernsteinBasis(args...) +``` +!!! warning + Calling `BernsteinBasis` with the filters (e.g. a `_p_filter`) rarely + yields a basis for the associated space (e.g. ``\mathbb{P}``). Indeed, the + term numbers do not correspond to the degree of the polynomial, because the + basis is not [`hierarchical`](@ref isHierarchical). + +```@docs +BernsteinBasis +ModalC0Basis +``` + +```@docs +PGradBasis +QGradBasis +PCurlGradBasis +QCurlGradBasis +``` +## Low level APIs and internals ![](./assets/poly_2.svg) -```@autodocs -Modules = [Polynomials,] +```@docs +UniformPolyBasis +UniformPolyBasis(::Type, ::Val{D}, ::Type, ::Int, ::Function) where D +UniformPolyBasis(::Type, ::Val{D}, ::Type{V}, ::NTuple{D,Int}, ::Function) where {D,V} +get_orders +get_exponents +CompWiseTensorPolyBasis +NedelecPolyBasisOnSimplex +RaviartThomasPolyBasis +``` + +### Deprecated APIs + +```@docs +num_terms +PGradMonomialBasis +PCurlGradMonomialBasis +QGradMonomialBasis +QCurlGradMonomialBasis +JacobiPolynomialBasis ``` diff --git a/docs/src/TensorValues.md b/docs/src/TensorValues.md index 6b04631cf..5b32b22ba 100644 --- a/docs/src/TensorValues.md +++ b/docs/src/TensorValues.md @@ -34,7 +34,7 @@ C = inner.(g,B) # inner product of g against all TensorValues in the array B # C = [2494 2494 2494 2494 2494] ``` -To create a [`::MultiValue`](@ref) tensor from components, these should be given +To create a [`MultiValue`](@ref) tensor from components, these should be given as separate arguments or all gathered in a `tuple`. The order of the arguments is the order of the linearized Cartesian indices of the corresponding array (order of the `Base.LinearIndices` indices): diff --git a/src/Helpers/HelperFunctions.jl b/src/Helpers/HelperFunctions.jl index 3a8e5fba5..b6b0cf20c 100644 --- a/src/Helpers/HelperFunctions.jl +++ b/src/Helpers/HelperFunctions.jl @@ -53,7 +53,7 @@ function public_names_in_md(m::Module) s = """ ### Exported names - `$( join(exported, "`, `") )` + [`$(join(exported,"`](@ref), [`"))`](@ref) """ isempty(non_exported_publics) && return s @@ -61,6 +61,6 @@ function public_names_in_md(m::Module) s * """ ### Other public names - `$(join(non_exported_publics,"`, `"))` + [`$(join(non_exported_publics,"`](@ref), [`"))`](@ref) """ end diff --git a/src/Polynomials/BernsteinBases.jl b/src/Polynomials/BernsteinBases.jl index 40489e62b..655f2b988 100644 --- a/src/Polynomials/BernsteinBases.jl +++ b/src/Polynomials/BernsteinBases.jl @@ -1,7 +1,7 @@ """ Bernstein <: Polynomial -Type representing Bernstein polynomials. +Type representing Bernstein polynomials, c.f. [Bernstein polynomials](@ref) section. """ struct Bernstein <: Polynomial end @@ -10,7 +10,7 @@ isHierarchical(::Type{Bernstein}) = false """ BernsteinBasis{D,V,K} = UniformPolyBasis{D,V,K,Bernstein} -Alias for Bernstein multivariate scalar' or `Multivalue`'d basis, see [`UniformPolyBasis`](@ref). +Alias for Bernstein multivariate scalar' or `Multivalue`'d basis. """ const BernsteinBasis{D,V,K} = UniformPolyBasis{D,V,K,Bernstein} diff --git a/src/Polynomials/ChebyshevBases.jl b/src/Polynomials/ChebyshevBases.jl index 71f9beb57..913b58e21 100644 --- a/src/Polynomials/ChebyshevBases.jl +++ b/src/Polynomials/ChebyshevBases.jl @@ -1,8 +1,10 @@ """ Chebyshev{kind} <: Polynomial -Type representing Chebyshev polynomials of first and second kind where `kind` is -either `:T` or `:U` for first and second kind Chebyshev polynomials respectively. +Type representing Chebyshev polynomials of the +- first kind: `Chebyshev{:T}` +- second kind: `Chebyshev{:U}` +C.f. [Chebyshev polynomials](@ref) section. """ struct Chebyshev{kind} <: Polynomial end @@ -11,7 +13,7 @@ isHierarchical(::Type{<:Chebyshev}) = true """ ChebyshevBasis{D,V,kind,K} = UniformPolyBasis{D,V,K,Chebyshev{kind}} -Alias for Chebyshev multivariate scalar' or `Multivalue`'d basis, see [`UniformPolyBasis`](@ref). +Alias for Chebyshev multivariate scalar' or `Multivalue`'d basis. """ const ChebyshevBasis{D,V,kind,K} = UniformPolyBasis{D,V,K,Chebyshev{kind}} diff --git a/src/Polynomials/CompWiseTensorPolyBases.jl b/src/Polynomials/CompWiseTensorPolyBases.jl index 536c5e89e..f0206e125 100644 --- a/src/Polynomials/CompWiseTensorPolyBases.jl +++ b/src/Polynomials/CompWiseTensorPolyBases.jl @@ -3,11 +3,11 @@ "Polynomial basis of component wise tensor product polynomial spaces" -Polynomial basis for a multivariate `MultiValue`'d polynomial space: +Polynomial basis for a `D`-multivariate `V`-valued polynomial space: `V`(𝕊¹, 𝕊², ..., 𝕊ᴸ) -with `L`>1, where the scalar multivariate spaces 𝕊ˡ (for 1 ≤ l ≤ `L`) of each +with `L`>1, where the scalar `D`-multivariate spaces 𝕊ˡ (for 1 ≤ l ≤ `L`) of each (independent) component of `V` is the tensor product of 1D ℙ spaces of order α(l,n) for 1 ≤ n ≤ `D`, that is: @@ -243,13 +243,13 @@ end Return a basis of -ℕ𝔻ᴰₙ(□) = (ℚₙ)ᴰ ⊕ x × (ℚₙ \\ ℚₙ₋₁)ᴰ +ℕ𝔻ᴰₙ(□) = (ℚᴰₙ)ᴰ ⊕ x × (ℚᴰₙ \\ ℚᴰₙ₋₁)ᴰ with n=`order`, the polynomial space for Nedelec elements on `D`-dimensional cubes with scalar type `T`. The `order`=n argument has the following meaning: the curl of the functions in -this basis is in ℚₙ. +this basis is in (ℚᴰₙ)ᴰ. `PT<:Polynomial` is the choice of the family of the scalar 1D basis polynomials. @@ -291,13 +291,13 @@ end Return a basis of -ℝ𝕋ᴰₙ(□) = (ℚₙ)ᴰ ⊕ x (ℚₙ \\ ℚₙ₋₁) +ℝ𝕋ᴰₙ(□) = (ℚᴰₙ)ᴰ ⊕ x (ℚᴰₙ \\ ℚᴰₙ₋₁) with n=`order`, the polynomial space for Raviart-Thomas elements on `D`-dimensional cubes with scalar type `T`. The `order`=n argument has the following meaning: the divergence of the functions -in this basis is in ℚₙ. +in this basis is in ℚᴰₙ. `PT<:Polynomial` is the choice of the family of the scalar 1D basis polynomials. diff --git a/src/Polynomials/Deprecated.jl b/src/Polynomials/Deprecated.jl index a36b96fbe..6d5b1ed2d 100644 --- a/src/Polynomials/Deprecated.jl +++ b/src/Polynomials/Deprecated.jl @@ -1,7 +1,21 @@ +""" + num_terms(a::PolynomialBasis) + +!!! warning + Deprecated in favor of length(a). +""" +function num_terms end + @deprecate num_terms(a::PolynomialBasis) length(a) @deprecate MonomialBasis{D}(args...) where D MonomialBasis(Val(D), args...) +""" + PGradMonomialBasis{D}(args...) where D + +!!! warning + Deprecated in favor of PGradBasis(Monomial, Val(D), args...). +""" struct PGradMonomialBasis{D} function PGradMonomialBasis() @unreachable @@ -10,6 +24,12 @@ struct PGradMonomialBasis{D} end @deprecate PGradMonomialBasis{D}(args...) where D PGradBasis(Monomial, Val(D), args...) false +""" + PCurlGradMonomialBasis{D}(args...) where D + +!!! warning + Deprecated in favor of PCurlGradBasis(Monomial, Val(D), args...). +""" struct PCurlGradMonomialBasis{D} function PCurlGradMonomialBasis() @unreachable @@ -18,6 +38,12 @@ struct PCurlGradMonomialBasis{D} end @deprecate PCurlGradMonomialBasis{D}(args...) where D PCurlGradBasis(Monomial, Val(D), args...) +""" + QGradMonomialBasis{D}(args...) where D + +!!! warning + Deprecated in favor of QGradBasis(Monomial, Val(D), args...). +""" struct QGradMonomialBasis{D} function QGradMonomialBasis() @unreachable @@ -26,6 +52,12 @@ struct QGradMonomialBasis{D} end @deprecate QGradMonomialBasis{D}(args...) where D QGradBasis(Monomial, Val(D), args...) +""" + QCurlGradMonomialBasis{D}(args...) where D + +!!! warning + Deprecated in favor of QCurlGradBasis(Monomial, Val(D), args...). +""" struct QCurlGradMonomialBasis{D} function QCurlGradMonomialBasis() @unreachable @@ -42,6 +74,12 @@ struct NedelecPreBasisOnSimplex{D} end @deprecate NedelecPreBasisOnSimplex{D}(args...) where D NedelecPolyBasisOnSimplex{D}(args...) false +""" + JacobiPolynomialBasis{D}(args...) where D + +!!! warning + Deprecated in favor of LegendreBasis(Val(D), args...). +""" struct JacobiPolynomialBasis{D} function JacobiPolynomialBasis() @unreachable diff --git a/src/Polynomials/LegendreBases.jl b/src/Polynomials/LegendreBases.jl index 491dbd9c0..9b5109d47 100644 --- a/src/Polynomials/LegendreBases.jl +++ b/src/Polynomials/LegendreBases.jl @@ -1,7 +1,7 @@ """ Legendre <: Polynomial -Type representing the Legendre polynomials. +Type representing the normalised shifted Legendre polynomials, c.f. [Legendre polynomials](@ref) section. """ struct Legendre <: Polynomial end @@ -10,7 +10,7 @@ isHierarchical(::Type{Legendre}) = true """ LegendreBasis{D,V,K} = UniformPolyBasis{D,V,K,Legendre} -Alias for Legendre multivariate scalar' or `Multivalue`'d basis, see [`UniformPolyBasis`](@ref). +Alias for Legendre multivariate scalar' or `Multivalue`'d basis. """ const LegendreBasis{D,V,K} = UniformPolyBasis{D,V,K,Legendre} diff --git a/src/Polynomials/ModalC0Bases.jl b/src/Polynomials/ModalC0Bases.jl index 6e89b1f22..20addc626 100644 --- a/src/Polynomials/ModalC0Bases.jl +++ b/src/Polynomials/ModalC0Bases.jl @@ -1,12 +1,19 @@ """ ModalC0 <: Polynomial -Type representing ModalC0 polynomials. +Type representing ModalC0 polynomials, c.f. [ModalC0 polynomials](@ref) section. Reference: Eq. (17) in https://doi.org/10.1016/j.camwa.2022.09.027 """ struct ModalC0 <: Polynomial end +""" + ModalC0Basis{D,V,T,K} <: PolynomialBasis{D,V,K,ModalC0} + +Tensor product basis of generalised modal C0 1D basis from section 5.2 in +https://doi.org/10.1016/j.camwa.2022.09.027. +See also [ModalC0 polynomials](@ref) section of the documentation. +""" struct ModalC0Basis{D,V,T,K} <: PolynomialBasis{D,V,K,ModalC0} orders::NTuple{D,Int} terms::Vector{CartesianIndex{D}} diff --git a/src/Polynomials/MonomialBases.jl b/src/Polynomials/MonomialBases.jl index 58a6282d7..f37a7ab55 100644 --- a/src/Polynomials/MonomialBases.jl +++ b/src/Polynomials/MonomialBases.jl @@ -1,7 +1,7 @@ """ Monomial <: Polynomial -Type representing the monomial polynomials. +Type representing the monomial polynomials, c.f. [Monomials](@ref) section. """ struct Monomial <: Polynomial end @@ -10,7 +10,7 @@ isHierarchical(::Type{Monomial}) = true """ MonomialBasis{D,V,K} = UniformPolyBasis{D,V,K,Monomial} -Alias for monomial Multivariate scalar' or `Multivalue`'d basis, see [`UniformPolyBasis`](@ref). +Alias for monomial Multivariate scalar' or `Multivalue`'d basis. """ const MonomialBasis{D,V,K} = UniformPolyBasis{D,V,K,Monomial} diff --git a/src/Polynomials/NedelecPolyBases.jl b/src/Polynomials/NedelecPolyBases.jl index 7c96be8a1..a5d49af9d 100644 --- a/src/Polynomials/NedelecPolyBases.jl +++ b/src/Polynomials/NedelecPolyBases.jl @@ -1,3 +1,13 @@ +""" + NedelecPolyBasisOnSimplex{D,V,K,PT} <: PolynomialBasis{D,V,K,PT} + +Basis of the vector valued (`V<:VectorValue{D}`) space ℕ𝔻ᴰₙ(△) for `D`=2,3. +This space is the polynomial space for Nedelec elements on simplices with +curl in (ℙᴰₙ)ᴰ. Its maximum degree is n+1 = `K`. `get_order` on it returns `K`. + +Currently, the basis is implemented as the union of a UniformPolyBasis{...,PT} +for ℙᴰₙ and a monomial basis for x × (ℙᴰₙ \\ ℙᴰₙ₋₁)ᴰ. +""" struct NedelecPolyBasisOnSimplex{D,V,K,PT} <: PolynomialBasis{D,V,K,PT} order::Int function NedelecPolyBasisOnSimplex{D}(::Type{PT},::Type{T},order::Integer) where {D,PT<:Polynomial,T} @@ -241,13 +251,13 @@ end Return a basis of -ℕ𝔻ᴰₙ(△) = (ℙₙ)ᴰ ⊕ x × (ℙₙ \\ ℙₙ₋₁)ᴰ +ℕ𝔻ᴰₙ(△) = (ℙᴰₙ)ᴰ ⊕ x × (ℙᴰₙ \\ ℙᴰₙ₋₁)ᴰ with n=`order`, the polynomial space for Nedelec elements on `D`-dimensional simplices with scalar type `T`. `D` must be 1, 2 or 3. The `order`=n argument has the following meaning: the curl of the functions in -this basis is in ℙₙ. +this basis is in (ℙᴰₙ)ᴰ. # Example: diff --git a/src/Polynomials/Polynomials.jl b/src/Polynomials/Polynomials.jl index 450fd4084..b4fb645f2 100644 --- a/src/Polynomials/Polynomials.jl +++ b/src/Polynomials/Polynomials.jl @@ -15,10 +15,10 @@ Constructors for commonly used bases (see the documentation for the spaces defin - ℙ spaces: `[Polynomial]Basis(..., Polynomials._p_filter)` - ℚₙ\\ℚₙ₋₁: `[Polynomial]Basis(..., Polynomials._qs_filter)` - ℙₙ\\ℙₙ₋₁: `[Polynomial]Basis(..., Polynomials._ps_filter)` -- ℕ𝔻(△): `PGradBasis(Val(D), T, order)` -- ℕ𝔻(□): `QGradBasis(...)` -- ℝ𝕋(△): `PCurlGradBasis(...)` -- ℝ𝕋(□): `QCurlGradBasis(...)` +- ℕ𝔻(△): [`PGradBasis`](@ref)`(Val(D), T, order)` +- ℕ𝔻(□): [`QGradBasis`](@ref)`(...)` +- ℝ𝕋(△): [`PCurlGradBasis`](@ref)`(...)` +- ℝ𝕋(□): [`QCurlGradBasis`](@ref)`(...)` ### Examples @@ -28,7 +28,7 @@ using Gridap.Polynomials using Gridap.Fields: return_type # Basis of ℚ¹₂ of Float64 value type based on Bernstein polynomials: -# {(1-x)², 2x(1-x), x²} +# {(1-x)², 2x(1-x), x²} D = 1; n = 2 # spatial dimension and order b = BernsteinBasis(Val(D), Float64, n) @@ -43,20 +43,21 @@ evaluate(Broadcasting(∇)(b), xi) # gradients evaluate(Broadcasting(∇∇)(b), xi) # hessians, not all basis support hessians evaluate(b, [xi, xi]) # evaluation on arrays of points -# Basis of ℚ²₂ of Float64 value type based on Legendre polynomials, our 1D Legendre -# polynomials are normalized for L2 scalar product and moved from [-1,1] to [0,1] using x -> 2x-1 -# { 1, √3(2x-1), √5(6x²-6x+2), -# √3(2y-1), √3(2x-1)√3(2y-1), √5(6x²-6x+2)√3(2y-1), -# √5(6y²-6y+2), √3(2x-1)√5(6x²-6x+2), √5(6x²-6x+2)√5(6y²-6y+2) } -D = 2; n = 2 # spatial dimension and order +# Basis of ℚ²₂ of Float64 value type based on Legendre polynomials, our 1D +# Legendre polynomials are normalized for L2 scalar product and moved from +# [-1,1] to [0,1] using the change of variable x -> 2x-1 +# { 1, √3(2x-1), √5(6x²-6x+2), +# √3(2y-1), √3(2x-1)√3(2y-1), √5(6x²-6x+2)√3(2y-1), +# √5(6y²-6y+2), √3(2x-1)√5(6x²-6x+2), √5(6x²-6x+2)√5(6y²-6y+2) } +D = 2; n = 2 b = LegendreBasis(Val(D), Float64, n) # Basis of (ℙ³₁)³ of VectorValue{3,Float64} value type, based on monomials: -# {(1,0,0), (0,1,0), (0,0,1) -# (x,0,0), (0,x,0), (0,0,x) -# (y,0,0), (0,y,0), (0,0,y) -# (z,0,0), (0,z,0), (0,0,z)} -D = 3; n = 1 # spatial dimension and order +# {(1,0,0), (0,1,0), (0,0,1) +# (x,0,0), (0,x,0), (0,0,x) +# (y,0,0), (0,y,0), (0,0,y) +# (z,0,0), (0,z,0), (0,0,z)} +D = 3; n = 1 b = MonomialBasis(Val(D), VectorValue{D,Float64}, n, Polynomials._p_filter) evaluate(b, Point(.1, .2, .3) diff --git a/src/Polynomials/RaviartThomasPolyBases.jl b/src/Polynomials/RaviartThomasPolyBases.jl index 8cc689194..bc810f6ca 100644 --- a/src/Polynomials/RaviartThomasPolyBases.jl +++ b/src/Polynomials/RaviartThomasPolyBases.jl @@ -5,17 +5,17 @@ Basis of the vector valued (`V<:VectorValue{D}`) space ℝ𝕋ᴰₙ = (𝕊ₙ)ᴰ ⊕ x (𝕊ₙ\\𝕊₍ₙ₋₁₎) -where 𝕊ₙ is a multivariate scalar polynomial space of maximum degree n = `K`-1. +where 𝕊ₙ is a `D`-multivariate scalar polynomial space of maximum degree n = `K`-1. This ℝ𝕋ᴰₙ is the polynomial space for Raviart-Thomas elements with divergence in 𝕊ₙ. Its maximum degree is n+1 = `K`. `get_order` on it returns `K`. -The multivariate scalar space 𝕊ₙ, typically ℙₙ or ℚₙ, does not need to have a -tensor product structure of 1D scalar spaces. Thus, the ℝ𝕋ᴰₙ component's scalar -spaces are not tensor products either. +The space 𝕊ₙ, typically ℙᴰₙ or ℚᴰₙ, does not need to have a tensor product +structure of 1D scalar spaces. Thus, the ℝ𝕋ᴰₙ component's scalar spaces are not +tensor products either. 𝕊ₙ is defined like a scalar valued [`UniformPolyBasis`](@ref) via the `_filter` -argument of the constructor, by default [`_p_filter`](@ref) for ℙₙ. +argument of the constructor, by default `_p_filter` for ℙᴰₙ. As a consequence, `PT` must be hierarchical, see [`isHierarchical`](@ref). """ struct RaviartThomasPolyBasis{D,V,K,PT} <: PolynomialBasis{D,V,K,PT} @@ -35,7 +35,7 @@ struct RaviartThomasPolyBasis{D,V,K,PT} <: PolynomialBasis{D,V,K,PT} @check T<:Real "T needs to be <:Real since represents the type of the components of the vector value" @check D > 1 @check isconcretetype(PT) "PT needs to be a concrete <:Polynomial type" - @check isHierarchical(PT) "The polynomial basis must be hierarchichal for this space." + @check isHierarchical(PT) "The polynomial basis must be hierarchical for this space." V = VectorValue{D,T} indexbase = 1 @@ -194,16 +194,16 @@ end Return a basis of -ℝ𝕋ᴰₙ(△) = (ℙₙ)ᴰ ⊕ x (ℙₙ \\ ℙₙ₋₁) +ℝ𝕋ᴰₙ(△) = (ℙᴰₙ)ᴰ ⊕ x (ℙᴰₙ \\ ℙᴰₙ₋₁) with n=`order`, the polynomial space for Raviart-Thomas elements on `D`-dimensional simplices with scalar type `T`. The `order`=n argument of this function has the following meaning: the divergence -of the functions in this basis is in ℙₙ. +of the functions in this basis is in ℙᴰₙ. `PT<:Polynomial` is the choice of the family of the scalar 1D basis polynomials, -it must be hierarchichal, see [`isHierarchichal`](@ref). +it must be hierarchical, see [`isHierarchical`](@ref). # Example: diff --git a/src/Polynomials/UniformPolyBases.jl b/src/Polynomials/UniformPolyBases.jl index 982c983ea..f6bdf6c41 100644 --- a/src/Polynomials/UniformPolyBases.jl +++ b/src/Polynomials/UniformPolyBases.jl @@ -10,14 +10,14 @@ polynomial space `V`(𝕊, 𝕊, ..., 𝕊) -where 𝕊 is a scalar polynomial space. So each (independant) component of `V` -holds the same space, which is here called 'uniform'. +where 𝕊 is a scalar multivariate polynomial space. So each (independant) +component of `V` holds the same space (hence the name 'uniform'). The scalar polynomial basis spanning 𝕊 is defined as - { x ⟶ b`ᴷ`\\_α(x) = b`ᴷ`\\_α₁(x₁) × b`ᴷ`\\_α₂(x₂) × ... × b`ᴷ`\\_α`D`(x`D`) | α ∈ `terms` } + { x ⟶ bα`ᴷ`(x) = bα₁`ᴷ`(x₁) × bα₂`ᴷ`(x₂) × ... × bα`Dᴷ`(x`D`) | α ∈ `terms` } -where b`ᴷ`\\_αᵢ(xᵢ) is the αᵢth 1D basis polynomial of the basis `PT` of order `K` +where bαᵢ`ᴷ`(xᵢ) is the αᵢth 1D basis polynomial of the basis `PT` of order `K` evaluated at xᵢ (iᵗʰ comp. of x), and where α = (α₁, α₂, ..., α`D`) is a multi-index in `terms`, a subset of ⟦0,`K`⟧`ᴰ`. `terms` is a field that can be passed in a constructor. @@ -62,7 +62,7 @@ end This constructor allows to pass a tuple `orders` containing the polynomial order to be used in each of the `D` spatial dimensions in order to construct a -tensorial anisotropic multivariate space 𝕊. +tensorial anisotropic `D`-multivariate space 𝕊. """ function UniformPolyBasis( ::Type{PT}, ::Val{D}, ::Type{V}, orders::NTuple{D,Int}, filter::Function=_q_filter @@ -73,25 +73,14 @@ function UniformPolyBasis( end """ - UniformPolyBasis(::Type{PT}, ::Type{V}, ::Val{D}, order::Int [, filter=_q_filter]) where {D,V} + UniformPolyBasis(::Type{PT}, ::Type{V}, ::Val{D}, order::Int [, filter=_q_filter]) -Returns an instance of `UniformPolyBasis{D,V,order,PT}`. +Return a `UniformPolyBasis{D,V,order,PT}` where 𝕊 is defined by the terms +filtered by -# Filter function + term -> `filter`(term, `order`). -The `filter` function is used to select which terms of the tensor product space -of order `order` in `D` spatial dimensions are to be used. If the filter is not -provided, the full tensor-product space is used by default leading to a -multivariate polynomial space of type ℚ. The signature of the filter function is - - (e,order) -> Bool - -where `e` is a tuple of `D` integers containing the exponents of a multivariate -monomial. The following filters are used to select well known polynomial spaces - -- ℚ space: `_p_filter = (e,order) -> maximum(e) <= order` -- ℙ space: `_q_filter = (e,order) -> sum(e) <= order` -- "Serendipity" space: `(e,order) -> sum( [ i for i in e if i>1 ] ) <= order` +See the [Filter functions](@ref) section of the documentation for more details. """ function UniformPolyBasis( ::Type{PT}, VD::Val{D}, ::Type{V}, order::Int, filter::Function=_q_filter) where {PT,D,V} @@ -105,7 +94,8 @@ end """ get_exponents(b::UniformPolyBasis) -Get a vector of tuples with the exponents of all the terms in the basis of 𝕊. +Get a vector of tuples with the exponents of all the terms in the basis of 𝕊, +the components scalar space of `b`. # Example From e34f2291fa93bcd80018313384bd26f9254d7977 Mon Sep 17 00:00:00 2001 From: Antoine Marteau Date: Fri, 10 Jan 2025 11:36:59 +1100 Subject: [PATCH 103/105] cleaning serendipity filter defs and doc --- src/Polynomials/ModalC0Bases.jl | 2 - src/Polynomials/PolynomialInterfaces.jl | 120 ++++++++++++++---------- src/Polynomials/UniformPolyBases.jl | 31 ++---- src/ReferenceFEs/ModalC0RefFEs.jl | 6 +- src/ReferenceFEs/ReferenceFEs.jl | 2 +- src/ReferenceFEs/SerendipityRefFEs.jl | 4 - 6 files changed, 81 insertions(+), 84 deletions(-) diff --git a/src/Polynomials/ModalC0Bases.jl b/src/Polynomials/ModalC0Bases.jl index 20addc626..77d88ac8a 100644 --- a/src/Polynomials/ModalC0Bases.jl +++ b/src/Polynomials/ModalC0Bases.jl @@ -104,8 +104,6 @@ end # Helpers -_s_filter_mc0(e,o) = ( sum( [ i for i in e if i>1 ] ) <= o ) - function _sort_by_nfaces!(terms::Vector{CartesianIndex{D}},orders) where D # Generate indices of n-faces and order s.t. diff --git a/src/Polynomials/PolynomialInterfaces.jl b/src/Polynomials/PolynomialInterfaces.jl index 8008cd1a6..bdabfde1a 100644 --- a/src/Polynomials/PolynomialInterfaces.jl +++ b/src/Polynomials/PolynomialInterfaces.jl @@ -68,9 +68,29 @@ Return the maximum polynomial order in a dimension, is should be `0` in 0D. @inline get_order(::PolynomialBasis{D,V,K}) where {D,V,K} = K -################################ -# Generic field implementation # -################################ +########### +# Helpers # +########### + +_q_filter( e,order) = (maximum(e,init=0) <= order) # ℚₙ +_qs_filter(e,order) = (maximum(e,init=0) == order) # ℚₙ\ℚ₍ₙ₋₁₎ +_p_filter( e,order) = (sum(e) <= order) # ℙₙ +_ps_filter(e,order) = (sum(e) == order) # ℙₙ\ℙ₍ₙ₋₁₎ +_ser_filter(e,order) = (sum( [ i for i in e if i>1 ] ) <= order) # Serendipity + +function _define_terms(filter,orders) + t = orders .+ 1 + g = (0 .* orders) .+ 1 + cis = CartesianIndices(t) + co = CartesianIndex(g) + maxorder = maximum(orders, init=0) + [ ci for ci in cis if filter(Int[Tuple(ci-co)...],maxorder) ] +end + + +######################################### +# Generic array of field implementation # +######################################### function _return_cache( f::PolynomialBasis{D}, x,::Type{G},::Val{N_deriv}) where {D,G,N_deriv} @@ -162,6 +182,53 @@ function evaluate!(cache, end +############################################## +# Optimizing of evaluation at a single point # +############################################## + +function return_cache(f::PolynomialBasis,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::PolynomialBasis,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,<:PolynomialBasis}, 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,<:PolynomialBasis}, 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 + + ############################### # nD internal polynomial APIs # ############################### @@ -227,53 +294,6 @@ function _hessian_nd!( end -############################################## -# Optimizing of evaluation at a single point # -############################################## - -function return_cache(f::PolynomialBasis,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::PolynomialBasis,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,<:PolynomialBasis}, 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,<:PolynomialBasis}, 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 - - ############################### # 1D internal polynomial APIs # ############################### diff --git a/src/Polynomials/UniformPolyBases.jl b/src/Polynomials/UniformPolyBases.jl index f6bdf6c41..8068a9c48 100644 --- a/src/Polynomials/UniformPolyBases.jl +++ b/src/Polynomials/UniformPolyBases.jl @@ -60,9 +60,12 @@ end """ UniformPolyBasis(::Type{PT}, ::Val{D}, ::Type{V}, orders::Tuple [, filter=_q_filter]) -This constructor allows to pass a tuple `orders` containing the polynomial order -to be used in each of the `D` spatial dimensions in order to construct a -tensorial anisotropic `D`-multivariate space 𝕊. +This constructor allows to pass a tuple `orders` containing the maximal +polynomial order to be used in each of the `D` spatial dimensions in order to +construct a tensorial anisotropic `D`-multivariate space 𝕊. + +If a filter is provided, it is applied on the cartesian product terms +CartesianIndices(`orders`), with maximum(`orders`) as order argument. """ function UniformPolyBasis( ::Type{PT}, ::Val{D}, ::Type{V}, orders::NTuple{D,Int}, filter::Function=_q_filter @@ -78,7 +81,7 @@ end Return a `UniformPolyBasis{D,V,order,PT}` where 𝕊 is defined by the terms filtered by - term -> `filter`(term, `order`). + term -> filter(term, order). See the [Filter functions](@ref) section of the documentation for more details. """ @@ -126,26 +129,6 @@ function get_orders(b::UniformPolyBasis) b.orders end - -########### -# Helpers # -########### - -_q_filter( e,order) = (maximum(e,init=0) <= order) # ℚₙ -_qs_filter(e,order) = (maximum(e,init=0) == order) # ℚₙ\ℚ₍ₙ₋₁₎ -_p_filter( e,order) = (sum(e) <= order) # ℙₙ -_ps_filter(e,order) = (sum(e) == order) # ℙₙ\ℙ₍ₙ₋₁₎ - -function _define_terms(filter,orders) - t = orders .+ 1 - g = (0 .* orders) .+ 1 - cis = CartesianIndices(t) - co = CartesianIndex(g) - maxorder = maximum(orders, init=0) - [ ci for ci in cis if filter(Int[Tuple(ci-co)...],maxorder) ] -end - - ################################# # nD evaluations implementation # ################################# diff --git a/src/ReferenceFEs/ModalC0RefFEs.jl b/src/ReferenceFEs/ModalC0RefFEs.jl index c6529906c..631d6681d 100644 --- a/src/ReferenceFEs/ModalC0RefFEs.jl +++ b/src/ReferenceFEs/ModalC0RefFEs.jl @@ -150,7 +150,7 @@ function compute_cell_to_modalC0_reffe( 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 = space == :Q ? _q_filter : _s_filter_mc0 + filter = space == :Q ? _q_filter : _ser_filter sh(bbs) = begin a = fill(Point{D,eltype(T)}(tfill(zero(eltype(T)),Val{D}())),ndofs) @@ -181,7 +181,7 @@ function compute_cell_to_modalC0_reffe( @notimplementedif ! is_n_cube(p) @notimplementedif minimum(orders) < one(eltype(orders)) - filter = space == :Q ? _q_filter : _s_filter_mc0 + filter = space == :Q ? _q_filter : _ser_filter ndofs, predofs, lag_reffe, face_dofs = compute_reffe_data(T,p,orders,space=space) reffe = GenericRefFE{ModalC0}(ndofs, @@ -193,4 +193,4 @@ function compute_cell_to_modalC0_reffe( ModalC0Basis{D}(T,orders,filter=filter)) Fill(reffe,ncells) -end \ No newline at end of file +end diff --git a/src/ReferenceFEs/ReferenceFEs.jl b/src/ReferenceFEs/ReferenceFEs.jl index ea663c9b2..289cdc256 100644 --- a/src/ReferenceFEs/ReferenceFEs.jl +++ b/src/ReferenceFEs/ReferenceFEs.jl @@ -18,7 +18,7 @@ using Gridap.TensorValues using Gridap.Fields using Gridap.Polynomials -using Gridap.Polynomials: _q_filter, _s_filter_mc0 +using Gridap.Polynomials: _q_filter, _ser_filter using Gridap.Polynomials: _compute_filter_mask using Gridap.Polynomials: _define_terms, _sort_by_nfaces! diff --git a/src/ReferenceFEs/SerendipityRefFEs.jl b/src/ReferenceFEs/SerendipityRefFEs.jl index 3fe15c0a7..064e75553 100644 --- a/src/ReferenceFEs/SerendipityRefFEs.jl +++ b/src/ReferenceFEs/SerendipityRefFEs.jl @@ -87,10 +87,6 @@ get_extrusion(p::SerendipityPolytope{D}) where D = Point(tfill(HEX_AXIS,Val{D}() # Implemented polytope interface for LagrangianRefFEs -function _ser_filter(e,order) - sum( [ i for i in e if i>1 ] ) <= order -end - function compute_monomial_basis(::Type{T},p::SerendipityPolytope{D},orders) where {T,D} MonomialBasis(Val(D),T,orders,_ser_filter) end From 7ab734164927bddd9221edcec2cb7a5a6334754f Mon Sep 17 00:00:00 2001 From: Antoine Marteau Date: Fri, 10 Jan 2025 18:15:12 +1100 Subject: [PATCH 104/105] make ModalC0 compatible with all basis parametrised by Polynomial --- docs/src/Polynomials.md | 26 ++++---- src/Polynomials/ModalC0Bases.jl | 69 ++++++++++++++++++---- test/PolynomialsTests/ModalC0BasesTests.jl | 45 +++++++++++++- 3 files changed, 117 insertions(+), 23 deletions(-) diff --git a/docs/src/Polynomials.md b/docs/src/Polynomials.md index 58143c701..c1f116dea 100644 --- a/docs/src/Polynomials.md +++ b/docs/src/Polynomials.md @@ -96,29 +96,35 @@ polynomials ``\phi_K`` for which ``\phi_K(0) = \delta_{0K}`` and ``\phi_K(1) = \delta_{1K}``. This module implements the generalised version introduced in Eq. 17 of [Badia-Neiva-Verdugo 2022](https://doi.org/10.1016/j.camwa.2022.09.027). +When `ModalC0` is used as a `<:Polynomial` parameter in +[`UniformPolyBasis`](@ref) or other bases (except `ModalC0Basis`), the trivial +bounding box `(a=Point{D}(0...), b=Point{D}(1...))` is assumed, which +coincides with the usual definition of the ModalC0 bases. + ### Polynomial spaces in FEM #### P and Q spaces +Let us denote ``\mathbb{P}_K(x)`` the space of univariate polynomials of order up to ``K`` in the varible ``x`` ```math -\mathbb{P}_K(x) = \text{Span}\big\{\quad x\rightarrow x^i \quad\big|\quad 0\leq i\leq K \quad\big\} +\mathbb{P}_K(x) = \text{Span}\big\{\quad x\rightarrow x^i \quad\big|\quad 0\leq i\leq K \quad\big\}. ``` +Then, ``\mathbb{Q}^D`` and ``\mathbb{P}^D`` are the spaces for Lagrange elements +on D-cubes and D-simplices respectively, defined by ```math \mathbb{Q}^D_K = \text{Span}\big\{\quad \bm{x}\rightarrow\bm{x}^\alpha \quad\big|\quad 0\leq - \alpha_1, \alpha_2, \dots, \alpha_D \leq K \quad\big\} + \alpha_1, \alpha_2, \dots, \alpha_D \leq K \quad\big\}, ``` - +and ```math \mathbb{P}^D_K = \text{Span}\big\{\quad \bm{x}\rightarrow\bm{x}^\alpha \quad\big|\quad 0\leq \alpha_1, \alpha_2, \dots, \alpha_D \leq K;\quad \sum_{d=1}^D \alpha_d \leq - K \quad\big\} + K \quad\big\}. ``` -```math -\mathbb{P}_K = \mathbb{P}^1_K = \mathbb{Q}^1_K -``` +To note, there is ``\mathbb{P}_K = \mathbb{P}^1_K = \mathbb{Q}^1_K``. #### Serendipity space Sr @@ -263,10 +269,6 @@ BernsteinBasis(args...) ```@docs BernsteinBasis -ModalC0Basis -``` - -```@docs PGradBasis QGradBasis PCurlGradBasis @@ -285,6 +287,8 @@ get_exponents CompWiseTensorPolyBasis NedelecPolyBasisOnSimplex RaviartThomasPolyBasis +ModalC0Basis +ModalC0Basis() ``` ### Deprecated APIs diff --git a/src/Polynomials/ModalC0Bases.jl b/src/Polynomials/ModalC0Bases.jl index 77d88ac8a..42cc40a0c 100644 --- a/src/Polynomials/ModalC0Bases.jl +++ b/src/Polynomials/ModalC0Bases.jl @@ -27,6 +27,8 @@ struct ModalC0Basis{D,V,T,K} <: PolynomialBasis{D,V,K,ModalC0} a::Vector{Point{D,T}}, b::Vector{Point{D,T}}) where {D,V,T} + _msg = "The number of bounding box points in a and b should match the number of terms" + @check length(terms) == length(a) == length(b) _msg @check T == eltype(V) "Point and polynomial values should have the same scalar body" K = maximum(orders, init=0) @@ -34,6 +36,25 @@ struct ModalC0Basis{D,V,T,K} <: PolynomialBasis{D,V,K,ModalC0} end end +""" + ModalC0Basis{D}(::Type{V},order::Int; [, filter][, sort!:]) + ModalC0Basis{D}(::Type{V},order::Int, a::Point ,b::Point ; [, filter][, sort!:]) + ModalC0Basis{D}(::Type{V},orders::Tuple; [, filter][, sort!:]) + ModalC0Basis{D}(::Type{V},orders::Tuple,a::Point ,b::Point ; [, filter][, sort!:]) + ModalC0Basis{D}(::Type{V},orders::Tuple,a::Vector,b::Vector; [, filter][, sort!:]) + +where `filter` is a `Function` defaulting to `_q_filter`, and `sort!` is a +`Function` defaulting to `_sort_by_nfaces!`. + +At last, all scalar basis polynomial will have its bounding box `(a[i],b[i])`, +but they are assumed iddentical if only two points `a` and `b` are provided, +and default to `a=Point{D}(0...)`, `b=Point{D}(1...)` if not provided. + +The basis is uniform, isotropic if one `order` is provided, or anisotropic if a +`D` tuple `orders` is provided. +""" +function ModalC0Basis() end + function ModalC0Basis{D}( ::Type{V}, orders::NTuple{D,Int}, @@ -94,6 +115,7 @@ function ModalC0Basis{D}( ModalC0Basis{D}(V,orders; filter=filter, sort! =sort!) end + # API @inline Base.size(a::ModalC0Basis{D,V}) where {D,V} = (length(a.terms)*num_indep_components(V),) @@ -352,12 +374,12 @@ end function _gradient_1d_mc0!(g::AbstractMatrix{T},x,a,b,order,d) where T @assert order > 0 n = order + 1 - z = one(T) - @inbounds g[d,1] = -z - @inbounds g[d,2] = z + o = one(T) + @inbounds g[d,1] = -o + @inbounds g[d,2] = o if n > 2 ξ = ( 2*x[d] - ( a[d] + b[d] ) ) / ( b[d] - a[d] ) - v1 = z - x[d] + v1 = o - x[d] v2 = x[d] for i in 3:n j, dj = jacobi_and_derivative(ξ,i-3,1,1) @@ -369,16 +391,16 @@ end function _hessian_1d_mc0!(h::AbstractMatrix{T},x,a,b,order,d) where T @assert order > 0 n = order + 1 - y = zero(T) - z = one(T) - @inbounds h[d,1] = y - @inbounds h[d,2] = y + z = zero(T) + o = one(T) + @inbounds h[d,1] = z + @inbounds h[d,2] = z if n > 2 ξ = ( 2*x[d] - ( a[d] + b[d] ) ) / ( b[d] - a[d] ) - v1 = z - x[d] + v1 = o - x[d] v2 = x[d] - dv1 = -z - dv2 = z + dv1 = -o + dv2 = o for i in 3:n j, dj = jacobi_and_derivative(ξ,i-3,1,1) _, d2j = jacobi_and_derivative(ξ,i-4,2,2) @@ -387,3 +409,28 @@ function _hessian_1d_mc0!(h::AbstractMatrix{T},x,a,b,order,d) where T end end + +####################################### +# Generic 1D internal polynomial APIs # +####################################### + +# For possible use with UniformPolyBasis etc. +# Make it for x∈[0,1] like the other 1D bases. + +function _evaluate_1d!(::Type{ModalC0},::Val{K},c::AbstractMatrix{T},x,d) where {K,T<:Number} + a = zero(x) + b = zero(x) .+ one(T) + @inline _evaluate_1d_mc0!(c,x,a,b,K,d) +end + +function _gradient_1d!(::Type{ModalC0},::Val{K},g::AbstractMatrix{T},x,d) where {K,T<:Number} + a = zero(x) + b = zero(x) .+ one(T) + @inline _gradient_1d_mc0!(g,x,a,b,K,d) +end + +function _hessian_1d!(::Type{ModalC0},::Val{K},h::AbstractMatrix{T},x,d) where {K,T<:Number} + a = zero(x) + b = zero(x) .+ one(T) + @inline _hessian_1d_mc0!(h,x,a,b,K,d) +end diff --git a/test/PolynomialsTests/ModalC0BasesTests.jl b/test/PolynomialsTests/ModalC0BasesTests.jl index de293238e..04c4f839b 100644 --- a/test/PolynomialsTests/ModalC0BasesTests.jl +++ b/test/PolynomialsTests/ModalC0BasesTests.jl @@ -39,6 +39,24 @@ b1 = ModalC0Basis{1}(V,order,a,b) (0.0,) (0.0,) (3.4641016151377544,) (-1.4907119849998598,); (0.0,) (0.0,) (3.4641016151377544,) (2.9814239699997196,)] +# Validate generic 1D implem using UniformPolyBasis + +order = 3 +a = fill(Point(0.),order+1) +b = fill(Point(1.),order+1) +b1 = ModalC0Basis{1}(V,order,a,b) +b1u= UniformPolyBasis(ModalC0,Val(1),V,order) + +∇b1 = Broadcasting(∇)(b1) +∇b1u = Broadcasting(∇)(b1u) +∇∇b1 = Broadcasting(∇)(∇b1) +∇∇b1u= Broadcasting(∇)(∇b1u) + +@test evaluate(b1, [x1,x2,x3,]) ≈ evaluate(b1u, [x1,x2,x3,]) +@test evaluate(∇b1, [x1,x2,x3,]) ≈ evaluate(∇b1u, [x1,x2,x3,]) +@test evaluate(∇∇b1,[x1,x2,x3,]) ≈ evaluate(∇∇b1u,[x1,x2,x3,]) + + x1 = Point(0.0,0.0) x2 = Point(0.5,0.5) x3 = Point(1.0,1.0) @@ -51,7 +69,6 @@ b = [ Point(1.0,1.0),Point(1.0,1.0),Point(1.0,1.0),Point(1.0,1.0), 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 = ModalC0Basis{2}(V,order,a,b) -#b2 = ModalC0Basis(Val(2),V,order,a,b) ∇b2 = Broadcasting(∇)(b2) ∇∇b2 = Broadcasting(∇)(∇b2) @@ -65,6 +82,32 @@ H = gradient_type(G,x1) @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) ] + +# Validate generic 2D implem using UniformPolyBasis + +order = 3 +len_b2 = (order+1)^2 +a = fill(Point(0.,0.), len_b2) +b = fill(Point(1.,1.), len_b2) + +b2 = ModalC0Basis{2}(V,order,a,b) +b2u= UniformPolyBasis(ModalC0,Val(2),V,order) +∇b2 = Broadcasting(∇)(b2) +∇b2u = Broadcasting(∇)(b2u) + +b2x = eachcol(evaluate(b2, [x1,x2,x3,])) +b2xu = eachcol(evaluate(b2u, [x1,x2,x3,])) +∇b2x = eachcol(evaluate(∇b2, [x1,x2,x3,])) +∇b2xu = eachcol(evaluate(∇b2u, [x1,x2,x3,])) + +# re order basis polynomials as each basis has different ordering ... +b2x_perm = b2x[ sortperm(b2x)[ invperm(sortperm(b2xu))]] +∇b2x_perm = ∇b2x[ sortperm(∇b2x)[ invperm(sortperm(∇b2xu))]] + +@test b2xu == b2x_perm +@test ∇b2xu == ∇b2x_perm + + # Misc # Derivatives not implemented for symetric tensor types From 57af29c6618281aaeb6d506130f4d13b232ca767 Mon Sep 17 00:00:00 2001 From: Antoine Marteau Date: Fri, 10 Jan 2025 19:21:04 +1100 Subject: [PATCH 105/105] fix test on julia 1.8 --- test/PolynomialsTests/ModalC0BasesTests.jl | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/test/PolynomialsTests/ModalC0BasesTests.jl b/test/PolynomialsTests/ModalC0BasesTests.jl index 04c4f839b..3ba41a78e 100644 --- a/test/PolynomialsTests/ModalC0BasesTests.jl +++ b/test/PolynomialsTests/ModalC0BasesTests.jl @@ -95,10 +95,10 @@ b2u= UniformPolyBasis(ModalC0,Val(2),V,order) ∇b2 = Broadcasting(∇)(b2) ∇b2u = Broadcasting(∇)(b2u) -b2x = eachcol(evaluate(b2, [x1,x2,x3,])) -b2xu = eachcol(evaluate(b2u, [x1,x2,x3,])) -∇b2x = eachcol(evaluate(∇b2, [x1,x2,x3,])) -∇b2xu = eachcol(evaluate(∇b2u, [x1,x2,x3,])) +b2x = collect(eachcol(evaluate(b2, [x1,x2,x3,]))) +b2xu = collect(eachcol(evaluate(b2u, [x1,x2,x3,]))) +∇b2x = collect(eachcol(evaluate(∇b2, [x1,x2,x3,]))) +∇b2xu = collect(eachcol(evaluate(∇b2u, [x1,x2,x3,]))) # re order basis polynomials as each basis has different ordering ... b2x_perm = b2x[ sortperm(b2x)[ invperm(sortperm(b2xu))]]