Skip to content

Commit

Permalink
Fix typos and improve docs and code comments
Browse files Browse the repository at this point in the history
  • Loading branch information
PicoCentauri committed Feb 17, 2023
1 parent 4cb9a4f commit 3c5de33
Show file tree
Hide file tree
Showing 2 changed files with 42 additions and 27 deletions.
65 changes: 40 additions & 25 deletions package/MDAnalysis/analysis/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,10 @@
plt.xlabel("time (ps)")
plt.ylabel("RMSD (Å)")
If you want to run two or more different analyses on the same trajectory you
can also efficently combine them using the
:class:`MDAnalysis.analysis.base.AnalysisCollection` class.
Writing new analysis tools
--------------------------
Expand Down Expand Up @@ -224,28 +228,32 @@ class AnalysisCollection(object):
"""
Class for running a collection of analysis classes on a single trajectory.
Running a collection of analysis with ``AnalysisCollection`` can result in
a speedup compared to running the individual object since here we only
perform the trajectory looping once.
Running a collection of analyses with ``AnalysisCollection`` can result in
a speedup compared to running the individual analyses since the trajectory
loop ins only performed once.
The class assumes that each analysis is a child of
:class:`MDAnalysis.analysis.base.AnalysisBase`. Additionally,
the trajectory of all ``analysis_objects`` must be the same.
:class:`MDAnalysis.analysis.base.AnalysisBase`. Additionally, the
trajectory of all `analysis_instances` must be the same.
By default, it is ensured that all analysis objects use the
By default, it is ensured that all analysis instances use the
*same original* timestep and not an altered one from a previous analysis
object. This behavior can be changed with the ``reset_timestep`` parameter
object. This behavior can be changed with the `reset_timestep` parameter
of the :meth:`MDAnalysis.analysis.base.AnalysisCollection.run` method.
Changing the default behaviour of `reset_timestep` might be useful to
avoid that subsequent analysis instances have to perform the same
transformation on the timestep.
Parameters
----------
*analysis_objects : tuple
*analysis_instances : tuple
List of analysis classes to run on the same trajectory.
Raises
------
AttributeError
If all the provided ``analysis_objects`` do not have the same trajectory.
If all the provided ``analysis_instances`` do not work on the same
trajectory.
AttributeError
If an ``analysis_object`` is not a child of
:class:`MDAnalysis.analysis.base.AnalysisBase`.
Expand All @@ -265,17 +273,17 @@ class AnalysisCollection(object):
ag_O = u.select_atoms("name O")
ag_H = u.select_atoms("name H")
# Create individual analysis objects
# Create individual analysis instances
rdf_OO = InterRDF(ag_O, ag_O)
rdf_OH = InterRDF(ag_H, ag_H)
# Create a collection for common trajectory
collection = AnalysisCollection(rdf_OO, rdf_OH)
# Run the collected analysis
collection.run(start=0, end=100, step=10)
collection.run(start=0, stop=100, step=10)
# Results are stored in individual objects
# Results are stored in the individual instances
print(rdf_OO.results)
print(rdf_OH.results)
Expand All @@ -284,17 +292,19 @@ class AnalysisCollection(object):
"""

def __init__(self, *analysis_objects):
for analysis_object in analysis_objects:
if analysis_objects[0]._trajectory != analysis_object._trajectory:
raise ValueError("`analysis_objects` do not have the same trajectory.")
def __init__(self, *analysis_instances):
for analysis_object in analysis_instances:
if analysis_instances[0]._trajectory != analysis_object._trajectory:
raise ValueError(
"`analysis_instances` do not have the same trajectory."
)
if not isinstance(analysis_object, AnalysisBase):
raise AttributeError(
f"Analysis object {analysis_object} is "
"not a child of `AnalysisBase`."
)

self._analysis_objects = analysis_objects
self._analysis_instances = analysis_instances

def run(
self,
Expand Down Expand Up @@ -326,20 +336,20 @@ def run(
Reset the timestep object after for each ``analysis_object``.
Setting this to ``False`` can be useful if an ``analysis_object``
is performing a trajectory manipulation which is also useful for the
subsequent ``analysis_objects`` e.g. unwrapping of molecules.
subsequent ``analysis_instances`` e.g. unwrapping of molecules.
"""

# Ensure compatibility with API of version 0.15.0
if not hasattr(self, "_analysis_objects"):
self._analysis_objects = (self,)
if not hasattr(self, "_analysis_instances"):
self._analysis_instances = (self,)

logger.info("Choosing frames to analyze")
# if verbose unchanged, use class default
verbose = getattr(self, "_verbose", False) if verbose is None else verbose

logger.info("Starting preparation")

for analysis_object in self._analysis_objects:
for analysis_object in self._analysis_instances:
analysis_object._setup_frames(
analysis_object._trajectory,
start=start,
Expand All @@ -350,29 +360,34 @@ def run(
analysis_object._prepare()

logger.info(
f"Starting analysis loop over {self._analysis_objects[0].n_frames} "
f"Starting analysis loop over {self._analysis_instances[0].n_frames} "
"trajectory frames."
)

for i, ts in enumerate(
ProgressBar(self._analysis_objects[0]._sliced_trajectory, verbose=verbose)
ProgressBar(self._analysis_instances[0]._sliced_trajectory, verbose=verbose)
):
if reset_timestep:
ts_original = ts.copy()

for analysis_object in self._analysis_objects:
for analysis_object in self._analysis_instances:
# Set attributes before calling `_single_frame()`. Setting
# these attributes explicitly is mandatory so that each
# instance can access the information of the current timestep.
analysis_object._frame_index = i
analysis_object._ts = ts
analysis_object.frames[i] = ts.frame
analysis_object.times[i] = ts.time

# Call the actual analysis of each instance.
analysis_object._single_frame()

if reset_timestep:
ts = ts_original

logger.info("Finishing up")

for analysis_object in self._analysis_objects:
for analysis_object in self._analysis_instances:
analysis_object._conclude()

return self
Expand Down
4 changes: 2 additions & 2 deletions testsuite/MDAnalysisTests/analysis/test_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -196,7 +196,7 @@ def _single_frame(self):
def test_inconsistent_trajectory(self, universe):
v = mda.Universe(TPR, XTC)

with pytest.raises(ValueError, match="`analysis_objects` do not have the same"):
with pytest.raises(ValueError, match="`analysis_instances` do not have the"):
base.AnalysisCollection(
InterRDF(universe.atoms, universe.atoms), InterRDF(v.atoms, v.atoms)
)
Expand All @@ -207,7 +207,7 @@ def __init__(self, trajectory):
self._trajectory = trajectory

# Create collection for common trajectory loop with inconsistent trajectory
with pytest.raises(AttributeError, match="not a child of `AnalysisBa"):
with pytest.raises(AttributeError, match="not a child of `AnalysisBase`"):
base.AnalysisCollection(CustomAnalysis(universe.trajectory))


Expand Down

0 comments on commit 3c5de33

Please sign in to comment.