Skip to content

Commit fb40490

Browse files
authored
Fix dangling pointers when running multiple FF calculations in a row (#322)
The force field arrays were left as dangling pointers when running multiple computational steps in a row, which manifested in #316. This fixes the issue by setting some cleanup code to run on garbage collection. It's not very pretty since you also have to manually run the garbage collector to make sure that the pointers are deleted in time, but it works.
1 parent 2d8c126 commit fb40490

10 files changed

+66
-2
lines changed

examples/pyridineDensOverlap/example_pyridine_density_overlap.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ def example_pyridine_density_overlap():
3232

3333
generate_conv_rho("-s CHGCAR.xsf -t tip/density_CO.xsf -B 1.0 -E".split())
3434
generate_elff("-i LOCPOT.xsf --tip_dens tip/density_CO.xsf --Rcore 0.7 -E --doDensity".split())
35-
generate_dftd3("-i LOCPOT.xsf --df_name PBE --energy".split())
35+
generate_dftd3("-i LOCPOT.xsf --df_name PBE".split())
3636

3737
relaxed_scan("-k 0.25 -q 1.0 --noLJ --Apauli 18.0 --bDebugFFtot".split()) # Note the --noLJ for loading separate Pauli and vdW instead of LJ force field
3838
plot_results("-k 0.25 -q 1.0 -a 2.0 --df".split())

ppafm/HighLevel.py

+5-1
Original file line numberDiff line numberDiff line change
@@ -191,6 +191,9 @@ def perform_relaxation(
191191
PPdisp = None
192192
if verbose > 0:
193193
print("<<<END: perform_relaxation()")
194+
195+
core.deleteFF_Fpointer()
196+
194197
return fzs, PPpos, PPdisp, lvecScan
195198

196199

@@ -204,7 +207,8 @@ def prepareArrays(FF, Vpot, parameters):
204207
gridN = parameters.gridN
205208
FF = np.zeros((gridN[2], gridN[1], gridN[0], 3))
206209
else:
207-
parameters.gridN = np.shape(FF)
210+
gridN = np.shape(FF)
211+
parameters.gridN = gridN
208212
core.setFF_Fpointer(FF)
209213
if Vpot:
210214
V = np.zeros((gridN[2], gridN[1], gridN[0]))

ppafm/cli/conv_rho.py

+6
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
#!/usr/bin/python
2+
import gc
3+
24
import numpy as np
35

46
from .. import common, fieldFFT, io
@@ -69,6 +71,10 @@ def main(argv=None):
6971
force_field = io.packVecGrid(f_x * args.Apauli, f_y * args.Apauli, f_z * args.Apauli)
7072
io.save_vec_field("FF" + namestr, force_field, lvec_sample, data_format=args.output_format, head=head_sample)
7173

74+
# Make sure that the energy and force field pointers are deleted so that they don't interfere if any other force fields are computed after this.
75+
del energy, force_field
76+
gc.collect()
77+
7278

7379
if __name__ == "__main__":
7480
main()

ppafm/cli/generateDFTD3.py

+4
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
#!/usr/bin/env python3
22

3+
import gc
34
import sys
45

56
from ppafm.defaults import d3
@@ -50,6 +51,9 @@ def main(argv=None):
5051

5152
computeDFTD3(args.input, df_params=df_params, geometry_format=args.input_format, save_format=args.output_format, compute_energy=args.energy, parameters=parameters)
5253

54+
# Make sure that the energy and force field pointers are deleted so that they don't interfere if any other force fields are computed after this.
55+
gc.collect()
56+
5357

5458
if __name__ == "__main__":
5559
main()

ppafm/cli/generateElFF.py

+5
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
#!/usr/bin/python
2+
import gc
23
import sys
34
from pathlib import Path
45

@@ -161,6 +162,10 @@ def main(argv=None):
161162
if args.energy:
162163
io.save_scal_field("Eel", e_electrostatic, lvec_samp, data_format=args.output_format, head=head_samp, atomic_info=(atoms_samp[:4], lvec_samp))
163164

165+
# Make sure that the energy and force field pointers are deleted so that they don't interfere if any other force fields are computed after this.
166+
del e_electrostatic, ff_electrostatic
167+
gc.collect()
168+
164169

165170
if __name__ == "__main__":
166171
main()

ppafm/cli/generateElFF_point_charges.py

+5
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
#!/usr/bin/python
22

3+
import gc
4+
35
from .. import common
46
from ..HighLevel import computeELFF_pointCharge
57

@@ -13,6 +15,9 @@ def main(argv=None):
1315

1416
computeELFF_pointCharge(args.input, geometry_format=args.input_format, tip=args.tip, save_format=args.output_format, computeVpot=args.energy, parameters=parameters)
1517

18+
# Make sure that the energy and force field pointers are deleted so that they don't interfere if any other force fields are computed after this.
19+
gc.collect()
20+
1621

1722
if __name__ == "__main__":
1823
main()

ppafm/cli/generateLJFF.py

+4
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
#!/usr/bin/python
2+
import gc
23
from pathlib import Path
34

45
from .. import common
@@ -22,6 +23,9 @@ def main(argv=None):
2223
parameters=parameters,
2324
)
2425

26+
# Make sure that the energy and force field pointers are deleted so that they don't interfere if any other force fields are computed after this.
27+
gc.collect()
28+
2529

2630
if __name__ == "__main__":
2731
main()

ppafm/core.py

+21
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
#!/usr/bin/python
22

3+
import weakref
34
from ctypes import POINTER, Structure, c_double, c_int
45

56
import numpy as np
@@ -76,6 +77,7 @@ def setFF_shape(n_, cell, parameters=None):
7677

7778
def setFF_Fpointer(gridF):
7879
lib.setFF_Fpointer(gridF)
80+
weakref.finalize(gridF, deleteFF_Fpointer) # Set array pointer to NULL when garbage collector runs.
7981

8082

8183
# void setFF_pointer( double * gridF, double * gridE )
@@ -85,6 +87,25 @@ def setFF_Fpointer(gridF):
8587

8688
def setFF_Epointer(gridE):
8789
lib.setFF_Epointer(gridE)
90+
weakref.finalize(gridE, deleteFF_Epointer) # Set array pointer to NULL when garbage collector runs.
91+
92+
93+
# void deleteFF_Fpointer()
94+
lib.deleteFF_Fpointer.argtypes = []
95+
lib.deleteFF_Fpointer.restype = None
96+
97+
98+
def deleteFF_Fpointer():
99+
lib.deleteFF_Fpointer()
100+
101+
102+
# void deleteFF_Epointer()
103+
lib.deleteFF_Epointer.argtypes = []
104+
lib.deleteFF_Epointer.restype = None
105+
106+
107+
def deleteFF_Epointer():
108+
lib.deleteFF_Epointer()
88109

89110

90111
def setFF(cell=None, gridF=None, gridE=None, parameters=None):

ppafm/cpp/ProbeParticle.cpp

+10
Original file line numberDiff line numberDiff line change
@@ -344,6 +344,16 @@ DLLEXPORT void setFF_Epointer( double * gridE_ ){
344344
gridE = gridE_;
345345
}
346346

347+
// set force field array pointer to NULL
348+
DLLEXPORT void deleteFF_Fpointer(){
349+
gridF = NULL;
350+
}
351+
352+
// set energy array pointer to NULL
353+
DLLEXPORT void deleteFF_Epointer(){
354+
gridE = NULL;
355+
}
356+
347357
// set forcefield grid dimension "n"
348358
DLLEXPORT void setGridN( int * n ){
349359
//gridShape.n.set( *(Vec3i*)n );

tests/_test_vdw.py

+5
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44
Compare the C++ and OpenCL implementations of the vdW calculation and check that they are consistent.
55
"""
66

7+
import gc
8+
79
import numpy as np
810
import pyopencl as cl
911

@@ -102,3 +104,6 @@ def test_dftd3():
102104
assert np.allclose(coeffs_ocl, coeffs_cpp)
103105
assert np.allclose(FF_ocl[..., :3], FF_cpp, rtol=1e-4, atol=1e-6)
104106
assert np.allclose(FF_ocl[..., 3], E_cpp, rtol=1e-4, atol=1e-6)
107+
108+
del E_cpp, FF_cpp
109+
gc.collect()

0 commit comments

Comments
 (0)