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, Shell, Solid), orheat_fluxandtemperaturesfor thermal models (TrussHeat, PlanarHeat, 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, differentiable_parameters=None)
¶
Solve the quasi-static finite-element problem by load increments.
Parameters:
-
increments(Tensor | None, default:None) –Load scale factors, 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, updated in place inside notebooks.
-
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. -
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.
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 |
✓ | ✓ | |
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.