mirror of
https://github.com/JuliaFEM/JuliaFEM.jl.git
synced 2026-09-17 17:22:10 +00:00
6a94155a32
- get_basis_functions() and get_basis_derivatives() recommended - Separation of concerns: topology and basis as separate parameters - Side-by-side examples for all common element types - Complete assembly example showing migration path - Type-stable implementation with no performance penalty - 302 lines: Comprehensive migration documentation
303 lines
7.4 KiB
Markdown
303 lines
7.4 KiB
Markdown
---
|
||
title: "Migration Guide: Deprecated Basis Function API"
|
||
date: 2025-11-11
|
||
author: "JuliaFEM Team"
|
||
status: "Authoritative"
|
||
tags: ["migration", "deprecation", "basis-functions", "api"]
|
||
---
|
||
|
||
## Overview
|
||
|
||
The old basis function API (`eval_basis!` and `eval_dbasis!`) is **DEPRECATED** and will be removed in a future release. This guide helps you migrate to the new, recommended API.
|
||
|
||
## Summary of Changes
|
||
|
||
| Old API (DEPRECATED) | New API (RECOMMENDED) |
|
||
|----------------------|------------------------|
|
||
| `eval_basis!(Lagrange{Triangle,1}, Float64, ξ)` | `get_basis_functions(Triangle(), Lagrange{1}(), ξ)` |
|
||
| `eval_dbasis!(Lagrange{Triangle,1}, ξ)` | `get_basis_derivatives(Triangle(), Lagrange{1}(), ξ)` |
|
||
|
||
## Why the Change?
|
||
|
||
**Key Improvements:**
|
||
|
||
1. **Separation of concerns** - Topology and basis are separate parameters
|
||
2. **Clearer naming** - `get_basis_functions` vs `eval_basis!` (no mutation despite `!`)
|
||
3. **Type parameter simplification** - No redundant `Float64` parameter
|
||
4. **Consistent API** - Same pattern across all basis evaluation functions
|
||
|
||
## Migration Examples
|
||
|
||
### Example 1: Triangle Linear Element
|
||
|
||
**OLD (deprecated):**
|
||
|
||
```julia
|
||
# Element with Lagrange{Triangle,1} basis
|
||
xi = Vec(0.25, 0.25)
|
||
|
||
N = eval_basis!(Lagrange{Triangle,1}, Float64, xi)
|
||
dN = eval_dbasis!(Lagrange{Triangle,1}, xi)
|
||
```
|
||
|
||
**NEW (recommended):**
|
||
|
||
```julia
|
||
# Topology and basis passed separately
|
||
topology = Triangle()
|
||
basis = Lagrange{1}()
|
||
xi = Vec(0.25, 0.25)
|
||
|
||
N = get_basis_functions(topology, basis, xi)
|
||
dN = get_basis_derivatives(topology, basis, xi)
|
||
```
|
||
|
||
### Example 2: Tetrahedron Quadratic Element
|
||
|
||
**OLD (deprecated):**
|
||
|
||
```julia
|
||
N = eval_basis!(Lagrange{Tetrahedron,2}, Float64, xi)
|
||
dN = eval_dbasis!(Lagrange{Tetrahedron,2}, xi)
|
||
```
|
||
|
||
**NEW (recommended):**
|
||
|
||
```julia
|
||
N = get_basis_functions(Tetrahedron(), Lagrange{2}(), xi)
|
||
dN = get_basis_derivatives(Tetrahedron(), Lagrange{2}(), xi)
|
||
```
|
||
|
||
### Example 3: Assembly Loop with Integration Points
|
||
|
||
**OLD (deprecated):**
|
||
|
||
```julia
|
||
for (w, xi) in get_gauss_points!(Triangle, Gauss{2})
|
||
N = eval_basis!(Lagrange{Triangle,1}, Float64, xi)
|
||
dN = eval_dbasis!(Lagrange{Triangle,1}, xi)
|
||
|
||
# Assembly...
|
||
end
|
||
```
|
||
|
||
**NEW (recommended):**
|
||
|
||
```julia
|
||
topology = Triangle()
|
||
basis = Lagrange{1}()
|
||
|
||
for (w, xi) in get_gauss_points!(Triangle, Gauss{2})
|
||
N = get_basis_functions(topology, basis, xi)
|
||
dN = get_basis_derivatives(topology, basis, xi)
|
||
|
||
# Assembly...
|
||
end
|
||
```
|
||
|
||
**Note:** `topology` and `basis` are type-stable constants, so there's no performance penalty from creating them outside the loop.
|
||
|
||
## Complete Assembly Example
|
||
|
||
**OLD (deprecated):**
|
||
|
||
```julia
|
||
function assemble_element_old(element::Element)
|
||
K_local = zeros(9, 9)
|
||
|
||
for ip in get_integration_points(element)
|
||
w = ip.weight
|
||
xi = Vec(ip.coords...)
|
||
|
||
# DEPRECATED API
|
||
N = eval_basis!(Lagrange{Triangle,2}, Float64, xi)
|
||
dN = eval_dbasis!(Lagrange{Triangle,2}, xi)
|
||
|
||
# Jacobian
|
||
J = sum(dN[i] ⊗ X[i] for i in 1:6)
|
||
detJ = det(J)
|
||
invJ = inv(J)
|
||
|
||
# Physical derivatives
|
||
dN_phys = tuple((invJ ⋅ dN[i] for i in 1:6)...)
|
||
|
||
# Accumulate stiffness
|
||
for i in 1:6, j in 1:6
|
||
K_local[i,j] += w * detJ * dot(dN_phys[i], dN_phys[j])
|
||
end
|
||
end
|
||
|
||
return K_local
|
||
end
|
||
```
|
||
|
||
**NEW (recommended):**
|
||
|
||
```julia
|
||
function assemble_element_new(element::Element)
|
||
K_local = zeros(9, 9)
|
||
|
||
# Define topology and basis (type-stable constants)
|
||
topology = Triangle()
|
||
basis = Lagrange{2}()
|
||
|
||
# NEW: Zero-allocation integration points
|
||
for (w, ξ) in get_gauss_points!(Triangle, Gauss{2})
|
||
# NEW API: Separate topology and basis
|
||
N = get_basis_functions(topology, basis, ξ)
|
||
dN = get_basis_derivatives(topology, basis, ξ)
|
||
|
||
# Jacobian (using new tuple-based API)
|
||
J = sum(dN[i] ⊗ X[i] for i in 1:6)
|
||
detJ = det(J)
|
||
invJ = inv(J)
|
||
|
||
# Physical derivatives
|
||
dN_phys = tuple((invJ ⋅ dN[i] for i in 1:6)...)
|
||
|
||
# Accumulate stiffness
|
||
for i in 1:6, j in 1:6
|
||
K_local[i,j] += w * detJ * dot(dN_phys[i], dN_phys[j])
|
||
end
|
||
end
|
||
|
||
return K_local
|
||
end
|
||
```
|
||
|
||
**Performance:** The new version is ~50× faster due to:
|
||
|
||
- Zero-allocation integration points
|
||
- Type-stable basis evaluation
|
||
- Compile-time optimizations
|
||
|
||
## Topology Types Reference
|
||
|
||
| Old Basis Type | New Topology | New Basis |
|
||
|----------------|--------------|-----------|
|
||
| `Lagrange{Segment,1}` | `Segment()` | `Lagrange{1}()` |
|
||
| `Lagrange{Triangle,1}` | `Triangle()` | `Lagrange{1}()` |
|
||
| `Lagrange{Triangle,2}` | `Triangle()` | `Lagrange{2}()` |
|
||
| `Lagrange{Quadrilateral,1}` | `Quadrilateral()` | `Lagrange{1}()` |
|
||
| `Lagrange{Quadrilateral,2}` | `Quadrilateral()` | `Lagrange{2}()` |
|
||
| `Lagrange{Tetrahedron,1}` | `Tetrahedron()` | `Lagrange{1}()` |
|
||
| `Lagrange{Tetrahedron,2}` | `Tetrahedron()` | `Lagrange{2}()` |
|
||
| `Lagrange{Hexahedron,1}` | `Hexahedron()` | `Lagrange{1}()` |
|
||
| `Lagrange{Hexahedron,2}` | `Hexahedron()` | `Lagrange{2}()` |
|
||
| `Lagrange{Wedge,1}` | `Wedge()` | `Lagrange{1}()` |
|
||
| `Lagrange{Pyramid,1}` | `Pyramid()` | `Lagrange{1}()` |
|
||
|
||
## Migration Checklist
|
||
|
||
### Step 1: Find All Uses of Old API
|
||
|
||
Search your codebase for:
|
||
|
||
```bash
|
||
grep -r "eval_basis!" src/
|
||
grep -r "eval_dbasis!" src/
|
||
```
|
||
|
||
### Step 2: Update Function Calls
|
||
|
||
For each occurrence:
|
||
|
||
1. Identify the topology (e.g., `Triangle`, `Tetrahedron`)
|
||
2. Identify the polynomial order (e.g., `1`, `2`)
|
||
3. Replace with new API:
|
||
|
||
```julia
|
||
# OLD: eval_basis!(Lagrange{Triangle,1}, Float64, xi)
|
||
# NEW: get_basis_functions(Triangle(), Lagrange{1}(), xi)
|
||
```
|
||
|
||
### Step 3: Update Integration Loops
|
||
|
||
If using old integration point API:
|
||
|
||
```julia
|
||
# OLD: for ip in get_integration_points(element)
|
||
# NEW: for (w, ξ) in get_gauss_points!(Triangle, Gauss{2})
|
||
```
|
||
|
||
### Step 4: Test
|
||
|
||
Run your tests to ensure:
|
||
|
||
- All functionality works
|
||
- Performance is maintained or improved
|
||
- No deprecation warnings appear
|
||
|
||
## Common Issues
|
||
|
||
### Issue 1: Type Parameter Confusion
|
||
|
||
**Problem:**
|
||
|
||
```julia
|
||
# This won't work!
|
||
N = get_basis_functions(Lagrange{Triangle,1}(), xi)
|
||
```
|
||
|
||
**Solution:**
|
||
|
||
```julia
|
||
# Topology and basis are separate!
|
||
N = get_basis_functions(Triangle(), Lagrange{1}(), xi)
|
||
```
|
||
|
||
### Issue 2: Integration Point Coordinates
|
||
|
||
**Problem:**
|
||
|
||
```julia
|
||
# Old IP struct had .coords field
|
||
xi = Vec(ip.coords...) # Tuple to Vec conversion
|
||
```
|
||
|
||
**Solution:**
|
||
|
||
```julia
|
||
# New API returns Vec{D} directly
|
||
for (w, ξ) in get_gauss_points!(Triangle, Gauss{2})
|
||
# ξ is already Vec{2}!
|
||
```
|
||
|
||
### Issue 3: Performance Regression
|
||
|
||
**Problem:** New code is slower than old code.
|
||
|
||
**Solution:** Make sure you're using the new integration points API:
|
||
|
||
```julia
|
||
# WRONG (old API, allocates):
|
||
for ip in get_integration_points(element)
|
||
...
|
||
|
||
# RIGHT (new API, zero allocation):
|
||
for (w, ξ) in get_gauss_points!(Triangle, Gauss{2})
|
||
...
|
||
```
|
||
|
||
## Further Reading
|
||
|
||
- **ADR-002:** Basis Function API Design
|
||
- **ADR-004:** Integration Points API Design
|
||
- **Performance Benchmarks:** `benchmarks/basis_function_access_patterns.jl`
|
||
- **Tests:** `test/test_integration_points_api.jl`
|
||
|
||
## Support
|
||
|
||
If you encounter issues during migration:
|
||
|
||
1. Check this guide for common issues
|
||
2. Review the ADR documents for design rationale
|
||
3. Open an issue on GitHub with a minimal example
|
||
|
||
## Timeline
|
||
|
||
- **v0.6.0:** Deprecation warnings added
|
||
- **v0.7.0:** Old API will be removed (planned)
|
||
|
||
**Migrate now** to avoid breaking changes in future releases!
|