From d491b71f13f07c7be628b41f6bf267259c50598e Mon Sep 17 00:00:00 2001 From: Anne Boschman Date: Thu, 26 Jun 2025 10:10:26 +1000 Subject: [PATCH 1/9] start Scaled Contravariant Piola Map --- src/FESpaces/DivConformingFESpaces.jl | 14 +++++++--- src/FESpaces/FESpaceFactories.jl | 10 ++++++-- src/ReferenceFEs/RaviartThomasRefFEs.jl | 34 +++++++++++++++++++++++-- src/ReferenceFEs/ReferenceFEs.jl | 1 + 4 files changed, 52 insertions(+), 7 deletions(-) diff --git a/src/FESpaces/DivConformingFESpaces.jl b/src/FESpaces/DivConformingFESpaces.jl index 5decd79ab..2e9115ab4 100644 --- a/src/FESpaces/DivConformingFESpaces.jl +++ b/src/FESpaces/DivConformingFESpaces.jl @@ -29,7 +29,8 @@ 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)) + map_type=nothing) + 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) @@ -48,9 +49,16 @@ end function get_cell_shapefuns(model::DiscreteModel, cell_reffe::AbstractArray{<:GenericRefFE{<:DivConforming}}, ::DivConformity, - sign_flip=get_sign_flip(model, cell_reffe)) + map_type=nothing) + sign_flip=get_sign_flip(model, cell_reffe) cell_reffe_shapefuns=lazy_map(get_shapefuns,cell_reffe) - k=ContraVariantPiolaMap() + + if map_type == nothing + k=ContraVariantPiolaMap() + else + k=map_type + end + lazy_map(k, cell_reffe_shapefuns, get_cell_map(Triangulation(model)), diff --git a/src/FESpaces/FESpaceFactories.jl b/src/FESpaces/FESpaceFactories.jl index b9a1d0831..7f9231e82 100644 --- a/src/FESpaces/FESpaceFactories.jl +++ b/src/FESpaces/FESpaceFactories.jl @@ -71,7 +71,8 @@ function FESpace( dirichlet_tags=Int[], dirichlet_masks=nothing, constraint=nothing, - vector_type=nothing) + vector_type=nothing, + map_type=nothing) conf = Conformity(testitem(cell_reffe),conformity) @@ -89,8 +90,13 @@ function FESpace( trian) return V end + + if map_type == nothing + cell_fe = CellFE(model,cell_reffe,conf) + else + cell_fe = CellFE(model,cell_reffe,conf,map_type) + end - cell_fe = CellFE(model,cell_reffe,conf) _vector_type = _get_vector_type(vector_type,cell_fe,trian) if conformity in (L2Conformity(),:L2) && dirichlet_tags == Int[] F = _DiscontinuousFESpace(_vector_type,trian,cell_fe) diff --git a/src/ReferenceFEs/RaviartThomasRefFEs.jl b/src/ReferenceFEs/RaviartThomasRefFEs.jl index d4388ea94..953ce087f 100644 --- a/src/ReferenceFEs/RaviartThomasRefFEs.jl +++ b/src/ReferenceFEs/RaviartThomasRefFEs.jl @@ -375,6 +375,7 @@ function _eval_moment_dof_basis!(dofs,vals::AbstractMatrix,b) end struct ContraVariantPiolaMap <: Map end +struct ScaledContraVariantPiolaMap <: Map end function evaluate!( cache, @@ -387,6 +388,17 @@ function evaluate!( Broadcasting(Operation(k))(∇v,Jt,detJ,sign_flip) end +function evaluate!( + cache, + ::Broadcasting{typeof(∇)}, + a::Fields.BroadcastOpFieldArray{ScaledContraVariantPiolaMap}) + v, Jt, detJ,sign_flip = a.args + # Assuming J comes from an affine map + ∇v = Broadcasting(∇)(v) + k = ContraVariantPiolaMap() + Broadcasting(Operation(k))(∇v,Jt,detJ,sign_flip) +end + function lazy_map( ::Broadcasting{typeof(gradient)}, a::LazyArray{<:Fill{Broadcasting{Operation{ContraVariantPiolaMap}}}}) @@ -396,6 +408,15 @@ function lazy_map( lazy_map(Broadcasting(Operation(k)),∇v,Jt,detJ,sign_flip) end +function lazy_map( + ::Broadcasting{typeof(gradient)}, + a::LazyArray{<:Fill{Broadcasting{Operation{ScaledContraVariantPiolaMap}}}}) + v, Jt, detJ,sign_flip = a.args + ∇v = lazy_map(Broadcasting(∇),v) + k = ScaledContraVariantPiolaMap() + lazy_map(Broadcasting(Operation(k)),∇v,Jt,detJ,sign_flip) +end + function evaluate!(cache,::ContraVariantPiolaMap, v::Number, Jt::Number, @@ -404,6 +425,15 @@ function evaluate!(cache,::ContraVariantPiolaMap, ((-1)^sign_flip*v)⋅((1/detJ)*Jt) end +function evaluate!(cache,::ScaledContraVariantPiolaMap, + v::Number, + Jt::Number, + detJ::Number, + sign_flip::Bool) + D = size(Jt)[1] + ((-1)^sign_flip*v)⋅((1/(detJ^(1/D)))*Jt) +end + function evaluate!(cache, k::ContraVariantPiolaMap, v::AbstractVector{<:Field}, @@ -415,7 +445,7 @@ function evaluate!(cache, end function lazy_map( - k::ContraVariantPiolaMap, + k::Union{ContraVariantPiolaMap,ScaledContraVariantPiolaMap}, cell_ref_shapefuns::AbstractArray{<:AbstractArray{<:Field}}, cell_map::AbstractArray{<:Field}, sign_flip::AbstractArray{<:AbstractArray{<:Field}}) @@ -424,4 +454,4 @@ function lazy_map( cell_detJ = lazy_map(Operation(meas),cell_Jt) lazy_map(Broadcasting(Operation(k)),cell_ref_shapefuns,cell_Jt,cell_detJ,sign_flip) -end +end \ No newline at end of file diff --git a/src/ReferenceFEs/ReferenceFEs.jl b/src/ReferenceFEs/ReferenceFEs.jl index 3a651eb42..dcb59e188 100644 --- a/src/ReferenceFEs/ReferenceFEs.jl +++ b/src/ReferenceFEs/ReferenceFEs.jl @@ -97,6 +97,7 @@ export TET_AXIS export INVALID_PERM export ContraVariantPiolaMap +export ScaledContraVariantPiolaMap export Dof export get_nodes From 920f7d0369a0046fe90c81a724131ea15281702e Mon Sep 17 00:00:00 2001 From: amboschman Date: Thu, 26 Jun 2025 15:24:08 +1000 Subject: [PATCH 2/9] updated interface of CellFE, cell_shapefuns, cell_dof_basis with args and kwargs --- src/FESpaces/ConformingFESpaces.jl | 15 ++++++++++----- src/FESpaces/CurlConformingFESpaces.jl | 8 ++++++-- src/FESpaces/DivConformingFESpaces.jl | 19 ++++++++----------- src/FESpaces/FESpaceFactories.jl | 8 ++------ src/ReferenceFEs/RaviartThomasRefFEs.jl | 5 +++-- src/ReferenceFEs/ReferenceFEs.jl | 1 + 6 files changed, 30 insertions(+), 26 deletions(-) diff --git a/src/FESpaces/ConformingFESpaces.jl b/src/FESpaces/ConformingFESpaces.jl index b00a2ec17..0a60b31e6 100644 --- a/src/FESpaces/ConformingFESpaces.jl +++ b/src/FESpaces/ConformingFESpaces.jl @@ -95,14 +95,15 @@ function CellFE( model::DiscreteModel, cell_reffe::AbstractArray{<:ReferenceFE}, conformity::Conformity, - args... + args...; + kwargs... ) cell_conformity = CellConformity(cell_reffe,conformity) ctype_reffe, cell_ctype = compress_cell_data(cell_reffe) ctype_num_dofs = map(num_dofs,ctype_reffe) ctype_ldof_comp = map(reffe->get_dof_to_comp(reffe),ctype_reffe) - cell_shapefuns = get_cell_shapefuns(model,cell_reffe,conformity,args...) - cell_dof_basis = get_cell_dof_basis(model,cell_reffe,conformity,args...) + cell_shapefuns = get_cell_shapefuns(model,cell_reffe,conformity,args...;kwargs...) + cell_dof_basis = get_cell_dof_basis(model,cell_reffe,conformity,args...;kwargs...) cell_shapefuns_domain = ReferenceDomain() cell_dof_basis_domain = cell_shapefuns_domain max_order = maximum(map(get_order,ctype_reffe)) @@ -122,13 +123,17 @@ end function get_cell_dof_basis(model::DiscreteModel, cell_reffe::AbstractArray{<:ReferenceFE}, - ::Conformity) + ::Conformity, + args...; + kwargs...) lazy_map(get_dof_basis,cell_reffe) end function get_cell_shapefuns(model::DiscreteModel, cell_reffe::AbstractArray{<:ReferenceFE}, - ::Conformity) + ::Conformity, + args...; + kwargs...) lazy_map(get_shapefuns,cell_reffe) end diff --git a/src/FESpaces/CurlConformingFESpaces.jl b/src/FESpaces/CurlConformingFESpaces.jl index 39690f46a..21c322dbb 100644 --- a/src/FESpaces/CurlConformingFESpaces.jl +++ b/src/FESpaces/CurlConformingFESpaces.jl @@ -2,7 +2,9 @@ function get_cell_dof_basis( model::DiscreteModel, cell_reffe::AbstractArray{<:GenericRefFE{Nedelec}}, - ::CurlConformity) + ::CurlConformity, + args...; + kwargs...) cell_map = get_cell_map(Triangulation(model)) phi = cell_map[1] reffe = cell_reffe[1] @@ -50,7 +52,9 @@ end function get_cell_shapefuns( model::DiscreteModel, cell_reffe::AbstractArray{<:GenericRefFE{Nedelec}}, - ::CurlConformity) + ::CurlConformity, + args...; + kwargs...) cell_reffe_shapefuns = lazy_map(get_shapefuns,cell_reffe) cell_map = get_cell_map(Triangulation(model)) diff --git a/src/FESpaces/DivConformingFESpaces.jl b/src/FESpaces/DivConformingFESpaces.jl index 2e9115ab4..75c0fa6f2 100644 --- a/src/FESpaces/DivConformingFESpaces.jl +++ b/src/FESpaces/DivConformingFESpaces.jl @@ -29,8 +29,9 @@ struct TransformRTDofBasis{Dc,Dp} <: Map end ; function get_cell_dof_basis(model::DiscreteModel, cell_reffe::AbstractArray{<:GenericRefFE{<:DivConforming}}, ::DivConformity, - map_type=nothing) - sign_flip = get_sign_flip(model, cell_reffe) + args...; + sign_flip=get_sign_flip(model, cell_reffe), + kwargs...) cell_map = get_cell_map(Triangulation(model)) phi = cell_map[1] Jt = lazy_map(Broadcasting(∇),cell_map) @@ -49,17 +50,13 @@ end function get_cell_shapefuns(model::DiscreteModel, cell_reffe::AbstractArray{<:GenericRefFE{<:DivConforming}}, ::DivConformity, - map_type=nothing) - sign_flip=get_sign_flip(model, cell_reffe) + args...; + sign_flip=get_sign_flip(model, cell_reffe), + contra_variant_piola_map_type=ContraVariantPiolaMap(), + kwargs...) cell_reffe_shapefuns=lazy_map(get_shapefuns,cell_reffe) - - if map_type == nothing - k=ContraVariantPiolaMap() - else - k=map_type - end - lazy_map(k, + lazy_map(contra_variant_piola_map_type, cell_reffe_shapefuns, get_cell_map(Triangulation(model)), lazy_map(Broadcasting(constant_field), sign_flip)) diff --git a/src/FESpaces/FESpaceFactories.jl b/src/FESpaces/FESpaceFactories.jl index 7f9231e82..4a2801a93 100644 --- a/src/FESpaces/FESpaceFactories.jl +++ b/src/FESpaces/FESpaceFactories.jl @@ -72,7 +72,7 @@ function FESpace( dirichlet_masks=nothing, constraint=nothing, vector_type=nothing, - map_type=nothing) + contra_variant_piola_map_type::ContraVariantPiolaMapType=ContraVariantPiolaMap()) conf = Conformity(testitem(cell_reffe),conformity) @@ -91,11 +91,7 @@ function FESpace( return V end - if map_type == nothing - cell_fe = CellFE(model,cell_reffe,conf) - else - cell_fe = CellFE(model,cell_reffe,conf,map_type) - end + cell_fe = CellFE(model,cell_reffe,conf;contra_variant_piola_map_type=contra_variant_piola_map_type) _vector_type = _get_vector_type(vector_type,cell_fe,trian) if conformity in (L2Conformity(),:L2) && dirichlet_tags == Int[] diff --git a/src/ReferenceFEs/RaviartThomasRefFEs.jl b/src/ReferenceFEs/RaviartThomasRefFEs.jl index 953ce087f..64e1e91ad 100644 --- a/src/ReferenceFEs/RaviartThomasRefFEs.jl +++ b/src/ReferenceFEs/RaviartThomasRefFEs.jl @@ -374,8 +374,9 @@ function _eval_moment_dof_basis!(dofs,vals::AbstractMatrix,b) end end -struct ContraVariantPiolaMap <: Map end -struct ScaledContraVariantPiolaMap <: Map end +abstract type ContraVariantPiolaMapType <: Map end +struct ContraVariantPiolaMap <: ContraVariantPiolaMapType end +struct ScaledContraVariantPiolaMap <: ContraVariantPiolaMapType end function evaluate!( cache, diff --git a/src/ReferenceFEs/ReferenceFEs.jl b/src/ReferenceFEs/ReferenceFEs.jl index dcb59e188..855b84d1f 100644 --- a/src/ReferenceFEs/ReferenceFEs.jl +++ b/src/ReferenceFEs/ReferenceFEs.jl @@ -96,6 +96,7 @@ export HEX_AXIS export TET_AXIS export INVALID_PERM +export ContraVariantPiolaMapType export ContraVariantPiolaMap export ScaledContraVariantPiolaMap From df5a09719c7756ae783c810cef95af684f5da8dc Mon Sep 17 00:00:00 2001 From: amboschman Date: Fri, 27 Jun 2025 11:13:20 +1000 Subject: [PATCH 3/9] Added scaled_meas as an operation --- src/Fields/Fields.jl | 2 +- src/Fields/FieldsInterfaces.jl | 2 +- src/TensorValues/Operations.jl | 14 ++++++++++++++ src/TensorValues/TensorValues.jl | 2 +- 4 files changed, 17 insertions(+), 3 deletions(-) diff --git a/src/Fields/Fields.jl b/src/Fields/Fields.jl index 43994319a..c2d3e5d15 100644 --- a/src/Fields/Fields.jl +++ b/src/Fields/Fields.jl @@ -28,7 +28,7 @@ import LinearAlgebra: det, inv, transpose, tr, cross import LinearAlgebra: ⋅, dot import Base: +, -, *, / -import Gridap.TensorValues: ⊗, ⊙, symmetric_part, outer, meas +import Gridap.TensorValues: ⊗, ⊙, symmetric_part, outer, meas, scaled_meas import Gridap.Arrays: IndexStyle import Gridap.Arrays: return_cache diff --git a/src/Fields/FieldsInterfaces.jl b/src/Fields/FieldsInterfaces.jl index 65bedaee5..5606bbb7d 100644 --- a/src/Fields/FieldsInterfaces.jl +++ b/src/Fields/FieldsInterfaces.jl @@ -363,7 +363,7 @@ evaluate!(cache,op::Broadcasting{<:Operation},x::Field...) = OperationField(op.f # Define some well known operations -for op in (:+,:-,:*,:/,:⋅,:⊙,:⊗,:inv,:det,:meas,:pinvJt,:tr,:grad2curl,:symmetric_part,:transpose) +for op in (:+,:-,:*,:/,:⋅,:⊙,:⊗,:inv,:det,:meas,:scaled_meas,:pinvJt,:tr,:grad2curl,:symmetric_part,:transpose) @eval ($op)(a::Field...) = Operation($op)(a...) end diff --git a/src/TensorValues/Operations.jl b/src/TensorValues/Operations.jl index 477a9ead0..d40ffd84e 100644 --- a/src/TensorValues/Operations.jl +++ b/src/TensorValues/Operations.jl @@ -788,6 +788,20 @@ function meas(Jt::MultiValue{Tuple{D1,D2}}) where {D1,D2} sqrt(det(Jt⋅J)) end +""" + scaled_meas(J::MultiValue{Tuple{D1,D2}}) + +Returns a rescaled version of the `D1`-dimensional volume of the parallelepiped +formed by the rows of `J`, that is `sqrt(det(J⋅Jᵀ))^(1/D1)`, or `abs(det(J))^(1/D1)` if `D1`=`D2`. +This is used to compute the scaled contribution of the Jacobian matrix `J` of a changes of variables in integrals, e.g. used in the ScaledContraVariantPiolaMap. +""" +scaled_meas(a::MultiValue{Tuple{D,D}}) where D = abs(det(a))^(1/D) + +function scaled_meas(Jt::MultiValue{Tuple{D1,D2}}) where {D1,D2} + J = transpose(Jt) + sqrt(det(Jt⋅J))^(1/D1) +end + """ norm(u::MultiValue{Tuple{D}}) norm(u::MultiValue{Tuple{D1,D2}}) diff --git a/src/TensorValues/TensorValues.jl b/src/TensorValues/TensorValues.jl index 7ff9efadf..0b3ca22d1 100644 --- a/src/TensorValues/TensorValues.jl +++ b/src/TensorValues/TensorValues.jl @@ -57,7 +57,7 @@ export QTensorValue export SymFourthOrderTensorValue export ThirdOrderTensorValue -export inner, outer, meas +export inner, outer, meas, scaled_meas export mutable export Mutable export symmetric_part From 6ee2923cd819792ea94f88ee2eae5d8cf8de849f Mon Sep 17 00:00:00 2001 From: amboschman Date: Fri, 27 Jun 2025 11:14:34 +1000 Subject: [PATCH 4/9] Redefined ScaledContraVariantMap through scaled_meas + small correction in a evaluate! --- src/ReferenceFEs/RaviartThomasRefFEs.jl | 39 ++++++++++++++++--------- 1 file changed, 26 insertions(+), 13 deletions(-) diff --git a/src/ReferenceFEs/RaviartThomasRefFEs.jl b/src/ReferenceFEs/RaviartThomasRefFEs.jl index 64e1e91ad..3c7963c9a 100644 --- a/src/ReferenceFEs/RaviartThomasRefFEs.jl +++ b/src/ReferenceFEs/RaviartThomasRefFEs.jl @@ -396,7 +396,7 @@ function evaluate!( v, Jt, detJ,sign_flip = a.args # Assuming J comes from an affine map ∇v = Broadcasting(∇)(v) - k = ContraVariantPiolaMap() + k = ScaledContraVariantPiolaMap() Broadcasting(Operation(k))(∇v,Jt,detJ,sign_flip) end @@ -418,21 +418,12 @@ function lazy_map( 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,::ScaledContraVariantPiolaMap, +function evaluate!(cache,::ContraVariantPiolaMapType, v::Number, Jt::Number, detJ::Number, sign_flip::Bool) - D = size(Jt)[1] - ((-1)^sign_flip*v)⋅((1/(detJ^(1/D)))*Jt) + ((-1)^sign_flip*v)⋅((1/(detJ))*Jt) end function evaluate!(cache, @@ -445,8 +436,18 @@ function evaluate!(cache, Broadcasting(Operation(k))(v,Jt,detJ,sign_flip) end +function evaluate!(cache, + k::ScaledContraVariantPiolaMap, + v::AbstractVector{<:Field}, + phi::Field, + sign_flip::AbstractVector{<:Field}) + Jt = ∇(phi) + scaled_detJ = Operation(scaled_meas)(Jt) + Broadcasting(Operation(k))(v,Jt,scaled_detJ,sign_flip) +end + function lazy_map( - k::Union{ContraVariantPiolaMap,ScaledContraVariantPiolaMap}, + k::ContraVariantPiolaMap, cell_ref_shapefuns::AbstractArray{<:AbstractArray{<:Field}}, cell_map::AbstractArray{<:Field}, sign_flip::AbstractArray{<:AbstractArray{<:Field}}) @@ -455,4 +456,16 @@ function lazy_map( cell_detJ = lazy_map(Operation(meas),cell_Jt) lazy_map(Broadcasting(Operation(k)),cell_ref_shapefuns,cell_Jt,cell_detJ,sign_flip) +end + +function lazy_map( + k::ScaledContraVariantPiolaMap, + cell_ref_shapefuns::AbstractArray{<:AbstractArray{<:Field}}, + cell_map::AbstractArray{<:Field}, + sign_flip::AbstractArray{<:AbstractArray{<:Field}}) + + cell_Jt = lazy_map(∇,cell_map) + cell_scaled_detJ = lazy_map(Operation(scaled_meas),cell_Jt) + + lazy_map(Broadcasting(Operation(k)),cell_ref_shapefuns,cell_Jt,cell_scaled_detJ,sign_flip) end \ No newline at end of file From 4aa7c9ee10747f51cb412a7356e236223f426d55 Mon Sep 17 00:00:00 2001 From: amboschman Date: Fri, 27 Jun 2025 15:56:14 +1000 Subject: [PATCH 5/9] minor - to improve consistency --- src/FESpaces/DivConformingFESpaces.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/FESpaces/DivConformingFESpaces.jl b/src/FESpaces/DivConformingFESpaces.jl index 75c0fa6f2..be999e4dc 100644 --- a/src/FESpaces/DivConformingFESpaces.jl +++ b/src/FESpaces/DivConformingFESpaces.jl @@ -52,7 +52,7 @@ function get_cell_shapefuns(model::DiscreteModel, ::DivConformity, args...; sign_flip=get_sign_flip(model, cell_reffe), - contra_variant_piola_map_type=ContraVariantPiolaMap(), + contra_variant_piola_map_type::ContraVariantPiolaMapType=ContraVariantPiolaMap(), kwargs...) cell_reffe_shapefuns=lazy_map(get_shapefuns,cell_reffe) From 1c69653a89e234d58186444cc3dfbf540823c2c5 Mon Sep 17 00:00:00 2001 From: amboschman Date: Fri, 4 Jul 2025 16:48:11 +1000 Subject: [PATCH 6/9] fix for issues with interpolation + DIV --- src/FESpaces/DivConformingFESpaces.jl | 45 +++++++++++++++++++++++---- 1 file changed, 39 insertions(+), 6 deletions(-) diff --git a/src/FESpaces/DivConformingFESpaces.jl b/src/FESpaces/DivConformingFESpaces.jl index be999e4dc..5f22ec5d4 100644 --- a/src/FESpaces/DivConformingFESpaces.jl +++ b/src/FESpaces/DivConformingFESpaces.jl @@ -24,13 +24,14 @@ # 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 ; +struct TransformRTDofBasis{Dc,Dp,k<:ContraVariantPiolaMapType} <: Map end ; function get_cell_dof_basis(model::DiscreteModel, cell_reffe::AbstractArray{<:GenericRefFE{<:DivConforming}}, ::DivConformity, args...; sign_flip=get_sign_flip(model, cell_reffe), + contra_variant_piola_map_type::ContraVariantPiolaMapType=ContraVariantPiolaMap(), kwargs...) cell_map = get_cell_map(Triangulation(model)) phi = cell_map[1] @@ -43,7 +44,7 @@ function get_cell_dof_basis(model::DiscreteModel, et = eltype(return_type(get_prebasis(reffe))) pt = Point{Dc,et} Dp = first(size(return_type(phi,zero(pt)))) - k = TransformRTDofBasis{Dc,Dp}() + k = TransformRTDofBasis{Dc,Dp,typeof(contra_variant_piola_map_type)}() lazy_map(k,cell_reffe,Jtx,sign_flip) end @@ -128,10 +129,10 @@ function get_sign_flip(model::DiscreteModel, IdentityVector(Int32(num_cells(model)))) end -function return_cache(::TransformRTDofBasis{Dc,Dp}, +function return_cache(::TransformRTDofBasis{Dc,Dp,k}, reffe::GenericRefFE{<:DivConforming}, Jtx, - ::AbstractVector{Bool}) where {Dc,Dp} + ::AbstractVector{Bool}) where {Dc,Dp,k} p = get_polytope(reffe) prebasis = get_prebasis(reffe) order = get_order(prebasis) @@ -149,10 +150,10 @@ function return_cache(::TransformRTDofBasis{Dc,Dp}, end function evaluate!(cache, - ::TransformRTDofBasis, + ::TransformRTDofBasis{Dc,Dp,<:ContraVariantPiolaMap}, reffe::GenericRefFE{<:DivConforming}, Jt_q, - sign_flip::AbstractVector{Bool}) + sign_flip::AbstractVector{Bool}) where {Dc,Dp} nodes, nf_nodes, nf_moments, face_moments = cache face_own_dofs=get_face_own_dofs(reffe) for face in 1:length(face_moments) @@ -173,6 +174,30 @@ function evaluate!(cache, MomentBasedDofBasis(nodes,face_moments,nf_nodes) end +function evaluate!(cache, + ::TransformRTDofBasis{Dc,Dp,<:ScaledContraVariantPiolaMap}, + reffe::GenericRefFE{<:DivConforming}, + Jt_q, + sign_flip::AbstractVector{Bool}) where {Dc,Dp} + nodes, nf_nodes, nf_moments, face_moments = cache + 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] + if length(nf_moments_face) > 0 + sign = (-1)^sign_flip[face_own_dofs[face][1]] + num_qpoints, num_moments = size(nf_moments_face) + for i in 1:num_qpoints + Jt_q_i = Jt_q[nf_nodes[face][i]] + change = sign * scaled_meas(Jt_q_i) * pinvJt(Jt_q_i) + for j in 1:num_moments + face_moments_face[i,j] = change ⋅ nf_moments_face[i,j] + end + end + end + end + MomentBasedDofBasis(nodes,face_moments,nf_nodes) +end # Support for DIV operator function DIV(f::LazyArray{<:Fill{T}}) where T @@ -187,6 +212,14 @@ function DIV(f::LazyArray{<:Fill{Broadcasting{Operation{ContraVariantPiolaMap}}} fsign_flip=lazy_map(Broadcasting(Operation(x->(-1)^x)), fsign_flip) lazy_map(Broadcasting(Operation(*)),fsign_flip,div_ϕrgₖ) end +function DIV(f::LazyArray{<:Fill{Broadcasting{Operation{ScaledContraVariantPiolaMap}}}}) + ϕrgₖ = f.args[1] + scaled_detJ = f.args[3] + fsign_flip = f.args[4] + div_ϕrgₖ = lazy_map(Broadcasting(divergence),ϕrgₖ) + fsign_flip=lazy_map(Broadcasting(Operation(x->(-1)^x)), fsign_flip) + lazy_map(Broadcasting(Operation(*)),scaled_detJ,fsign_flip,div_ϕrgₖ) +end function DIV(a::LazyArray{<:Fill{typeof(linear_combination)}}) i_to_basis = DIV(a.args[2]) i_to_values = a.args[1] From 1a3563397fef8f57a8c45cdb18e9d6c05476cf78 Mon Sep 17 00:00:00 2001 From: amboschman Date: Fri, 4 Jul 2025 22:24:14 +1000 Subject: [PATCH 7/9] Note about how to fix DIV when D>2 --- src/FESpaces/DivConformingFESpaces.jl | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/FESpaces/DivConformingFESpaces.jl b/src/FESpaces/DivConformingFESpaces.jl index 5f22ec5d4..0baed755b 100644 --- a/src/FESpaces/DivConformingFESpaces.jl +++ b/src/FESpaces/DivConformingFESpaces.jl @@ -212,12 +212,15 @@ function DIV(f::LazyArray{<:Fill{Broadcasting{Operation{ContraVariantPiolaMap}}} fsign_flip=lazy_map(Broadcasting(Operation(x->(-1)^x)), fsign_flip) lazy_map(Broadcasting(Operation(*)),fsign_flip,div_ϕrgₖ) end +# To fix the issue for D>2 with this operator we need to pass detJ^{(D-1)/D} in the returned lazy_map function DIV(f::LazyArray{<:Fill{Broadcasting{Operation{ScaledContraVariantPiolaMap}}}}) ϕrgₖ = f.args[1] scaled_detJ = f.args[3] fsign_flip = f.args[4] div_ϕrgₖ = lazy_map(Broadcasting(divergence),ϕrgₖ) fsign_flip=lazy_map(Broadcasting(Operation(x->(-1)^x)), fsign_flip) + # scaled_detJ = lazy_map(Broadcasting(Operation(x->x^(D-1))), scaled_detJ) + # Update this so that we can access D! lazy_map(Broadcasting(Operation(*)),scaled_detJ,fsign_flip,div_ϕrgₖ) end function DIV(a::LazyArray{<:Fill{typeof(linear_combination)}}) From 037b91ad307b68e7b2b1d0620788f379f6e93a27 Mon Sep 17 00:00:00 2001 From: amboschman Date: Fri, 4 Jul 2025 23:09:59 +1000 Subject: [PATCH 8/9] added test for ScaledContraVariantPiolaMap - stretched mesh and DIV for 3D do not work --- .../DivConformingFESpacesTests.jl | 226 ++++++++++++++++++ 1 file changed, 226 insertions(+) diff --git a/test/FESpacesTests/DivConformingFESpacesTests.jl b/test/FESpacesTests/DivConformingFESpacesTests.jl index 8bda23e06..42a7ca6b0 100644 --- a/test/FESpacesTests/DivConformingFESpacesTests.jl +++ b/test/FESpacesTests/DivConformingFESpacesTests.jl @@ -8,6 +8,7 @@ using Gridap.CellData using Gridap.TensorValues using Gridap.Fields using Gridap.Io +using FillArrays function test_div_v_q_equiv(U,V,P,Q,Ω) v=get_fe_basis(V) @@ -245,4 +246,229 @@ test_div_v_q_equiv(U,V,P,Q,Ω) end +@testset "Test ScaledContraVariantPiolaMap" begin + + # on square (QUAD) + + 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],contra_variant_piola_map_type=ScaledContraVariantPiolaMap()) + test_single_field_fe_space(V) + U = TrialFESpace(V,u) + + reffe = ReferenceFE(lagrangian,Float64,order) + Q = TestFESpace(model,reffe,conformity=:L2) + P = TrialFESpace(Q) + + uh = interpolate(u,U) + + e = u - uh + + Ω = Triangulation(model) + dΩ = Measure(Ω,2*order) + + el2 = sqrt(sum( ∫( e⋅e )*dΩ )) + @test el2 < 1.0e-10 + + test_div_v_q_equiv(U,V,P,Q,Ω) + + #using Gridap.Visualization + # + #writevtk(Ω,"trian",nsubcells=10,cellfields=["uh"=>uh]) + + # on square (TRI) + + domain =(0,1,0,1) + partition = (3,3) + model = CartesianDiscreteModel(domain,partition) + model = simplexify(model) + order = 1 + + u(x) = x + + reffe = ReferenceFE(raviart_thomas,order) + + V = TestFESpace(model,reffe,dirichlet_tags = [1,6],contra_variant_piola_map_type=ScaledContraVariantPiolaMap()) + test_single_field_fe_space(V) + U = TrialFESpace(V,u) + + reffe = ReferenceFE(lagrangian,Float64,order) + Q = TestFESpace(model,reffe,conformity=:L2) + P = TrialFESpace(Q) + + uh = interpolate(u,U) + + e = u - uh + + Ω = Triangulation(model) + dΩ = Measure(Ω,2*order) + + el2 = sqrt(sum( ∫( e⋅e )*dΩ )) + @test el2 < 1.0e-10 + + test_div_v_q_equiv(U,V,P,Q,Ω) + + #using Gridap.Visualization + # + #writevtk(Ω,"trian",nsubcells=10,cellfields=["uh"=>uh]) + + # on square with stretched elements (QUAD) + + function stretching(x::Point) + m = zeros(length(x)) + m[1] = x[1]^2 + for i in 2:2 + m[i] = x[i] + end + Point(m) + end + + partition = (3,3) + pmin = Point(Fill(0,2)) + pmax = Point(Fill(1,2)) + model = CartesianDiscreteModel(pmin,pmax,partition,map=stretching) + + order = 1 + + u(x) = x + + reffe = ReferenceFE(raviart_thomas,order) + + V = TestFESpace(model,reffe,dirichlet_tags = [1,6],contra_variant_piola_map_type=ScaledContraVariantPiolaMap()) + test_single_field_fe_space(V) + U = TrialFESpace(V,u) + + reffe = ReferenceFE(lagrangian,Float64,order) + Q = TestFESpace(model,reffe,conformity=:L2) + P = TrialFESpace(Q) + + uh = interpolate(u,U) + + e = u - uh + + Ω = Triangulation(model) + dΩ = Measure(Ω,2*order) + + el2 = sqrt(sum( ∫( e⋅e )*dΩ )) + @test el2 < 1.0e-10 + + test_div_v_q_equiv(U,V,P,Q,Ω) + + #using Gridap.Visualization + # + #writevtk(Ω,"trian",nsubcells=10,cellfields=["uh"=>uh]) + + # on cube (TET) + + order = 1 + + reffe = ReferenceFE(TET,raviart_thomas,order) + + domain =(0,1,0,1,0,1) + partition = (3,3,3) + model = simplexify(CartesianDiscreteModel(domain,partition)) + + labels = get_face_labeling(model) + dir_tags = Array{Integer}(undef,0) + + V = FESpace(model,reffe,conformity=DivConformity(),contra_variant_piola_map_type=ScaledContraVariantPiolaMap()) + U = TrialFESpace(V,u) + + reffe = ReferenceFE(lagrangian,Float64,order) + Q = TestFESpace(model,reffe,conformity=:L2) + P = TrialFESpace(Q) + + 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) + dΩ = Measure(Ω,2*order) + + el2 = sqrt(sum( ∫( e⋅e )*dΩ )) + @test el2 < 1.0e-10 + + test_div_v_q_equiv(U,V,P,Q,Ω) + + # on cube (HEX) + + order = 1 + + reffe = ReferenceFE(HEX,raviart_thomas,order) + + domain =(0,1,0,1,0,1) + partition = (3,3,3) + model = CartesianDiscreteModel(domain,partition) + # model = simplexify(CartesianDiscreteModel(domain,partition)) + + labels = get_face_labeling(model) + dir_tags = Array{Integer}(undef,0) + + V = FESpace(model,reffe,conformity=DivConformity(),contra_variant_piola_map_type=ScaledContraVariantPiolaMap()) + U = TrialFESpace(V,u) + + reffe = ReferenceFE(lagrangian,Float64,order) + Q = TestFESpace(model,reffe,conformity=:L2) + P = TrialFESpace(Q) + + 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) + dΩ = Measure(Ω,2*order) + + el2 = sqrt(sum( ∫( e⋅e )*dΩ )) + @test el2 < 1.0e-10 + + test_div_v_q_equiv(U,V,P,Q,Ω) + + #using Gridap.Visualization + # + #writevtk(trian,"test",order=3,cellfields=["vh"=>vh]) + + # Tests on manifold + + # Create domain + domain = (0,1,0,1,0,1) + cells = (2,2,2) + model = CartesianDiscreteModel(domain,cells) + + # Restrict model to cube surface + labels = get_face_labeling(model) + bgface_to_mask = get_face_mask(labels,"boundary",2) + Γface_to_bgface = findall(bgface_to_mask) + Dc2Dp3model = DiscreteModelPortion(DiscreteModel(Polytope{2},model),Γface_to_bgface) + + order = 0 + degree = 1 + + reffe_rt = ReferenceFE(raviart_thomas,Float64,order) + V = FESpace(Dc2Dp3model, reffe_rt ; conformity=:HDiv,contra_variant_piola_map_type=ScaledContraVariantPiolaMap()) + U = TrialFESpace(V,u) + reffe = ReferenceFE(lagrangian,Float64,order) + Q = TestFESpace(Dc2Dp3model,reffe,conformity=:L2) + P = TrialFESpace(Q) + uh = FEFunction(V,rand(num_free_dofs(V))) + vh = interpolate_everywhere(uh,V) + + Ω = Triangulation(Dc2Dp3model) + dΩ = Measure(Ω,2*order) + e=sqrt(sum(∫((uh-vh)⋅(uh-vh))dΩ)) + @test e < 1.0e-12 + + test_div_v_q_equiv(U,V,P,Q,Ω) + +end + end # module From 690d97df59c8dc5a649eeabf93203e167dc69cf9 Mon Sep 17 00:00:00 2001 From: amboschman Date: Wed, 30 Jul 2025 14:21:34 +1000 Subject: [PATCH 9/9] Undoing Gridap#master commit b331c89 (https://github.com/gridap/Gridap.jl/commit/b331c89cdf9593b07653936345dd839a0bf47708) to fix bug in integrating proj term for GridapEmbedded#bulk_penalty_stab_div (commit 3b70225) --- src/Geometry/Triangulations.jl | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/Geometry/Triangulations.jl b/src/Geometry/Triangulations.jl index d3b41c444..1638d5a7c 100644 --- a/src/Geometry/Triangulations.jl +++ b/src/Geometry/Triangulations.jl @@ -284,11 +284,11 @@ function extend(a::LazyArray{<:Fill{<:Broadcasting{<:Operation}}},b::PosNegParti lazy_map(k,args...) end -function extend(a::LazyArray{<:Fill{<:Broadcasting{typeof(∘)}}},b::PosNegPartition) - k = a.maps.value - args = map(i->extend(i,b),a.args) - lazy_map(k,args...) -end +# function extend(a::LazyArray{<:Fill{<:Broadcasting{typeof(∘)}}},b::PosNegPartition) +# k = a.maps.value +# args = map(i->extend(i,b),a.args) +# lazy_map(k,args...) +# end # function extend(a::LazyArray{<:Fill},b::PosNegPartition) # k = a.maps.value