OpenSees Cloud
OpenSees AMI
Thermal Loads on an Elastic Beam
05 Apr 2026 - Michael H. Scott
Long before structural fire analysis became an OpenSees side hustle, the
elasticBeamColumn element was able to handle thermal loads. The
-alpha and -depth options are not really Easter eggs so much as
undocumented features.
Back in 2002, when
OpenSees source control
used CVS,
Scott Hamilton
modified the elasticBeamColumn element with two optional parameters
for the coefficient of thermal expansion and beam depth. He also defined
Beam2dTempLoad to apply temperatures as element loads. The options are
for two-dimensional models only.
To confirm the implementation still works today, I used Example 7.3 from J.C. Smith’s Structural Analysis. The model is a two-span continuous steel beam heated 60 degrees Fahrenheit more on the top than on the bottom.

The beam section is W21x68 with depth d=21.13 inch, A= 20 inch2, and I=1480 inch4. The elastic modulus is E=29000 ksi and the coefficient of thermal expansion is \(\alpha\)=6.5e-6 / degF.
The reported reactions due to the thermal loading are 4.95 kip (up) at A, 7.43 kip (down) at B, and 2.48 kip (up) at C.
The OpenSees Python script below confirms the expected reactions using
the elasticBeamColumn element and a beamTemp element load.
import openseespy.opensees as ops
from numpy import isclose
kip = 1.0
ft = 1.0
degF = 1.0
inch = ft/12
ksi = kip/inch**2
E = 29000*ksi
alpha = 6.5e-6/degF
# W21x68
A = 20*inch**2
I = 1480*inch**4
d = 21.13*inch # Recent editions of the Steel Manual show d=21.1 inch
ops.wipe()
ops.model('basic','-ndm',2,'-ndf',3)
ops.node(1,0,0); ops.fix(1,1,1,0)
ops.node(2,20*ft,0); ops.fix(2,0,1,0)
ops.node(3,60*ft,0); ops.fix(3,0,1,0)
ops.geomTransf('Linear',1)
ops.element('elasticBeamColumn',1,1,2,A,E,I,1,'-alpha',alpha,'-depth',d)
ops.element('elasticBeamColumn',2,2,3,A,E,I,1,'-alpha',alpha,'-depth',d)
dT = 60*degF # Change in temperature
ops.timeSeries('Constant',1)
ops.pattern('Plain',1,1)
ops.eleLoad('-ele',1,2,'-type','-beamTemp',dT,0) # top,bottom
ops.analysis('Static','-noWarnings')
ops.analyze(1)
ops.reactions()
assert isclose( 4.95,ops.nodeReaction(1,2)/kip,atol=0.5e-2)
assert isclose(-7.43,ops.nodeReaction(2,2)/kip,atol=0.5e-2)
assert isclose( 2.48,ops.nodeReaction(3,2)/kip,atol=0.5e-2)
The assertions should pass.
Remember that the beamTemp temperatures refer to the “top” and
“bottom” of the basic system, so swapping the element nodes also swaps
the thermal loading, no different from
mechanical element loads
that follow the local axes.
I work on problems related to modeling and nonlinear structural analysis. If these problems are relevant to a current professional project, feel free to reach out.