-
Notifications
You must be signed in to change notification settings - Fork 11
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
Add ensemble_wrapper decorator function #199
base: develop
Are you sure you want to change the base?
Changes from all commits
166b5dc
d7e8999
fd3de18
aa4cec1
848a0ed
9afc298
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -3,7 +3,7 @@ | |
|
||
import os | ||
import errno | ||
from typing import Optional, List | ||
from typing import Optional, List, Union | ||
|
||
import numpy as np | ||
|
||
|
@@ -550,3 +550,48 @@ def check_groups_from_common_ensemble(groups: List[EnsembleAtomGroup]): | |
from the same Ensemble.''' | ||
logger.error(msg) | ||
raise ValueError(msg) | ||
|
||
|
||
def ensemble_wrapper(cls): | ||
"""A decorator for :class:`MDAnalysis.Universe <MDAnalysis.analysis.base.AnalysisBase>` subclasses modifying | ||
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. that's a long 1-line string – shorten 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. and add this in the body |
||
them to accept an :class:`~mdpow.analysis.ensemble.Ensemble` or | ||
:class:`~mdpow.analysis.ensemble.EnsembleAtomGroup`. | ||
|
||
.. rubric:: Example Analysis | ||
|
||
@ensemble_wrapper | ||
class Example(AnalysisBase): | ||
pass | ||
Comment on lines
+563
to
+564
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. A pass class is too trivial, it does not show how you deal with AtomGroups and whatever else one might want to know. It just shows how to use a decorator. Less trivial example here and perhaps a proper example in the text outside the function. |
||
|
||
Ens = Ensemble(dirname='mol_dir) | ||
ExRun = Example(Ens) | ||
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. I'd call the instance |
||
|
||
""" | ||
class EnsembleWrapper: | ||
def __init__(self, ensemble: Union[Ensemble, EnsembleAtomGroup], *args, **kwargs): | ||
self._ensemble = ensemble | ||
self._args = args | ||
self._kwargs = kwargs | ||
self._Analysis = cls | ||
|
||
def _prepare_ensemble(self): | ||
# Defined separately so user can modify behavior | ||
self._results_dict = {x: None for x in self._ensemble.keys()} | ||
|
||
def _conclude_system(self): | ||
# Defined separately so user can modify behavior | ||
self._results_dict[self._key] = self._SystemRun.results | ||
|
||
def _conclude_ensemble(self): | ||
self.results = self._results_dict | ||
|
||
def run(self, start=0, stop=0, step=1): | ||
self._prepare_ensemble() | ||
for self._key in self._ensemble.keys(): | ||
self._SystemRun = self._Analysis(self._ensemble[self._key], *self._args, **self._kwargs) | ||
self._SystemRun.run(start=start, step=step, stop=stop) | ||
self._conclude_system() | ||
self._conclude_ensemble() | ||
return self | ||
Comment on lines
+577
to
+595
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. This looks like code duplication – my bigger conceptual problem is that a wrapped AnalysisBase is not an instance of EnsembleAnalysis. |
||
|
||
return EnsembleWrapper |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -13,10 +13,11 @@ | |
|
||
import MDAnalysis as mda | ||
from MDAnalysis.exceptions import NoDataError, SelectionError | ||
from MDAnalysis.analysis.base import AnalysisBase | ||
|
||
from gromacs.utilities import in_dir | ||
|
||
from ..analysis.ensemble import Ensemble, EnsembleAnalysis, EnsembleAtomGroup | ||
from ..analysis.ensemble import Ensemble, EnsembleAnalysis, EnsembleAtomGroup, ensemble_wrapper | ||
from ..analysis.dihedral import DihedralAnalysis | ||
|
||
from pkg_resources import resource_filename | ||
|
@@ -161,3 +162,29 @@ def test_value_error(self): | |
dh4 = ens.select_atoms('name C4 or name C17 or name S2 or name N3') | ||
with pytest.raises(ValueError): | ||
dh_run = DihedralAnalysis([dh1, dh2, dh4, dh3]).run(start=0, stop=4, step=1) | ||
|
||
def test_ensemble_wrapper1(self): | ||
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. why the "1" ? |
||
|
||
class BaseTest(AnalysisBase): | ||
def __init__(self, system: mda.Universe): | ||
super(BaseTest, self).__init__(system.trajectory) | ||
self.system = system | ||
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. Why not call it |
||
|
||
def _prepare(self): | ||
self._res_arr = [] | ||
|
||
def _single_frame(self): | ||
self._res_arr.append(len(self.system.select_atoms('not resname SOL'))) | ||
assert self._res_arr[-1] == 42 | ||
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. Why is there an 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. I think you want to say that you know that there are 42 atoms in the solute. However, this assert looks as if you wanted it to be part of the test so it's confusing. At a minimum, add a comment. But perhaps you could just make it a more realistic analysis? People also look at tests to see how to use code so giving a good example here is useful. And you could use it for the docs as well. |
||
|
||
def _conclude(self): | ||
self.results = self._res_arr | ||
|
||
@ensemble_wrapper | ||
class EnsembleTest(BaseTest): | ||
pass | ||
|
||
Sim = Ensemble(dirname=self.tmpdir.name, solvents=['water']) | ||
SolvCount = EnsembleTest(Sim).run(stop=10) | ||
assert isinstance(SolvCount, EnsembleTest) | ||
assert SolvCount.results[('water', 'VDW', '0000')] == ([42] * 10) |
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.
Add more text here that shows a developer how to use it. Perhaps based on a real world example using some of the simpler MDAnalysis base classes?
There should be enough information for another developer (or future you) to learn how to use it. This means answers to the following questions: