Skip to content
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 #2469 #2614

Merged
merged 37 commits into from
Mar 28, 2020
Merged
Show file tree
Hide file tree
Changes from 33 commits
Commits
Show all changes
37 commits
Select commit Hold shift + click to select a range
5ffa7f7
issue #2469
siddharthjain1611 Mar 11, 2020
3936978
update #2469
siddharthjain1611 Mar 11, 2020
43ee2fc
update
siddharthjain1611 Mar 11, 2020
1a74436
update
siddharthjain1611 Mar 12, 2020
32a8c17
update
siddharthjain1611 Mar 13, 2020
e52942f
update
siddharthjain1611 Mar 14, 2020
17312cd
update tests
siddharthjain1611 Mar 16, 2020
dc880be
update
siddharthjain1611 Mar 17, 2020
c200ad7
gro
siddharthjain1611 Mar 17, 2020
35dce0c
syntax
siddharthjain1611 Mar 17, 2020
db10c13
empty universe review req
siddharthjain1611 Mar 18, 2020
437e9ec
final commit
PicoCentauri Mar 21, 2020
c132c7a
change error type to value error
siddharthjain1611 Mar 20, 2020
280802a
Fixed CHANGELOG
PicoCentauri Mar 21, 2020
7c13435
made changes
siddharthjain1611 Mar 21, 2020
d149183
spell error
siddharthjain1611 Mar 21, 2020
c847829
commit
siddharthjain1611 Mar 22, 2020
89588ae
changes
siddharthjain1611 Mar 22, 2020
75165ce
ref
siddharthjain1611 Mar 22, 2020
3dc7bc5
review required
siddharthjain1611 Mar 22, 2020
cdee252
removed tupe error from fit.py
siddharthjain1611 Mar 23, 2020
3cb78d0
changes
siddharthjain1611 Mar 24, 2020
2f36ba5
rewiew required
siddharthjain1611 Mar 24, 2020
ab6c1a7
type error to value error
siddharthjain1611 Mar 25, 2020
2ea0f70
fix
siddharthjain1611 Mar 25, 2020
c960a41
fix
siddharthjain1611 Mar 25, 2020
17fbcb1
test to value
siddharthjain1611 Mar 25, 2020
5056a8b
changes suggested
siddharthjain1611 Mar 25, 2020
d24641b
change
siddharthjain1611 Mar 26, 2020
94d0be9
rollback
siddharthjain1611 Mar 26, 2020
2c711d9
changes
siddharthjain1611 Mar 26, 2020
88e361b
update fit.py
siddharthjain1611 Mar 26, 2020
4435b96
pep8
siddharthjain1611 Mar 27, 2020
31f10b8
Update package/MDAnalysis/analysis/align.py
siddharthjain1611 Mar 27, 2020
040b33f
Update package/MDAnalysis/analysis/align.py
siddharthjain1611 Mar 27, 2020
0decdf7
PEP and Value Error
siddharthjain1611 Mar 27, 2020
96a8f1f
removed file
siddharthjain1611 Mar 28, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions package/AUTHORS
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,7 @@ Chronological list of authors
- Morgan L. Nance
- Faraaz Shah
- Wiep van der Toorn
- Siddharth Jain


External code
Expand Down
3 changes: 2 additions & 1 deletion package/CHANGELOG
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ mm/dd/yy richardjgowers, kain88-de, lilyminium, p-j-smith, bdice, joaomcteixeira
* 0.21.0

Fixes
* Proper error message for AlignTraj on trajectory without mass (Issue #2469)
* Updated tests to have explicit fixtures (Issue #2618)
* XDR offsets now read from trajectory if offsets file read-in fails on
IOError (Issue #1893, PR #2611)
Expand All @@ -34,7 +35,7 @@ Fixes
* Fixed mda.Merge for Universes without coordinates (Issue #2470)(PR #2580)
* PCA(align=True) now correctly aligns the trajectory and computes the
correct means and covariance matrix (Issue #2561)
* Correct args order of base.AnalysisFromFunction (Issue #2503)
* Correct args order of base.AnalysisFromFunction (Issue #2503)
* encore.dres() returns dimensionality reduction details instead of a
reference to itself (Issue #2471)
* Handle exception when PDBWriter is trying to remove an invalid StringIO
Expand Down
86 changes: 46 additions & 40 deletions package/MDAnalysis/analysis/align.py
Original file line number Diff line number Diff line change
Expand Up @@ -411,7 +411,7 @@ def alignto(mobile, reference, select=None, weights=None,
:class:`~MDAnalysis.core.groups.AtomGroup` with defined atom order as
described under :ref:`ordered-selections-label`).
match_atoms : bool (optional)
Whether to match the mobile and reference atom-by-atom. Default ``True``.
Whether to match the mobile and reference atom-by-atom. Default ``True``.
weights : {"mass", ``None``} or array_like (optional)
choose weights. With ``"mass"`` uses masses as weights; with ``None``
weigh each atom equally. If a float array of the same length as
Expand Down Expand Up @@ -569,7 +569,7 @@ def __init__(self, mobile, reference, select='all', filename=None,
tol_mass : float (optional)
Tolerance given to `get_matching_atoms` to find appropriate atoms
match_atoms : bool (optional)
Whether to match the mobile and reference atom-by-atom. Default ``True``.
Whether to match the mobile and reference atom-by-atom. Default ``True``.
strict : bool (optional)
Force `get_matching_atoms` to fail if atoms can't be found using
exact methods
Expand Down Expand Up @@ -701,7 +701,7 @@ def _conclude(self):


class AverageStructure(AnalysisBase):
"""RMS-align trajectory to a reference structure using a selection,
"""RMS-align trajectory to a reference structure using a selection,
and calculate the average coordinates of the trajectory.

Both the reference `reference` and the trajectory `mobile` must be
Expand All @@ -710,7 +710,7 @@ class AverageStructure(AnalysisBase):
current frame.

The output file format is determined by the file extension of
`filename`.
`filename`.

Example
-------
Expand Down Expand Up @@ -753,7 +753,7 @@ def __init__(self, mobile, reference=None, select='all', filename=None,
tol_mass : float (optional)
Tolerance given to `get_matching_atoms` to find appropriate atoms
match_atoms : bool (optional)
Whether to match the mobile and reference atom-by-atom. Default ``True``.
Whether to match the mobile and reference atom-by-atom. Default ``True``.
strict : bool (optional)
Force `get_matching_atoms` to fail if atoms can't be found using
exact methods
Expand All @@ -768,7 +768,7 @@ def __init__(self, mobile, reference=None, select='all', filename=None,
ref_frame : int (optional)
frame index to select frame from `reference`
verbose : bool (optional)
Set logger to show more information and show detailed progress of
Set logger to show more information and show detailed progress of
the calculation if set to ``True``; the default is ``False``.


Expand All @@ -785,7 +785,7 @@ def __init__(self, mobile, reference=None, select='all', filename=None,
rmsd : float
Average RMSD per frame
filename : str
String reflecting the filename of the file where the average
String reflecting the filename of the file where the average
structure is written


Expand Down Expand Up @@ -882,7 +882,7 @@ def _single_frame(self):
self.mobile,
mobile_com,
self._ref_com, self._weights)[1]
self.positions += self.mobile_atoms.positions
self.positions += self.mobile_atoms.positions

def _conclude(self):
self.positions /= self.n_frames
Expand Down Expand Up @@ -1350,45 +1350,51 @@ def get_atoms_byres(g, match_mask=np.logical_not(mismatch_mask)):

# stop if we created empty selections (by removing ALL residues...)
if ag1.n_atoms == 0 or ag2.n_atoms == 0:
errmsg = ("Failed to automatically find matching atoms: created empty selections. " +
errmsg = ("Failed to automatically find matching atoms: created empty selections. "
"Try to improve your selections for mobile and reference.")
logger.error(errmsg)
raise SelectionError(errmsg)

if match_atoms:
# check again because the residue matching heuristic is not very
# good and can easily be misled (e.g., when one of the selections
# had fewer atoms but the residues in mobile and reference have
# each the same number)
try:
mass_mismatches = (np.absolute(ag1.masses - ag2.masses) > tol_mass)
except ValueError:
errmsg = ("Failed to find matching atoms: len(reference) = {}, len(mobile) = {} " +
"Try to improve your selections for mobile and reference.").format(
ag1.n_atoms, ag2.n_atoms)
logger.error(errmsg)
raise_from(SelectionError(errmsg), None)

if np.any(mass_mismatches):
# Test 2 failed.
# diagnostic output:
logger.error("Atoms: reference | trajectory")
for ar, at in zip(ag1[mass_mismatches], ag2[mass_mismatches]):
logger.error(
"{0!s:>4} {1:3d} {2!s:>3} {3!s:>3} {4:6.3f} | {5!s:>4} {6:3d} {7!s:>3} {8!s:>3} {9:6.3f}".format(
ar.segid,
ar.resid,
ar.resname,
ar.name,
ar.mass,
at.segid,
at.resid,
at.resname,
at.name,
at.mass))
errmsg = ("Inconsistent selections, masses differ by more than {0}; "
"mis-matching atoms are shown above.").format(tol_mass)
logger.error(errmsg)
raise SelectionError(errmsg)
if (not hasattr(ag1, 'masses') or not hasattr(ag2, 'masses')):
# # WARNING:
Copy link
Contributor

@PicoCentauri PicoCentauri Mar 27, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Finally, remove this superfluous comment if you want.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I saw a comment of this kind somewhere else, therefore I wrote it.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Having the comment there is not bad but I would say not needed because the code itself clearly says "warning".

However, I see a PEP8 issue below...

msg = "Atoms could not be matched since they don't contain masses."
logger.info(msg)
warnings.warn(msg,category=SelectionWarning)
siddharthjain1611 marked this conversation as resolved.
Show resolved Hide resolved
else:
try:
mass_mismatches = (np.absolute(ag1.masses - ag2.masses) > tol_mass)
except ValueError:
errmsg = ("Failed to find matching atoms: len(reference) = {}, len(mobile) = {} "
siddharthjain1611 marked this conversation as resolved.
Show resolved Hide resolved
"Try to improve your selections for mobile and reference.").format(
ag1.n_atoms, ag2.n_atoms)
logger.error(errmsg)
raise_from(SelectionError(errmsg), None)

if np.any(mass_mismatches):
# Test 2 failed.
# diagnostic output:
logger.error("Atoms: reference | trajectory")
for ar, at in zip(ag1[mass_mismatches], ag2[mass_mismatches]):
logger.error(
"{0!s:>4} {1:3d} {2!s:>3} {3!s:>3} {4:6.3f} | {5!s:>4} {6:3d} {7!s:>3} {8!s:>3} {9:6.3f}".format(
ar.segid,
ar.resid,
ar.resname,
ar.name,
ar.mass,
at.segid,
at.resid,
at.resname,
at.name,
at.mass))
errmsg = ("Inconsistent selections, masses differ by more than {0}; "
"mis-matching atoms are shown above.").format(tol_mass)
logger.error(errmsg)
raise SelectionError(errmsg)

return ag1, ag2
4 changes: 2 additions & 2 deletions package/MDAnalysis/analysis/rms.py
Original file line number Diff line number Diff line change
Expand Up @@ -417,7 +417,7 @@ def __init__(self, atomgroup, reference=None, select='all',
length) or if it is not a 1D array (see
:func:`MDAnalysis.lib.util.get_weights`).

A :exc:`ValueError` is also raised if the length of `weights_groupselections`
A :exc:`ValueError` is also raised if the length of `weights_groupselections`
are not compatible with `groupselections`.

Notes
Expand Down Expand Up @@ -546,7 +546,7 @@ def __init__(self, atomgroup, reference=None, select='all',
# check weights type
if iterable(self.weights) and (np.array(weights).dtype
not in (np.dtype('float64'),np.dtype('int64'))):
raise TypeError("weight should only be be 'mass', None or 1D float array."
raise ValueError("weight should only be be 'mass', None or 1D float array."
siddharthjain1611 marked this conversation as resolved.
Show resolved Hide resolved
"For weights on groupselections, use **weight_groupselections** ")
if iterable(self.weights) or self.weights != "mass":
get_weights(self.mobile_atoms, self.weights)
Expand Down
6 changes: 3 additions & 3 deletions package/MDAnalysis/lib/util.py
Original file line number Diff line number Diff line change
Expand Up @@ -924,7 +924,7 @@ def get_ext(filename):
ext : str
"""
root, ext = os.path.splitext(filename)

if ext.startswith(os.extsep):
ext = ext[1:]

Expand Down Expand Up @@ -1369,7 +1369,7 @@ def get_weights(atoms, weights):
"the atoms ({1})".format(
len(weights), len(atoms)))
elif weights is not None:
raise TypeError("weights must be {'mass', None} or an iterable of the "
raise ValueError("weights must be {'mass', None} or an iterable of the "
"same size as the atomgroup.")

return weights
Expand Down Expand Up @@ -1581,7 +1581,7 @@ def unique_rows(arr, return_index=False):
Examples
--------
Remove dupicate rows from an array:

>>> a = np.array([[0, 1], [1, 2], [1, 2], [0, 1], [2, 3]])
>>> b = unique_rows(a)
>>> b
Expand Down
68 changes: 30 additions & 38 deletions package/MDAnalysis/transformations/fit.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,47 +45,47 @@

def fit_translation(ag, reference, plane=None, weights=None):

"""Translates a given AtomGroup so that its center of geometry/mass matches
"""Translates a given AtomGroup so that its center of geometry/mass matches
the respective center of the given reference. A plane can be given by the
user using the option `plane`, and will result in the removal of
the translation motions of the AtomGroup over that particular plane.

Example
-------
Removing the translations of a given AtomGroup `ag` on the XY plane by fitting
Removing the translations of a given AtomGroup `ag` on the XY plane by fitting
its center of mass to the center of mass of a reference `ref`:

.. code-block:: python

ag = u.select_atoms("protein")
ref = mda.Universe("reference.pdb")
transform = mda.transformations.fit_translation(ag, ref, plane="xy",
weights="mass")
u.trajectory.add_transformations(transform)

Parameters
----------
ag : Universe or AtomGroup
structure to translate, a
:class:`~MDAnalysis.core.groups.AtomGroup` or a whole
:class:`~MDAnalysis.core.groups.AtomGroup` or a whole
:class:`~MDAnalysis.core.universe.Universe`
reference : Universe or AtomGroup
reference structure, a :class:`~MDAnalysis.core.groups.AtomGroup` or a whole
reference structure, a :class:`~MDAnalysis.core.groups.AtomGroup` or a whole
:class:`~MDAnalysis.core.universe.Universe`
plane: str, optional
used to define the plane on which the translations will be removed. Defined as a
used to define the plane on which the translations will be removed. Defined as a
string of the plane. Suported planes are yz, xz and xy planes.
weights : {"mass", ``None``} or array_like, optional
choose weights. With ``"mass"`` uses masses as weights; with ``None``
weigh each atom equally. If a float array of the same length as
`ag` is provided, use each element of the `array_like` as a
weight for the corresponding atom in `ag`.

Returns
-------
MDAnalysis.coordinates.base.Timestep
"""

if plane is not None:
axes = {'yz' : 0, 'xz' : 1, 'xy' : 2}
try:
Expand All @@ -100,23 +100,19 @@ def fit_translation(ag, reference, plane=None, weights=None):
AttributeError("{} or {} is not valid Universe/AtomGroup".format(ag,reference)),
None)
ref, mobile = align.get_matching_atoms(reference.atoms, ag.atoms)
try:
weights = align.get_weights(ref.atoms, weights=weights)
except (ValueError, TypeError):
raise_from(ValueError("weights must be {'mass', None} or an iterable of the "
"same size as the atomgroup."), None)

ref_com = np.asarray(ref.center(weights), np.float32)
weights = align.get_weights(ref.atoms, weights=weights)
ref_com = ref.center(weights)
ref_coordinates = ref.atoms.positions - ref_com

def wrapped(ts):
mobile_com = np.asarray(mobile.atoms.center(weights), np.float32)
vector = ref_com - mobile_com
if plane is not None:
vector[plane] = 0
ts.positions += vector

return ts

return wrapped


Expand All @@ -125,44 +121,44 @@ def fit_rot_trans(ag, reference, plane=None, weights=None):

Spatially align the group of atoms `ag` to `reference` by doing a RMSD
fit.

This fit works as a way to remove translations and rotations of a given
AtomGroup in a trajectory. A plane can be given using the flag `plane`
so that only translations and rotations in that particular plane are
removed. This is useful for protein-membrane systems to where the membrane
must remain in the same orientation.

Example
-------
Removing the translations and rotations of a given AtomGroup `ag` on the XY plane
by fitting it to a reference `ref`, using the masses as weights for the RMSD fit:

.. code-block:: python

ag = u.select_atoms("protein")
ref = mda.Universe("reference.pdb")
transform = mda.transformations.fit_rot_trans(ag, ref, plane="xy",
transform = mda.transformations.fit_rot_trans(ag, ref, plane="xy",
weights="mass")
u.trajectory.add_transformations(transform)

Parameters
----------
ag : Universe or AtomGroup
structure to translate and rotate, a
:class:`~MDAnalysis.core.groups.AtomGroup` or a whole
:class:`~MDAnalysis.core.groups.AtomGroup` or a whole
:class:`~MDAnalysis.core.universe.Universe`
reference : Universe or AtomGroup
reference structure, a :class:`~MDAnalysis.core.groups.AtomGroup` or a whole
reference structure, a :class:`~MDAnalysis.core.groups.AtomGroup` or a whole
:class:`~MDAnalysis.core.universe.Universe`
plane: str, optional
used to define the plane on which the rotations and translations will be removed.
used to define the plane on which the rotations and translations will be removed.
Defined as a string of the plane. Supported planes are "yz", "xz" and "xy" planes.
weights : {"mass", ``None``} or array_like, optional
choose weights. With ``"mass"`` uses masses as weights; with ``None``
weigh each atom equally. If a float array of the same length as
`ag` is provided, use each element of the `array_like` as a
weight for the corresponding atom in `ag`.

Returns
-------
MDAnalysis.coordinates.base.Timestep
Expand All @@ -179,14 +175,10 @@ def fit_rot_trans(ag, reference, plane=None, weights=None):
except AttributeError:
raise_from(AttributeError("{} or {} is not valid Universe/AtomGroup".format(ag,reference)), None)
ref, mobile = align.get_matching_atoms(reference.atoms, ag.atoms)
try:
PicoCentauri marked this conversation as resolved.
Show resolved Hide resolved
weights = align.get_weights(ref.atoms, weights=weights)
except (ValueError, TypeError):
raise_from(ValueError("weights must be {'mass', None} or an iterable of the "
"same size as the atomgroup."), None)
weights = align.get_weights(ref.atoms, weights=weights)
ref_com = ref.center(weights)
ref_coordinates = ref.atoms.positions - ref_com

def wrapped(ts):
mobile_com = mobile.atoms.center(weights)
mobile_coordinates = mobile.atoms.positions - mobile_com
Expand All @@ -203,7 +195,7 @@ def wrapped(ts):
ts.positions = ts.positions - mobile_com
ts.positions = np.dot(ts.positions, rotation.T)
ts.positions = ts.positions + vector

return ts

return wrapped
Loading