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

Refactor Transformations from closure into class #2859

Merged
merged 33 commits into from
Sep 8, 2020
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
05f2ddd
refactor transformations into class
yuxuanzhuang Jul 20, 2020
92a9653
pep8 and test fix
yuxuanzhuang Jul 20, 2020
9fc5071
move fit prep toinit
yuxuanzhuang Jul 20, 2020
6b7c45b
move rotate prep to init
yuxuanzhuang Jul 20, 2020
ae51c5b
move translate prep to init
yuxuanzhuang Jul 20, 2020
a542792
move wrap prep to init
yuxuanzhuang Jul 20, 2020
1729ee6
pep8
yuxuanzhuang Jul 20, 2020
26950b2
pep
yuxuanzhuang Jul 20, 2020
5aa8d2b
test pickle
yuxuanzhuang Jul 20, 2020
1c5bbdb
pep
yuxuanzhuang Jul 20, 2020
b09bef6
doc for each module
yuxuanzhuang Jul 27, 2020
235e8a9
read_offset issue?
yuxuanzhuang Jul 30, 2020
4447869
Merge remote-tracking branch 'mda_origin/develop' into new_transforma…
yuxuanzhuang Jul 30, 2020
5ef7d3f
change to dcd
yuxuanzhuang Jul 30, 2020
f6331d3
doc transformation
yuxuanzhuang Jul 31, 2020
1ebcc33
note revise
yuxuanzhuang Aug 1, 2020
c412104
doc for transformation
yuxuanzhuang Aug 3, 2020
3b1c470
changelog
yuxuanzhuang Aug 4, 2020
6e87625
Merge remote-tracking branch 'mda_origin/develop' into new_transforma…
yuxuanzhuang Aug 9, 2020
11f3644
enable universe pickle
yuxuanzhuang Aug 9, 2020
7c857f6
merge to develop
yuxuanzhuang Aug 12, 2020
58f6e4e
transformation doc
yuxuanzhuang Aug 12, 2020
26dab2f
Merge branch 'develop' into new_transformation
orbeckst Aug 16, 2020
648c9b0
merge to develop
yuxuanzhuang Aug 27, 2020
3f083ae
change universe pickle to reduce
yuxuanzhuang Aug 27, 2020
9ae7f83
remove pure pickle/unpickle tests
yuxuanzhuang Aug 27, 2020
26f68bc
Merge branch 'new_transformation' of https://github.com/yuxuanzhuang/…
yuxuanzhuang Aug 27, 2020
ae8a2a2
pep
yuxuanzhuang Aug 27, 2020
499efc1
example
yuxuanzhuang Aug 28, 2020
2793ba2
doc
yuxuanzhuang Aug 28, 2020
acb4488
Merge branch 'develop' into new_transformation
yuxuanzhuang Aug 30, 2020
1080678
Update package/MDAnalysis/transformations/__init__.py
yuxuanzhuang Sep 4, 2020
0655275
change snippet
yuxuanzhuang Sep 6, 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/CHANGELOG
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,7 @@ Changes
* Sets the minimal RDKit version for CI to 2020.03.1 (Issue #2827, PR #2831)
* Removes deprecated waterdynamics.HydrogenBondLifetimes (PR #2842)
* Make NeighborSearch return empty atomgroup, residue, segments instead of list (Issue #2892, PR #2907)
* Updated Universe creation function signatures to named arguments (Issue #2921)
* The transformation was changed from a function/closure to a class with
`__call__` (Issue #2860, PR #2859)

Expand Down
33 changes: 30 additions & 3 deletions package/MDAnalysis/transformations/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
#


"""\
"""
Trajectory transformations --- :mod:`MDAnalysis.transformations`
================================================================

Expand All @@ -32,7 +32,9 @@
are often required for some analyses and visualization, and the functions in
this module allow transformations to be applied on-the-fly.

A typical transformation class looks like this:
A typical transformation class looks like this (note that we keep its name
lowercase because we will treat it as a function, thanks to the ``__call__``
method):

.. code-blocks:: python

Expand All @@ -48,7 +50,32 @@ def __call__(self, ts):

return ts

Copy link
Member

@orbeckst orbeckst Aug 28, 2020

Choose a reason for hiding this comment

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

You showed the abstract class. Now show a concrete example to address @richardjgowers comment. For instance

Suggested change
As a concrete example we will write a transformation that rotates a group of atoms around
the z-axis through the center of geometry by a fixed increment for every time step. We will use
:meth:`MDAnalysis.core.groups.AtomGroup.rotateby` and simply increase the rotation angle
every time the transformation is called::
class spin_atoms(object):
def __init__(self, atoms, dphi):
"""Rotate atoms by dphi degrees for every time step (around the z axis)"""
self.atoms = atoms
self.dphi = dphi
self.axis = np.array([0, 0, 1])
def __call__(self, ts):
phi = self.dphi * ts.frame
self.atoms.rotateby(phi, self.axis)
return ts
This transformation can be used as ::
u = mda.Universe(PSF, DCD)
u.trajectory.add_transformations(spin_atoms(u.select_atoms("protein"), 1.0))

I currently can't get nglview to work in my notebook so you'll need to check that this actually works... or come up with another example.

EDIT: forgot to increment phi...

EDIT 2: yes, it's pretty dumb that the phi angle just keeps incrementing, no matter what you do with the trajectory. Perhaps better to do something like phi = ts.frame * self.dphi

EDIT 3: changed it to phi = ts.frame * self.dphi

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Thanks! it works. And I think it makes sense to just rotate by a fixed angle. (for visualization perhaps)

Copy link
Member

Choose a reason for hiding this comment

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

I changed the snippet.

See `MDAnalysis.transformations.translate` for a simple example.
As a concrete example we will write a transformation that rotates a group of
atoms around the z-axis through the center of geometry by a fixed increment
for every time step. We will use
:meth:`MDAnalysis.core.groups.AtomGroup.rotateby`
and simply increment the rotation angle every time the
transformation is called ::

class spin_atoms(object):
def __init__(self, atoms, dphi):
# Rotate atoms by dphi degrees for every ts around the z axis
self.atoms = atoms
self.dphi = dphi
self.axis = np.array([0, 0, 1])
self.phi = 0

def __call__(self, ts):
self.atoms.rotateby(self.phi, self.axis)
self.phi += self.dphi
return ts

This transformation can be used as ::

u = mda.Universe(PSF, DCD)
u.trajectory.add_transformations(spin_atoms(u.select_atoms("protein"), 1.0))

Alos see :mod:`MDAnalysis.transformations.translate` for a simple example.
yuxuanzhuang marked this conversation as resolved.
Show resolved Hide resolved

These transformation functions can be called by the user for any given timestep
of the trajectory, added as a workflow using :meth:`add_transformations`
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
#
import pytest
import pickle
from numpy.testing import assert_equal
from numpy.testing import assert_almost_equal

import MDAnalysis as mda

Expand Down Expand Up @@ -93,7 +93,7 @@ def test_add_fit_translation_pickle(fit_translation_transformation, u):
u_p = pickle.loads(pickle.dumps(u))
u.trajectory[0]
for u_ts, u_p_ts in zip(u.trajectory[:5], u_p.trajectory[:5]):
assert_equal(u_ts.positions, u_p_ts.positions)
assert_almost_equal(u_ts.positions, u_p_ts.positions)


def test_add_fit_rot_trans_pickle(fit_rot_trans_transformation,
Expand All @@ -102,52 +102,52 @@ def test_add_fit_rot_trans_pickle(fit_rot_trans_transformation,
u_p = pickle.loads(pickle.dumps(u))
u.trajectory[0]
for u_ts, u_p_ts in zip(u.trajectory[:5], u_p.trajectory[:5]):
assert_equal(u_ts.positions, u_p_ts.positions)
assert_almost_equal(u_ts.positions, u_p_ts.positions)


def test_add_PositionAverager_pickle(PositionAverager_transformation, u):
u.trajectory.add_transformations(PositionAverager_transformation)
u_p = pickle.loads(pickle.dumps(u))
u.trajectory[0]
for u_ts, u_p_ts in zip(u.trajectory[:5], u_p.trajectory[:5]):
assert_equal(u_ts.positions, u_p_ts.positions)
assert_almost_equal(u_ts.positions, u_p_ts.positions)


def test_add_rotateby_pickle(rotateby_transformation, u):
u.trajectory.add_transformations(rotateby_transformation)
u_p = pickle.loads(pickle.dumps(u))
u.trajectory[0]
for u_ts, u_p_ts in zip(u.trajectory[:5], u_p.trajectory[:5]):
assert_equal(u_ts.positions, u_p_ts.positions)
assert_almost_equal(u_ts.positions, u_p_ts.positions)


def test_add_translate_pickle(translate_transformation, u):
u.trajectory.add_transformations(translate_transformation)
u_p = pickle.loads(pickle.dumps(u))
u.trajectory[0]
for u_ts, u_p_ts in zip(u.trajectory[:5], u_p.trajectory[:5]):
assert_equal(u_ts.positions, u_p_ts.positions)
assert_almost_equal(u_ts.positions, u_p_ts.positions)


def test_add_center_in_box_pickle(center_in_box_transformation, u):
u.trajectory.add_transformations(center_in_box_transformation)
u_p = pickle.loads(pickle.dumps(u))
u.trajectory[0]
for u_ts, u_p_ts in zip(u.trajectory[:5], u_p.trajectory[:5]):
assert_equal(u_ts.positions, u_p_ts.positions)
assert_almost_equal(u_ts.positions, u_p_ts.positions)


def test_add_wrap_pickle(wrap_transformation, u):
u.trajectory.add_transformations(wrap_transformation)
u_p = pickle.loads(pickle.dumps(u))
u.trajectory[0]
for u_ts, u_p_ts in zip(u.trajectory[:5], u_p.trajectory[:5]):
assert_equal(u_ts.positions, u_p_ts.positions)
assert_almost_equal(u_ts.positions, u_p_ts.positions)


def test_add_unwrap_pickle(unwrap_transformation, u):
u.trajectory.add_transformations(unwrap_transformation)
u_p = pickle.loads(pickle.dumps(u))
u.trajectory[0]
for u_ts, u_p_ts in zip(u.trajectory[:5], u_p.trajectory[:5]):
assert_equal(u_ts.positions, u_p_ts.positions)
assert_almost_equal(u_ts.positions, u_p_ts.positions)