OpenSees Cloud
OpenSees AMI
A Better Way to Find a Memory Leak in OpenSees
Original Post - 27 Dec 2023 - Michael H. Scott
Visit Structural Analysis Is Simple on Substack.
In a previous post, I explained how to find a memory leak in OpenSees. The basic idea was to put the analysis inside a loop, run the loop a million times, and monitor your operating system for increasing memory usage. A perfectly fine leak hunting approach–as long as you are willing to monitor your operating system in real time.
Recently, I found a slightly better approach that will allow you to make plots of memory usage and not run the analysis a million times–probably a few thousand instead.
Some light Googling led me to Stack Exchange (of course), where
this post
described how to use the
psutil
Python package to get memory usage
statistics. Although I only tried the package with Ubuntu 20.04, the
package should also work on Windows and MacOS.
Below is the leaky analysis from the aforementioned previous post.
First, use the os
package to get the process ID of your OpenSeesPy
script and pass the ID when creating a psutil
Process object. You can
obtain the memory used by this process by calling the
memory_info()
method–the 'rss'
value (index 0) of the returned tuple gives the
physical memory used by the process.
Put the wipe
command at the end of the analysis loop, then get the
memory information. Plot the memory usage and see if the usage creeps up
with the repeated analyses.
import openseespy.opensees as ops
import matplotlib.pyplot as plt
import os
import psutil
pid = os.getpid()
thisprocess = psutil.Process(pid)
Nruns = 2500
mem = [0]*Nruns
MB = 1024**2 # Megabytes
for i in range(Nruns):
ops.wipe()
ops.model('basic','-ndm',3,'-ndf',6)
ops.node(1,0,0,0)
ops.node(2,1,0,0)
ops.node(3,1,1,0)
ops.node(4,0,1,0)
ops.fix(1,1,1,1,0,0,0)
ops.fix(2,1,1,1,0,0,0)
ops.fix(3,1,1,1,0,0,0)
ops.fix(4,1,1,1,0,0,0)
# No memory leak
#ops.nDMaterial('ElasticIsotropic',1,20,0.1)
# Causes memory leak
ops.nDMaterial('ElasticOrthotropic',1,20,20,20,0.1,0.1,0.1,10,10,10)
ops.section('PlateFiber',1,1,0.1)
ops.element('ShellMITC4',1,1,2,3,4,1)
ops.timeSeries('Constant',1)
ops.pattern('Plain',1,1)
ops.load(1,0,0,0,20,0,0)
ops.system('UmfPack')
ops.numberer('RCM')
ops.constraints('Plain')
ops.integrator('LoadControl',0)
ops.algorithm('Newton')
ops.analysis('Static')
ops.analyze(1)
ops.wipe()
if i == 0: # First run
mem0 = thisprocess.memory_info()[0]
mem[i] = (thisprocess.memory_info()[0] - mem0)/MB
plt.plot(mem,'-k')
The memory leak for the PlateFiber
material wrapper was plugged over a
year ago via
web edit,
so I downgraded OpenSeesPy
to version 3.3.0.1 in
order to reproduce the leak. Running the above scripts gives the
following plots when using the ElasticIsotropic
material (no wrapper, no
memory leak) and the ElasticOrthotropic
material (with wrapper, memory
leak).
Note that it takes many analysis runs (nearly 1000 in this case) for the memory leak to overcome the initial memory allocated to this process by the operating system. As the analysis runs continue, chunks of memory are allocated periodically, creating a stepped plot of memory usage as the leak gobbles up the memory chunks.
The plot of memory usage will look different depending on the magnitude of the memory leak, the size of the model, the length of each analysis run, and the operating system.
The nice thing is you can run the script and walk away. When you come back, a plot is waiting for you!