mirror of
https://github.com/JuliaFEM/JuliaFEM.jl.git
synced 2026-09-19 01:48:47 +00:00
refactor(topology): Implement Hexahedron{N} with type parameter
Update Hexahedron to use node count type parameter per ADR-002.
Changes:
- struct Hexahedron → struct Hexahedron{N} <: AbstractTopology{N}
- Aliases now specify node count: Hex8 = Hexahedron{8}
- Add nnodes() implementation: returns N from type parameter
- Simplify documentation: remove 150+ lines explaining old design
- Keep reference_coordinates() for Hexahedron{8} only
- Generic edges() and faces() work for any N
Benefits:
- Type system encodes node count (compile-time)
- Hex8, Hex20, Hex27 are distinct types (better dispatch)
- Matches mesh file reality (mesh specifies node count)
- Implements ADR-002 decision (November 13, 2025)
Old files removed: hex8.jl, hex20.jl, hex27.jl (separate files)
New file: Single hexahedra.jl handles all variants via {N}
This commit is contained in:
+26
-168
@@ -2,189 +2,47 @@
|
||||
# License is MIT: see https://github.com/JuliaFEM/JuliaFEM.jl/blob/master/LICENSE
|
||||
|
||||
"""
|
||||
Hexahedron <: AbstractTopology
|
||||
Hexahedron{N} <: AbstractTopology{N}
|
||||
|
||||
Hexahedral element topology (3D tensor product).
|
||||
Hexahedral (brick) topology with N nodes.
|
||||
|
||||
**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)
|
||||
# Node Count Variants
|
||||
- `Hexahedron{8}` (alias `Hex8`): Linear hexahedron (P1 Lagrange)
|
||||
- `Hexahedron{20}` (alias `Hex20`): Quadratic serendipity hexahedron (P2, no center nodes)
|
||||
- `Hexahedron{27}` (alias `Hex27`): Quadratic full hexahedron (P2, includes center nodes)
|
||||
"""
|
||||
struct Hexahedron <: AbstractTopology end
|
||||
struct Hexahedron{N} <: AbstractTopology{N} end
|
||||
|
||||
dim(::Hexahedron) = 3
|
||||
const Hex8 = Hexahedron{8}
|
||||
const Hex20 = Hexahedron{20}
|
||||
const Hex27 = Hexahedron{27}
|
||||
|
||||
"""
|
||||
reference_coordinates(::Hexahedron)
|
||||
nnodes(::Hexahedron{N}) where {N} = N
|
||||
dim(::Hexahedron{N}) where {N} = 3
|
||||
|
||||
Get corner node positions for Hexahedron (8 vertices in [-1,1]³).
|
||||
"""
|
||||
function reference_coordinates(::Hexahedron)
|
||||
function reference_coordinates(::Hexahedron{8})
|
||||
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
|
||||
(-1.0, -1.0, -1.0), (1.0, -1.0, -1.0),
|
||||
(1.0, 1.0, -1.0), (-1.0, 1.0, -1.0),
|
||||
(-1.0, -1.0, 1.0), (1.0, -1.0, 1.0),
|
||||
(1.0, 1.0, 1.0), (-1.0, 1.0, 1.0)
|
||||
)
|
||||
end
|
||||
|
||||
"""
|
||||
edges(::Hexahedron)
|
||||
|
||||
Edge connectivity for hexahedron (corner nodes).
|
||||
"""
|
||||
function edges(::Hexahedron)
|
||||
function edges(::Hexahedron{N}) where {N}
|
||||
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
|
||||
(1, 2), (2, 3), (3, 4), (4, 1),
|
||||
(5, 6), (6, 7), (7, 8), (8, 5),
|
||||
(1, 5), (2, 6), (3, 7), (4, 8)
|
||||
)
|
||||
end
|
||||
|
||||
"""
|
||||
faces(::Hexahedron)
|
||||
|
||||
Face connectivity for hexahedron (quadrilateral faces, corner nodes).
|
||||
"""
|
||||
function faces(::Hexahedron)
|
||||
function faces(::Hexahedron{N}) where {N}
|
||||
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)
|
||||
(1, 4, 3, 2), (5, 6, 7, 8),
|
||||
(1, 2, 6, 5), (2, 3, 7, 6),
|
||||
(3, 4, 8, 7), (4, 1, 5, 8)
|
||||
)
|
||||
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.
|
||||
export Hex8, Hex20, Hex27
|
||||
|
||||
Reference in New Issue
Block a user