OpenSees Cloud

OpenSees AMI

Gotta Catch 'Em All

Original Post - 10 Sep 2023 - Michael H. Scott

Visit Structural Analysis Is Simple on Substack.


Python has built-in Exception types for dealing with various run-time errors. You’ve probably seen a few Exceptions like ModuleNotFoundError if you had issues installing OpenSeesPy or KeyboardInterrupt when you have used Ctrl+C to get your script out of an infinite loop.

A common Exception encountered within a script is divide by zero, in which case an Exception of type ZeroDivisionError is thrown.

Divide by zero

Of course you would never actually type 1/0 in your script. Instead, in the denominator you will use a variable expression that could become zero.

At any rate, because the Exception was not caught, or dealt with, the script stops immediately.

To catch an Exception, use a try-except block.

Divide by zero in try-except block

With the Exception caught, the script is able to move on–despite the divide by zero–and print Hello to the screen.

Now you may ask, what do Exceptions have to do with OpenSees model building and analysis?

To answer this question, keep in mind that OpenSees is a Python module and in that module are many exceptions waiting to happen–incorrect input commands, errors when saving or restoring a database, failure of an eigenvalue solver–the list goes on. The core of OpenSees simply wasn’t set up to throw exceptions back to a script.

Fortunately, Minjie, who is always a few years ahead of everyone else, had the foresight to define a new Exception type, OpenSeesError, when he made the OpenSees Python module.

You have undoubtedly seen the OpenSeesError Exception raised in your scripts. For example, when you have insufficient or incorrect inputs to a material model.

Material command error in try-except block

The Exception string tells you to see the standard error (stderr) output for more information on what caused the exception.

With the Exception not caught, the script stops. In the simple case above, your OpenSees analysis probably shouldn’t continue anyway. However, there are some cases where it would be nice to keep going.

Imagine a script where you are looping over frame models of various sizes and performing eigenvalue analysis to get the periods and mode shapes before launching an awesome IDA with modal damping. But in that script you ask for too many modes and the eigen() command will fail and throw an Exception on smaller models with few dynamic DOFs. You don’t want one bad eigen() command to kill the entire parametric IDA, so what should you do?

You should put the eigen() command in a try-except block and catch exceptions of type OpenSeesError (EDIT based on comment from P. Talley). If the default eigen() command fails, you can bite the bullet and use the FullGenLapack option, or try the default eigen() command again asking for fewer modes, or set a flag and move on to the next analysis, or whatever.

try:
    w2 = ops.eigen(Nmodes)
except ops.OpenSeesError as e:
    print(e) # Not required
    w2 = ops.eigen('fullGenLapack',Nmodes)

print(w2)
ops.modalDamping(....)
# etc.

The point is, your script won’t grind to a halt due to an OpenSeesError Exception emanating from the OpenSees core–you and your parametric IDA can recover and move on.

I’m not advocating that you put every OpenSees command in a try-except block, just the commands that routinely stop your scripts.