Closed lilyminium closed 2 years ago
Hello @lilyminium! Thanks for updating this PR. We checked the lines you've touched for PEP 8 issues, and found:
hooks/pre_gen_project.py
:Line 33:80: E501 line too long (100 > 79 characters)
tests/test_options.py
:Line 25:80: E501 line too long (82 > 79 characters)
tests/utils.py
:Line 26:1: E302 expected 2 blank lines, found 1 Line 27:80: E501 line too long (89 > 79 characters)
{{cookiecutter.repo_name}}/{{cookiecutter.package_name}}/analysis/{{cookiecutter.template_analysis_class.lower()}}.py
](https://github.com/MDAnalysis/cookiecutter-mdakit/blob/e3d28d3e5c1b3111fa927c47b98cd53ee42e6bae/{{cookiecutter.repo_name}}/{{cookiecutter.package_name}}/analysis/{{cookiecutter.template_analysis_class.lower()}}.py):[Line 2:80](https://github.com/MDAnalysis/cookiecutter-mdakit/blob/e3d28d3e5c1b3111fa927c47b98cd53ee42e6bae/{{cookiecutter.repo_name}}/{{cookiecutter.package_name}}/analysis/{{cookiecutter.template_analysis_class.lower()}}.py#L2): E501 line too long (137 > 79 characters) [Line 5:80](https://github.com/MDAnalysis/cookiecutter-mdakit/blob/e3d28d3e5c1b3111fa927c47b98cd53ee42e6bae/{{cookiecutter.repo_name}}/{{cookiecutter.package_name}}/analysis/{{cookiecutter.template_analysis_class.lower()}}.py#L5): E501 line too long (83 > 79 characters) [Line 16:1](https://github.com/MDAnalysis/cookiecutter-mdakit/blob/e3d28d3e5c1b3111fa927c47b98cd53ee42e6bae/{{cookiecutter.repo_name}}/{{cookiecutter.package_name}}/analysis/{{cookiecutter.template_analysis_class.lower()}}.py#L16): E302 expected 2 blank lines, found 1 [Line 16:9](https://github.com/MDAnalysis/cookiecutter-mdakit/blob/e3d28d3e5c1b3111fa927c47b98cd53ee42e6bae/{{cookiecutter.repo_name}}/{{cookiecutter.package_name}}/analysis/{{cookiecutter.template_analysis_class.lower()}}.py#L16): E201 whitespace after '{' [Line 16:46](https://github.com/MDAnalysis/cookiecutter-mdakit/blob/e3d28d3e5c1b3111fa927c47b98cd53ee42e6bae/{{cookiecutter.repo_name}}/{{cookiecutter.package_name}}/analysis/{{cookiecutter.template_analysis_class.lower()}}.py#L16): E202 whitespace before '}' [Line 23:80](https://github.com/MDAnalysis/cookiecutter-mdakit/blob/e3d28d3e5c1b3111fa927c47b98cd53ee42e6bae/{{cookiecutter.repo_name}}/{{cookiecutter.package_name}}/analysis/{{cookiecutter.template_analysis_class.lower()}}.py#L23): E501 line too long (116 > 79 characters) [Line 28:80](https://github.com/MDAnalysis/cookiecutter-mdakit/blob/e3d28d3e5c1b3111fa927c47b98cd53ee42e6bae/{{cookiecutter.repo_name}}/{{cookiecutter.package_name}}/analysis/{{cookiecutter.template_analysis_class.lower()}}.py#L28): E501 line too long (82 > 79 characters) [Line 56:5](https://github.com/MDAnalysis/cookiecutter-mdakit/blob/e3d28d3e5c1b3111fa927c47b98cd53ee42e6bae/{{cookiecutter.repo_name}}/{{cookiecutter.package_name}}/analysis/{{cookiecutter.template_analysis_class.lower()}}.py#L56): E303 too many blank lines (2) [Line 74:1](https://github.com/MDAnalysis/cookiecutter-mdakit/blob/e3d28d3e5c1b3111fa927c47b98cd53ee42e6bae/{{cookiecutter.repo_name}}/{{cookiecutter.package_name}}/analysis/{{cookiecutter.template_analysis_class.lower()}}.py#L74): W293 blank line contains whitespace [Line 76:5](https://github.com/MDAnalysis/cookiecutter-mdakit/blob/e3d28d3e5c1b3111fa927c47b98cd53ee42e6bae/{{cookiecutter.repo_name}}/{{cookiecutter.package_name}}/analysis/{{cookiecutter.template_analysis_class.lower()}}.py#L76): E303 too many blank lines (3) [Line 95:1](https://github.com/MDAnalysis/cookiecutter-mdakit/blob/e3d28d3e5c1b3111fa927c47b98cd53ee42e6bae/{{cookiecutter.repo_name}}/{{cookiecutter.package_name}}/analysis/{{cookiecutter.template_analysis_class.lower()}}.py#L95): W293 blank line contains whitespace [Line 110:80](https://github.com/MDAnalysis/cookiecutter-mdakit/blob/e3d28d3e5c1b3111fa927c47b98cd53ee42e6bae/{{cookiecutter.repo_name}}/{{cookiecutter.package_name}}/analysis/{{cookiecutter.template_analysis_class.lower()}}.py#L110): E501 line too long (89 > 79 characters) [Line 111:80](https://github.com/MDAnalysis/cookiecutter-mdakit/blob/e3d28d3e5c1b3111fa927c47b98cd53ee42e6bae/{{cookiecutter.repo_name}}/{{cookiecutter.package_name}}/analysis/{{cookiecutter.template_analysis_class.lower()}}.py#L111): E501 line too long (90 > 79 characters)
{{cookiecutter.repo_name}}/{{cookiecutter.package_name}}/tests/analysis/test_{{cookiecutter.template_analysis_class.lower()}}.py
](https://github.com/MDAnalysis/cookiecutter-mdakit/blob/e3d28d3e5c1b3111fa927c47b98cd53ee42e6bae/{{cookiecutter.repo_name}}/{{cookiecutter.package_name}}/tests/analysis/test_{{cookiecutter.template_analysis_class.lower()}}.py):[Line 4:80](https://github.com/MDAnalysis/cookiecutter-mdakit/blob/e3d28d3e5c1b3111fa927c47b98cd53ee42e6bae/{{cookiecutter.repo_name}}/{{cookiecutter.package_name}}/tests/analysis/test_{{cookiecutter.template_analysis_class.lower()}}.py#L4): E501 line too long (132 > 79 characters) [Line 7:1](https://github.com/MDAnalysis/cookiecutter-mdakit/blob/e3d28d3e5c1b3111fa927c47b98cd53ee42e6bae/{{cookiecutter.repo_name}}/{{cookiecutter.package_name}}/tests/analysis/test_{{cookiecutter.template_analysis_class.lower()}}.py#L7): E302 expected 2 blank lines, found 1 [Line 8:1](https://github.com/MDAnalysis/cookiecutter-mdakit/blob/e3d28d3e5c1b3111fa927c47b98cd53ee42e6bae/{{cookiecutter.repo_name}}/{{cookiecutter.package_name}}/tests/analysis/test_{{cookiecutter.template_analysis_class.lower()}}.py#L8): W293 blank line contains whitespace [Line 27:5](https://github.com/MDAnalysis/cookiecutter-mdakit/blob/e3d28d3e5c1b3111fa927c47b98cd53ee42e6bae/{{cookiecutter.repo_name}}/{{cookiecutter.package_name}}/tests/analysis/test_{{cookiecutter.template_analysis_class.lower()}}.py#L27): E303 too many blank lines (2) [Line 37:80](https://github.com/MDAnalysis/cookiecutter-mdakit/blob/e3d28d3e5c1b3111fa927c47b98cd53ee42e6bae/{{cookiecutter.repo_name}}/{{cookiecutter.package_name}}/tests/analysis/test_{{cookiecutter.template_analysis_class.lower()}}.py#L37): E501 line too long (84 > 79 characters) [Line 39:1](https://github.com/MDAnalysis/cookiecutter-mdakit/blob/e3d28d3e5c1b3111fa927c47b98cd53ee42e6bae/{{cookiecutter.repo_name}}/{{cookiecutter.package_name}}/tests/analysis/test_{{cookiecutter.template_analysis_class.lower()}}.py#L39): W293 blank line contains whitespace [Line 41:5](https://github.com/MDAnalysis/cookiecutter-mdakit/blob/e3d28d3e5c1b3111fa927c47b98cd53ee42e6bae/{{cookiecutter.repo_name}}/{{cookiecutter.package_name}}/tests/analysis/test_{{cookiecutter.template_analysis_class.lower()}}.py#L41): E303 too many blank lines (2) [Line 53:80](https://github.com/MDAnalysis/cookiecutter-mdakit/blob/e3d28d3e5c1b3111fa927c47b98cd53ee42e6bae/{{cookiecutter.repo_name}}/{{cookiecutter.package_name}}/tests/analysis/test_{{cookiecutter.template_analysis_class.lower()}}.py#L53): E501 line too long (80 > 79 characters) [Line 58:24](https://github.com/MDAnalysis/cookiecutter-mdakit/blob/e3d28d3e5c1b3111fa927c47b98cd53ee42e6bae/{{cookiecutter.repo_name}}/{{cookiecutter.package_name}}/tests/analysis/test_{{cookiecutter.template_analysis_class.lower()}}.py#L58): E261 at least two spaces before inline comment [Line 59:20](https://github.com/MDAnalysis/cookiecutter-mdakit/blob/e3d28d3e5c1b3111fa927c47b98cd53ee42e6bae/{{cookiecutter.repo_name}}/{{cookiecutter.package_name}}/tests/analysis/test_{{cookiecutter.template_analysis_class.lower()}}.py#L59): E261 at least two spaces before inline comment
The files have just been printed out in CI until we figure out how to have example repos:
Analysis file:
"""
MyAnalysis --- :mod:`testmdakit_deps_condaforge_rtd_y.analysis.MyAnalysis`
===========================================================
This module contains the :class:`MyAnalysis` class.
"""
from typing import Union, TYPE_CHECKING
from MDAnalysis.analysis.base import AnalysisBase
import numpy as np
if TYPE_CHECKING:
from MDAnalysis.core.universe import Universe, AtomGroup
class MyAnalysis(AnalysisBase):
"""MyAnalysis class.
This class is used to perform analysis on a trajectory.
Parameters
----------
universe_or_atomgroup: :class:`~MDAnalysis.core.universe.Universe` or :class:`~MDAnalysis.core.groups.AtomGroup`
Universe or group of atoms to apply this analysis to.
If a trajectory is associated with the atoms,
then the computation iterates over the trajectory.
Attributes
----------
universe: :class:`~MDAnalysis.core.universe.Universe`
The universe to which this analysis is applied
atomgroup: :class:`~MDAnalysis.core.groups.AtomGroup`
The atoms to which this analysis is applied
results: :class:`~MDAnalysis.analysis.base.Results`
results of calculation are stored here, after calling
:meth:`MyAnalysis.run`
start: Optional[int]
The first frame of the trajectory used to compute the analysis
stop: Optional[int]
The frame to stop at for the analysis
step: Optional[int]
Number of frames to skip between each analyzed frame
n_frames: int
Number of frames analysed in the trajectory
times: numpy.ndarray
array of Timestep times. Only exists after calling
:meth:`MyAnalysis.run`
frames: numpy.ndarray
array of Timestep frame indices. Only exists after calling
:meth:`MyAnalysis.run`
"""
def __init__(
self,
universe_or_atomgroup: Union[Universe, AtomGroup],
select: str = "all",
# TODO: add your own parameters here
**kwargs
):
# the below line must be kept to initialize the AnalysisBase class!
super().__init__(universe_or_atomgroup.trajectory)
# after this you will be able to access `self.results`
self.universe = universe_or_atomgroup.universe
self.atomgroup = universe_or_atomgroup.select_atoms(select)
def _prepare(self):
"""Set things up before the analysis loop begins"""
# This is an optional method that runs before
# _single_frame loops over the trajectory.
# It is useful for setting up results arrays
# For example, below we create an array to store
# the number of atoms with negative coordinates
# in each frame.
self.results.is_negative = np.zeros(
(self.n_frames, self.atomgroup.n_atoms),
dtype=bool,
)
def _single_frame(self):
"""Calculate data from a single frame of trajectory"""
# This runs once for each frame of the trajectory
# It can contain the main analysis method, or just collect data
# so that analysis can be done over the aggregate data
# in _conclude.
# The trajectory positions update automatically
negative = self.atomgroup.positions < 0
# You can access the frame number using self._frame_index
self.results.is_negative[self._frame_index] = negative.any(axis=1)
def _conclude(self):
"""Calculate the final results of the analysis"""
# This is an optional method that runs after
# _single_frame loops over the trajectory.
# It is useful for calculating the final results
# of the analysis.
# For example, below we determine the
# which atoms always have negative coordinates.
self.results.always_negative = self.results.is_negative.all(axis=0)
self.results.always_negative_atoms = self.atomgroup[self.results.always_negative]
self.results.always_negative_atom_names = self.results.always_negative_atoms.names
# results don't have to be arrays -- they can be any value, e.g. floats
self.results.n_negative_atoms = self.results.is_negative.sum(axis=1)
self.results.mean_negative_atoms = self.results.n_negative_atoms.mean()
Test file:
```python
import pytest
from numpy.testing import assert_allclose
from testmdakit_deps_condaforge_rtd_y.analysis.MyAnalysis import MyAnalysis
from testmdakit_deps_condaforge_rtd_y.tests.utils import make_Universe
class TestMyAnalysis:
# fixtures are helpful functions that set up a test
# See more at https://docs.pytest.org/en/stable/how-to/fixtures.html
@pytest.fixture
def universe(self):
u = make_Universe(
extras=("names", "resnames",),
n_frames=3,
)
# create toy data to test assumptions
for ts in u.trajectory.ts:
ts.positions[:ts.frame] *= -1
return u
@pytest.fixture
def analysis(self, universe):
return MyAnalysis(universe)
@pytest.mark.parametrize(
"select, n_atoms", # argument names
[ # argument values in a tuple, in order
("all", 125),
("index < 10", 10),
("resindex > 2", 50),
]
)
def test_atom_selection(self, universe, select, n_atoms):
# `universe` here is the fixture defined above
analysis = MyAnalysis(universe, select=select)
assert analysis.atomgroup.n_atoms == n_atoms
@pytest.mark.parametrize(
"stop, expected_mean",
[
(0, 0),
(1, 1),
(2, 1.5),
]
)
def test_mean_negative_atoms(self, analysis, stop, expected_mean):
# assert we haven't run yet and the result doesn't exist yet
assert "mean_negative_atoms" not in analysis.results
analysis.run(stop=stop)
# when comparing floating point values, it's best to use assert_allclose
# to allow for floating point precision differences
assert_allclose(
analysis.results.mean_negative_atoms, # computed data
expected_mean, # reference / desired data
rtol=1e-07, # relative tolerance
atol=0, # absolute tolerance
err_msg="mean_negative_atoms is not correct",
)
This PR adds an analysis template and some tests.
Relates to #11