-
Notifications
You must be signed in to change notification settings - Fork 824
*Group.unwrap #2189
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
Merged
+1,069
−18
Merged
*Group.unwrap #2189
Changes from all commits
Commits
Show all changes
21 commits
Select commit
Hold shift + click to select a range
e8319b2
made inplace position modification of make_whole() optional
zemanj a43b6be
added *Group.unwrap() method
zemanj 44cd709
fixed docs of make_whole()
zemanj e89a89d
cleaned up unwrap()
zemanj fc7cc38
doc fixes
zemanj e04ec6e
fix make_whole() for double precision boxes
zemanj 737b4fe
fixed group.unwrap() for reference="com"
zemanj 781b0ee
fix group.unwrap() for empty group
zemanj 19c4190
moved *group.unwrap() from core.topologyattrs to core.groups
zemanj 5bd767f
tests for *group.unwrap()
zemanj b7a0760
tests for modifications in make_whole()
zemanj 415c443
make_whole() test for double precision box
zemanj 8d8dfe5
changed dtype of molnums from int to np.int64
zemanj de53c87
fixed typo and removed unneccessary "elif"s to avoid CodeCov partials
zemanj 5bce7c0
removed unnecessary import
zemanj 9599c5e
use libc.math.fabs instead of abs in make_whole()
zemanj a7e8fcc
unwrap test for universe without masses
zemanj 407879f
added changelog entry for *Group.unwrap()
zemanj f01a878
added changelog entry for lib.mdamath.make_whole()
zemanj 1c83cf3
docs candy
zemanj 986e59f
eliminated another useless elif
zemanj File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -25,7 +25,7 @@ | |
| import cython | ||
| import numpy as np | ||
| cimport numpy as np | ||
| from libc.math cimport sqrt | ||
| from libc.math cimport sqrt, fabs | ||
|
|
||
| from MDAnalysis import NoDataError | ||
|
|
||
|
|
@@ -103,10 +103,9 @@ cdef intset difference(intset a, intset b): | |
|
|
||
| @cython.boundscheck(False) | ||
| @cython.wraparound(False) | ||
| def make_whole(atomgroup, reference_atom=None): | ||
| """Move all atoms in a single molecule so that bonds don't split over images | ||
|
|
||
| Atom positions are modified in place. | ||
| def make_whole(atomgroup, reference_atom=None, inplace=True): | ||
| """Move all atoms in a single molecule so that bonds don't split over | ||
| images. | ||
|
|
||
| This function is most useful when atoms have been packed into the primary | ||
| unit cell, causing breaks mid molecule, with the molecule then appearing | ||
|
|
@@ -133,6 +132,13 @@ def make_whole(atomgroup, reference_atom=None): | |
| reference_atom : :class:`~MDAnalysis.core.groups.Atom` | ||
| The atom around which all other atoms will be moved. | ||
| Defaults to atom 0 in the atomgroup. | ||
| inplace : bool, optional | ||
| If ``True``, coordinates are modified in place. | ||
|
|
||
| Returns | ||
| ------- | ||
| coords : numpy.ndarray | ||
| The unwrapped atom coordinates. | ||
|
|
||
| Raises | ||
| ------ | ||
|
|
@@ -155,12 +161,12 @@ def make_whole(atomgroup, reference_atom=None): | |
| # This algorithm requires bonds, these can be guessed! | ||
| u = mda.Universe(......, guess_bonds=True) | ||
|
|
||
| # MDAnalysis can split molecules into their fragments | ||
| # MDAnalysis can split AtomGroups into their fragments | ||
| # based on bonding information. | ||
| # Note that this function will only handle a single fragment | ||
| # at a time, necessitating a loop. | ||
| for frag in u.fragments: | ||
| make_whole(frag) | ||
| for frag in u.atoms.fragments: | ||
| make_whole(frag) | ||
|
|
||
| Alternatively, to keep a single atom in place as the anchor:: | ||
|
|
||
|
|
@@ -169,24 +175,41 @@ def make_whole(atomgroup, reference_atom=None): | |
| make_whole(atomgroup, reference_atom=atomgroup[10]) | ||
|
|
||
|
|
||
| See Also | ||
| -------- | ||
| :meth:`MDAnalysis.core.groups.AtomGroup.unwrap` | ||
|
|
||
|
|
||
| .. versionadded:: 0.11.0 | ||
| .. versionchanged:: 0.20.0 | ||
| Inplace-modification of atom positions is now optional, and positions | ||
| are returned as a numpy array. | ||
| """ | ||
| cdef intset refpoints, todo, done | ||
| cdef int i, nloops, ref, atom, other, natoms | ||
| cdef int i, j, nloops, ref, atom, other, natoms | ||
| cdef cmap[int, int] ix_to_rel | ||
| cdef intmap bonding | ||
| cdef int[:, :] bonds | ||
| cdef float[:, :] oldpos, newpos | ||
| cdef bint ortho | ||
| cdef float[:] box | ||
| cdef float tri_box[3][3] | ||
| cdef float half_box[3] | ||
| cdef float inverse_box[3] | ||
| cdef double vec[3] | ||
| cdef ssize_t[:] ix_view | ||
| cdef bint is_unwrapped | ||
|
|
||
| # map of global indices to local indices | ||
| ix_view = atomgroup.ix[:] | ||
| natoms = atomgroup.ix.shape[0] | ||
|
|
||
| oldpos = atomgroup.positions | ||
|
|
||
| # Nothing to do for less than 2 atoms | ||
| if natoms < 2: | ||
| return np.array(oldpos) | ||
|
|
||
| for i in range(natoms): | ||
| ix_to_rel[ix_view[i]] = i | ||
|
|
||
|
|
@@ -198,9 +221,11 @@ def make_whole(atomgroup, reference_atom=None): | |
| raise ValueError("Reference atom not in atomgroup") | ||
| ref = ix_to_rel[reference_atom.ix] | ||
|
|
||
| box = atomgroup.dimensions | ||
| box = atomgroup.dimensions.astype(np.float32) | ||
| # TODO: remove astype(np.float32) once all universes return float32 boxes | ||
|
|
||
| for i in range(3): | ||
| half_box[i] = 0.5 * box[i] | ||
| if box[i] == 0.0: | ||
| raise ValueError("One or more dimensions was zero. " | ||
| "You can set dimensions using 'atomgroup.dimensions='") | ||
|
|
@@ -211,6 +236,17 @@ def make_whole(atomgroup, reference_atom=None): | |
| ortho = False | ||
|
|
||
| if ortho: | ||
| # If atomgroup is already unwrapped, bail out | ||
| is_unwrapped = True | ||
| for i in range(1, natoms): | ||
| for j in range(3): | ||
| if fabs(oldpos[i, j] - oldpos[0, j]) >= half_box[j]: | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
| is_unwrapped = False | ||
| break | ||
| if not is_unwrapped: | ||
| break | ||
| if is_unwrapped: | ||
| return np.array(oldpos) | ||
| for i in range(3): | ||
| inverse_box[i] = 1.0 / box[i] | ||
| else: | ||
|
|
@@ -233,7 +269,6 @@ def make_whole(atomgroup, reference_atom=None): | |
| bonding[atom].insert(other) | ||
| bonding[other].insert(atom) | ||
|
|
||
| oldpos = atomgroup.positions | ||
| newpos = np.zeros((oldpos.shape[0], 3), dtype=np.float32) | ||
|
|
||
| refpoints = intset() # Who is safe to use as reference point? | ||
|
|
@@ -274,8 +309,9 @@ def make_whole(atomgroup, reference_atom=None): | |
|
|
||
| if refpoints.size() < natoms: | ||
| raise ValueError("AtomGroup was not contiguous from bonds, process failed") | ||
| else: | ||
| if inplace: | ||
| atomgroup.positions = newpos | ||
| return np.array(newpos) | ||
|
|
||
|
|
||
| @cython.boundscheck(False) | ||
|
|
||
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
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.
there's a
requiresdecorator that can do this type of thing (and create uniform error messages)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.
@richardjgowers I know. The
@requires()decorator only works on AtomGroups, that's why it's not used here.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.
One could extend the
@requiresdecorator to check forGroupBaseinstances instead ofAtomGroupand then check the group's.atomsmember for the given attribute(s). However, I don't like this solution since itResiduGrouporSegmentGroupattributes and.atomsmember.The check I implemented doesn't instantiate additional objects and its impact on performance should therefore be negligible.
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.
Hmm, really we could have something like
assert 'bonds' in u.topologywhich works... I'll raise an issue. I just like error messages being uniform, so hitting eg 'no masses' always looks similar