OpenSees Cloud
OpenSees AMI
Don't Invert the Matrix
Original Post - 18 Nov 2022 - Michael H. Scott
Visit Structural Analysis Is Simple on Substack.
A common issue with linear algebra textbooks is the depiction of x=A-1b as the solution to the linear system of equations Ax=b.
Find the inverse of the matrix A, then multiply that inverse with the right-hand side vector, b.
Theoretically correct? Yes. Practical? No. Numerical linear algebraicists? They scream.
The practical approach to find x is direct solution by Gauss elimination (LU decomposition). In MATLAB speak, this operation is x=A \ b, a notation that I will adopt in the equation environments of my lecture notes.
Not only is inversion and multiplication more expensive than direct solution for x, it is also less accurate. Google something like “reasons to not invert a matrix” and you’ll find plenty of explanations, such as this one.
There’s a few element and material models in OpenSees that use the “by the book” inversion and multiplication approach. Usually, the offending operations appear in a local Newton loop where the Matrix::Invert() method is called in preparation for multiplication on the next line.
Matrix A(4,4); // Jacobian
Matrix Ainv(4,4); // Inverse of Jacobian
Vector x(4); // Unknowns
Vector b(4); // Right-hand side
// Form initial b
// Solve using Newton's method
while (b.Norm() > tol) {
//
// Form A based on current x
//
A.Invert(Ainv); // DON'T DO THIS
dx = Ainv*b;
x -= dx;
//
// Update b based on current x
//
}
The use of the overloaded *
operator to get dx
is
an offense
that I’m willing to overlook.
What you should do instead is call the Matrix::Solve() method to get dx
directly.
A.Solve(b,dx);
x -= dx;
You may say the matrices at the element and material level are pretty small, so it’s not a big deal. But all the unnecessary computations add up.
Also, don’t get me wrong, there are some instances where computing and
storing the inverse is necessary within some element and material models
in OpenSees, e.g., in the mixedBeamColumn
element. In this formulation,
a matrix inverse is used a few times across multiple operations.
Ideally, we’d have the Matrix class store the factors from direct
solution so that one could call Matrix::Solve() multiple times,
performing backward substitution after the first call.
But that’s a PR for another day. And Frank doesn’t like it when people mess with the Matrix class.