Postulates

  1. The position of the body is described by
    1. a (time-dependent) map ,
    2. where has a time-independent mass density and
    3. has a time-independent metric .
      1. It is a covariant symmetric 2-tensor, with 2 open covector slots.
      2. Physically, it is just the dot product.
  2. The elastic potential energy for a map takes the form: for some fiber-wise (nonlinear) mapping (depending on material) i.e. the potential is only a function of the induced metric encoding its notion of distances in the world. (Frame-indifference)

Terminology

The deformation gradient is defined by the pushforward :

In index notation, it is . If (a material tangent vector) then . We can think of as a row index and as a column index, such that pushes it forward via matrix multiplication.

Idea

is a linear map (since it lives in the bundle over a tensor algebra) that “eats” a material vector and spits out a world vector.

Since we want a measurement tool for material space (to measure deformation without having to constantly reference the world space ), we need to pullback. Indeed, the adjoint is the pullback.

The induced metric can be understood by:

"\\usepackage{tikz}\n\\usetikzlibrary{arrows.meta}\n\n\\begin{document}\n\\begin{tikzpicture}[>=stealth]\n\n% --- Color Definitions ---\n\\definecolor{blueNode}{RGB}{45, 85, 165}\n\\definecolor{purpleNode}{RGB}{115, 55, 155}\n\n% --- Core Nodes ---\n\\node (TL) at (0, 2) {\\LARGE $\\textcolor{blueNode}{T_pM}$};\n\\node (TR) at (4, 2) {\\LARGE $\\textcolor{purpleNode}{T_{\\phi(p)}W}$};\n\\node (BR) at (4, 0) {\\LARGE $\\textcolor{purpleNode}{T_{\\phi(p)}^*W}$};\n\\node (BL) at (0, 0) {\\LARGE $\\textcolor{blueNode}{T_p^*M}$};\n\n% --- Left Equation Node ---\n\\node (EQ) at (-1.5, 1) {\\LARGE $F^* \\textcolor{purpleNode}{\\flat_W} F = \\phi^* \\textcolor{purpleNode}{\\flat_W}$};\n\n% --- Arrows ---\n% Top arrow (F)\n\\draw[->, very thick] (TL) -- (TR) node[midway, above, yshift=4pt] {\\Large $F$};\n\n% Right arrow (flat_W)\n\\draw[->, very thick] (TR) -- (BR) node[midway, right, xshift=4pt] {\\Large $\\textcolor{purpleNode}{\\flat_W}$};\n\n% Bottom arrow (F^*)\n\\draw[->, very thick] (BR) -- (BL) node[midway, above, yshift=4pt] {\\Large $F^*$};\n\n% Dashed curved arrow\n% Bulges to the right using explicit departure and arrival angles\n\\draw[->, very thick, dashed] (TL) to[out=-40, in=40] (BL);\n\n\\end{tikzpicture}\n\\end{document}"TpMTÁ(p)WT¤Á(p)WT¤pMF¤[WF=Á¤[WF[WF¤
source code

The induced metric is called the (right)-Cauchy-Green tensor.

Understanding the Diagram: How to Measure Deformed Length

The diagram illustrates how the Cauchy-Green tensor operates by composing three separate maps:

  1. (Deformation Gradient): We start with a small undeformed vector in the material tangent space . pushes this vector forward into the physical world , telling us what the deformed vector looks like in the spatial tangent space .
  2. (World Metric): Once in the physical world, you use the standard metric to measure its length (or compute the dot product with another vector). This mathematically “lowers the index”, mapping the spatial vector to a spatial covector in .
  3. (Pullback): Finally, the adjoint pulls that measurement result back to the material covector space .

By taking the direct dashed path , we skip the intermediate steps. acts as an “induced metric” living entirely in the undeformed material space. If we feed two undeformed material vectors, it instantly outputs what their dot product will be after they are stretched and twisted into the physical world.

Another Perspective

Another way to think about this is, suppose we had , two infinitesimal material vectors. We want to build a machine (tesnor) that lives entirely in , but want to measure the dot product of after deformation in world space .

Let us call this machine , where . The dot product, represents the distance/length/angle of the two material vectors.

First, we simulate deformation. Then we measure them.

This giving us our operator.

In 3D Cartesian coordinates:

  • Let denote the Cartesian coordinate for and the Cartesian coordinate for .
  • Flow map
  • Deformation gradient
  • Right Cauchy-Green tensor .
    • Obviously, the adjoint is the transpose, so .
    • because it takes the inner product (dot product) of the basis vectors. Since the basis vectors in are orthogonal to each other, it forms the identity matrix.

Deriving Elastic Force

Back to our potential energy, given some pointwise nonlinear elastic model :

The elastic force is the negative gradient of the potential energy: . The assumption of elasticity is that the total potential energy is only a function of the Cauchy-Green tesnor in the above form.

In particular, at each is a -form-valued (measures energy density) function of a metric.

To compute this derivative, we can view the energy functional as a sequence of mappings and apply the chain rule. In the context of fields, this is essentially continuous backpropagation.

1. The Global Sequence of Maps

First, we can express the entire energy functional as a sequence of macroscopic maps from the space of all possible flow maps to the final scalar energy:

2. The Tangent Space Forward Pass (Kinematics)

Consider a small perturbation to the flow map , which gives us a velocity field . How does this velocity propagate through our quantities?

  • : Takes the velocity field and computes its spatial gradient (giving the perturbation to the deformation gradient ).
  • : Maps the change in to the change in the Cauchy-Green tensor (where ).
  • : Maps the change in the metric/strain to a change in the local energy density (an -form).
  • : Integrates the energy density to get the total change in scalar energy .

3. The Backward Pass (Forces and Stresses)

To find the force, we need the derivative . We start from the scalar energy output (the number ) and pull it backward through the adjoint/dual of each linear map:

As we pull back the scalar "", it transforms into different physical stress quantities at each intermediate space.

Actual Model

Let be a fixed reference metric. This is the geometry of the material in its undeformed, relaxed state (the rest length). Let be the deformation tensor. In particular,

Recall that is the current deformed lengths of the material vectors. The operator “divides by the resting length”. So, .

We have some different ways to model strain:

Write , where we separate the energy density into a function of the strain and the mass density. represents the energy per unit mass, or the specific internal energy.

St Venant-Kirchhoff Model

See here for derivation of the StVK model.

Terminology (Stress Tensors)

By defining names for the intermediate states of the pullback, we recover the classic stress tensors of continuum mechanics!

2nd Piola-Kirchhoff Stress

When the pullback reaches the space dual to the Cauchy-Green tensor , we get the 2nd Piola-Kirchhoff stress:

  • Physical Meaning: This stress lives entirely in the undeformed material space (). It is the energetic conjugate to the metric . If you change the deformed lengths of the material (change ), tells you how much work is done.

1st Piola-Kirchhoff Stress Tensor

Pulling back one step further to the space dual to the deformation gradient , we get the 1st Piola-Kirchhoff stress:

  • Physical Meaning: This is a two-point tensor. It eats a material -form (an area element in the undeformed state) and outputs the actual physical force vector in the world space . This is the mathematical formalization of “Force per unit undeformed area”.
  • Notice its type: . It’s an -form valued in world covectors!

Finally, pulling back through the spatial divergence yields the actual elastic force density .

Force Evaluation

The formal derivation of all the above physics is quite long. We distill the math into the following pipeline for simulation:

The input is , or a map of the current node positions.

  1. Evaluate
    1. The spatial derivative of the current node positions relative to the original rest positions.
  2. , the right Cauchy-Green stress tensor.
  3. , the St Venant Strain. Since in our orthonormal frame, then .
  4. , the 2nd Piola-Kirchhoff Stress-Strain relationship
  5. , the 1st Piola-Kirchhoff Stress Tensor,
  6. , the force output that accelerates the nodes in the next step.

Finite Element Simulation

We can discretize the deformable body by triangle mesh (2D) or tetrahedral mesh (3D), where . Each vertex stores a fixed rest position (material coordinate) and a variable world position (representing value of flow map, .

"\\usepackage{tikz}\n\n\\begin{document}\n\\begin{tikzpicture}\n\n% --- Color Definitions ---\n% Matched to the reference image for clarity\n\\definecolor{meshBlue}{RGB}{50, 100, 175}\n\\definecolor{meshPurple}{RGB}{135, 75, 175}\n\n% =====================================================\n% Left Mesh: Reference Configuration (\\mathbf{X}_i)\n% =====================================================\n\\begin{scope}[shift={(0,0)}]\n % -- Coordinate Mapping --\n \\coordinate (X0) at (0, 0); % Center\n \\coordinate (X1) at (-1.3, 1.0); % Top Left\n \\coordinate (X2) at (0.2, 1.6); % Top\n \\coordinate (X3) at (1.5, 0.5); % Right\n \\coordinate (X4) at (1.4, -1.0); % Bottom Right\n \\coordinate (X5) at (-0.1, -1.7); % Bottom\n \\coordinate (X6) at (-1.4, -0.7); % Bottom Left\n\n % -- Background: Edges --\n % Perimeter\n \\draw[meshBlue, thick] (X1) -- (X2) -- (X3) -- (X4) -- (X5) -- (X6) -- cycle;\n % Internal Spokes\n \\draw[meshBlue, thick] (X0) -- (X1);\n \\draw[meshBlue, thick] (X0) -- (X2);\n \\draw[meshBlue, thick] (X0) -- (X3);\n \\draw[meshBlue, thick] (X0) -- (X4);\n \\draw[meshBlue, thick] (X0) -- (X5);\n \\draw[meshBlue, thick] (X0) -- (X6);\n\n % -- Foreground: Nodes --\n \\fill[meshBlue] (X1) circle (0.12);\n \\fill[meshBlue] (X2) circle (0.12);\n \\fill[meshBlue] (X3) circle (0.12);\n \\fill[meshBlue] (X4) circle (0.12);\n \\fill[meshBlue] (X5) circle (0.12);\n \\fill[meshBlue] (X6) circle (0.12);\n \\fill[meshBlue] (X0) circle (0.12);\n\n % -- Label --\n \\node[meshBlue, font=\\Large] at (-0.3, 0.6) {$\\mathbf{X}_i$};\n\\end{scope}\n\n% =====================================================\n% Right Mesh: Current/Deformed Configuration (\\mathbf{x}_i)\n% =====================================================\n\\begin{scope}[shift={(8,0)}] % Shifted to the right\n % -- Coordinate Mapping (Stretched/Deformed) --\n \\coordinate (x0) at (0, 0); % Center\n \\coordinate (x1) at (-2.2, 0.9); % Top Left\n \\coordinate (x2) at (-0.2, 1.4); % Top\n \\coordinate (x3) at (1.8, 0.5); % Right\n \\coordinate (x4) at (1.5, -0.9); % Bottom Right\n \\coordinate (x5) at (-0.7, -1.5); % Bottom\n \\coordinate (x6) at (-2.4, -0.6); % Bottom Left\n\n % -- Background: Edges --\n % Perimeter\n \\draw[meshPurple, thick] (x1) -- (x2) -- (x3) -- (x4) -- (x5) -- (x6) -- cycle;\n % Internal Spokes\n \\draw[meshPurple, thick] (x0) -- (x1);\n \\draw[meshPurple, thick] (x0) -- (x2);\n \\draw[meshPurple, thick] (x0) -- (x3);\n \\draw[meshPurple, thick] (x0) -- (x4);\n \\draw[meshPurple, thick] (x0) -- (x5);\n \\draw[meshPurple, thick] (x0) -- (x6);\n\n % -- Foreground: Nodes --\n \\fill[meshPurple] (x1) circle (0.12);\n \\fill[meshPurple] (x2) circle (0.12);\n \\fill[meshPurple] (x3) circle (0.12);\n \\fill[meshPurple] (x4) circle (0.12);\n \\fill[meshPurple] (x5) circle (0.12);\n \\fill[meshPurple] (x6) circle (0.12);\n \\fill[meshPurple] (x0) circle (0.12);\n\n % -- Label --\n \\node[meshPurple, font=\\Large] at (-0.3, 0.4) {$\\mathbf{x}_i$};\n\\end{scope}\n\n\\end{tikzpicture}\n\\end{document}"Xixi
source code

The data on the vertices can be linearly interpolated into a piecewise linear deformation map

"\\usepackage{tikz}\n\n\\begin{document}\n\\begin{tikzpicture}\n\n% --- Color Definitions ---\n% Reference Mesh (Left)\n\\definecolor{meshBlue}{RGB}{45, 95, 175}\n\\definecolor{pointBlue}{RGB}{135, 185, 255}\n\n% Current Mesh (Right)\n\\definecolor{meshPurple}{RGB}{135, 65, 175}\n\\definecolor{pointPurple}{RGB}{200, 160, 220}\n\n% =====================================================\n% Left Mesh: Reference Configuration\n% =====================================================\n\\begin{scope}[shift={(0,0)}]\n % -- Coordinate Mapping --\n \\coordinate (X1) at (0, 0); % Center\n \\coordinate (Xn) at (-1.3, 1.0); % Top Left\n \\coordinate (X0) at (-1.5, -0.6); % Bottom Left\n \\coordinate (Xtop) at (0.2, 1.6); % Top\n \\coordinate (Xr) at (1.5, 0.5); % Right\n \\coordinate (Xbr) at (1.4, -1.0); % Bottom Right\n \\coordinate (Xbot) at (-0.1, -1.7); % Bottom\n \\coordinate (Y) at (-0.9, 0.1); % Interior point\n\n % -- Background: Edges --\n \\draw[meshBlue, thick] (Xn) -- (Xtop) -- (Xr) -- (Xbr) -- (Xbot) -- (X0) -- cycle;\n \\draw[meshBlue, thick] (X1) -- (Xn);\n \\draw[meshBlue, thick] (X1) -- (Xtop);\n \\draw[meshBlue, thick] (X1) -- (Xr);\n \\draw[meshBlue, thick] (X1) -- (Xbr);\n \\draw[meshBlue, thick] (X1) -- (Xbot);\n \\draw[meshBlue, thick] (X1) -- (X0);\n\n % -- Foreground: Nodes --\n \\fill[meshBlue] (Xn) circle (0.12);\n \\fill[meshBlue] (Xtop) circle (0.12);\n \\fill[meshBlue] (Xr) circle (0.12);\n \\fill[meshBlue] (Xbr) circle (0.12);\n \\fill[meshBlue] (Xbot) circle (0.12);\n \\fill[meshBlue] (X0) circle (0.12);\n \\fill[meshBlue] (X1) circle (0.12);\n\n % -- Interior Point --\n \\fill[pointBlue] (Y) circle (0.12);\n\n % -- Labels --\n \\node[meshBlue, font=\\Large, anchor=south east] at (-1.2, 1.1) {$\\mathbf{X}_n$};\n \\node[meshBlue, font=\\Large, anchor=east] at (-1.6, -0.5) {$\\mathbf{X}_0$};\n \\node[meshBlue, font=\\Large, anchor=south east] at (-0.1, 0.1) {$\\mathbf{X}_1$};\n \\node[pointBlue, font=\\Large, anchor=south] at (-1.1, 0.1) {$\\mathbf{Y}$};\n\\end{scope}\n\n% =====================================================\n% Right Mesh: Current Configuration\n% =====================================================\n\\begin{scope}[shift={(8,0)}] \n % -- Coordinate Mapping (Stretched/Deformed) --\n \\coordinate (x1) at (0, 0); % Center\n \\coordinate (xn) at (-2.2, 0.9); % Top Left\n \\coordinate (x0) at (-2.4, -0.6); % Bottom Left\n \\coordinate (xtop) at (-0.2, 1.4); % Top\n \\coordinate (xr) at (1.8, 0.5); % Right\n \\coordinate (xbr) at (1.5, -0.9); % Bottom Right\n \\coordinate (xbot) at (-0.7, -1.5); % Bottom\n \\coordinate (y) at (-1.5, 0.1); % Interior point\n\n % -- Background: Edges --\n \\draw[meshPurple, thick] (xn) -- (xtop) -- (xr) -- (xbr) -- (xbot) -- (x0) -- cycle;\n \\draw[meshPurple, thick] (x1) -- (xn);\n \\draw[meshPurple, thick] (x1) -- (xtop);\n \\draw[meshPurple, thick] (x1) -- (xr);\n \\draw[meshPurple, thick] (x1) -- (xbr);\n \\draw[meshPurple, thick] (x1) -- (xbot);\n \\draw[meshPurple, thick] (x1) -- (x0);\n\n % -- Foreground: Nodes --\n \\fill[meshPurple] (xn) circle (0.12);\n \\fill[meshPurple] (xtop) circle (0.12);\n \\fill[meshPurple] (xr) circle (0.12);\n \\fill[meshPurple] (xbr) circle (0.12);\n \\fill[meshPurple] (xbot) circle (0.12);\n \\fill[meshPurple] (x0) circle (0.12);\n \\fill[meshPurple] (x1) circle (0.12);\n\n % -- Interior Point --\n \\fill[pointPurple] (y) circle (0.12);\n\n % -- Labels --\n \\node[meshPurple, font=\\Large, anchor=south east] at (-2.2, 1.0) {$\\mathbf{x}_n$};\n \\node[meshPurple, font=\\Large, anchor=east] at (-2.5, -0.5) {$\\mathbf{x}_0$};\n \\node[meshPurple, font=\\Large, anchor=south east] at (-0.1, 0.1) {$\\mathbf{x}_1$};\n \\node[pointPurple, font=\\Large, anchor=west] at (-1.35, 0.2) {$\\mathbf{y}$};\n\\end{scope}\n\n\\end{tikzpicture}\n\\end{document}"XnX0X1Yxnx0x1y
source code

Consider some point inside an undeformed triangle/tetrahedron. We can express as a linear combination (weighted sum) of the triangle’s vertices . These are called the barycentric coordinates of . Two rules must be true:

  1. , such that the point is the weighted sum.
  2. The weights must sum to 1: . This ensures that the point is inside the triangle/tetrahedron.

The interior points must also not change the weights during deformation, i.e they must stay at the same position relative to the other world space particles. Thus,

By substitution, we get the above equation.

Deformation Gradient

The deformation gradient is a piecewise constant matrix

Let is the area normal of the opposite face of -th vertex and is the volume of the cell.

"\\usepackage{tikz}\n\\usetikzlibrary{arrows.meta}\n\n\\begin{document}\n\\begin{tikzpicture}\n\n% --- Color Definitions ---\n% Reference Mesh\n\\definecolor{meshBlue}{RGB}{45, 95, 175}\n\\definecolor{highlightBlue}{RGB}{170, 205, 245} % Light blue for edge A1\n\n% Current Mesh\n\\definecolor{meshPurple}{RGB}{135, 65, 175}\n\n% =====================================================\n% Left Mesh: Reference Configuration\n% =====================================================\n\\begin{scope}[shift={(0,0)}]\n % -- Coordinate Mapping --\n \\coordinate (X1) at (0, 0); % Center\n \\coordinate (Xn) at (-1.3, 1.0); % Top Left\n \\coordinate (X0) at (-1.5, -0.6); % Bottom Left\n \\coordinate (Xtop) at (0.2, 1.6); % Top\n \\coordinate (Xr) at (1.5, 0.5); % Right\n \\coordinate (Xbr) at (1.4, -1.0); % Bottom Right\n \\coordinate (Xbot) at (-0.1, -1.7); % Bottom\n\n % -- Background Highlight (Edge A1) --\n % Drawn first so the standard thin edge renders cleanly on top\n \\draw[highlightBlue, line width=6pt] (Xn) -- (X0);\n\n % -- Background: Edges --\n \\draw[meshBlue, thick] (Xn) -- (Xtop) -- (Xr) -- (Xbr) -- (Xbot) -- (X0) -- cycle;\n \\draw[meshBlue, thick] (X1) -- (Xn);\n \\draw[meshBlue, thick] (X1) -- (Xtop);\n \\draw[meshBlue, thick] (X1) -- (Xr);\n \\draw[meshBlue, thick] (X1) -- (Xbr);\n \\draw[meshBlue, thick] (X1) -- (Xbot);\n \\draw[meshBlue, thick] (X1) -- (X0);\n\n % -- Normal Vector (n1) --\n % Originating from the exact midpoint of X0 and Xn (-1.4, 0.2)\n \\draw[meshBlue, thick, -{Stealth[scale=1.2]}] (-1.4, 0.2) -- (-2.0, 0.3);\n\n % -- Foreground: Nodes --\n \\fill[meshBlue] (Xn) circle (0.12);\n \\fill[meshBlue] (Xtop) circle (0.12);\n \\fill[meshBlue] (Xr) circle (0.12);\n \\fill[meshBlue] (Xbr) circle (0.12);\n \\fill[meshBlue] (Xbot) circle (0.12);\n \\fill[meshBlue] (X0) circle (0.12);\n \\fill[meshBlue] (X1) circle (0.12);\n\n % -- Labels --\n \\node[meshBlue, font=\\Large, anchor=south east] at (-1.2, 1.1) {$\\mathbf{X}_n$};\n \\node[meshBlue, font=\\Large, anchor=east] at (-1.6, -0.5) {$\\mathbf{X}_0$};\n \\node[meshBlue, font=\\Large, anchor=south east] at (0.15, 0.3) {$\\mathbf{X}_1$};\n \n % New geometric labels\n \\node[highlightBlue, font=\\Large, anchor=east] at (-1.4, 0.7) {$A_1$};\n \\node[meshBlue, font=\\Large, anchor=east] at (-2.1, 0.3) {$\\mathbf{n}_1$};\n \\node[meshBlue, font=\\Large] at (-0.7, 0.1) {c};\n\\end{scope}\n\n% =====================================================\n% Right Mesh: Current Configuration\n% =====================================================\n\\begin{scope}[shift={(8,0)}] \n % -- Coordinate Mapping (Stretched/Deformed) --\n \\coordinate (x1) at (0, 0); % Center\n \\coordinate (xn) at (-2.2, 0.9); % Top Left\n \\coordinate (x0) at (-2.4, -0.6); % Bottom Left\n \\coordinate (xtop) at (-0.2, 1.4); % Top\n \\coordinate (xr) at (1.8, 0.5); % Right\n \\coordinate (xbr) at (1.5, -0.9); % Bottom Right\n \\coordinate (xbot) at (-0.7, -1.5); % Bottom\n\n % -- Background: Edges --\n \\draw[meshPurple, thick] (xn) -- (xtop) -- (xr) -- (xbr) -- (xbot) -- (x0) -- cycle;\n \\draw[meshPurple, thick] (x1) -- (xn);\n \\draw[meshPurple, thick] (x1) -- (xtop);\n \\draw[meshPurple, thick] (x1) -- (xr);\n \\draw[meshPurple, thick] (x1) -- (xbr);\n \\draw[meshPurple, thick] (x1) -- (xbot);\n \\draw[meshPurple, thick] (x1) -- (x0);\n\n % -- Foreground: Nodes --\n \\fill[meshPurple] (xn) circle (0.12);\n \\fill[meshPurple] (xtop) circle (0.12);\n \\fill[meshPurple] (xr) circle (0.12);\n \\fill[meshPurple] (xbr) circle (0.12);\n \\fill[meshPurple] (xbot) circle (0.12);\n \\fill[meshPurple] (x0) circle (0.12);\n \\fill[meshPurple] (x1) circle (0.12);\n\n % -- Labels --\n \\node[meshPurple, font=\\Large, anchor=south east] at (-2.2, 1.0) {$\\mathbf{x}_n$};\n \\node[meshPurple, font=\\Large, anchor=east] at (-2.5, -0.5) {$\\mathbf{x}_0$};\n \\node[meshPurple, font=\\Large, anchor=south east] at (-0.1, 0.1) {$\\mathbf{x}_1$};\n\\end{scope}\n\n\\end{tikzpicture}\n\\end{document}"XnX0X1A1n1cxnx0x1
source code

Then the deformation for that cell is

Now, in each cell we have deformation gradient . We

To compute the force on a vertex , we take the adjoint of the gradient. The differential of with respect to is

where are all the vertices of cell . We get a tiny perturbation of a vertex as . The resulting change is the change in the deformation gradient . The adjoint of this gradient accumulates the traction force to the vertices:

where is the dimension of the cell (3 for tetrahedra, 2 for triangles).

We also can calculate the total mass of each vertex by approximating the mass of each cell and distributing it to the vertices:

or the lumped mass.

Time Integration

For external forces

We can use RK4 or symplectic Euler method. We just need to evaluate the force given current positions at each time step. The step size is .

Or implicit Euler (with incremental potential):

We can use gradient descent or Newton’s method.

  • Need evalulation of .
  • Need evaluation of differential of potential (same as force evaluation)
  • Need an (approximate) Hessian for the potential.
    • The Laplacian can serve as one
    • is the step to get .