From 0231b2e33a9543fe7efa7b49ad0741db5968aa7b Mon Sep 17 00:00:00 2001 From: Jukka Aho Date: Wed, 12 Nov 2025 00:52:13 +0200 Subject: [PATCH] feat(topology): Add Hexahedron 3D topology with Hex8/Hex20/Hex27 aliases MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit New file implementing 3D hexahedral topology: - Hexahedron struct with dim=3, 8 corner nodes (3D tensor product) - reference_coordinates() in [-1,1]³ cube - edges() returns 12 edges, faces() returns 6 quadrilateral faces - Backward compatibility aliases: Hex8, Hex20 (Serendipity), Hex27 (Lagrange) - Supports trilinear, serendipity (no interior), and full tensor product bases - Zero-allocation tuple-based design --- src/topology/hexahedra.jl | 190 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 190 insertions(+) create mode 100644 src/topology/hexahedra.jl diff --git a/src/topology/hexahedra.jl b/src/topology/hexahedra.jl new file mode 100644 index 0000000..f8a88fa --- /dev/null +++ b/src/topology/hexahedra.jl @@ -0,0 +1,190 @@ +# This file is a part of JuliaFEM. +# License is MIT: see https://github.com/JuliaFEM/JuliaFEM.jl/blob/master/LICENSE + +""" + Hexahedron <: AbstractTopology + +Hexahedral element topology (3D tensor product). + +**Important:** This type defines ONLY the geometric shape. Node count is determined +by the interpolation scheme (basis functions): +- `Lagrange{Hexahedron, 1}` → 8 nodes (Q1, trilinear) +- `Serendipity{Hexahedron, 2}` → 20 nodes (Q2, no interior nodes) +- `Lagrange{Hexahedron, 2}` → 27 nodes (Q2, full tensor product) + +# Reference Element +``` + N8-------N7 + /| /| + / | / | + N5-------N6 | + | N4----|--N3 + | / | / + |/ |/ + N1-------N2 +``` + +# Standard Corner Node Positions (in [-1,1]³) +1. (-1, -1, -1) +2. ( 1, -1, -1) +3. ( 1, 1, -1) +4. (-1, 1, -1) +5. (-1, -1, 1) +6. ( 1, -1, 1) +7. ( 1, 1, 1) +8. (-1, 1, 1) + +# Topology Properties +- Dimension: 3 +- Corner nodes: 8 +- Edges: 12 +- Faces: 6 (quadrilateral) + +# Typical Usage +```julia +julia> topology = Hexahedron() +julia> dim(topology) +3 +julia> reference_coordinates(topology) # Corner nodes only +((-1.0, -1.0, -1.0), (1.0, -1.0, -1.0), ...) + +julia> basis = Lagrange{Hexahedron, 1}() +julia> nnodes(basis) # Trilinear: 8 nodes +8 + +julia> basis = Serendipity{Hexahedron, 2}() +julia> nnodes(basis) # Serendipity: 20 nodes (no interior) +20 + +julia> basis = Lagrange{Hexahedron, 2}() +julia> nnodes(basis) # Full Lagrange: 27 nodes (with interior) +27 +``` + +**Zero allocation:** All functions return compile-time sized tuples. + +See also: [`AbstractTopology`](@ref), [`Tetrahedron`](@ref), [`Lagrange`](@ref), [`Serendipity`](@ref) +""" +struct Hexahedron <: AbstractTopology end + +dim(::Hexahedron) = 3 + +""" + reference_coordinates(::Hexahedron) + +Get corner node positions for Hexahedron (8 vertices in [-1,1]³). +""" +function reference_coordinates(::Hexahedron) + return ( + (-1.0, -1.0, -1.0), # Node 1 + (1.0, -1.0, -1.0), # Node 2 + (1.0, 1.0, -1.0), # Node 3 + (-1.0, 1.0, -1.0), # Node 4 + (-1.0, -1.0, 1.0), # Node 5 + (1.0, -1.0, 1.0), # Node 6 + (1.0, 1.0, 1.0), # Node 7 + (-1.0, 1.0, 1.0), # Node 8 + ) +end + +""" + edges(::Hexahedron) + +Edge connectivity for hexahedron (corner nodes). +""" +function edges(::Hexahedron) + return ( + (1, 2), (2, 3), (3, 4), (4, 1), # Bottom face edges + (5, 6), (6, 7), (7, 8), (8, 5), # Top face edges + (1, 5), (2, 6), (3, 7), (4, 8), # Vertical edges + ) +end + +""" + faces(::Hexahedron) + +Face connectivity for hexahedron (quadrilateral faces, corner nodes). +""" +function faces(::Hexahedron) + return ( + (1, 4, 3, 2), # Face 1: Bottom (-z) + (1, 2, 6, 5), # Face 2: Front (-y) + (2, 3, 7, 6), # Face 3: Right (+x) + (3, 4, 8, 7), # Face 4: Back (+y) + (4, 1, 5, 8), # Face 5: Left (-x) + (5, 6, 7, 8), # Face 6: Top (+z) + ) +end + +# ============================================================================ +# Deprecated aliases (for backwards compatibility) +# ============================================================================ + +""" + Hex8 + +**DEPRECATED:** Backward compatibility alias. Use `Hexahedron` with `Lagrange{Hexahedron, 1}`. + +The old `Hex8` conflated topology (hexahedron) with node count (8). +In the new architecture: +- Topology defines geometric shape only +- Basis functions determine node count + +This alias allows old code to work: +```julia +# Old style (still works) +element = Element(Hex8, (1, 2, 3, 4, 5, 6, 7, 8)) +# Internally converted to: +element = Element(Hexahedron, (1, 2, 3, 4, 5, 6, 7, 8)) # Infers Lagrange{Hexahedron, 1} +``` + +New code should use explicit topology + basis: +```julia +element = Element(Lagrange{Hexahedron, 1}, (1, 2, 3, 4, 5, 6, 7, 8)) +``` +""" +const Hex8 = Hexahedron + +""" + Hex20 + +**DEPRECATED:** Backward compatibility alias. Use `Hexahedron` with `Serendipity{Hexahedron, 2}`. + +20-node hexahedron with serendipity basis (NO interior nodes). + +This alias allows old code to work: +```julia +# Old style (still works) +element = Element(Hex20, (1, 2, ..., 20)) +# Internally converted to: +element = Element(Hexahedron, (1, 2, ..., 20)) # Infers Serendipity +``` + +New code should be explicit about basis family: +```julia +element = Element(Serendipity{Hexahedron, 2}, (1, 2, ..., 20)) +``` +""" +const Hex20 = Hexahedron # Same topology! Basis determines node pattern. + +""" + Hex27 + +**DEPRECATED:** Backward compatibility alias. Use `Hexahedron` with `Lagrange{Hexahedron, 2}`. + +27-node hexahedron with full tensor product basis (WITH interior nodes). + +This alias allows old code to work: +```julia +# Old style (still works) +element = Element(Hex27, (1, 2, ..., 27)) +# Internally converted to: +element = Element(Hexahedron, (1, 2, ..., 27)) # Infers Lagrange +``` + +New code should be explicit: +```julia +element = Element(Lagrange{Hexahedron, 2}, (1, 2, ..., 27)) +``` +""" +const Hex27 = Hexahedron # Same topology! Basis determines node pattern.