OpenSees Cloud
OpenSees AMI
Verification Is More Than Matching Numbers
05 Jul 2026 - Michael H. Scott
I’ve been writing OpenSees scripts for the examples in Ann Jeffers’s book Linear and Nonlinear Matrix Methods of Structural Analysis. The first five chapters cover linear analysis where either you get the same answer as the textbook or you don’t. If you do, you move on to the next problem. If you don’t, barring an unlikely error in the textbook, you figure out what you did wrong in your OpenSees model.
But when you get to nonlinear analysis, the answer in the book and the answer from OpenSees can both be correct. The verification shifts from matching a number to understanding formulations of nonlinear structural response.
The model shown below is Jeffers Example 8.1, from Chapter 8 on geometric nonlinearity. The cross-sectional area of each member is 13 cm2 and the constitutive response is linear-elastic with E=200 GPa. Due to symmetry of the model and loading, there is only one DOF for the analysis.

The go-to approach for analyzing this model in OpenSees is to use corotational truss elements.
import openseespy.opensees as ops
m = 1
kN = 1
GPa = 1e6*kN/m**2
E = 200*GPa
A = 0.0013*m**2
P = 350*kN
u0 = 0.25*m
ops.wipe()
ops.model('basic','-ndm',2,'-ndf',2)
ops.node(1,0,0); ops.fix(1,1,1)
ops.node(2,3*m,-u0)
ops.node(3,6*m,0); ops.fix(3,1,1)
ops.uniaxialMaterial('Elastic',1,E)
ops.element('corotTruss',1,1,2,A,1)
ops.element('corotTruss',2,2,3,A,1)
ops.timeSeries('Constant',1)
ops.pattern('Plain',1,1)
ops.load(2,0,-P)
ops.test('NormUnbalance',1.0*kN,10,1)
ops.analysis('Static','-noWarnings')
ops.analyze(1)
The load is applied in a single step and the analysis uses a convergence tolerance of 1 kN on the norm of the unbalanced (residual) load vector.
Both the textbook and OpenSees analyses have the same initial stiffness, 1191.27 kN/m, and displacement increment, 0.293803 m (downward), on the first iteration (Iteration 0).
The residuals at subsequent iterations, presented in the textbook and obtained from OpenSees, are shown below.
| Iteration | Textbook (kN) | OpenSees (kN) |
|---|---|---|
| 1 | 843.425 | 835.844 |
| 2 | 169.440 | 164.882 |
| 3 | 15.2586 | 14.2683 |
| 4 | 0.18716 | 0.14737 |
The textbook and OpenSees iterations are close, but not close enough to be dismissed as rounding error.
The OpenSees residuals are smaller than those reported in the book, but this doesn’t indicate better convergence. Also note that the residuals at the first iteration exceed the magnitude of the applied load because of tension stiffening in the load-displacement response.
The final vertical deflection reported in the textbook is 0.14498 m downward while that computed in OpenSees is 0.14520 m (downward), less than the deflection after iteration 0 due to tension stiffening.
The discrepancy comes from the element formulation where the textbook uses an element with a second order strain measure while the corotational truss uses first order (engineering) strain. Both formulations converge to valid solutions. Neither solution is right or wrong.
You could spend hours trying to get the OpenSees solution to match the textbook. But if you understand that the element formulations use different strain measures, you won’t waste time worrying about small differences in the results. In nonlinear structural analysis, discerning modeling mistakes from legitimate differences in formulation is far more valuable than trying to match a textbook result.