Frame element Response¶
Supported frame elements include most frame elements in OpenSees, including:
✅ elasticBeamColumn
✅ ElasticTimoshenkoBeam
✅ dispBeamColumn
✅ forceBeamColumn
✅ nonlinearBeamColumn
✅ ……
[1]:
import matplotlib.pyplot as plt
import numpy as np
import openseespy.opensees as ops
import opstool as opst
[2]:
opst.load_ops_examples("Frame3D2") # or your model code here
# add beam loads
ops.timeSeries("Linear", 1)
ops.pattern("Plain", 1, 1)
ops.load(6, 10, 0.0, 0.0, 0.0, 0.0, 0.0)
ops.load(8, 10, 0.0, 0.0, 0.0, 0.0, 0.0)
for etag in [5, 6, 7, 8, 15, 16, 17, 18, 19, 20, 21]:
ops.eleLoad("-ele", etag, "-type", "-beamUniform", 0.0, -10)
# plot
opst.vis.pyvista.set_plot_props(notebook=True)
fig = opst.vis.pyvista.plot_model(show_nodal_loads=True, show_ele_loads=True)
fig.show(jupyter_backend="static")
Result Saving¶
[3]:
ops.system("BandGeneral")
# Create the constraint handler, the transformation method
ops.constraints("Transformation")
# Create the DOF numberer, the reverse Cuthill-McKee algorithm
ops.numberer("RCM")
# Create the convergence test, the norm of the residual with a tolerance of
# 1e-12 and a max number of iterations of 10
ops.test("NormDispIncr", 1.0e-12, 10, 3)
# Create the solution algorithm, a Newton-Raphson algorithm
ops.algorithm("Newton")
# Create the integration scheme, the LoadControl scheme using steps of 0.1
ops.integrator("LoadControl", 0.1)
# Create the analysis object
ops.analysis("Static")
opstool allows us to save the data at each step of the analysis!
First, we create a database class using opstool.post.CreateODB, and then, during each step of the analysis, we call its method .fetch_response_step to retrieve the data for the current step.
Once all the analysis steps are completed, we use the .save_response method to save the data in one go.
[4]:
ODB = opst.post.CreateODB(odb_tag=1, elastic_frame_sec_points=9)
for i in range(10):
ops.analyze(1)
ODB.fetch_response_step()
ODB.save_response()
dict_keys(['NodalData', 'FixedNodalData', 'NodalLoadData', 'FrameLoadData', 'BeamData', 'AllLineElesData', 'eleCenters'])
dict_keys(['NodalData', 'FixedNodalData', 'NodalLoadData', 'FrameLoadData', 'BeamData', 'AllLineElesData', 'eleCenters'])
dict_keys(['NodalData', 'FixedNodalData', 'NodalLoadData', 'FrameLoadData', 'BeamData', 'AllLineElesData', 'eleCenters'])
dict_keys(['NodalData', 'FixedNodalData', 'NodalLoadData', 'FrameLoadData', 'BeamData', 'AllLineElesData', 'eleCenters'])
dict_keys(['NodalData', 'FixedNodalData', 'NodalLoadData', 'FrameLoadData', 'BeamData', 'AllLineElesData', 'eleCenters'])
dict_keys(['NodalData', 'FixedNodalData', 'NodalLoadData', 'FrameLoadData', 'BeamData', 'AllLineElesData', 'eleCenters'])
dict_keys(['NodalData', 'FixedNodalData', 'NodalLoadData', 'FrameLoadData', 'BeamData', 'AllLineElesData', 'eleCenters'])
dict_keys(['NodalData', 'FixedNodalData', 'NodalLoadData', 'FrameLoadData', 'BeamData', 'AllLineElesData', 'eleCenters'])
dict_keys(['NodalData', 'FixedNodalData', 'NodalLoadData', 'FrameLoadData', 'BeamData', 'AllLineElesData', 'eleCenters'])
dict_keys(['NodalData', 'FixedNodalData', 'NodalLoadData', 'FrameLoadData', 'BeamData', 'AllLineElesData', 'eleCenters'])
dict_keys(['NodalData', 'FixedNodalData', 'NodalLoadData', 'FrameLoadData', 'BeamData', 'AllLineElesData', 'eleCenters'])
OPSTOOL :: All responses data with _odb_tag = 1 saved in .opstool.output/RespStepData-1.nc!
Result Reading¶
The provided function opstool.post.get_element_responses() make it easy to read element responses.
ele_type="Frame" is used to specify extracting the response of frame elements.
[5]:
all_resp = opst.post.get_element_responses(odb_tag=1, ele_type="Frame")
OPSTOOL :: Loading Frame response data from .opstool.output/RespStepData-1.nc ...
The result is an xarray DataSet object, and we can access the associated DataArray objects through .data_vars.
[6]:
all_resp.data_vars
[6]:
Data variables:
localForces (time, eleTags, localDofs) float32 25kB -0.0 ... 89.87
basicForces (time, eleTags, basicDofs) float32 12kB 0.0 ... -1.6...
basicDeformations (time, eleTags, basicDofs) float32 12kB 0.0 ... -2.0...
plasticDeformation (time, eleTags, basicDofs) float32 12kB 0.0 ... 0.0
sectionForces (time, eleTags, secPoints, secDofs) float32 112kB -0...
sectionDeformations (time, eleTags, secPoints, secDofs) float32 112kB 0....
sectionLocs (time, eleTags, secPoints, locs) float32 74kB 0.0 .....
The variable names, along with their dimensions and coordinates, are displayed above. The first four represent the resistance and deformation at both ends of the element, while the last three are quantities related to the cross-section.
Element resistance¶
The element local coordinate system resistance in opstool follows the following sign convention:

[7]:
print(all_resp["localForces"])
<xarray.DataArray 'localForces' (time: 11, eleTags: 47, localDofs: 12)> Size: 25kB
array([[[-0.00000000e+00, -0.00000000e+00, -0.00000000e+00, ...,
0.00000000e+00, -0.00000000e+00, 0.00000000e+00],
[-0.00000000e+00, -0.00000000e+00, -0.00000000e+00, ...,
0.00000000e+00, -0.00000000e+00, 0.00000000e+00],
[-0.00000000e+00, -0.00000000e+00, -0.00000000e+00, ...,
0.00000000e+00, -0.00000000e+00, 0.00000000e+00],
...,
[-0.00000000e+00, -0.00000000e+00, -0.00000000e+00, ...,
0.00000000e+00, -0.00000000e+00, 0.00000000e+00],
[-0.00000000e+00, -0.00000000e+00, -0.00000000e+00, ...,
0.00000000e+00, -0.00000000e+00, 0.00000000e+00],
[-0.00000000e+00, -0.00000000e+00, -0.00000000e+00, ...,
0.00000000e+00, -0.00000000e+00, 0.00000000e+00]],
[[-4.75025928e+03, -8.00031189e+02, 5.74703491e+02, ...,
3.84109741e+02, -1.13577975e+06, 1.30496588e+06],
[-4.75025928e+03, 8.00031189e+02, 5.74703491e+02, ...,
-3.84109741e+02, -1.13577975e+06, -1.30496588e+06],
[-4.74974072e+03, 7.65632324e+02, -5.73703491e+02, ...,
1.15370697e+02, 1.13461275e+06, -1.28301525e+06],
...
3.50827158e-12, 1.56379141e+05, -7.66474533e+01],
[-1.05462512e+03, 3.20596888e-14, -2.44472167e-13, ...,
4.20992581e-11, 2.65055031e+05, -6.29041748e+02],
[-5.87808105e+02, -2.68585152e-14, 1.22236084e-13, ...,
5.87635496e-11, 1.50153531e+05, 8.08818741e+01]],
[[-4.75025938e+04, -8.00031152e+03, 5.74703516e+03, ...,
3.84109741e+03, -1.13577970e+07, 1.30496580e+07],
[-4.75025938e+04, 8.00031152e+03, 5.74703516e+03, ...,
-3.84109741e+03, -1.13577970e+07, -1.30496580e+07],
[-4.74974062e+04, 7.65632324e+03, -5.73703516e+03, ...,
1.15370691e+03, 1.13461270e+07, -1.28301530e+07],
...,
[-4.95212158e+02, -1.04023460e-15, -5.23868939e-14, ...,
0.00000000e+00, 1.73754609e+05, -8.51638336e+01],
[-1.17180566e+03, -9.48148268e-15, 2.44472167e-13, ...,
-2.80661726e-11, 2.94505594e+05, -6.98935303e+02],
[-6.53120117e+02, 2.95017340e-15, 1.04773788e-13, ...,
-1.66642897e-11, 1.66837250e+05, 8.98687515e+01]]],
dtype=float32)
Coordinates:
* time (time) float32 44B 0.0 0.1 0.2 0.3 0.4 0.5 0.6 0.7 0.8 0.9 1.0
* eleTags (eleTags) int32 188B 1 2 3 4 5 6 7 8 ... 40 41 42 43 44 45 46 47
* localDofs (localDofs) <U3 144B 'FX1' 'FY1' 'FZ1' ... 'MX2' 'MY2' 'MZ2'
The basic forces are a subset of the local resistance forces and are used to represent the independent components that need to be solved, while the remaining components can be obtained from the equilibrium equations.
[8]:
print(all_resp["basicForces"])
<xarray.DataArray 'basicForces' (time: 11, eleTags: 47, basicDofs: 6)> Size: 12kB
array([[[ 0.0000000e+00, -0.0000000e+00, 0.0000000e+00, 0.0000000e+00,
-0.0000000e+00, 0.0000000e+00],
[ 0.0000000e+00, -0.0000000e+00, 0.0000000e+00, 0.0000000e+00,
-0.0000000e+00, 0.0000000e+00],
[ 0.0000000e+00, -0.0000000e+00, 0.0000000e+00, 0.0000000e+00,
-0.0000000e+00, 0.0000000e+00],
...,
[ 0.0000000e+00, -0.0000000e+00, 0.0000000e+00, 0.0000000e+00,
-0.0000000e+00, 0.0000000e+00],
[ 0.0000000e+00, -0.0000000e+00, 0.0000000e+00, 0.0000000e+00,
-0.0000000e+00, 0.0000000e+00],
[ 0.0000000e+00, -0.0000000e+00, 0.0000000e+00, 0.0000000e+00,
-0.0000000e+00, 0.0000000e+00]],
[[-4.7502593e+03, -1.0951276e+06, 1.3049659e+06, 5.8833088e+05,
-1.1357798e+06, 3.8410974e+02],
[-4.7502593e+03, 1.0951276e+06, -1.3049659e+06, 5.8833088e+05,
-1.1357798e+06, -3.8410974e+02],
[-4.7497407e+03, 1.0138817e+06, -1.2830152e+06, -5.8649788e+05,
1.1346128e+06, 1.1537070e+02],
...
[-4.4569095e+02, -7.6647453e+01, -7.6647453e+01, 1.5637914e+05,
1.5637914e+05, 3.5082716e-12],
[-1.0546251e+03, -6.2904175e+02, -6.2904175e+02, 2.6505503e+05,
2.6505503e+05, 4.2099258e-11],
[-5.8780811e+02, 8.0881874e+01, 8.0881874e+01, 1.5015353e+05,
1.5015353e+05, 5.8763550e-11]],
[[-4.7502594e+04, -1.0951277e+07, 1.3049658e+07, 5.8833085e+06,
-1.1357797e+07, 3.8410974e+03],
[-4.7502594e+04, 1.0951277e+07, -1.3049658e+07, 5.8833085e+06,
-1.1357797e+07, -3.8410974e+03],
[-4.7497406e+04, 1.0138817e+07, -1.2830153e+07, -5.8649785e+06,
1.1346127e+07, 1.1537069e+03],
...,
[-4.9521216e+02, -8.5163834e+01, -8.5163834e+01, 1.7375461e+05,
1.7375461e+05, 0.0000000e+00],
[-1.1718057e+03, -6.9893530e+02, -6.9893530e+02, 2.9450559e+05,
2.9450559e+05, -2.8066173e-11],
[-6.5312012e+02, 8.9868752e+01, 8.9868752e+01, 1.6683725e+05,
1.6683725e+05, -1.6664290e-11]]], dtype=float32)
Coordinates:
* time (time) float32 44B 0.0 0.1 0.2 0.3 0.4 0.5 0.6 0.7 0.8 0.9 1.0
* eleTags (eleTags) int32 188B 1 2 3 4 5 6 7 8 ... 40 41 42 43 44 45 46 47
* basicDofs (basicDofs) <U3 72B 'N' 'MZ1' 'MZ2' 'MY1' 'MY2' 'T'
We can also get the properties:
[9]:
all_resp.attrs
[9]:
{'localDofs': 'local coord system dofs at end 1 and end 2',
'basicDofs': 'basic coord system dofs at end 1 and end 2',
'secPoints': 'section points No.',
'secDofs': 'section forces and deformations Dofs. Note that the section DOFs are only valid for <Elastic Section>, <Elastic Shear Section>, and <Fiber Section>. For <Aggregator Section>, you should carefully check the data, as it may not correspond directly to the DOFs.',
'Notes': 'Note that the deformations are displacements and rotations in the basicDofs;And strains and curvatures in the secDofs'}
Section response¶
Sometimes we are more concerned with the response at the section level, which can be easily extracted. For example, extracting section forces involves four dimensions: time (time), element IDs (eleTags), section locations (secLocs), and degrees of freedom (secDoFs).
[10]:
sec_forces = all_resp["sectionForces"]
sec_defos = all_resp["sectionDeformations"]
[11]:
sec_locs = all_resp["sectionLocs"].sel(eleTags=6, time=0)
sec_locs
[11]:
<xarray.DataArray 'sectionLocs' (secPoints: 9, locs: 4)> Size: 144B
array([[0.000e+00, 4.500e+03, 0.000e+00, 1.350e+04],
[1.250e-01, 4.500e+03, 6.250e+02, 1.350e+04],
[2.500e-01, 4.500e+03, 1.250e+03, 1.350e+04],
[3.750e-01, 4.500e+03, 1.875e+03, 1.350e+04],
[5.000e-01, 4.500e+03, 2.500e+03, 1.350e+04],
[6.250e-01, 4.500e+03, 3.125e+03, 1.350e+04],
[7.500e-01, 4.500e+03, 3.750e+03, 1.350e+04],
[8.750e-01, 4.500e+03, 4.375e+03, 1.350e+04],
[1.000e+00, 4.500e+03, 5.000e+03, 1.350e+04]], dtype=float32)
Coordinates:
time float32 4B 0.0
eleTags int32 4B 6
* secPoints (secPoints) int32 36B 1 2 3 4 5 6 7 8 9
* locs (locs) <U5 80B 'alpha' 'X' 'Y' 'Z'We can select the response of multiple elements. .isel(time=-1) is used to retrieve the data at the last time step, where .isel means indexing by position.
[12]:
sec_forces_my = sec_forces.sel(
eleTags=[5, 6, 7, 8, 15, 16, 17, 18, 19, 20, 21],
secDofs="MY",
).isel(time=-1)
# sec_forces_my.coords["secPoints"] = sec_locs
# plot
sec_forces_my.plot.line(x="secPoints")
plt.show()
Here, we retrieve the moment about the local y-axis for element 6:
Note: .sel is the abbreviation for “select”.
[13]:
sec_forces_my = sec_forces.sel(eleTags=6, secDofs="MY")
sec_defos_my = sec_defos.sel(eleTags=6, secDofs="MY")
print(sec_forces_my)
<xarray.DataArray 'sectionForces' (time: 11, secPoints: 9)> Size: 396B
array([[ 0. , 0. , 0. , 0. ,
0. , 0. , 0. , 0. ,
0. ],
[ 1302830.2 , -64357.25, -1040919.75, -1626857.2 ,
-1822169.8 , -1626857.2 , -1040919.75, -64357.25,
1302830.2 ],
[ 2605660.5 , -128714.5 , -2081839.5 , -3253714.5 ,
-3644339.5 , -3253714.5 , -2081839.5 , -128714.5 ,
2605660.5 ],
[ 3908490.8 , -193071.75, -3122759.2 , -4880571.5 ,
-5466509. , -4880571.5 , -3122759.2 , -193071.75,
3908490.8 ],
[ 5211321. , -257429. , -4163679. , -6507429. ,
-7288679. , -6507429. , -4163679. , -257429. ,
5211321. ],
[ 6514151.5 , -321786. , -5204598.5 , -8134286. ,
-9110848. , -8134286. , -5204598.5 , -321786. ,
6514151.5 ],
[ 7816981.5 , -386143.5 , -6245518.5 , -9761144. ,
-10933018. , -9761144. , -6245518.5 , -386143.5 ,
7816981.5 ],
[ 9119812. , -450500.5 , -7286438. , -11388000. ,
-12755188. , -11388000. , -7286438. , -450500.5 ,
9119812. ],
[ 10422642. , -514858. , -8327358. , -13014858. ,
-14577358. , -13014858. , -8327358. , -514858. ,
10422642. ],
[ 11725472. , -579215.5 , -9368278. , -14641716. ,
-16399528. , -14641716. , -9368278. , -579215.5 ,
11725472. ],
[ 13028303. , -643572. , -10409197. , -16268572. ,
-18221698. , -16268572. , -10409197. , -643572. ,
13028303. ]], dtype=float32)
Coordinates:
* time (time) float32 44B 0.0 0.1 0.2 0.3 0.4 0.5 0.6 0.7 0.8 0.9 1.0
eleTags int32 4B 6
* secPoints (secPoints) int32 36B 1 2 3 4 5 6 7 8 9
secDofs <U2 8B 'MY'
We can plot the moment at different section locations for various time steps.
[14]:
sec_locs
[14]:
<xarray.DataArray 'sectionLocs' (secPoints: 9, locs: 4)> Size: 144B
array([[0.000e+00, 4.500e+03, 0.000e+00, 1.350e+04],
[1.250e-01, 4.500e+03, 6.250e+02, 1.350e+04],
[2.500e-01, 4.500e+03, 1.250e+03, 1.350e+04],
[3.750e-01, 4.500e+03, 1.875e+03, 1.350e+04],
[5.000e-01, 4.500e+03, 2.500e+03, 1.350e+04],
[6.250e-01, 4.500e+03, 3.125e+03, 1.350e+04],
[7.500e-01, 4.500e+03, 3.750e+03, 1.350e+04],
[8.750e-01, 4.500e+03, 4.375e+03, 1.350e+04],
[1.000e+00, 4.500e+03, 5.000e+03, 1.350e+04]], dtype=float32)
Coordinates:
time float32 4B 0.0
eleTags int32 4B 6
* secPoints (secPoints) int32 36B 1 2 3 4 5 6 7 8 9
* locs (locs) <U5 80B 'alpha' 'X' 'Y' 'Z'[15]:
times = np.linspace(0, 1, 11)
sec_forces_my.coords["time"] = [f"{d:.1f}" for d in times]
# plot
sec_forces_my.plot.line(x="secPoints")
plt.xticks(ticks=sec_forces_my.secPoints, labels=sec_locs[:, 0].data, rotation=45)
plt.xlabel("Section location")
plt.show()
[16]:
times = np.linspace(0, 1, 11)
sec_defos_my.coords["time"] = [f"{d:.1f}" for d in times]
# plot
sec_defos_my.plot.line(x="secPoints")
plt.xticks(ticks=sec_defos_my.secPoints, labels=sec_locs[:, 0].data, rotation=45)
plt.xlabel("Section location")
plt.show()