Memory leak in flowengine - again
Issue:
There is a memory leak in FlowEngine that becomes apparent for larger simulations >10000 particles on long time scales (3-48 hrs+). Ultimately the leak will crash a relatively static FlowEngine simulation. The leak can be observed by running the following oedometer.py script [2] and then watching the 'mem' plot. On my desktop the standard oedometer.py script [2] increases ram by about 450 mb/hour [3], consuming about 7.9 Gb over 18 hrs. Further, I am not the only one to run into this issue since there was recently a question on launchpad regarding the same issue [1].
It is likely that the other issue related to this was never truly resolved (considering the magnitude of the leak appears to be about the same). I thought it was associated with the solver, but it turns out that this memleak is solver independent. I open a new issue here to avoid confusion of resolution.
Progress:
The leak only occurs when a meshUpdateInterval>0. Therefore the leak must be something to do with the creation and destruction of the triangulations during retriangulation or rebuilding the linear system of equations. When meshUpdateInterval>0
, updateTriangulation
is triggered within the FlowEngine and ultimately, the buildTriangulation()
method is called. I stepped through the buildTriangulation()
using keystroke input while monitoring ram usage and noticed that almost no ram is freed during flow.resetNetwork()
while additional ram is consumed during triangulate()
. So I figured the triangulation clearing was not deep enough, therefore I modified the tesselation method Clear()
to properly remove the entire triangulation and create a new one, but that does not fix the problem:
template<class TT>
void _Tesselation<TT>::Clear ( void )
{
delete Tri; // we have a memory leak somewhere in FlowEngine, trying to free the array consumed by old triangulation here
Tri = new RTriangulation; // asking for new heap space
Tes = Tri;
//Tri->clear(); // I suspected that this is not sufficient for freeing the old heap space. see full delete above
vertexHandles.clear();
vertexHandles.shrink_to_fit();
maxId=0;
}
I also tried using the vector method shrink_to_fit()
for all cellHandles
and vertexHandles
resizes. But it also did not fix the leak.
I've stepped through the construction and factorization of the linear system of equations using key stroke input, but the memory appears to be fully contained.
My final attempt was to run a fully debug compiled (-g -o0) version of Yade through valgrind but it was unable to identify the correct leak location.
Clue collection
- It only happens when we use a meshUpdateInterval
- It is solver independent
- It happens with the vanilla flowengine and vanilla oedometer.py.
- Memory does not increase at EACH remesh. Sometimes every third, fourth, fifth
- Memory seems to increase when there is more activity in the system (more movement of particles)? (not guaranteed yet)
- Valgrind reports nothing of substance wrt FlowEngine.
[1] https://answers.launchpad.net/yade/+question/683238
[2] Memory tracked and simplified oedometer.py, you will want to download [4] ('30000.spheres', the 30,000 sphere packing) to save time and avoid recreating the packing.:
from yade import pack, ymport,export
from yade import timing
import numpy as np
import shutil
import psutil
young=10e7 #250e7
finalFricDegree = 30
density = 2600
rate = 0.01
iterper=400
intRadius=1.0
packingName = '30000.spheres'
sp = O.bodies.append(ymport.textExt(packingName, 'x_y_z_r'))
# get packing dimensions
dim=utils.aabbExtrema()
xinf=dim[0][0]
xsup=dim[1][0]
X=xsup-xinf
yinf=dim[0][1]
ysup=dim[1][1]
Y=ysup-yinf
zinf=dim[0][2]
zsup=dim[1][2]
Z=zsup-zinf
mn,mx=Vector3(xinf, yinf,zinf),Vector3(xsup, ysup, zsup)
O.reset()
O.materials.append(FrictMat(density=density,young=young,poisson=.3,frictionAngle=radians(18.),label='spheres'))
O.materials.append(FrictMat(density=0,young=8.8e10,poisson=.8,frictionAngle=0.,label='walls'))
walls=aabbWalls([mn,mx],thickness=0,material='walls')
wallIds=O.bodies.append(walls)
sp = O.bodies.append(ymport.textExt(packingName, 'x_y_z_r',color=(0,0.2,0.7), material='spheres'))
O.engines=[
ForceResetter(),
InsertionSortCollider([Bo1_Sphere_Aabb(aabbEnlargeFactor=intRadius, label='Saabb'),Bo1_Box_Aabb()]),
InteractionLoop(
[Ig2_Sphere_Sphere_ScGeom6D(interactionDetectionFactor=intRadius, label='SSgeom'),Ig2_Box_Sphere_ScGeom6D()],
[Ip2_FrictMat_FrictMat_MindlinPhys(label="hertzIp")],
[Law2_ScGeom_MindlinPhys_Mindlin(includeAdhesion=True,label='hertzLaw')],label="iloop"
),
FlowEngine(dead=1,label="flow",multithread=False), GlobalStiffnessTimeStepper(active=1,timeStepUpdateInterval=100,timestepSafetyCoefficient=0.8),
TriaxialStressController(thickness=0,stressMask=7,internalCompaction=False,label='triax'),
NewtonIntegrator(gravity=(0,0,0), damping=0.4,label='newton')
]
setContactFriction(radians(finalFricDegree))
SSgeom.interactionDetectionFactor=1.
Saabb.aabbEnlargeFactor=1.
flow.dead=0
flow.debug=0
#flow.fluidBulkModulus=2.2e9
flow.defTolerance=-1
flow.meshUpdateInterval=200
flow.useSolver=3
flow.permeabilityFactor=1
flow.viscosity=0.001
O.dynDt=False
newton.damping=0
triax.internalCompaction=False
triax.wall_bottom_activated=False
triax.stressMask=2
triax.goal1=triax.goal3=0
triax.goal2=-11000
flow.bndCondIsPressure=[0,0,0,1,0,0]
flow.bndCondValue=[0,0,0,0,0,0]
flow.boundaryUseMaxMin=[0,0,0,0,0,0]
O.dt=0.1e-3
O.dynDt=False
from yade import plot
def history():
plot.addData(t=O.time,p=flow.getPorePressure((xsup/2,ysup/2,zsup/2)),
unbal = unbalancedForce(),
displacement=triax.depth0-triax.depth,
P = abs(O.forces.f(4)[2]),
mem = psutil.Process(os.getpid()).memory_info().rss/1e6 ,
speed = O.speed
)
O.engines=O.engines+[PyRunner(dead=0,iterPeriod=iterper,command='history()',label='recorder')]
plot.plots={'t':(('speed')),'t ':(('unbal')),' t':(('mem'))}
plot.plot()
O.run()
[4] 30000.spheres