mirror of
https://github.com/JuliaFEM/JuliaFEM.jl.git
synced 2026-09-26 20:01:32 +00:00
194 lines
5.1 KiB
Markdown
194 lines
5.1 KiB
Markdown
|
|
---
|
|||
|
|
title: "ADR-004: Zero-Allocation Integration Points API"
|
|||
|
|
date: 2025-11-11
|
|||
|
|
author: "Jukka Aho + AI Assistant"
|
|||
|
|
status: "Accepted"
|
|||
|
|
tags: ["adr", "integration", "performance", "api-design"]
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## Context
|
|||
|
|
|
|||
|
|
Integration points (Gauss quadrature) are accessed millions of times during FEM assembly. The original implementation used runtime dispatch with mutable structs containing Dict fields, causing:
|
|||
|
|
|
|||
|
|
1. **Type instability** - Dict fields unknown at compile time
|
|||
|
|
2. **Allocations** - New struct allocations every query
|
|||
|
|
3. **~50× performance penalty** vs optimal approach
|
|||
|
|
|
|||
|
|
**Problem Statement:** How should integration points be accessed in assembly loops?
|
|||
|
|
|
|||
|
|
## Decision
|
|||
|
|
|
|||
|
|
**Adopt compile-time integration point API matching basis function design (eval_basis!).**
|
|||
|
|
|
|||
|
|
### New API
|
|||
|
|
|
|||
|
|
```julia
|
|||
|
|
@inline function get_gauss_points!(::Type{T}, ::Type{S}) where {T<:AbstractTopology, S<:Gauss}
|
|||
|
|
-> NTuple{N, Tuple{Float64, Vec{D}}}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**Key Properties:**
|
|||
|
|
|
|||
|
|
- **Compile-time generation**: Like `eval_basis!`, returns literal tuples
|
|||
|
|
- **Vec{D} coordinates**: Tensors.jl Vec for efficient FEM math
|
|||
|
|
- **Zero allocation**: Fully inlined, no runtime overhead
|
|||
|
|
- **Type-stable**: All types known at compile time
|
|||
|
|
|
|||
|
|
### Usage Pattern
|
|||
|
|
|
|||
|
|
```julia
|
|||
|
|
# Assembly loop - zero allocations:
|
|||
|
|
for (weight, ξ) in get_gauss_points!(Triangle, Gauss{2})
|
|||
|
|
N = eval_basis!(Lagrange{Triangle,1}, Float64, ξ)
|
|||
|
|
dN = eval_dbasis!(Lagrange{Triangle,1}, ξ)
|
|||
|
|
detJ = compute_jacobian(ξ)
|
|||
|
|
K += weight * detJ * (dN' * D * dN)
|
|||
|
|
end
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**Why Vec{D}?**
|
|||
|
|
|
|||
|
|
- Natural for FEM: `dN/dξ ⋅ v`, tensor products, etc.
|
|||
|
|
- GPU-friendly (immutable, stack-allocated)
|
|||
|
|
- Matches golden standard (nodal assembly demos)
|
|||
|
|
|
|||
|
|
## Alternatives Considered
|
|||
|
|
|
|||
|
|
### Option A: Plain Tuples
|
|||
|
|
|
|||
|
|
```julia
|
|||
|
|
get_gauss_points!(Triangle, Gauss{1})
|
|||
|
|
# → ((0.5, (1/3, 1/3)),)
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**Rejected:** Tuple coordinates less convenient for FEM math.
|
|||
|
|
|
|||
|
|
### Option B: Store in Element
|
|||
|
|
|
|||
|
|
```julia
|
|||
|
|
struct Element{N,NIP,...}
|
|||
|
|
ips::NTuple{NIP, IntegrationPoint{D}}
|
|||
|
|
end
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**Rejected:** Slight overhead, less flexible (fixed at construction).
|
|||
|
|
|
|||
|
|
### Option C: Global Constants
|
|||
|
|
|
|||
|
|
```julia
|
|||
|
|
const TRI3_GAUSS1_IPS = ((0.5, Vec{2}((1/3, 1/3))),)
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**Rejected:** Not composable (can't parameterize on topology/order).
|
|||
|
|
|
|||
|
|
### Option D: Runtime Dispatch (OLD)
|
|||
|
|
|
|||
|
|
```julia
|
|||
|
|
get_integration_points(element::Seg2)
|
|||
|
|
# → Vector{IP} # Mutable struct with Dict
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**Rejected:** 50× slower, allocates, type-unstable.
|
|||
|
|
|
|||
|
|
## Performance Results
|
|||
|
|
|
|||
|
|
Benchmark: 1000 elements, 3 integration points each
|
|||
|
|
|
|||
|
|
| Approach | Time | Allocations | Speedup |
|
|||
|
|
|----------|------|-------------|---------|
|
|||
|
|
| OLD (runtime + Dict) | 53 μs | 515 KiB | 1× |
|
|||
|
|
| NEW (compile-time + Vec) | **1.1 μs** | **0 bytes** | **48×** |
|
|||
|
|
|
|||
|
|
**Realistic FEM assembly:**
|
|||
|
|
|
|||
|
|
- OLD: 53 μs + 515 KiB allocations
|
|||
|
|
- NEW: 1.1 μs + 0 allocations
|
|||
|
|
|
|||
|
|
## Implementation
|
|||
|
|
|
|||
|
|
### File Structure
|
|||
|
|
|
|||
|
|
```text
|
|||
|
|
src/integration/
|
|||
|
|
├── integration.jl # Abstract types (IntegrationPoint, AbstractIntegration)
|
|||
|
|
├── gauss.jl # High-level Gauss{N} type
|
|||
|
|
└── gauss_points.jl # NEW: Compile-time get_gauss_points!()
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### Supported Topologies
|
|||
|
|
|
|||
|
|
**1D:**
|
|||
|
|
|
|||
|
|
- Segment: Gauss{1}, Gauss{2}, Gauss{3}
|
|||
|
|
|
|||
|
|
**2D:**
|
|||
|
|
|
|||
|
|
- Triangle: Gauss{1} (1 pt), Gauss{2} (3 pt), Gauss{3} (4 pt)
|
|||
|
|
- Quadrilateral: Gauss{1} (1 pt), Gauss{2} (4 pt), Gauss{3} (9 pt)
|
|||
|
|
|
|||
|
|
**3D:**
|
|||
|
|
|
|||
|
|
- Tetrahedron: Gauss{1} (1 pt), Gauss{2} (4 pt), Gauss{3} (5 pt)
|
|||
|
|
- Hexahedron: Gauss{1} (1 pt), Gauss{2} (8 pt), Gauss{3} (27 pt)
|
|||
|
|
- Wedge: Gauss{1}, Gauss{2}
|
|||
|
|
- Pyramid: Gauss{1}, Gauss{2}
|
|||
|
|
|
|||
|
|
## Consequences
|
|||
|
|
|
|||
|
|
### Positive
|
|||
|
|
|
|||
|
|
1. **50× faster** than old approach
|
|||
|
|
2. **Zero allocations** in assembly loops
|
|||
|
|
3. **Type-stable** - compiler knows everything
|
|||
|
|
4. **Consistent with basis API** - same pattern as `get_basis_functions` (NOTE: `eval_basis!` is deprecated)
|
|||
|
|
5. **GPU-ready** - Vec{D} immutable, can transfer to GPU
|
|||
|
|
6. **Matches golden standard** - nodal assembly architecture
|
|||
|
|
|
|||
|
|
### Negative
|
|||
|
|
|
|||
|
|
1. **Breaking change** - old `get_integration_points(element)` deprecated
|
|||
|
|
2. **Migration needed** - update assembly code to new API
|
|||
|
|
3. **More verbose** - must specify topology and scheme explicitly
|
|||
|
|
|
|||
|
|
### Neutral
|
|||
|
|
|
|||
|
|
1. **Compile-time only** - dynamic integration orders need workaround
|
|||
|
|
2. **Fixed quadrature rules** - pre-defined Gauss{1}, Gauss{2}, etc.
|
|||
|
|
|
|||
|
|
## Migration Strategy
|
|||
|
|
|
|||
|
|
### Phase 1: Add New API (✅ Complete)
|
|||
|
|
|
|||
|
|
- Implement `get_gauss_points!()` for all topologies
|
|||
|
|
- Comprehensive tests
|
|||
|
|
- Benchmark validation
|
|||
|
|
|
|||
|
|
### Phase 2: Update Assembly Code (In Progress)
|
|||
|
|
|
|||
|
|
- Fix `get_integration_points(element)` in elements.jl
|
|||
|
|
- Update problem assembly functions
|
|||
|
|
- Ensure tests pass
|
|||
|
|
|
|||
|
|
### Phase 3: Deprecate Old API
|
|||
|
|
|
|||
|
|
- Add deprecation warnings to old functions
|
|||
|
|
- Document migration path
|
|||
|
|
- Remove after one release cycle
|
|||
|
|
|
|||
|
|
## Related
|
|||
|
|
|
|||
|
|
- **ADR-002:** Basis function API (same pattern)
|
|||
|
|
- **Golden Standard:** docs/book/multigpu_nodal_assembly.md
|
|||
|
|
- **Nodal Assembly Demos:** demos/nodal_assembly_{cpu,gpu}.jl
|
|||
|
|
|
|||
|
|
## References
|
|||
|
|
|
|||
|
|
1. Benchmark: `benchmarks/integration_points_benchmark.jl`
|
|||
|
|
2. Tests: `test/test_integration_points_api.jl`
|
|||
|
|
3. Implementation: `src/integration/gauss_points.jl`
|
|||
|
|
|
|||
|
|
## Status History
|
|||
|
|
|
|||
|
|
- 2025-11-11: Accepted, implemented, tested
|
|||
|
|
- Performance validated: 48× speedup, zero allocations
|