Skip to content

Tutorial

This page shows how to wire solvers from this package into OpenSeesPy. Model-building details are omitted; see the OpenSeesPy documentation and examples. For install steps, see Installation.

Linear analysis

The finite element method discretizes a continuous structural model into nodes and elements. After assembly, the governing equations can be written, without loss of generality, as

\[ \mathbf{M} \ddot{\mathbf{u}} + \mathbf{C} \dot{\mathbf{u}} + \mathbf{p}_r(\mathbf{u}) = \mathbf{p}_f(t), \]

where \(\mathbf{M}\) is the mass matrix, \(\mathbf{C}\) is the damping matrix, \(\mathbf{u}\) is the vector of nodal displacements (and rotations), \(\mathbf{p}_r(\mathbf{u})\) collects resisting forces (which may be nonlinear in \(\mathbf{u}\)), and \(\mathbf{p}_f(t)\) is the applied load vector. For a static analysis the inertial term \(\mathbf{M} \ddot{\mathbf{u}}\) and the damping term \(\mathbf{C} \dot{\mathbf{u}}\) are absent.

OpenSees advances the solution in time with an incremental integrator. At each step the nonlinear equilibrium equations are linearized, typically in a Newton iteration, resulting in the linear system $$ \mathbf{A} \mathbf{x} = \mathbf{b}, $$

where \(\mathbf{A}\) is a matrix, and \(\mathbf{x}\) and \(\mathbf{b}\) are vectors. What they represent depends on the integrator and algorithm.

OpenSees assembles \(\mathbf{A}\) and \(\mathbf{b}\) from the model and passes them to a linear solver selected with the system command.

A linear solver object from this library can be used in OpenSeesPy with the following syntax:

ops.system("PythonSparse", solver.to_openseespy())

CPU direct solver

A good default on CPU is scipy.spsolve:

import openseespy.opensees as ops
from openseespy_solvers.scipy import spsolve

solver = spsolve()
ops.system("PythonSparse", solver.to_openseespy())

For larger sparse systems on CPU, scipy.umfpack is often faster once UMFPACK is installed (installation — UMFPACK).

GPU direct solver

If you have an NVIDIA GPU and the matching optional wheels (GPU install), use nvmath.direct_solver:

import openseespy.opensees as ops
from openseespy_solvers.nvmath import direct_solver

solver = direct_solver()
ops.system("PythonSparse", solver.to_openseespy())

When a full factorization is too expensive, try iterative solvers (cg, gmres) with a preconditioner, or hybrid to reuse a direct factorization as a GMRES preconditioner. See the API overview for all solver constructors.

Generalized eigenvalue analysis

Modal analysis solves the generalized eigenvalue problem:

\[ \left( \mathbf{K} - \lambda \mathbf{M} \right) \mathbf{\Phi} = \mathbf{0}. \]

Here \(\lambda\) is an eigenvalue, \(\mathbf{\Phi}\) is the corresponding eigenvector, \(\mathbf{K}\) is the stiffness matrix, and \(\mathbf{M}\) is the mass matrix. The eigenvalue is the square of the natural angular frequency, \(\omega = \sqrt{\lambda}\) (rad/s); \(\mathbf{\Phi}\) gives the mode shape.

An eigen solver object from this library can be used in OpenSeesPy with the following syntax:

lam = ops.eigen("PythonSparse", num_modes, eig_solver.to_openseespy())

CPU eigen solver (scipy.eigsh)

A good default on CPU is scipy.eigsh:

import openseespy.opensees as ops
from openseespy_solvers.scipy import eigsh

eig_solver = eigsh()
num_modes = 5
lam = ops.eigen("PythonSparse", num_modes, eig_solver.to_openseespy())

GPU eigen solver (cupy.eigsh)

A good default on GPU is cupy.eigsh:

import openseespy.opensees as ops
from openseespy_solvers.cupy import eigsh

eig_solver = eigsh()
num_modes = 5
lam = ops.eigen("PythonSparse", num_modes, eig_solver.to_openseespy())