Skip to content

Commit 735abb8

Browse files
Merge pull request #3441 from scal444/nsgrid_debug
Add check for too many grids in NSGrid
2 parents a33e303 + 6afc2dc commit 735abb8

File tree

5 files changed

+57
-9
lines changed

5 files changed

+57
-9
lines changed

.github/workflows/gh-ci.yaml

+1
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,7 @@ jobs:
9494
gfortran-9 -v
9595
echo "FC=gfortran-9" >> $GITHUB_ENV
9696
echo "numprocs=3" >> $GITHUB_ENV
97+
echo "ENABLE_HIGH_MEM_UNIT_TESTS=true" >> $GITHUB_ENV
9798
9899
- name: setup_linux
99100
if: startsWith(matrix.os, 'ubuntu')

package/AUTHORS

+1
Original file line numberDiff line numberDiff line change
@@ -170,6 +170,7 @@ Chronological list of authors
170170
- Filip T. Szczypiński
171171
- Marcelo C. R. Melo
172172
- Mark D. Driver
173+
- Kevin Boyd
173174

174175

175176
External code

package/CHANGELOG

+2-2
Original file line numberDiff line numberDiff line change
@@ -13,11 +13,11 @@ The rules for this file:
1313
* release numbers follow "Semantic Versioning" http://semver.org
1414

1515
------------------------------------------------------------------------------
16-
??/??/?? IAlibay, melomcr, mdd31, ianmkenney, richardjgowers, hmacdope, orbeckst
17-
16+
??/??/?? IAlibay, melomcr, mdd31, ianmkenney, richardjgowers, hmacdope, orbeckst, scal444
1817
* 2.1.0
1918

2019
Fixes
20+
* Avoid integer overflow in NSGrid with too many grids (Issue #3183)
2121
* Fix ITPParser charge reading (Issue #3419).
2222
* TRRWriter preserves timestep data (Issue #3350).
2323
* Fixed Universe creation from a custom object which only provided a topology,

package/MDAnalysis/lib/nsgrid.pyx

+12-4
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,9 @@ not reflect in the results.
7171
.. versionadded:: 0.19.0
7272
.. versionchanged:: 1.0.2
7373
Rewrote module
74-
74+
.. versionchanged:: 2.1.0
75+
Capped max grid dimension to 1290, which when cubed is the max value of
76+
a 32 bit signed integer.
7577
7678
Classes
7779
-------
@@ -90,6 +92,10 @@ DEF XZ = 6
9092
DEF YZ = 7
9193
DEF ZZ = 8
9294

95+
# Cube root of the maximum size of a 32 bit signed integer. If the system is divided into more
96+
# grids than this, integer overflow will occur.
97+
DEF MAX_GRID_DIM = 1290
98+
9399
ctypedef float coordinate[3];
94100

95101
cdef extern from "calc_distances.h" nogil:
@@ -342,9 +348,11 @@ cdef class FastNS(object):
342348
# add 0.001 here to avoid floating point errors
343349
# will make cells slightly too large as a result, ah well
344350
min_cellsize += 0.001
345-
self.ncells[0] = <int> math.floor(self.triclinic_dimensions[XX] / min_cellsize)
346-
self.ncells[1] = <int> math.floor(self.triclinic_dimensions[YY] / min_cellsize)
347-
self.ncells[2] = <int> math.floor(self.triclinic_dimensions[ZZ] / min_cellsize)
351+
# If the cell size is too small, indexing overflow will occur. Limit the number
352+
# of cells in any dimension to the cube root of the maximum of 32 bit integer values.
353+
self.ncells[0] = <int> min(math.floor(self.triclinic_dimensions[XX] / min_cellsize), MAX_GRID_DIM)
354+
self.ncells[1] = <int> min(math.floor(self.triclinic_dimensions[YY] / min_cellsize), MAX_GRID_DIM)
355+
self.ncells[2] = <int> min(math.floor(self.triclinic_dimensions[ZZ] / min_cellsize), MAX_GRID_DIM)
348356

349357
self.pbc = pbc
350358
# If there aren't enough cells in a given dimension it's equivalent to one

testsuite/MDAnalysisTests/lib/test_nsgrid.py

+41-3
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,9 @@
2121
# J. Comput. Chem. 32 (2011), 2319--2327, doi:10.1002/jcc.21787
2222
#
2323

24+
from distutils.util import strtobool
25+
import os
26+
2427
import pytest
2528

2629
from collections import defaultdict, Counter
@@ -65,9 +68,11 @@ def test_pbc_box(box):
6568
nsgrid.FastNS(4.0, coords, box=box)
6669

6770

68-
@pytest.mark.parametrize('cutoff', [-4, 100000])
69-
def test_nsgrid_badcutoff(universe, cutoff):
70-
with pytest.raises(ValueError):
71+
@pytest.mark.parametrize('cutoff, match', ((-4, "Cutoff must be positive"),
72+
(100000,
73+
"Cutoff 100000 too large for box")))
74+
def test_nsgrid_badcutoff(universe, cutoff, match):
75+
with pytest.raises(ValueError, match=match):
7176
run_grid_search(universe, 0, cutoff)
7277

7378

@@ -371,3 +376,36 @@ def test_issue_2670():
371376

372377
# should return the one atom overlap
373378
assert len(ag2.select_atoms('around 0.0 resid 3')) == 1
379+
380+
381+
def high_mem_tests_enabled():
382+
""" Returns true if ENABLE_HIGH_MEM_UNIT_TESTS is set to true."""
383+
env = os.getenv("ENABLE_HIGH_MEM_UNIT_TESTS", default="0")
384+
try:
385+
return strtobool(env)
386+
except ValueError:
387+
return False
388+
389+
390+
reason = ("Turned off by default. The test can be enabled by setting "
391+
"the ENABLE_HIGH_MEM_UNIT_TESTS "
392+
"environment variable. Make sure you have at least 10GB of RAM.")
393+
394+
395+
# Tests that with a tiny cutoff to box ratio, the number of grids is capped
396+
# to avoid indexing overflow. Expected results copied from test_nsgrid_search
397+
# with no box.
398+
@pytest.mark.skipif(not high_mem_tests_enabled(), reason=reason)
399+
def test_issue_3183():
400+
np.random.seed(90003)
401+
points = (np.random.uniform(low=0, high=1.0,
402+
size=(100, 3)) * (10.)).astype(np.float32)
403+
cutoff = 2.0
404+
query = np.array([1., 1., 1.], dtype=np.float32).reshape((1, 3))
405+
box = np.array([10000., 10000., 10000., 90., 90., 90.])
406+
407+
searcher = nsgrid.FastNS(cutoff, points, box)
408+
searchresults = searcher.search(query)
409+
indices = searchresults.get_pairs()[:, 1]
410+
want_results = [3, 13, 24]
411+
assert_equal(np.sort(indices), want_results)

0 commit comments

Comments
 (0)