Finite strain¶

Open In Colab Binder

We compare planar strain and planar stress models employing a Neo-Hookean hyperelastic material model.

In [1]:
import matplotlib.pyplot as plt
import numpy as np
import torch

from torchfem.data import get_data
from torchfem.io import import_mesh
from torchfem.materials import HyperelasticPlaneStrain, HyperelasticPlaneStress

# Set default data type to double precision
torch.set_default_dtype(torch.float64)

# Dimensions (thickness of plate t and edge length L)
t = 0.01
L = 0.2

# Material parameters
G = 0.5
D = 1.0

# Incremental loading
increments = torch.linspace(0.0, 1.0, 21)
DU = 0.05

Neo-Hookean strain energy¶

The strain energy function for this Neo-Hookean material is chosen as

$$ \psi(C) = G \left( \textrm{tr}(\bar{C}) - 3 \right) + \frac{1}{D} \left( J - 1 \right)^2 $$

with the deviatoric right Cauchy-Green tensor $$ \bar{C} = J^{-\frac{2}{3}}F^\top F $$

and the Jacobian $$ J = \det(F). $$

In [2]:
def psi(F, params):
    """Neo-Hookean strain energy density function."""
    # Extract material parameters
    G = params[0]
    D = params[1]
    # Compute right Cauchy-Green tensor
    C = F.transpose(-1, -2) @ F
    # Stable computation of Jacobian
    J = torch.exp(0.5 * torch.logdet(C))
    C_bar = C * J ** (-2.0 / 3.0)
    return G * (torch.trace(C_bar) - 3.0) + 1 / D * (J - 1) ** 2

Plane strain¶

The strain energy is formulated as regular 3D version, the plane strain condition $F_{33}=1$ is ensured in the HyperelasticPlaneStrain material class by constructing an appropriate 3D deformation gradient.

In [3]:
# Material model (plane strain)
material = HyperelasticPlaneStrain(psi, params=[G, D])

# Import mesh
plate = import_mesh(get_data("plate_hole.vtk"), material, thickness=t)

# Fixed boundary at left end
left = plate.nodes[:, 0] < 1e-6
plate.constraints[left] = True

# Load at right end
right = plate.nodes[:, 0] > L - 1e-6
plate.constraints[right] = True
plate.displacements[right, 0] = DU

# Solve
u, f, σ, F, _ = plate.solve(
    increments=increments, return_intermediate=True, nlgeom=True, atol=1e-8
)

# Post-process
C = F.transpose(-1, -2) @ F
ev, Q = torch.linalg.eigh(C)
LE = 0.5 * Q @ torch.diag_embed(torch.log(ev)) @ Q.transpose(-1, -2)
In [4]:
ref_data = np.array(
    [
        [0.000, 0.00],
        [0.028, 0.08],
        [0.055, 0.16],
        [0.081, 0.23],
        [0.107, 0.31],
        [0.132, 0.38],
        [0.157, 0.45],
        [0.181, 0.52],
        [0.204, 0.58],
        [0.227, 0.65],
        [0.250, 0.71],
        [0.272, 0.77],
        [0.293, 0.83],
        [0.314, 0.89],
        [0.335, 0.95],
        [0.356, 1.01],
        [0.376, 1.07],
        [0.395, 1.12],
        [0.415, 1.18],
        [0.434, 1.24],
        [0.452, 1.29],
    ]
)

fig, ax = plt.subplots(1, 3, figsize=(15, 5))
ax[0].plot(ref_data[:, 1], ".-", color="gray", label="ABAQUS")
ax[0].plot(σ[:, 405, 0, 0], ".--", color="deeppink", label="torch-fem")
ax[0].grid()
ax[0].title.set_text("Element 405")
ax[0].set_ylabel("Stress (σ_xx)")
ax[0].legend()
ax[1].plot(ref_data[:, 0], ".-", color="gray", label="ABAQUS")
ax[1].plot(LE[:, 405, 0, 0], ".--", color="deeppink", label="torch-fem")
ax[1].grid()
ax[1].title.set_text("Element 405")
ax[1].set_ylabel("Logarithmic strain (LE_xx)")
ax[1].legend()
ax[1].set_xlabel("Iterations")

plate.plot(
    u=u[-1],
    node_property=u[-1, :, 1],
    cmap="coolwarm",
    title="Displacement (y-direction)",
    ax=ax[2],
)
plt.tight_layout()
plt.show()
No description has been provided for this image

Plane stress¶

In [5]:
# Material model (plane stress)
material = HyperelasticPlaneStress(psi, params=[G, D])

# Import mesh
plate = import_mesh(get_data("plate_hole.vtk"), material, thickness=t)

# Fixed boundary at left end
left = plate.nodes[:, 0] < 1e-6
plate.constraints[left] = True

# Load at right end
right = plate.nodes[:, 0] > L - 1e-6
plate.constraints[right] = True
plate.displacements[right, 0] = DU
In [6]:
# Solve
u, f, σ, F, state = plate.solve(
    increments=increments, return_intermediate=True, nlgeom=True, atol=1e-8
)

# Post-process (correcting Cauchy stress computation with out-of-plane stretch)
C = F.transpose(-1, -2) @ F
ev, Q = torch.linalg.eigh(C)
LE = 0.5 * Q @ torch.diag_embed(torch.log(ev)) @ Q.transpose(-1, -2)
σ = σ / (1 + state[..., 0, None, None])
In [7]:
ref_data = np.array(
    [
        [0.000, 0.00],
        [0.028, 0.07],
        [0.054, 0.14],
        [0.081, 0.21],
        [0.106, 0.28],
        [0.131, 0.34],
        [0.155, 0.41],
        [0.179, 0.47],
        [0.202, 0.53],
        [0.224, 0.59],
        [0.246, 0.65],
        [0.268, 0.71],
        [0.289, 0.77],
        [0.309, 0.83],
        [0.329, 0.89],
        [0.349, 0.94],
        [0.369, 1.00],
        [0.388, 1.05],
        [0.406, 1.11],
        [0.425, 1.16],
        [0.442, 1.22],
    ]
)

fig, ax = plt.subplots(1, 3, figsize=(15, 5))
ax[0].plot(ref_data[:, 1], ".-", color="gray", label="ABAQUS")
ax[0].plot(σ[:, 405, 0, 0], ".--", color="deeppink", label="torch-fem")
ax[0].grid()
ax[0].title.set_text("Element 405")
ax[0].set_ylabel("Stress (σ_xx)")
ax[0].legend()
ax[1].plot(ref_data[:, 0], ".-", color="gray", label="ABAQUS")
ax[1].plot(LE[:, 405, 0, 0], ".--", color="deeppink", label="torch-fem")
ax[1].grid()
ax[1].title.set_text("Element 405")
ax[1].set_ylabel("Logarithmic strain (LE_xx)")
ax[1].legend()
ax[1].set_xlabel("Iterations")

plate.plot(
    u=u[-1],
    node_property=u[-1, :, 1],
    cmap="coolwarm",
    title="Displacement (y-direction)",
    ax=ax[2],
)

plate.plot(
    u=u[-1],
    element_property=state[-1, :, 0] + 1,
    cmap="viridis",
    title="Out-of-plane stretch (λ_z)",
    ax=ax[2],
    colorbar=True,
)
plt.tight_layout()
plt.show()
No description has been provided for this image