OpenSees Cloud
OpenSees AMI
Distributed Moments
Original Post - 17 Aug 2025 - Michael H. Scott
Visit Structural Analysis Is Simple on Substack.
I have often posited that we can use OpenSees to solve every reasonable problem from any textbook on structural analysis, dynamics, or mechanics. I even put together a few posts, e.g., here and here, on how OpenSees can solve rigid body dynamics problems, the ones that torment every civil engineering sophomore.
But a seemingly easy structural analysis problem that OpenSees cannot solve is distributed force (traction) along a side of a beam. For example, an axial distributed load along the top side of a cantilever.
The traction is applied away from the reference axis of the section. Using concepts from equivalent force-couples, moving the traction to the reference axis leads to a distributed axial load and a distributed moment, m=(h/2)w.
The units of the distributed moment, m, are moment/length, i.e., force-length/length, or just force.
OpenSees can handle the distributed axial load, but cannot handle the distributed moment–not without discretizing the beam into multiple elements and applying concentrated loads and moments at the nodes. Another approach is to use solid elements with actual traction stresses. But no one really wants to do either of those things.
A better way is to implement the effects of distributed moments directly
in the state determination of OpenSees’s most commonly-used frame
elements–elasticBeamColumn
, dispBeamColumn
, and forceBeamColumn
.
That’s what I set out to do with
PR #1650.
Below is a minimal working
example showing the beamUniformMoment
option for the eleLoad
command. The assertions test the moment reaction, mL
(counter-clockwise), at the fixed end and the deflection and rotation,
mL3/(3EI) (down) and mL2/(2EI) (clockwise),
respectively, at the free end.
import openseespy.opensees as ops
from math import isclose
L = 48
E = 29000
h = 6
A = h*h
I = h**4/12
w = 0.1
m = (h/2)*w
ops.wipe()
ops.model('basic','-ndm',2,'-ndf',3)
ops.node(1,0,0); ops.fix(1,1,1,1)
ops.node(2,L,0)
ops.geomTransf('Linear',1)
ops.section('Elastic',1,E,A,I)
ops.beamIntegration('Legendre',1,1,2)
ops.element('forceBeamColumn',1,1,2,1,1)
ops.timeSeries('Constant',1)
ops.pattern('Plain',1,1)
ops.eleLoad('-ele',1,'-type','beamUniform',0,w)
ops.eleLoad('-ele',1,'-type','beamUniformMoment',-m) # CW about local z
ops.analysis('Static','-noWarnings')
ops.analyze(1)
ops.reactions()
assert isclose(m*L,ops.nodeReaction(1,3))
assert isclose(-m*L*L*L/(3*E*I),ops.nodeDisp(2,2))
assert isclose(-m*L*L/(2*E*I),ops.nodeDisp(2,3))
The beamUniformMoment
option should be available in the version of
OpenSeesPy that comes after 3.7.1.2 and what comes after version 3.7.1
of OpenSees.exe Tcl. Or if you can’t wait (but I’m sure you can), you
can checkout the PR and compile the new code yourself.
I’m working on distributed moments about the local x, y, and z axes of 3D frame elements. Dealing with the distributed torsional moment (about x) has proven to be more difficult than expected, i.e., not trivial, because of some assumptions made in the geometric transformation classes.