Files
JuliaFEM.jl/src/dofs/api.jl
T
Jukka Aho f56c9f2d11 docs(dofs): refresh api.jl DOF docstrings for @DOFSet
Align module docs with flat dof_indices, LICENSE.md link, and NamedTuple caveat.
2026-05-09 16:34:16 +03:00

225 lines
6.6 KiB
Julia
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# This file is a part of JuliaFEM.
# License is MIT: see https://github.com/JuliaFEM/JuliaFEM.jl/blob/master/LICENSE.md
"""
DOF Type - Type-Level Field Specification
`DOF{T, E}` is an abstract type used purely for type-level field specifications.
# Philosophy
DOF types are NEVER instantiated - they exist purely at the type level to specify
field structure in a clean, readable way.
# Usage
Field specifications are written with the `@DOFSet` macro and `DOF{Quantity, Entity}`
type syntax:
```julia
S = @DOFSet{u::DOF{Displacement{3}, Vertex},
p::DOF{Pressure, Cell}}
```
Single-field specifications use the same form:
```julia
S = @DOFSet{u::DOF{Displacement{3}, Vertex}}
```
# Connection to Assembly
The DOF specification `S` is a type parameter in Element:
```julia
Element{Tetrahedron{4}, Lagrange{1}, S}
```
The assembler uses `S` to:
1. Compute total DOF count: `ndofs(S, Tetrahedron{4})`.
2. Extract field information: `fieldnames(S)`, `fieldtypes(S)`.
3. Count DOFs per field: `field_ndofs(field_type, topology)`.
4. Access element DOFs via `element_dofs(elem, :u)` (the `dof_indices`
field itself is a flat `NTuple` for zero-allocation assembly).
All type-level, compile-time resolved; no runtime overhead.
# Compatibility
`DOFSet` is currently implemented as `NamedTuple`, so a plain
`@NamedTuple{u::DOF{...}, ...}` literal also works as long as each field
type is a `DOF{Quantity, Entity}`. The bare `Tuple{Quantity, Entity}` form
that older drafts used is no longer accepted by the DOFHandler; new code
should use `@DOFSet` with `DOF{T,E}`.
"""
# ============================================================================
# DOF Size Function (utility for counting DOF components)
# ============================================================================
"""
dof_size(::Type{T}) → Int
Number of DOF components for a quantity type.
# Examples
```julia
dof_size(Float64) # 1 (scalar)
dof_size(Vec{2}) # 2 (2D vector)
dof_size(Vec{3}) # 3 (3D vector)
dof_size(Tensor{2,2}) # 4 (2×2 tensor)
dof_size(Tensor{2,3}) # 9 (3×3 tensor)
dof_size(SymmetricTensor{2,3}) # 6 (symmetric 3×3)
```
"""
Base.@pure dof_size(::Type{Float64}) = 1
Base.@pure dof_size(::Type{<:Vec{D}}) where {D} = D
Base.@pure dof_size(::Type{<:Tensor{2,D}}) where {D} = D * D
Base.@pure dof_size(::Type{<:SymmetricTensor{2,D}}) where {D} = div(D * (D + 1), 2)
# ============================================================================
# Abstract DOF Type (Base Type for All Field Specifications)
# ============================================================================
"""
abstract type AbstractDOF
Abstract base type for all field specifications.
# Purpose
Base type for `DOF{T,E}` which is used inside `DOFSet` specifications:
```julia
S = @DOFSet{u::DOF{Displacement{3}, Vertex}} # DOF{T,E} <: AbstractDOF
Element{K, P, S} # where S <: DOFSet
```
# Subtypes
- `DOF{T,E}`: Type-level field specifications (never instantiated)
# Interface
Subtypes should support:
- `Base.fieldnames(::Type{<:AbstractDOF})` - field names as tuple
- `ndofs(::Type{<:AbstractDOF}, ::Type{<:AbstractTopology})` - total DOF count
"""
abstract type AbstractDOF end
# ============================================================================
# DOFSet - Multi-Field DOF Specification
# ============================================================================
"""
DOFSet
Type alias for multi-field DOF specifications.
Current implementation: NamedTuple (but this is an implementation detail!)
Use `@DOFSet` macro to create multi-field specifications:
```julia
S = @DOFSet{T::DOF{Temperature, Vertex}, u::DOF{Displacement{3}, Vertex}}
```
# Purpose
Hides implementation detail so we can change from NamedTuple to custom struct later
without breaking user code.
# Usage in Element Type
```julia
# Single-field - always use @DOFSet
S = @DOFSet{u::DOF{Displacement{3}, Vertex}}
Element{K, P, S}
# Multi-field - use @DOFSet
S = @DOFSet{T::DOF{Temperature, Vertex}, u::DOF{Displacement{3}, Vertex}}
Element{K, P, S}
```
# Compatibility Note
`NamedTuple` also works (since `DOFSet = NamedTuple`), but `DOFSet` is the preferred
interface. Using `DOFSet` ensures your code will continue to work if we change the
internal representation in the future.
"""
const DOFSet = NamedTuple
"""
@DOFSet{field1::Type1, field2::Type2, ...}
Create a multi-field DOF specification.
# Syntax
Each field is specified using `DOF{FieldType, EntityType}`:
```julia
field_name::DOF{FieldType, EntityType}
```
Where:
- `FieldType`: AbstractField type (Displacement{3}, Temperature, etc.)
- `EntityType`: Topological entity (Vertex, Edge, Face, Cell)
# Examples
```julia
# Thermo-mechanical coupling
S = @DOFSet{T::DOF{Temperature, Vertex}, u::DOF{Displacement{3}, Vertex}}
# THM-E (full multi-physics)
S = @DOFSet{
T::DOF{Temperature, Vertex}, # Temperature
u::DOF{Displacement{3}, Vertex}, # Displacement
p::DOF{Pressure, Cell}, # Pressure
φ::DOF{ElectricPotential, Edge} # Electric potential
}
# Create element with multi-field spec
element = Element{Tetrahedron{4}, Lagrange{1}, S}(id, dof_indices)
```
# Implementation Note
Currently wraps `@NamedTuple` but this is hidden from users.
Can be changed to custom struct later without breaking code!
# Compatibility
You can also use `@NamedTuple` directly, but `@DOFSet` is preferred for future compatibility.
"""
macro DOFSet(expr)
return :(@NamedTuple($expr))
end
# ============================================================================
# DOF Type (Abstract - Type-Level Only)
# ============================================================================
"""
abstract type DOF{T, E<:TopologicalEntity} <: AbstractDOF
Abstract type for type-level field specifications. NEVER instantiated.
Used purely for clean syntax inside `@DOFSet`:
```julia
S = @DOFSet{u::DOF{Vec{3,Float64}, Vertex},
p::DOF{Float64, Cell}}
```
# 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
DOF{SymmetricTensor{2,2}, Edge} # 2×2 symmetric tensor on edges
```
"""
abstract type DOF{T, E<:TopologicalEntity} <: AbstractDOF end
# DOF size for DOF type (type-level query)
Base.@pure function dof_size(::Type{<:DOF{T,E}}) where {T,E}
return dof_size(T)
end
# Fieldnames for single-field DOF types (default field name is :dof)
Base.@pure Base.fieldnames(::Type{<:DOF}) = (:dof,)