OpenSees Cloud

OpenSees AMI

No Exit

Original Post - 24 Oct 2021 - Michael H. Scott

Visit Structural Analysis Is Simple on Substack.


Have you ever seen OpenSees.exe vanish all of a sudden or received a “Kernel died” message when running OpenSeesPy in a Jupyter Notebook?

Dead kernal in Jupyter Notebook

The C++ exit() function is the likely culprit. Or a segmentation fault. But this post will focus on the exit() function.

Some grepping and line counting reveals over 2,000 calls to exit() embedded in OpenSees/SRC. So why all the calls to exit()?

The one most frequently encountered occurrence is when the vector in the x-z plane supplied to the geometric transformation is parallel to the element x-axis. There’s an open GitHub issue on this topic along with a minimal working example and a proposed solution.

Beyond this common case with the geometric transformation, a large number of exit() calls are due to C++ constructors not being able to return an error code. If the input arguments are incorrect, the exit() function allows you to pull the plug on the entire analysis and not move on with bad inputs.

We used to have a g3ErrorHandler that would allow a graceful exit from a constructor, or elsewhere. Here is the error handler in use back in 2001–if the deformation points on the backbone of a HystereticMaterial are not strictly increasing, we signaled an error.

if (error) {
   g3ErrorHandler->fatal("%s -- input backbone is not unique (one-to-one)",
   "HystereticMaterial::HystereticMaterial");
}

But for some reason, the error handler was removed from OpenSees and now exit() is called.

if (error) {
   opserr << "HystereticMaterial::HystereticMaterial -- input backbone is not unique (one-to-one)\n";
   exit(-1);
}

Automatically correcting the inputs for a backbone function is not so straightforward, so removing this occurrence of exit() will require some thinking.

However, fixing inputs is relatively easy in other cases. Consider the constructor of the HyperbolicGapMaterial, which calls exit() if the input gap is positive instead of the desired negative.

HyperbolicGapMaterial::HyperbolicGapMaterial(int tag, double kmax, double kur, double rf, double fult, double gap0)
    :UniaxialMaterial(tag,MAT_TAG_HyperbolicGapMaterial),
    Kmax(kmax), Kur(kur), Rf(rf), Fult(fult), gap(gap0)
{
    if (gap>=0) {
        opserr << "HyperbolicGapMaterial::HyperbolicGapMaterial -- Initial gap size must be negative for compression-only material, setting to negative\n";
        exit(-1);
    }
    ...

No offense to the author of this material model, but instead of calling exit(), you could have just written gap = -gap; and let users move on despite their mistake. No doubt, there are many other similarly punitive uses of exit() in OpenSees and HyperbolicGapMaterial was not the first.

Calling exit() in the constructor became standard practice as the number of material models exploded. However, not all 2000+ exit() calls are from material models. The calls come from all over the OpenSees framework for various reasons.

Getting rid of the exit() calls will be challenging. I got rid of a few exits today and in doing so found easily fixable bugs in nearby lines of code. So, it will be a good thing to examine all the occurrences of exit().

The segmentation faults will be much more difficult to find.