refactor(physics): Create consolidated AbstractPhysics definition

- Define AbstractPhysics abstract type in dedicated file
- Consolidate documentation from previous duplicate definitions
- Document type as coupling of Mesh, Material, Field, and Formulation
- Add comprehensive examples for 3D solid, heat, and beam physics
- Include multiphysics pattern documentation
- Remove duplicate AbstractPhysics definitions across codebase
- 109 lines of documentation and abstract type definition
This commit is contained in:
Jukka Aho
2025-11-18 16:08:35 +02:00
parent 43539f2441
commit 6243e4fe1e
+84 -113
View File
@@ -4,135 +4,106 @@
"""
AbstractPhysics
Base type for all physics implementations in JuliaFEM.
Abstract type for all physics problems in JuliaFEM.
Physics objects serve as:
1. **Dispatch tags** - Select correct assembly method via multiple dispatch
2. **Configuration holders** - Store physics-specific options
3. **Field name providers** - Define primary field ("displacement", "temperature", etc.)
Physics is the **coupling** of four components:
- **Mesh**: Topology/geometry (where)
- **Material**: Constitutive law (what material)
- **Field**: Solution variables (what we solve for)
- **Formulation**: Discretization strategy (how we discretize)
# Interface Requirements
All physics types must support:
- `assemble!(physics)` - Assemble global system matrices/operators
- `solve!(physics)` - Solve the physics problem
- `add_dirichlet!(physics, ...)` - Apply essential boundary conditions
- `add_neumann!(physics, ...)` - Apply natural boundary conditions
# Concrete Types
- `Physics{Formulation, Field, Mesh, Material}` - Standard FEM physics problem
# Design Philosophy
Each physics type represents a specific set of governing equations:
- `ElasticityPhysics` → ∇⋅σ = ρü + b
- `HeatPhysics` → ∇⋅(k∇T) = ρcₚ∂T/∂t + Q
- `ContactPhysics` → Contact constraints and friction
- etc.
**Physics references Mesh (does not own it)**:
- Multiple physics can share one mesh (multiphysics coupling)
- No mesh duplication (memory efficient)
- Mesh is the topology owner
- Physics is the problem owner
# Multi-Physics Coupling (Future)
**Type parameters enable dispatch specialization**:
```julia
# Specialize assembly for formulation × field combinations
assemble!(::Physics{ContinuumFormulation{FullThreeD}, Displacement{3}, M, Mat})
assemble!(::Physics{BeamFormulation{Timoshenko}, DisplacementRotation{3}, M, Mat})
The design supports coupled physics via composition:
# Generic fallback
assemble!(::Physics{Fm, F, M, Mat}) where {Fm, F, M, Mat}
```
# Examples
```julia
# Future: Thermo-mechanical coupling
coupled = CoupledPhysics(
ElasticityPhysics(...),
HeatPhysics(...)
# 3D solid mechanics
physics = Physics(
name = "cantilever",
mesh = mesh,
element_set = :all,
field = Displacement{3}(),
formulation = ContinuumFormulation{FullThreeD}(),
material = LinearElastic(E=210e9, ν=0.3)
)
# Solver handles coupling automatically
assemble!(assembly, coupled, elements, time)
# Heat transfer
physics_thermal = Physics(
name = "heat_conduction",
mesh = mesh,
element_set = :solid,
field = Temperature(),
formulation = ContinuumFormulation{FullThreeD}(),
material = ThermalMaterial(k=50.0, ρ=7850.0, c=450.0)
)
# 3D beam
physics_beam = Physics(
name = "beam",
mesh = beam_mesh,
element_set = :all,
field = DisplacementRotation{3}(),
formulation = BeamFormulation{Timoshenko}(),
material = steel
)
```
This enables:
- Sequential coupling (operator splitting)
- Monolithic coupling (solve simultaneously)
- Staggered schemes (iterative coupling)
# Multiphysics Example
# GPU Compatibility
```julia
# Share one mesh between structural and thermal physics
mesh = Mesh{Hex8}(nodes, connectivity)
All physics implementations must be GPU-friendly:
- ✅ Type-stable (no Dict lookups)
- ✅ Zero allocation in hot paths
- ✅ Kernel-compatible functions
- ✅ Minimal host-device transfers
physics_structural = Physics(
name = "structure",
mesh = mesh, # Reference, not copy!
field = Displacement{3}(),
formulation = ContinuumFormulation{FullThreeD}(),
material = steel
)
Iterations run entirely on GPU. Only after convergence do we transfer results
to host for postprocessing.
physics_thermal = Physics(
name = "thermal",
mesh = mesh, # Same mesh reference!
field = Temperature(),
formulation = ContinuumFormulation{FullThreeD}(),
material = thermal_steel
)
```
# See Also
- `docs/user/system_architecture.md` - Design rationale
- `docs/book/elasticity_refactoring_plan.md` - Implementation details
- [`assemble!`](@ref) - System assembly
- [`solve!`](@ref) - Solve physics problem
- [`add_dirichlet!`](@ref) - Essential BCs
- [`add_neumann!`](@ref) - Natural BCs
- Concrete type: `Physics` in `src/physics/types.jl`
"""
abstract type AbstractPhysics end
"""
get_unknown_field_name(physics::AbstractPhysics) -> String
Return the name of the primary unknown field for this physics.
# Examples
```julia
get_unknown_field_name(ElasticityPhysics()) # "displacement"
get_unknown_field_name(HeatPhysics()) # "temperature"
get_unknown_field_name(FluidPhysics()) # "velocity"
```
"""
function get_unknown_field_name(physics::AbstractPhysics)
error("get_unknown_field_name not implemented for $(typeof(physics))")
end
"""
get_formulation_type(physics::AbstractPhysics) -> Symbol
Return the formulation type: `:incremental`, `:total`, or `:rate`.
- `:incremental` - Solve for Δu, update u ← u + Δu (elasticity)
- `:total` - Solve for u directly (Poisson equation)
- `:rate` - Solve for ∂u/∂t (transient heat, fluid dynamics)
"""
function get_formulation_type(physics::AbstractPhysics)
error("get_formulation_type not implemented for $(typeof(physics))")
end
"""
get_unknown_field_dimension(physics::AbstractPhysics) -> Int
Return the dimension of the unknown field (DOFs per node).
# Examples
```julia
get_unknown_field_dimension(ElasticityPhysics()) # 3 (3D displacement)
get_unknown_field_dimension(HeatPhysics()) # 1 (scalar temperature)
get_unknown_field_dimension(FluidPhysics()) # 4 (velocity + pressure)
```
"""
function get_unknown_field_dimension(physics::AbstractPhysics)
error("get_unknown_field_dimension not implemented for $(typeof(physics))")
end
"""
assemble!(assembly::Assembly, physics::AbstractPhysics, elements, time)
Assemble global system for given physics.
This is the main dispatch point for physics-specific assembly. Each physics
type implements its own method.
# GPU-Friendly Design
The assembly loop must be GPU-compatible:
```julia
# CPU version (reference)
for element in elements
Kₑ, fₑ = assemble_element(physics, element, time)
add_to_global!(assembly, Kₑ, fₑ, element.gdofs)
end
# GPU version (future)
@cuda threads=256 assemble_kernel!(assembly, physics, elements, time)
```
Key principles:
- No heap allocations inside element loop
- All buffers pre-allocated
- Type-stable throughout
- Deterministic execution order (for GPU atomics)
"""
function assemble!(assembly, physics::AbstractPhysics, elements, time)
error("assemble! not implemented for $(typeof(physics))")
end