Code
Your first elastic model in OpenSeesPy
Detect the OpenSeesPy install on your machine, build a 2D elastic cantilever, and check tip displacement against the Euler–Bernoulli closed form. This site does not bundle OpenSees.
An elastic model is the first honest check that your solver, units, and connectivity describe the problem you think they do. This tutorial builds a 2D prismatic cantilever in OpenSeesPy, then compares the tip displacement to the elementary closed form. It is a static linear check, not a code check and not a dynamics analysis.
Detect first: your install, not a bundled solver
OpenSees is developed at the University of California, Berkeley / PEER. OpenSeesPy is a Python interface to that engine. You install it in the interpreter you already use for engineering scripts. This website does not ship OpenSees, does not redistribute binaries, and does not wrap a Mamut-bundled solver. If an assistant or product page implies otherwise, treat that as an error.
Confirm the import before you build a model:
def require_openseespy():
try:
import openseespy.opensees as ops
except ImportError as exc:
raise SystemExit(
"OpenSeesPy is not importable in this interpreter. "
"Install it yourself, for example `pip install openseespy`, "
"in the same environment you use for engineering work. "
"This tutorial does not bundle or redistribute OpenSees."
) from exc
return ops
ops = require_openseespy()
If that call exits, stop. Do not paste a Tcl model into a Python session, and do not assume a second machine (CI, a colleague’s laptop, a container) has the same wheel. Official API notes live in the OpenSeesPy documentation. Installation and platform caveats change; trust the docs for your version, not a copied command from an old blog.
Tcl OpenSees is a separate binary. This article uses OpenSeesPy only.
Prerequisites
- Python 3 on your machine
- OpenSeesPy importable in that interpreter (the detect step above)
- Comfort with 2D beam degrees of freedom (axial, transverse, rotation)
- A unit system you will not mix (see the units tutorial linked below)
The engineering problem
A horizontal prismatic cantilever, length L, flexural rigidity EI, clamped at x = 0, tip load P in the negative y direction. Under Euler–Bernoulli beam theory (small deflection, no shear deformation, linear elastic material), the tip transverse displacement magnitude is:
δ = P L³ / (3 E I)
That closed form is the standard result in elementary strength of materials — for example Timoshenko and Gere, Mechanics of Materials — not a clause copied from a building code. We use it as an independent check, not as a substitute for a standard.
OpenSees is unitless. Pick one coherent set and stay in it. This script uses newtons and metres (P in N, E in Pa, I in m⁴, L in m, displacement in m). Mixing millimetres into I while leaving L in metres is a common way to get a “solved” model that is wrong by many orders of magnitude.
Implementation
One elasticBeamColumn with a linear geometric transformation and cubic Hermitian interpolation recovers the Euler–Bernoulli cantilever exactly for a prismatic member with loads only at the ends (within floating-point noise). That is the point of the first model: if this disagrees with P L³ / (3 E I), the bug is in units, boundary conditions, load direction, or the element signature — not in “mesh refinement.”
ops = require_openseespy()
L = 4.0 # m
E = 2.0e11 # Pa (N/m²)
I = 8.333e-6 # m⁴ (100 mm × 100 mm rectangle: b h³ / 12)
A = 0.01 # m² (needed by the element; does not affect this tip load)
P = 1000.0 # N, downward
ops.wipe()
ops.model("basic", "-ndm", 2, "-ndf", 3)
ops.node(1, 0.0, 0.0)
ops.node(2, L, 0.0)
ops.fix(1, 1, 1, 1)
ops.geomTransf("Linear", 1)
ops.element("elasticBeamColumn", 1, 1, 2, A, E, I, 1)
ops.timeSeries("Linear", 1)
ops.pattern("Plain", 1, 1)
ops.load(2, 0.0, -P, 0.0)
ops.constraints("Plain")
ops.numberer("RCM")
ops.system("BandGeneral")
ops.algorithm("Linear")
ops.integrator("LoadControl", 1.0)
ops.analysis("Static")
ok = ops.analyze(1)
if ok != 0:
raise RuntimeError(f"static analysis failed, analyze() returned {ok}")
uy = ops.nodeDisp(2, 2)
delta_closed = P * L**3 / (3.0 * E * I)
rel = abs(abs(uy) - delta_closed) / delta_closed
print(f"OpenSees uy = {uy:.6e} m")
print(f"Closed form = {delta_closed:.6e} m")
print(f"Relative error on |uy| = {rel:.3e}")
Read the elasticBeamColumn argument list for your OpenSeesPy build. In 2D the usual order is tag, nodes, A, E, Iz, transformation tag. A 3D signature inserts G, J, Iy and will not match this script.
Validation
Treat the closed form as the referee for this specific problem:
|uy|should match P L³ / (3 E I) to roughly 1e-8 relative or better on a typical double-precision build. If you see 1e-3 or worse, do not “accept engineering tolerance” — find the unit or DOF mistake.uyshould be negative with the load above. A positive tip displacement with a negative Py means the load or the displacement DOF is not what you think.- Split the member into several
elasticBeamColumnsegments with intermediate nodes. End-loaded Euler–Bernoulli cubics should stay on the closed form; a large jump after meshing usually means a transformation, release, or constraint error, not “more elements needed.” - Rotate the cantilever to vertical and load it axially: the tip transverse displacement must collapse to numerical zero. If it does not, the geometric transformation or local axes are wrong.
A language model that prints a displacement without this check is not a solver. Keep the closed form in the same script that calls OpenSees.
Common mistakes
- Import succeeded on a different Python. Editors, notebooks, and CI often do not share the interpreter where you ran
pip install. - Units. E in MPa with I in mm⁴ and L in m is the classic silent failure. OpenSees will still “converge.”
- Wrong
fixmask. Leaving rotation free at the support is not a cantilever. - 3D element into a 2D model (or the reverse). The analysis may fail or, worse, run with a mis-parsed argument list.
geomTransfomitted or reused with the wrong dimension. 2DLinearis not the 3DLinearthat needs a vector defining the local xz plane.- Trusting default mass or
geomTransf('PDelta')on a first elastic check. Start withLinearand no mass.
Limitations
- Euler–Bernoulli: no shear deformation. A deep beam needs a Timoshenko-type element and a different closed form.
- Linear geometry: this is not a P-delta or large-displacement check.
- Prismatic, elastic, isotropic, end load only. Distributed load, tapered sections, and semi-rigid joints are different models.
- One load case, static. Natural periods, response spectra, and modal combination are outside this article. For dynamics, use a structural dynamics reference such as Chopra, Dynamics of Structures — this hub’s book entry uses content id
chopra-dynamics-of-structures— and still keep a closed-form or hand-spectrum check. - OpenSeesPy wheels and Tcl builds differ by platform. A result you cannot regenerate on a second machine is not a result you should sign.
- Nothing here is a substitute for the building code, the licensed engineer of record, or a documented verification plan.
Professional context
A first elastic model is a verification step: does this installation, this unit system, and this connectivity reproduce a problem whose answer you already know? Production models need that habit plus independent review. Do not paste this cantilever into a project file and call the structure “checked.”
References
- OpenSees / OpenSeesPy documentation, University of California, Berkeley / PEER. https://openseespydoc.readthedocs.io/
- McKenna, F. OpenSees: a framework for earthquake engineering simulation. Computing in Science & Engineering, 2011 (framework context; not a substitute for the version you installed).
- Timoshenko, S. P., and Gere, J. M. Mechanics of Materials. Elementary cantilever result δ = P L³ / (3 E I). Cite the edition on your shelf; do not copy long excerpts.
- Chopra, A. K. Dynamics of Structures. For the step after static elastic verification (content id
chopra-dynamics-of-structures).