OpenSees Cloud
OpenSees AMI
Runnin' Down a Leak
Original Post - 04 Jun 2023 - Michael H. Scott
Visit Structural Analysis Is Simple on Substack.
Issue #1214 by zAlexliu-8895 on OpenSees GitHub demonstrated a memory leak with creating patches for fiber sections. The script posted with the GitHub issue is reproduced below.
import openseespy.opensees as ops
Counter = 0
while Counter < 100000000:
Counter += 1
ops.wipe()
ops.model('basic', '-ndm', 2, '-ndf', 3)
ops.uniaxialMaterial("Concrete02", 1, -33, -0.0015, -20, -0.005, 0.1, 2.2, 1100)
ops.section("Fiber", 1, "-GJ", 200e9)
ops.patch("quad", 1, 40, 10, -200, -38, 200, -38, 200, 62, -200, 62)
Sure enough, if you run this simple script, you’ll see the available system memory go to zero, throwing your device into a tizzy.
Thank you zAlexLiu-8895 for making a minimal leaking example that clearly illustrates the issue!
In running this leak down, I asked myself three questions.
1. Does The Leak Happen in Tcl?
Since the script is short and simple, a Tcl version was easy to create.
set counter 0
while {$counter < 100000000} {
incr counter
wipe
model basic -ndm 2 -ndf 3
uniaxialMaterial Concrete02 1 -33 -0.0015 -20 -0.005 0.1 2.2 1100
section Fiber 1 -GJ 200e9 {
patch quad 1 40 10 -200 -38 200 -38 200 62 -200 62
}
}
No, no memory leak with Tcl. So the issue must be in parsing the Python commands, not in the constructors and destructors of the C++ classes.
2. Is the Leak Due to the Material?
Back to the original Python script for a quick check to see if the issue is somehow related to parsing the Concrete02
command. After changing the material to Elastic
, there’s still a memory leak. So, the problem is definitely in parsing the section
and patch
commands for OpenSeesPy.
3. What Does valgrind Have to Say?
Rather than stare at C++ code and try to match every new with a delete, I usually turn to valgrind
, a Linux-based tool for detecting memory management issues. You can use valgrind
with the OpenSeesPy pip package or with a locally compiled OpenSeesPy library.
OpenSeesPy pip Package
Run valgrind
with the OpenSeesPy package to get basic information on a leak. First, decrease the counter limit from 100000000 down to like 100, otherwise the script will take a while to run through valgrind
–and, more importantly, you don’t want to run out of memory while valgrinding.
import openseespy.opensees as ops
Counter = 0
while Counter < 100:
Counter += 1
ops.wipe()
ops.model('basic', '-ndm', 2, '-ndf', 3)
ops.uniaxialMaterial("Concrete02", 1, -33, -0.0015, -20, -0.005, 0.1, 2.2, 1100)
ops.section("Fiber", 1, "-GJ", 200e9)
ops.patch("quad", 1, 40, 10, -200, -38, 200, -38, 200, 62, -200, 62)
Next, run valgrind
from the command line, passing flags to check for and show
all leaks and to dump the results to a log file.
~/$ valgrind --leak-check=full --show-leak-kinds=all --log-file=leaks.txt python3 fiberSection.py
When you open leaks.txt
, you’ll see there’s a lot going on. But search for the word “definitely” and you’ll find information on the memory leak.
The log file says we’re definitely leaking memory in OPS_Patch(), from
somewhere in the opensees.so
library of the OpenSeesPy package installation.
For this simple model, that’s probably enough information to track down the
issue.
But if the leak is not so obvious and/or you want to get more detailed information out of valgrind
, use a compiled version of the opensees.so
library.
OpenSeesPy Library Compiled in Debug Mode
To take full advantage of valgrind
, you need to recompile OpenSees in DEBUG mode. In DEBUG mode, lots of hooks, symbols, and assertions are added to the compiled OpenSees object files, making it easy for valgrind and other debugging tools to figure out what’s going on and to relay that information on to you.
Due to all the added stuff, OpenSees runs slowly in DEBUG mode. So, be sure to get out of DEBUG mode after you’ve finished debugging. More on that later.
First, in OpenSees/Makefile.def
, set the DEBUG_MODE
variable to DEBUG
–or simply comment the NO_DEBUG
line.
DEBUG_MODE = DEBUG
#DEBUG_MODE = NO_DEBUG
Next, wipe your current build of opensees.so
and re-build everything in DEBUG mode.
~/OpenSees$ make wipe
~/OpenSees$ make python -j 4
The re-build will take a few minutes. When the job is done, change the import statement from the OpenSeesPy package to the opensees.so
Python library that was just built.
import sys
sys.path.append('/home/mhscott/OpenSees/SRC/interpreter')
import opensees as ops
#import openseespy.opensees as ops
Counter = 0
while Counter < 100:
Counter += 1
ops.wipe()
ops.model('basic', '-ndm', 2, '-ndf', 3)
ops.uniaxialMaterial("Concrete02", 1, -33, -0.0015, -20, -0.005, 0.1, 2.2, 1100)
ops.section("Fiber", 1, "-GJ", 200e9)
ops.patch("quad", 1, 40, 10, -200, -38, 200, -38, 200, 62, -200, 62)
Now, run valgrind with the same options as before.
~/$ valgrind --leak-check=full --show-leak-kinds=all --log-file=leaks.txt python3 fiberSection.py
With this execution of valgrind
, the leaks.txt
file will show better information.
The valgrind
log file now says the leak in OPS_Patch() emanates from line 535 of OpenSeesSectionCommands.cpp. The problem is we’re not deleting the Fiber object that was allocated on this line of code.
The fix was easy, and you can see details in PR #1215.
Now we can confirm that the leak is plugged.
Compile OpenSees with the updated code, still in DEBUG mode (no need to first make wipe
this time), then run valgrind
again. Open the log file and search for “definitely”–you’ll see there were no leaks.
Yay! Leak plugged.
Now, so that you don’t keep running OpenSees in sluggish DEBUG mode, uncomment the NO_DEBUG
line in Makefile.def
.
DEBUG_MODE = DEBUG
DEBUG_MODE = NO_DEBUG
Then rebuild from zero again, this time removing all the debugging symbols from the object files.
~/OpenSees$ make wipe
~/OpenSees$ make python -j 4
Now you’re good to go.
Note that other types of leaks will require asking different questions. But whatever type of leak it is, using valgrind
will be incredibly helpful. In fact, use valgrind every now and then on your IDA and Monte Carlo scripts–some leaks are likely to appear. Let me know what you find.
Before you run down the next leak, enjoy some Tom Petty.