OpenSees Cloud
OpenSees AMI
Choose Your Own Topology
Original Post - 22 Jun 2025 - Michael H. Scott
Visit Structural Analysis Is Simple on Substack.
I’ve been working on a sparse linear equation solver. Not anything new, just implementing the methods presented by Timothy Davis in Direct Methods for Sparse Linear Systems.
Why? Because I want to learn how sparse matrix solvers work.
So for the sake of discussion, let’s call my solver BennySparse.
Aside from implementing
a standalone sparse solver for personal use,
I’d like to incorporate BennySparse in OpenSees. I know BennySparse won’t be
as fast as industrial strength sparse solvers like Mumps
, UmfPack
,
SuperLU
, and SparseSPD
, but it should be better than the banded and
profile solvers (maybe) and much better than the full general solver
(hopefully).
An important part of any sparse solver is symbolic factorization–basically some pre-analysis graph theory to determine matrix fill-in from matrix topology. For finite element models, matrix topology is based on the equation numbers and element connectivity. Although the numeric factorization of the stiffness matrix changes during a nonlinear analysis, the symbolic factorization remains unchanged unless you remove an element or node from the model.
To introduce symbolic factorization and demonstrate fill-in during Cholesky decomposition, Davis shows the topology of some matrix (Figure 4.2 if you’re reading along). The columns are numbered on the diagonal while the dots indicate non-zero off-diagonals.
In addition to the non-zeros shown above, six more non-zeros will appear in, or fill-in to, the lower triangular matrix resulting from Cholesky factorization. For larger systems, the amount of fill-in can be significant, leading to more time spent on numeric factorizations. Renumbering equations can mitigate fill-in.
But this post is not about equation numbering or symbolic or numeric factorization. No, this post is about how to make OpenSees assemble a stiffness matrix with the topology shown above–or any other specified matrix topology for that matter–so that I can test BennySparse.
Sure, I could write a main() function and manually populate a matrix for BennySparse to gnaw on. But if I shoot in the dark and populate the matrix in such a way that it is not positive definite, Cholesky factorization will fail.
Instead of guessing, I know a 1D model of zero length elements will
ensure a positive definite stiffness matrix. Elements attached to a
fixed base node establish the diagonal entries while elements connecting
unrestrained nodes give the off-diagonal entries from a list of (i,j)
tuples.
import openseespy.opensees as ops
import numpy as np
ops.wipe()
ops.model('basic','-ndm',1,'-ndf',1)
k = 10
ops.uniaxialMaterial('Elastic',1,k)
Neqn = 11
eleTag = 0
ops.node(0,0); ops.fix(0,1)
# Diagonals
for i in range(Neqn):
ops.node(i+1,0)
eleTag += 1
ops.element('zeroLength',eleTag,0,i+1,'-mat',1,'-dir',1)
# Off-diagonals
for i,j in [(1,6),(1,7),(2,3),(2,8),(3,10),(3,11),(4,6),(4,10),(5,8),(5,11),(6,9),(6,10),(7,11),(8,10),(8,11),(10,11)]:
eleTag += 1
ops.element('zeroLength',eleTag,i,j,'-mat',1,'-dir',1)
ops.numberer('Plain')
ops.system('FullGeneral')
# ops.system('BennySparse')
ops.analysis('Static','-noWarnings')
ops.analyze(1)
K = np.array(ops.printA('-ret')).reshape((Neqn,Neqn))
print(K)
Note that I’m using FullGeneral
so printA
will return the stiffness
matrix. Also, I’m using the Plain
numberer so the stiffness matrix is
not permuted. The result matches the topology of the sparse matrix shown
above, from Figure 4.2 of Davis’s book.
[[ 30. 0. 0. 0. 0. -10. -10. 0. 0. 0. 0.]
[ 0. 30. -10. 0. 0. 0. 0. -10. 0. 0. 0.]
[ 0. -10. 40. 0. 0. 0. 0. 0. 0. -10. -10.]
[ 0. 0. 0. 30. 0. -10. 0. 0. 0. -10. 0.]
[ 0. 0. 0. 0. 30. 0. 0. -10. 0. 0. -10.]
[-10. 0. 0. -10. 0. 50. 0. 0. -10. -10. 0.]
[-10. 0. 0. 0. 0. 0. 30. 0. 0. 0. -10.]
[ 0. -10. 0. 0. -10. 0. 0. 50. 0. -10. -10.]
[ 0. 0. 0. 0. 0. -10. 0. 0. 20. 0. 0.]
[ 0. 0. -10. -10. 0. -10. 0. -10. 0. 60. -10.]
[ 0. 0. -10. 0. -10. 0. -10. -10. 0. -10. 60.]]
The assembled stiffness matrix is symmetric and diagonally dominant. And, for what it’s worth, the matrix is also an M-matrix.
Now I can move forward and verify that BennySparse produces the correct fill-in pattern for Cholesky decomposition, then test BennySparse against other solvers in OpenSees. Stay tuned!