Note
Go to the end to download the full example code.
Modular UMAT under Finite Strain — Hencky Hyperelasto-Plasticity
Runs the same composable "MODUL" material as MODUL.py, but under
finite strain (NLGEOM): control type "logarithmic" drives the logarithmic
strain / Kirchhoff stress conjugate pair, and the solver kinematics hand the
model the log strain of the actual deformation gradient. The elasticity block
then acts as a Hencky stored-energy function of ln V and the plasticity
mechanism rides additively on that measure — a genuine hyperelasto-plastic
model.
This holds only when the accumulated corotational strain is exactly ln V,
which is the log_R corate: corate="logarithmic_R" is REQUIRED for MODUL
under NLGEOM and any other corate raises a RuntimeError (a Jaumann- or
Green-Naghdi-integrated model would be hypoelastic and dissipate spuriously
in closed cycles).
The loading is a log-strain cycle +15% / -15% / 0 — genuinely finite stretches (lambda from 0.86 to 1.16) — exposing the elasto-plastic hysteresis loop in the (ln V, tau) work-conjugate plane.
Both the material and the loading path are built in Python, and the results come back in memory.
import matplotlib.pyplot as plt
from simcoon import solver
from simcoon.modular import (
ModularMaterial,
IsotropicElasticity,
Plasticity,
VonMisesYield,
VoceHardening,
)
plt.rcParams["figure.figsize"] = (14, 6)
1. Compose the constitutive model
Same composition as MODUL.py: isotropic elasticity (“Enu”: C1 = E, C2 = nu) + von Mises yield + Voce isotropic hardening.
mat = ModularMaterial(
elasticity=IsotropicElasticity(
C1=210000.0, C2=0.3, alpha=1.2e-5, convention="Enu"
),
mechanisms=[
Plasticity(
sigma_Y=300.0,
yield_criterion=VonMisesYield(),
isotropic_hardening=VoceHardening(Q=200.0, b=10.0),
),
],
)
print(mat.summary())
ModularMaterial:
Elasticity: IsotropicElasticity
C1=210000.0, C2=0.3, alpha=1.2e-05 [ENU]
Mechanisms (1):
[0] Plasticity
sigma_Y=300.0
yield: VonMisesYield
iso_hard: VoceHardening
kin_hard: NoKinematicHardening
nprops=15, nstatev=8
2. Build the loading path
Three steps driving the axial log strain to +15%, back to -15% and finally to 0, the five other components held stress-free (uniaxial tension / compression). Each step covers 1 s in 100 increments, with adaptive sub-stepping down to Dn_mini = 1e-3 of an increment.
steps = [
solver.StepMeca(
control=["strain"] + ["stress"] * 5,
value=[target, 0, 0, 0, 0, 0],
time=1.0,
ninc=100,
Dn_mini=1.0e-3,
)
for target in (0.15, -0.15, 0.0)
]
3. Run the solver under NLGEOM
control_type="logarithmic" is the finite-strain (log strain / Kirchhoff
stress) control, and log_R is the only hyper/hypo-consistent corate for
MODUL. It is also the solver default, but spelled out here because the
model depends on it.
res = solver.solve(
solver.Block(steps=steps, control_type="logarithmic"),
mat.umat_name,
mat.props,
mat.nstatev,
T_init=293.0,
corate="logarithmic_R",
)
4. Plot the response
The in-memory results carry every stress and strain measure the solver
integrated, so the model’s own work-conjugate pair is read directly:
LogStrain is ln V and Kirchhoff is tau.
e11 = res["LogStrain"][0]
tau11 = res["Kirchhoff"][0]
Wm, Wm_r, _, Wm_d = res["Wm"]
fig = plt.figure()
ax1 = fig.add_subplot(1, 2, 1)
plt.grid(True)
plt.tick_params(axis="both", which="major", labelsize=13)
plt.xlabel(r"Log strain $(\ln \mathbf{V})_{11}$", size=14)
plt.ylabel(r"Kirchhoff stress $\tau_{11}$ (MPa)", size=14)
plt.plot(e11, tau11, c="royalblue", lw=1.5,
label="MODUL, NLGEOM logarithmic, corate log_R")
plt.axhline(y=300.0, color="0.6", linestyle="--", lw=0.8,
label=r"initial $\sigma_Y$")
plt.axhline(y=-300.0, color="0.6", linestyle="--", lw=0.8)
plt.legend(loc="best")
plt.title("Finite-strain hysteresis loop")
ax2 = fig.add_subplot(1, 2, 2)
plt.grid(True)
plt.tick_params(axis="both", which="major", labelsize=13)
plt.xlabel("time (s)", size=14)
plt.ylabel("Work (MPa)", size=14)
plt.plot(res["Time"], Wm, c="black", label=r"$W_m$ (total)")
plt.plot(res["Time"], Wm_r, c="green", label=r"$W_m^r$ (recoverable)")
plt.plot(res["Time"], Wm_d, c="red", label=r"$W_m^d$ (dissipated)")
plt.legend(loc="best")
plt.title("Energy decomposition")
plt.tight_layout()
plt.savefig("MODUL_finite_stress_strain.png", dpi=120)
plt.show()

Total running time of the script: (0 minutes 0.447 seconds)