Writing a UMAT
A UMAT has 37 arguments. The ones that go wrong are not the complicated ones — they are the ones whose names look self-explanatory.
Most UMAT bugs are not physics errors. They are convention errors: an array indexed in the wrong order, a factor of two on shear, a constant read from the wrong slot. Every one of them compiles, runs, and produces a plausible number.
What you must return
At the end of the increment the subroutine owns two arrays and must fill both:
STRESS— the Cauchy stress at the end of the increment. It arrives holding the value at the start of the increment, so the usual pattern adds to it rather than assigning over it.DDSDDE— the material Jacobian. It arrives holding whatever was in memory. Zero it before you assemble, on every path through the subroutine including the elastic branch.
You also own STATEV, and optionally SSE, SPD, SCD for the energies and PNEWDT to ask for a different increment size. Everything else is supplied.
PROPS is a contract nothing checks
The material constants arrive in PROPS, in the order the input file lists them under *USER MATERIAL, CONSTANTS=n. Nothing in Abaqus knows what those numbers mean. Swap two of them and the job runs, converges, and models a different material.
*MATERIAL, NAME=STEEL
*USER MATERIAL, CONSTANTS=5
200000., 0.3, 250., 180., 12.,
*DEPVAR
1, E = PROPS(1)
XNU = PROPS(2)
SY0 = PROPS(3)
QINF = PROPS(4)
BSAT = PROPS(5)State variables and *DEPVAR
STATEV carries everything the material must remember between increments: accumulated plastic strain, damage, back stress, hardening variables. Its length is set by *DEPVAR, and writing past the end corrupts memory Abaqus owns.
The symptom is rarely a crash. It is a result that changes when the mesh changes, or when the number of processors changes, because what sits after the array in memory is different.
Two rules save most of the trouble. Write STATEV once, at the end of the increment — never inside a local iteration, or an equilibrium iteration that gets discarded will leave the state advanced. And write it on every path, including the elastic branch, or the value drifts depending on which branch ran last.
The three conventions that cause the most damage
1. Strain arrays carry engineering shear; stress arrays do not
STRAN(4) = gamma_12 = 2 * eps_12 engineering
STRESS(4) = sigma_12 tensorSo DDSDDE is not the Voigt image of a single fourth-order tensor: its shear columns absorb the factor of two. For isotropic elasticity the shear diagonal is G. Writing 2G there gives a material twice as stiff in shear, which passes every visual inspection.
2. Component order is not the same in Standard and Explicit
UMAT (Abaqus/Standard) 3-D: 11 22 33 12 13 23
VUMAT (Abaqus/Explicit) 3-D: 11 22 33 12 23 31The first four agree; the last two are transposed. A tangent copied from a working VUMAT into a UMAT compiles, runs, and converges badly in a way that looks like a physics problem. It is only visible when all three shear components are loaded to different levels.
3. The trace runs over three directions, not over NDI
NDI counts the direct components Abaqus stores, not the number of physical directions. Dividing the trace by NDI instead of by three gives a hydrostatic stress that is correct in 3-D and wrong in plane stress — the case people test last.
The shape a stress update usually takes
- Read the constants and the statePROPS into named variables, STATEV into the internal variables. Naming them costs nothing and makes the rest readable.
- Build the elastic stiffness and zero DDSDDEBoth before anything else, so no path can leave the Jacobian unassigned.
- Take an elastic trial stepAdd the elastic stress increment to the incoming STRESS. Everything downstream corrects this guess.
- Test the yield conditionIf the trial state is admissible, you are done: return the elastic tangent and the trial stress, and leave the state untouched.
- Correct, if it is notSolve for the plastic multiplier, update the stress, then assemble the consistent tangent from the converged values — not from the trial ones.
- Write the state backLast, once. Nothing above this line is visible to the next increment otherwise.
Fixed-form Fortran, and the column that eats your code
Most UMATs are fixed form, and fixed form is a column layout rather than a style. Column 1 marks a comment, columns 1 to 5 hold statement labels, column 6 continues the previous line, and columns 7 to 72 hold the statement. Everything past column 72 is ignored, silently.
A statement that runs long does not fail to compile. It compiles with the tail removed, which turns SYIELD = SY0 + HARD*EQPLAS into SYIELD = SY0 + HARD*EQP and gives you a subroutine that runs and is wrong. Modern compilers usually warn; the ones installed at sites for Abaqus often do not, and the warning is buried in the log.
One more: write real constants with a D0 suffix. A bare 1.5 in double-precision code is a single-precision constant, widened after it has already been rounded. On a constant like one third that costs seven digits, which is enough to stop a Newton iteration converging quadratically.
How to know it is right
Compiling is not evidence. Neither is one full-scale simulation that produced a plausible picture. The cheap checks, in the order they pay off:
- Read the moduli straight back. Load a single material point elastically and check that Young's modulus, Poisson's ratio, the shear modulus and the bulk modulus come out where you put them in. The shear modulus is where the engineering-shear factor shows up.
- Check yield onset. Load until flow begins and compare the von Mises stress against the yield stress you declared.
- Check the geometry of plastic flow. Volumetric plastic strain should be zero for J2, and the plastic strain increment should be parallel to the deviatoric stress. Both are exact identities, so they hold to machine precision or something is wrong.
- Apply the same total strain in one increment and in sixty-four. For proportional loading a correct radial return gives the same answer either way. A difference here means the algorithm is accumulating something it should be solving for.
- Check the tangent against finite differences. See the material Jacobian.
- Run one element in Abaqus. With the expected stresses written down beforehand. This is the only check that exercises your compiler, your link line and the real solver.
Common questions
Why does my UMAT give different results on a different machine?
Almost always an array written past its end — STATEV beyond *DEPVAR, or PROPS beyond CONSTANTS. What sits after the array differs between builds, so the corruption differs.
Check the two counts against the highest index your code touches before looking anywhere else.
Do I need to rotate my state variables?
In a small-strain analysis, no: Abaqus hands you co-rotational quantities and the subroutine is not responsible for rotation.
In finite strain, a tensor-valued state variable must be rotated with DROT and a scalar must not. Getting this backwards is quiet — it only shows up under large rotation, which is the case you test last.
What does PNEWDT do?
It asks Abaqus to change the time increment. Below one it requests a cutback. That is the right response when your local iteration failed to converge, and better than returning a stress that does not sit on the yield surface.
Above one it suggests a larger increment. Abaqus takes the most restrictive request from all integration points.
Can I write a UMAT in free-form Fortran?
Yes, Abaqus accepts .f90. But the overwhelming majority of existing UMATs, every example in circulation, and every snippet you will paste next to yours are fixed form.
Mixing the two in one file is where the trouble starts.