General concepts » Solid mechanics

Finite-strain solid mechanics: kinematics, constitutive laws, and Newton solution.

Introduction

The Solid module implements finite-strain (hyperelastic) solid mechanics on top of the variational form language. It is organized exactly like the underlying mathematics:

LayerDirectoryContents
KinematicsSolid/Kinematics/Deformation measures and invariants at a point
Constitutive lawsSolid/Constitutive/Strain-energy densities and their derivatives
IntegratorsSolid/Integrators/The internal virtual work residual and tangent
Point plumbingSolid/Local/Constitutive-point inputs (state, fibers, activation)
PostprocessingSolid/Fields/Stress and strain fields for output
Linear theorySolid/Linear/Small-strain elasticity integrators

Working examples: examples/Solid/BlockGravity.cpp (quasi-static NeoHookean block under ramped gravity), examples/Solid/CantileverBeam.cpp, and examples/Solid/ActiveContractionPlaneWave.cpp (active fiber contraction driven by an activation wave).

Kinematics

With displacement $ u $ , the deformation gradient is $ F = I + \nabla_X u $ , the right Cauchy–Green tensor $ C = F^\top F $ , and the Green–Lagrange strain $ E = \tfrac12 (C - I) $ . Isotropic response is expressed through the invariants $ I_1 = \operatorname{tr} C $ , $ I_2 $ , $ I_3 = \det C = J^2 $ ; a fiber direction $ f_0 $ adds the anisotropic invariant $ I_{4f} = f_0 \cdot C f_0 $ (squared fiber stretch).

These live in Solid/Kinematics/ (KinematicState, Invariants) and are computed at constitutive points — laws never see meshes or quadrature, only local state.

Constitutive laws

A hyperelastic law is a strain-energy density $ W $ ; stress and tangent follow by differentiation: $ P = \partial W / \partial F $ , $ \mathbb{C} = \partial^2 W / \partial F^2 $ . All laws derive from HyperElasticLaw and return, at a constitutive point, the stress and the consistent tangent.

LawEnergyRegime / notes
Hookequadratic in $ \varepsilon $ Linear elasticity
SaintVenantKirchhoffquadratic in $ E $ Large rotations, small strains; loses ellipticity in strong compression (a property of the model, not a bug)
NeoHookean $ I_1 $ -based, compressibleGeneral-purpose rubber-like
MooneyRivlin $ I_1, I_2 $ -basedRubber with second-invariant sensitivity
HolzapfelOgdenisotropic matrix + exponential fiber term in $ I_{4f} $ Anisotropic soft tissue, one fiber family
ActiveFiberLaw1D fiber element with internal stateActive (muscle) contraction
ActiveContraction<Passive, Active>passive law + active fiber lawComposition wrapper

Constructing a law and the internal virtual work form (from examples/Solid/BlockGravity.cpp):

Solid::NeoHookean law(lambda, mu);
auto ivw = Solid::InternalVirtualWork(law, u);   // u: current displacement GridFunction

The weak form and Newton's method

Static equilibrium in weak form is

\[ R(u; v) = \int_{\Omega_0} P(F) : \nabla_X v \; dX - \ell_{ext}(v) = 0 , \]

and Newton's method solves, at each iterate, $ R'(u)(\delta u, v) = -R(u; v) $ . The tangent $ R' $ contains both the material stiffness (from $ \partial^2 W/\partial C^2 $ ) and the geometric (initial-stress) stiffness; omitting the latter forfeits quadratic convergence. InternalVirtualWork packages residual and tangent together — the expression ivw(du, v) contributes both to the Newton problem:

// Newton linearization:  K δu = -F_int(u) + F_ext
Problem newton(du, v);
newton = ivw(du, v)
       - Integral(bodyForce, v)
       + DirichletBC(du, zero).on(bottomBC);

SparseLU linearSolver(newton);
NewtonSolver solver(linearSolver);
solver.setMaxIterations(50)
      .setAbsoluteTolerance(1e-10)
      .setRelativeTolerance(1e-8);
solver.solve(u);      // iterates du-solves, accumulating into u

Note the structure: the increment $ \delta u $ is the trial function; the Dirichlet condition on $ \delta u $ is homogeneous once $ u $ satisfies the constraint; loads are ramped in steps (incremental loading) because a full load applied to the undeformed state may lie outside Newton's basin of attraction.

Constitutive-point inputs

Laws that need more than kinematics (time step, activation, previous internal state, fiber directions) receive them through the constitutive point* mechanism (Solid/Local/ConstitutivePoint.h): the integrators stamp tags — cell index, quadrature-point index, and any user-relevant quantities — onto the point, and a user-supplied input callable maps tags to law inputs:

auto ivw = Solid::InternalVirtualWork(law, u).setInput(activeInput);

This keeps laws pure functions of local state (unit-testable against finite differences at a single point) and keeps integrators ignorant of constitutive details. Solid/Local/FiberKinematics.h carries preferred directions; fibers are material (reference-configuration) vectors — their pushforward $ F f_0 $ happens inside laws and fields, never in user setup.

Internal variables

Active laws evolve internal state (active extension, cross-bridge stiffness-like variables) by local ODEs. Two facts worth knowing:

  • The preferred architecture for internal variables in Rodin is to make them first-class fields (grid functions on discontinuous spaces, coupled into the global Newton residual) rather than hiding local solves and Schur-condensed tangents inside the law evaluation.
  • Quasi-static problems can latch: after activation ceases, a contracted configuration can itself be an equilibrium, so the model never relaxes (no sliding, no decay path). The remedies are a spontaneous-decay regularization parameter, or a genuinely dynamic formulation (inertia + Newmark-type integration) in which the elastic restoring force drives relaxation. If a quasi-static simulation "won't relax", check the formulation regime before suspecting the law.

Postprocessing fields

Solid/Fields/ provides stress/strain functions for output and coupling: FirstPiolaKirchhoffStress ( $ P $ ), CauchyStress ( $ \sigma = J^{-1} P F^\top $ ), GreenLagrangeStrain ( $ E $ ). They are ordinary form-language functions: project them onto an output space and write them with IO::XDMF.

A note on units

The laws are unit-agnostic; consistency is the user's contract. Mixing a Pa-scale modulus with kPa-scale loads or activation is a silent modeling error no assertion can catch — fix a unit system per example and state it in a comment near the parameters.

See also