mirror of
https://github.com/JuliaFEM/JuliaFEM.jl.git
synced 2026-09-20 18:18:31 +00:00
feat(interface): add InterfaceDOFHandler and element factory
Mirror DOFHandler discipline on interface meshes with Vertex/Cell-only layouts and inverse connectivity placeholders compatible with volume tooling. - Build field starts per interface entity id without facet maps yet. - Hook `create_interface_elements!` for template-specific dof tuples.
This commit is contained in:
@@ -0,0 +1,145 @@
|
||||
# This file is a part of JuliaFEM.
|
||||
# License is MIT: see https://github.com/JuliaFEM/JuliaFEM.jl/blob/master/LICENSE.md
|
||||
|
||||
"""
|
||||
InterfaceDOFHandler{M, S, NF}
|
||||
|
||||
DOF numbering on an [`AbstractInterfaceMesh`](@ref), independent of volume
|
||||
[`DOFHandler`](@ref). Same block ordering: field 1 entities, then field 2, …
|
||||
|
||||
Fields:
|
||||
|
||||
- `mesh::M` — [`InterfaceMesh`](@ref)
|
||||
- `field_starts::NTuple{NF, Vector{Int}}` — first global DOF per entity
|
||||
- `total_dofs::Int`
|
||||
- `dof_connectivity::DOFConnectivity` — filled by
|
||||
[`create_interface_elements!`](@ref), same role as on [`DOFHandler`](@ref).
|
||||
Carries an empty placeholder until then.
|
||||
|
||||
Only `Vertex` and `Cell` entity kinds are supported on interface meshes until
|
||||
interface facet maps exist.
|
||||
"""
|
||||
mutable struct InterfaceDOFHandler{M<:AbstractInterfaceMesh, S<:DOFSet, NF}
|
||||
mesh::M
|
||||
field_starts::NTuple{NF, Vector{Int}}
|
||||
total_dofs::Int
|
||||
dof_connectivity::DOFConnectivity
|
||||
end
|
||||
|
||||
"""
|
||||
InterfaceDOFHandler(mesh::InterfaceMesh, ::Type{S}) → InterfaceDOFHandler
|
||||
|
||||
Build global DOF indices for interface unknowns (Lagrange multipliers, interface
|
||||
temperatures, …).
|
||||
"""
|
||||
function InterfaceDOFHandler(mesh::InterfaceMesh, ::Type{S}) where {S<:DOFSet}
|
||||
_dofset_uses_edge_or_face(S) &&
|
||||
error("InterfaceDOFHandler: Edge/Face fields are not supported on InterfaceMesh yet.")
|
||||
field_names = fieldnames(S)
|
||||
NF = length(field_names)
|
||||
starts_vec = Vector{Vector{Int}}(undef, NF)
|
||||
next_dof = 1
|
||||
for (fi, fname) in enumerate(field_names)
|
||||
_, EntityType, dpe = _field_quantity_and_entity(S, fname)
|
||||
nent = _field_entity_count(mesh, EntityType, nothing)
|
||||
v = Vector{Int}(undef, nent)
|
||||
@inbounds for e in 1:nent
|
||||
v[e] = next_dof
|
||||
next_dof += dpe
|
||||
end
|
||||
starts_vec[fi] = v
|
||||
end
|
||||
total = next_dof - 1
|
||||
field_starts = ntuple(i -> starts_vec[i], NF)
|
||||
return InterfaceDOFHandler{typeof(mesh), S, NF}(mesh, field_starts, total, DOFConnectivity())
|
||||
end
|
||||
|
||||
@generated function _make_interface_element_dofs(
|
||||
handler::InterfaceDOFHandler{M, S, NF},
|
||||
::Type{Element{K, P, S, NDOF}},
|
||||
elem_id::Integer,
|
||||
connectivity::NTuple{Nnodes, T},
|
||||
) where {M, S, NF, K, P, NDOF, Nnodes, T<:Integer}
|
||||
field_names = fieldnames(S)
|
||||
expressions = Expr[]
|
||||
total_emitted = 0
|
||||
|
||||
for (fidx, fname) in enumerate(field_names)
|
||||
FT = fieldtype(S, fname)
|
||||
if !(FT <: DOF)
|
||||
return :(error("InterfaceDOFHandler: field :$($fname) has type $($FT), expected DOF{Q,E}"))
|
||||
end
|
||||
Q = quantity_type(FT)
|
||||
E = FT.parameters[2]
|
||||
dpe = dof_size(Q)
|
||||
|
||||
if E === Vertex
|
||||
for k in 1:Nnodes, c in 1:dpe
|
||||
push!(
|
||||
expressions,
|
||||
:(UInt64(@inbounds(handler.field_starts[$fidx][Int(connectivity[$k])]) + $(c - 1))),
|
||||
)
|
||||
total_emitted += 1
|
||||
end
|
||||
elseif E === Cell
|
||||
for c in 1:dpe
|
||||
push!(
|
||||
expressions,
|
||||
:(UInt64(@inbounds(handler.field_starts[$fidx][Int(elem_id)]) + $(c - 1))),
|
||||
)
|
||||
total_emitted += 1
|
||||
end
|
||||
else
|
||||
return :(error("InterfaceDOFHandler: entity type $($E) is not supported."))
|
||||
end
|
||||
end
|
||||
|
||||
if total_emitted != NDOF
|
||||
return :(error(
|
||||
"InterfaceDOFHandler: Element{$($K),$($P),$($S),$($NDOF)} expected $($NDOF) DOFs, got $($total_emitted).",
|
||||
))
|
||||
end
|
||||
|
||||
return Expr(:tuple, expressions...)
|
||||
end
|
||||
|
||||
"""
|
||||
create_interface_elements!(mesh::InterfaceMesh, ElementType) → (elements, handler)
|
||||
|
||||
Like [`create_elements!`](@ref) for volumes: build [`Element`](@ref) templates on the
|
||||
interface mesh and attach [`InterfaceDOFHandler`](@ref) + [`DOFConnectivity`](@ref).
|
||||
"""
|
||||
function create_interface_elements!(
|
||||
mesh::InterfaceMesh{Nm, MeshTopo},
|
||||
::Type{Element{K, P, S, NDOF}},
|
||||
) where {Nm, MeshTopo, K, P, S<:DOFSet, NDOF}
|
||||
K !== MeshTopo && @warn "Interface element topology $K does not match InterfaceMesh topology $MeshTopo."
|
||||
handler = InterfaceDOFHandler(mesh, S)
|
||||
ET = Element{K, P, S, NDOF}
|
||||
elements = Vector{ET}(undef, length(mesh.connectivity))
|
||||
@inbounds for (eid, conn) in enumerate(mesh.connectivity)
|
||||
dofs = _make_interface_element_dofs(handler, ET, eid, conn)
|
||||
elements[eid] = ET(UInt(eid), dofs)
|
||||
end
|
||||
handler.dof_connectivity = build_dof_connectivity(elements, handler.total_dofs)
|
||||
return elements, handler
|
||||
end
|
||||
|
||||
function create_interface_elements!(
|
||||
mesh::InterfaceMesh,
|
||||
::Type{Element{K, P, S}},
|
||||
) where {K, P, S<:DOFSet}
|
||||
NDOF = ndofs(K, S)
|
||||
return create_interface_elements!(mesh, Element{K, P, S, NDOF})
|
||||
end
|
||||
|
||||
function create_interface_elements!(
|
||||
mesh::InterfaceMesh,
|
||||
::Type{Element{K, P, S}},
|
||||
) where {K, P, S<:DOF}
|
||||
Swrapped = NamedTuple{(:dof,), Tuple{S}}
|
||||
return create_interface_elements!(mesh, Element{K, P, Swrapped})
|
||||
end
|
||||
|
||||
build_dof_connectivity(elements::Vector, handler::InterfaceDOFHandler) =
|
||||
build_dof_connectivity(elements, handler.total_dofs)
|
||||
Reference in New Issue
Block a user