mirror of
https://github.com/JuliaFEM/JuliaFEM.jl.git
synced 2026-09-06 19:26:23 +00:00
142 lines
5.5 KiB
Markdown
142 lines
5.5 KiB
Markdown
|
|
# [Elements, DOF placement, materials, and multiphysics](@id elements_multiphysics_teaser)
|
|||
|
|
|
|||
|
|
This page is a conceptual teaser for the current (0.x) element pipeline: what the
|
|||
|
|
types mean, how global numbering attaches to an element, how materials
|
|||
|
|
hook in, and how a deliberately mixed DOF specification still follows one
|
|||
|
|
mechanical pattern.
|
|||
|
|
|
|||
|
|
For API listings, see [API Reference](@ref). For maintainer detail and
|
|||
|
|
invariants, see `AGENTS.md` and the `README.md` files under `src/elements/`
|
|||
|
|
and `src/dofs/`.
|
|||
|
|
|
|||
|
|
## Element as a typed template
|
|||
|
|
|
|||
|
|
A finite element is Ciarlet's triple ``(K, P, \Sigma)`` carried almost
|
|||
|
|
entirely in the type system:
|
|||
|
|
|
|||
|
|
| Role | Julia type |
|
|||
|
|
|------|----------------|
|
|||
|
|
| Reference domain ``K`` | Topology type, e.g. `Tetrahedron{4}`, `Hex8` |
|
|||
|
|
| Local space ``P`` | Basis type, e.g. `Lagrange{1}` |
|
|||
|
|
| Degrees of freedom ``\Sigma`` | `S`, a `@DOFSet{...}` (NamedTuple of `DOF{Quantity, Entity}`) |
|
|||
|
|
| Total local DOF count | Type parameter `N`, inferred from `K` and `S` |
|
|||
|
|
|
|||
|
|
The runtime struct is minimal:
|
|||
|
|
|
|||
|
|
```julia
|
|||
|
|
struct Element{K, P, S, N}
|
|||
|
|
id::UInt
|
|||
|
|
dof_indices::NTuple{N, UInt64}
|
|||
|
|
end
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
`K` and `P` are not stored; they only refine the type. The only heavy
|
|||
|
|
per-element data is the flat tuple `dof_indices`, one global DOF index per
|
|||
|
|
local DOF, in the canonical order defined below.
|
|||
|
|
|
|||
|
|
## Where each DOF "lives"
|
|||
|
|
|
|||
|
|
You declare fields with `@DOFSet`:
|
|||
|
|
|
|||
|
|
```julia
|
|||
|
|
S = @DOFSet{
|
|||
|
|
T::DOF{Temperature, Vertex},
|
|||
|
|
u::DOF{Displacement{3}, Vertex},
|
|||
|
|
p::DOF{Float64, Cell}, # discontinuous scalar (e.g. pressure bubble)
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
Each entry is `DOF{Quantity, Entity}`:
|
|||
|
|
|
|||
|
|
- `Quantity` fixes the number of components per attachment point
|
|||
|
|
(scalar temperature, 3-vector displacement, …) via `dof_size`.
|
|||
|
|
- `Entity` selects the topological anchor: `Vertex`, `Edge`, `Face`, or
|
|||
|
|
`Cell`.
|
|||
|
|
|
|||
|
|
The constructor for `Element{K,P,S,N}` checks that `N` equals `ndofs(K, S)`.
|
|||
|
|
|
|||
|
|
`create_elements!(mesh, Element{K,P,S})` builds a `DOFHandler` and fills
|
|||
|
|
each element's `dof_indices` so shared mesh entities reuse the same global
|
|||
|
|
indices. You do not hand-wire indices by hand in normal use.
|
|||
|
|
|
|||
|
|
## Compile-time layout: `local_dof_layout`
|
|||
|
|
|
|||
|
|
The order of entries in `dof_indices` is defined by
|
|||
|
|
`local_dof_layout(::Type{Element{K,P,S,N}})`, a `@generated` function that
|
|||
|
|
returns `NTuple{N, DOFLayoutEntry}`. Each entry stores
|
|||
|
|
`(field_idx, entity_local, component)` for one local DOF index.
|
|||
|
|
|
|||
|
|
Fields are visited in NamedTuple key order. For each field, all vertex
|
|||
|
|
DOFs (if `Entity === Vertex`) are enumerated in local vertex order, then
|
|||
|
|
components; then the next field. A `Cell`-anchored field contributes one
|
|||
|
|
logical "entity" worth of components per element (pressure bubble, …).
|
|||
|
|
|
|||
|
|
Assemblers and matrix-free operators index this tuple instead of doing
|
|||
|
|
runtime `div`/`mod` arithmetic to decode local DOFs.
|
|||
|
|
|
|||
|
|
Current limitation: the generated `local_dof_layout` implements `Vertex`
|
|||
|
|
and `Cell` attachments. `Edge` and `Face` are part of the long-term design
|
|||
|
|
(documented in `src/dofs/fields.jl`) but are not wired through the
|
|||
|
|
generator yet; adding them is a localized extension.
|
|||
|
|
|
|||
|
|
## Materials: traits and kernels
|
|||
|
|
|
|||
|
|
Materials subtype `AbstractMaterial`. Several open functions form a small
|
|||
|
|
trait layer, for example:
|
|||
|
|
|
|||
|
|
- `material_behavior(material)` — constant tangent, strain-dependent,
|
|||
|
|
stateful plasticity, …
|
|||
|
|
- `supported_physics(material)` — tuple of physics markers such as
|
|||
|
|
`Elasticity{3}()`, `Thermal{3}()`
|
|||
|
|
- `required_state_variables(material)` — what must live in IP state
|
|||
|
|
storage
|
|||
|
|
|
|||
|
|
Concrete materials implement these in their own files; see
|
|||
|
|
`src/materials/traits.jl` and `src/materials/api.jl`.
|
|||
|
|
|
|||
|
|
Important separation: the element type `S` describes which global DOFs exist
|
|||
|
|
and how they are ordered. A kernel decides which physics runs at a
|
|||
|
|
quadrature point and which material(s) supply tangents and forces.
|
|||
|
|
|
|||
|
|
Example: `ContinuumKernel` pairs one elastic material with a displacement
|
|||
|
|
field. `ThermoElasticKernel` (under `src/domains/thermo_elastic/`) holds
|
|||
|
|
both a mechanical and a thermal material; `evaluate_entry` dispatches on
|
|||
|
|
the `field_idx` pair from `DOFLayoutEntry` so `K_uu`, `K_TT`, and the
|
|||
|
|
coupling blocks are assembled from one element loop. That is the intended
|
|||
|
|
pattern for multiphysics: one `S`, one element storage model, kernels that
|
|||
|
|
know the physics blocks.
|
|||
|
|
|
|||
|
|
## A deliberately mixed specification
|
|||
|
|
|
|||
|
|
Thermo-mechanics with temperature and displacement on vertices is the
|
|||
|
|
simplest coupled story (four DOFs per node on a tetrahedron). A harsher
|
|||
|
|
stress test for the DOF machinery is vertex displacement plus a
|
|||
|
|
discontinuous cell pressure (Stokes-style `u`–`p`):
|
|||
|
|
|
|||
|
|
```julia
|
|||
|
|
S = @DOFSet{u::DOF{Displacement{3}, Vertex},
|
|||
|
|
p::DOF{Float64, Cell}}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
The test file `test/dofs/test_multifield_dof_system.jl` walks this path
|
|||
|
|
end-to-end: `ndofs` matches `local_dof_layout`, block ordering (all `u`
|
|||
|
|
DOFs, then all `p` DOFs), handler connectivity, and helpers such as
|
|||
|
|
`field_dof_range` / `element_dofs` aligned with the layout tuple.
|
|||
|
|
|
|||
|
|
Adding more named fields is mostly declaring another `DOF{…, …}` line in
|
|||
|
|
`@DOFSet` and providing a kernel that implements the cross-blocks you care
|
|||
|
|
about. The expensive part is physics and testing, not extending the
|
|||
|
|
element struct.
|
|||
|
|
|
|||
|
|
## Summary
|
|||
|
|
|
|||
|
|
- Elements are types plus a flat global index tuple; `S` is the single
|
|||
|
|
source of truth for DOF count and ordering.
|
|||
|
|
- `create_elements!` maps mesh topology to those indices in a type-stable
|
|||
|
|
way.
|
|||
|
|
- Materials expose traits; kernels connect materials to the relevant
|
|||
|
|
`field_idx` blocks via `local_dof_layout`.
|
|||
|
|
- Multifield and mixed `Vertex`/`Cell` anchoring already exercise the
|
|||
|
|
production DOF path; richer entity attachments follow the same layout
|
|||
|
|
idea once implemented.
|