refactor(topology): Implement Segment{N} with type parameter

Update Segment to use node count type parameter per ADR-002.

Changes:
- struct Segment → struct Segment{N} <: AbstractTopology{N}
- Aliases: Seg2 = Segment{2}, Seg3 = Segment{3}
- Add nnodes(), dim() implementations
- reference_coordinates() for Segment{2} and Segment{3}
- Generic edges() and faces() for any N
- Remove 100+ lines of old design documentation

Implements ADR-002 (November 13, 2025): node count from mesh, not basis.

Old files removed: seg2.jl, seg3.jl
New file: Single segments.jl handles all variants via {N}
This commit is contained in:
Jukka Aho
2025-11-15 02:27:02 +02:00
parent 4c49570cec
commit be550320b5
+23 -116
View File
@@ -2,129 +2,36 @@
# License is MIT: see https://github.com/JuliaFEM/JuliaFEM.jl/blob/master/LICENSE
"""
Segment <: AbstractTopology
Segment{N} <: AbstractTopology{N}
Linear segment/line element topology (1D).
1D segment (line) topology with N nodes.
Reference element in 1D parametric space [-1, 1].
# Node numbering (corners only)
```
N1 -------- N2
-1 +1
```
**Important:** This type defines ONLY the geometric shape (1D line segment).
Node count is determined by the basis functions:
- `Lagrange{Segment, 1}` → 2 nodes (linear)
- `Lagrange{Segment, 2}` → 3 nodes (quadratic, adds midpoint)
- `Lagrange{Segment, 3}` → 4 nodes (cubic)
# Topology Properties
- Dimension: 1
- Corner nodes: 2
- Edges: 1 (the element itself)
- Faces: 0 (none in 1D)
# Typical Usage
```julia
julia> topology = Segment()
julia> dim(topology)
1
julia> reference_coordinates(topology) # Corner nodes only
((-1.0,), (1.0,))
julia> basis = Lagrange{Segment, 1}()
julia> nnodes(basis) # Node count from BASIS
2
julia> basis = Lagrange{Segment, 2}()
julia> nnodes(basis) # Quadratic has 3 nodes
3
```
**Zero allocation:** All functions return compile-time sized tuples.
See also: [`AbstractTopology`](@ref), [`Lagrange`](@ref)
# Node Count Variants
- `Segment{2}` (alias `Seg2`): Linear segment (P1)
- `Segment{3}` (alias `Seg3`): Quadratic segment (P2, includes midpoint)
"""
struct Segment <: AbstractTopology end
struct Segment{N} <: AbstractTopology{N} end
dim(::Segment) = 1
const Seg2 = Segment{2}
const Seg3 = Segment{3}
"""
reference_coordinates(::Segment) -> NTuple{2, NTuple{1, Float64}}
nnodes(::Segment{N}) where {N} = N
dim(::Segment{N}) where {N} = 1
Reference coordinates for segment corner nodes: (-1.0,) and (1.0,).
function reference_coordinates(::Segment{2})
return ((-1.0,), (1.0,))
end
**Zero allocation:** Returns tuple of tuples (stack allocated).
"""
reference_coordinates(::Segment) = ((-1.0,), (1.0,))
function reference_coordinates(::Segment{3})
return ((-1.0,), (1.0,), (0.0,))
end
"""
edges(::Segment) -> NTuple{1, Tuple{Int, Int}}
function edges(::Segment{N}) where {N}
return ((1, 2),)
end
Edge connectivity for segment (the segment itself, corner nodes).
function faces(::Segment{N}) where {N}
return ((1,), (2,)) # Endpoints are "faces" in 1D
end
**Zero allocation:** Returns tuple of tuples (stack allocated).
"""
edges(::Segment) = ((1, 2),)
"""
faces(::Segment) -> NTuple{0, Tuple{}}
Faces for 1D element (none in 1D).
**Zero allocation:** Returns empty tuple (stack allocated).
"""
faces(::Segment) = ()
# ============================================================================
# Deprecated aliases (for backwards compatibility)
# ============================================================================
"""
Seg2
**DEPRECATED:** Backward compatibility alias. Use `Segment` with `Lagrange{Segment, 1}`.
The old `Seg2` conflated topology (segment) with node count (2).
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(Seg2, (1, 2))
# Internally converted to:
element = Element(Segment, (1, 2)) # Infers Lagrange{Segment, 1} from 2 nodes
```
New code should use explicit topology + basis:
```julia
element = Element(Lagrange{Segment, 1}, (1, 2))
```
"""
const Seg2 = Segment
"""
Seg3
**DEPRECATED:** Backward compatibility alias. Use `Segment` with `Lagrange{Segment, 2}`.
The old `Seg3` conflated topology (segment) with node count (3).
This alias allows old code to work:
```julia
# Old style (still works)
element = Element(Seg3, (1, 2, 3))
# Internally converted to:
element = Element(Segment, (1, 2, 3)) # Infers Lagrange{Segment, 2} from 3 nodes
```
New code should use explicit topology + basis:
```julia
element = Element(Lagrange{Segment, 2}, (1, 2, 3))
```
"""
const Seg3 = Segment # Yes, same topology! Node count from basis.
export Seg2, Seg3