Rate-independent metal plasticity integrated the way finite-element codes do it at every integration point: a trial elastic step, a check of the yield function, and an implicit return to the yield surface. Two models, both in plain NumPy:
- 1D with combined linear isotropic + linear kinematic hardening, under stress or strain control, with a time-step convergence study.
- 3D J2 (von Mises) with Armstrong-Frederick nonlinear kinematic hardening and power-law isotropic hardening, under strain control, applied to a thin-walled tube in combined axial-torsion loading — the classic test for nonproportional cyclic plasticity.
pip install numpy scipy matplotlib
python run_examples.py # every figure below, ~1 minute
python j2_plasticity.py # built-in physics checksTip
Both models are small classes with one integrate(path) method. To try
your own material or loading, change the constructor arguments or hand
integrate a different strain history — a (steps, 3, 3) array of
strain tensors for the 3D model, a 1D array for the 1D model.
Yield function
| Stress-controlled cycling | Time-step convergence |
|---|---|
![]() |
![]() |
The middle panel shows the discipline of the scheme:
The Armstrong-Frederick "dynamic recovery" term
Implicit update. Backward Euler on the backstress gives
so the flow direction is known once
Important
This is solved with a bracketed root finder rather than Newton's
method. The power-law term has derivative
Note
Stresses and strains are stored as 3×3 tensors, not 6-component Voigt vectors. This avoids the classic trap where a Euclidean norm of a Voigt vector under-counts the shear components by a factor of 2.
A tube under axial strain integrate(..., free_lateral=True)). Prescribing all other strains as
zero instead — a common simplification in coursework — adds a large
spurious hydrostatic stress (
The headline figure shows an Inconel-like material (E = 210 GPa, ν = 0.26,
Ramp to the same final strains three ways — axial first then shear, shear first then axial, or both together:
| History | final |
final |
|---|---|---|
| normal first, then shear | 524 | 702 |
| shear first, then normal | 1353 | 116 |
| simultaneous | 1195 | 409 |
Same final strain state, stresses that differ by a factor of two and more: plasticity is path dependent, and the component loaded last carries most of the stress because the backstress has rotated toward it. This is why incremental integration along the actual history is necessary — a deformation-theory shortcut from final strains cannot get these numbers.
plasticity1d.py 1D model (stress and strain control)
j2_plasticity.py 3D J2 model with AF + power-law hardening, tube loading programs
run_examples.py regenerates every figure
figures/
matlab/ the original 2014 MATLAB scripts, kept for reference
- J. C. Simo and T. J. R. Hughes, Computational Inelasticity, Springer, 1998 — the return-mapping framework.
- P. J. Armstrong and C. O. Frederick, "A mathematical representation of the multiaxial Bauschinger effect," CEGB Report RD/B/N731, 1966.
- J. L. Chaboche, "Constitutive equations for cyclic plasticity and cyclic viscoplasticity," Int. J. Plasticity 5, 247–302, 1989.
Aiden Azarnoush — originally developed in MATLAB for a graduate plasticity course (2014); rewritten in Python.
MIT — see LICENSE.



