Skip to content

Commit

Permalink
Analysis progmeter (#892)
Browse files Browse the repository at this point in the history
* PEP8 Changes

* Add progressmeter to AnalysisBase

This also adds an initialization function to AnalysisBase. This is done
now because we need to do more then just to setup the iteration.

* Adopt Contacts

* Follow best practices for matplotlib

Returning an axis object is considered to be better since the user is
able to adjust the plot by himself if he wishes to do so.

* Addopt Persistencelength

* Adopt InterRDF

* Adopt LinearDensity

* Adopt AlignTrj

* update changelog

* Ensure old API still works

Since user code might already exist we keep make sure the old API still
works as well.
  • Loading branch information
kain88-de authored and richardjgowers committed Jul 14, 2016
1 parent 98d5c10 commit 054a9de
Show file tree
Hide file tree
Showing 12 changed files with 231 additions and 156 deletions.
7 changes: 7 additions & 0 deletions package/CHANGELOG
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,13 @@ The rules for this file:

* 0.15.1

Enhancements

* All classes derived from 'AnalysisBase' now display a ProgressMeter
with 'quiet=False'
* The 'run' method from all 'AnalysisBase' derived classes return the
class itself.

Fixes
* GROWriter resids now truncated properly (Issue #886)
* reading/writing lambda value in trr files (Issue #859)
Expand Down
17 changes: 7 additions & 10 deletions package/MDAnalysis/analysis/align.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@
# vim: tabstop=4 expandtab shiftwidth=4 softtabstop=4
#
# MDAnalysis --- http://www.MDAnalysis.org
# Copyright (c) 2006-2015 Naveen Michaud-Agrawal, Elizabeth J. Denning, Oliver Beckstein
# and contributors (see AUTHORS for the full list)
# Copyright (c) 2006-2015 Naveen Michaud-Agrawal, Elizabeth J. Denning, Oliver
# Beckstein and contributors (see AUTHORS for the full list)
#
# Released under the GNU Public Licence, v2 or any higher version
#
Expand Down Expand Up @@ -472,8 +472,7 @@ class AlignTraj(AnalysisBase):

def __init__(self, mobile, reference, select='all', filename=None,
prefix='rmsfit_', mass_weighted=False, tol_mass=0.1,
strict=False, force=True, quiet=False, start=None, stop=None,
step=None, **kwargs):
strict=False, force=True, **kwargs):
"""Initialization
Parameters
Expand Down Expand Up @@ -516,18 +515,16 @@ def __init__(self, mobile, reference, select='all', filename=None,
exception
"""
self._quiet = quiet
super(AlignTraj, self).__init__(mobile.trajectory, **kwargs)
if self._quiet:
logging.disable(logging.WARN)

traj = mobile.trajectory
select = rms.process_selection(select)
self.ref_atoms = reference.select_atoms(*select['reference'])
self.mobile_atoms = mobile.select_atoms(*select['mobile'])
kwargs.setdefault('remarks', 'RMS fitted trajectory to reference')

if filename is None:
path, fn = os.path.split(traj.filename)
path, fn = os.path.split(self._trajectory.filename)
filename = os.path.join(path, prefix + fn)
logger.info('filename of rms_align with no filename given'
': {0}'.format(filename))
Expand All @@ -540,7 +537,8 @@ def __init__(self, mobile, reference, select='all', filename=None,

natoms = self.mobile_atoms.n_atoms
self.ref_atoms, self.mobile_atoms = get_matching_atoms(
self.ref_atoms, self.mobile_atoms, tol_mass=tol_mass, strict=strict)
self.ref_atoms, self.mobile_atoms, tol_mass=tol_mass,
strict=strict)

self._writer = mda.Writer(self.filename, natoms)

Expand All @@ -551,7 +549,6 @@ def __init__(self, mobile, reference, select='all', filename=None,
self._weights = None

logger.info("RMS-fitting on {0:d} atoms.".format(len(self.ref_atoms)))
self._setup_frames(traj, start, stop, step)

def _prepare(self):
# reference centre of mass system
Expand Down
125 changes: 91 additions & 34 deletions package/MDAnalysis/analysis/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@
# vim: tabstop=4 expandtab shiftwidth=4 softtabstop=4
#
# MDAnalysis --- http://www.MDAnalysis.org
# Copyright (c) 2006-2015 Naveen Michaud-Agrawal, Elizabeth J. Denning, Oliver Beckstein
# and contributors (see AUTHORS for the full list)
# Copyright (c) 2006-2015 Naveen Michaud-Agrawal, Elizabeth J. Denning, Oliver
# Beckstein and contributors (see AUTHORS for the full list)
#
# Released under the GNU Public Licence, v2 or any higher version
#
Expand All @@ -24,61 +24,116 @@
"""
from six.moves import range

import numpy as np
import logging
from MDAnalysis.lib.log import ProgressMeter

logger = logging.getLogger(__name__)


class AnalysisBase(object):
"""Base class for defining multi frame analysis
The analysis base class is designed as a template for creating
multiframe analysis.
The class implements the following methods:
_setup_frames(trajectory, start=None, stop=None, step=None)
Pass a Reader object and define the desired iteration pattern
through the trajectory
run
The user facing run method. Calls the analysis methods
defined below
Your analysis can implement the following methods, which are
called from run:
_prepare
Called before iteration on the trajectory has begun.
Data structures can be set up at this time, however most
error checking should be done in the __init__
_single_frame
Called after the trajectory is moved onto each new frame.
_conclude
Called once iteration on the trajectory is finished.
Apply normalisation and averaging to results here.
"""Base class for defining multi frame analysis, it is designed as a
template for creating multiframe analysis. This class will automatically
take care of setting up the trajectory reader for iterating and offers to
show a progress meter.
To define a new Analysis, `AnalysisBase` needs to be subclassed
`_single_frame` must be defined. It is also possible to define
`_prepare` and `_conclude` for pre and post processing. See the example
below.
.. code-block:: python
class NewAnalysis(AnalysisBase):
def __init__(self, atomgroup, parameter, **kwargs):
super(NewAnalysis, self).__init__(atomgroup.universe.trajectory,
**kwargs)
self._parameter = parameter
self._ag = atomgroup
def _prepare(self):
# OPTIONAL
# Called before iteration on the trajectory has begun.
# Data structures can be set up at this time
self.result = []
def _single_frame(self):
# REQUIRED
# Called after the trajectory is moved onto each new frame.
# store result of `some_function` for a single frame
self.result.append(some_function(self._ag, self._parameter))
def _conclude(self):
# OPTIONAL
# Called once iteration on the trajectory is finished.
# Apply normalisation and averaging to results here.
self.result = np.asarray(self.result) / np.sum(self.result)
Afterwards the new analysis can be run like this.
.. code-block:: python
na = NewAnalysis(u.select_atoms('name CA'), 35).run()
print(na.result)
"""
def __init__(self, trajectory, start=None,
stop=None, step=None, quiet=True):
"""
Parameters
----------
trajectory : mda.Reader
A trajectory Reader
start : int, optional
start frame of analysis
stop : int, optional
stop frame of analysis
step : int, optional
number of frames to skip between each analysed frame
quiet : bool, optional
Turn off verbosity
"""
self._quiet = quiet
self._setup_frames(trajectory, start, stop, step)

def _setup_frames(self, trajectory, start=None,
stop=None, step=None):
"""
Pass a Reader object and define the desired iteration pattern
through the trajectory
Parameters
----------
trajectory : mda.Reader
A trajectory Reader
start : int, optional
start frame of analysis
stop : int, optional
stop frame of analysis
step : int, optional
number of frames to skip between each analysed frame
"""
self._trajectory = trajectory
start, stop, step = trajectory.check_slice_indices(
start, stop, step)
self.start = start
self.stop = stop
self.step = step
self.n_frames = len(range(start, stop, step))
interval = int(self.n_frames // 100)
if interval == 0:
interval = 1

# ensure _quiet is set when __init__ wasn't called, this is to not
# break pre 0.16.0 API usage of AnalysisBase
if not hasattr(self, '_quiet'):
self._quiet = True
self._pm = ProgressMeter(self.n_frames if self.n_frames else 1,
interval=interval, quiet=self._quiet)

def _single_frame(self):
"""Calculate data from a single frame of trajectory
Don't worry about normalising, just deal with a single frame.
"""
pass
raise NotImplementedError("Only implemented in child classes")

def _prepare(self):
"""Set things up before the analysis loop begins"""
Expand All @@ -91,7 +146,7 @@ def _conclude(self):
"""
pass

def run(self, **kwargs):
def run(self):
"""Perform the calculation"""
logger.info("Starting preparation")
self._prepare()
Expand All @@ -101,5 +156,7 @@ def run(self, **kwargs):
self._ts = ts
# logger.info("--> Doing frame {} of {}".format(i+1, self.n_frames))
self._single_frame()
self._pm.echo(self._frame_index)
logger.info("Finishing up")
self._conclude()
return self
9 changes: 4 additions & 5 deletions package/MDAnalysis/analysis/contacts.py
Original file line number Diff line number Diff line change
Expand Up @@ -376,7 +376,7 @@ class Contacts(AnalysisBase):
"""
def __init__(self, u, selection, refgroup, method="hard_cut", radius=4.5,
kwargs=None, start=None, stop=None, step=None,):
kwargs=None, **basekwargs):
"""Initialization
Parameters
Expand Down Expand Up @@ -404,6 +404,9 @@ def __init__(self, u, selection, refgroup, method="hard_cut", radius=4.5,
Step between frames to analyse, Default: 1
"""
self.u = u
super(Contacts, self).__init__(self.u.trajectory, **basekwargs)

if method == 'hard_cut':
self.fraction_contacts = hard_cut_q
elif method == 'soft_cut':
Expand All @@ -413,10 +416,6 @@ def __init__(self, u, selection, refgroup, method="hard_cut", radius=4.5,
raise ValueError("method has to be callable")
self.fraction_contacts = method

# setup boilerplate
self.u = u
self._setup_frames(self.u.trajectory, start, stop, step)

self.selection = selection
self.grA = u.select_atoms(selection[0])
self.grB = u.select_atoms(selection[1])
Expand Down
Loading

0 comments on commit 054a9de

Please sign in to comment.