mirror of
https://github.com/JuliaFEM/JuliaFEM.jl.git
synced 2026-09-10 22:06:40 +00:00
docs(dofs): rewrite README for DOFHandler and matrix-free connectivity
Replace outdated DOFManager/@Fields prose with current files, create_elements!, facet-DOF notes, and assembler pointers.
This commit is contained in:
+102
-175
@@ -1,204 +1,131 @@
|
||||
# DOF System
|
||||
# src/dofs/
|
||||
|
||||
Type-level field specifications for finite elements.
|
||||
Type-stable degree-of-freedom infrastructure: the field specification
|
||||
language (`DOF{Q, E}`, `@DOFSet`), the global DOF numbering
|
||||
(`DOFHandler`), and the inverse DOF connectivity used by the matrix-free
|
||||
assembler.
|
||||
|
||||
---
|
||||
The previous `Dict`-based `DOFManager`/`register_fields!`/`count_field_dofs`
|
||||
API has been removed. `DOFManager` is now an alias for `DOFHandler` for
|
||||
backward compatibility.
|
||||
|
||||
## Philosophy
|
||||
Contract and vocabulary: `AGENTS.md` (repository root). Executable examples:
|
||||
`test/dofs/test_multifield_dof_system.jl` and the DOF topic tests under
|
||||
`test/dofs/runtests.jl`.
|
||||
|
||||
**Multi-field is fundamental. Single-field is just a special case with one key.**
|
||||
## Files
|
||||
|
||||
Field specifications are `DOFSet` types (currently implemented as NamedTuples), used purely at the type level:
|
||||
- `api.jl` — `DOF{Quantity, Entity}` abstract type, `dof_size`
|
||||
for the supported quantity types (`Float64`, `Vec{D}`, `Tensor{2,D}`),
|
||||
and the entity types (`Vertex`, `Edge`, `Face`, `Cell`).
|
||||
- `fields.jl` — the `@DOFSet` macro (NamedTuple of `DOF{Q, E}`)
|
||||
and field accessors (`field_names`, `field_count`, `field_ndofs`,
|
||||
`field_dof_range`, `is_single_field`).
|
||||
- `dof_handler.jl` — `DOFHandler{Mesh, S, NF}`. One flat
|
||||
`Vector{Int}` per field stores the starting global DOF for each
|
||||
entity ID; `total_dofs` is computed once. `create_elements!(mesh, ET)`
|
||||
walks the mesh, assigns DOFs, builds the element vector and the
|
||||
inverse DOF connectivity in one pass.
|
||||
- `dof_connectivity.jl` — `DOFConnectivity`. Maps each DOF to the
|
||||
elements and local-DOF positions that contribute to its row, which is
|
||||
what the DOF-based assembler iterates over.
|
||||
|
||||
## DOF specification
|
||||
|
||||
`DOF{Quantity, Entity}` is purely a type-level marker; instances are
|
||||
never created. `Quantity` carries the per-entity DOF size via
|
||||
`dof_size`, and `Entity` selects the topological entity that owns it.
|
||||
|
||||
```julia
|
||||
# Clean syntax using abstract DOF{T,E} types:
|
||||
(T = DOF{Float64, Vertex}, u = DOF{Vec{3,Float64}, Vertex})
|
||||
|
||||
# Using DOFSet macro (preferred):
|
||||
S = @DOFSet{T::DOF{Temperature, Vertex}, u::DOF{Displacement{3}, Vertex}}
|
||||
|
||||
# Used in Element type parameter:
|
||||
Element{Tetrahedron{4}, Lagrange{1}, S}
|
||||
```
|
||||
|
||||
**Note**: `DOFSet` is currently an alias for `NamedTuple`, but using `DOFSet` ensures your code will continue to work if we change the internal representation in the future. You can also use `@NamedTuple` directly, but `@DOFSet` is the preferred interface.
|
||||
|
||||
---
|
||||
|
||||
## Core Concept: DOF{T,E}
|
||||
|
||||
`DOF{T, E}` is an **abstract type** for type-level specifications (never instantiated):
|
||||
|
||||
```julia
|
||||
abstract type DOF{T, E<:TopologicalEntity} end
|
||||
```
|
||||
|
||||
**Type Parameters:**
|
||||
|
||||
- `T`: Quantity type (Float64, Vec{D}, Tensor{2,D}, etc.)
|
||||
- `E`: Topological entity (Vertex, Edge, Face, Cell)
|
||||
|
||||
**Examples:**
|
||||
|
||||
```julia
|
||||
DOF{Float64, Vertex} # Scalar at vertices
|
||||
DOF{Vec{3,Float64}, Vertex} # 3D vector at vertices
|
||||
DOF{Tensor{2,3,Float64}, Cell} # 3×3 tensor at cells
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Field Specifications with @DOFSet
|
||||
|
||||
Use the `@DOFSet` macro for multi-field specifications:
|
||||
|
||||
```julia
|
||||
# Multi-field specification with DOF syntax (preferred)
|
||||
S = @DOFSet{
|
||||
T::DOF{Temperature, Vertex},
|
||||
u::DOF{Displacement{3}, Vertex},
|
||||
p::DOF{Pressure, Cell}
|
||||
}
|
||||
|
||||
# Returns: DOFSet type (currently NamedTuple, but this is an implementation detail)
|
||||
```
|
||||
|
||||
**Always use `@DOFSet`, even for single-field elements:**
|
||||
|
||||
```julia
|
||||
# Single-field - always wrap in @DOFSet
|
||||
# Single field
|
||||
S = @DOFSet{u::DOF{Displacement{3}, Vertex}}
|
||||
Element{Tetrahedron{4}, Lagrange{1}, S}
|
||||
|
||||
# Multi-field (e.g. thermo-mechanical)
|
||||
S = @DOFSet{T::DOF{Temperature, Vertex},
|
||||
u::DOF{Displacement{3}, Vertex}}
|
||||
|
||||
# `S` is a NamedTuple type with field types `DOF{Q, E}`.
|
||||
```
|
||||
|
||||
This simplifies generated code by having a single, consistent syntax for all cases.
|
||||
`@DOFSet` is the preferred entry point. `DOFSet` is currently a type
|
||||
alias for `NamedTuple`, but using `@DOFSet` insulates calling code from
|
||||
that implementation detail.
|
||||
|
||||
---
|
||||
## Building elements and the handler
|
||||
|
||||
## Element Integration
|
||||
|
||||
Field specification `S` is the third type parameter in Element:
|
||||
`create_elements!` is the only call most user code needs:
|
||||
|
||||
```julia
|
||||
struct Element{K<:AbstractTopology, P<:AbstractBasis, S<:DOFSet, N}
|
||||
id::UInt
|
||||
dof_indices::NTuple{N,UInt64} # Flat tuple of global DOF indices
|
||||
end
|
||||
ET = Element{Hex8, Lagrange{1}, S}
|
||||
elements, handler = create_elements!(mesh, ET)
|
||||
# elements::Vector{Element{Hex8, Lagrange{1}, S, 24}}
|
||||
# handler.dof_connectivity is already built.
|
||||
```
|
||||
|
||||
**Type Parameters:**
|
||||
- `K`: Topology (Triangle{3}, Tetrahedron{4}, ...)
|
||||
- `P`: Basis (Lagrange{1}, Lagrange{2}, ...)
|
||||
- `S`: Field specification (`DOF{T,E}` for single-field, `DOFSet` for multi-field)
|
||||
- `N::Int`: Total DOF count (automatically inferred from S and K)
|
||||
`DOFHandler` exposes:
|
||||
|
||||
**Example with single field:**
|
||||
- `field_starts::NTuple{NF, Vector{Int}}` — per-field starting DOF for
|
||||
each entity ID.
|
||||
- `total_dofs::Int`.
|
||||
- `dof_connectivity::DOFConnectivity` for the matrix-free path.
|
||||
|
||||
```julia
|
||||
# Most common case - use DOF{T,E} directly
|
||||
S = @DOFSet{u::DOF{Displacement{3}, Vertex}}
|
||||
elem = Element{Tetrahedron{4}, Lagrange{1}, S}(
|
||||
UInt(1),
|
||||
(dof=(1,2,3,4,5,6,7,8,9,10,11,12),) # Single field, default name :dof
|
||||
)
|
||||
```
|
||||
The compile-time DOF layout for the element template is generated by
|
||||
`local_dof_layout(::Type{Element{K, P, S, N}})` (in `src/elements/`).
|
||||
|
||||
**Example with multiple fields:**
|
||||
## Element-level DOF utilities
|
||||
|
||||
```julia
|
||||
S = @DOFSet{
|
||||
T::DOF{Temperature, Vertex},
|
||||
u::DOF{Displacement{3}, Vertex}
|
||||
}
|
||||
These helpers operate on a single `Element` instance and are used inside
|
||||
assembly hot paths:
|
||||
|
||||
elem = Element{Tetrahedron{4}, Lagrange{1}, S}(
|
||||
UInt(1),
|
||||
(1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16) # Flat tuple: T DOFs first, then u DOFs
|
||||
)
|
||||
```
|
||||
- `element_dofs(elem)` — flat `NTuple{N, UInt64}`.
|
||||
- `n_element_dofs(elem)` — `N`.
|
||||
- `local_to_global_map(elem)` — same as above, for clarity.
|
||||
- `field_dof_range(elem, :field)` — `UnitRange` of local indices for
|
||||
one field block (compile-time constant).
|
||||
- `extract_element_dofs(elem, u)` — flat `NamedTuple` of scalars
|
||||
grouped by field.
|
||||
- `extract_element_dofs_structured(elem, u)` — values reinterpreted into
|
||||
the field's quantity type (e.g. `Vec{3}` per node).
|
||||
- `interpolate_field`,
|
||||
`interpolate_fields`,
|
||||
`interpolate_field_value`,
|
||||
`interpolate_local_fields` — point-evaluation utilities at quadrature
|
||||
points.
|
||||
|
||||
---
|
||||
## Multiple DOFs per Edge or Face
|
||||
|
||||
## DOF Counting
|
||||
The topological facet id from [`AbstractFacetConnectivityMaps`](@ref) still
|
||||
identifies one **mesh edge** or **mesh face**. If the field quantity has
|
||||
`dof_size(Q) > 1` (for example `DOF{Vec{2,Float64}, Edge}`), that entity owns
|
||||
several consecutive global DOFs starting at `field_starts[field][entity_gid]`.
|
||||
`local_dof_layout` and `_make_element_dofs` treat them as separate components
|
||||
on the same local entity index; assembly kernels that replicate a scalar facet
|
||||
measure per unknown should match `component(layout_i) == component(layout_j)`
|
||||
when filling diagonal blocks.
|
||||
|
||||
All DOF information is computed at compile time:
|
||||
Hp-refinement, NURBS/IGA, or hierarchic enrichment imply either distinct
|
||||
`Element{K, P, S, N}` templates per patch (so `N` and layout stay compile-time
|
||||
constants) or precomputed facet tables built once when the mesh changes — not
|
||||
dynamic `Dict` lookups inside element quadrature loops.
|
||||
|
||||
```julia
|
||||
# Components per quantity type:
|
||||
dof_size(Float64) = 1
|
||||
dof_size(Vec{3}) = 3
|
||||
dof_size(Tensor{2,3}) = 9
|
||||
Variable numbers of DOFs on different mesh edges (non-uniform `p` along edges)
|
||||
need matching counts on shared edges for conformity. A natural numbering stores
|
||||
one contiguous band per global edge with widths given by a CSR offset vector;
|
||||
see `edge_dof_csr_offsets` on [`AbstractFacetConnectivityMaps`](@ref) in
|
||||
`src/mesh/hex8_facet_maps.jl`. Wiring that into `DOFHandler` and
|
||||
`local_dof_layout` is future work when hp facet spaces are added.
|
||||
|
||||
# DOFs per field:
|
||||
field_ndofs(DOF{Temperature, Vertex}, Tetrahedron{4}) # 1 × 4 = 4
|
||||
field_ndofs(DOF{Displacement{3}, Vertex}, Tetrahedron{4}) # 3 × 4 = 12
|
||||
## Heterogeneous element types
|
||||
|
||||
# Total DOFs for element:
|
||||
ndofs(S, Tetrahedron{4}) # Sum over all fields
|
||||
```
|
||||
`create_elements!` can be called repeatedly on the same mesh with
|
||||
different `Element{K, P, S}` types (for example a thermal `S1` and a
|
||||
mechanical `S2`); the handler keeps DOFs of identically-named fields
|
||||
shared between the element types.
|
||||
|
||||
---
|
||||
## Related code
|
||||
|
||||
## Examples
|
||||
|
||||
### Single-Field Elements
|
||||
|
||||
```julia
|
||||
# Heat conduction:
|
||||
S = @Fields begin
|
||||
T: Float64, Vertex
|
||||
end
|
||||
Element{Triangle{3}, Lagrange{1}, S} # 3 DOFs
|
||||
|
||||
# 3D Elasticity:
|
||||
S = @Fields begin
|
||||
u: Vec{3,Float64}, Vertex
|
||||
end
|
||||
Element{Tetrahedron{4}, Lagrange{1}, S} # 12 DOFs
|
||||
```
|
||||
|
||||
### Multi-Field Elements
|
||||
|
||||
```julia
|
||||
# Thermo-mechanical:
|
||||
S = @Fields begin
|
||||
T: Float64, Vertex
|
||||
u: Vec{3,Float64}, Vertex
|
||||
end
|
||||
Element{Tetrahedron{4}, Lagrange{1}, S} # 4 + 12 = 16 DOFs
|
||||
|
||||
# Thermo-hydro-mechanical:
|
||||
S = @Fields begin
|
||||
T: Float64, Vertex # Temperature at vertices
|
||||
p: Float64, Vertex # Pressure at vertices
|
||||
u: Vec{3,Float64}, Vertex # Displacement at vertices
|
||||
end
|
||||
Element{Tetrahedron{4}, Lagrange{1}, S} # 4 + 4 + 12 = 20 DOFs
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Key Benefits
|
||||
|
||||
1. **Type-level computation** - All DOF information resolved at compile time
|
||||
2. **Named field access** - `elem.dof_indices.T` self-documenting
|
||||
3. **Multi-field natural** - No special cases, just more keys in NamedTuple
|
||||
4. **Zero overhead** - No runtime type checks, no Dict lookups
|
||||
5. **Extensible** - Add new fields without changing Element struct
|
||||
|
||||
---
|
||||
|
||||
## Module Structure
|
||||
|
||||
- **`api.jl`** - DOF{T,E} abstract type + dof_size() utility
|
||||
- **`fields.jl`** - FieldSpec, @Fields macro, ndofs(), field queries
|
||||
- **`dof_manager.jl`** - Global DOF numbering and element creation
|
||||
- **`dofs.jl`** - Main entry point
|
||||
|
||||
---
|
||||
|
||||
## Related Modules
|
||||
|
||||
- `/src/topology/` - TopologicalEntity types (Vertex, Edge, Face, Cell)
|
||||
- `/src/basis/` - Basis functions (Lagrange, etc.)
|
||||
- `/src/elements/` - Element implementation
|
||||
- `src/topology/` — `Vertex`, `Edge`, `Face`, `Cell`.
|
||||
- `src/elements/` — `Element{K, P, S, N}` and `local_dof_layout`.
|
||||
- `src/assemblers/` — `DOFBasedCOOAssembler` is the primary consumer
|
||||
of `DOFConnectivity`.
|
||||
- `test/dofs/` — public test suite.
|
||||
|
||||
Reference in New Issue
Block a user