-
Notifications
You must be signed in to change notification settings - Fork 670
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Issue 2656 nsgrid segfault #2665
Conversation
package/MDAnalysis/lib/distances.py
Outdated
@@ -1102,9 +1102,9 @@ def _nsgrid_capped_self(reference, max_cutoff, min_cutoff=None, box=None, | |||
gridsearch = FastNS(max_cutoff, reference, box=box) | |||
results = gridsearch.self_search() | |||
|
|||
pairs = results.get_pairs()[::2, :] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Here we used to double save pairs then slice them, so instead you can just not double save
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Does anyone depend on that behavior? This is the unit test change below?
package/MDAnalysis/lib/nsgrid.pyx
Outdated
|
||
cdef ns_int i, cellindex = -1 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The old data structure was a sort of 2 dimensional array, which required two passes through the data to first calculate the size to allocate, then another to fill this array. Instead a simple linked list (of fixed and smaller size) can be used
searchcoords_bbox = self.box.fast_put_atoms_in_bbox(searchcoords) | ||
searchgrid = _NSGrid(searchcoords_bbox.shape[0], self.grid.cutoff, self.box, self.max_gridsize, force=True) | ||
searchgrid.fill_grid(searchcoords_bbox) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The second grid didn't need to get populated, just sized out for later calculations
package/MDAnalysis/lib/nsgrid.pyx
Outdated
j = self.grid.cell_head[cellindex_probe] | ||
while j != -1: | ||
# find distance between search coords[i] and coords[j] | ||
d2 = self.box.fast_distance2(&searchcoords_bbox[i, XX], | ||
&self.coords_bbox[j, XX]) | ||
if d2 <= cutoff2: | ||
results.add_neighbors(current_beadid, bid, d2) | ||
npairs += 1 | ||
results.add_neighbors(i, j, d2) | ||
|
||
j = self.grid.next_id[j] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is now looping through a linked list of unknown size (-1 terminates)
@@ -231,3 +231,14 @@ def test_nsgrid_probe_close_to_box_boundary(): | |||
expected_dists = np.array([2.3689647], dtype=np.float64) | |||
assert_equal(results.get_pairs(), expected_pairs) | |||
assert_allclose(results.get_pair_distances(), expected_dists, rtol=1.e-6) | |||
|
|||
|
|||
def test_zero_max_dist(): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This previously used to segfault (see #2656)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could you add a comparison to the expected value of no pairs found?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If you're going to supersede my PR to fix the same issue, you should probably reference it: #2657
Some initial comments:
- Why combine performance improvements with a bug fix in the same PR?
- Why not include the unit tests Lily and I wrote in BUG: fix segfault with 0.0 around sels #2657 for
around 0.0
selections?
Codecov Report
@@ Coverage Diff @@
## develop #2665 +/- ##
===========================================
- Coverage 91.22% 91.15% -0.07%
===========================================
Files 176 159 -17
Lines 24033 21936 -2097
Branches 3140 3175 +35
===========================================
- Hits 21923 19996 -1927
+ Misses 1488 1327 -161
+ Partials 622 613 -9
Continue to review full report at Codecov.
|
@@ -213,7 +213,7 @@ def test_nsgrid_selfsearch(box, result): | |||
searcher = nsgrid.FastNS(cutoff, points, box=box) | |||
searchresults = searcher.self_search() | |||
pairs = searchresults.get_pairs() | |||
assert_equal(len(pairs)//2, result) | |||
assert_equal(len(pairs), result) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
explanation for changing the result of an old unit test?
I think the performance improvements need to be split out to a separate PR with an indication of the benchmarks reflecting the performance change. None of the comments here currently clearly explain which change actually fixed the issue, which is detracting from the review process. |
ab0cf0e
to
1b7d43f
Compare
def test_around_superposed_small_res(u_pbc_triclinic): | ||
ag = u_pbc_triclinic.select_atoms('around 0.0 resid 10') | ||
assert len(ag) == 0 | ||
|
||
|
||
def test_around_superposed_large_res(u_pbc_triclinic): | ||
ag = u_pbc_triclinic.select_atoms('around 0.0 resid 3') | ||
assert len(ag) == 0 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These are a little different to Lily's tests, I think the correct answer is 0 atoms, because an Around selection won't select the thing it's around-ing (https://www.mdanalysis.org/docs/documentation_pages/selections.html#geometric)
I think the small box (0.001) is causing an infinite loop here but I'm looking into it....
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I split out the tests by residue size because the smaller one uses the brute-force method and the larger one uses the nsgrid method. bruteforce
never seg-faulted so I'm not sure that test_around_small_res
is needed, and it would be nice to save time on tests given #2671.
To nitpick the test_around_superposed_large_res
name -- the larger box means there are no more atoms in exactly the same spot anymore, so the superposed
isn't really accurate anymore. Also, it would be helpful for future readers to rename or comment the test to make clear which method of capped_distance
is being tested here.
if not force: | ||
# Calculate best cutoff, with 0.01A minimum | ||
cutoff = max(cutoff, 0.01) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is the fix here
Edit: sorry, that was wrong, sick brain. Haven't looked at your code but it's nice that the below finds the mirrored atoms! 🎉 Thanks! >>> import MDAnalysis as mda
>>> from MDAnalysis.lib import distances
>>> import numpy as np
>>> u = mda.Universe.empty(60, trajectory=True)
>>> xyz = np.zeros((60, 3))
>>> x = np.tile(np.arange(12), (5,))+np.repeat(np.arange(5)*100, 12)
>>> x # 5 images of 12 atoms
array([ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 100,
101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 200, 201,
202, 203, 204, 205, 206, 207, 208, 209, 210, 211, 300, 301, 302,
303, 304, 305, 306, 307, 308, 309, 310, 311, 400, 401, 402, 403,
404, 405, 406, 407, 408, 409, 410, 411])
>>> xyz[:, 0] = x # y and z are 0
>>> u.load_new(xyz)
<Universe with 60 atoms>
>>> u.dimensions = [100, 100, 100, 60, 60, 60]
>>> dist = distances.distance_array(u.atoms[:12].positions,
... u.atoms[12:].positions,
... box=u.dimensions)
>>> np.count_nonzero(np.any(dist <= 0.0, axis=0))
48
>>> u.select_atoms('around 0.0 index 0:11')
<AtomGroup with 48 atoms> |
The patch looks more focused here now, I'll close my other PR and let you and Lily wrap this up then. |
Performance changes have been separate out & Lily is checking Richard's unit tests
@lilyminium your original test: box = np.array([boxsize, boxsize, boxsize, 60., 60., 60], dtype=np.float32)
u = mda.Universe(PDB)
u.dimensions = box
u.select_atoms('around 0.0 resid 3') still causes the interpreter to hang. And it doesn't like being interrupted so I think it's something in the C/Cython layer. I'm going to try and fix that in this PR too as it's related then hopefully this is good to go. |
@lilyminium ok I made a new issue for the tiny box - #2670 it seems separate from the zero sized box issue. I think this PR is finished for fixing #2656 and can be reviewed |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think adding the test in #2665 (comment) would be beneficial as it is easily interpreted and all the other tests find 0 atoms overlapping.
Otherwise LGTM but I'm not very familiar with Cython or the lib
module.
@@ -231,3 +231,14 @@ def test_nsgrid_probe_close_to_box_boundary(): | |||
expected_dists = np.array([2.3689647], dtype=np.float64) | |||
assert_equal(results.get_pairs(), expected_pairs) | |||
assert_allclose(results.get_pair_distances(), expected_dists, rtol=1.e-6) | |||
|
|||
|
|||
def test_zero_max_dist(): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could you add a comparison to the expected value of no pairs found?
def test_around_superposed_small_res(u_pbc_triclinic): | ||
ag = u_pbc_triclinic.select_atoms('around 0.0 resid 10') | ||
assert len(ag) == 0 | ||
|
||
|
||
def test_around_superposed_large_res(u_pbc_triclinic): | ||
ag = u_pbc_triclinic.select_atoms('around 0.0 resid 3') | ||
assert len(ag) == 0 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I split out the tests by residue size because the smaller one uses the brute-force method and the larger one uses the nsgrid method. bruteforce
never seg-faulted so I'm not sure that test_around_small_res
is needed, and it would be nice to save time on tests given #2671.
To nitpick the test_around_superposed_large_res
name -- the larger box means there are no more atoms in exactly the same spot anymore, so the superposed
isn't really accurate anymore. Also, it would be helpful for future readers to rename or comment the test to make clear which method of capped_distance
is being tested here.
segfaulting on zero box size
fixes Issue #2656
1b66dd8
to
96a074d
Compare
Ok @lilyminium I think I've addressed comments |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Assuming Travis is green, LGTM! Thank you!
Co-authored-by: Lily Wang <[email protected]>
Fixes #2656
Changes made in this Pull Request:
PR Checklist