Like Spinning Nodes

After posting on reasons that the solution to Ax=b fails, I realized I omitted an important case: truss nodes in a frame model. Although this post might be a stretch for an LBU (least bloggable unit), the blogging equivalent of an LPU, there are important factors to consider for structural models comprised of truss and frame elements.

If you define a model with three DOFs per node in 2D or six DOFs per node in 3D in order to mix frame and truss elements, you may end up with DOFs that have no rotational stiffness. Consider the truss-supported beam shown below.

We can define beam elements across the top and truss elements for the vertical and diagonals.

ops.wipe()
ops.model('basic','-ndm',2,'-ndf',3)

ops.node(1,0,0); ops.fix(1,1,1,0)
ops.node(2,50,0)
ops.node(3,100,0); ops.fix(3,1,1,0)
ops.node(4,50,-20)

ops.geomTransf('Linear',1)

ops.element('elasticBeamColumn',1,1,2,...,1)
ops.element('elasticBeamColumn',2,2,3,...,1)

ops.uniaxialMaterial('Elastic',1,E)

ops.element('truss',3,1,4,...,1)
ops.element('truss',4,2,4,...,1)
ops.element('truss',5,3,4,...,1)

Any analysis of this model will fail because the A matrix (stiffness matrix) will be singular. There is no flexural resistance at node 4, i.e., nothing will assemble into the node’s rotational DOF. As a result, the model has a local “spinning node” mechanism at this “truss only” node.

Most solvers in OpenSees will tell you which equation number wreaked havoc. In this case, the ProfileSPD solver indicates that equation number 3 caused problems.

For this simple model, the quick solution is to fix the rotation of node 4.

ops.fix(4,0,0,1)

For larger models, it could be difficult to determine which node is causing problems. The analyze command returns a negative number if the analysis failed, and specifically -3 if the linear equation solver failed. You can loop through the nodes and output the equation numbers using the nodeDOFs command.

ok = ops.analyze(1)
if ok == -3: # Ax=b failed
    for nd in ops.getNodeTags():
        print(f'Node {nd}: {ops.nodeDOFs(nd)}')

Note that equation numbers are not assigned until you issue the analyze command, so you cannot get meaningful results from this loop prior to an analysis.

In addition, many “smart analyze” scripts only check whether analyze returns 0 (success) or a negative number (failure). So you can burn through 38 different equilibrium solution algorithms, 42 different time integrators, and 8 time step subdivisions with no success because the A matrix was singular in the first place. Check for -3 first.

For the simple beam and truss model, we get the following nodes and equation numbers.

Here we see that the problem arises from node 4, whose rotational DOF was assigned equation number 3.

Some software will constrain the rotation of “truss only” nodes automatically, but OpenSees does not. You could write a more elaborate procedure to constrain all spinning nodes; however, it gets a little complicated. You are better off to correct the issue instead of relying on a function to solve the issue for you.

Of course, if you are going to define a model with only truss elements and no beams, declare two DOFs per node in the model builder or three DOFs per node for three-dimensional models.

# 2D
ops.model('basic','-ndm',2,'-ndf',2)

# 3D
ops.model('basic','-ndm',3,'-ndf',3)

With only translational DOFs, none of the nodes will spin.


The title of this post was inspired by Radiohead’s “Like Spinning Plates”. Unlike the song, this post has no underlying social or political meaning.

2 thoughts on “Like Spinning Nodes

  1. I had this same problem, curiously with exactly the same structure! My solution was to use ‘elasticBeamColumn’ in all elements with appropriate end releases (making sure that in all nodes there are at least one element without end release). My solution was:

    element(‘elasticBeamColumn’, 1, 1, 2, A1, E1, I1, 1)
    element(‘elasticBeamColumn’, 2, 2, 3, A1, E1, I1, 1)
    element(‘elasticBeamColumn’, 3, 1, 4, A2, E2, I2, 1, ‘-release’, 3)
    element(‘elasticBeamColumn’, 4, 4, 2, A3, E2, I3, 1, ‘-release’, 2)
    element(‘elasticBeamColumn’, 5, 4, 3, A2, E2, I2, 1, ‘-release’, 3)

    Your solution is simpler and more elegant. I’m new in OpenSees and I found the blog very useful. Tank you.

    Liked by 1 person

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

This site uses Akismet to reduce spam. Learn how your comment data is processed.