Models¶
A FEM model combines a mesh (nodes and elements) with a material to form a solvable finite-element problem. All models share the same workflow:
- Create the model from nodes, elements, and a material. Several models can be combined as one system in an Assembly and coupled by kinematic constraints.
- Apply loads and boundary conditions by setting entries of the model attributes:
forcesanddisplacementsfor mechanics models (Truss, Planar, Axisymmetric, Shell, Solid), orheat_fluxandtemperaturesfor thermal models (TrussHeat, PlanarHeat, AxisymmetricHeat, ShellHeat, SolidHeat). Prescribed values are activated by setting the corresponding entries of the boolean maskconstraintstoTrue. A distributed load, such as gravity or a pressure, becomes nodal values with the load integrators below. - Solve with
solve(), which returns the nodal solution, the internal nodal forces, and the flux, gradient, and material state at the elements. - Postprocess the resulting tensors, e.g. with
plot().
See Getting Started for a worked example.
All models inherit their construction and solution interface from the abstract base class FEM:
FEM¶
Bases: ABC
Abstract base class for all finite-element models.
A model is defined by nodal coordinates, an element connectivity, and a
material. Loads and boundary conditions are set through attributes of the
concrete model classes, and the quasi-static solution is computed with
solve().
Attributes:
-
nodes–Nodal coordinates with shape [n_nod, n_dim].
-
elements–Element connectivity with shape [n_elem, nodes_per_element].
-
material(Material | None) –Vectorized material model (or None for laminate shells).
-
constraints(Tensor) –Boolean mask of constrained degrees of freedom with shape [n_nod, n_dof_per_node].
-
n_nod–Number of nodes.
-
n_elem–Number of elements.
-
n_dofs–Total number of degrees of freedom.
__init__(nodes, elements, material)
¶
Initialize a finite-element model.
Parameters:
-
nodes(Tensor) –Nodal coordinates with shape [n_nod, n_dim].
-
elements(Tensor) –Connectivity with shape [n_elem, n_nodes_per_element].
-
material(Material | None) –Material model. If not vectorized, it is vectorized over elements during initialization. May be
Nonefor shells that use a laminate section instead.
solve(increments=None, max_iter=10, rtol=1e-08, atol=1e-06, stol=1e-10, cutback_factor=0.5, growth_factor=1.1, max_cutbacks=10, verbose=False, method=None, preconditioner=None, device=None, return_intermediate=False, aggregate_integration_points=True, alpha=0.0, hessian_modulus=None, differentiable_parameters=None)
¶
Solve the quasi-static finite-element problem by load increments.
Parameters:
-
increments(Tensor | None, default:None) –Load scale factors starting at 0, typically [0, 1]. They may rise and fall, so a load cycle is expressed as a sequence like [0, 1, 0]. Results are always returned at exactly these values. If a Newton solve does not converge, the increment is subdivided internally and retried, and the substep is grown again after each success.
-
max_iter(int, default:10) –Maximum Newton iterations before an increment is cut back.
-
rtol(float, default:1e-08) –Relative residual tolerance for Newton convergence.
-
atol(float, default:1e-06) –Absolute residual tolerance for Newton convergence.
-
stol(float, default:1e-10) –Tolerance used by iterative linear solvers.
-
cutback_factor(float, default:0.5) –Factor applied to the substep size after a Newton solve failed to converge.
-
growth_factor(float, default:1.1) –Factor applied to the substep size after a Newton solve converged, capped at the requested increment.
-
max_cutbacks(int, default:10) –Number of successive cutbacks accepted within an increment before the solve is given up.
-
verbose(bool, default:False) –If True, reports the solver configuration and a table of per-increment progress.
-
method(Literal['direct', 'cg', 'bicgstab'] | None, default:None) –Linear solver method, chosen by size and tangent symmetry when omitted.
-
preconditioner(Literal['amg', 'jacobi', 'none'] | None, default:None) –Preconditioner for an iterative method, chosen by device and available backends when omitted.
-
device(str | None, default:None) –Optional device hint for the linear solver backend.
-
return_intermediate(bool, default:False) –If True, returns values for all increments.
-
aggregate_integration_points(bool, default:True) –If True, averages flux, gradient, and state over integration points.
-
alpha(float, default:0.0) –Damping factor for viscous stabilization. Dissipated energy is accumulated in
self.stabilization_energy. -
hessian_modulus(Tensor | float | None, default:None) –Modulus k of the regularization energy ½ k ∇∇u ⋮ ∇∇u, a float or with shape [n_elem].
-
differentiable_parameters(Tensor | Iterable[Tensor] | None, default:None) –Explicit parameter(s) to differentiate through implicit Newton/sparse solves. Accepts either a single tensor or an iterable of tensors. A tensor that requires grad and enters the residual must be listed.
Returns:
-
tuple[Tensor, Tensor, Tensor, Tensor, Tensor]–Tuple of displacement, internal force, flux, gradient, and material state. If return_intermediate is True, each tensor includes an increment dimension as the leading axis.
integrate_field(field=None)
¶
Integrate a nodal scalar field over each element.
The measure is that of the mesh and excludes volume_scale, so a planar
model integrates over areas and a truss over lengths. Scaling to a volume
is left to the caller, which keeps this constant where a thickness or a
cross section is a design variable.
Parameters:
-
field(Tensor | None, default:None) –Nodal scalar values with shape [n_nod]. If None, integrates a unit field and therefore returns the measure of each element.
Returns:
-
Tensor–Per-element integral values with shape [n_elem].
Loads¶
A distributed load is integrated into consistent nodal loads, which are added to the forces of a mechanics model or the heat_flux of a thermal one:
model.forces += model.integrate_body_load([0.0, -rho * g])
Which integrators a model offers follows from the dimension of its elements:
| Model | Body | Surface | Line |
|---|---|---|---|
Truss, TrussHeat |
✓ | ||
Planar, PlanarHeat |
✓ | ✓ | |
Axisymmetric, AxisymmetricHeat |
✓ | ✓ | |
Shell, ShellHeat |
✓ | ✓ | ✓ |
Solid, SolidHeat |
✓ | ✓ |
integrate_body_load(load)
¶
Consistent nodal loads from a load per unit volume, e.g. gravity.
Parameters:
-
load(float | Tensor) –Load per unit volume as a float, with shape [k] if uniform, or with shape [n_elem, k] to vary it per element.
kis the number of loaded degrees of freedom per node, i.e.n_dimfor a force and 1 for a heat source.
Returns:
-
Tensor–Nodal loads with shape [n_nod, k], to be added to
forcesorheat_flux.
integrate_surface_load(mask, load)
¶
Consistent nodal loads from a load per unit area on a surface.
The surface is made up of the element faces whose nodes all lie in mask
and that are on the boundary of the mesh.
Parameters:
-
mask(Tensor) –Boolean nodal mask with shape [n_nod] selecting the surface.
-
load(float | Tensor) –Load per unit area. A float is a pressure acting along the outward normal, while shape [k] or [n_face, k] is a traction in global coordinates.
Returns:
-
Tensor–Nodal loads with shape [n_nod, k], to be added to
forcesorheat_flux.
A Shell element is its own surface, so Shell.integrate_surface_load(...) loads the elements in the mask rather than the boundary faces of the mesh.
integrate_line_load(mask, load)
¶
Consistent nodal loads from a load per unit length on a line.
The line is made up of the element edges whose nodes all lie in mask and
that are on the boundary of the mesh.
Parameters:
-
mask(Tensor) –Boolean nodal mask with shape [n_nod] selecting the line.
-
load(float | Tensor) –Load per unit length with shape [k] or [n_edge, k]. For a planar model a float is a pressure acting along the outward normal.
Returns:
-
Tensor–Nodal loads with shape [n_nod, k], to be added to
forcesorheat_flux.
An Axisymmetric model carries the revolution in every measure, so a body load is per unit volume of the revolved solid and an edge load is per unit area of the surface it sweeps. Both integrate to the load on the full circumference, not per radian.
A thermal model also exchanges heat with its surroundings through a boundary surface or line, which convection(...) selects like a load but applies as a condition on the temperature.
convection(mask, h, T_inf)
¶
Add convection to ambient, q = h (T - T_inf), on a boundary surface.
The surface is selected like a surface or line load, and a planar edge includes the thickness. Each call adds a surface, applied in full at every increment.
Parameters: