refactor(topology): consolidate files and simplify documentation

Consolidate topology module by merging topology.jl into api.jl and
significantly simplify documentation across all topology files. Remove
verbose docstrings, examples, and design notes in favor of minimal,
code-focused documentation.

Major changes:
- Merge topology.jl into api.jl (delete topology.jl)
- Simplify docstrings in api.jl, triangles.jl, and tetrahedra.jl
- Remove verbose documentation from type aliases and functions
- Remove topology type parameter from entity types (Vertex, Edge, Face, Cell)
- Update all entity constructors to remove topology parameter
- Add entities dispatcher and helper functions to api.jl

File changes:
- api.jl: Simplified AbstractTopology docstring, added topological entities
  and helpers from topology.jl, added entities dispatcher
- triangles.jl: Removed verbose documentation
- tetrahedra.jl: Removed verbose documentation
- hexahedra.jl, pyramids.jl, quadrilaterals.jl, segments.jl, wedges.jl:
  Updated entity constructors to remove topology type parameter
- topology.jl: Deleted (content moved to api.jl)

Documentation philosophy:
- Keep docstrings minimal and code-focused
- Remove examples unless function use is unclear
- Move comprehensive documentation to website/docs, not in code
- Maintain essential type information and interface requirements
This commit is contained in:
Jukka Aho
2025-12-12 22:27:09 +02:00
parent b1292a0ee6
commit b9fafd4e09
9 changed files with 215 additions and 1189 deletions
+176 -288
View File
@@ -4,10 +4,10 @@
"""
Topology API definitions.
This file defines element topology abstractions - the geometric shape and node ordering
Defines element topology abstractions - geometric shape and node ordering
of finite elements in their reference configuration.
Must be included after core api.jl.
See `src/topology/README.md` for complete documentation.
"""
# ============================================================================
@@ -15,132 +15,32 @@ Must be included after core api.jl.
# ============================================================================
"""
AbstractTopology
AbstractTopology{N}
Abstract type for element topology (geometric shape and node ordering).
Topology defines the **shape** of an element in its reference (parametric) space:
- Reference element coordinates (ξ, η, ζ positions for its nodes)
- Edge connectivity (which nodes form edges)
- Face connectivity (which nodes form faces, 3D only)
- Spatial dimension (1D, 2D, or 3D)
Topology defines the **shape** of an element in reference space: coordinates,
edge/face connectivity, and spatial dimension.
# Type Parameter
- `N::Int`: Number of nodes (from mesh connectivity)
# Interface Requirements
All topology types must implement:
- `nnodes(topology)` - Number of nodes
- `dim(topology)` - Spatial dimension (1, 2, or 3)
- `reference_coordinates(topology)` - Node positions in reference element (returns `SVector` of `Vec`)
- `edges(topology)` - Edge connectivity (returns tuple of tuples)
- `faces(topology)` - Face connectivity (returns tuple of tuples, 3D only)
# Concrete Types
**1D (Lines):**
- `Segment` - Generic 1D line segment
**2D (Surfaces):**
- `Triangle` - 2D simplex (straight or curved edges)
- `Quadrilateral` - 2D quadrilateral (straight or curved edges)
**3D (Volumes):**
- `Tetrahedron` - 3D simplex (straight or curved faces)
- `Hexahedron` - 3D brick (straight or curved faces)
- `Pyramid` - 3D pyramid (quad base, triangular sides)
- `Wedge` - 3D prism (triangular extrusion)
# Design Philosophy
**Key Insight:** Topology defines SHAPE, not interpolation.
Node count is part of the topology type parameter and comes from mesh connectivity,
while interpolation comes from the basis. Keep them separate so any topology can pair
with any basis family/order that makes sense.
**Examples:**
```julia
# Triangle with different basis orders
Triangle + Lagrange{1} → 3 nodes (corners)
Triangle + Lagrange{2} → 6 nodes (corners + mid-edges)
Triangle + Lagrange{3} → 10 nodes (corners + edges + interior)
# Quadrilateral with different basis families
Quadrilateral + Lagrange{1} → 4 nodes (corners)
Quadrilateral + Serendipity{2} → 8 nodes (corners + mid-edges, no center)
Quadrilateral + Lagrange{2} → 9 nodes (corners + mid-edges + center)
```
**Separation of Concerns:**
- Topology: "This is a triangle" (shape + node ownership in the mesh)
- Basis: "These are the interpolation functions over that topology"
- Integration: "Use 3-point Gauss rule" (numerical quadrature)
# Backward Compatibility
Old names like `Tri3`, `Quad4`, `Tet10` are **deprecated** but aliased:
- `Tri3` → `Triangle{3}`
- `Quad4` → `Quadrilateral{4}`
- `Tet10` → `Tetrahedron{10}`
New code should use shape names (`Triangle`, `Quadrilateral`, etc.) with explicit basis
specification passed separately.
# Reference Element Coordinates
Each topology has standard reference coordinates:
**Segment:** ξ ∈ [-1, 1]
**Triangle:** (ξ, η) where ξ, η ≥ 0 and ξ + η ≤ 1
**Quadrilateral:** (ξ, η) ∈ [-1, 1] × [-1, 1]
**Tetrahedron:** (ξ, η, ζ) where ξ, η, ζ ≥ 0 and ξ + η + ζ ≤ 1
**Hexahedron:** (ξ, η, ζ) ∈ [-1, 1]³
**Pyramid:** (ξ, η, ζ) where (ξ, η) ∈ [-1, 1]² and ζ ∈ [0, 1], with ξ²+η² ≤ (1-ζ)²
**Wedge:** (ξ, η, ζ) where (ξ, η) triangle and ζ ∈ [-1, 1]
# Usage
```julia
# Query topology properties
topology = Triangle()
dim(topology) # 2
reference_coordinates(topology) # ((0,0), (1,0), (0,1))
edges(topology) # ((1,2), (2,3), (3,1))
# Topology is independent of basis order
element_linear = Element(Triangle, Lagrange{Triangle,1}, (1,2,3)) # 3 nodes
element_quad = Element(Triangle, Lagrange{Triangle,2}, (1,2,3,4,5,6)) # 6 nodes
# Both elements have the same topology (Triangle), different basis orders
```
# See Also
- [`dim`](@ref) - Spatial dimension
- [`nnodes`](@ref) - Number of nodes (depends on basis, not topology!)
- [`reference_coordinates`](@ref) - Reference element node positions (SVector of Vec)
- [`edges`](@ref) - Edge connectivity
- [`faces`](@ref) - Face connectivity (3D only)
- Architecture docs: `docs/book/element_architecture.md`
# Type Parameter
`AbstractTopology{N}` where `N` is the number of nodes. Node count comes from mesh connectivity.
- `reference_coordinates(topology)` - Node positions (SVector of Vec)
- `edges(topology)` - Edge connectivity (tuple of tuples)
- `faces(topology)` - Face connectivity (tuple of tuples, 3D only)
# Examples
```julia
Triangle{3} <: AbstractTopology{3} # 3-node triangle (linear)
Triangle{6} <: AbstractTopology{6} # 6-node triangle (quadratic)
Hexahedron{8} <: AbstractTopology{8} # 8-node hex (linear)
Hexahedron{20} <: AbstractTopology{20} # 20-node hex (quadratic serendipity)
Hexahedron{27} <: AbstractTopology{27} # 27-node hex (quadratic full)
Triangle{3} <: AbstractTopology{3} # 3-node triangle
Triangle{6} <: AbstractTopology{6} # 6-node triangle
Hexahedron{8} <: AbstractTopology{8} # 8-node hex
```
# Rationale
Node count is included in the type parameter for compile-time performance optimization:
- Enables `Val(N)` for zero-allocation ntuple operations
- Allows loop unrolling for small N
- Node count comes from mesh connectivity, not basis choice
- See ADR-002 for detailed design rationale
See `src/topology/README.md` for comprehensive documentation.
"""
abstract type AbstractTopology{N} end
@@ -149,213 +49,201 @@ abstract type AbstractTopology{N} end
# ============================================================================
"""
nnodes(topology::AbstractTopology{N}) -> Int
nnodes(topology) -> Int
Number of nodes in the reference element.
This is a compile-time constant derived from the type parameter `N`.
# Examples
```julia
nnodes(Triangle{3}()) # 3
nnodes(Triangle{6}()) # 6
nnodes(Quadrilateral{4}()) # 4
nnodes(Quadrilateral{9}()) # 9
nnodes(Hexahedron{8}()) # 8
nnodes(Hexahedron{27}()) # 27
```
# Implementation
The default implementation extracts `N` from the type parameter:
```julia
nnodes(::AbstractTopology{N}) where N = N
```
Concrete types inherit this implementation automatically.
Number of nodes in the reference element (compile-time constant from type parameter N).
"""
nnodes(::AbstractTopology{N}) where N = N
"""
nnodes(::Type{<:AbstractTopology{N}}) -> Int
Number of nodes for a topology type (compile-time constant from type parameter).
# Examples
```julia
nnodes(Triangle{3}) # 3
nnodes(Triangle{6}) # 6
nnodes(Quadrilateral{4}) # 4
nnodes(Quadrilateral{9}) # 9
```
"""
nnodes(::Type{<:AbstractTopology{N}}) where N = N
"""
dim(topology::AbstractTopology) -> Int
nedges(topology) -> Int
Number of edges in the topology.
"""
nedges(t::AbstractTopology) = length(edges(t))
nedges(::Type{T}) where {T<:AbstractTopology} = length(edges(T()))
"""
nfaces(topology) -> Int
Number of faces in the topology (3D only).
"""
nfaces(t::AbstractTopology) = length(faces(t))
nfaces(::Type{T}) where {T<:AbstractTopology} = length(faces(T()))
"""
dim(topology) -> Int
Spatial dimension of the topology (1, 2, or 3).
# Examples
```julia
dim(Segment()) # 1
dim(Triangle()) # 2
dim(Quadrilateral()) # 2
dim(Tetrahedron()) # 3
dim(Hexahedron()) # 3
dim(Pyramid()) # 3
dim(Wedge()) # 3
```
# Implementation
Each concrete topology type must provide:
```julia
dim(::Segment) = 1
dim(::Triangle) = 2
dim(::Tetrahedron) = 3
# etc.
```
Each concrete topology must implement this.
"""
function dim end
"""
Base.ndims(topology::AbstractTopology) -> Int
Base.ndims(::Type{<:AbstractTopology}) -> Int
Base.ndims(topology) -> Int
Alias to `dim` for interoperability with Base API.
Alias to `dim` for Base API compatibility.
"""
Base.ndims(t::AbstractTopology) = dim(t)
Base.ndims(::Type{T}) where {T<:AbstractTopology} = dim(T())
"""
reference_coordinates(topology::AbstractTopology) -> SVector{N, Vec{D,Float64}}
reference_coordinates(topology) -> SVector{N, Vec{D,Float64}}
Reference element coordinates for the topology's nodes.
Returns an `SVector` of `Vec{D}` coordinate vectors, one per node.
The actual number of nodes depends on the basis order (not shown here).
Returns `SVector` of `Vec` coordinate vectors (zero allocation).
# Examples
```julia
# Triangle (linear: 3 nodes, quadratic: 6 nodes, etc.)
reference_coordinates(Triangle{3}())
# For linear basis (3 nodes):
# SVector(Vec{2,Float64}((0.0, 0.0)), Vec{2,Float64}((1.0, 0.0)), Vec{2,Float64}((0.0, 1.0)))
# Quadrilateral (4 corner nodes minimum)
reference_coordinates(Quadrilateral{4}())
# SVector(Vec{2,Float64}((-1.0, -1.0)), Vec{2,Float64}((1.0, -1.0)),
# Vec{2,Float64}((1.0, 1.0)), Vec{2,Float64}((-1.0, 1.0)))
```
# Note
This returns coordinates for **corner nodes** by default.
Mid-edge and interior nodes are computed by the basis function module.
# Implementation
Each concrete topology type must provide:
```julia
reference_coordinates(::Triangle) =
SVector(Vec{2,Float64}((0.0, 0.0)), Vec{2,Float64}((1.0, 0.0)), Vec{2,Float64}((0.0, 1.0)))
reference_coordinates(::Quadrilateral) =
SVector(Vec{2,Float64}((-1.0, -1.0)), Vec{2,Float64}((1.0, -1.0)),
Vec{2,Float64}((1.0, 1.0)), Vec{2,Float64}((-1.0, 1.0)))
# etc.
```
Each concrete topology must implement this.
"""
function reference_coordinates end
"""
edges(topology::AbstractTopology) -> NTuple{N, NTuple{2, Int}}
edges(topology) -> NTuple{M, NTuple{2, Int}}
Edge connectivity for the topology.
Edge connectivity (tuple of node index pairs).
Returns a tuple of 2-tuples, each containing node indices that form an edge.
# Examples
```julia
# Triangle has 3 edges
edges(Triangle()) # ((1,2), (2,3), (3,1))
# Quadrilateral has 4 edges
edges(Quadrilateral()) # ((1,2), (2,3), (3,4), (4,1))
# Tetrahedron has 6 edges
edges(Tetrahedron()) # ((1,2), (2,3), (3,1), (1,4), (2,4), (3,4))
```
# Usage
Edge connectivity is used for:
- Surface extraction
- Boundary condition application
- Contact surface identification
- Mesh refinement (edge splitting)
# Implementation
Each concrete topology type must provide:
```julia
edges(::Triangle) = ((1,2), (2,3), (3,1))
edges(::Quadrilateral) = ((1,2), (2,3), (3,4), (4,1))
# etc.
```
Each concrete topology must implement this.
"""
function edges end
"""
faces(topology::AbstractTopology) -> NTuple{N, NTuple{M, Int}}
faces(topology) -> NTuple{M, NTuple{K, Int}}
Face connectivity for 3D topologies.
Face connectivity for 3D topologies (tuple of node index tuples).
Returns a tuple of tuples, each containing node indices that form a face.
Only applicable to 3D topologies (Tetrahedron, Hexahedron, Pyramid, Wedge).
# Examples
```julia
# Tetrahedron has 4 triangular faces
faces(Tetrahedron())
# ((1,3,2), (1,2,4), (1,4,3), (2,3,4))
# Hexahedron has 6 quadrilateral faces
faces(Hexahedron())
# ((1,4,3,2), (1,2,6,5), (2,3,7,6), (3,4,8,7), (4,1,5,8), (5,6,7,8))
# Pyramid has 1 quad base + 4 triangular sides
faces(Pyramid())
# ((1,4,3,2), (1,2,5), (2,3,5), (3,4,5), (4,1,5))
```
# Usage
Face connectivity is used for:
- Surface element creation
- Traction boundary conditions
- Contact surface identification
- Visualization
- Mesh refinement (face splitting)
# Note
2D topologies do not have faces (they ARE faces).
Calling `faces()` on 2D topology should error or return empty tuple.
# Implementation
Each concrete 3D topology type must provide:
```julia
faces(::Tetrahedron) = ((1,3,2), (1,2,4), (1,4,3), (2,3,4))
faces(::Hexahedron) = ((1,4,3,2), (1,2,6,5), (2,3,7,6), (3,4,8,7), (4,1,5,8), (5,6,7,8))
# etc.
```
Each concrete 3D topology must implement this.
"""
function faces end
"""
cells(topology) -> SVector{M, Cell}
Cell entities for the topology (typically one cell per element).
Each concrete topology must implement this.
"""
function cells end
"""
vertices(topology) -> SVector{M, Vertex}
Vertex entities for the topology.
Each concrete topology must implement this.
"""
function vertices end
# ============================================================================
# ENTITIES DISPATCHER
# ============================================================================
"""
entities(::Type{Topo}, ::Val{D}) where {Topo<:AbstractTopology, D}
Return entities of dimension D for the given topology.
Dispatches to dimension-specific functions:
- D=0 → vertices(topology)
- D=1 → edges(topology)
- D=2 → faces(topology)
- D=3 → cells(topology)
"""
entities(::Type{Topo}, ::Val{0}) where {Topo<:AbstractTopology} = vertices(Topo())
entities(::Type{Topo}, ::Val{1}) where {Topo<:AbstractTopology} = edges(Topo())
entities(::Type{Topo}, ::Val{2}) where {Topo<:AbstractTopology} = faces(Topo())
entities(::Type{Topo}, ::Val{3}) where {Topo<:AbstractTopology} = cells(Topo())
# Integer dimension interface
entities(topo::Type{<:AbstractTopology}, d::Int) = entities(topo, Val(d))
# ============================================================================
# TOPOLOGICAL ENTITIES - Typed structures for geometric primitives
# ============================================================================
"""
TopologicalEntity{D}
Abstract type for topological entities at dimension `D`.
# Type Parameters
- `D::Int`: Geometric dimension (0=vertex, 1=edge, 2=face, 3=cell)
# Concrete Types
- `Vertex`: 0-dimensional point entity
- `Edge`: 1-dimensional line entity (bounded by 2 vertices)
- `Face`: 2-dimensional surface entity (bounded by edges)
- `Cell`: 3-dimensional volume entity (bounded by faces)
"""
abstract type TopologicalEntity{D} end
"""
Vertex <: TopologicalEntity{0}
A 0-dimensional point entity (vertex/node).
"""
struct Vertex <: TopologicalEntity{0} end
"""
Edge <: TopologicalEntity{1}
A 1-dimensional line entity bounded by two vertices.
# Fields
- `vertices::NTuple{2, Int}`: Local vertex indices bounding this edge
"""
struct Edge <: TopologicalEntity{1}
vertices::NTuple{2, Int}
end
"""
Face <: TopologicalEntity{2}
A 2-dimensional surface entity bounded by edges.
# Fields
- `vertices::NTuple{N, Int}`: Local vertex indices bounding this face
"""
struct Face <: TopologicalEntity{2}
vertices::NTuple{N, Int} where N
end
"""
Cell <: TopologicalEntity{3}
A 3-dimensional volume entity (the element interior itself).
"""
struct Cell <: TopologicalEntity{3} end
# ============================================================================
# ENTITY DIMENSION QUERIES
# ============================================================================
"""
dim(::Type{<:TopologicalEntity{D}}) where D -> Int
Return the geometric dimension of an entity type.
"""
dim(::Type{<:TopologicalEntity{D}}) where {D} = D
# ============================================================================
# HELPER FUNCTIONS FOR ENTITY COUNTS
# ============================================================================
"""
nentities(::Type{Topo}, ::Type{<:TopologicalEntity{D}}) where {Topo<:AbstractTopology, D}
Return the number of entities of dimension D for the given topology.
"""
function nentities(::Type{Topo}, ::Type{E}) where {Topo<:AbstractTopology, E<:TopologicalEntity}
D = entity_dim(E)
return length(entities(Topo, D))
end
# Helper to extract dimension from entity type
entity_dim(::Type{<:Vertex}) = 0
entity_dim(::Type{<:Edge}) = 1
entity_dim(::Type{<:Face}) = 2
entity_dim(::Type{<:Cell}) = 3