From 6a44252496600e699c4784c6082a72799c02aff0 Mon Sep 17 00:00:00 2001 From: Rocco Meli Date: Fri, 20 Dec 2024 21:52:15 +0100 Subject: [PATCH 1/5] format analysis --- package/MDAnalysis/analysis/__init__.py | 54 +- package/MDAnalysis/analysis/backends.py | 27 +- package/MDAnalysis/analysis/base.py | 221 ++- package/MDAnalysis/analysis/bat.py | 228 +-- package/MDAnalysis/analysis/contacts.py | 98 +- package/MDAnalysis/analysis/data/filenames.py | 9 +- package/MDAnalysis/analysis/density.py | 236 +-- package/MDAnalysis/analysis/dielectric.py | 36 +- package/MDAnalysis/analysis/dihedrals.py | 206 ++- package/MDAnalysis/analysis/distances.py | 44 +- package/MDAnalysis/analysis/dssp/dssp.py | 11 +- .../MDAnalysis/analysis/dssp/pydssp_numpy.py | 13 +- .../MDAnalysis/analysis/encore/__init__.py | 28 +- .../MDAnalysis/analysis/encore/bootstrap.py | 37 +- .../encore/clustering/ClusterCollection.py | 89 +- .../encore/clustering/ClusteringMethod.py | 193 +-- .../analysis/encore/clustering/__init__.py | 5 +- .../analysis/encore/clustering/cluster.py | 57 +- .../analysis/encore/confdistmatrix.py | 291 ++-- .../MDAnalysis/analysis/encore/covariance.py | 101 +- .../DimensionalityReductionMethod.py | 67 +- .../dimensionality_reduction/__init__.py | 5 +- .../reduce_dimensionality.py | 59 +- .../MDAnalysis/analysis/encore/similarity.py | 578 +++++--- package/MDAnalysis/analysis/encore/utils.py | 59 +- package/MDAnalysis/analysis/gnm.py | 110 +- .../MDAnalysis/analysis/hbonds/__init__.py | 3 +- .../analysis/hbonds/hbond_autocorrel.py | 38 +- package/MDAnalysis/analysis/helix_analysis.py | 149 +- package/MDAnalysis/analysis/hole2/__init__.py | 10 +- .../analysis/hydrogenbonds/__init__.py | 7 +- .../hydrogenbonds/hbond_autocorrel.py | 250 ++-- .../hydrogenbonds/wbridge_analysis.py | 908 ++++++++---- package/MDAnalysis/analysis/leaflet.py | 103 +- package/MDAnalysis/analysis/legacy/x3dna.py | 675 +++++++-- package/MDAnalysis/analysis/lineardensity.py | 117 +- package/MDAnalysis/analysis/msd.py | 85 +- package/MDAnalysis/analysis/nucleicacids.py | 162 ++- package/MDAnalysis/analysis/nuclinfo.py | 478 ++++-- package/MDAnalysis/analysis/pca.py | 221 +-- package/MDAnalysis/analysis/polymer.py | 95 +- package/MDAnalysis/analysis/psa.py | 10 +- package/MDAnalysis/analysis/rdf.py | 209 +-- package/MDAnalysis/analysis/results.py | 25 +- package/MDAnalysis/analysis/rms.py | 322 +++-- package/MDAnalysis/analysis/waterdynamics.py | 10 +- package/pyproject.toml | 2 + .../MDAnalysisTests/analysis/conftest.py | 59 +- .../MDAnalysisTests/analysis/test_align.py | 468 +++--- .../analysis/test_atomicdistances.py | 33 +- .../MDAnalysisTests/analysis/test_backends.py | 38 +- .../MDAnalysisTests/analysis/test_base.py | 406 ++++-- .../MDAnalysisTests/analysis/test_bat.py | 76 +- .../MDAnalysisTests/analysis/test_contacts.py | 658 +++++++-- .../MDAnalysisTests/analysis/test_data.py | 12 +- .../MDAnalysisTests/analysis/test_density.py | 267 ++-- .../analysis/test_dielectric.py | 16 +- .../analysis/test_diffusionmap.py | 94 +- .../analysis/test_dihedrals.py | 214 ++- .../analysis/test_distances.py | 157 +- .../MDAnalysisTests/analysis/test_dssp.py | 25 +- .../MDAnalysisTests/analysis/test_encore.py | 1287 +++++++++++------ .../MDAnalysisTests/analysis/test_gnm.py | 731 +++++++++- .../analysis/test_helix_analysis.py | 498 ++++--- .../analysis/test_hydrogenbondautocorrel.py | 332 +++-- .../test_hydrogenbondautocorrel_deprecated.py | 361 +++-- .../analysis/test_hydrogenbonds_analysis.py | 524 +++---- .../MDAnalysisTests/analysis/test_leaflet.py | 80 +- .../analysis/test_lineardensity.py | 220 ++- .../MDAnalysisTests/analysis/test_msd.py | 143 +- .../analysis/test_nucleicacids.py | 14 +- .../MDAnalysisTests/analysis/test_nuclinfo.py | 201 ++- .../MDAnalysisTests/analysis/test_pca.py | 161 ++- .../analysis/test_persistencelength.py | 20 +- .../MDAnalysisTests/analysis/test_rdf.py | 38 +- .../MDAnalysisTests/analysis/test_rdf_s.py | 58 +- .../MDAnalysisTests/analysis/test_results.py | 15 +- .../MDAnalysisTests/analysis/test_rms.py | 425 +++--- .../MDAnalysisTests/analysis/test_wbridge.py | 901 ++++++++---- testsuite/pyproject.toml | 1 + 80 files changed, 10032 insertions(+), 5242 deletions(-) diff --git a/package/MDAnalysis/analysis/__init__.py b/package/MDAnalysis/analysis/__init__.py index 056c7899826..804f9dbf24c 100644 --- a/package/MDAnalysis/analysis/__init__.py +++ b/package/MDAnalysis/analysis/__init__.py @@ -22,31 +22,31 @@ # __all__ = [ - 'align', - 'backends', - 'base', - 'contacts', - 'density', - 'distances', - 'diffusionmap', - 'dihedrals', - 'distances', - 'dielectric', - 'gnm', - 'hbonds', - 'helix_analysis', - 'hole2', - 'hydrogenbonds', - 'leaflet', - 'lineardensity', - 'msd', - 'nuclinfo', - 'nucleicacids', - 'polymer', - 'pca', - 'psa', - 'rdf', - 'results', - 'rms', - 'waterdynamics', + "align", + "backends", + "base", + "contacts", + "density", + "distances", + "diffusionmap", + "dihedrals", + "distances", + "dielectric", + "gnm", + "hbonds", + "helix_analysis", + "hole2", + "hydrogenbonds", + "leaflet", + "lineardensity", + "msd", + "nuclinfo", + "nucleicacids", + "polymer", + "pca", + "psa", + "rdf", + "results", + "rms", + "waterdynamics", ] diff --git a/package/MDAnalysis/analysis/backends.py b/package/MDAnalysis/analysis/backends.py index 38917cb2ae7..0cfad4e1c31 100644 --- a/package/MDAnalysis/analysis/backends.py +++ b/package/MDAnalysis/analysis/backends.py @@ -35,6 +35,7 @@ ------- """ + import warnings from typing import Callable from MDAnalysis.lib.util import is_installed @@ -102,8 +103,9 @@ def _get_checks(self): checked during ``_validate()`` run """ return { - isinstance(self.n_workers, int) and self.n_workers > 0: - f"n_workers should be positive integer, got {self.n_workers=}", + isinstance(self.n_workers, int) + and self.n_workers + > 0: f"n_workers should be positive integer, got {self.n_workers=}", } def _get_warnings(self): @@ -183,8 +185,8 @@ def _get_warnings(self): checked during ``_validate()`` run """ return { - self.n_workers == 1: - "n_workers is ignored when executing with backend='serial'" + self.n_workers + == 1: "n_workers is ignored when executing with backend='serial'" } def apply(self, func: Callable, computations: list) -> list: @@ -307,10 +309,12 @@ def apply(self, func: Callable, computations: list) -> list: import dask computations = [delayed(func)(task) for task in computations] - results = dask.compute(computations, - scheduler="processes", - chunksize=1, - num_workers=self.n_workers)[0] + results = dask.compute( + computations, + scheduler="processes", + chunksize=1, + num_workers=self.n_workers, + )[0] return results def _get_checks(self): @@ -326,8 +330,9 @@ def _get_checks(self): """ base_checks = super()._get_checks() checks = { - is_installed("dask"): - ("module 'dask' is missing. Please install 'dask': " - "https://docs.dask.org/en/stable/install.html") + is_installed("dask"): ( + "module 'dask' is missing. Please install 'dask': " + "https://docs.dask.org/en/stable/install.html" + ) } return base_checks | checks diff --git a/package/MDAnalysis/analysis/base.py b/package/MDAnalysis/analysis/base.py index 4e7f58dc0bd..675c6d6967b 100644 --- a/package/MDAnalysis/analysis/base.py +++ b/package/MDAnalysis/analysis/base.py @@ -159,7 +159,12 @@ def _get_aggregator(self): from ..core.groups import AtomGroup from ..lib.log import ProgressBar -from .backends import BackendDask, BackendMultiprocessing, BackendSerial, BackendBase +from .backends import ( + BackendDask, + BackendMultiprocessing, + BackendSerial, + BackendBase, +) from .results import Results, ResultsGroup logger = logging.getLogger(__name__) @@ -288,10 +293,10 @@ def get_supported_backends(cls): """ return ("serial",) - # class authors: override _analysis_algorithm_is_parallelizable - # in derived classes and only set to True if you have confirmed - # that your algorithm works reliably when parallelized with - # the split-apply-combine approach (see docs) + # class authors: override _analysis_algorithm_is_parallelizable + # in derived classes and only set to True if you have confirmed + # that your algorithm works reliably when parallelized with + # the split-apply-combine approach (see docs) _analysis_algorithm_is_parallelizable = False @property @@ -301,13 +306,13 @@ def parallelizable(self): :meth:`_single_frame` to multiple workers and then combine them with a proper :meth:`_conclude` call. If set to ``False``, no backends except for ``serial`` are supported. - + .. note:: If you want to check parallelizability of the whole class, without explicitly creating an instance of the class, see :attr:`_analysis_algorithm_is_parallelizable`. Note that you setting it to other value will break things if the algorithm behind the analysis is not trivially parallelizable. - + Returns ------- @@ -325,9 +330,9 @@ def __init__(self, trajectory, verbose=False, **kwargs): self._verbose = verbose self.results = Results() - def _define_run_frames(self, trajectory, - start=None, stop=None, step=None, frames=None - ) -> Union[slice, np.ndarray]: + def _define_run_frames( + self, trajectory, start=None, stop=None, step=None, frames=None + ) -> Union[slice, np.ndarray]: """Defines limits for the whole run, as passed by self.run() arguments Parameters @@ -362,10 +367,14 @@ def _define_run_frames(self, trajectory, self._trajectory = trajectory if frames is not None: if not all(opt is None for opt in [start, stop, step]): - raise ValueError("start/stop/step cannot be combined with frames") + raise ValueError( + "start/stop/step cannot be combined with frames" + ) slicer = frames else: - start, stop, step = trajectory.check_slice_indices(start, stop, step) + start, stop, step = trajectory.check_slice_indices( + start, stop, step + ) slicer = slice(start, stop, step) self.start, self.stop, self.step = start, stop, step return slicer @@ -388,7 +397,9 @@ def _prepare_sliced_trajectory(self, slicer: Union[slice, np.ndarray]): self.frames = np.zeros(self.n_frames, dtype=int) self.times = np.zeros(self.n_frames) - def _setup_frames(self, trajectory, start=None, stop=None, step=None, frames=None): + def _setup_frames( + self, trajectory, start=None, stop=None, step=None, frames=None + ): """Pass a Reader object and define the desired iteration pattern through the trajectory @@ -417,11 +428,11 @@ def _setup_frames(self, trajectory, start=None, stop=None, step=None, frames=Non .. versionchanged:: 1.0.0 Added .frames and .times arrays as attributes - + .. versionchanged:: 2.2.0 Added ability to iterate through trajectory by passing a list of frame indices in the `frames` keyword argument - + .. versionchanged:: 2.8.0 Split function into two: :meth:`_define_run_frames` and :meth:`_prepare_sliced_trajectory`: first one defines the limits @@ -450,8 +461,8 @@ def _single_frame(self): def _prepare(self): """ - Set things up before the analysis loop begins. - + Set things up before the analysis loop begins. + Notes ----- ``self.results`` is initialized already in :meth:`self.__init__` with an @@ -477,9 +488,13 @@ def _conclude(self): """ pass # pylint: disable=unnecessary-pass - def _compute(self, indexed_frames: np.ndarray, - verbose: bool = None, - *, progressbar_kwargs=None) -> "AnalysisBase": + def _compute( + self, + indexed_frames: np.ndarray, + verbose: bool = None, + *, + progressbar_kwargs=None, + ) -> "AnalysisBase": """Perform the calculation on a balanced slice of frames that have been setup prior to that using _setup_computation_groups() @@ -504,7 +519,9 @@ def _compute(self, indexed_frames: np.ndarray, progressbar_kwargs = {} logger.info("Choosing frames to analyze") # if verbose unchanged, use class default - verbose = getattr(self, "_verbose", False) if verbose is None else verbose + verbose = ( + getattr(self, "_verbose", False) if verbose is None else verbose + ) frames = indexed_frames[:, 1] @@ -514,10 +531,11 @@ def _compute(self, indexed_frames: np.ndarray, if len(frames) == 0: # if `frames` were empty in `run` or `stop=0` return self - for idx, ts in enumerate(ProgressBar( - self._sliced_trajectory, - verbose=verbose, - **progressbar_kwargs)): + for idx, ts in enumerate( + ProgressBar( + self._sliced_trajectory, verbose=verbose, **progressbar_kwargs + ) + ): self._frame_index = idx # accessed later by subclasses self._ts = ts self.frames[idx] = ts.frame @@ -527,9 +545,12 @@ def _compute(self, indexed_frames: np.ndarray, return self def _setup_computation_groups( - self, n_parts: int, - start: int = None, stop: int = None, step: int = None, - frames: Union[slice, np.ndarray] = None + self, + n_parts: int, + start: int = None, + stop: int = None, + step: int = None, + frames: Union[slice, np.ndarray] = None, ) -> list[np.ndarray]: """ Splits the trajectory frames, defined by ``start/stop/step`` or @@ -566,7 +587,9 @@ def _setup_computation_groups( .. versionadded:: 2.8.0 """ if frames is None: - start, stop, step = self._trajectory.check_slice_indices(start, stop, step) + start, stop, step = self._trajectory.check_slice_indices( + start, stop, step + ) used_frames = np.arange(start, stop, step) elif not all(opt is None for opt in [start, stop, step]): raise ValueError("start/stop/step cannot be combined with frames") @@ -578,23 +601,27 @@ def _setup_computation_groups( used_frames = arange[used_frames] # similar to list(enumerate(frames)) - enumerated_frames = np.vstack([np.arange(len(used_frames)), used_frames]).T + enumerated_frames = np.vstack( + [np.arange(len(used_frames)), used_frames] + ).T if len(enumerated_frames) == 0: return [np.empty((0, 2), dtype=np.int64)] elif len(enumerated_frames) < n_parts: # Issue #4685 n_parts = len(enumerated_frames) - warnings.warn(f"Set `n_parts` to {n_parts} to match the total " - "number of frames being analyzed") + warnings.warn( + f"Set `n_parts` to {n_parts} to match the total " + "number of frames being analyzed" + ) return np.array_split(enumerated_frames, n_parts) def _configure_backend( - self, - backend: Union[str, BackendBase], - n_workers: int, - unsupported_backend: bool = False - ) -> BackendBase: + self, + backend: Union[str, BackendBase], + n_workers: int, + unsupported_backend: bool = False, + ) -> BackendBase: """Matches a passed backend string value with class attributes :attr:`parallelizable` and :meth:`get_supported_backends()` to check if downstream calculations can be performed. @@ -642,13 +669,12 @@ def _configure_backend( builtin_backends = { "serial": BackendSerial, "multiprocessing": BackendMultiprocessing, - "dask": BackendDask + "dask": BackendDask, } backend_class = builtin_backends.get(backend, backend) supported_backend_classes = [ - builtin_backends.get(b) - for b in self.get_supported_backends() + builtin_backends.get(b) for b in self.get_supported_backends() ] # check for serial-only classes @@ -656,20 +682,28 @@ def _configure_backend( raise ValueError(f"Can not parallelize class {self.__class__}") # make sure user enabled 'unsupported_backend=True' for custom classes - if (not unsupported_backend and self.parallelizable - and backend_class not in supported_backend_classes): - raise ValueError(( - f"Must specify 'unsupported_backend=True'" - f"if you want to use a custom {backend_class=} for {self.__class__}" - )) + if ( + not unsupported_backend + and self.parallelizable + and backend_class not in supported_backend_classes + ): + raise ValueError( + ( + f"Must specify 'unsupported_backend=True'" + f"if you want to use a custom {backend_class=} for {self.__class__}" + ) + ) # check for the presence of parallelizable transformations if backend_class is not BackendSerial and any( - not t.parallelizable - for t in self._trajectory.transformations): - raise ValueError(( - "Trajectory should not have " - "associated unparallelizable transformations")) + not t.parallelizable for t in self._trajectory.transformations + ): + raise ValueError( + ( + "Trajectory should not have " + "associated unparallelizable transformations" + ) + ) # conclude mapping from string to backend class if it's a builtin backend if isinstance(backend, str): @@ -679,21 +713,27 @@ def _configure_backend( if ( isinstance(backend, BackendBase) and n_workers is not None - and hasattr(backend, 'n_workers') + and hasattr(backend, "n_workers") and backend.n_workers != n_workers ): - raise ValueError(( - f"n_workers specified twice: in {backend.n_workers=}" - f"and in run({n_workers=}). Remove it from run()" - )) + raise ValueError( + ( + f"n_workers specified twice: in {backend.n_workers=}" + f"and in run({n_workers=}). Remove it from run()" + ) + ) # or pass along an instance of the class itself # after ensuring it has apply method - if not isinstance(backend, BackendBase) or not hasattr(backend, "apply"): - raise ValueError(( - f"{backend=} is invalid: should have 'apply' method " - "and be instance of MDAnalysis.analysis.backends.BackendBase" - )) + if not isinstance(backend, BackendBase) or not hasattr( + backend, "apply" + ): + raise ValueError( + ( + f"{backend=} is invalid: should have 'apply' method " + "and be instance of MDAnalysis.analysis.backends.BackendBase" + ) + ) return backend def run( @@ -775,18 +815,23 @@ def run( # default to serial execution backend = "serial" if backend is None else backend - progressbar_kwargs = {} if progressbar_kwargs is None else progressbar_kwargs - if ((progressbar_kwargs or verbose) and - not (backend == "serial" or - isinstance(backend, BackendSerial))): - raise ValueError("Can not display progressbar with non-serial backend") + progressbar_kwargs = ( + {} if progressbar_kwargs is None else progressbar_kwargs + ) + if (progressbar_kwargs or verbose) and not ( + backend == "serial" or isinstance(backend, BackendSerial) + ): + raise ValueError( + "Can not display progressbar with non-serial backend" + ) # if number of workers not specified, try getting the number from # the backend instance if possible, or set to 1 if n_workers is None: n_workers = ( backend.n_workers - if isinstance(backend, BackendBase) and hasattr(backend, "n_workers") + if isinstance(backend, BackendBase) + and hasattr(backend, "n_workers") else 1 ) @@ -798,22 +843,31 @@ def run( executor = self._configure_backend( backend=backend, n_workers=n_workers, - unsupported_backend=unsupported_backend) + unsupported_backend=unsupported_backend, + ) if ( hasattr(executor, "n_workers") and n_parts < executor.n_workers ): # using executor's value here for non-default executors - warnings.warn(( - f"Analysis not making use of all workers: " - f"{executor.n_workers=} is greater than {n_parts=}")) + warnings.warn( + ( + f"Analysis not making use of all workers: " + f"{executor.n_workers=} is greater than {n_parts=}" + ) + ) # start preparing the run worker_func = partial( self._compute, progressbar_kwargs=progressbar_kwargs, - verbose=verbose) + verbose=verbose, + ) self._setup_frames( trajectory=self._trajectory, - start=start, stop=stop, step=step, frames=frames) + start=start, + stop=stop, + step=step, + frames=frames, + ) computation_groups = self._setup_computation_groups( start=start, stop=stop, step=step, frames=frames, n_parts=n_parts ) @@ -822,7 +876,8 @@ def run( # we need `AnalysisBase` classes # since they hold `frames`, `times` and `results` attributes remote_objects: list["AnalysisBase"] = executor.apply( - worker_func, computation_groups) + worker_func, computation_groups + ) self.frames = np.hstack([obj.frames for obj in remote_objects]) self.times = np.hstack([obj.times for obj in remote_objects]) @@ -911,8 +966,9 @@ def get_supported_backends(cls): return ("serial", "multiprocessing", "dask") def __init__(self, function, trajectory=None, *args, **kwargs): - if (trajectory is not None) and (not isinstance( - trajectory, coordinates.base.ProtoReader)): + if (trajectory is not None) and ( + not isinstance(trajectory, coordinates.base.ProtoReader) + ): args = (trajectory,) + args trajectory = None @@ -940,7 +996,9 @@ def _get_aggregator(self): return ResultsGroup({"timeseries": ResultsGroup.flatten_sequence}) def _single_frame(self): - self.results.timeseries.append(self.function(*self.args, **self.kwargs)) + self.results.timeseries.append( + self.function(*self.args, **self.kwargs) + ) def _conclude(self): self.results.frames = self.frames @@ -1001,7 +1059,9 @@ def RotationMatrix(mobile, ref): class WrapperClass(AnalysisFromFunction): def __init__(self, trajectory=None, *args, **kwargs): - super(WrapperClass, self).__init__(function, trajectory, *args, **kwargs) + super(WrapperClass, self).__init__( + function, trajectory, *args, **kwargs + ) @classmethod def get_supported_backends(cls): @@ -1045,8 +1105,9 @@ def _filter_baseanalysis_kwargs(function, kwargs): n_base_defaults = len(base_argspec.defaults) base_kwargs = { name: val - for name, val in zip(base_argspec.args[-n_base_defaults:], - base_argspec.defaults) + for name, val in zip( + base_argspec.args[-n_base_defaults:], base_argspec.defaults + ) } try: diff --git a/package/MDAnalysis/analysis/bat.py b/package/MDAnalysis/analysis/bat.py index 9c1995f7ccc..9b08c7dbff4 100644 --- a/package/MDAnalysis/analysis/bat.py +++ b/package/MDAnalysis/analysis/bat.py @@ -215,20 +215,30 @@ def _find_torsions(root, atoms): torsionAdded = False for a1 in selected_atoms: # Find a0, which is a new atom connected to the selected atom - a0_list = _sort_atoms_by_mass(a for a in a1.bonded_atoms \ - if (a in atoms) and (a not in selected_atoms)) + a0_list = _sort_atoms_by_mass( + a + for a in a1.bonded_atoms + if (a in atoms) and (a not in selected_atoms) + ) for a0 in a0_list: # Find a2, which is connected to a1, is not a terminal atom, # and has been selected - a2_list = _sort_atoms_by_mass(a for a in a1.bonded_atoms \ - if (a!=a0) and len(a.bonded_atoms)>1 and \ - (a in atoms) and (a in selected_atoms)) + a2_list = _sort_atoms_by_mass( + a + for a in a1.bonded_atoms + if (a != a0) + and len(a.bonded_atoms) > 1 + and (a in atoms) + and (a in selected_atoms) + ) for a2 in a2_list: # Find a3, which is # connected to a2, has been selected, and is not a1 - a3_list = _sort_atoms_by_mass(a for a in a2.bonded_atoms \ - if (a!=a1) and \ - (a in atoms) and (a in selected_atoms)) + a3_list = _sort_atoms_by_mass( + a + for a in a2.bonded_atoms + if (a != a1) and (a in atoms) and (a in selected_atoms) + ) for a3 in a3_list: # Add the torsion to the list of torsions torsions.append(mda.AtomGroup([a0, a1, a2, a3])) @@ -239,11 +249,11 @@ def _find_torsions(root, atoms): break # out of the a3 loop break # out of the a2 loop if torsionAdded is False: - print('Selected atoms:') + print("Selected atoms:") print([a.index + 1 for a in selected_atoms]) - print('Torsions found:') + print("Torsions found:") print([list(t.indices + 1) for t in torsions]) - raise ValueError('Additional torsions not found.') + raise ValueError("Additional torsions not found.") return torsions @@ -254,13 +264,14 @@ class BAT(AnalysisBase): the group of atoms and all frame in the trajectory belonging to `ag`. .. versionchanged:: 2.8.0 - Enabled **parallel execution** with the ``multiprocessing`` and ``dask`` - backends; use the new method :meth:`get_supported_backends` to see all + Enabled **parallel execution** with the ``multiprocessing`` and ``dask`` + backends; use the new method :meth:`get_supported_backends` to see all supported backends. """ + _analysis_algorithm_is_parallelizable = True - + @classmethod def get_supported_backends(cls): return ( @@ -270,11 +281,10 @@ def get_supported_backends(cls): ) @due.dcite( - Doi("10.1002/jcc.26036"), - description="Bond-Angle-Torsions Coordinate Transformation", - path="MDAnalysis.analysis.bat.BAT", + Doi("10.1002/jcc.26036"), + description="Bond-Angle-Torsions Coordinate Transformation", + path="MDAnalysis.analysis.bat.BAT", ) - def __init__(self, ag, initial_atom=None, filename=None, **kwargs): r"""Parameters ---------- @@ -307,20 +317,21 @@ def __init__(self, ag, initial_atom=None, filename=None, **kwargs): self._ag = ag # Check that the ag contains bonds - if not hasattr(self._ag, 'bonds'): - raise AttributeError('AtomGroup has no attribute bonds') + if not hasattr(self._ag, "bonds"): + raise AttributeError("AtomGroup has no attribute bonds") if len(self._ag.fragments) > 1: - raise ValueError('AtomGroup has more than one molecule') + raise ValueError("AtomGroup has more than one molecule") # Determine the root # The initial atom must be a terminal atom - terminal_atoms = _sort_atoms_by_mass(\ - [a for a in self._ag.atoms if len(a.bonds)==1], reverse=True) - if (initial_atom is None): + terminal_atoms = _sort_atoms_by_mass( + [a for a in self._ag.atoms if len(a.bonds) == 1], reverse=True + ) + if initial_atom is None: # Select the heaviest root atoms from the heaviest terminal atom initial_atom = terminal_atoms[0] - elif (not initial_atom in terminal_atoms): - raise ValueError('Initial atom is not a terminal atom') + elif not initial_atom in terminal_atoms: + raise ValueError("Initial atom is not a terminal atom") # The next atom in the root is bonded to the initial atom # Since the initial atom is a terminal atom, there is only # one bonded atom @@ -330,16 +341,25 @@ def __init__(self, ag, initial_atom=None, filename=None, **kwargs): # If there are more than three atoms, # then the last atom cannot be a terminal atom. if self._ag.n_atoms != 3: - third_atom = _sort_atoms_by_mass(\ - [a for a in second_atom.bonded_atoms \ - if (a in self._ag) and (a!=initial_atom) \ - and (a not in terminal_atoms)], \ - reverse=True)[0] + third_atom = _sort_atoms_by_mass( + [ + a + for a in second_atom.bonded_atoms + if (a in self._ag) + and (a != initial_atom) + and (a not in terminal_atoms) + ], + reverse=True, + )[0] else: - third_atom = _sort_atoms_by_mass(\ - [a for a in second_atom.bonded_atoms \ - if (a in self._ag) and (a!=initial_atom)], \ - reverse=True)[0] + third_atom = _sort_atoms_by_mass( + [ + a + for a in second_atom.bonded_atoms + if (a in self._ag) and (a != initial_atom) + ], + reverse=True, + )[0] self._root = mda.AtomGroup([initial_atom, second_atom, third_atom]) # Construct a list of torsion angles @@ -347,18 +367,23 @@ def __init__(self, ag, initial_atom=None, filename=None, **kwargs): # Get indices of the root and torsion atoms # in a Cartesian positions array that matches the AtomGroup - self._root_XYZ_inds = [(self._ag.indices==a.index).nonzero()[0][0] \ - for a in self._root] - self._torsion_XYZ_inds = [[(self._ag.indices==a.index).nonzero()[0][0] \ - for a in t] for t in self._torsions] + self._root_XYZ_inds = [ + (self._ag.indices == a.index).nonzero()[0][0] for a in self._root + ] + self._torsion_XYZ_inds = [ + [(self._ag.indices == a.index).nonzero()[0][0] for a in t] + for t in self._torsions + ] # The primary torsion is the first torsion on the list # with the same central atoms prior_atoms = [sorted([a1, a2]) for (a0, a1, a2, a3) in self._torsions] - self._primary_torsion_indices = [prior_atoms.index(prior_atoms[n]) \ - for n in range(len(prior_atoms))] - self._unique_primary_torsion_indices = \ - list(set(self._primary_torsion_indices)) + self._primary_torsion_indices = [ + prior_atoms.index(prior_atoms[n]) for n in range(len(prior_atoms)) + ] + self._unique_primary_torsion_indices = list( + set(self._primary_torsion_indices) + ) self._ag1 = mda.AtomGroup([ag[0] for ag in self._torsions]) self._ag2 = mda.AtomGroup([ag[1] for ag in self._torsions]) @@ -370,7 +395,8 @@ def __init__(self, ag, initial_atom=None, filename=None, **kwargs): def _prepare(self): self.results.bat = np.zeros( - (self.n_frames, 3*self._ag.n_atoms), dtype=np.float64) + (self.n_frames, 3 * self._ag.n_atoms), dtype=np.float64 + ) def _single_frame(self): # Calculate coordinates based on the root atoms @@ -384,13 +410,24 @@ def _single_frame(self): v01 = p1 - p0 v21 = p1 - p2 # Internal coordinates - r01 = np.sqrt(np.einsum('i,i->',v01,v01)) + r01 = np.sqrt(np.einsum("i,i->", v01, v01)) # Distance between first two root atoms - r12 = np.sqrt(np.einsum('i,i->',v21,v21)) + r12 = np.sqrt(np.einsum("i,i->", v21, v21)) # Distance between second two root atoms # Angle between root atoms - a012 = np.arccos(max(-1.,min(1.,np.einsum('i,i->',v01,v21)/\ - np.sqrt(np.einsum('i,i->',v01,v01)*np.einsum('i,i->',v21,v21))))) + a012 = np.arccos( + max( + -1.0, + min( + 1.0, + np.einsum("i,i->", v01, v21) + / np.sqrt( + np.einsum("i,i->", v01, v01) + * np.einsum("i,i->", v21, v21) + ), + ), + ) + ) # External coordinates e = v01 / r01 phi = np.arctan2(e[1], e[0]) # Polar angle @@ -400,35 +437,41 @@ def _single_frame(self): sp = np.sin(phi) ct = np.cos(theta) st = np.sin(theta) - Rz = np.array([[cp * ct, ct * sp, -st], [-sp, cp, 0], - [cp * st, sp * st, ct]]) + Rz = np.array( + [[cp * ct, ct * sp, -st], [-sp, cp, 0], [cp * st, sp * st, ct]] + ) pos2 = Rz.dot(p2 - p1) # Angle about the rotation axis omega = np.arctan2(pos2[1], pos2[0]) root_based = np.concatenate((p0, [phi, theta, omega, r01, r12, a012])) # Calculate internal coordinates from the torsion list - bonds = calc_bonds(self._ag1.positions, - self._ag2.positions, - box=self._ag1.dimensions) - angles = calc_angles(self._ag1.positions, - self._ag2.positions, - self._ag3.positions, - box=self._ag1.dimensions) - torsions = calc_dihedrals(self._ag1.positions, - self._ag2.positions, - self._ag3.positions, - self._ag4.positions, - box=self._ag1.dimensions) + bonds = calc_bonds( + self._ag1.positions, self._ag2.positions, box=self._ag1.dimensions + ) + angles = calc_angles( + self._ag1.positions, + self._ag2.positions, + self._ag3.positions, + box=self._ag1.dimensions, + ) + torsions = calc_dihedrals( + self._ag1.positions, + self._ag2.positions, + self._ag3.positions, + self._ag4.positions, + box=self._ag1.dimensions, + ) # When appropriate, calculate improper torsions shift = torsions[self._primary_torsion_indices] - shift[self._unique_primary_torsion_indices] = 0. + shift[self._unique_primary_torsion_indices] = 0.0 torsions -= shift # Wrap torsions to between -np.pi and np.pi torsions = ((torsions + np.pi) % (2 * np.pi)) - np.pi self.results.bat[self._frame_index, :] = np.concatenate( - (root_based, bonds, angles, torsions)) + (root_based, bonds, angles, torsions) + ) def load(self, filename, start=None, stop=None, step=None): """Loads the bat trajectory from a file in numpy binary format @@ -455,16 +498,20 @@ def load(self, filename, start=None, stop=None, step=None): self.results.bat = np.load(filename) # Check array dimensions - if self.results.bat.shape != (self.n_frames, 3*self._ag.n_atoms): - errmsg = ('Dimensions of array in loaded file, ' - f'({self.results.bat.shape[0]},' - f'{self.results.bat.shape[1]}), differ from required ' - f'dimensions of ({self.n_frames, 3*self._ag.n_atoms})') + if self.results.bat.shape != (self.n_frames, 3 * self._ag.n_atoms): + errmsg = ( + "Dimensions of array in loaded file, " + f"({self.results.bat.shape[0]}," + f"{self.results.bat.shape[1]}), differ from required " + f"dimensions of ({self.n_frames, 3*self._ag.n_atoms})" + ) raise ValueError(errmsg) # Check position of initial atom if (self.results.bat[0, :3] != self._root[0].position).any(): - raise ValueError('Position of initial atom in file ' + \ - 'inconsistent with current trajectory in starting frame.') + raise ValueError( + "Position of initial atom in file " + + "inconsistent with current trajectory in starting frame." + ) return self def save(self, filename): @@ -501,21 +548,21 @@ def Cartesian(self, bat_frame): origin = bat_frame[:3] (phi, theta, omega) = bat_frame[3:6] (r01, r12, a012) = bat_frame[6:9] - n_torsions = (self._ag.n_atoms - 3) - bonds = bat_frame[9:n_torsions + 9] - angles = bat_frame[n_torsions + 9:2 * n_torsions + 9] - torsions = copy.deepcopy(bat_frame[2 * n_torsions + 9:]) + n_torsions = self._ag.n_atoms - 3 + bonds = bat_frame[9 : n_torsions + 9] + angles = bat_frame[n_torsions + 9 : 2 * n_torsions + 9] + torsions = copy.deepcopy(bat_frame[2 * n_torsions + 9 :]) # When appropriate, convert improper to proper torsions shift = torsions[self._primary_torsion_indices] - shift[self._unique_primary_torsion_indices] = 0. + shift[self._unique_primary_torsion_indices] = 0.0 torsions += shift # Wrap torsions to between -np.pi and np.pi torsions = ((torsions + np.pi) % (2 * np.pi)) - np.pi # Set initial root atom positions based on internal coordinates - p0 = np.array([0., 0., 0.]) - p1 = np.array([0., 0., r01]) - p2 = np.array([r12 * np.sin(a012), 0., r01 - r12 * np.cos(a012)]) + p0 = np.array([0.0, 0.0, 0.0]) + p1 = np.array([0.0, 0.0, r01]) + p2 = np.array([r12 * np.sin(a012), 0.0, r01 - r12 * np.cos(a012)]) # Rotate the third atom by the appropriate value co = np.cos(omega) @@ -529,8 +576,9 @@ def Cartesian(self, bat_frame): ct = np.cos(theta) st = np.sin(theta) # $R_Z(\phi) R_Y(\theta)$ - Re = np.array([[cp * ct, -sp, cp * st], [ct * sp, cp, sp * st], - [-st, 0, ct]]) + Re = np.array( + [[cp * ct, -sp, cp * st], [ct * sp, cp, sp * st], [-st, 0, ct]] + ) p1 = Re.dot(p1) p2 = Re.dot(p2) # Translate the first three atoms by the origin @@ -544,8 +592,9 @@ def Cartesian(self, bat_frame): XYZ[self._root_XYZ_inds[2]] = p2 # Place the remaining atoms - for ((a0,a1,a2,a3), r01, angle, torsion) \ - in zip(self._torsion_XYZ_inds, bonds, angles, torsions): + for (a0, a1, a2, a3), r01, angle, torsion in zip( + self._torsion_XYZ_inds, bonds, angles, torsions + ): p1 = XYZ[a1] p3 = XYZ[a3] @@ -557,19 +606,20 @@ def Cartesian(self, bat_frame): cs_tor = np.cos(torsion) v21 = p1 - p2 - v21 /= np.sqrt(np.einsum('i,i->',v21,v21)) + v21 /= np.sqrt(np.einsum("i,i->", v21, v21)) v32 = p2 - p3 - v32 /= np.sqrt(np.einsum('i,i->',v32,v32)) + v32 /= np.sqrt(np.einsum("i,i->", v32, v32)) vp = np.cross(v32, v21) - cs = np.einsum('i,i->',v21,v32) + cs = np.einsum("i,i->", v21, v32) sn = max(np.sqrt(1.0 - cs * cs), 0.0000000001) vp = vp / sn vu = np.cross(vp, v21) - XYZ[a0] = p1 + \ - r01*(vu*sn_ang*cs_tor + vp*sn_ang*sn_tor - v21*cs_ang) + XYZ[a0] = p1 + r01 * ( + vu * sn_ang * cs_tor + vp * sn_ang * sn_tor - v21 * cs_ang + ) return XYZ @property @@ -578,4 +628,4 @@ def atoms(self): return self._ag def _get_aggregator(self): - return ResultsGroup(lookup={'bat': ResultsGroup.ndarray_vstack}) + return ResultsGroup(lookup={"bat": ResultsGroup.ndarray_vstack}) diff --git a/package/MDAnalysis/analysis/contacts.py b/package/MDAnalysis/analysis/contacts.py index f29fd4961e8..5d63471ae7d 100644 --- a/package/MDAnalysis/analysis/contacts.py +++ b/package/MDAnalysis/analysis/contacts.py @@ -260,7 +260,7 @@ def soft_cut_q(r, r0, beta=5.0, lambda_constant=1.8): """ r = np.asarray(r) r0 = np.asarray(r0) - result = 1/(1 + np.exp(beta*(r - lambda_constant * r0))) + result = 1 / (1 + np.exp(beta * (r - lambda_constant * r0))) return result.sum() / len(r0) @@ -392,8 +392,17 @@ def get_supported_backends(cls): "dask", ) - def __init__(self, u, select, refgroup, method="hard_cut", radius=4.5, - pbc=True, kwargs=None, **basekwargs): + def __init__( + self, + u, + select, + refgroup, + method="hard_cut", + radius=4.5, + pbc=True, + kwargs=None, + **basekwargs, + ): """ Parameters ---------- @@ -437,12 +446,14 @@ def __init__(self, u, select, refgroup, method="hard_cut", radius=4.5, self.fraction_kwargs = kwargs if kwargs is not None else {} - if method == 'hard_cut': + if method == "hard_cut": self.fraction_contacts = hard_cut_q - elif method == 'soft_cut': + elif method == "soft_cut": self.fraction_contacts = soft_cut_q - elif method == 'radius_cut': - self.fraction_contacts = functools.partial(radius_cut_q, radius=radius) + elif method == "radius_cut": + self.fraction_contacts = functools.partial( + radius_cut_q, radius=radius + ) else: if not callable(method): raise ValueError("method has to be callable") @@ -453,7 +464,7 @@ def __init__(self, u, select, refgroup, method="hard_cut", radius=4.5, self.grA, self.grB = (self._get_atomgroup(u, sel) for sel in select) self.pbc = pbc - + # contacts formed in reference self.r0 = [] self.initial_contacts = [] @@ -463,23 +474,37 @@ def __init__(self, u, select, refgroup, method="hard_cut", radius=4.5, if isinstance(refgroup[0], AtomGroup): refA, refB = refgroup - self.r0.append(distance_array(refA.positions, refB.positions, - box=self._get_box(refA.universe))) + self.r0.append( + distance_array( + refA.positions, + refB.positions, + box=self._get_box(refA.universe), + ) + ) self.initial_contacts.append(contact_matrix(self.r0[-1], radius)) else: for refA, refB in refgroup: - self.r0.append(distance_array(refA.positions, refB.positions, - box=self._get_box(refA.universe))) - self.initial_contacts.append(contact_matrix(self.r0[-1], radius)) + self.r0.append( + distance_array( + refA.positions, + refB.positions, + box=self._get_box(refA.universe), + ) + ) + self.initial_contacts.append( + contact_matrix(self.r0[-1], radius) + ) self.n_initial_contacts = self.initial_contacts[0].sum() @staticmethod def _get_atomgroup(u, sel): - select_error_message = ("selection must be either string or a " - "static AtomGroup. Updating AtomGroups " - "are not supported.") + select_error_message = ( + "selection must be either string or a " + "static AtomGroup. Updating AtomGroups " + "are not supported." + ) if isinstance(sel, str): return u.select_atoms(sel) elif isinstance(sel, AtomGroup): @@ -513,17 +538,19 @@ def _get_box_func(ts, pbc): return ts.dimensions if pbc else None def _prepare(self): - self.results.timeseries = np.empty((self.n_frames, len(self.r0)+1)) + self.results.timeseries = np.empty((self.n_frames, len(self.r0) + 1)) def _single_frame(self): self.results.timeseries[self._frame_index][0] = self._ts.frame - + # compute distance array for a frame - d = distance_array(self.grA.positions, self.grB.positions, - box=self._get_box(self._ts)) - - for i, (initial_contacts, r0) in enumerate(zip(self.initial_contacts, - self.r0), 1): + d = distance_array( + self.grA.positions, self.grB.positions, box=self._get_box(self._ts) + ) + + for i, (initial_contacts, r0) in enumerate( + zip(self.initial_contacts, self.r0), 1 + ): # select only the contacts that were formed in the reference state r = d[initial_contacts] r0 = r0[initial_contacts] @@ -532,14 +559,17 @@ def _single_frame(self): @property def timeseries(self): - wmsg = ("The `timeseries` attribute was deprecated in MDAnalysis " - "2.0.0 and will be removed in MDAnalysis 3.0.0. Please use " - "`results.timeseries` instead") + wmsg = ( + "The `timeseries` attribute was deprecated in MDAnalysis " + "2.0.0 and will be removed in MDAnalysis 3.0.0. Please use " + "`results.timeseries` instead" + ) warnings.warn(wmsg, DeprecationWarning) return self.results.timeseries def _get_aggregator(self): - return ResultsGroup(lookup={'timeseries': ResultsGroup.ndarray_vstack}) + return ResultsGroup(lookup={"timeseries": ResultsGroup.ndarray_vstack}) + def _new_selections(u_orig, selections, frame): """create stand alone AGs from selections at frame""" @@ -548,7 +578,7 @@ def _new_selections(u_orig, selections, frame): return [u.select_atoms(s) for s in selections] -def q1q2(u, select='all', radius=4.5): +def q1q2(u, select="all", radius=4.5): """Perform a q1-q2 analysis. Compares native contacts between the starting structure and final structure @@ -568,7 +598,7 @@ def q1q2(u, select='all', radius=4.5): contacts : :class:`Contacts` Contact Analysis that is set up for a q1-q2 analysis - + .. versionchanged:: 1.0.0 Changed `selection` keyword to `select` Support for setting ``start``, ``stop``, and ``step`` has been removed. @@ -577,6 +607,10 @@ def q1q2(u, select='all', radius=4.5): selection = (select, select) first_frame_refs = _new_selections(u, selection, 0) last_frame_refs = _new_selections(u, selection, -1) - return Contacts(u, selection, - (first_frame_refs, last_frame_refs), - radius=radius, method='radius_cut') + return Contacts( + u, + selection, + (first_frame_refs, last_frame_refs), + radius=radius, + method="radius_cut", + ) diff --git a/package/MDAnalysis/analysis/data/filenames.py b/package/MDAnalysis/analysis/data/filenames.py index a747450b86d..68ab3d96551 100644 --- a/package/MDAnalysis/analysis/data/filenames.py +++ b/package/MDAnalysis/analysis/data/filenames.py @@ -103,16 +103,17 @@ __all__ = [ - "Rama_ref", "Janin_ref", + "Rama_ref", + "Janin_ref", # reference plots for Ramachandran and Janin classes ] from importlib import resources -_base_ref = resources.files('MDAnalysis.analysis.data') -Rama_ref = (_base_ref / 'rama_ref_data.npy').as_posix() -Janin_ref = (_base_ref / 'janin_ref_data.npy').as_posix() +_base_ref = resources.files("MDAnalysis.analysis.data") +Rama_ref = (_base_ref / "rama_ref_data.npy").as_posix() +Janin_ref = (_base_ref / "janin_ref_data.npy").as_posix() # This should be the last line: clean up namespace del resources diff --git a/package/MDAnalysis/analysis/density.py b/package/MDAnalysis/analysis/density.py index eb2522531f2..61c4e679899 100644 --- a/package/MDAnalysis/analysis/density.py +++ b/package/MDAnalysis/analysis/density.py @@ -161,8 +161,12 @@ import MDAnalysis from MDAnalysis.core import groups -from MDAnalysis.lib.util import (fixedwidth_bins, iterable, asiterable, - deprecate,) +from MDAnalysis.lib.util import ( + fixedwidth_bins, + iterable, + asiterable, + deprecate, +) from MDAnalysis.lib import NeighborSearch as NS from MDAnalysis import NoDataError, MissingDataWarning from .. import units @@ -400,16 +404,24 @@ class DensityAnalysis(AnalysisBase): for parallel execution on :mod:`multiprocessing` and :mod:`dask` backends. """ + _analysis_algorithm_is_parallelizable = True @classmethod def get_supported_backends(cls): - return ('serial', 'multiprocessing', 'dask') - - def __init__(self, atomgroup, delta=1.0, - metadata=None, padding=2.0, - gridcenter=None, - xdim=None, ydim=None, zdim=None): + return ("serial", "multiprocessing", "dask") + + def __init__( + self, + atomgroup, + delta=1.0, + metadata=None, + padding=2.0, + gridcenter=None, + xdim=None, + ydim=None, + zdim=None, + ): u = atomgroup.universe super(DensityAnalysis, self).__init__(u.trajectory) self._atomgroup = atomgroup @@ -423,16 +435,19 @@ def __init__(self, atomgroup, delta=1.0, # The grid with its dimensions has to be set up in __init__ # so that parallel analysis works correctly: each process # needs to have a results._grid of the same size and the - # same self._bins and self._arange (so this cannot happen - # in _prepare(), which is executed in parallel on different - # parts of the trajectory). + # same self._bins and self._arange (so this cannot happen + # in _prepare(), which is executed in parallel on different + # parts of the trajectory). coord = self._atomgroup.positions - if (self._gridcenter is not None or - any([self._xdim, self._ydim, self._zdim])): + if self._gridcenter is not None or any( + [self._xdim, self._ydim, self._zdim] + ): # Issue 2372: padding is ignored, defaults to 2.0 therefore warn if self._padding > 0: - msg = (f"Box padding (currently set at {self._padding}) " - f"is not used in user defined grids.") + msg = ( + f"Box padding (currently set at {self._padding}) " + f"is not used in user defined grids." + ) warnings.warn(msg) logger.warning(msg) # Generate a copy of smin/smax from coords to later check if the @@ -441,18 +456,25 @@ def __init__(self, atomgroup, delta=1.0, smin = np.min(coord, axis=0) smax = np.max(coord, axis=0) except ValueError as err: - msg = ("No atoms in AtomGroup at input time frame. " - "This may be intended; please ensure that " - "your grid selection covers the atomic " - "positions you wish to capture.") + msg = ( + "No atoms in AtomGroup at input time frame. " + "This may be intended; please ensure that " + "your grid selection covers the atomic " + "positions you wish to capture." + ) warnings.warn(msg) logger.warning(msg) - smin = self._gridcenter #assigns limits to be later - - smax = self._gridcenter #overwritten by _set_user_grid + smin = self._gridcenter # assigns limits to be later - + smax = self._gridcenter # overwritten by _set_user_grid # Overwrite smin/smax with user defined values - smin, smax = self._set_user_grid(self._gridcenter, self._xdim, - self._ydim, self._zdim, smin, - smax) + smin, smax = self._set_user_grid( + self._gridcenter, + self._xdim, + self._ydim, + self._zdim, + smin, + smax, + ) else: # Make the box bigger to avoid as much as possible 'outlier'. This # is important if the sites are defined at a high density: in this @@ -465,18 +487,21 @@ def __init__(self, atomgroup, delta=1.0, smin = np.min(coord, axis=0) - self._padding smax = np.max(coord, axis=0) + self._padding except ValueError as err: - errmsg = ("No atoms in AtomGroup at input time frame. " - "Grid for density could not be automatically" - " generated. If this is expected, a user" - " defined grid will need to be " - "provided instead.") + errmsg = ( + "No atoms in AtomGroup at input time frame. " + "Grid for density could not be automatically" + " generated. If this is expected, a user" + " defined grid will need to be " + "provided instead." + ) raise ValueError(errmsg) from err BINS = fixedwidth_bins(self._delta, smin, smax) - arange = np.transpose(np.vstack((BINS['min'], BINS['max']))) - bins = BINS['Nbins'] + arange = np.transpose(np.vstack((BINS["min"], BINS["max"]))) + bins = BINS["Nbins"] # create empty grid with the right dimensions (and get the edges) - grid, edges = np.histogramdd(np.zeros((1, 3)), bins=bins, - range=arange, density=False) + grid, edges = np.histogramdd( + np.zeros((1, 3)), bins=bins, range=arange, density=False + ) grid *= 0.0 self.results._grid = grid self._edges = edges @@ -484,30 +509,36 @@ def __init__(self, atomgroup, delta=1.0, self._bins = bins def _single_frame(self): - h, _ = np.histogramdd(self._atomgroup.positions, - bins=self._bins, range=self._arange, - density=False) + h, _ = np.histogramdd( + self._atomgroup.positions, + bins=self._bins, + range=self._arange, + density=False, + ) self.results._grid += h def _conclude(self): # average: self.results._grid /= float(self.n_frames) - density = Density(grid=self.results._grid, edges=self._edges, - units={'length': "Angstrom"}, - parameters={'isDensity': False}) + density = Density( + grid=self.results._grid, + edges=self._edges, + units={"length": "Angstrom"}, + parameters={"isDensity": False}, + ) density.make_density() self.results.density = density def _get_aggregator(self): - return ResultsGroup(lookup={ - '_grid': ResultsGroup.ndarray_sum} - ) + return ResultsGroup(lookup={"_grid": ResultsGroup.ndarray_sum}) @property def density(self): - wmsg = ("The `density` attribute was deprecated in MDAnalysis 2.0.0 " - "and will be removed in MDAnalysis 3.0.0. Please use " - "`results.density` instead") + wmsg = ( + "The `density` attribute was deprecated in MDAnalysis 2.0.0 " + "and will be removed in MDAnalysis 3.0.0. Please use " + "`results.density` instead" + ) warnings.warn(wmsg, DeprecationWarning) return self.results.density @@ -544,10 +575,12 @@ def _set_user_grid(gridcenter, xdim, ydim, zdim, smin, smax): """ # Check user inputs if any(x is None for x in [gridcenter, xdim, ydim, zdim]): - errmsg = ("Gridcenter or grid dimensions are not provided") + errmsg = "Gridcenter or grid dimensions are not provided" raise ValueError(errmsg) try: - gridcenter = np.asarray(gridcenter, dtype=np.float32).reshape(3,) + gridcenter = np.asarray(gridcenter, dtype=np.float32).reshape( + 3, + ) except ValueError as err: raise ValueError("Gridcenter must be a 3D coordinate") from err try: @@ -557,16 +590,17 @@ def _set_user_grid(gridcenter, xdim, ydim, zdim, smin, smax): if any(np.isnan(gridcenter)) or any(np.isnan(xyzdim)): raise ValueError("Gridcenter or grid dimensions have NaN element") - # Set min/max by shifting by half the edge length of each dimension - umin = gridcenter - xyzdim/2 - umax = gridcenter + xyzdim/2 + umin = gridcenter - xyzdim / 2 + umax = gridcenter + xyzdim / 2 # Here we test if coords of selection fall outside of the defined grid # if this happens, we warn users they may want to resize their grids if any(smin < umin) or any(smax > umax): - msg = ("Atom selection does not fit grid --- " - "you may want to define a larger box") + msg = ( + "Atom selection does not fit grid --- " + "you may want to define a larger box" + ) warnings.warn(msg) logger.warning(msg) return umin, umax @@ -725,22 +759,25 @@ class Density(Grid): """ def __init__(self, *args, **kwargs): - length_unit = 'Angstrom' - - parameters = kwargs.pop('parameters', {}) - if (len(args) > 0 and isinstance(args[0], str) or - isinstance(kwargs.get('grid', None), str)): + length_unit = "Angstrom" + + parameters = kwargs.pop("parameters", {}) + if ( + len(args) > 0 + and isinstance(args[0], str) + or isinstance(kwargs.get("grid", None), str) + ): # try to be smart: when reading from a file then it is likely that # this is a density - parameters.setdefault('isDensity', True) + parameters.setdefault("isDensity", True) else: - parameters.setdefault('isDensity', False) - units = kwargs.pop('units', {}) - units.setdefault('length', length_unit) - if parameters['isDensity']: - units.setdefault('density', length_unit) + parameters.setdefault("isDensity", False) + units = kwargs.pop("units", {}) + units.setdefault("length", length_unit) + if parameters["isDensity"]: + units.setdefault("density", length_unit) else: - units.setdefault('density', None) + units.setdefault("density", None) super(Density, self).__init__(*args, **kwargs) @@ -767,25 +804,31 @@ def _check_set_unit(self, u): # all this unit crap should be a class... try: for unit_type, value in u.items(): - if value is None: # check here, too iffy to use dictionary[None]=None + if ( + value is None + ): # check here, too iffy to use dictionary[None]=None self.units[unit_type] = None continue try: units.conversion_factor[unit_type][value] self.units[unit_type] = value except KeyError: - errmsg = (f"Unit {value} of type {unit_type} is not " - f"recognized.") + errmsg = ( + f"Unit {value} of type {unit_type} is not " + f"recognized." + ) raise ValueError(errmsg) from None except AttributeError: - errmsg = '"unit" must be a dictionary with keys "length" and "density.' + errmsg = ( + '"unit" must be a dictionary with keys "length" and "density.' + ) logger.fatal(errmsg) raise ValueError(errmsg) from None # need at least length and density (can be None) - if 'length' not in self.units: + if "length" not in self.units: raise ValueError('"unit" must contain a unit for "length".') - if 'density' not in self.units: - self.units['density'] = None + if "density" not in self.units: + self.units["density"] = None def make_density(self): """Convert the grid (a histogram, counts in a cell) to a density (counts/volume). @@ -800,7 +843,7 @@ def make_density(self): # Make it a density by dividing by the volume of each grid cell # (from numpy.histogramdd, which is for general n-D grids) - if self.parameters['isDensity']: + if self.parameters["isDensity"]: msg = "Running make_density() makes no sense: Grid is already a density. Nothing done." logger.warning(msg) warnings.warn(msg) @@ -812,11 +855,11 @@ def make_density(self): shape = np.ones(D, int) shape[i] = len(dedges[i]) self.grid /= dedges[i].reshape(shape) - self.parameters['isDensity'] = True + self.parameters["isDensity"] = True # see units.densityUnit_factor for units - self.units['density'] = self.units['length'] + "^{-3}" + self.units["density"] = self.units["length"] + "^{-3}" - def convert_length(self, unit='Angstrom'): + def convert_length(self, unit="Angstrom"): """Convert Grid object to the new `unit`. Parameters @@ -833,14 +876,16 @@ def convert_length(self, unit='Angstrom'): histogram and a length unit and use :meth:`make_density`. """ - if unit == self.units['length']: + if unit == self.units["length"]: return - cvnfact = units.get_conversion_factor('length', self.units['length'], unit) + cvnfact = units.get_conversion_factor( + "length", self.units["length"], unit + ) self.edges = [x * cvnfact for x in self.edges] - self.units['length'] = unit + self.units["length"] = unit self._update() # needed to recalculate midpoints and origin - def convert_density(self, unit='Angstrom^{-3}'): + def convert_density(self, unit="Angstrom^{-3}"): """Convert the density to the physical units given by `unit`. Parameters @@ -879,24 +924,33 @@ def convert_density(self, unit='Angstrom^{-3}'): and floating point artifacts for multiple conversions. """ - if not self.parameters['isDensity']: - errmsg = 'The grid is not a density so convert_density() makes no sense.' + if not self.parameters["isDensity"]: + errmsg = "The grid is not a density so convert_density() makes no sense." logger.fatal(errmsg) raise RuntimeError(errmsg) - if unit == self.units['density']: + if unit == self.units["density"]: return try: - self.grid *= units.get_conversion_factor('density', - self.units['density'], unit) + self.grid *= units.get_conversion_factor( + "density", self.units["density"], unit + ) except KeyError: - errmsg = (f"The name of the unit ({unit} supplied) must be one " - f"of:\n{units.conversion_factor['density'].keys()}") + errmsg = ( + f"The name of the unit ({unit} supplied) must be one " + f"of:\n{units.conversion_factor['density'].keys()}" + ) raise ValueError(errmsg) from None - self.units['density'] = unit + self.units["density"] = unit def __repr__(self): - if self.parameters['isDensity']: - grid_type = 'density' + if self.parameters["isDensity"]: + grid_type = "density" else: - grid_type = 'histogram' - return '' + grid_type = "histogram" + return ( + "" + ) diff --git a/package/MDAnalysis/analysis/dielectric.py b/package/MDAnalysis/analysis/dielectric.py index 4f14eb88074..28a22b5116b 100644 --- a/package/MDAnalysis/analysis/dielectric.py +++ b/package/MDAnalysis/analysis/dielectric.py @@ -37,10 +37,12 @@ from MDAnalysis.due import due, Doi from MDAnalysis.exceptions import NoDataError -due.cite(Doi("10.1080/00268978300102721"), - description="Dielectric analysis", - path="MDAnalysis.analysis.dielectric", - cite_module=True) +due.cite( + Doi("10.1080/00268978300102721"), + description="Dielectric analysis", + path="MDAnalysis.analysis.dielectric", + cite_module=True, +) del Doi @@ -121,9 +123,11 @@ class DielectricConstant(AnalysisBase): .. versionadded:: 2.1.0 """ + def __init__(self, atomgroup, temperature=300, make_whole=True, **kwargs): - super(DielectricConstant, self).__init__(atomgroup.universe.trajectory, - **kwargs) + super(DielectricConstant, self).__init__( + atomgroup.universe.trajectory, **kwargs + ) self.atomgroup = atomgroup self.temperature = temperature self.make_whole = make_whole @@ -132,11 +136,14 @@ def _prepare(self): if not hasattr(self.atomgroup, "charges"): raise NoDataError("No charges defined given atomgroup.") - if not np.allclose(self.atomgroup.total_charge(compound='fragments'), - 0.0, atol=1E-5): - raise NotImplementedError("Analysis for non-neutral systems or" - " systems with free charges are not" - " available.") + if not np.allclose( + self.atomgroup.total_charge(compound="fragments"), 0.0, atol=1e-5 + ): + raise NotImplementedError( + "Analysis for non-neutral systems or" + " systems with free charges are not" + " available." + ) self.volume = 0 self.results.M = np.zeros(3) @@ -163,8 +170,11 @@ def _conclude(self): self.results.fluct = self.results.M2 - self.results.M * self.results.M self.results.eps = self.results.fluct / ( - convert(constants["Boltzmann_constant"], "kJ/mol", "eV") * - self.temperature * self.volume * constants["electric_constant"]) + convert(constants["Boltzmann_constant"], "kJ/mol", "eV") + * self.temperature + * self.volume + * constants["electric_constant"] + ) self.results.eps_mean = self.results.eps.mean() diff --git a/package/MDAnalysis/analysis/dihedrals.py b/package/MDAnalysis/analysis/dihedrals.py index c6a5585f7a0..0c5ecb33cdf 100644 --- a/package/MDAnalysis/analysis/dihedrals.py +++ b/package/MDAnalysis/analysis/dihedrals.py @@ -271,12 +271,16 @@ class Dihedral(AnalysisBase): introduced :meth:`get_supported_backends` allowing for parallel execution on ``multiprocessing`` and ``dask`` backends. """ + _analysis_algorithm_is_parallelizable = True @classmethod def get_supported_backends(cls): - return ('serial', 'multiprocessing', 'dask',) - + return ( + "serial", + "multiprocessing", + "dask", + ) def __init__(self, atomgroups, **kwargs): """Parameters @@ -292,7 +296,8 @@ def __init__(self, atomgroups, **kwargs): """ super(Dihedral, self).__init__( - atomgroups[0].universe.trajectory, **kwargs) + atomgroups[0].universe.trajectory, **kwargs + ) self.atomgroups = atomgroups if any([len(ag) != 4 for ag in atomgroups]): @@ -307,12 +312,16 @@ def _prepare(self): self.results.angles = [] def _get_aggregator(self): - return ResultsGroup(lookup={'angles': ResultsGroup.ndarray_vstack}) + return ResultsGroup(lookup={"angles": ResultsGroup.ndarray_vstack}) def _single_frame(self): - angle = calc_dihedrals(self.ag1.positions, self.ag2.positions, - self.ag3.positions, self.ag4.positions, - box=self.ag1.dimensions) + angle = calc_dihedrals( + self.ag1.positions, + self.ag2.positions, + self.ag3.positions, + self.ag4.positions, + box=self.ag1.dimensions, + ) self.results.angles.append(angle) def _conclude(self): @@ -320,9 +329,11 @@ def _conclude(self): @property def angles(self): - wmsg = ("The `angle` attribute was deprecated in MDAnalysis 2.0.0 " - "and will be removed in MDAnalysis 3.0.0. Please use " - "`results.angles` instead") + wmsg = ( + "The `angle` attribute was deprecated in MDAnalysis 2.0.0 " + "and will be removed in MDAnalysis 3.0.0. Please use " + "`results.angles` instead" + ) warnings.warn(wmsg, DeprecationWarning) return self.results.angles @@ -394,16 +405,29 @@ class Ramachandran(AnalysisBase): introduced :meth:`get_supported_backends` allowing for parallel execution on ``multiprocessing`` and ``dask`` backends. """ + _analysis_algorithm_is_parallelizable = True @classmethod def get_supported_backends(cls): - return ('serial', 'multiprocessing', 'dask',) - - def __init__(self, atomgroup, c_name='C', n_name='N', ca_name='CA', - check_protein=True, **kwargs): + return ( + "serial", + "multiprocessing", + "dask", + ) + + def __init__( + self, + atomgroup, + c_name="C", + n_name="N", + ca_name="CA", + check_protein=True, + **kwargs, + ): super(Ramachandran, self).__init__( - atomgroup.universe.trajectory, **kwargs) + atomgroup.universe.trajectory, **kwargs + ) self.atomgroup = atomgroup residues = self.atomgroup.residues @@ -411,12 +435,16 @@ def __init__(self, atomgroup, c_name='C', n_name='N', ca_name='CA', protein = self.atomgroup.universe.select_atoms("protein").residues if not residues.issubset(protein): - raise ValueError("Found atoms outside of protein. Only atoms " - "inside of a 'protein' selection can be used to " - "calculate dihedrals.") + raise ValueError( + "Found atoms outside of protein. Only atoms " + "inside of a 'protein' selection can be used to " + "calculate dihedrals." + ) elif not residues.isdisjoint(protein[[0, -1]]): - warnings.warn("Cannot determine phi and psi angles for the first " - "or last residues") + warnings.warn( + "Cannot determine phi and psi angles for the first " + "or last residues" + ) residues = residues.difference(protein[[0, -1]]) prev = residues._get_prev_residues_by_resid() @@ -425,17 +453,20 @@ def __init__(self, atomgroup, c_name='C', n_name='N', ca_name='CA', keep = keep & np.array([r is not None for r in nxt]) if not np.all(keep): - warnings.warn("Some residues in selection do not have " - "phi or psi selections") + warnings.warn( + "Some residues in selection do not have " + "phi or psi selections" + ) prev = sum(prev[keep]) nxt = sum(nxt[keep]) residues = residues[keep] # find n, c, ca - keep_prev = [sum(r.atoms.names==c_name)==1 for r in prev] + keep_prev = [sum(r.atoms.names == c_name) == 1 for r in prev] rnames = [n_name, c_name, ca_name] - keep_res = [all(sum(r.atoms.names == n) == 1 for n in rnames) - for r in residues] + keep_res = [ + all(sum(r.atoms.names == n) == 1 for n in rnames) for r in residues + ] keep_next = [sum(r.atoms.names == n_name) == 1 for r in nxt] # alright we'll keep these @@ -451,20 +482,27 @@ def __init__(self, atomgroup, c_name='C', n_name='N', ca_name='CA', self.ag4 = res.atoms[rnames == c_name] self.ag5 = nxt.atoms[nxt.atoms.names == n_name] - def _prepare(self): self.results.angles = [] def _get_aggregator(self): - return ResultsGroup(lookup={'angles': ResultsGroup.ndarray_vstack}) + return ResultsGroup(lookup={"angles": ResultsGroup.ndarray_vstack}) def _single_frame(self): - phi_angles = calc_dihedrals(self.ag1.positions, self.ag2.positions, - self.ag3.positions, self.ag4.positions, - box=self.ag1.dimensions) - psi_angles = calc_dihedrals(self.ag2.positions, self.ag3.positions, - self.ag4.positions, self.ag5.positions, - box=self.ag1.dimensions) + phi_angles = calc_dihedrals( + self.ag1.positions, + self.ag2.positions, + self.ag3.positions, + self.ag4.positions, + box=self.ag1.dimensions, + ) + psi_angles = calc_dihedrals( + self.ag2.positions, + self.ag3.positions, + self.ag4.positions, + self.ag5.positions, + box=self.ag1.dimensions, + ) phi_psi = [(phi, psi) for phi, psi in zip(phi_angles, psi_angles)] self.results.angles.append(phi_psi) @@ -499,31 +537,40 @@ def plot(self, ax=None, ref=False, **kwargs): if ax is None: ax = plt.gca() ax.axis([-180, 180, -180, 180]) - ax.axhline(0, color='k', lw=1) - ax.axvline(0, color='k', lw=1) - ax.set(xticks=range(-180, 181, 60), yticks=range(-180, 181, 60), - xlabel=r"$\phi$", ylabel=r"$\psi$") + ax.axhline(0, color="k", lw=1) + ax.axvline(0, color="k", lw=1) + ax.set( + xticks=range(-180, 181, 60), + yticks=range(-180, 181, 60), + xlabel=r"$\phi$", + ylabel=r"$\psi$", + ) degree_formatter = plt.matplotlib.ticker.StrMethodFormatter( - r"{x:g}$\degree$") + r"{x:g}$\degree$" + ) ax.xaxis.set_major_formatter(degree_formatter) ax.yaxis.set_major_formatter(degree_formatter) if ref: - X, Y = np.meshgrid(np.arange(-180, 180, 4), - np.arange(-180, 180, 4)) + X, Y = np.meshgrid( + np.arange(-180, 180, 4), np.arange(-180, 180, 4) + ) levels = [1, 17, 15000] - colors = ['#A1D4FF', '#35A1FF'] + colors = ["#A1D4FF", "#35A1FF"] ax.contourf(X, Y, np.load(Rama_ref), levels=levels, colors=colors) a = self.results.angles.reshape( - np.prod(self.results.angles.shape[:2]), 2) + np.prod(self.results.angles.shape[:2]), 2 + ) ax.scatter(a[:, 0], a[:, 1], **kwargs) return ax @property def angles(self): - wmsg = ("The `angle` attribute was deprecated in MDAnalysis 2.0.0 " - "and will be removed in MDAnalysis 3.0.0. Please use " - "`results.angles` instead") + wmsg = ( + "The `angle` attribute was deprecated in MDAnalysis 2.0.0 " + "and will be removed in MDAnalysis 3.0.0. Please use " + "`results.angles` instead" + ) warnings.warn(wmsg, DeprecationWarning) return self.results.angles @@ -549,10 +596,13 @@ class Janin(Ramachandran): """ - def __init__(self, atomgroup, - select_remove="resname ALA CYS* GLY PRO SER THR VAL", - select_protein="protein", - **kwargs): + def __init__( + self, + atomgroup, + select_remove="resname ALA CYS* GLY PRO SER THR VAL", + select_protein="protein", + **kwargs, + ): r"""Parameters ---------- atomgroup : AtomGroup or ResidueGroup @@ -588,20 +638,25 @@ def __init__(self, atomgroup, :class:`MDAnalysis.analysis.base.Results` instance. """ super(Ramachandran, self).__init__( - atomgroup.universe.trajectory, **kwargs) + atomgroup.universe.trajectory, **kwargs + ) self.atomgroup = atomgroup residues = atomgroup.residues protein = atomgroup.select_atoms(select_protein).residues remove = residues.atoms.select_atoms(select_remove).residues if not residues.issubset(protein): - raise ValueError("Found atoms outside of protein. Only atoms " - "inside of a protein " - f"(select_protein='{select_protein}') can be " - "used to calculate dihedrals.") + raise ValueError( + "Found atoms outside of protein. Only atoms " + "inside of a protein " + f"(select_protein='{select_protein}') can be " + "used to calculate dihedrals." + ) elif len(remove) != 0: - warnings.warn(f"All residues selected with '{select_remove}' " - "have been removed from the selection.") + warnings.warn( + f"All residues selected with '{select_remove}' " + "have been removed from the selection." + ) residues = residues.difference(remove) self.ag1 = residues.atoms.select_atoms("name N") @@ -613,14 +668,19 @@ def __init__(self, atomgroup, # if there is an altloc attribute, too many atoms will be selected which # must be removed before using the class, or the file is missing atoms # for some residues which must also be removed - if any(len(self.ag1) != len(ag) for ag in [self.ag2, self.ag3, - self.ag4, self.ag5]): - raise ValueError("Too many or too few atoms selected. Check for " - "missing or duplicate atoms in topology.") + if any( + len(self.ag1) != len(ag) + for ag in [self.ag2, self.ag3, self.ag4, self.ag5] + ): + raise ValueError( + "Too many or too few atoms selected. Check for " + "missing or duplicate atoms in topology." + ) def _conclude(self): - self.results.angles = (np.rad2deg(np.array( - self.results.angles)) + 360) % 360 + self.results.angles = ( + np.rad2deg(np.array(self.results.angles)) + 360 + ) % 360 def plot(self, ax=None, ref=False, **kwargs): """Plots data into standard Janin plot. @@ -650,21 +710,27 @@ def plot(self, ax=None, ref=False, **kwargs): if ax is None: ax = plt.gca() ax.axis([0, 360, 0, 360]) - ax.axhline(180, color='k', lw=1) - ax.axvline(180, color='k', lw=1) - ax.set(xticks=range(0, 361, 60), yticks=range(0, 361, 60), - xlabel=r"$\chi_1$", ylabel=r"$\chi_2$") + ax.axhline(180, color="k", lw=1) + ax.axvline(180, color="k", lw=1) + ax.set( + xticks=range(0, 361, 60), + yticks=range(0, 361, 60), + xlabel=r"$\chi_1$", + ylabel=r"$\chi_2$", + ) degree_formatter = plt.matplotlib.ticker.StrMethodFormatter( - r"{x:g}$\degree$") + r"{x:g}$\degree$" + ) ax.xaxis.set_major_formatter(degree_formatter) ax.yaxis.set_major_formatter(degree_formatter) if ref: X, Y = np.meshgrid(np.arange(0, 360, 6), np.arange(0, 360, 6)) levels = [1, 6, 600] - colors = ['#A1D4FF', '#35A1FF'] + colors = ["#A1D4FF", "#35A1FF"] ax.contourf(X, Y, np.load(Janin_ref), levels=levels, colors=colors) - a = self.results.angles.reshape(np.prod( - self.results.angles.shape[:2]), 2) + a = self.results.angles.reshape( + np.prod(self.results.angles.shape[:2]), 2 + ) ax.scatter(a[:, 0], a[:, 1], **kwargs) return ax diff --git a/package/MDAnalysis/analysis/distances.py b/package/MDAnalysis/analysis/distances.py index 9e81de95688..44a09c4fcbd 100644 --- a/package/MDAnalysis/analysis/distances.py +++ b/package/MDAnalysis/analysis/distances.py @@ -38,28 +38,38 @@ :mod:`MDAnalysis.lib.distances` """ -__all__ = ['distance_array', 'self_distance_array', - 'contact_matrix', 'dist', 'between'] +__all__ = [ + "distance_array", + "self_distance_array", + "contact_matrix", + "dist", + "between", +] import numpy as np import scipy.sparse from MDAnalysis.lib.distances import ( - capped_distance, - self_distance_array, distance_array, # legacy reasons + capped_distance, + self_distance_array, + distance_array, # legacy reasons +) +from MDAnalysis.lib.c_distances import ( + contact_matrix_no_pbc, + contact_matrix_pbc, ) -from MDAnalysis.lib.c_distances import contact_matrix_no_pbc, contact_matrix_pbc from MDAnalysis.lib.NeighborSearch import AtomNeighborSearch from MDAnalysis.lib.distances import calc_bonds import warnings import logging + logger = logging.getLogger("MDAnalysis.analysis.distances") def contact_matrix(coord, cutoff=15.0, returntype="numpy", box=None): - '''Calculates a matrix of contacts. + """Calculates a matrix of contacts. There is a fast, high-memory-usage version for small systems (*returntype* = 'numpy'), and a slower, low-memory-usage version for @@ -97,20 +107,24 @@ def contact_matrix(coord, cutoff=15.0, returntype="numpy", box=None): .. versionchanged:: 0.11.0 Keyword *suppress_progmet* and *progress_meter_freq* were removed. - ''' + """ if returntype == "numpy": adj = np.full((len(coord), len(coord)), False, dtype=bool) - pairs = capped_distance(coord, coord, max_cutoff=cutoff, box=box, return_distances=False) - + pairs = capped_distance( + coord, coord, max_cutoff=cutoff, box=box, return_distances=False + ) + idx, idy = np.transpose(pairs) - adj[idx, idy]=True - + adj[idx, idy] = True + return adj elif returntype == "sparse": # Initialize square List of Lists matrix of dimensions equal to number # of coordinates passed - sparse_contacts = scipy.sparse.lil_matrix((len(coord), len(coord)), dtype='bool') + sparse_contacts = scipy.sparse.lil_matrix( + (len(coord), len(coord)), dtype="bool" + ) if box is not None: # with PBC contact_matrix_pbc(coord, sparse_contacts, box, cutoff) @@ -154,14 +168,16 @@ def dist(A, B, offset=0, box=None): """ if A.atoms.n_atoms != B.atoms.n_atoms: - raise ValueError("AtomGroups A and B do not have the same number of atoms") + raise ValueError( + "AtomGroups A and B do not have the same number of atoms" + ) try: off_A, off_B = offset except (TypeError, ValueError): off_A = off_B = int(offset) residues_A = np.array(A.resids) + off_A residues_B = np.array(B.resids) + off_B - + d = calc_bonds(A.positions, B.positions, box) return np.array([residues_A, residues_B, d]) diff --git a/package/MDAnalysis/analysis/dssp/dssp.py b/package/MDAnalysis/analysis/dssp/dssp.py index 21dd9423e49..d88f1bdbe45 100644 --- a/package/MDAnalysis/analysis/dssp/dssp.py +++ b/package/MDAnalysis/analysis/dssp/dssp.py @@ -292,7 +292,6 @@ def get_supported_backends(cls): "dask", ) - def __init__( self, atoms: Union[Universe, AtomGroup], @@ -317,14 +316,15 @@ def __init__( for t in heavyatom_names } self._hydrogens: list["AtomGroup"] = [ - res.atoms.select_atoms(f"name {hydrogen_name}") for res in ag.residues + res.atoms.select_atoms(f"name {hydrogen_name}") + for res in ag.residues ] # can't do it the other way because I need missing values to exist # so that I could fill them in later if not self._guess_hydrogens: # zip() assumes that _heavy_atoms and _hydrogens is ordered in the # same way. This is true as long as the original AtomGroup ag is - # sorted. With the hard-coded protein selection for ag this is always + # sorted. With the hard-coded protein selection for ag this is always # true but if the code on L277 ever changes, make sure to sort first! for calpha, hydrogen in zip( self._heavy_atoms["CA"][1:], self._hydrogens[1:] @@ -373,7 +373,9 @@ def _get_coords(self) -> np.ndarray: coords = np.array(positions) if not self._guess_hydrogens: - guessed_h_coords = _get_hydrogen_atom_position(coords.swapaxes(0, 1)) + guessed_h_coords = _get_hydrogen_atom_position( + coords.swapaxes(0, 1) + ) h_coords = np.array( [ @@ -402,6 +404,7 @@ def _get_aggregator(self): lookup={"dssp_ndarray": ResultsGroup.flatten_sequence}, ) + def translate(onehot: np.ndarray) -> np.ndarray: """Translate a one-hot encoding summary into char-based secondary structure assignment. diff --git a/package/MDAnalysis/analysis/dssp/pydssp_numpy.py b/package/MDAnalysis/analysis/dssp/pydssp_numpy.py index 1ae8ac369ea..f54ea5443af 100644 --- a/package/MDAnalysis/analysis/dssp/pydssp_numpy.py +++ b/package/MDAnalysis/analysis/dssp/pydssp_numpy.py @@ -65,7 +65,10 @@ def _upsample(a: np.ndarray, window: int) -> np.ndarray: def _unfold(a: np.ndarray, window: int, axis: int): "Helper function for 2D array upsampling" - idx = np.arange(window)[:, None] + np.arange(a.shape[axis] - window + 1)[None, :] + idx = ( + np.arange(window)[:, None] + + np.arange(a.shape[axis] - window + 1)[None, :] + ) unfolded = np.take(a, idx, axis=axis) return np.moveaxis(unfolded, axis - 1, -1) @@ -154,7 +157,9 @@ def get_hbond_map( h_1 = coord[1:, 4] coord = coord[:, :4] else: # pragma: no cover - raise ValueError("Number of atoms should be 4 (N,CA,C,O) or 5 (N,CA,C,O,H)") + raise ValueError( + "Number of atoms should be 4 (N,CA,C,O) or 5 (N,CA,C,O,H)" + ) # after this: # h.shape == (n_atoms, 3) # coord.shape == (n_atoms, 4, 3) @@ -176,7 +181,9 @@ def get_hbond_map( # electrostatic interaction energy # e[i, j] = e(CO_i) - e(NH_j) e = np.pad( - CONST_Q1Q2 * (1.0 / d_on + 1.0 / d_ch - 1.0 / d_oh - 1.0 / d_cn) * CONST_F, + CONST_Q1Q2 + * (1.0 / d_on + 1.0 / d_ch - 1.0 / d_oh - 1.0 / d_cn) + * CONST_F, [[1, 0], [0, 1]], ) diff --git a/package/MDAnalysis/analysis/encore/__init__.py b/package/MDAnalysis/analysis/encore/__init__.py index 49095ecfd5c..880b8b5d46c 100644 --- a/package/MDAnalysis/analysis/encore/__init__.py +++ b/package/MDAnalysis/analysis/encore/__init__.py @@ -20,31 +20,35 @@ # MDAnalysis: A Toolkit for the Analysis of Molecular Dynamics Simulations. # J. Comput. Chem. 32 (2011), 2319--2327, doi:10.1002/jcc.21787 # -from .similarity import hes, ces, dres, \ - ces_convergence, dres_convergence +from .similarity import hes, ces, dres, ces_convergence, dres_convergence from .clustering.ClusterCollection import ClusterCollection, Cluster from .clustering.ClusteringMethod import * from .clustering.cluster import cluster from .dimensionality_reduction.DimensionalityReductionMethod import * from .dimensionality_reduction.reduce_dimensionality import ( - reduce_dimensionality) + reduce_dimensionality, +) from .confdistmatrix import get_distance_matrix from .utils import merge_universes -__all__ = ['covariance', 'similarity', 'confdistmatrix', 'clustering'] +__all__ = ["covariance", "similarity", "confdistmatrix", "clustering"] import warnings -wmsg = ("Deprecation in version 2.8.0\n" - "MDAnalysis.analysis.encore is deprecated in favour of the " - "MDAKit mdaencore (https://www.mdanalysis.org/mdaencore/) " - "and will be removed in MDAnalysis version 3.0.0.") +wmsg = ( + "Deprecation in version 2.8.0\n" + "MDAnalysis.analysis.encore is deprecated in favour of the " + "MDAKit mdaencore (https://www.mdanalysis.org/mdaencore/) " + "and will be removed in MDAnalysis version 3.0.0." +) warnings.warn(wmsg, category=DeprecationWarning) from ...due import due, Doi -due.cite(Doi("10.1371/journal.pcbi.1004415"), - description="ENCORE Ensemble Comparison", - path="MDAnalysis.analysis.encore", - cite_module=True) +due.cite( + Doi("10.1371/journal.pcbi.1004415"), + description="ENCORE Ensemble Comparison", + path="MDAnalysis.analysis.encore", + cite_module=True, +) diff --git a/package/MDAnalysis/analysis/encore/bootstrap.py b/package/MDAnalysis/analysis/encore/bootstrap.py index 80761a8fdd2..8784409c7f6 100644 --- a/package/MDAnalysis/analysis/encore/bootstrap.py +++ b/package/MDAnalysis/analysis/encore/bootstrap.py @@ -70,9 +70,13 @@ def bootstrapped_matrix(matrix, ensemble_assignment): indexes = [] for ens in ensemble_identifiers: old_indexes = np.where(ensemble_assignment == ens)[0] - indexes.append(np.random.randint(low=np.min(old_indexes), - high=np.max(old_indexes) + 1, - size=old_indexes.shape[0])) + indexes.append( + np.random.randint( + low=np.min(old_indexes), + high=np.max(old_indexes) + 1, + size=old_indexes.shape[0], + ) + ) indexes = np.hstack(indexes) for j in range(this_m.size): @@ -83,10 +87,9 @@ def bootstrapped_matrix(matrix, ensemble_assignment): return this_m -def get_distance_matrix_bootstrap_samples(distance_matrix, - ensemble_assignment, - samples=100, - ncores=1): +def get_distance_matrix_bootstrap_samples( + distance_matrix, ensemble_assignment, samples=100, ncores=1 +): """ Calculates distance matrices corresponding to bootstrapped ensembles, by resampling with replacement. @@ -113,8 +116,9 @@ def get_distance_matrix_bootstrap_samples(distance_matrix, confdistmatrix : list of encore.utils.TriangularMatrix """ - bs_args = \ - [([distance_matrix, ensemble_assignment]) for i in range(samples)] + bs_args = [ + ([distance_matrix, ensemble_assignment]) for i in range(samples) + ] pc = ParallelCalculation(ncores, bootstrapped_matrix, bs_args) @@ -125,8 +129,7 @@ def get_distance_matrix_bootstrap_samples(distance_matrix, return bootstrap_matrices -def get_ensemble_bootstrap_samples(ensemble, - samples=100): +def get_ensemble_bootstrap_samples(ensemble, samples=100): """ Generates a bootstrapped ensemble by resampling with replacement. @@ -152,9 +155,13 @@ def get_ensemble_bootstrap_samples(ensemble, indices = np.random.randint( low=0, high=ensemble.trajectory.timeseries().shape[1], - size=ensemble.trajectory.timeseries().shape[1]) + size=ensemble.trajectory.timeseries().shape[1], + ) ensembles.append( - mda.Universe(ensemble.filename, - ensemble.trajectory.timeseries(order='fac')[indices,:,:], - format=mda.coordinates.memory.MemoryReader)) + mda.Universe( + ensemble.filename, + ensemble.trajectory.timeseries(order="fac")[indices, :, :], + format=mda.coordinates.memory.MemoryReader, + ) + ) return ensembles diff --git a/package/MDAnalysis/analysis/encore/clustering/ClusterCollection.py b/package/MDAnalysis/analysis/encore/clustering/ClusterCollection.py index e4b7070dcac..8c545c54215 100644 --- a/package/MDAnalysis/analysis/encore/clustering/ClusterCollection.py +++ b/package/MDAnalysis/analysis/encore/clustering/ClusterCollection.py @@ -63,7 +63,7 @@ class Cluster(object): elements : numpy.array array containing the cluster elements. - """ + """ def __init__(self, elem_list=None, centroid=None, idn=None, metadata=None): """Class constructor. If elem_list is None, an empty cluster is created @@ -85,7 +85,7 @@ def __init__(self, elem_list=None, centroid=None, idn=None, metadata=None): metadata, one value for each cluster element. The iterable must have the same length as the elements array. - """ + """ self.id = idn @@ -99,16 +99,20 @@ def __init__(self, elem_list=None, centroid=None, idn=None, metadata=None): self.metadata = {} self.elements = elem_list if centroid not in self.elements: - raise LookupError("Centroid of cluster not found in the element list") + raise LookupError( + "Centroid of cluster not found in the element list" + ) self.centroid = centroid self.size = self.elements.shape[0] if metadata: for name, data in metadata.items(): if len(data) != self.size: - raise TypeError('Size of metadata having label "{0}" ' - 'is not equal to the number of cluster ' - 'elements'.format(name)) + raise TypeError( + 'Size of metadata having label "{0}" ' + "is not equal to the number of cluster " + "elements".format(name) + ) self.add_metadata(name, data) def __iter__(self): @@ -125,8 +129,10 @@ def __len__(self): def add_metadata(self, name, data): if len(data) != self.size: - raise TypeError("Size of metadata is not equal to the number of " - "cluster elements") + raise TypeError( + "Size of metadata is not equal to the number of " + "cluster elements" + ) self.metadata[name] = np.array(data) def __repr__(self): @@ -137,9 +143,9 @@ def __repr__(self): return "" else: return "".format( - self.size, - self.centroid, - self.id) + self.size, self.centroid, self.id + ) + class ClusterCollection(object): """Clusters collection class; this class represents the results of a full @@ -152,38 +158,38 @@ class ClusterCollection(object): clusters : list list of of Cluster objects which are part of the Cluster collection -""" + """ def __init__(self, elements=None, metadata=None): """Class constructor. If elements is None, an empty cluster collection - will be created. Otherwise, the constructor takes as input an - iterable of ints, for instance: + will be created. Otherwise, the constructor takes as input an + iterable of ints, for instance: - [ a, a, a, a, b, b, b, c, c, ... , z, z ] + [ a, a, a, a, b, b, b, c, c, ... , z, z ] - the variables a,b,c,...,z are cluster centroids, here as cluster - element numbers (i.e. 3 means the 4th element of the ordered input - for clustering). The array maps a correspondence between - cluster elements (which are implicitly associated with the - position in the array) with centroids, i. e. defines clusters. - For instance: + the variables a,b,c,...,z are cluster centroids, here as cluster + element numbers (i.e. 3 means the 4th element of the ordered input + for clustering). The array maps a correspondence between + cluster elements (which are implicitly associated with the + position in the array) with centroids, i. e. defines clusters. + For instance: - [ 1, 1, 1, 4, 4, 5 ] + [ 1, 1, 1, 4, 4, 5 ] - means that elements 0, 1, 2 form a cluster which has 1 as centroid, - elements 3 and 4 form a cluster which has 4 as centroid, and - element 5 has its own cluster. + means that elements 0, 1, 2 form a cluster which has 1 as centroid, + elements 3 and 4 form a cluster which has 4 as centroid, and + element 5 has its own cluster. - Parameters - ---------- + Parameters + ---------- - elements : iterable of ints or None - clustering results. See the previous description for details + elements : iterable of ints or None + clustering results. See the previous description for details - metadata : {str:list, str:list,...} or None - metadata for the data elements. The list must be of the same - size as the elements array, with one value per element. + metadata : {str:list, str:list,...} or None + metadata for the data elements. The list must be of the same + size as the elements array, with one value per element. """ idn = 0 @@ -198,9 +204,10 @@ def __init__(self, elements=None, metadata=None): centroids = np.unique(elements_array) for i in centroids: if elements[i] != i: - raise ValueError("element {0}, which is a centroid, doesn't " - "belong to its own cluster".format( - elements[i])) + raise ValueError( + "element {0}, which is a centroid, doesn't " + "belong to its own cluster".format(elements[i]) + ) for c in centroids: this_metadata = {} this_array = np.where(elements_array == c) @@ -208,8 +215,13 @@ def __init__(self, elements=None, metadata=None): for k, v in metadata.items(): this_metadata[k] = np.asarray(v)[this_array] self.clusters.append( - Cluster(elem_list=this_array[0], idn=idn, centroid=c, - metadata=this_metadata)) + Cluster( + elem_list=this_array[0], + idn=idn, + centroid=c, + metadata=this_metadata, + ) + ) idn += 1 @@ -259,4 +271,5 @@ def __repr__(self): return "" else: return "".format( - len(self.clusters)) + len(self.clusters) + ) diff --git a/package/MDAnalysis/analysis/encore/clustering/ClusteringMethod.py b/package/MDAnalysis/analysis/encore/clustering/ClusteringMethod.py index 8071d5eac4a..9af8d48fa86 100644 --- a/package/MDAnalysis/analysis/encore/clustering/ClusteringMethod.py +++ b/package/MDAnalysis/analysis/encore/clustering/ClusteringMethod.py @@ -50,8 +50,10 @@ import sklearn.cluster except ImportError: sklearn = None - msg = "sklearn.cluster could not be imported: some functionality will " \ - "not be available in encore.fit_clusters()" + msg = ( + "sklearn.cluster could not be imported: some functionality will " + "not be available in encore.fit_clusters()" + ) warnings.warn(msg, category=ImportWarning) logging.warning(msg) del msg @@ -69,13 +71,13 @@ def encode_centroid_info(clusters, cluster_centers_indices): return values[indices] -class ClusteringMethod (object): +class ClusteringMethod(object): """ Base class for any Clustering Method """ # Whether the method accepts a distance matrix - accepts_distance_matrix=True + accepts_distance_matrix = True def __call__(self, x): """ @@ -90,21 +92,29 @@ def __call__(self, x): Raises ------ NotImplementedError - Method or behavior needs to be defined by a subclass - + Method or behavior needs to be defined by a subclass + """ - raise NotImplementedError("Class {0} doesn't implement __call__()" - .format(self.__class__.__name__)) + raise NotImplementedError( + "Class {0} doesn't implement __call__()".format( + self.__class__.__name__ + ) + ) class AffinityPropagationNative(ClusteringMethod): """ Interface to the natively implemented Affinity propagation procedure. """ - def __init__(self, - damping=0.9, preference=-1.0, - max_iter=500, convergence_iter=50, - add_noise=True): + + def __init__( + self, + damping=0.9, + preference=-1.0, + max_iter=500, + convergence_iter=50, + add_noise=True, + ): """ Parameters ---------- @@ -150,21 +160,24 @@ def __call__(self, distance_matrix): Returns ------- - numpy.array : array, shape(n_elements) + numpy.array : array, shape(n_elements) centroid frames of the clusters for all of the elements .. versionchanged:: 1.0.0 This method no longer returns ``details`` """ clusters = affinityprop.AffinityPropagation( - s=distance_matrix * -1., # invert sign + s=distance_matrix * -1.0, # invert sign preference=self.preference, lam=self.damping, - max_iterations = self.max_iter, - convergence = self.convergence_iter, - noise=int(self.add_noise)) - + max_iterations=self.max_iter, + convergence=self.convergence_iter, + noise=int(self.add_noise), + ) + return clusters + + if sklearn: class AffinityPropagation(ClusteringMethod): @@ -173,10 +186,14 @@ class AffinityPropagation(ClusteringMethod): in sklearn. """ - def __init__(self, - damping=0.9, preference=-1.0, - max_iter=500, convergence_iter=50, - **kwargs): + def __init__( + self, + damping=0.9, + preference=-1.0, + max_iter=500, + convergence_iter=50, + **kwargs, + ): """ Parameters ---------- @@ -204,14 +221,14 @@ def __init__(self, Other keyword arguments are passed to :class:`sklearn.cluster.AffinityPropagation`. """ - self.ap = \ - sklearn.cluster.AffinityPropagation( - damping=damping, - preference=preference, - max_iter=max_iter, - convergence_iter=convergence_iter, - affinity="precomputed", - **kwargs) + self.ap = sklearn.cluster.AffinityPropagation( + damping=damping, + preference=preference, + max_iter=max_iter, + convergence_iter=convergence_iter, + affinity="precomputed", + **kwargs, + ) def __call__(self, distance_matrix): """ @@ -223,35 +240,40 @@ def __call__(self, distance_matrix): Returns ------- - numpy.array : array, shape(n_elements) + numpy.array : array, shape(n_elements) centroid frames of the clusters for all of the elements .. versionchanged:: 1.0.0 This method no longer returns ``details`` """ - logging.info("Starting Affinity Propagation: {0}".format - (self.ap.get_params())) + logging.info( + "Starting Affinity Propagation: {0}".format( + self.ap.get_params() + ) + ) # Convert from distance matrix to similarity matrix similarity_matrix = distance_matrix.as_array() * -1 clusters = self.ap.fit_predict(similarity_matrix) - clusters = encode_centroid_info(clusters, - self.ap.cluster_centers_indices_) - - return clusters - + clusters = encode_centroid_info( + clusters, self.ap.cluster_centers_indices_ + ) + return clusters class DBSCAN(ClusteringMethod): """ Interface to the DBSCAN clustering procedure implemented in sklearn. """ - def __init__(self, - eps=0.5, - min_samples=5, - algorithm="auto", - leaf_size=30, - **kwargs): + + def __init__( + self, + eps=0.5, + min_samples=5, + algorithm="auto", + leaf_size=30, + **kwargs, + ): """ Parameters ---------- @@ -284,12 +306,14 @@ def __init__(self, """ - self.dbscan = sklearn.cluster.DBSCAN(eps=eps, - min_samples = min_samples, - algorithm=algorithm, - leaf_size = leaf_size, - metric="precomputed", - **kwargs) + self.dbscan = sklearn.cluster.DBSCAN( + eps=eps, + min_samples=min_samples, + algorithm=algorithm, + leaf_size=leaf_size, + metric="precomputed", + **kwargs, + ) def __call__(self, distance_matrix): """ @@ -302,23 +326,23 @@ def __call__(self, distance_matrix): Returns ------- - numpy.array : array, shape(n_elements) + numpy.array : array, shape(n_elements) centroid frames of the clusters for all of the elements .. versionchanged:: 1.0.0 This method no longer returns ``details`` """ - logging.info("Starting DBSCAN: {0}".format( - self.dbscan.get_params())) + logging.info( + "Starting DBSCAN: {0}".format(self.dbscan.get_params()) + ) clusters = self.dbscan.fit_predict(distance_matrix.as_array()) if np.min(clusters == -1): clusters += 1 # No centroid information is provided by DBSCAN, so we just # pick random members cluster_representatives = np.unique(clusters, return_index=True)[1] - clusters = encode_centroid_info(clusters, - cluster_representatives) - + clusters = encode_centroid_info(clusters, cluster_representatives) + return clusters class KMeans(ClusteringMethod): @@ -329,17 +353,20 @@ class KMeans(ClusteringMethod): """ Interface to the KMeans clustering procedure implemented in sklearn. """ - def __init__(self, - n_clusters, - max_iter=300, - n_init=10, - init='k-means++', - algorithm="auto", - tol=1e-4, - verbose=False, - random_state=None, - copy_x=True, - **kwargs): + + def __init__( + self, + n_clusters, + max_iter=300, + n_init=10, + init="k-means++", + algorithm="auto", + tol=1e-4, + verbose=False, + random_state=None, + copy_x=True, + **kwargs, + ): """ Parameters ---------- @@ -388,15 +415,17 @@ def __init__(self, the data mean. """ - self.kmeans = sklearn.cluster.KMeans(n_clusters=n_clusters, - max_iter=max_iter, - n_init=n_init, - init=init, - tol=tol, - verbose=verbose, - random_state=random_state, - copy_x=copy_x, - **kwargs) + self.kmeans = sklearn.cluster.KMeans( + n_clusters=n_clusters, + max_iter=max_iter, + n_init=n_init, + init=init, + tol=tol, + verbose=verbose, + random_state=random_state, + copy_x=copy_x, + **kwargs, + ) def __call__(self, coordinates): """ @@ -409,18 +438,18 @@ def __call__(self, coordinates): Returns ------- - numpy.array : array, shape(n_elements) + numpy.array : array, shape(n_elements) centroid frames of the clusters for all of the elements .. versionchanged:: 1.0.0 This method no longer returns ``details`` """ - logging.info("Starting Kmeans: {0}".format( - (self.kmeans.get_params()))) + logging.info( + "Starting Kmeans: {0}".format((self.kmeans.get_params())) + ) clusters = self.kmeans.fit_predict(coordinates) distances = self.kmeans.transform(coordinates) cluster_center_indices = np.argmin(distances, axis=0) - clusters = encode_centroid_info(clusters, - cluster_center_indices) - + clusters = encode_centroid_info(clusters, cluster_center_indices) + return clusters diff --git a/package/MDAnalysis/analysis/encore/clustering/__init__.py b/package/MDAnalysis/analysis/encore/clustering/__init__.py index 33f828ce5f4..69527468748 100644 --- a/package/MDAnalysis/analysis/encore/clustering/__init__.py +++ b/package/MDAnalysis/analysis/encore/clustering/__init__.py @@ -26,8 +26,9 @@ from .ClusteringMethod import AffinityPropagationNative from .ClusterCollection import ClusterCollection, Cluster -__all__ = ['ClusterCollection', 'Cluster', 'AffinityPropagationNative'] +__all__ = ["ClusterCollection", "Cluster", "AffinityPropagationNative"] if ClusteringMethod.sklearn: from .ClusteringMethod import AffinityPropagation, DBSCAN - __all__ += ['AffinityPropagation', 'DBSCAN'] + + __all__ += ["AffinityPropagation", "DBSCAN"] diff --git a/package/MDAnalysis/analysis/encore/clustering/cluster.py b/package/MDAnalysis/analysis/encore/clustering/cluster.py index 3bffa490236..2173a8d207d 100644 --- a/package/MDAnalysis/analysis/encore/clustering/cluster.py +++ b/package/MDAnalysis/analysis/encore/clustering/cluster.py @@ -160,11 +160,11 @@ def cluster( method = ClusteringMethod.AffinityPropagationNative() # Internally, ensembles are always transformed to a list of lists if ensembles is not None: - if not hasattr(ensembles, '__iter__'): + if not hasattr(ensembles, "__iter__"): ensembles = [ensembles] ensembles_list = ensembles - if not hasattr(ensembles[0], '__iter__'): + if not hasattr(ensembles[0], "__iter__"): ensembles_list = [ensembles] # Calculate merged ensembles and transfer to memory @@ -176,35 +176,41 @@ def cluster( merged_ensembles.append(merge_universes(ensembles)) methods = method - if not hasattr(method, '__iter__'): + if not hasattr(method, "__iter__"): methods = [method] # Check whether any of the clustering methods can make use of a distance # matrix - any_method_accept_distance_matrix = \ - np.any([_method.accepts_distance_matrix for _method in methods]) + any_method_accept_distance_matrix = np.any( + [_method.accepts_distance_matrix for _method in methods] + ) # If distance matrices are provided, check that it matches the number # of ensembles if distance_matrix: - if not hasattr(distance_matrix, '__iter__'): + if not hasattr(distance_matrix, "__iter__"): distance_matrix = [distance_matrix] - if ensembles is not None and \ - len(distance_matrix) != len(merged_ensembles): - raise ValueError("Dimensions of provided list of distance matrices " - "does not match that of provided list of " - "ensembles: {0} vs {1}" - .format(len(distance_matrix), - len(merged_ensembles))) + if ensembles is not None and len(distance_matrix) != len( + merged_ensembles + ): + raise ValueError( + "Dimensions of provided list of distance matrices " + "does not match that of provided list of " + "ensembles: {0} vs {1}".format( + len(distance_matrix), len(merged_ensembles) + ) + ) else: # Calculate distance matrices for all merged ensembles - if not provided if any_method_accept_distance_matrix: distance_matrix = [] for merged_ensemble in merged_ensembles: - distance_matrix.append(get_distance_matrix(merged_ensemble, - select=select, - **kwargs)) + distance_matrix.append( + get_distance_matrix( + merged_ensemble, select=select, **kwargs + ) + ) args = [] for method in methods: @@ -212,11 +218,14 @@ def cluster( args += [(d,) for d in distance_matrix] else: for merged_ensemble in merged_ensembles: - coordinates = merged_ensemble.trajectory.timeseries(order="fac") + coordinates = merged_ensemble.trajectory.timeseries( + order="fac" + ) # Flatten coordinate matrix into n_frame x n_coordinates - coordinates = np.reshape(coordinates, - (coordinates.shape[0], -1)) + coordinates = np.reshape( + coordinates, (coordinates.shape[0], -1) + ) args.append((coordinates,)) @@ -231,14 +240,16 @@ def cluster( if ensembles is not None: ensemble_assignment = [] for i, ensemble in enumerate(ensembles): - ensemble_assignment += [i+1]*len(ensemble.trajectory) + ensemble_assignment += [i + 1] * len(ensemble.trajectory) ensemble_assignment = np.array(ensemble_assignment) - metadata = {'ensemble_membership': ensemble_assignment} + metadata = {"ensemble_membership": ensemble_assignment} # Create clusters collections from clustering results, # one for each cluster. None if clustering didn't work. - ccs = [ClusterCollection(clusters[1], - metadata=metadata) for clusters in results] + ccs = [ + ClusterCollection(clusters[1], metadata=metadata) + for clusters in results + ] if allow_collapsed_result and len(ccs) == 1: ccs = ccs[0] diff --git a/package/MDAnalysis/analysis/encore/confdistmatrix.py b/package/MDAnalysis/analysis/encore/confdistmatrix.py index 739d715865f..95b22bf0563 100644 --- a/package/MDAnalysis/analysis/encore/confdistmatrix.py +++ b/package/MDAnalysis/analysis/encore/confdistmatrix.py @@ -57,11 +57,18 @@ class to compute an RMSD matrix in such a way is also available. from .utils import TriangularMatrix, trm_indices -def conformational_distance_matrix(ensemble, - conf_dist_function, select="", - superimposition_select="", n_jobs=1, pairwise_align=True, weights='mass', - metadata=True, verbose=False, - max_nbytes=None): +def conformational_distance_matrix( + ensemble, + conf_dist_function, + select="", + superimposition_select="", + n_jobs=1, + pairwise_align=True, + weights="mass", + metadata=True, + verbose=False, + max_nbytes=None, +): """ Run the conformational distance matrix calculation. args and kwargs are passed to conf_dist_function. @@ -104,33 +111,44 @@ def conformational_distance_matrix(ensemble, """ # framesn: number of frames - framesn = len(ensemble.trajectory.timeseries( - ensemble.select_atoms(select), order='fac')) + framesn = len( + ensemble.trajectory.timeseries( + ensemble.select_atoms(select), order="fac" + ) + ) # Prepare metadata recarray if metadata: - metadata = np.array([(gethostname(), - getuser(), - str(datetime.now()), - ensemble.filename, - framesn, - pairwise_align, - select, - weights=='mass')], - dtype=[('host', object), - ('user', object), - ('date', object), - ('topology file', object), - ('number of frames', int), - ('pairwise superimposition', bool), - ('superimposition subset', object), - ('mass-weighted', bool)]) + metadata = np.array( + [ + ( + gethostname(), + getuser(), + str(datetime.now()), + ensemble.filename, + framesn, + pairwise_align, + select, + weights == "mass", + ) + ], + dtype=[ + ("host", object), + ("user", object), + ("date", object), + ("topology file", object), + ("number of frames", int), + ("pairwise superimposition", bool), + ("superimposition subset", object), + ("mass-weighted", bool), + ], + ) # Prepare alignment subset coordinates as necessary rmsd_coordinates = ensemble.trajectory.timeseries( - ensemble.select_atoms(select), - order='fac') + ensemble.select_atoms(select), order="fac" + ) if pairwise_align: if superimposition_select: @@ -139,31 +157,45 @@ def conformational_distance_matrix(ensemble, subset_select = select fitting_coordinates = ensemble.trajectory.timeseries( - ensemble.select_atoms(subset_select), - order='fac') + ensemble.select_atoms(subset_select), order="fac" + ) else: fitting_coordinates = None - if not isinstance(weights, (list, tuple, np.ndarray)) and weights == 'mass': + if ( + not isinstance(weights, (list, tuple, np.ndarray)) + and weights == "mass" + ): weights = ensemble.select_atoms(select).masses.astype(np.float64) if pairwise_align: - subset_weights = ensemble.select_atoms(subset_select).masses.astype(np.float64) + subset_weights = ensemble.select_atoms( + subset_select + ).masses.astype(np.float64) else: subset_weights = None elif weights is None: - weights = np.ones((ensemble.trajectory.timeseries( - ensemble.select_atoms(select))[0].shape[0])).astype(np.float64) + weights = np.ones( + ( + ensemble.trajectory.timeseries(ensemble.select_atoms(select))[ + 0 + ].shape[0] + ) + ).astype(np.float64) if pairwise_align: - subset_weights = np.ones((fit_coords[0].shape[0])).astype(np.float64) + subset_weights = np.ones((fit_coords[0].shape[0])).astype( + np.float64 + ) else: subset_weights = None else: if pairwise_align: if len(weights) != 2: - raise RuntimeError("used pairwise alignment with custom " - "weights. Please provide 2 tuple with " - "weights for 'select' and " - "'superimposition_select'") + raise RuntimeError( + "used pairwise alignment with custom " + "weights. Please provide 2 tuple with " + "weights for 'select' and " + "'superimposition_select'" + ) subset_weights = weights[1] weights = weights[0] else: @@ -176,24 +208,38 @@ def conformational_distance_matrix(ensemble, # Initialize workers. Simple worker doesn't perform fitting, # fitter worker does. indices = trm_indices((0, 0), (framesn - 1, framesn - 1)) - Parallel(n_jobs=n_jobs, verbose=verbose, require='sharedmem', - max_nbytes=max_nbytes)(delayed(conf_dist_function)( - np.int64(element), - rmsd_coordinates, - distmat, - weights, - fitting_coordinates, - subset_weights) for element in indices) - + Parallel( + n_jobs=n_jobs, + verbose=verbose, + require="sharedmem", + max_nbytes=max_nbytes, + )( + delayed(conf_dist_function)( + np.int64(element), + rmsd_coordinates, + distmat, + weights, + fitting_coordinates, + subset_weights, + ) + for element in indices + ) # When the workers have finished, return a TriangularMatrix object return TriangularMatrix(distmat, metadata=metadata) -def set_rmsd_matrix_elements(tasks, coords, rmsdmat, weights, fit_coords=None, - fit_weights=None, *args, **kwargs): - - ''' +def set_rmsd_matrix_elements( + tasks, + coords, + rmsdmat, + weights, + fit_coords=None, + fit_weights=None, + *args, + **kwargs, +): + """ RMSD Matrix calculator Parameters @@ -223,51 +269,60 @@ def set_rmsd_matrix_elements(tasks, coords, rmsdmat, weights, fit_coords=None, fit_weights : numpy.array. optional Array of atomic weights, having the same order as the fit_coords array - ''' + """ i, j = tasks if fit_coords is None and fit_weights is None: sumweights = np.sum(weights) - rmsdmat[(i + 1) * i // 2 + j] = PureRMSD(coords[i].astype(np.float64), - coords[j].astype(np.float64), - coords[j].shape[0], - weights, - sumweights) + rmsdmat[(i + 1) * i // 2 + j] = PureRMSD( + coords[i].astype(np.float64), + coords[j].astype(np.float64), + coords[j].shape[0], + weights, + sumweights, + ) elif fit_coords is not None and fit_weights is not None: sumweights = np.sum(weights) subset_weights = np.asarray(fit_weights) / np.mean(fit_weights) - com_i = np.average(fit_coords[i], axis=0, - weights=fit_weights) + com_i = np.average(fit_coords[i], axis=0, weights=fit_weights) translated_i = coords[i] - com_i subset1_coords = fit_coords[i] - com_i - com_j = np.average(fit_coords[j], axis=0, - weights=fit_weights) + com_j = np.average(fit_coords[j], axis=0, weights=fit_weights) translated_j = coords[j] - com_j subset2_coords = fit_coords[j] - com_j - rotamat = rotation_matrix(subset1_coords, subset2_coords, - subset_weights)[0] + rotamat = rotation_matrix( + subset1_coords, subset2_coords, subset_weights + )[0] rotated_i = np.transpose(np.dot(rotamat, np.transpose(translated_i))) rmsdmat[(i + 1) * i // 2 + j] = PureRMSD( - rotated_i.astype(np.float64), translated_j.astype(np.float64), - coords[j].shape[0], weights, sumweights) + rotated_i.astype(np.float64), + translated_j.astype(np.float64), + coords[j].shape[0], + weights, + sumweights, + ) else: - raise TypeError("Both fit_coords and fit_weights must be specified " - "if one of them is given") - - -def get_distance_matrix(ensemble, - select="name CA", - load_matrix=None, - save_matrix=None, - superimpose=True, - superimposition_subset="name CA", - weights='mass', - n_jobs=1, - max_nbytes=None, - verbose=False, - *conf_dist_args, - **conf_dist_kwargs): + raise TypeError( + "Both fit_coords and fit_weights must be specified " + "if one of them is given" + ) + + +def get_distance_matrix( + ensemble, + select="name CA", + load_matrix=None, + save_matrix=None, + superimpose=True, + superimposition_subset="name CA", + weights="mass", + n_jobs=1, + max_nbytes=None, + verbose=False, + *conf_dist_args, + **conf_dist_kwargs, +): """ Retrieves or calculates the conformational distance (RMSD) matrix. The distance matrix is calculated between all the frames of all @@ -321,60 +376,76 @@ def get_distance_matrix(ensemble, # Load the matrix if required if load_matrix: logging.info( - " Loading similarity matrix from: {0}".format(load_matrix)) - confdistmatrix = \ - TriangularMatrix( - size=ensemble.trajectory.timeseries( - ensemble.select_atoms(select), - order='fac').shape[0], - loadfile=load_matrix) + " Loading similarity matrix from: {0}".format(load_matrix) + ) + confdistmatrix = TriangularMatrix( + size=ensemble.trajectory.timeseries( + ensemble.select_atoms(select), order="fac" + ).shape[0], + loadfile=load_matrix, + ) logging.info(" Done!") for key in confdistmatrix.metadata.dtype.names: - logging.info(" {0} : {1}".format( - key, str(confdistmatrix.metadata[key][0]))) + logging.info( + " {0} : {1}".format( + key, str(confdistmatrix.metadata[key][0]) + ) + ) # Check matrix size for consistency - if not confdistmatrix.size == \ - ensemble.trajectory.timeseries( - ensemble.select_atoms(select), - order='fac').shape[0]: + if ( + not confdistmatrix.size + == ensemble.trajectory.timeseries( + ensemble.select_atoms(select), order="fac" + ).shape[0] + ): logging.error( "ERROR: The size of the loaded matrix and of the ensemble" - " do not match") + " do not match" + ) return None - # Calculate the matrix else: # Transfer universe to memory to ensure timeseries() support ensemble.transfer_to_memory() - if not isinstance(weights, (list, tuple, np.ndarray)) and weights == 'mass': - weight_type = 'Mass' + if ( + not isinstance(weights, (list, tuple, np.ndarray)) + and weights == "mass" + ): + weight_type = "Mass" elif weights is None: - weight_type = 'None' + weight_type = "None" else: - weight_type = 'Custom' + weight_type = "Custom" + logging.info( + " Perform pairwise alignment: {0}".format(str(superimpose)) + ) logging.info( - " Perform pairwise alignment: {0}".format(str(superimpose))) - logging.info(" weighted alignment and RMSD: {0}".format(weight_type)) + " weighted alignment and RMSD: {0}".format(weight_type) + ) if superimpose: logging.info( - " Atoms subset for alignment: {0}" - .format(superimposition_subset)) + " Atoms subset for alignment: {0}".format( + superimposition_subset + ) + ) logging.info(" Calculating similarity matrix . . .") # Use superimposition subset, if necessary. If the pairwise alignment # is not required, it will not be performed anyway. - confdistmatrix = conformational_distance_matrix(ensemble, - conf_dist_function=set_rmsd_matrix_elements, - select=select, - pairwise_align=superimpose, - weights=weights, - n_jobs=n_jobs, - max_nbytes=max_nbytes, - verbose=verbose) + confdistmatrix = conformational_distance_matrix( + ensemble, + conf_dist_function=set_rmsd_matrix_elements, + select=select, + pairwise_align=superimpose, + weights=weights, + n_jobs=n_jobs, + max_nbytes=max_nbytes, + verbose=verbose, + ) logging.info(" Done!") diff --git a/package/MDAnalysis/analysis/encore/covariance.py b/package/MDAnalysis/analysis/encore/covariance.py index 5c7b3b363a5..08e014e315c 100644 --- a/package/MDAnalysis/analysis/encore/covariance.py +++ b/package/MDAnalysis/analysis/encore/covariance.py @@ -39,6 +39,7 @@ """ import numpy as np + def ml_covariance_estimator(coordinates, reference_coordinates=None): """ Standard maximum likelihood estimator of the covariance matrix. @@ -70,17 +71,17 @@ def ml_covariance_estimator(coordinates, reference_coordinates=None): coordinates_offset = coordinates - np.average(coordinates, axis=0) # Calculate covariance manually - coordinates_cov = np.zeros((coordinates.shape[1], - coordinates.shape[1])) + coordinates_cov = np.zeros((coordinates.shape[1], coordinates.shape[1])) for frame in coordinates_offset: coordinates_cov += np.outer(frame, frame) coordinates_cov /= coordinates.shape[0] return coordinates_cov -def shrinkage_covariance_estimator( coordinates, - reference_coordinates=None, - shrinkage_parameter=None): + +def shrinkage_covariance_estimator( + coordinates, reference_coordinates=None, shrinkage_parameter=None +): """ Shrinkage estimator of the covariance matrix using the method described in @@ -125,8 +126,11 @@ def shrinkage_covariance_estimator( coordinates, xmkt = np.average(x, axis=1) # Call maximum likelihood estimator (note the additional column) - sample = ml_covariance_estimator(np.hstack([x, xmkt[:, np.newaxis]]), 0)\ - * (t-1)/float(t) + sample = ( + ml_covariance_estimator(np.hstack([x, xmkt[:, np.newaxis]]), 0) + * (t - 1) + / float(t) + ) # Split covariance matrix into components covmkt = sample[0:n, n] @@ -134,53 +138,59 @@ def shrinkage_covariance_estimator( coordinates, sample = sample[:n, :n] # Prior - prior = np.outer(covmkt, covmkt)/varmkt + prior = np.outer(covmkt, covmkt) / varmkt prior[np.ma.make_mask(np.eye(n))] = np.diag(sample) # If shrinkage parameter is not set, estimate it if shrinkage_parameter is None: # Frobenius norm - c = np.linalg.norm(sample - prior, ord='fro')**2 + c = np.linalg.norm(sample - prior, ord="fro") ** 2 y = x**2 - p = 1/float(t)*np.sum(np.dot(np.transpose(y), y))\ - - np.sum(np.sum(sample**2)) - rdiag = 1/float(t)*np.sum(np.sum(y**2))\ - - np.sum(np.diag(sample)**2) + p = 1 / float(t) * np.sum(np.dot(np.transpose(y), y)) - np.sum( + np.sum(sample**2) + ) + rdiag = 1 / float(t) * np.sum(np.sum(y**2)) - np.sum( + np.diag(sample) ** 2 + ) z = x * np.repeat(xmkt[:, np.newaxis], n, axis=1) - v1 = 1/float(t) * np.dot(np.transpose(y), z) \ - - np.repeat(covmkt[:, np.newaxis], n, axis=1)*sample - roff1 = (np.sum( - v1*np.transpose( - np.repeat( - covmkt[:, np.newaxis], n, axis=1) - ) - )/varmkt - - np.sum(np.diag(v1)*covmkt)/varmkt) - v3 = 1/float(t)*np.dot(np.transpose(z), z) - varmkt*sample - roff3 = (np.sum(v3*np.outer(covmkt, covmkt))/varmkt**2 - - np.sum(np.diag(v3)*covmkt**2)/varmkt**2) - roff = 2*roff1-roff3 - r = rdiag+roff + v1 = ( + 1 / float(t) * np.dot(np.transpose(y), z) + - np.repeat(covmkt[:, np.newaxis], n, axis=1) * sample + ) + roff1 = ( + np.sum( + v1 * np.transpose(np.repeat(covmkt[:, np.newaxis], n, axis=1)) + ) + / varmkt + - np.sum(np.diag(v1) * covmkt) / varmkt + ) + v3 = 1 / float(t) * np.dot(np.transpose(z), z) - varmkt * sample + roff3 = ( + np.sum(v3 * np.outer(covmkt, covmkt)) / varmkt**2 + - np.sum(np.diag(v3) * covmkt**2) / varmkt**2 + ) + roff = 2 * roff1 - roff3 + r = rdiag + roff # Shrinkage constant - k = (p-r)/c - shrinkage_parameter = max(0, min(1, k/float(t))) + k = (p - r) / c + shrinkage_parameter = max(0, min(1, k / float(t))) # calculate covariance matrix - sigma = shrinkage_parameter*prior+(1-shrinkage_parameter)*sample + sigma = shrinkage_parameter * prior + (1 - shrinkage_parameter) * sample return sigma - - -def covariance_matrix(ensemble, - select="name CA", - estimator=shrinkage_covariance_estimator, - weights='mass', - reference=None): +def covariance_matrix( + ensemble, + select="name CA", + estimator=shrinkage_covariance_estimator, + weights="mass", + reference=None, +): """ Calculates (optionally mass weighted) covariance matrix @@ -209,8 +219,8 @@ def covariance_matrix(ensemble, """ # Extract coordinates from ensemble coordinates = ensemble.trajectory.timeseries( - ensemble.select_atoms(select), - order='fac') + ensemble.select_atoms(select), order="fac" + ) # Flatten coordinate matrix into n_frame x n_coordinates coordinates = np.reshape(coordinates, (coordinates.shape[0], -1)) @@ -230,7 +240,10 @@ def covariance_matrix(ensemble, # Optionally correct with weights if weights is not None: # Calculate mass-weighted covariance matrix - if not isinstance(weights, (list, tuple, np.ndarray)) and weights == 'mass': + if ( + not isinstance(weights, (list, tuple, np.ndarray)) + and weights == "mass" + ): if select: weights = ensemble.select_atoms(select).masses else: @@ -241,13 +254,15 @@ def covariance_matrix(ensemble, else: req_len = ensemble.atoms.n_atoms if req_len != len(weights): - raise ValueError("number of weights is unequal to number of " - "atoms in ensemble") + raise ValueError( + "number of weights is unequal to number of " + "atoms in ensemble" + ) # broadcast to a (len(weights), 3) array weights = np.repeat(weights, 3) - weight_matrix = np.sqrt(np.identity(len(weights))*weights) + weight_matrix = np.sqrt(np.identity(len(weights)) * weights) sigma = np.dot(weight_matrix, np.dot(sigma, weight_matrix)) return sigma diff --git a/package/MDAnalysis/analysis/encore/dimensionality_reduction/DimensionalityReductionMethod.py b/package/MDAnalysis/analysis/encore/dimensionality_reduction/DimensionalityReductionMethod.py index 50349960bdd..3ca43202f5e 100644 --- a/package/MDAnalysis/analysis/encore/dimensionality_reduction/DimensionalityReductionMethod.py +++ b/package/MDAnalysis/analysis/encore/dimensionality_reduction/DimensionalityReductionMethod.py @@ -50,18 +50,22 @@ except ImportError: sklearn = None import warnings - warnings.warn("sklearn.decomposition could not be imported: some " - "functionality will not be available in " - "encore.dimensionality_reduction()", category=ImportWarning) + warnings.warn( + "sklearn.decomposition could not be imported: some " + "functionality will not be available in " + "encore.dimensionality_reduction()", + category=ImportWarning, + ) -class DimensionalityReductionMethod (object): + +class DimensionalityReductionMethod(object): """ Base class for any Dimensionality Reduction Method """ # Whether the method accepts a distance matrix - accepts_distance_matrix=True + accepts_distance_matrix = True def __call__(self, x): """ @@ -80,21 +84,27 @@ def __call__(self, x): coordinates in reduced space """ - raise NotImplementedError("Class {0} doesn't implement __call__()" - .format(self.__class__.__name__)) + raise NotImplementedError( + "Class {0} doesn't implement __call__()".format( + self.__class__.__name__ + ) + ) class StochasticProximityEmbeddingNative(DimensionalityReductionMethod): """ Interface to the natively implemented Affinity propagation procedure. """ - def __init__(self, - dimension = 2, - distance_cutoff = 1.5, - min_lam = 0.1, - max_lam = 2.0, - ncycle = 100, - nstep = 10000,): + + def __init__( + self, + dimension=2, + distance_cutoff=1.5, + min_lam=0.1, + max_lam=2.0, + ncycle=100, + nstep=10000, + ): """ Parameters ---------- @@ -140,21 +150,21 @@ def __call__(self, distance_matrix): coordinates in reduced space """ - final_stress, coordinates = \ + final_stress, coordinates = ( stochasticproxembed.StochasticProximityEmbedding( - s=distance_matrix, - rco=self.distance_cutoff, - dim=self.dimension, - minlam = self.min_lam, - maxlam = self.max_lam, - ncycle = self.ncycle, - nstep = self.nstep, - stressfreq = self.stressfreq + s=distance_matrix, + rco=self.distance_cutoff, + dim=self.dimension, + minlam=self.min_lam, + maxlam=self.max_lam, + ncycle=self.ncycle, + nstep=self.nstep, + stressfreq=self.stressfreq, + ) ) return coordinates, {"final_stress": final_stress} - if sklearn: class PrincipalComponentAnalysis(DimensionalityReductionMethod): @@ -166,9 +176,7 @@ class PrincipalComponentAnalysis(DimensionalityReductionMethod): # Whether the method accepts a distance matrix accepts_distance_matrix = False - def __init__(self, - dimension = 2, - **kwargs): + def __init__(self, dimension=2, **kwargs): """ Parameters ---------- @@ -177,8 +185,9 @@ def __init__(self, Number of dimensions to which the conformational space will be reduced to (default is 3). """ - self.pca = sklearn.decomposition.PCA(n_components=dimension, - **kwargs) + self.pca = sklearn.decomposition.PCA( + n_components=dimension, **kwargs + ) def __call__(self, coordinates): """ diff --git a/package/MDAnalysis/analysis/encore/dimensionality_reduction/__init__.py b/package/MDAnalysis/analysis/encore/dimensionality_reduction/__init__.py index fefd1b85acd..a2ca041d305 100644 --- a/package/MDAnalysis/analysis/encore/dimensionality_reduction/__init__.py +++ b/package/MDAnalysis/analysis/encore/dimensionality_reduction/__init__.py @@ -24,8 +24,9 @@ from .DimensionalityReductionMethod import StochasticProximityEmbeddingNative -__all__ = ['StochasticProximityEmbeddingNative'] +__all__ = ["StochasticProximityEmbeddingNative"] if DimensionalityReductionMethod.sklearn: from .DimensionalityReductionMethod import PrincipalComponentAnalysis - __all__ += ['PrincipalComponentAnalysis'] + + __all__ += ["PrincipalComponentAnalysis"] diff --git a/package/MDAnalysis/analysis/encore/dimensionality_reduction/reduce_dimensionality.py b/package/MDAnalysis/analysis/encore/dimensionality_reduction/reduce_dimensionality.py index 82e805c91bf..d1e05e1cd2f 100644 --- a/package/MDAnalysis/analysis/encore/dimensionality_reduction/reduce_dimensionality.py +++ b/package/MDAnalysis/analysis/encore/dimensionality_reduction/reduce_dimensionality.py @@ -41,7 +41,8 @@ from ..confdistmatrix import get_distance_matrix from ..utils import ParallelCalculation, merge_universes from ..dimensionality_reduction.DimensionalityReductionMethod import ( - StochasticProximityEmbeddingNative) + StochasticProximityEmbeddingNative, +) def reduce_dimensionality( @@ -157,11 +158,11 @@ def reduce_dimensionality( if method is None: method = StochasticProximityEmbeddingNative() if ensembles is not None: - if not hasattr(ensembles, '__iter__'): + if not hasattr(ensembles, "__iter__"): ensembles = [ensembles] ensembles_list = ensembles - if not hasattr(ensembles[0], '__iter__'): + if not hasattr(ensembles[0], "__iter__"): ensembles_list = [ensembles] # Calculate merged ensembles and transfer to memory @@ -173,37 +174,40 @@ def reduce_dimensionality( merged_ensembles.append(merge_universes(ensembles)) methods = method - if not hasattr(method, '__iter__'): + if not hasattr(method, "__iter__"): methods = [method] # Check whether any of the methods can make use of a distance matrix - any_method_accept_distance_matrix = \ - np.any([_method.accepts_distance_matrix for _method in - methods]) - - + any_method_accept_distance_matrix = np.any( + [_method.accepts_distance_matrix for _method in methods] + ) # If distance matrices are provided, check that it matches the number # of ensembles if distance_matrix: - if not hasattr(distance_matrix, '__iter__'): + if not hasattr(distance_matrix, "__iter__"): distance_matrix = [distance_matrix] - if ensembles is not None and \ - len(distance_matrix) != len(merged_ensembles): - raise ValueError("Dimensions of provided list of distance matrices " - "does not match that of provided list of " - "ensembles: {0} vs {1}" - .format(len(distance_matrix), - len(merged_ensembles))) + if ensembles is not None and len(distance_matrix) != len( + merged_ensembles + ): + raise ValueError( + "Dimensions of provided list of distance matrices " + "does not match that of provided list of " + "ensembles: {0} vs {1}".format( + len(distance_matrix), len(merged_ensembles) + ) + ) else: # Calculate distance matrices for all merged ensembles - if not provided if any_method_accept_distance_matrix: distance_matrix = [] for merged_ensemble in merged_ensembles: - distance_matrix.append(get_distance_matrix(merged_ensemble, - select=select, - **kwargs)) + distance_matrix.append( + get_distance_matrix( + merged_ensemble, select=select, **kwargs + ) + ) args = [] for method in methods: @@ -211,11 +215,14 @@ def reduce_dimensionality( args += [(d,) for d in distance_matrix] else: for merged_ensemble in merged_ensembles: - coordinates = merged_ensemble.trajectory.timeseries(order="fac") + coordinates = merged_ensemble.trajectory.timeseries( + order="fac" + ) # Flatten coordinate matrix into n_frame x n_coordinates - coordinates = np.reshape(coordinates, - (coordinates.shape[0], -1)) + coordinates = np.reshape( + coordinates, (coordinates.shape[0], -1) + ) args.append((coordinates,)) @@ -230,16 +237,16 @@ def reduce_dimensionality( if ensembles is not None: ensemble_assignment = [] for i, ensemble in enumerate(ensembles): - ensemble_assignment += [i+1]*len(ensemble.trajectory) + ensemble_assignment += [i + 1] * len(ensemble.trajectory) ensemble_assignment = np.array(ensemble_assignment) - details['ensemble_membership'] = ensemble_assignment + details["ensemble_membership"] = ensemble_assignment coordinates = [] for result in results: coordinates.append(result[1][0]) # details.append(result[1][1]) - if allow_collapsed_result and len(coordinates)==1: + if allow_collapsed_result and len(coordinates) == 1: coordinates = coordinates[0] # details = details[0] diff --git a/package/MDAnalysis/analysis/encore/similarity.py b/package/MDAnalysis/analysis/encore/similarity.py index c9a8ff1486c..17b9fb28860 100644 --- a/package/MDAnalysis/analysis/encore/similarity.py +++ b/package/MDAnalysis/analysis/encore/similarity.py @@ -180,24 +180,32 @@ from ...coordinates.memory import MemoryReader from .confdistmatrix import get_distance_matrix -from .bootstrap import (get_distance_matrix_bootstrap_samples, - get_ensemble_bootstrap_samples) +from .bootstrap import ( + get_distance_matrix_bootstrap_samples, + get_ensemble_bootstrap_samples, +) from .clustering.cluster import cluster from .clustering.ClusteringMethod import AffinityPropagationNative from .dimensionality_reduction.DimensionalityReductionMethod import ( - StochasticProximityEmbeddingNative) + StochasticProximityEmbeddingNative, +) from .dimensionality_reduction.reduce_dimensionality import ( - reduce_dimensionality) + reduce_dimensionality, +) from .covariance import ( - covariance_matrix, ml_covariance_estimator, shrinkage_covariance_estimator) + covariance_matrix, + ml_covariance_estimator, + shrinkage_covariance_estimator, +) from .utils import merge_universes from .utils import trm_indices_diag, trm_indices_nodiag # Low boundary value for log() argument - ensure no nans -EPSILON = 1E-15 +EPSILON = 1e-15 xlogy = np.vectorize( - lambda x, y: 0.0 if (x <= EPSILON and y <= EPSILON) else x * np.log(y)) + lambda x, y: 0.0 if (x <= EPSILON and y <= EPSILON) else x * np.log(y) +) def discrete_kullback_leibler_divergence(pA, pB): @@ -242,16 +250,15 @@ def discrete_jensen_shannon_divergence(pA, pB): djs : float Discrete Jensen-Shannon divergence -""" - return 0.5 * (discrete_kullback_leibler_divergence(pA, (pA + pB) * 0.5) + - discrete_kullback_leibler_divergence(pB, (pA + pB) * 0.5)) + """ + return 0.5 * ( + discrete_kullback_leibler_divergence(pA, (pA + pB) * 0.5) + + discrete_kullback_leibler_divergence(pB, (pA + pB) * 0.5) + ) # calculate harmonic similarity -def harmonic_ensemble_similarity(sigma1, - sigma2, - x1, - x2): +def harmonic_ensemble_similarity(sigma1, sigma2, x1, x2): """ Calculate the harmonic ensemble similarity measure as defined in :footcite:p:`Tiberti2015`. @@ -288,18 +295,22 @@ def harmonic_ensemble_similarity(sigma1, d_avg = x1 - x2 # Distance measure - trace = np.trace(np.dot(sigma1, sigma2_inv) + - np.dot(sigma2, sigma1_inv) - - 2 * np.identity(sigma1.shape[0])) - - d_hes = 0.25 * (np.dot(np.transpose(d_avg), - np.dot(sigma1_inv + sigma2_inv, - d_avg)) + trace) + trace = np.trace( + np.dot(sigma1, sigma2_inv) + + np.dot(sigma2, sigma1_inv) + - 2 * np.identity(sigma1.shape[0]) + ) + + d_hes = 0.25 * ( + np.dot(np.transpose(d_avg), np.dot(sigma1_inv + sigma2_inv, d_avg)) + + trace + ) return d_hes -def clustering_ensemble_similarity(cc, ens1, ens1_id, ens2, ens2_id, - select="name CA"): +def clustering_ensemble_similarity( + cc, ens1, ens1_id, ens2, ens2_id, select="name CA" +): """Clustering ensemble similarity: calculate the probability densities from the clusters and calculate discrete Jensen-Shannon divergence. @@ -332,16 +343,26 @@ def clustering_ensemble_similarity(cc, ens1, ens1_id, ens2, ens2_id, Jensen-Shannon divergence between the two ensembles, as calculated by the clustering ensemble similarity method """ - ens1_coordinates = ens1.trajectory.timeseries(ens1.select_atoms(select), - order='fac') - ens2_coordinates = ens2.trajectory.timeseries(ens2.select_atoms(select), - order='fac') - tmpA = np.array([np.where(c.metadata['ensemble_membership'] == ens1_id)[ - 0].shape[0] / float(ens1_coordinates.shape[0]) for - c in cc]) - tmpB = np.array([np.where(c.metadata['ensemble_membership'] == ens2_id)[ - 0].shape[0] / float(ens2_coordinates.shape[0]) for - c in cc]) + ens1_coordinates = ens1.trajectory.timeseries( + ens1.select_atoms(select), order="fac" + ) + ens2_coordinates = ens2.trajectory.timeseries( + ens2.select_atoms(select), order="fac" + ) + tmpA = np.array( + [ + np.where(c.metadata["ensemble_membership"] == ens1_id)[0].shape[0] + / float(ens1_coordinates.shape[0]) + for c in cc + ] + ) + tmpB = np.array( + [ + np.where(c.metadata["ensemble_membership"] == ens2_id)[0].shape[0] + / float(ens2_coordinates.shape[0]) + for c in cc + ] + ) # Exclude clusters which have 0 elements in both ensembles pA = tmpA[tmpA + tmpB > EPSILON] @@ -350,8 +371,9 @@ def clustering_ensemble_similarity(cc, ens1, ens1_id, ens2, ens2_id, return discrete_jensen_shannon_divergence(pA, pB) -def cumulative_clustering_ensemble_similarity(cc, ens1_id, ens2_id, - ens1_id_min=1, ens2_id_min=1): +def cumulative_clustering_ensemble_similarity( + cc, ens1_id, ens2_id, ens1_id_min=1, ens2_id_min=1 +): """ Calculate clustering ensemble similarity between joined ensembles. This means that, after clustering has been performed, some ensembles are @@ -383,16 +405,28 @@ def cumulative_clustering_ensemble_similarity(cc, ens1_id, ens2_id, Jensen-Shannon divergence between the two ensembles, as calculated by the clustering ensemble similarity method -""" + """ - ensA = [np.where(np.logical_and( - c.metadata['ensemble_membership'] <= ens1_id, - c.metadata['ensemble_membership']) - >= ens1_id_min)[0].shape[0] for c in cc] - ensB = [np.where(np.logical_and( - c.metadata['ensemble_membership'] <= ens2_id, - c.metadata['ensemble_membership']) - >= ens2_id_min)[0].shape[0] for c in cc] + ensA = [ + np.where( + np.logical_and( + c.metadata["ensemble_membership"] <= ens1_id, + c.metadata["ensemble_membership"], + ) + >= ens1_id_min + )[0].shape[0] + for c in cc + ] + ensB = [ + np.where( + np.logical_and( + c.metadata["ensemble_membership"] <= ens2_id, + c.metadata["ensemble_membership"], + ) + >= ens2_id_min + )[0].shape[0] + for c in cc + ] sizeA = float(np.sum(ensA)) sizeB = float(np.sum(ensB)) @@ -406,8 +440,7 @@ def cumulative_clustering_ensemble_similarity(cc, ens1_id, ens2_id, return discrete_jensen_shannon_divergence(pA, pB) -def gen_kde_pdfs(embedded_space, ensemble_assignment, nensembles, - nsamples): +def gen_kde_pdfs(embedded_space, ensemble_assignment, nensembles, nsamples): """ Generate Kernel Density Estimates (KDE) from embedded spaces and elaborate the coordinates for later use. @@ -452,7 +485,8 @@ def gen_kde_pdfs(embedded_space, ensemble_assignment, nensembles, for i in range(1, nensembles + 1): this_embedded = embedded_space.transpose()[ - np.where(np.array(ensemble_assignment) == i)].transpose() + np.where(np.array(ensemble_assignment) == i) + ].transpose() embedded_ensembles.append(this_embedded) kdes.append(scipy.stats.gaussian_kde(this_embedded)) @@ -467,9 +501,16 @@ def gen_kde_pdfs(embedded_space, ensemble_assignment, nensembles, return (kdes, resamples, embedded_ensembles) -def dimred_ensemble_similarity(kde1, resamples1, kde2, resamples2, - ln_P1_exp_P1=None, ln_P2_exp_P2=None, - ln_P1P2_exp_P1=None, ln_P1P2_exp_P2=None): +def dimred_ensemble_similarity( + kde1, + resamples1, + kde2, + resamples2, + ln_P1_exp_P1=None, + ln_P2_exp_P2=None, + ln_P1P2_exp_P1=None, + ln_P1P2_exp_P2=None, +): r"""Calculate the Jensen-Shannon divergence according the Dimensionality reduction method. @@ -541,21 +582,38 @@ def dimred_ensemble_similarity(kde1, resamples1, kde2, resamples2, """ - if not ln_P1_exp_P1 and not ln_P2_exp_P2 and not ln_P1P2_exp_P1 and not \ - ln_P1P2_exp_P2: + if ( + not ln_P1_exp_P1 + and not ln_P2_exp_P2 + and not ln_P1P2_exp_P1 + and not ln_P1P2_exp_P2 + ): ln_P1_exp_P1 = np.average(np.log(kde1.evaluate(resamples1))) ln_P2_exp_P2 = np.average(np.log(kde2.evaluate(resamples2))) - ln_P1P2_exp_P1 = np.average(np.log( - 0.5 * (kde1.evaluate(resamples1) + kde2.evaluate(resamples1)))) - ln_P1P2_exp_P2 = np.average(np.log( - 0.5 * (kde1.evaluate(resamples2) + kde2.evaluate(resamples2)))) + ln_P1P2_exp_P1 = np.average( + np.log( + 0.5 * (kde1.evaluate(resamples1) + kde2.evaluate(resamples1)) + ) + ) + ln_P1P2_exp_P2 = np.average( + np.log( + 0.5 * (kde1.evaluate(resamples2) + kde2.evaluate(resamples2)) + ) + ) return 0.5 * ( - ln_P1_exp_P1 - ln_P1P2_exp_P1 + ln_P2_exp_P2 - ln_P1P2_exp_P2) + ln_P1_exp_P1 - ln_P1P2_exp_P1 + ln_P2_exp_P2 - ln_P1P2_exp_P2 + ) -def cumulative_gen_kde_pdfs(embedded_space, ensemble_assignment, nensembles, - nsamples, ens_id_min=1, ens_id_max=None): +def cumulative_gen_kde_pdfs( + embedded_space, + ensemble_assignment, + nensembles, + nsamples, + ens_id_min=1, + ens_id_max=None, +): """ Generate Kernel Density Estimates (KDE) from embedded spaces and elaborate the coordinates for later use. However, consider more than @@ -615,9 +673,13 @@ def cumulative_gen_kde_pdfs(embedded_space, ensemble_assignment, nensembles, if not ens_id_max: ens_id_max = nensembles + 1 for i in range(ens_id_min, ens_id_max): - this_embedded = embedded_space.transpose()[np.where( - np.logical_and(ensemble_assignment >= ens_id_min, - ensemble_assignment <= i))].transpose() + this_embedded = embedded_space.transpose()[ + np.where( + np.logical_and( + ensemble_assignment >= ens_id_min, ensemble_assignment <= i + ) + ) + ].transpose() embedded_ensembles.append(this_embedded) kdes.append(scipy.stats.gaussian_kde(this_embedded)) @@ -628,8 +690,9 @@ def cumulative_gen_kde_pdfs(embedded_space, ensemble_assignment, nensembles, return (kdes, resamples, embedded_ensembles) -def write_output(matrix, base_fname=None, header="", suffix="", - extension="dat"): +def write_output( + matrix, base_fname=None, header="", suffix="", extension="dat" +): """ Write output matrix with a nice format, to stdout and optionally a file. @@ -662,9 +725,9 @@ def write_output(matrix, base_fname=None, header="", suffix="", matrix.square_print(header=header, fname=fname) -def prepare_ensembles_for_convergence_increasing_window(ensemble, - window_size, - select="name CA"): +def prepare_ensembles_for_convergence_increasing_window( + ensemble, window_size, select="name CA" +): """ Generate ensembles to be fed to ces_convergence or dres_convergence from a single ensemble. Basically, the different slices the algorithm @@ -693,8 +756,9 @@ def prepare_ensembles_for_convergence_increasing_window(ensemble, """ - ens_size = ensemble.trajectory.timeseries(ensemble.select_atoms(select), - order='fac').shape[0] + ens_size = ensemble.trajectory.timeseries( + ensemble.select_atoms(select), order="fac" + ).shape[0] rest_slices = ens_size // window_size residuals = ens_size % window_size @@ -706,24 +770,30 @@ def prepare_ensembles_for_convergence_increasing_window(ensemble, slices_n.append(slices_n[-1] + window_size) slices_n.append(slices_n[-1] + residuals + window_size) - for s,sl in enumerate(slices_n[:-1]): - tmp_ensembles.append(mda.Universe( - ensemble.filename, - ensemble.trajectory.timeseries(order='fac') - [slices_n[s]:slices_n[s + 1], :, :], - format=MemoryReader)) + for s, sl in enumerate(slices_n[:-1]): + tmp_ensembles.append( + mda.Universe( + ensemble.filename, + ensemble.trajectory.timeseries(order="fac")[ + slices_n[s] : slices_n[s + 1], :, : + ], + format=MemoryReader, + ) + ) return tmp_ensembles -def hes(ensembles, - select="name CA", - cov_estimator="shrinkage", - weights='mass', - align=False, - estimate_error=False, - bootstrapping_samples=100, - calc_diagonal=False): +def hes( + ensembles, + select="name CA", + cov_estimator="shrinkage", + weights="mass", + align=False, + estimate_error=False, + bootstrapping_samples=100, + calc_diagonal=False, +): r"""Calculates the Harmonic Ensemble Similarity (HES) between ensembles. The HES is calculated with the symmetrized version of Kullback-Leibler @@ -835,8 +905,11 @@ def hes(ensembles, """ - if not isinstance(weights, (list, tuple, np.ndarray)) and weights == 'mass': - weights = ['mass' for _ in range(len(ensembles))] + if ( + not isinstance(weights, (list, tuple, np.ndarray)) + and weights == "mass" + ): + weights = ["mass" for _ in range(len(ensembles))] elif weights is not None: if len(weights) != len(ensembles): raise ValueError("need weights for every ensemble") @@ -848,10 +921,9 @@ def hes(ensembles, # on the universe. if align: for e, w in zip(ensembles, weights): - mda.analysis.align.AlignTraj(e, ensembles[0], - select=select, - weights=w, - in_memory=True).run() + mda.analysis.align.AlignTraj( + e, ensembles[0], select=select, weights=w, in_memory=True + ).run() else: for ensemble in ensembles: ensemble.transfer_to_memory() @@ -871,7 +943,8 @@ def hes(ensembles, else: logging.error( "Covariance estimator {0} is not supported. " - "Choose between 'shrinkage' and 'ml'.".format(cov_estimator)) + "Choose between 'shrinkage' and 'ml'.".format(cov_estimator) + ) return None out_matrix_eln = len(ensembles) @@ -885,8 +958,9 @@ def hes(ensembles, for i, ensemble in enumerate(ensembles): ensembles_list.append( get_ensemble_bootstrap_samples( - ensemble, - samples=bootstrapping_samples)) + ensemble, samples=bootstrapping_samples + ) + ) for t in range(bootstrapping_samples): logging.info("The coordinates will be bootstrapped.") @@ -894,21 +968,30 @@ def hes(ensembles, sigmas = [] values = np.zeros((out_matrix_eln, out_matrix_eln)) for i, e_orig in enumerate(ensembles): - xs.append(np.average( - ensembles_list[i][t].trajectory.timeseries( - e_orig.select_atoms(select), - order=('fac')), - axis=0).flatten()) - sigmas.append(covariance_matrix(ensembles_list[i][t], - weights=weights[i], - estimator=covariance_estimator, - select=select)) + xs.append( + np.average( + ensembles_list[i][t].trajectory.timeseries( + e_orig.select_atoms(select), order=("fac") + ), + axis=0, + ).flatten() + ) + sigmas.append( + covariance_matrix( + ensembles_list[i][t], + weights=weights[i], + estimator=covariance_estimator, + select=select, + ) + ) for pair in pairs_indices: - value = harmonic_ensemble_similarity(x1=xs[pair[0]], - x2=xs[pair[1]], - sigma1=sigmas[pair[0]], - sigma2=sigmas[pair[1]]) + value = harmonic_ensemble_similarity( + x1=xs[pair[0]], + x2=xs[pair[1]], + sigma1=sigmas[pair[0]], + sigma2=sigmas[pair[1]], + ) values[pair[0], pair[1]] = value values[pair[1], pair[0]] = value data.append(values) @@ -923,31 +1006,32 @@ def hes(ensembles, for e, w in zip(ensembles, weights): # Extract coordinates from each ensemble - coordinates_system = e.trajectory.timeseries(e.select_atoms(select), - order='fac') + coordinates_system = e.trajectory.timeseries( + e.select_atoms(select), order="fac" + ) # Average coordinates in each system xs.append(np.average(coordinates_system, axis=0).flatten()) # Covariance matrices in each system - sigmas.append(covariance_matrix(e, - weights=w, - estimator=covariance_estimator, - select=select)) + sigmas.append( + covariance_matrix( + e, weights=w, estimator=covariance_estimator, select=select + ) + ) for i, j in pairs_indices: - value = harmonic_ensemble_similarity(x1=xs[i], - x2=xs[j], - sigma1=sigmas[i], - sigma2=sigmas[j]) + value = harmonic_ensemble_similarity( + x1=xs[i], x2=xs[j], sigma1=sigmas[i], sigma2=sigmas[j] + ) values[i, j] = value values[j, i] = value # Save details as required details = {} for i in range(out_matrix_eln): - details['ensemble{0:d}_mean'.format(i + 1)] = xs[i] - details['ensemble{0:d}_covariance_matrix'.format(i + 1)] = sigmas[i] + details["ensemble{0:d}_mean".format(i + 1)] = xs[i] + details["ensemble{0:d}_covariance_matrix".format(i + 1)] = sigmas[i] return values, details @@ -1099,39 +1183,42 @@ def ces( pairs_indices = list(trm_indices_nodiag(len(ensembles))) clustering_methods = clustering_method - if not hasattr(clustering_method, '__iter__'): + if not hasattr(clustering_method, "__iter__"): clustering_methods = [clustering_method] - any_method_accept_distance_matrix = \ - np.any([method.accepts_distance_matrix for method in clustering_methods]) - all_methods_accept_distance_matrix = \ - np.all([method.accepts_distance_matrix for method in clustering_methods]) + any_method_accept_distance_matrix = np.any( + [method.accepts_distance_matrix for method in clustering_methods] + ) + all_methods_accept_distance_matrix = np.all( + [method.accepts_distance_matrix for method in clustering_methods] + ) # Register which ensembles the samples belong to ensemble_assignment = [] for i, ensemble in enumerate(ensembles): - ensemble_assignment += [i+1]*len(ensemble.trajectory) + ensemble_assignment += [i + 1] * len(ensemble.trajectory) # Calculate distance matrix if not provided if any_method_accept_distance_matrix and not distance_matrix: - distance_matrix = get_distance_matrix(merge_universes(ensembles), - select=select, - ncores=ncores) + distance_matrix = get_distance_matrix( + merge_universes(ensembles), select=select, ncores=ncores + ) if estimate_error: if any_method_accept_distance_matrix: - distance_matrix = \ - get_distance_matrix_bootstrap_samples( - distance_matrix, - ensemble_assignment, - samples=bootstrapping_samples, - ncores=ncores) + distance_matrix = get_distance_matrix_bootstrap_samples( + distance_matrix, + ensemble_assignment, + samples=bootstrapping_samples, + ncores=ncores, + ) if not all_methods_accept_distance_matrix: ensembles_list = [] for i, ensemble in enumerate(ensembles): ensembles_list.append( get_ensemble_bootstrap_samples( - ensemble, - samples=bootstrapping_samples)) + ensemble, samples=bootstrapping_samples + ) + ) ensembles = [] for j in range(bootstrapping_samples): ensembles.append([]) @@ -1141,16 +1228,17 @@ def ces( # if all methods accept distances matrices, duplicate # ensemble so that it matches size of distance matrices # (no need to resample them since they will not be used) - ensembles = [ensembles]*bootstrapping_samples - + ensembles = [ensembles] * bootstrapping_samples # Call clustering procedure - ccs = cluster(ensembles, - method= clustering_methods, - select=select, - distance_matrix = distance_matrix, - ncores = ncores, - allow_collapsed_result=False) + ccs = cluster( + ensembles, + method=clustering_methods, + select=select, + distance_matrix=distance_matrix, + ncores=ncores, + allow_collapsed_result=False, + ) # Do error analysis if estimate_error: @@ -1166,20 +1254,20 @@ def ces( failed_runs += 1 k += 1 continue - values[i].append(np.zeros((len(ensembles[j]), - len(ensembles[j])))) + values[i].append( + np.zeros((len(ensembles[j]), len(ensembles[j]))) + ) for pair in pairs_indices: # Calculate dJS - this_djs = \ - clustering_ensemble_similarity(ccs[k], - ensembles[j][ - pair[0]], - pair[0] + 1, - ensembles[j][ - pair[1]], - pair[1] + 1, - select=select) + this_djs = clustering_ensemble_similarity( + ccs[k], + ensembles[j][pair[0]], + pair[0] + 1, + ensembles[j][pair[1]], + pair[1] + 1, + select=select, + ) values[i][-1][pair[0], pair[1]] = this_djs values[i][-1][pair[1], pair[0]] = this_djs k += 1 @@ -1187,7 +1275,7 @@ def ces( avgs.append(np.average(outs, axis=0)) stds.append(np.std(outs, axis=0)) - if hasattr(clustering_method, '__iter__'): + if hasattr(clustering_method, "__iter__"): pass else: avgs = avgs[0] @@ -1205,19 +1293,20 @@ def ces( for pair in pairs_indices: # Calculate dJS - this_val = \ - clustering_ensemble_similarity(ccs[i], - ensembles[pair[0]], - pair[0] + 1, - ensembles[pair[1]], - pair[1] + 1, - select=select) + this_val = clustering_ensemble_similarity( + ccs[i], + ensembles[pair[0]], + pair[0] + 1, + ensembles[pair[1]], + pair[1] + 1, + select=select, + ) values[-1][pair[0], pair[1]] = this_val values[-1][pair[1], pair[0]] = this_val - details['clustering'] = ccs + details["clustering"] = ccs - if allow_collapsed_result and not hasattr(clustering_method, '__iter__'): + if allow_collapsed_result and not hasattr(clustering_method, "__iter__"): values = values[0] return values, details @@ -1374,43 +1463,54 @@ def dres( pairs_indices = list(trm_indices_nodiag(len(ensembles))) dimensionality_reduction_methods = dimensionality_reduction_method - if not hasattr(dimensionality_reduction_method, '__iter__'): + if not hasattr(dimensionality_reduction_method, "__iter__"): dimensionality_reduction_methods = [dimensionality_reduction_method] - any_method_accept_distance_matrix = \ - np.any([method.accepts_distance_matrix for method in dimensionality_reduction_methods]) - all_methods_accept_distance_matrix = \ - np.all([method.accepts_distance_matrix for method in dimensionality_reduction_methods]) + any_method_accept_distance_matrix = np.any( + [ + method.accepts_distance_matrix + for method in dimensionality_reduction_methods + ] + ) + all_methods_accept_distance_matrix = np.all( + [ + method.accepts_distance_matrix + for method in dimensionality_reduction_methods + ] + ) # Register which ensembles the samples belong to ensemble_assignment = [] for i, ensemble in enumerate(ensembles): - ensemble_assignment += [i+1]*len(ensemble.trajectory) + ensemble_assignment += [i + 1] * len(ensemble.trajectory) # Calculate distance matrix if not provided if any_method_accept_distance_matrix and not distance_matrix: - distance_matrix = get_distance_matrix(merge_universes(ensembles), - select=select, - ncores=ncores) + distance_matrix = get_distance_matrix( + merge_universes(ensembles), select=select, ncores=ncores + ) if estimate_error: if any_method_accept_distance_matrix: - distance_matrix = \ - get_distance_matrix_bootstrap_samples( - distance_matrix, - ensemble_assignment, - samples=bootstrapping_samples, - ncores=ncores) + distance_matrix = get_distance_matrix_bootstrap_samples( + distance_matrix, + ensemble_assignment, + samples=bootstrapping_samples, + ncores=ncores, + ) if not all_methods_accept_distance_matrix: ensembles_list = [] for i, ensemble in enumerate(ensembles): ensembles_list.append( get_ensemble_bootstrap_samples( - ensemble, - samples=bootstrapping_samples)) + ensemble, samples=bootstrapping_samples + ) + ) ensembles = [] for j in range(bootstrapping_samples): - ensembles.append(ensembles_list[i, j] for i - in range(ensembles_list.shape[0])) + ensembles.append( + ensembles_list[i, j] + for i in range(ensembles_list.shape[0]) + ) else: # if all methods accept distances matrices, duplicate # ensemble so that it matches size of distance matrices @@ -1422,9 +1522,10 @@ def dres( ensembles, method=dimensionality_reduction_methods, select=select, - distance_matrix = distance_matrix, - ncores = ncores, - allow_collapsed_result = False) + distance_matrix=distance_matrix, + ncores=ncores, + allow_collapsed_result=False, + ) details = {} details["reduced_coordinates"] = coordinates @@ -1435,24 +1536,28 @@ def dres( values = {} avgs = [] stds = [] - for i,method in enumerate(dimensionality_reduction_methods): + for i, method in enumerate(dimensionality_reduction_methods): values[i] = [] for j in range(bootstrapping_samples): - values[i].append(np.zeros((len(ensembles[j]), - len(ensembles[j])))) + values[i].append( + np.zeros((len(ensembles[j]), len(ensembles[j]))) + ) kdes, resamples, embedded_ensembles = gen_kde_pdfs( coordinates[k], ensemble_assignment, len(ensembles[j]), - nsamples=nsamples) + nsamples=nsamples, + ) for pair in pairs_indices: - this_value = dimred_ensemble_similarity(kdes[pair[0]], - resamples[pair[0]], - kdes[pair[1]], - resamples[pair[1]]) + this_value = dimred_ensemble_similarity( + kdes[pair[0]], + resamples[pair[0]], + kdes[pair[1]], + resamples[pair[1]], + ) values[i][-1][pair[0], pair[1]] = this_value values[i][-1][pair[1], pair[0]] = this_value @@ -1461,7 +1566,7 @@ def dres( avgs.append(np.average(outs, axis=0)) stds.append(np.std(outs, axis=0)) - if hasattr(dimensionality_reduction_method, '__iter__'): + if hasattr(dimensionality_reduction_method, "__iter__"): pass else: avgs = avgs[0] @@ -1471,24 +1576,29 @@ def dres( values = [] - for i,method in enumerate(dimensionality_reduction_methods): + for i, method in enumerate(dimensionality_reduction_methods): values.append(np.zeros((len(ensembles), len(ensembles)))) - kdes, resamples, embedded_ensembles = gen_kde_pdfs(coordinates[i], - ensemble_assignment, - len(ensembles), - nsamples=nsamples) + kdes, resamples, embedded_ensembles = gen_kde_pdfs( + coordinates[i], + ensemble_assignment, + len(ensembles), + nsamples=nsamples, + ) for pair in pairs_indices: - this_value = dimred_ensemble_similarity(kdes[pair[0]], - resamples[pair[0]], - kdes[pair[1]], - resamples[pair[1]]) + this_value = dimred_ensemble_similarity( + kdes[pair[0]], + resamples[pair[0]], + kdes[pair[1]], + resamples[pair[1]], + ) values[-1][pair[0], pair[1]] = this_value values[-1][pair[1], pair[0]] = this_value - if allow_collapsed_result and not hasattr(dimensionality_reduction_method, - '__iter__'): + if allow_collapsed_result and not hasattr( + dimensionality_reduction_method, "__iter__" + ): values = values[0] return values, details @@ -1576,13 +1686,16 @@ def ces_convergence( ) ensembles = prepare_ensembles_for_convergence_increasing_window( - original_ensemble, window_size, select=select) + original_ensemble, window_size, select=select + ) - ccs = cluster(ensembles, - select=select, - method=clustering_method, - allow_collapsed_result=False, - ncores=ncores) + ccs = cluster( + ensembles, + select=select, + method=clustering_method, + allow_collapsed_result=False, + ncores=ncores, + ) out = [] for cc in ccs: @@ -1591,9 +1704,8 @@ def ces_convergence( out.append(np.zeros(len(ensembles))) for j, ensemble in enumerate(ensembles): out[-1][j] = cumulative_clustering_ensemble_similarity( - cc, - len(ensembles), - j + 1) + cc, len(ensembles), j + 1 + ) out = np.array(out).T return out @@ -1680,19 +1792,20 @@ def dres_convergence( ) ensembles = prepare_ensembles_for_convergence_increasing_window( - original_ensemble, window_size, select=select) + original_ensemble, window_size, select=select + ) - coordinates, dimred_details = \ - reduce_dimensionality( - ensembles, - select=select, - method=dimensionality_reduction_method, - allow_collapsed_result=False, - ncores=ncores) + coordinates, dimred_details = reduce_dimensionality( + ensembles, + select=select, + method=dimensionality_reduction_method, + allow_collapsed_result=False, + ncores=ncores, + ) ensemble_assignment = [] for i, ensemble in enumerate(ensembles): - ensemble_assignment += [i+1]*len(ensemble.trajectory) + ensemble_assignment += [i + 1] * len(ensemble.trajectory) ensemble_assignment = np.array(ensemble_assignment) out = [] @@ -1700,18 +1813,17 @@ def dres_convergence( out.append(np.zeros(len(ensembles))) - kdes, resamples, embedded_ensembles = \ - cumulative_gen_kde_pdfs( - coordinates[i], - ensemble_assignment=ensemble_assignment, - nensembles=len(ensembles), - nsamples=nsamples) + kdes, resamples, embedded_ensembles = cumulative_gen_kde_pdfs( + coordinates[i], + ensemble_assignment=ensemble_assignment, + nensembles=len(ensembles), + nsamples=nsamples, + ) for j, ensemble in enumerate(ensembles): - out[-1][j] = dimred_ensemble_similarity(kdes[-1], - resamples[-1], - kdes[j], - resamples[j]) + out[-1][j] = dimred_ensemble_similarity( + kdes[-1], resamples[-1], kdes[j], resamples[j] + ) out = np.array(out).T return out diff --git a/package/MDAnalysis/analysis/encore/utils.py b/package/MDAnalysis/analysis/encore/utils.py index 13a028f45c4..ae6407ddd19 100644 --- a/package/MDAnalysis/analysis/encore/utils.py +++ b/package/MDAnalysis/analysis/encore/utils.py @@ -123,14 +123,16 @@ def loadz(self, fname): """ loaded = np.load(fname, allow_pickle=True) - if loaded['metadata'].shape != (): - if loaded['metadata']['number of frames'] != self.size: + if loaded["metadata"].shape != (): + if loaded["metadata"]["number of frames"] != self.size: raise TypeError - self.metadata = loaded['metadata'] + self.metadata = loaded["metadata"] else: - if self.size*(self.size-1)/2+self.size != len(loaded['elements']): + if self.size * (self.size - 1) / 2 + self.size != len( + loaded["elements"] + ): raise TypeError - self._elements = loaded['elements'] + self._elements = loaded["elements"] def __add__(self, scalar): """Add scalar to matrix elements. @@ -142,7 +144,7 @@ def __add__(self, scalar): Scalar to be added. """ newMatrix = self.__class__(self.size) - newMatrix._elements = self._elements + scalar; + newMatrix._elements = self._elements + scalar return newMatrix def __iadd__(self, scalar): @@ -157,7 +159,6 @@ def __iadd__(self, scalar): self._elements += scalar return self - def __mul__(self, scalar): """Multiply with scalar. @@ -168,7 +169,7 @@ def __mul__(self, scalar): Scalar to multiply with. """ newMatrix = self.__class__(self.size) - newMatrix._elements = self._elements * scalar; + newMatrix._elements = self._elements * scalar return newMatrix def __imul__(self, scalar): @@ -237,10 +238,12 @@ class description. self.n_jobs = cpu_count() self.functions = function - if not hasattr(self.functions, '__iter__'): + if not hasattr(self.functions, "__iter__"): self.functions = [self.functions] * len(args) if len(self.functions) != len(args): - self.functions = self.functions[:] * (len(args) // len(self.functions)) + self.functions = self.functions[:] * ( + len(args) // len(self.functions) + ) # Arguments should be present if args is None: @@ -273,10 +276,12 @@ def worker(self, q, results): """ while True: i = q.get() - if i == 'STOP': + if i == "STOP": return - results.put((i, self.functions[i](*self.args[i], **self.kwargs[i]))) + results.put( + (i, self.functions[i](*self.args[i], **self.kwargs[i])) + ) def run(self): r""" @@ -294,20 +299,23 @@ def run(self): results_list = [] if self.n_jobs == 1: for i in range(self.nruns): - results_list.append((i, self.functions[i](*self.args[i], - **self.kwargs[i]))) + results_list.append( + (i, self.functions[i](*self.args[i], **self.kwargs[i])) + ) else: manager = Manager() q = manager.Queue() results = manager.Queue() - workers = [Process(target=self.worker, args=(q, results)) for i in - range(self.n_jobs)] + workers = [ + Process(target=self.worker, args=(q, results)) + for i in range(self.n_jobs) + ] for i in range(self.nruns): q.put(i) for w in workers: - q.put('STOP') + q.put("STOP") for w in workers: w.start() @@ -315,8 +323,8 @@ def run(self): for w in workers: w.join() - results.put('STOP') - for i in iter(results.get, 'STOP'): + results.put("STOP") + for i in iter(results.get, "STOP"): results_list.append(i) return tuple(sorted(results_list, key=lambda x: x[0])) @@ -361,7 +369,7 @@ def trm_indices_nodiag(n): n : int Matrix size -""" + """ for i in range(1, n): for j in range(i): @@ -377,7 +385,7 @@ def trm_indices_diag(n): n : int Matrix size -""" + """ for i in range(0, n): for j in range(i + 1): @@ -403,6 +411,9 @@ def merge_universes(universes): return mda.Universe( universes[0].filename, - np.concatenate(tuple([e.trajectory.timeseries(order='fac') for e in universes]), - axis=0), - format=MemoryReader) + np.concatenate( + tuple([e.trajectory.timeseries(order="fac") for e in universes]), + axis=0, + ), + format=MemoryReader, + ) diff --git a/package/MDAnalysis/analysis/gnm.py b/package/MDAnalysis/analysis/gnm.py index ee42bc165ef..f2f373543ea 100644 --- a/package/MDAnalysis/analysis/gnm.py +++ b/package/MDAnalysis/analysis/gnm.py @@ -21,9 +21,9 @@ # J. Comput. Chem. 32 (2011), 2319--2327, doi:10.1002/jcc.21787 # -#Analyse a trajectory using elastic network models, following the approach of Hall et al (JACS 2007) -#Ben Hall (benjamin.a.hall@ucl.ac.uk) is to blame -#Copyright 2011; Consider under GPL v2 or later +# Analyse a trajectory using elastic network models, following the approach of Hall et al (JACS 2007) +# Ben Hall (benjamin.a.hall@ucl.ac.uk) is to blame +# Copyright 2011; Consider under GPL v2 or later r""" Elastic network analysis of MD trajectories --- :mod:`MDAnalysis.analysis.gnm` ============================================================================== @@ -97,11 +97,11 @@ from MDAnalysis.analysis.base import Results -logger = logging.getLogger('MDAnalysis.analysis.GNM') +logger = logging.getLogger("MDAnalysis.analysis.GNM") def _dsq(a, b): - diff = (a - b) + diff = a - b return np.dot(diff, diff) @@ -133,10 +133,14 @@ def generate_grid(positions, cutoff): low_x = x.min() low_y = y.min() low_z = z.min() - #Ok now generate a list with 3 dimensions representing boxes in x, y and z - grid = [[[[] for i in range(int((high_z - low_z) / cutoff) + 1)] - for j in range(int((high_y - low_y) / cutoff) + 1)] - for k in range(int((high_x - low_x) / cutoff) + 1)] + # Ok now generate a list with 3 dimensions representing boxes in x, y and z + grid = [ + [ + [[] for i in range(int((high_z - low_z) / cutoff) + 1)] + for j in range(int((high_y - low_y) / cutoff) + 1) + ] + for k in range(int((high_x - low_x) / cutoff) + 1) + ] for i, pos in enumerate(positions): x_pos = int((pos[0] - low_x) / cutoff) y_pos = int((pos[1] - low_y) / cutoff) @@ -166,7 +170,8 @@ def neighbour_generator(positions, cutoff): n_y = len(grid[0]) n_z = len(grid[0][0]) for cell_x, cell_y, cell_z in itertools.product( - range(n_x), range(n_y), range(n_z)): + range(n_x), range(n_y), range(n_z) + ): atoms = grid[cell_x][cell_y][cell_z] # collect all atoms in own cell and neighboring cell all_atoms = [] @@ -247,8 +252,8 @@ class GNMAnalysis(AnalysisBase): ``eigenvectors`` of the ``results`` attribute. .. versionchanged:: 2.8.0 - Enabled **parallel execution** with the ``multiprocessing`` and ``dask`` - backends; use the new method :meth:`get_supported_backends` to see all + Enabled **parallel execution** with the ``multiprocessing`` and ``dask`` + backends; use the new method :meth:`get_supported_backends` to see all supported backends. """ @@ -257,13 +262,15 @@ class GNMAnalysis(AnalysisBase): @classmethod def get_supported_backends(cls): return ("serial", "multiprocessing", "dask") - - def __init__(self, - universe, - select='protein and name CA', - cutoff=7.0, - ReportVector=None, - Bonus_groups=None): + + def __init__( + self, + universe, + select="protein and name CA", + cutoff=7.0, + ReportVector=None, + Bonus_groups=None, + ): super(GNMAnalysis, self).__init__(universe.trajectory) self.u = universe self.select = select @@ -273,12 +280,16 @@ def __init__(self, self.results.eigenvectors = [] self._timesteps = None # time for each frame self.ReportVector = ReportVector - self.Bonus_groups = [self.u.select_atoms(item) for item in Bonus_groups] \ - if Bonus_groups else [] + self.Bonus_groups = ( + [self.u.select_atoms(item) for item in Bonus_groups] + if Bonus_groups + else [] + ) self.ca = self.u.select_atoms(self.select) - def _generate_output(self, w, v, outputobject, - ReportVector=None, counter=0): + def _generate_output( + self, w, v, outputobject, ReportVector=None, counter=0 + ): """Appends time, eigenvalues and eigenvectors to results. This generates the output by adding eigenvalue and @@ -297,7 +308,8 @@ def _generate_output(self, w, v, outputobject, item[0] + 1, w[list_map[1]], item[1], - file=oup) + file=oup, + ) outputobject.eigenvalues.append(w[list_map[1]]) outputobject.eigenvectors.append(v[list_map[1]]) @@ -316,9 +328,9 @@ def generate_kirchoff(self): """ positions = self.ca.positions - #add the com from each bonus group to the ca_positions list + # add the com from each bonus group to the ca_positions list for item in self.Bonus_groups: - #bonus = self.u.select_atoms(item) + # bonus = self.u.select_atoms(item) positions = np.vstack((positions, item.center_of_mass())) natoms = len(positions) @@ -327,8 +339,10 @@ def generate_kirchoff(self): cutoffsq = self.cutoff**2 for i_atom, j_atom in neighbour_generator(positions, self.cutoff): - if j_atom > i_atom and _dsq(positions[i_atom], - positions[j_atom]) < cutoffsq: + if ( + j_atom > i_atom + and _dsq(positions[i_atom], positions[j_atom]) < cutoffsq + ): matrix[i_atom][j_atom] = -1.0 matrix[j_atom][i_atom] = -1.0 matrix[i_atom][i_atom] = matrix[i_atom][i_atom] + 1 @@ -352,7 +366,8 @@ def _single_frame(self): v, self.results, ReportVector=self.ReportVector, - counter=self._ts.frame) + counter=self._ts.frame, + ) def _conclude(self): self.results.times = self.times @@ -427,16 +442,17 @@ class closeContactGNMAnalysis(GNMAnalysis): ``eigenvectors`` of the `results` attribute. """ - def __init__(self, - universe, - select='protein', - cutoff=4.5, - ReportVector=None, - weights="size"): - super(closeContactGNMAnalysis, self).__init__(universe, - select, - cutoff, - ReportVector) + def __init__( + self, + universe, + select="protein", + cutoff=4.5, + ReportVector=None, + weights="size", + ): + super(closeContactGNMAnalysis, self).__init__( + universe, select, cutoff, ReportVector + ) self.weights = weights def generate_kirchoff(self): @@ -452,17 +468,21 @@ def generate_kirchoff(self): # cache sqrt of residue sizes (slow) so that sr[i]*sr[j] == sqrt(r[i]*r[j]) inv_sqrt_res_sizes = np.ones(len(self.ca.residues)) - if self.weights == 'size': + if self.weights == "size": inv_sqrt_res_sizes = 1 / np.sqrt( - [r.atoms.n_atoms for r in self.ca.residues]) + [r.atoms.n_atoms for r in self.ca.residues] + ) for i_atom, j_atom in neighbour_generator(positions, self.cutoff): - if j_atom > i_atom and _dsq(positions[i_atom], - positions[j_atom]) < cutoffsq: + if ( + j_atom > i_atom + and _dsq(positions[i_atom], positions[j_atom]) < cutoffsq + ): iresidue = residue_index_map[i_atom] jresidue = residue_index_map[j_atom] - contact = (inv_sqrt_res_sizes[iresidue] * - inv_sqrt_res_sizes[jresidue]) + contact = ( + inv_sqrt_res_sizes[iresidue] * inv_sqrt_res_sizes[jresidue] + ) matrix[iresidue][jresidue] -= contact matrix[jresidue][iresidue] -= contact matrix[iresidue][iresidue] += contact diff --git a/package/MDAnalysis/analysis/hbonds/__init__.py b/package/MDAnalysis/analysis/hbonds/__init__.py index b74b96638b4..4f01974ff25 100644 --- a/package/MDAnalysis/analysis/hbonds/__init__.py +++ b/package/MDAnalysis/analysis/hbonds/__init__.py @@ -22,7 +22,8 @@ # __all__ = [ - 'HydrogenBondAutoCorrel', 'find_hydrogen_donors', + "HydrogenBondAutoCorrel", + "find_hydrogen_donors", ] from .hbond_autocorrel import HydrogenBondAutoCorrel, find_hydrogen_donors diff --git a/package/MDAnalysis/analysis/hbonds/hbond_autocorrel.py b/package/MDAnalysis/analysis/hbonds/hbond_autocorrel.py index a5204236a07..3f80affc814 100644 --- a/package/MDAnalysis/analysis/hbonds/hbond_autocorrel.py +++ b/package/MDAnalysis/analysis/hbonds/hbond_autocorrel.py @@ -45,10 +45,12 @@ import warnings with warnings.catch_warnings(): - warnings.simplefilter('always', DeprecationWarning) - wmsg = ("This module was moved to " - "MDAnalysis.analysis.hydrogenbonds.hbond_autocorrel; " - "hbonds.hbond_autocorrel will be removed in 3.0.0.") + warnings.simplefilter("always", DeprecationWarning) + wmsg = ( + "This module was moved to " + "MDAnalysis.analysis.hydrogenbonds.hbond_autocorrel; " + "hbonds.hbond_autocorrel will be removed in 3.0.0." + ) warnings.warn(wmsg, category=DeprecationWarning) from MDAnalysis.lib.util import deprecate @@ -56,16 +58,18 @@ from ..hydrogenbonds import hbond_autocorrel -find_hydrogen_donors = deprecate(hbond_autocorrel.find_hydrogen_donors, - release="2.0.0", remove="3.0.0", - message="The function was moved to " - "MDAnalysis.analysis.hbonds.hbond_autocorrel.") - -HydrogenBondAutoCorrel = deprecate(hbond_autocorrel.HydrogenBondAutoCorrel, - release="2.0.0", remove="3.0.0", - message="The class was moved to " - "MDAnalysis.analysis.hbonds.hbond_autocorrel.") - - - - +find_hydrogen_donors = deprecate( + hbond_autocorrel.find_hydrogen_donors, + release="2.0.0", + remove="3.0.0", + message="The function was moved to " + "MDAnalysis.analysis.hbonds.hbond_autocorrel.", +) + +HydrogenBondAutoCorrel = deprecate( + hbond_autocorrel.HydrogenBondAutoCorrel, + release="2.0.0", + remove="3.0.0", + message="The class was moved to " + "MDAnalysis.analysis.hbonds.hbond_autocorrel.", +) diff --git a/package/MDAnalysis/analysis/helix_analysis.py b/package/MDAnalysis/analysis/helix_analysis.py index 1b1bdf9ce3f..e0edf763708 100644 --- a/package/MDAnalysis/analysis/helix_analysis.py +++ b/package/MDAnalysis/analysis/helix_analysis.py @@ -126,7 +126,7 @@ def vector_of_best_fit(coordinates): # does vector face first local helix origin? angle = mdamath.angle(centered[0], vector) - if angle > np.pi/2: + if angle > np.pi / 2: vector *= -1 return vector @@ -168,7 +168,7 @@ def local_screw_angles(global_axis, ref_axis, helix_directions): # project helix_directions onto global to remove contribution norm_global_sq = np.dot(global_axis, global_axis) - mag_g = np.matmul(global_axis, helix_directions.T)/norm_global_sq + mag_g = np.matmul(global_axis, helix_directions.T) / norm_global_sq # projection onto global_axis proj_g = mag_g.reshape(-1, 1) @ global_axis.reshape(1, -1) # projection onto plane w/o global_axis contribution @@ -176,9 +176,10 @@ def local_screw_angles(global_axis, ref_axis, helix_directions): # angles from projection to perp refs = np.array([perp, ortho]) # (2, 3) - norms = _, ortho_norm = np.outer(mdamath.pnorm(refs), - mdamath.pnorm(proj_plane)) - cos = cos_perp, cos_ortho = np.matmul(refs, proj_plane.T)/norms + norms = _, ortho_norm = np.outer( + mdamath.pnorm(refs), mdamath.pnorm(proj_plane) + ) + cos = cos_perp, cos_ortho = np.matmul(refs, proj_plane.T) / norms to_perp, to_ortho = np.arccos(np.clip(cos, -1, 1)) # (2, n_vec) to_ortho[ortho_norm == 0] = 0 # ? to_ortho[cos_perp < 0] *= -1 @@ -251,11 +252,11 @@ def helix_analysis(positions, ref_axis=(0, 0, 1)): adjacent_mag = bimags[:-1] * bimags[1:] # (n_res-3,) # find angle between bisectors for twist and n_residue/turn - cos_theta = mdamath.pdot(bisectors[:-1], bisectors[1:])/adjacent_mag + cos_theta = mdamath.pdot(bisectors[:-1], bisectors[1:]) / adjacent_mag cos_theta = np.clip(cos_theta, -1, 1) twists = np.arccos(cos_theta) # (n_res-3,) local_twists = np.rad2deg(twists) - local_nres_per_turn = 2*np.pi / twists + local_nres_per_turn = 2 * np.pi / twists # find normal to bisectors for local axes cross_bi = np.cross(bisectors[:-1], bisectors[1:]) # (n_res-3, 3) @@ -266,42 +267,46 @@ def helix_analysis(positions, ref_axis=(0, 0, 1)): # find angles between axes for bends bend_theta = np.matmul(local_axes, local_axes.T) # (n_res-3, n_res-3) # set angles to 0 between zero-vectors - bend_theta = np.where(zero_vectors+zero_vectors.T, # (n_res-3, n_res-3) - bend_theta, 1) + bend_theta = np.where( + zero_vectors + zero_vectors.T, bend_theta, 1 # (n_res-3, n_res-3) + ) bend_matrix = np.rad2deg(np.arccos(np.clip(bend_theta, -1, 1))) # local bends are between axes 3 windows apart local_bends = np.diagonal(bend_matrix, offset=3) # (n_res-6,) # radius of local cylinder - radii = (adjacent_mag**0.5) / (2*(1.0-cos_theta)) # (n_res-3,) + radii = (adjacent_mag**0.5) / (2 * (1.0 - cos_theta)) # (n_res-3,) # special case: angle b/w bisectors is 0 (should virtually never happen) # guesstimate radius = half bisector magnitude - radii = np.where(cos_theta != 1, radii, (adjacent_mag**0.5)/2) + radii = np.where(cos_theta != 1, radii, (adjacent_mag**0.5) / 2) # height of local cylinder heights = np.abs(mdamath.pdot(vectors[1:-1], local_axes)) # (n_res-3,) - local_helix_directions = (bisectors.T/bimags).T # (n_res-2, 3) + local_helix_directions = (bisectors.T / bimags).T # (n_res-2, 3) # get origins by subtracting radius from atom i+1 origins = positions[1:-1].copy() # (n_res-2, 3) - origins[:-1] -= (radii*local_helix_directions[:-1].T).T + origins[:-1] -= (radii * local_helix_directions[:-1].T).T # subtract radius from atom i+2 in last one - origins[-1] -= radii[-1]*local_helix_directions[-1] + origins[-1] -= radii[-1] * local_helix_directions[-1] helix_axes = vector_of_best_fit(origins) - screw = local_screw_angles(helix_axes, np.asarray(ref_axis), - local_helix_directions) - - results = {'local_twists': local_twists, - 'local_nres_per_turn': local_nres_per_turn, - 'local_axes': local_axes, - 'local_bends': local_bends, - 'local_heights': heights, - 'local_helix_directions': local_helix_directions, - 'local_origins': origins, - 'all_bends': bend_matrix, - 'global_axis': helix_axes, - 'local_screw_angles': screw} + screw = local_screw_angles( + helix_axes, np.asarray(ref_axis), local_helix_directions + ) + + results = { + "local_twists": local_twists, + "local_nres_per_turn": local_nres_per_turn, + "local_axes": local_axes, + "local_bends": local_bends, + "local_heights": heights, + "local_helix_directions": local_helix_directions, + "local_origins": origins, + "all_bends": bend_matrix, + "global_axis": helix_axes, + "local_screw_angles": screw, + } return results @@ -377,14 +382,14 @@ class HELANAL(AnalysisBase): # shapes of properties from each frame, relative to n_residues attr_shapes = { - 'local_twists': (-3,), - 'local_bends': (-6,), - 'local_heights': (-3,), - 'local_nres_per_turn': (-3,), - 'local_origins': (-2, 3), - 'local_axes': (-3, 3), - 'local_helix_directions': (-2, 3), - 'local_screw_angles': (-2,), + "local_twists": (-3,), + "local_bends": (-6,), + "local_heights": (-3,), + "local_nres_per_turn": (-3,), + "local_origins": (-2, 3), + "local_axes": (-3, 3), + "local_helix_directions": (-2, 3), + "local_screw_angles": (-2,), } def __init__( @@ -407,9 +412,9 @@ def __init__( groups = util.group_same_or_consecutive_integers(ag.resindices) counter = 0 if len(groups) > 1: - msg = 'Your selection {} has gaps in the residues.'.format(s) + msg = "Your selection {} has gaps in the residues.".format(s) if split_residue_sequences: - msg += ' Splitting into {} helices.'.format(len(groups)) + msg += " Splitting into {} helices.".format(len(groups)) else: groups = [ag.resindices] warnings.warn(msg) @@ -418,22 +423,26 @@ def __init__( ng = len(g) counter += ng if ng < 9: - warnings.warn('Fewer than 9 atoms found for helix in ' - 'selection {} with these resindices: {}. ' - 'This sequence will be skipped. HELANAL ' - 'is designed to work on at sequences of ' - '≥9 residues.'.format(s, g)) + warnings.warn( + "Fewer than 9 atoms found for helix in " + "selection {} with these resindices: {}. " + "This sequence will be skipped. HELANAL " + "is designed to work on at sequences of " + "≥9 residues.".format(s, g) + ) continue ids, counts = np.unique(g, return_counts=True) if np.any(counts > 1): - dup = ', '.join(map(str, ids[counts > 1])) - warnings.warn('Your selection {} includes multiple atoms ' - 'for residues with these resindices: {}.' - 'HELANAL is designed to work on one alpha-' - 'carbon per residue.'.format(s, dup)) + dup = ", ".join(map(str, ids[counts > 1])) + warnings.warn( + "Your selection {} includes multiple atoms " + "for residues with these resindices: {}." + "HELANAL is designed to work on one alpha-" + "carbon per residue.".format(s, dup) + ) - consecutive.append(ag[counter-ng:counter]) + consecutive.append(ag[counter - ng : counter]) self.atomgroups = consecutive self.ref_axis = np.asarray(ref_axis) @@ -442,19 +451,25 @@ def __init__( def _zeros_per_frame(self, dims, n_positions=0): """Create zero arrays where first 2 dims are n_frames, n_values""" first = dims[0] + n_positions - npdims = (self.n_frames, first,) + dims[1:] # py27 workaround + npdims = ( + self.n_frames, + first, + ) + dims[ + 1: + ] # py27 workaround return np.zeros(npdims, dtype=np.float64) def _prepare(self): n_res = [len(ag) for ag in self.atomgroups] for key, dims in self.attr_shapes.items(): - empty = [self._zeros_per_frame( - dims, n_positions=n) for n in n_res] + empty = [self._zeros_per_frame(dims, n_positions=n) for n in n_res] self.results[key] = empty self.results.global_axis = [self._zeros_per_frame((3,)) for n in n_res] - self.results.all_bends = [self._zeros_per_frame((n-3, n-3)) for n in n_res] + self.results.all_bends = [ + self._zeros_per_frame((n - 3, n - 3)) for n in n_res + ] def _single_frame(self): _f = self._frame_index @@ -469,12 +484,13 @@ def _conclude(self): self.results.global_tilts = tilts = [] norm_ref = (self.ref_axis**2).sum() ** 0.5 for axes in self.results.global_axis: - cos = np.matmul(self.ref_axis, axes.T) / \ - (mdamath.pnorm(axes)*norm_ref) + cos = np.matmul(self.ref_axis, axes.T) / ( + mdamath.pnorm(axes) * norm_ref + ) cos = np.clip(cos, -1.0, 1.0) tilts.append(np.rad2deg(np.arccos(cos))) - global_attrs = ['global_axis', 'global_tilts', 'all_bends'] + global_attrs = ["global_axis", "global_tilts", "all_bends"] attrnames = list(self.attr_shapes.keys()) + global_attrs # summarise self.results.summary = [] @@ -483,15 +499,17 @@ def _conclude(self): for name in attrnames: attr = self.results[name] mean = attr[i].mean(axis=0) - dev = np.abs(attr[i]-mean) - stats[name] = {'mean': mean, - 'sample_sd': attr[i].std(axis=0, ddof=1), - 'abs_dev': dev.mean(axis=0)} + dev = np.abs(attr[i] - mean) + stats[name] = { + "mean": mean, + "sample_sd": attr[i].std(axis=0, ddof=1), + "abs_dev": dev.mean(axis=0), + } self.results.summary.append(stats) # flatten? if len(self.atomgroups) == 1 and self._flatten: - for name in attrnames + ['summary']: + for name in attrnames + ["summary"]: attr = self.results[name] self.results[name] = attr[0] @@ -506,7 +524,7 @@ def universe_from_origins(self): try: origins = self.results.local_origins except AttributeError: - raise ValueError('Call run() before universe_from_origins') + raise ValueError("Call run() before universe_from_origins") if not isinstance(origins, list): origins = [origins] @@ -514,9 +532,12 @@ def universe_from_origins(self): universe = [] for xyz in origins: n_res = xyz.shape[1] - u = mda.Universe.empty(n_res, n_residues=n_res, - atom_resindex=np.arange(n_res), - trajectory=True).load_new(xyz) + u = mda.Universe.empty( + n_res, + n_residues=n_res, + atom_resindex=np.arange(n_res), + trajectory=True, + ).load_new(xyz) universe.append(u) if not isinstance(self.results.local_origins, list): universe = universe[0] diff --git a/package/MDAnalysis/analysis/hole2/__init__.py b/package/MDAnalysis/analysis/hole2/__init__.py index d09359f0917..a958b16adf8 100644 --- a/package/MDAnalysis/analysis/hole2/__init__.py +++ b/package/MDAnalysis/analysis/hole2/__init__.py @@ -40,8 +40,10 @@ from mdahole2.analysis import utils, templates from mdahole2.analysis.utils import create_vmd_surface -wmsg = ("Deprecated in version 2.8.0\n" - "MDAnalysis.analysis.hole2 is deprecated in favour of the " - "MDAKit madahole2 (https://www.mdanalysis.org/mdahole2/) " - "and will be removed in MDAnalysis version 3.0.0") +wmsg = ( + "Deprecated in version 2.8.0\n" + "MDAnalysis.analysis.hole2 is deprecated in favour of the " + "MDAKit madahole2 (https://www.mdanalysis.org/mdahole2/) " + "and will be removed in MDAnalysis version 3.0.0" +) warnings.warn(wmsg, category=DeprecationWarning) diff --git a/package/MDAnalysis/analysis/hydrogenbonds/__init__.py b/package/MDAnalysis/analysis/hydrogenbonds/__init__.py index 7bb75ea625f..7297f5b4141 100644 --- a/package/MDAnalysis/analysis/hydrogenbonds/__init__.py +++ b/package/MDAnalysis/analysis/hydrogenbonds/__init__.py @@ -22,9 +22,10 @@ # __all__ = [ - 'HydrogenBondAnalysis', - 'WaterBridgeAnalysis', - 'HydrogenBondAutoCorrel', 'find_hydrogen_donors' + "HydrogenBondAnalysis", + "WaterBridgeAnalysis", + "HydrogenBondAutoCorrel", + "find_hydrogen_donors", ] from .hbond_analysis import HydrogenBondAnalysis diff --git a/package/MDAnalysis/analysis/hydrogenbonds/hbond_autocorrel.py b/package/MDAnalysis/analysis/hydrogenbonds/hbond_autocorrel.py index 51fb1bd19aa..749fe3533c1 100644 --- a/package/MDAnalysis/analysis/hydrogenbonds/hbond_autocorrel.py +++ b/package/MDAnalysis/analysis/hydrogenbonds/hbond_autocorrel.py @@ -214,14 +214,16 @@ from MDAnalysis.core.groups import requires from MDAnalysis.due import due, Doi -due.cite(Doi("10.1063/1.4922445"), - description="Hydrogen bonding autocorrelation time", - path='MDAnalysis.analysis.hydrogenbonds.hbond_autocorrel', + +due.cite( + Doi("10.1063/1.4922445"), + description="Hydrogen bonding autocorrelation time", + path="MDAnalysis.analysis.hydrogenbonds.hbond_autocorrel", ) del Doi -@requires('bonds') +@requires("bonds") def find_hydrogen_donors(hydrogens): """Returns the donor atom for each hydrogen @@ -287,18 +289,24 @@ class HydrogenBondAutoCorrel(object): :attr:`HydrogenBondAutoCorrel.solution['results']` instead. """ - def __init__(self, universe, - hydrogens=None, acceptors=None, donors=None, - bond_type=None, - exclusions=None, - angle_crit=130.0, dist_crit=3.0, # geometric criteria - sample_time=100, # expected length of the decay in ps - time_cut=None, # cutoff time for intermittent hbonds - nruns=1, # number of times to iterate through the trajectory - nsamples=50, # number of different points to sample in a run - pbc=True): - - #warnings.warn("This class is deprecated, use analysis.hbonds.HydrogenBondAnalysis " + def __init__( + self, + universe, + hydrogens=None, + acceptors=None, + donors=None, + bond_type=None, + exclusions=None, + angle_crit=130.0, + dist_crit=3.0, # geometric criteria + sample_time=100, # expected length of the decay in ps + time_cut=None, # cutoff time for intermittent hbonds + nruns=1, # number of times to iterate through the trajectory + nsamples=50, # number of different points to sample in a run + pbc=True, + ): + + # warnings.warn("This class is deprecated, use analysis.hbonds.HydrogenBondAnalysis " # "which has .autocorrelation function", # category=DeprecationWarning) @@ -313,23 +321,27 @@ def __init__(self, universe, self.a = acceptors self.d = donors if not len(self.h) == len(self.d): - raise ValueError("Donors and Hydrogen groups must be identical " - "length. Try using `find_hydrogen_donors`.") + raise ValueError( + "Donors and Hydrogen groups must be identical " + "length. Try using `find_hydrogen_donors`." + ) if exclusions is not None: if len(exclusions[0]) != len(exclusions[1]): raise ValueError( - "'exclusion' must be two arrays of identical length") - self.exclusions = np.column_stack(( - exclusions[0], exclusions[1] - )).astype(np.intp) + "'exclusion' must be two arrays of identical length" + ) + self.exclusions = np.column_stack( + (exclusions[0], exclusions[1]) + ).astype(np.intp) else: self.exclusions = None self.bond_type = bond_type - if self.bond_type not in ['continuous', 'intermittent']: + if self.bond_type not in ["continuous", "intermittent"]: raise ValueError( - "bond_type must be either 'continuous' or 'intermittent'") + "bond_type must be either 'continuous' or 'intermittent'" + ) self.a_crit = np.deg2rad(angle_crit) self.d_crit = dist_crit @@ -341,11 +353,11 @@ def __init__(self, universe, self.time_cut = time_cut self.solution = { - 'results': None, # Raw results - 'time': None, # Time axis of raw results - 'fit': None, # coefficients for fit - 'tau': None, # integral of exponential fit - 'estimate': None # y values of fit against time + "results": None, # Raw results + "time": None, # Time axis of raw results + "fit": None, # coefficients for fit + "tau": None, # integral of exponential fit + "estimate": None, # y values of fit against time } def _slice_traj(self, sample_time): @@ -357,16 +369,22 @@ def _slice_traj(self, sample_time): n_frames = len(self.u.trajectory) if req_frames > n_frames: - warnings.warn("Number of required frames ({}) greater than the" - " number of frames in trajectory ({})" - .format(req_frames, n_frames), RuntimeWarning) + warnings.warn( + "Number of required frames ({}) greater than the" + " number of frames in trajectory ({})".format( + req_frames, n_frames + ), + RuntimeWarning, + ) numruns = self.nruns if numruns > n_frames: numruns = n_frames - warnings.warn("Number of runs ({}) greater than the number of" - " frames in trajectory ({})" - .format(self.nruns, n_frames), RuntimeWarning) + warnings.warn( + "Number of runs ({}) greater than the number of" + " frames in trajectory ({})".format(self.nruns, n_frames), + RuntimeWarning, + ) self._starts = np.arange(0, n_frames, n_frames / numruns, dtype=int) # limit stop points using clip @@ -374,8 +392,12 @@ def _slice_traj(self, sample_time): self._skip = req_frames // self.nsamples if self._skip == 0: # If nsamples > req_frames - warnings.warn("Desired number of sample points too high, using {0}" - .format(req_frames), RuntimeWarning) + warnings.warn( + "Desired number of sample points too high, using {0}".format( + req_frames + ), + RuntimeWarning, + ) self._skip = 1 def run(self, force=False): @@ -387,19 +409,21 @@ def run(self, force=False): Will overwrite previous results if they exist """ # if results exist, don't waste any time - if self.solution['results'] is not None and not force: + if self.solution["results"] is not None and not force: return - main_results = np.zeros_like(np.arange(self._starts[0], - self._stops[0], - self._skip), - dtype=np.float32) + main_results = np.zeros_like( + np.arange(self._starts[0], self._stops[0], self._skip), + dtype=np.float32, + ) # for normalising later counter = np.zeros_like(main_results, dtype=np.float32) - for i, (start, stop) in ProgressBar(enumerate(zip(self._starts, - self._stops)), total=self.nruns, - desc="Performing run"): + for i, (start, stop) in ProgressBar( + enumerate(zip(self._starts, self._stops)), + total=self.nruns, + desc="Performing run", + ): # needed else trj seek thinks a np.int64 isn't an int? results = self._single_run(int(start), int(stop)) @@ -414,10 +438,12 @@ def run(self, force=False): main_results /= counter - self.solution['time'] = np.arange( - len(main_results), - dtype=np.float32) * self.u.trajectory.dt * self._skip - self.solution['results'] = main_results + self.solution["time"] = ( + np.arange(len(main_results), dtype=np.float32) + * self.u.trajectory.dt + * self._skip + ) + self.solution["results"] = main_results def _single_run(self, start, stop): """Perform a single pass of the trajectory""" @@ -427,49 +453,62 @@ def _single_run(self, start, stop): box = self.u.dimensions if self.pbc else None # 2d array of all distances - pair = capped_distance(self.h.positions, self.a.positions, - max_cutoff=self.d_crit, box=box, - return_distances=False) + pair = capped_distance( + self.h.positions, + self.a.positions, + max_cutoff=self.d_crit, + box=box, + return_distances=False, + ) if self.exclusions is not None: - pair = pair[~ _in2d(pair, self.exclusions)] + pair = pair[~_in2d(pair, self.exclusions)] hidx, aidx = np.transpose(pair) - - a = calc_angles(self.d.positions[hidx], self.h.positions[hidx], - self.a.positions[aidx], box=box) + a = calc_angles( + self.d.positions[hidx], + self.h.positions[hidx], + self.a.positions[aidx], + box=box, + ) # from amongst those, who also satisfiess angle crit idx2 = np.where(a > self.a_crit) hidx = hidx[idx2] aidx = aidx[idx2] nbonds = len(hidx) # number of hbonds at t=0 - results = np.zeros_like(np.arange(start, stop, self._skip), - dtype=np.float32) + results = np.zeros_like( + np.arange(start, stop, self._skip), dtype=np.float32 + ) if self.time_cut: # counter for time criteria count = np.zeros(nbonds, dtype=np.float64) - for i, ts in enumerate(self.u.trajectory[start:stop:self._skip]): + for i, ts in enumerate(self.u.trajectory[start : stop : self._skip]): box = self.u.dimensions if self.pbc else None - d = calc_bonds(self.h.positions[hidx], self.a.positions[aidx], - box=box) - a = calc_angles(self.d.positions[hidx], self.h.positions[hidx], - self.a.positions[aidx], box=box) + d = calc_bonds( + self.h.positions[hidx], self.a.positions[aidx], box=box + ) + a = calc_angles( + self.d.positions[hidx], + self.h.positions[hidx], + self.a.positions[aidx], + box=box, + ) winners = (d < self.d_crit) & (a > self.a_crit) results[i] = winners.sum() - if self.bond_type == 'continuous': + if self.bond_type == "continuous": # Remove losers for continuous definition hidx = hidx[np.where(winners)] aidx = aidx[np.where(winners)] - elif self.bond_type == 'intermittent': + elif self.bond_type == "intermittent": if self.time_cut: # Add to counter of where losers are - count[~ winners] += self._skip * self.u.trajectory.dt + count[~winners] += self._skip * self.u.trajectory.dt count[winners] = 0 # Reset timer for winners # Remove if you've lost too many times @@ -521,14 +560,15 @@ def solve(self, p_guess=None): """ - if self.solution['results'] is None: + if self.solution["results"] is None: raise ValueError( - "Results have not been generated use, the run method first") + "Results have not been generated use, the run method first" + ) # Prevents an odd bug with leastsq where it expects # double precision data sometimes... - time = self.solution['time'].astype(np.float64) - results = self.solution['results'].astype(np.float64) + time = self.solution["time"].astype(np.float64) + results = self.solution["results"].astype(np.float64) def within_bounds(p): """Returns True/False if boundary conditions are met or not. @@ -542,13 +582,19 @@ def within_bounds(p): """ if len(p) == 3: A1, tau1, tau2 = p - return (A1 > 0.0) & (A1 < 1.0) & \ - (tau1 > 0.0) & (tau2 > 0.0) + return (A1 > 0.0) & (A1 < 1.0) & (tau1 > 0.0) & (tau2 > 0.0) elif len(p) == 5: A1, A2, tau1, tau2, tau3 = p - return (A1 > 0.0) & (A1 < 1.0) & (A2 > 0.0) & \ - (A2 < 1.0) & ((A1 + A2) < 1.0) & \ - (tau1 > 0.0) & (tau2 > 0.0) & (tau3 > 0.0) + return ( + (A1 > 0.0) + & (A1 < 1.0) + & (A2 > 0.0) + & (A2 < 1.0) + & ((A1 + A2) < 1.0) + & (tau1 > 0.0) + & (tau2 > 0.0) + & (tau3 > 0.0) + ) def err(p, x, y): """Custom residual function, returns real residual if all @@ -561,52 +607,66 @@ def err(p, x, y): return np.full_like(y, 100000) def double(x, A1, tau1, tau2): - """ Sum of two exponential functions """ + """Sum of two exponential functions""" A2 = 1 - A1 return A1 * np.exp(-x / tau1) + A2 * np.exp(-x / tau2) def triple(x, A1, A2, tau1, tau2, tau3): - """ Sum of three exponential functions """ + """Sum of three exponential functions""" A3 = 1 - (A1 + A2) - return A1 * np.exp(-x / tau1) + A2 * np.exp(-x / tau2) + A3 * np.exp(-x / tau3) + return ( + A1 * np.exp(-x / tau1) + + A2 * np.exp(-x / tau2) + + A3 * np.exp(-x / tau3) + ) - if self.bond_type == 'continuous': + if self.bond_type == "continuous": self._my_solve = double if p_guess is None: p_guess = (0.5, 10 * self.sample_time, self.sample_time) p, cov, infodict, mesg, ier = scipy.optimize.leastsq( - err, p_guess, args=(time, results), full_output=True) - self.solution['fit'] = p + err, p_guess, args=(time, results), full_output=True + ) + self.solution["fit"] = p A1, tau1, tau2 = p A2 = 1 - A1 - self.solution['tau'] = A1 * tau1 + A2 * tau2 + self.solution["tau"] = A1 * tau1 + A2 * tau2 else: self._my_solve = triple if p_guess is None: - p_guess = (0.33, 0.33, 10 * self.sample_time, - self.sample_time, 0.1 * self.sample_time) + p_guess = ( + 0.33, + 0.33, + 10 * self.sample_time, + self.sample_time, + 0.1 * self.sample_time, + ) p, cov, infodict, mesg, ier = scipy.optimize.leastsq( - err, p_guess, args=(time, results), full_output=True) - self.solution['fit'] = p + err, p_guess, args=(time, results), full_output=True + ) + self.solution["fit"] = p A1, A2, tau1, tau2, tau3 = p A3 = 1 - A1 - A2 - self.solution['tau'] = A1 * tau1 + A2 * tau2 + A3 * tau3 + self.solution["tau"] = A1 * tau1 + A2 * tau2 + A3 * tau3 - self.solution['infodict'] = infodict - self.solution['mesg'] = mesg - self.solution['ier'] = ier + self.solution["infodict"] = infodict + self.solution["mesg"] = mesg + self.solution["ier"] = ier if ier in [1, 2, 3, 4]: # solution found if ier is one of these values - self.solution['estimate'] = self._my_solve( - self.solution['time'], *p) + self.solution["estimate"] = self._my_solve( + self.solution["time"], *p + ) else: warnings.warn("Solution to results not found", RuntimeWarning) def __repr__(self): - return ("" - "".format(btype=self.bond_type, n=len(self.h))) + return ( + "" + "".format(btype=self.bond_type, n=len(self.h)) + ) diff --git a/package/MDAnalysis/analysis/hydrogenbonds/wbridge_analysis.py b/package/MDAnalysis/analysis/hydrogenbonds/wbridge_analysis.py index 69f281b4f75..b8aef4f4185 100644 --- a/package/MDAnalysis/analysis/hydrogenbonds/wbridge_analysis.py +++ b/package/MDAnalysis/analysis/hydrogenbonds/wbridge_analysis.py @@ -716,7 +716,7 @@ def analysis(current, output, u, **kwargs): from MDAnalysis.lib.distances import capped_distance, calc_angles from MDAnalysis import NoDataError, MissingDataWarning, SelectionError -logger = logging.getLogger('MDAnalysis.analysis.WaterBridgeAnalysis') +logger = logging.getLogger("MDAnalysis.analysis.WaterBridgeAnalysis") class WaterBridgeAnalysis(AnalysisBase): @@ -730,6 +730,7 @@ class WaterBridgeAnalysis(AnalysisBase): .. versionadded:: 0.17.0 """ + # use tuple(set()) here so that one can just copy&paste names from the # table; set() takes care for removing duplicates. At the end the # DEFAULT_DONORS and DEFAULT_ACCEPTORS should simply be tuples. @@ -738,39 +739,91 @@ class WaterBridgeAnalysis(AnalysisBase): #: (see :ref:`Default atom names for water bridge analysis`); #: use the keyword `donors` to add a list of additional donor names. DEFAULT_DONORS = { - 'CHARMM27': tuple( - {'N', 'OH2', 'OW', 'NE', 'NH1', 'NH2', 'ND2', 'SG', 'NE2', 'ND1', - 'NZ', 'OG', 'OG1', 'NE1', 'OH'}), - 'GLYCAM06': tuple({'N', 'NT', 'N3', 'OH', 'OW'}), - 'other': tuple(set([]))} + "CHARMM27": tuple( + { + "N", + "OH2", + "OW", + "NE", + "NH1", + "NH2", + "ND2", + "SG", + "NE2", + "ND1", + "NZ", + "OG", + "OG1", + "NE1", + "OH", + } + ), + "GLYCAM06": tuple({"N", "NT", "N3", "OH", "OW"}), + "other": tuple(set([])), + } #: default atom names that are treated as hydrogen *acceptors* #: (see :ref:`Default atom names for water bridge analysis`); #: use the keyword `acceptors` to add a list of additional acceptor names. DEFAULT_ACCEPTORS = { - 'CHARMM27': tuple( - {'O', 'OC1', 'OC2', 'OH2', 'OW', 'OD1', 'OD2', 'SG', 'OE1', 'OE1', - 'OE2', 'ND1', 'NE2', 'SD', 'OG', 'OG1', 'OH'}), - 'GLYCAM06': - tuple({'N', 'NT', 'O', 'O2', 'OH', 'OS', 'OW', 'OY', 'SM'}), - 'other': tuple(set([]))} + "CHARMM27": tuple( + { + "O", + "OC1", + "OC2", + "OH2", + "OW", + "OD1", + "OD2", + "SG", + "OE1", + "OE1", + "OE2", + "ND1", + "NE2", + "SD", + "OG", + "OG1", + "OH", + } + ), + "GLYCAM06": tuple( + {"N", "NT", "O", "O2", "OH", "OS", "OW", "OY", "SM"} + ), + "other": tuple(set([])), + } #: A :class:`collections.defaultdict` of covalent radii of common donors #: (used in :meth`_get_bonded_hydrogens_list` to check if a hydrogen is #: sufficiently close to its donor heavy atom). Values are stored for #: N, O, P, and S. Any other heavy atoms are assumed to have hydrogens #: covalently bound at a maximum distance of 1.5 Å. - r_cov = defaultdict(lambda: 1.5, # default value - N=1.31, O=1.31, P=1.58, S=1.55) # noqa: E741 - - def __init__(self, universe, selection1='protein', - selection2='not resname SOL', water_selection='resname SOL', - order=1, selection1_type='both', update_selection=False, - update_water_selection=True, filter_first=True, - distance_type='hydrogen', distance=3.0, angle=120.0, - forcefield='CHARMM27', donors=None, acceptors=None, - output_format="sele1_sele2", debug=None, - pbc=False, **kwargs): + r_cov = defaultdict( + lambda: 1.5, N=1.31, O=1.31, P=1.58, S=1.55 # default value + ) # noqa: E741 + + def __init__( + self, + universe, + selection1="protein", + selection2="not resname SOL", + water_selection="resname SOL", + order=1, + selection1_type="both", + update_selection=False, + update_water_selection=True, + filter_first=True, + distance_type="hydrogen", + distance=3.0, + angle=120.0, + forcefield="CHARMM27", + donors=None, + acceptors=None, + output_format="sele1_sele2", + debug=None, + pbc=False, + **kwargs, + ): """Set up the calculation of water bridges between two selections in a universe. @@ -904,8 +957,9 @@ def __init__(self, universe, selection1='protein', """ - super(WaterBridgeAnalysis, self).__init__(universe.trajectory, - **kwargs) + super(WaterBridgeAnalysis, self).__init__( + universe.trajectory, **kwargs + ) self.water_selection = water_selection self.update_water_selection = update_water_selection # per-frame debugging output? @@ -929,7 +983,9 @@ def __init__(self, universe, selection1='protein', self.filter_first = filter_first self.distance = distance if distance_type not in {"hydrogen", "heavy"}: - raise ValueError(f"Only 'hydrogen' and 'heavy' are allowed for option `distance_type' ({distance_type}).") + raise ValueError( + f"Only 'hydrogen' and 'heavy' are allowed for option `distance_type' ({distance_type})." + ) self.distance_type = distance_type # will give the default behavior self.angle = angle @@ -943,13 +999,15 @@ def __init__(self, universe, selection1='protein', acceptors = () self.forcefield = forcefield self.donors = tuple(set(self.DEFAULT_DONORS[forcefield]).union(donors)) - self.acceptors = tuple(set(self.DEFAULT_ACCEPTORS[forcefield]).union( - acceptors)) + self.acceptors = tuple( + set(self.DEFAULT_ACCEPTORS[forcefield]).union(acceptors) + ) - if self.selection1_type not in ('both', 'donor', 'acceptor'): - raise ValueError('WaterBridgeAnalysis: ' - 'Invalid selection type {0!s}'.format( - self.selection1_type)) + if self.selection1_type not in ("both", "donor", "acceptor"): + raise ValueError( + "WaterBridgeAnalysis: " + "Invalid selection type {0!s}".format(self.selection1_type) + ) # final result accessed as self.results.network self.results.network = [] @@ -960,18 +1018,31 @@ def __init__(self, universe, selection1='protein', def _log_parameters(self): """Log important parameters to the logfile.""" - logger.info("WaterBridgeAnalysis: selection = %r (update: %r)", - self.selection2, self.update_selection) - logger.info("WaterBridgeAnalysis: water selection = %r (update: %r)", - self.water_selection, self.update_water_selection) - logger.info("WaterBridgeAnalysis: criterion: donor %s atom and " - "acceptor atom distance <= %.3f A", self.distance_type, - self.distance) - logger.info("WaterBridgeAnalysis: criterion: " - "angle D-H-A >= %.3f degrees", - self.angle) - logger.info("WaterBridgeAnalysis: force field %s to guess donor and \ - acceptor names", self.forcefield) + logger.info( + "WaterBridgeAnalysis: selection = %r (update: %r)", + self.selection2, + self.update_selection, + ) + logger.info( + "WaterBridgeAnalysis: water selection = %r (update: %r)", + self.water_selection, + self.update_water_selection, + ) + logger.info( + "WaterBridgeAnalysis: criterion: donor %s atom and " + "acceptor atom distance <= %.3f A", + self.distance_type, + self.distance, + ) + logger.info( + "WaterBridgeAnalysis: criterion: " "angle D-H-A >= %.3f degrees", + self.angle, + ) + logger.info( + "WaterBridgeAnalysis: force field %s to guess donor and \ + acceptor names", + self.forcefield, + ) def _build_residue_dict(self, selection): # Build the residue_dict where the key is the residue name @@ -985,15 +1056,17 @@ def _build_residue_dict(self, selection): for atom in residue.atoms: if atom.name in self.donors: self._residue_dict[residue.resname][atom.name].update( - self._get_bonded_hydrogens(atom).names) + self._get_bonded_hydrogens(atom).names + ) def _update_donor_h(self, atom_ix, h_donors, donors_h): atom = self.u.atoms[atom_ix] residue = atom.residue hydrogen_names = self._residue_dict[residue.resname][atom.name] if hydrogen_names: - hydrogens = residue.atoms.select_atoms('name {0}'.format( - ' '.join(hydrogen_names))).ix + hydrogens = residue.atoms.select_atoms( + "name {0}".format(" ".join(hydrogen_names)) + ).ix for atom in hydrogens: h_donors[atom] = atom_ix donors_h[atom_ix].append(atom) @@ -1013,68 +1086,108 @@ def _update_selection(self): self._s2 = self.u.select_atoms(self.selection2).ix if self.filter_first and len(self._s1): - self.logger_debug('Size of selection 1 before filtering:' - ' {} atoms'.format(len(self._s1))) - ns_selection_1 = AtomNeighborSearch(self.u.atoms[self._s1], - box=self.box) - self._s1 = ns_selection_1.search(self.u.atoms[self._s2], - self.selection_distance).ix - self.logger_debug("Size of selection 1: {0} atoms".format( - len(self._s1))) + self.logger_debug( + "Size of selection 1 before filtering:" + " {} atoms".format(len(self._s1)) + ) + ns_selection_1 = AtomNeighborSearch( + self.u.atoms[self._s1], box=self.box + ) + self._s1 = ns_selection_1.search( + self.u.atoms[self._s2], self.selection_distance + ).ix + self.logger_debug( + "Size of selection 1: {0} atoms".format(len(self._s1)) + ) if len(self._s1) == 0: - logger.warning('Selection 1 "{0}" did not select any atoms.' - .format(str(self.selection1)[:80])) + logger.warning( + 'Selection 1 "{0}" did not select any atoms.'.format( + str(self.selection1)[:80] + ) + ) return if self.filter_first and len(self._s2): - self.logger_debug('Size of selection 2 before filtering:' - ' {} atoms'.format(len(self._s2))) - ns_selection_2 = AtomNeighborSearch(self.u.atoms[self._s2], - box=self.box) - self._s2 = ns_selection_2.search(self.u.atoms[self._s1], - self.selection_distance).ix - self.logger_debug('Size of selection 2: {0} atoms'.format( - len(self._s2))) + self.logger_debug( + "Size of selection 2 before filtering:" + " {} atoms".format(len(self._s2)) + ) + ns_selection_2 = AtomNeighborSearch( + self.u.atoms[self._s2], box=self.box + ) + self._s2 = ns_selection_2.search( + self.u.atoms[self._s1], self.selection_distance + ).ix + self.logger_debug( + "Size of selection 2: {0} atoms".format(len(self._s2)) + ) if len(self._s2) == 0: - logger.warning('Selection 2 "{0}" did not select any atoms.' - .format(str(self.selection2)[:80])) + logger.warning( + 'Selection 2 "{0}" did not select any atoms.'.format( + str(self.selection2)[:80] + ) + ) return - if self.selection1_type in ('donor', 'both'): - self._s1_donors = self.u.atoms[self._s1].select_atoms( - 'name {0}'.format(' '.join(self.donors))).ix + if self.selection1_type in ("donor", "both"): + self._s1_donors = ( + self.u.atoms[self._s1] + .select_atoms("name {0}".format(" ".join(self.donors))) + .ix + ) for atom_ix in self._s1_donors: - self._update_donor_h(atom_ix, self._s1_h_donors, - self._s1_donors_h) - self.logger_debug("Selection 1 donors: {0}".format( - len(self._s1_donors))) - self.logger_debug("Selection 1 donor hydrogens: {0}".format( - len(self._s1_h_donors))) - if self.selection1_type in ('acceptor', 'both'): - self._s1_acceptors = self.u.atoms[self._s1].select_atoms( - 'name {0}'.format(' '.join(self.acceptors))).ix - self.logger_debug("Selection 1 acceptors: {0}".format( - len(self._s1_acceptors))) + self._update_donor_h( + atom_ix, self._s1_h_donors, self._s1_donors_h + ) + self.logger_debug( + "Selection 1 donors: {0}".format(len(self._s1_donors)) + ) + self.logger_debug( + "Selection 1 donor hydrogens: {0}".format( + len(self._s1_h_donors) + ) + ) + if self.selection1_type in ("acceptor", "both"): + self._s1_acceptors = ( + self.u.atoms[self._s1] + .select_atoms("name {0}".format(" ".join(self.acceptors))) + .ix + ) + self.logger_debug( + "Selection 1 acceptors: {0}".format(len(self._s1_acceptors)) + ) if len(self._s2) == 0: return None - if self.selection1_type in ('donor', 'both'): - self._s2_acceptors = self.u.atoms[self._s2].select_atoms( - 'name {0}'.format(' '.join(self.acceptors))).ix - self.logger_debug("Selection 2 acceptors: {0:d}".format( - len(self._s2_acceptors))) - if self.selection1_type in ('acceptor', 'both'): - self._s2_donors = self.u.atoms[self._s2].select_atoms( - 'name {0}'.format(' '.join(self.donors))).ix + if self.selection1_type in ("donor", "both"): + self._s2_acceptors = ( + self.u.atoms[self._s2] + .select_atoms("name {0}".format(" ".join(self.acceptors))) + .ix + ) + self.logger_debug( + "Selection 2 acceptors: {0:d}".format(len(self._s2_acceptors)) + ) + if self.selection1_type in ("acceptor", "both"): + self._s2_donors = ( + self.u.atoms[self._s2] + .select_atoms("name {0}".format(" ".join(self.donors))) + .ix + ) for atom_ix in self._s2_donors: - self._update_donor_h(atom_ix, self._s2_h_donors, - self._s2_donors_h) - self.logger_debug("Selection 2 donors: {0:d}".format( - len(self._s2_donors))) - self.logger_debug("Selection 2 donor hydrogens: {0:d}".format( - len(self._s2_h_donors))) + self._update_donor_h( + atom_ix, self._s2_h_donors, self._s2_donors_h + ) + self.logger_debug( + "Selection 2 donors: {0:d}".format(len(self._s2_donors)) + ) + self.logger_debug( + "Selection 2 donor hydrogens: {0:d}".format( + len(self._s2_h_donors) + ) + ) def _update_water_selection(self): self._water_donors = [] @@ -1083,37 +1196,55 @@ def _update_water_selection(self): self._water_acceptors = [] self._water = self.u.select_atoms(self.water_selection).ix - self.logger_debug('Size of water selection before filtering:' - ' {} atoms'.format(len(self._water))) + self.logger_debug( + "Size of water selection before filtering:" + " {} atoms".format(len(self._water)) + ) if len(self._water) and self.filter_first: - filtered_s1 = AtomNeighborSearch(self.u.atoms[self._water], - box=self.box).search( - self.u.atoms[self._s1], self.selection_distance) + filtered_s1 = AtomNeighborSearch( + self.u.atoms[self._water], box=self.box + ).search(self.u.atoms[self._s1], self.selection_distance) if filtered_s1: - self._water = AtomNeighborSearch(filtered_s1, - box=self.box).search( - self.u.atoms[self._s2], self.selection_distance).ix - - self.logger_debug("Size of water selection: {0} atoms".format( - len(self._water))) + self._water = ( + AtomNeighborSearch(filtered_s1, box=self.box) + .search(self.u.atoms[self._s2], self.selection_distance) + .ix + ) + + self.logger_debug( + "Size of water selection: {0} atoms".format(len(self._water)) + ) if len(self._water) == 0: - logger.warning("Water selection '{0}' did not select any atoms." - .format(str(self.water_selection)[:80])) + logger.warning( + "Water selection '{0}' did not select any atoms.".format( + str(self.water_selection)[:80] + ) + ) else: - self._water_donors = self.u.atoms[self._water].select_atoms( - 'name {0}'.format(' '.join(self.donors))).ix + self._water_donors = ( + self.u.atoms[self._water] + .select_atoms("name {0}".format(" ".join(self.donors))) + .ix + ) for atom_ix in self._water_donors: - self._update_donor_h(atom_ix, self._water_h_donors, - self._water_donors_h) - self.logger_debug("Water donors: {0}".format( - len(self._water_donors))) - self.logger_debug("Water donor hydrogens: {0}".format( - len(self._water_h_donors))) - self._water_acceptors = self.u.atoms[self._water].select_atoms( - 'name {0}'.format(' '.join(self.acceptors))).ix - self.logger_debug("Water acceptors: {0}".format( - len(self._water_acceptors))) + self._update_donor_h( + atom_ix, self._water_h_donors, self._water_donors_h + ) + self.logger_debug( + "Water donors: {0}".format(len(self._water_donors)) + ) + self.logger_debug( + "Water donor hydrogens: {0}".format(len(self._water_h_donors)) + ) + self._water_acceptors = ( + self.u.atoms[self._water] + .select_atoms("name {0}".format(" ".join(self.acceptors))) + .ix + ) + self.logger_debug( + "Water acceptors: {0}".format(len(self._water_acceptors)) + ) def _get_bonded_hydrogens(self, atom): """Find hydrogens bonded within cutoff to `atom`. @@ -1145,7 +1276,8 @@ def _get_bonded_hydrogens(self, atom): try: return atom.residue.atoms.select_atoms( "(name H* 1H* 2H* 3H* or type H) and around {0:f} name {1!s}" - "".format(self.r_cov[atom.name[0]], atom.name)) + "".format(self.r_cov[atom.name[0]], atom.name) + ) except NoDataError: return [] @@ -1157,7 +1289,7 @@ def _prepare(self): # The distance for selection is defined as twice the maximum bond # length of an O-H bond (2A) plus order of water bridge times the # length of OH bond plus hydrogne bond distance - self.selection_distance = (2 * 2 + self.order * (2 + self.distance)) + self.selection_distance = 2 * 2 + self.order * (2 + self.distance) self.box = self.u.dimensions if self.pbc else None self._residue_dict = {} @@ -1171,42 +1303,51 @@ def _prepare(self): if len(self._s1) and len(self._s2): self._update_water_selection() else: - logger.info("WaterBridgeAnalysis: " - "no atoms found in the selection.") + logger.info( + "WaterBridgeAnalysis: " "no atoms found in the selection." + ) logger.info("WaterBridgeAnalysis: initial checks passed.") logger.info("WaterBridgeAnalysis: starting") logger.debug("WaterBridgeAnalysis: donors %r", self.donors) logger.debug("WaterBridgeAnalysis: acceptors %r", self.acceptors) - logger.debug("WaterBridgeAnalysis: water bridge %r", - self.water_selection) + logger.debug( + "WaterBridgeAnalysis: water bridge %r", self.water_selection + ) if self.debug: logger.debug("Toggling debug to %r", self.debug) else: - logger.debug("WaterBridgeAnalysis: For full step-by-step " - "debugging output use debug=True") + logger.debug( + "WaterBridgeAnalysis: For full step-by-step " + "debugging output use debug=True" + ) - logger.info("Starting analysis " - "(frame index start=%d stop=%d, step=%d)", - self.start, self.stop, self.step) + logger.info( + "Starting analysis " "(frame index start=%d stop=%d, step=%d)", + self.start, + self.stop, + self.step, + ) def _donor2acceptor(self, donors, h_donors, acceptor): if len(donors) == 0 or len(acceptor) == 0: return [] - if self.distance_type != 'heavy': + if self.distance_type != "heavy": donors_idx = list(h_donors.keys()) else: donors_idx = list(donors.keys()) result = [] # Code modified from p-j-smith - pairs, distances = capped_distance(self.u.atoms[donors_idx].positions, - self.u.atoms[acceptor].positions, - max_cutoff=self.distance, - box=self.box, - return_distances=True) - if self.distance_type == 'hydrogen': + pairs, distances = capped_distance( + self.u.atoms[donors_idx].positions, + self.u.atoms[acceptor].positions, + max_cutoff=self.distance, + box=self.box, + return_distances=True, + ) + if self.distance_type == "hydrogen": tmp_distances = distances tmp_donors = [h_donors[donors_idx[idx]] for idx in pairs[:, 0]] tmp_hydrogens = [donors_idx[idx] for idx in pairs[:, 0]] @@ -1231,7 +1372,7 @@ def _donor2acceptor(self, donors, h_donors, acceptor): self.u.atoms[tmp_donors].positions, self.u.atoms[tmp_hydrogens].positions, self.u.atoms[tmp_acceptors].positions, - box=self.box + box=self.box, ) ) hbond_indices = np.where(angles > self.angle)[0] @@ -1239,9 +1380,17 @@ def _donor2acceptor(self, donors, h_donors, acceptor): h = tmp_hydrogens[index] d = tmp_donors[index] a = tmp_acceptors[index] - result.append((h, d, a, self._expand_index(h), - self._expand_index(a), - tmp_distances[index], angles[index])) + result.append( + ( + h, + d, + a, + self._expand_index(h), + self._expand_index(a), + tmp_distances[index], + angles[index], + ) + ) return result def _single_frame(self): @@ -1262,87 +1411,151 @@ def _single_frame(self): next_round_water = set([]) selection_2 = [] - if self.selection1_type in ('donor', 'both'): + if self.selection1_type in ("donor", "both"): # check for direct hbond from s1 to s2 self.logger_debug("Selection 1 Donors <-> Selection 2 Acceptors") results = self._donor2acceptor( - self._s1_donors_h, self._s1_h_donors, self._s2_acceptors) + self._s1_donors_h, self._s1_h_donors, self._s2_acceptors + ) for line in results: - h_index, d_index, a_index, (h_resname, h_resid, h_name), \ - (a_resname, a_resid, a_name), dist, angle = line + ( + h_index, + d_index, + a_index, + (h_resname, h_resid, h_name), + (a_resname, a_resid, a_name), + dist, + angle, + ) = line water_pool[(a_resname, a_resid)] = None selection_1.append( - (h_index, d_index, a_index, None, dist, angle)) + (h_index, d_index, a_index, None, dist, angle) + ) selection_2.append((a_resname, a_resid)) if self.order > 0: self.logger_debug("Selection 1 Donors <-> Water Acceptors") results = self._donor2acceptor( - self._s1_donors_h, self._s1_h_donors, - self._water_acceptors) + self._s1_donors_h, self._s1_h_donors, self._water_acceptors + ) for line in results: - h_index, d_index, a_index, (h_resname, h_resid, h_name), ( - a_resname, a_resid, a_name), dist, angle = line + ( + h_index, + d_index, + a_index, + (h_resname, h_resid, h_name), + (a_resname, a_resid, a_name), + dist, + angle, + ) = line selection_1.append( - (h_index, d_index, a_index, None, dist, angle)) + (h_index, d_index, a_index, None, dist, angle) + ) self.logger_debug("Water Donors <-> Selection 2 Acceptors") results = self._donor2acceptor( - self._water_donors_h, self._water_h_donors, - self._s2_acceptors) + self._water_donors_h, + self._water_h_donors, + self._s2_acceptors, + ) for line in results: - h_index, d_index, a_index, (h_resname, h_resid, h_name), ( - a_resname, a_resid, a_name), dist, angle = line + ( + h_index, + d_index, + a_index, + (h_resname, h_resid, h_name), + (a_resname, a_resid, a_name), + dist, + angle, + ) = line water_pool[(h_resname, h_resid)].append( - (h_index, d_index, a_index, None, dist, angle)) + (h_index, d_index, a_index, None, dist, angle) + ) selection_2.append((a_resname, a_resid)) - if self.selection1_type in ('acceptor', 'both'): + if self.selection1_type in ("acceptor", "both"): self.logger_debug("Selection 2 Donors <-> Selection 1 Acceptors") - results = self._donor2acceptor(self._s2_donors_h, - self._s2_h_donors, - self._s1_acceptors) + results = self._donor2acceptor( + self._s2_donors_h, self._s2_h_donors, self._s1_acceptors + ) for line in results: - h_index, d_index, a_index, (h_resname, h_resid, h_name), \ - (a_resname, a_resid, a_name), dist, angle = line + ( + h_index, + d_index, + a_index, + (h_resname, h_resid, h_name), + (a_resname, a_resid, a_name), + dist, + angle, + ) = line water_pool[(h_resname, h_resid)] = None selection_1.append( - (a_index, None, h_index, d_index, dist, angle)) + (a_index, None, h_index, d_index, dist, angle) + ) selection_2.append((h_resname, h_resid)) if self.order > 0: self.logger_debug("Selection 2 Donors <-> Water Acceptors") results = self._donor2acceptor( - self._s2_donors_h, self._s2_h_donors, - self._water_acceptors) + self._s2_donors_h, self._s2_h_donors, self._water_acceptors + ) for line in results: - h_index, d_index, a_index, (h_resname, h_resid, h_name), ( - a_resname, a_resid, a_name), dist, angle = line + ( + h_index, + d_index, + a_index, + (h_resname, h_resid, h_name), + (a_resname, a_resid, a_name), + dist, + angle, + ) = line water_pool[(a_resname, a_resid)].append( - (a_index, None, h_index, d_index, dist, angle)) + (a_index, None, h_index, d_index, dist, angle) + ) selection_2.append((h_resname, h_resid)) self.logger_debug("Selection 1 Acceptors <-> Water Donors") results = self._donor2acceptor( - self._water_donors_h, self._water_h_donors, - self._s1_acceptors) + self._water_donors_h, + self._water_h_donors, + self._s1_acceptors, + ) for line in results: - h_index, d_index, a_index, (h_resname, h_resid, h_name), ( - a_resname, a_resid, a_name), dist, angle = line + ( + h_index, + d_index, + a_index, + (h_resname, h_resid, h_name), + (a_resname, a_resid, a_name), + dist, + angle, + ) = line selection_1.append( - (a_index, None, h_index, d_index, dist, angle)) + (a_index, None, h_index, d_index, dist, angle) + ) if self.order > 1: self.logger_debug("Water donor <-> Water Acceptors") - results = self._donor2acceptor(self._water_donors_h, - self._water_h_donors, - self._water_acceptors) + results = self._donor2acceptor( + self._water_donors_h, + self._water_h_donors, + self._water_acceptors, + ) for line in results: - h_index, d_index, a_index, (h_resname, h_resid, h_name), ( - a_resname, a_resid, a_name), dist, angle = line + ( + h_index, + d_index, + a_index, + (h_resname, h_resid, h_name), + (a_resname, a_resid, a_name), + dist, + angle, + ) = line water_pool[(a_resname, a_resid)].append( - (a_index, None, h_index, d_index, dist, angle)) + (a_index, None, h_index, d_index, dist, angle) + ) water_pool[(h_resname, h_resid)].append( - (h_index, d_index, a_index, None, dist, angle)) + (h_index, d_index, a_index, None, dist, angle) + ) # solve the connectivity network # The following code attempt to generate a water network which is @@ -1362,24 +1575,25 @@ def _single_frame(self): # If the value of a certain key is None, which means it is reaching # selection 2. - result = {'start': defaultdict(dict), 'water': defaultdict(dict)} + result = {"start": defaultdict(dict), "water": defaultdict(dict)} def add_route(result, route): if len(route) == 1: - result['start'][route[0]] = None + result["start"][route[0]] = None else: # exclude the the selection which goes back to itself - if (sorted(route[0][0:3:2]) == sorted(route[-1][0:3:2])): + if sorted(route[0][0:3:2]) == sorted(route[-1][0:3:2]): return # selection 2 to water - result['water'][route[-1]] = None + result["water"][route[-1]] = None # water to water for i in range(1, len(route) - 1): - result['water'][route[i]][route[i+1]] = \ - result['water'][route[i+1]] + result["water"][route[i]][route[i + 1]] = result["water"][ + route[i + 1] + ] # selection 1 to water - result['start'][route[0]][route[1]] = result['water'][route[1]] + result["start"][route[0]][route[1]] = result["water"][route[1]] def traverse_water_network(graph, node, end, route, maxdepth, result): if len(route) > self.order + 1: @@ -1395,20 +1609,33 @@ def traverse_water_network(graph, node, end, route, maxdepth, result): new_route = route[:] new_route.append(new_node) new_node = self._expand_timeseries( - new_node, 'sele1_sele2')[3][:2] - traverse_water_network(graph, new_node, end, new_route, - maxdepth, result) - for s1 in selection_1: - route = [s1, ] - next_mol = self._expand_timeseries(s1, 'sele1_sele2')[3][:2] - traverse_water_network(water_pool, next_mol, selection_2, route[:], - self.order, result) + new_node, "sele1_sele2" + )[3][:2] + traverse_water_network( + graph, new_node, end, new_route, maxdepth, result + ) - self.results.network.append(result['start']) + for s1 in selection_1: + route = [ + s1, + ] + next_mol = self._expand_timeseries(s1, "sele1_sele2")[3][:2] + traverse_water_network( + water_pool, next_mol, selection_2, route[:], self.order, result + ) - def _traverse_water_network(self, graph, current, analysis_func=None, - output=None, link_func=None, **kwargs): - ''' + self.results.network.append(result["start"]) + + def _traverse_water_network( + self, + graph, + current, + analysis_func=None, + output=None, + link_func=None, + **kwargs, + ): + """ This function recursively traverses the water network self.results.network and finds the hydrogen bonds which connect the current atom to the next atom. The newly found hydrogen bond will be @@ -1426,7 +1653,7 @@ def _traverse_water_network(self, graph, current, analysis_func=None, :param link_func: The new hydrogen bonds will be appended to current. :param kwargs: the keywords which are passed into the analysis_func. :return: - ''' + """ if link_func is None: # If no link_func is provided, the default link_func will be used link_func = self._full_link @@ -1443,19 +1670,24 @@ def _traverse_water_network(self, graph, current, analysis_func=None, for node in graph: # the new hydrogen bond will be added to the existing bonds new = link_func(current, node) - self._traverse_water_network(graph[node], new, - analysis_func, output, - link_func, **kwargs) + self._traverse_water_network( + graph[node], + new, + analysis_func, + output, + link_func, + **kwargs, + ) def _expand_index(self, index): - ''' + """ Expand the index into (resname, resid, name). - ''' + """ atom = self.u.atoms[index] return (atom.resname, atom.resid, atom.name) def _expand_timeseries(self, entry, output_format=None): - ''' + """ Expand the compact data format into the old timeseries form. The old is defined as the format for release up to 0.19.2. As is discussed in Issue #2177, the internal storage of the hydrogen @@ -1479,17 +1711,17 @@ def _expand_timeseries(self, entry, output_format=None): [donor_index, acceptor_index, (donor_resname, donor_resid, donor_name), (acceptor_resname, acceptor_resid, acceptor_name), dist, angle] - ''' + """ output_format = output_format or self.output_format # Expand the compact entry into atom1, which is the first index in the # output and atom2, which is the second # entry. atom1, heavy_atom1, atom2, heavy_atom2, dist, angle = entry - if output_format == 'sele1_sele2': + if output_format == "sele1_sele2": # If the output format is the sele1_sele2, no change will be # executed atom1, atom2 = atom1, atom2 - elif output_format == 'donor_acceptor': + elif output_format == "donor_acceptor": # If the output format is donor_acceptor, use heavy atom position # to check which is donor and which is # acceptor @@ -1503,13 +1735,20 @@ def _expand_timeseries(self, entry, output_format=None): else: raise KeyError( "Only 'sele1_sele2' or 'donor_acceptor' are allowed as output " - "format") + "format" + ) - return (atom1, atom2, self._expand_index(atom1), - self._expand_index(atom2), dist, angle) + return ( + atom1, + atom2, + self._expand_index(atom1), + self._expand_index(atom2), + dist, + angle, + ) def _generate_timeseries(self, output_format=None): - r'''Time series of water bridges. + r"""Time series of water bridges. The output is generated per frame as is explained in :ref:`wb_Analysis_Timeseries`. The format of output can be changed via @@ -1529,7 +1768,7 @@ def _generate_timeseries(self, output_format=None): (resname string, resid, name_string). - ''' + """ output_format = output_format or self.output_format def analysis(current, output, *args, **kwargs): @@ -1538,40 +1777,47 @@ def analysis(current, output, *args, **kwargs): timeseries = [] for frame in self.results.network: new_frame = [] - self._traverse_water_network(frame, new_frame, - analysis_func=analysis, - output=new_frame, - link_func=self._compact_link) - timeseries.append([ - self._expand_timeseries(entry, output_format) - for entry in new_frame]) + self._traverse_water_network( + frame, + new_frame, + analysis_func=analysis, + output=new_frame, + link_func=self._compact_link, + ) + timeseries.append( + [ + self._expand_timeseries(entry, output_format) + for entry in new_frame + ] + ) return timeseries - def set_network(self, network): - wmsg = ("The `set_network` method was deprecated in MDAnalysis 2.0.0 " - "and will be removed in MDAnalysis 3.0.0. Please use " - "`results.network` instead") + wmsg = ( + "The `set_network` method was deprecated in MDAnalysis 2.0.0 " + "and will be removed in MDAnalysis 3.0.0. Please use " + "`results.network` instead" + ) warnings.warn(wmsg, DeprecationWarning) self.results.network = network @classmethod def _full_link(self, output, node): - ''' + """ A function used in _traverse_water_network to add the new hydrogen bond to the existing bonds. :param output: The existing hydrogen bonds from selection 1 :param node: The new hydrogen bond :return: The hydrogen bonds from selection 1 with the new hydrogen bond added - ''' + """ result = output[:] result.append(node) return result @classmethod def _compact_link(self, output, node): - ''' + """ A function used in _traverse_water_network to add the new hydrogen bond to the existing bonds. In this form no new list is created and thus, one bridge will only appear once. @@ -1579,24 +1825,34 @@ def _compact_link(self, output, node): :param node: The new hydrogen bond :return: The hydrogen bonds from selection 1 with the new hydrogen bond added - ''' + """ output.append(node) return output def _count_by_type_analysis(self, current, output, *args, **kwargs): - ''' + """ Generates the key for count_by_type analysis. :return: - ''' + """ - s1_index, to_index, s1, to_residue, dist, angle = \ + s1_index, to_index, s1, to_residue, dist, angle = ( self._expand_timeseries(current[0]) + ) s1_resname, s1_resid, s1_name = s1 - from_index, s2_index, from_residue, s2, dist, angle = \ + from_index, s2_index, from_residue, s2, dist, angle = ( self._expand_timeseries(current[-1]) + ) s2_resname, s2_resid, s2_name = s2 - key = (s1_index, s2_index, - s1_resname, s1_resid, s1_name, s2_resname, s2_resid, s2_name) + key = ( + s1_index, + s2_index, + s1_resname, + s1_resid, + s1_name, + s2_resname, + s2_resid, + s2_name, + ) output[key] += 1 def count_by_type(self, analysis_func=None, **kwargs): @@ -1625,41 +1881,57 @@ def count_by_type(self, analysis_func=None, **kwargs): output = None if analysis_func is None: analysis_func = self._count_by_type_analysis - output = 'combined' + output = "combined" if self.results.network: length = len(self.results.network) result_dict = defaultdict(int) for frame in self.results.network: frame_dict = defaultdict(int) - self._traverse_water_network(frame, [], - analysis_func=analysis_func, - output=frame_dict, - link_func=self._full_link, - **kwargs) + self._traverse_water_network( + frame, + [], + analysis_func=analysis_func, + output=frame_dict, + link_func=self._full_link, + **kwargs, + ) for key, value in frame_dict.items(): result_dict[key] += frame_dict[key] - if output == 'combined': + if output == "combined": result = [[i for i in key] for key in result_dict] - [result[i].append(result_dict[key]/length) - for i, key in enumerate(result_dict)] + [ + result[i].append(result_dict[key] / length) + for i, key in enumerate(result_dict) + ] else: - result = [(key, - result_dict[key]/length) for key in result_dict] + result = [ + (key, result_dict[key] / length) for key in result_dict + ] return result else: return None def _count_by_time_analysis(self, current, output, *args, **kwargs): - s1_index, to_index, s1, to_residue, dist, angle = \ + s1_index, to_index, s1, to_residue, dist, angle = ( self._expand_timeseries(current[0]) + ) s1_resname, s1_resid, s1_name = s1 - from_index, s2_index, from_residue, s2, dist, angle = \ + from_index, s2_index, from_residue, s2, dist, angle = ( self._expand_timeseries(current[-1]) + ) s2_resname, s2_resid, s2_name = s2 - key = (s1_index, s2_index, - s1_resname, s1_resid, s1_name, s2_resname, s2_resid, s2_name) + key = ( + s1_index, + s2_index, + s1_resname, + s1_resid, + s1_name, + s2_resname, + s2_resid, + s2_name, + ) output[key] += 1 def count_by_time(self, analysis_func=None, **kwargs): @@ -1681,27 +1953,41 @@ def count_by_time(self, analysis_func=None, **kwargs): result = [] for time, frame in zip(self.timesteps, self.results.network): result_dict = defaultdict(int) - self._traverse_water_network(frame, [], - analysis_func=analysis_func, - output=result_dict, - link_func=self._full_link, - **kwargs) - result.append((time, - sum([result_dict[key] for key in result_dict]))) + self._traverse_water_network( + frame, + [], + analysis_func=analysis_func, + output=result_dict, + link_func=self._full_link, + **kwargs, + ) + result.append( + (time, sum([result_dict[key] for key in result_dict])) + ) return result else: return None def _timesteps_by_type_analysis(self, current, output, *args, **kwargs): - s1_index, to_index, s1, to_residue, dist, angle = \ + s1_index, to_index, s1, to_residue, dist, angle = ( self._expand_timeseries(current[0]) + ) s1_resname, s1_resid, s1_name = s1 - from_index, s2_index, from_residue, s2, dist, angle = \ + from_index, s2_index, from_residue, s2, dist, angle = ( self._expand_timeseries(current[-1]) + ) s2_resname, s2_resid, s2_name = s2 - key = (s1_index, s2_index, s1_resname, s1_resid, s1_name, s2_resname, - s2_resid, s2_name) - output[key].append(kwargs.pop('time')) + key = ( + s1_index, + s2_index, + s1_resname, + s1_resid, + s1_name, + s2_resname, + s2_resid, + s2_name, + ) + output[key].append(kwargs.pop("time")) def timesteps_by_type(self, analysis_func=None, **kwargs): """Frames during which each water bridges existed, sorted by each water @@ -1724,7 +2010,7 @@ def timesteps_by_type(self, analysis_func=None, **kwargs): output = None if analysis_func is None: analysis_func = self._timesteps_by_type_analysis - output = 'combined' + output = "combined" if self.results.network: result = defaultdict(list) @@ -1733,16 +2019,20 @@ def timesteps_by_type(self, analysis_func=None, **kwargs): else: timesteps = self.timesteps for time, frame in zip(timesteps, self.results.network): - self._traverse_water_network(frame, [], - analysis_func=analysis_func, - output=result, - link_func=self._full_link, - time=time, **kwargs) + self._traverse_water_network( + frame, + [], + analysis_func=analysis_func, + output=result, + link_func=self._full_link, + time=time, + **kwargs, + ) result_list = [] for key, time_list in result.items(): for time in time_list: - if output == 'combined': + if output == "combined": key = list(key) key.append(time) result_list.append(key) @@ -1783,8 +2073,10 @@ def generate_table(self, output_format=None): logger.warning(msg) return None - if self.results.timeseries is not None \ - and output_format == self.output_format: + if ( + self.results.timeseries is not None + and output_format == self.output_format + ): timeseries = self.results.timeseries else: # Recompute timeseries with correct output format @@ -1792,24 +2084,34 @@ def generate_table(self, output_format=None): num_records = np.sum([len(hframe) for hframe in timeseries]) # build empty output table - if output_format == 'sele1_sele2': + if output_format == "sele1_sele2": dtype = [ ("time", float), - ("sele1_index", int), ("sele2_index", int), - ("sele1_resnm", "|U4"), ("sele1_resid", int), + ("sele1_index", int), + ("sele2_index", int), + ("sele1_resnm", "|U4"), + ("sele1_resid", int), ("sele1_atom", "|U4"), - ("sele2_resnm", "|U4"), ("sele2_resid", int), + ("sele2_resnm", "|U4"), + ("sele2_resid", int), ("sele2_atom", "|U4"), - ("distance", float), ("angle", float)] - elif output_format == 'donor_acceptor': + ("distance", float), + ("angle", float), + ] + elif output_format == "donor_acceptor": dtype = [ ("time", float), - ("donor_index", int), ("acceptor_index", int), - ("donor_resnm", "|U4"), ("donor_resid", int), + ("donor_index", int), + ("acceptor_index", int), + ("donor_resnm", "|U4"), + ("donor_resid", int), ("donor_atom", "|U4"), - ("acceptor_resnm", "|U4"), ("acceptor_resid", int), + ("acceptor_resnm", "|U4"), + ("acceptor_resid", int), ("acceptor_atom", "|U4"), - ("distance", float), ("angle", float)] + ("distance", float), + ("angle", float), + ] # according to Lukas' notes below, using a recarray at this stage is # ineffective and speedups of ~x10 can be achieved by filling a @@ -1817,18 +2119,30 @@ def generate_table(self, output_format=None): out = np.empty((num_records,), dtype=dtype) cursor = 0 # current row for t, hframe in zip(self.timesteps, timeseries): - for (donor_index, acceptor_index, donor, - acceptor, distance, angle) in hframe: + for ( + donor_index, + acceptor_index, + donor, + acceptor, + distance, + angle, + ) in hframe: # donor|acceptor = (resname, resid, atomid) - out[cursor] = (t, donor_index, acceptor_index) + \ - donor + acceptor + (distance, angle) + out[cursor] = ( + (t, donor_index, acceptor_index) + + donor + + acceptor + + (distance, angle) + ) cursor += 1 - assert cursor == num_records, \ - "Internal Error: Not all wb records stored" + assert ( + cursor == num_records + ), "Internal Error: Not all wb records stored" table = out.view(np.rec.recarray) logger.debug( "WBridge: Stored results as table with %(num_records)d entries.", - vars()) + vars(), + ) self.table = table return table @@ -1838,16 +2152,20 @@ def _conclude(self): @property def network(self): - wmsg = ("The `network` attribute was deprecated in MDAnalysis 2.0.0 " - "and will be removed in MDAnalysis 3.0.0. Please use " - "`results.network` instead") + wmsg = ( + "The `network` attribute was deprecated in MDAnalysis 2.0.0 " + "and will be removed in MDAnalysis 3.0.0. Please use " + "`results.network` instead" + ) warnings.warn(wmsg, DeprecationWarning) return self.results.network @property def timeseries(self): - wmsg = ("The `timeseries` attribute was deprecated in MDAnalysis " - "2.0.0 and will be removed in MDAnalysis 3.0.0. Please use " - "`results.timeseries` instead") + wmsg = ( + "The `timeseries` attribute was deprecated in MDAnalysis " + "2.0.0 and will be removed in MDAnalysis 3.0.0. Please use " + "`results.timeseries` instead" + ) warnings.warn(wmsg, DeprecationWarning) return self.results.timeseries diff --git a/package/MDAnalysis/analysis/leaflet.py b/package/MDAnalysis/analysis/leaflet.py index 5ea0d362a90..9ba5de87e7f 100644 --- a/package/MDAnalysis/analysis/leaflet.py +++ b/package/MDAnalysis/analysis/leaflet.py @@ -87,10 +87,12 @@ HAS_NX = True -due.cite(Doi("10.1002/jcc.21787"), - description="LeafletFinder algorithm", - path="MDAnalysis.analysis.leaflet", - cite_module=True) +due.cite( + Doi("10.1002/jcc.21787"), + description="LeafletFinder algorithm", + path="MDAnalysis.analysis.leaflet", + cite_module=True, +) del Doi @@ -157,9 +159,11 @@ class LeafletFinder(object): def __init__(self, universe, select, cutoff=15.0, pbc=False, sparse=None): # Raise an error if networkx is not installed if not HAS_NX: - errmsg = ("The LeafletFinder class requires an installation of " - "networkx. Please install networkx " - "https://networkx.org/documentation/stable/install.html") + errmsg = ( + "The LeafletFinder class requires an installation of " + "networkx. Please install networkx " + "https://networkx.org/documentation/stable/install.html" + ) raise ImportError(errmsg) self.universe = universe @@ -194,31 +198,47 @@ def _get_graph(self): if self.sparse is False: # only try distance array try: - adj = distances.contact_matrix(coord, cutoff=self.cutoff, returntype="numpy", box=box) - except ValueError: # pragma: no cover - warnings.warn('N x N matrix too big, use sparse=True or sparse=None', category=UserWarning, - stacklevel=2) + adj = distances.contact_matrix( + coord, cutoff=self.cutoff, returntype="numpy", box=box + ) + except ValueError: # pragma: no cover + warnings.warn( + "N x N matrix too big, use sparse=True or sparse=None", + category=UserWarning, + stacklevel=2, + ) raise elif self.sparse is True: # only try sparse - adj = distances.contact_matrix(coord, cutoff=self.cutoff, returntype="sparse", box=box) + adj = distances.contact_matrix( + coord, cutoff=self.cutoff, returntype="sparse", box=box + ) else: # use distance_array and fall back to sparse matrix try: # this works for small-ish systems and depends on system memory - adj = distances.contact_matrix(coord, cutoff=self.cutoff, returntype="numpy", box=box) - except ValueError: # pragma: no cover + adj = distances.contact_matrix( + coord, cutoff=self.cutoff, returntype="numpy", box=box + ) + except ValueError: # pragma: no cover # but use a sparse matrix method for larger systems for memory reasons warnings.warn( - 'N x N matrix too big - switching to sparse matrix method (works fine, but is currently rather ' - 'slow)', - category=UserWarning, stacklevel=2) - adj = distances.contact_matrix(coord, cutoff=self.cutoff, returntype="sparse", box=box) + "N x N matrix too big - switching to sparse matrix method (works fine, but is currently rather " + "slow)", + category=UserWarning, + stacklevel=2, + ) + adj = distances.contact_matrix( + coord, cutoff=self.cutoff, returntype="sparse", box=box + ) return NX.Graph(adj) def _get_components(self): """Return connected components (as sorted numpy arrays), sorted by size.""" - return [np.sort(list(component)) for component in NX.connected_components(self.graph)] + return [ + np.sort(list(component)) + for component in NX.connected_components(self.graph) + ] def update(self, cutoff=None): """Update components, possibly with a different *cutoff*""" @@ -228,7 +248,12 @@ def update(self, cutoff=None): def sizes(self): """Dict of component index with size of component.""" - return dict(((idx, len(component)) for idx, component in enumerate(self.components))) + return dict( + ( + (idx, len(component)) + for idx, component in enumerate(self.components) + ) + ) def groups(self, component_index=None): """Return a :class:`MDAnalysis.core.groups.AtomGroup` for *component_index*. @@ -265,23 +290,37 @@ def write_selection(self, filename, **kwargs): See :class:`MDAnalysis.selections.base.SelectionWriter` for all options. """ - sw = selections.get_writer(filename, kwargs.pop('format', None)) - with sw(filename, mode=kwargs.pop('mode', 'w'), - preamble="leaflets based on select={selectionstring!r} cutoff={cutoff:f}\n".format( - **vars(self)), - **kwargs) as writer: + sw = selections.get_writer(filename, kwargs.pop("format", None)) + with sw( + filename, + mode=kwargs.pop("mode", "w"), + preamble="leaflets based on select={selectionstring!r} cutoff={cutoff:f}\n".format( + **vars(self) + ), + **kwargs, + ) as writer: for i, ag in enumerate(self.groups_iter()): name = "leaflet_{0:d}".format((i + 1)) writer.write(ag, name=name) def __repr__(self): return "".format( - self.selectionstring, self.cutoff, self.selection.n_atoms, - len(self.components)) - - -def optimize_cutoff(universe, select, dmin=10.0, dmax=20.0, step=0.5, - max_imbalance=0.2, **kwargs): + self.selectionstring, + self.cutoff, + self.selection.n_atoms, + len(self.components), + ) + + +def optimize_cutoff( + universe, + select, + dmin=10.0, + dmax=20.0, + step=0.5, + max_imbalance=0.2, + **kwargs, +): r"""Find cutoff that minimizes number of disconnected groups. Applies heuristics to find best groups: @@ -325,7 +364,7 @@ def optimize_cutoff(universe, select, dmin=10.0, dmax=20.0, step=0.5, .. versionchanged:: 1.0.0 Changed `selection` keyword to `select` """ - kwargs.pop('cutoff', None) # not used, so we filter it + kwargs.pop("cutoff", None) # not used, so we filter it _sizes = [] for cutoff in np.arange(dmin, dmax, step): LF = LeafletFinder(universe, select, cutoff=cutoff, **kwargs) diff --git a/package/MDAnalysis/analysis/legacy/x3dna.py b/package/MDAnalysis/analysis/legacy/x3dna.py index 46a2f5a8f60..9ace5edf233 100644 --- a/package/MDAnalysis/analysis/legacy/x3dna.py +++ b/package/MDAnalysis/analysis/legacy/x3dna.py @@ -139,9 +139,14 @@ logger = logging.getLogger("MDAnalysis.analysis.x3dna") -@deprecate(release="2.7.0", remove="3.0.0", - message=("X3DNA module is deprecated and will be removed in" - "MDAnalysis 3.0.0, see #3788")) +@deprecate( + release="2.7.0", + remove="3.0.0", + message=( + "X3DNA module is deprecated and will be removed in" + "MDAnalysis 3.0.0, see #3788" + ), +) def mean_std_from_x3dnaPickle(profile): """Get mean and standard deviation of helicoidal parameters from a saved `profile`. @@ -168,10 +173,25 @@ def mean_std_from_x3dnaPickle(profile): .. deprecated:: 2.7.0 X3DNA will be removed in 3.0.0. """ - warnings.warn("X3DNA module is deprecated and will be removed in MDAnalysis 3.0, see #3788", category=DeprecationWarning) + warnings.warn( + "X3DNA module is deprecated and will be removed in MDAnalysis 3.0, see #3788", + category=DeprecationWarning, + ) if profile.x3dna_param is False: - bp_shear, bp_stretch, bp_stagger, bp_rise, bp_shift, bp_slide, bp_buckle, bp_prop, bp_open, bp_tilt, bp_roll,\ - bp_twist = [], [], [], [], [], [], [], [], [], [], [], [] + ( + bp_shear, + bp_stretch, + bp_stagger, + bp_rise, + bp_shift, + bp_slide, + bp_buckle, + bp_prop, + bp_open, + bp_tilt, + bp_roll, + bp_twist, + ) = ([], [], [], [], [], [], [], [], [], [], [], []) for i in range(len(profile)): bp_shear.append(profile.values()[i].Shear) bp_stretch.append(profile.values()[i].Stretch) @@ -185,44 +205,126 @@ def mean_std_from_x3dnaPickle(profile): bp_tilt.append(profile.values()[i].Tilt) bp_roll.append(profile.values()[i].Roll) bp_twist.append(profile.values()[i].Twist) - bp_shear, bp_stretch, bp_stagger, bp_rise, bp_shift, bp_slide, bp_buckle, bp_prop, bp_open, bp_tilt, bp_roll,\ - bp_twist = np.array(bp_shear), np.array(bp_stretch), np.array(bp_stagger), np.array(bp_rise),\ - np.array(bp_shift), np.array(bp_slide), np.array(bp_buckle), np.array(bp_prop), \ - np.array(bp_open), np.array(bp_tilt), np.array(bp_roll), np.array(bp_twist) + ( + bp_shear, + bp_stretch, + bp_stagger, + bp_rise, + bp_shift, + bp_slide, + bp_buckle, + bp_prop, + bp_open, + bp_tilt, + bp_roll, + bp_twist, + ) = ( + np.array(bp_shear), + np.array(bp_stretch), + np.array(bp_stagger), + np.array(bp_rise), + np.array(bp_shift), + np.array(bp_slide), + np.array(bp_buckle), + np.array(bp_prop), + np.array(bp_open), + np.array(bp_tilt), + np.array(bp_roll), + np.array(bp_twist), + ) na_avg, na_std = [], [] for j in range(len(bp_shear[0])): - na_avg.append([ - np.mean(bp_shear[:, j]), np.mean(bp_stretch[:, j]), np.mean(bp_stagger[:, j]), - np.mean(bp_buckle[:, j]), np.mean(bp_prop[:, j]), np.mean(bp_open[:, j]), - np.mean(bp_shift[:, j]), np.mean(bp_slide[:, j]), np.mean(bp_rise[:, j]), - np.mean(bp_tilt[:, j]), np.mean(bp_roll[:, j]), np.mean(bp_twist[:, j])]) - na_std.append([ - np.std(bp_shear[:, j]), np.std(bp_stretch[:, j]), np.std(bp_stagger[:, j]), - np.std(bp_buckle[:, j]), np.std(bp_prop[:, j]), np.std(bp_open[:, j]), - np.std(bp_shift[:, j]), np.std(bp_slide[:, j]), np.std(bp_rise[:, j]), - np.std(bp_tilt[:, j]), np.std(bp_roll[:, j]), np.std(bp_twist[:, j])]) + na_avg.append( + [ + np.mean(bp_shear[:, j]), + np.mean(bp_stretch[:, j]), + np.mean(bp_stagger[:, j]), + np.mean(bp_buckle[:, j]), + np.mean(bp_prop[:, j]), + np.mean(bp_open[:, j]), + np.mean(bp_shift[:, j]), + np.mean(bp_slide[:, j]), + np.mean(bp_rise[:, j]), + np.mean(bp_tilt[:, j]), + np.mean(bp_roll[:, j]), + np.mean(bp_twist[:, j]), + ] + ) + na_std.append( + [ + np.std(bp_shear[:, j]), + np.std(bp_stretch[:, j]), + np.std(bp_stagger[:, j]), + np.std(bp_buckle[:, j]), + np.std(bp_prop[:, j]), + np.std(bp_open[:, j]), + np.std(bp_shift[:, j]), + np.std(bp_slide[:, j]), + np.std(bp_rise[:, j]), + np.std(bp_tilt[:, j]), + np.std(bp_roll[:, j]), + np.std(bp_twist[:, j]), + ] + ) else: - bp_rise, bp_shift, bp_slide, bp_tilt, bp_roll, bp_twist = [], [], [], [], [], [], [], [], [], [], [], [] + bp_rise, bp_shift, bp_slide, bp_tilt, bp_roll, bp_twist = ( + [], + [], + [], + [], + [], + [], + [], + [], + [], + [], + [], + [], + ) for i in range(len(profile)): - #print i + # print i bp_rise.append(profile.values()[i].Rise) bp_shift.append(profile.values()[i].Shift) bp_slide.append(profile.values()[i].Slide) bp_tilt.append(profile.values()[i].Tilt) bp_roll.append(profile.values()[i].Roll) bp_twist.append(profile.values()[i].Twist) - bp_rise, bp_shift, bp_slide, bp_tilt, bp_roll, bp_twist = np.array(bp_shear),np.array(bp_stretch),\ - np.array(bp_stagger), np.array(bp_rise), np.array(bp_shift), np.array(bp_slide),\ - np.array(bp_buckle), np.array(bp_prop), np.array(bp_open), np.array(bp_tilt),\ - np.array(bp_roll), np.array(bp_twist) + bp_rise, bp_shift, bp_slide, bp_tilt, bp_roll, bp_twist = ( + np.array(bp_shear), + np.array(bp_stretch), + np.array(bp_stagger), + np.array(bp_rise), + np.array(bp_shift), + np.array(bp_slide), + np.array(bp_buckle), + np.array(bp_prop), + np.array(bp_open), + np.array(bp_tilt), + np.array(bp_roll), + np.array(bp_twist), + ) na_avg, na_std = [], [] for j in range(len(bp_shift[0])): - na_avg.append([ - np.mean(bp_shift[:, j]), np.mean(bp_slide[:, j]), np.mean(bp_rise[:, j]), - np.mean(bp_tilt[:, j]), np.mean(bp_roll[:, j]), np.mean(bp_twist[:, j])]) - na_std.append([ - np.std(bp_shift[:, j]), np.std(bp_slide[:, j]), np.std(bp_rise[:, j]), - np.std(bp_tilt[:, j]), np.std(bp_roll[:, j]), np.std(bp_twist[:, j])]) + na_avg.append( + [ + np.mean(bp_shift[:, j]), + np.mean(bp_slide[:, j]), + np.mean(bp_rise[:, j]), + np.mean(bp_tilt[:, j]), + np.mean(bp_roll[:, j]), + np.mean(bp_twist[:, j]), + ] + ) + na_std.append( + [ + np.std(bp_shift[:, j]), + np.std(bp_slide[:, j]), + np.std(bp_rise[:, j]), + np.std(bp_tilt[:, j]), + np.std(bp_roll[:, j]), + np.std(bp_twist[:, j]), + ] + ) na_avg, na_std = np.array(na_avg), np.array(na_std) return na_avg, na_std @@ -271,7 +373,9 @@ def save(self, filename="x3dna.pickle"): """ import cPickle - cPickle.dump(self.profiles, open(filename, "wb"), cPickle.HIGHEST_PROTOCOL) + cPickle.dump( + self.profiles, open(filename, "wb"), cPickle.HIGHEST_PROTOCOL + ) def mean_std(self): """Returns the mean and standard deviation of base parameters. @@ -285,8 +389,20 @@ def mean_std(self): roll, twist]``. """ - bp_shear, bp_stretch, bp_stagger, bp_rise, bp_shift, bp_slide, bp_buckle, bp_prop, bp_open, bp_tilt, bp_roll,\ - bp_twist = [], [], [], [], [], [], [], [], [], [], [], [] + ( + bp_shear, + bp_stretch, + bp_stagger, + bp_rise, + bp_shift, + bp_slide, + bp_buckle, + bp_prop, + bp_open, + bp_tilt, + bp_roll, + bp_twist, + ) = ([], [], [], [], [], [], [], [], [], [], [], []) for i in range(len(self.profiles)): bp_shear.append(self.profiles.values()[i].Shear) bp_stretch.append(self.profiles.values()[i].Stretch) @@ -300,22 +416,67 @@ def mean_std(self): bp_tilt.append(self.profiles.values()[i].Tilt) bp_roll.append(self.profiles.values()[i].Roll) bp_twist.append(self.profiles.values()[i].Twist) - bp_shear, bp_stretch, bp_stagger, bp_rise, bp_shift, bp_slide, bp_buckle, bp_prop, bp_open, bp_tilt, bp_roll,\ - bp_twist = np.array(bp_shear), np.array(bp_stretch), np.array(bp_stagger), np.array(bp_rise),\ - np.array(bp_shift), np.array(bp_slide), np.array(bp_buckle), np.array(bp_prop),\ - np.array(bp_open), np.array(bp_tilt), np.array(bp_roll), np.array(bp_twist) + ( + bp_shear, + bp_stretch, + bp_stagger, + bp_rise, + bp_shift, + bp_slide, + bp_buckle, + bp_prop, + bp_open, + bp_tilt, + bp_roll, + bp_twist, + ) = ( + np.array(bp_shear), + np.array(bp_stretch), + np.array(bp_stagger), + np.array(bp_rise), + np.array(bp_shift), + np.array(bp_slide), + np.array(bp_buckle), + np.array(bp_prop), + np.array(bp_open), + np.array(bp_tilt), + np.array(bp_roll), + np.array(bp_twist), + ) na_avg, na_std = [], [] for j in range(len(bp_shear[0])): - na_avg.append([ - np.mean(bp_shear[:, j]), np.mean(bp_stretch[:, j]), np.mean(bp_stagger[:, j]), - np.mean(bp_buckle[:, j]), np.mean(bp_prop[:, j]), np.mean(bp_open[:, j]), - np.mean(bp_shift[:, j]), np.mean(bp_slide[:, j]), np.mean(bp_rise[:, j]), - np.mean(bp_tilt[:, j]), np.mean(bp_roll[:, j]), np.mean(bp_twist[:, j])]) - na_std.append([ - np.std(bp_shear[:, j]), np.std(bp_stretch[:, j]), np.std(bp_stagger[:, j]), - np.std(bp_buckle[:, j]), np.std(bp_prop[:, j]), np.std(bp_open[:, j]), - np.std(bp_shift[:, j]), np.std(bp_slide[:, j]), np.std(bp_rise[:, j]), - np.std(bp_tilt[:, j]), np.std(bp_roll[:, j]), np.std(bp_twist[:, j])]) + na_avg.append( + [ + np.mean(bp_shear[:, j]), + np.mean(bp_stretch[:, j]), + np.mean(bp_stagger[:, j]), + np.mean(bp_buckle[:, j]), + np.mean(bp_prop[:, j]), + np.mean(bp_open[:, j]), + np.mean(bp_shift[:, j]), + np.mean(bp_slide[:, j]), + np.mean(bp_rise[:, j]), + np.mean(bp_tilt[:, j]), + np.mean(bp_roll[:, j]), + np.mean(bp_twist[:, j]), + ] + ) + na_std.append( + [ + np.std(bp_shear[:, j]), + np.std(bp_stretch[:, j]), + np.std(bp_stagger[:, j]), + np.std(bp_buckle[:, j]), + np.std(bp_prop[:, j]), + np.std(bp_open[:, j]), + np.std(bp_shift[:, j]), + np.std(bp_slide[:, j]), + np.std(bp_rise[:, j]), + np.std(bp_tilt[:, j]), + np.std(bp_roll[:, j]), + np.std(bp_twist[:, j]), + ] + ) na_avg, na_std = np.array(na_avg), np.array(na_std) return na_avg, na_std @@ -330,8 +491,20 @@ def mean(self): shift, slide, rise, tilt, roll, twist]``. """ - bp_shear, bp_stretch, bp_stagger, bp_rise, bp_shift, bp_slide, bp_buckle, bp_prop, bp_open, bp_tilt, bp_roll,\ - bp_twist = [], [], [], [], [], [], [], [], [], [], [], [] + ( + bp_shear, + bp_stretch, + bp_stagger, + bp_rise, + bp_shift, + bp_slide, + bp_buckle, + bp_prop, + bp_open, + bp_tilt, + bp_roll, + bp_twist, + ) = ([], [], [], [], [], [], [], [], [], [], [], []) for i in range(len(self.profiles)): bp_shear.append(self.profiles.values()[i].Shear) bp_stretch.append(self.profiles.values()[i].Stretch) @@ -345,18 +518,52 @@ def mean(self): bp_tilt.append(self.profiles.values()[i].Tilt) bp_roll.append(self.profiles.values()[i].Roll) bp_twist.append(self.profiles.values()[i].Twist) - bp_shear, bp_stretch, bp_stagger, bp_rise, bp_shift, bp_slide, bp_buckle, bp_prop, bp_open, bp_tilt, bp_roll,\ - bp_twist = np.array(bp_shear), np.array(bp_stretch), np.array(bp_stagger), np.array(bp_rise),\ - np.array(bp_shift), np.array(bp_slide), np.array(bp_buckle), np.array(bp_prop),\ - np.array(bp_open), np.array(bp_tilt), np.array(bp_roll), np.array(bp_twist) + ( + bp_shear, + bp_stretch, + bp_stagger, + bp_rise, + bp_shift, + bp_slide, + bp_buckle, + bp_prop, + bp_open, + bp_tilt, + bp_roll, + bp_twist, + ) = ( + np.array(bp_shear), + np.array(bp_stretch), + np.array(bp_stagger), + np.array(bp_rise), + np.array(bp_shift), + np.array(bp_slide), + np.array(bp_buckle), + np.array(bp_prop), + np.array(bp_open), + np.array(bp_tilt), + np.array(bp_roll), + np.array(bp_twist), + ) na_avg = [] for j in range(len(bp_shear[0])): - na_avg.append([ - np.mean(bp_shear[:, j]), np.mean(bp_stretch[:, j]), np.mean(bp_stagger[:, j]), - np.mean(bp_buckle[:j]), np.mean(bp_prop[:, j]), np.mean(bp_open[:, j]), - np.mean(bp_shift[:, j]), np.mean(bp_slide[:, j]), np.mean(bp_rise[:, j]), - np.mean(bp_tilt[:, j]), np.mean(bp_roll[:, j]), np.mean(bp_twist[:, j])]) + na_avg.append( + [ + np.mean(bp_shear[:, j]), + np.mean(bp_stretch[:, j]), + np.mean(bp_stagger[:, j]), + np.mean(bp_buckle[:j]), + np.mean(bp_prop[:, j]), + np.mean(bp_open[:, j]), + np.mean(bp_shift[:, j]), + np.mean(bp_slide[:, j]), + np.mean(bp_rise[:, j]), + np.mean(bp_tilt[:, j]), + np.mean(bp_roll[:, j]), + np.mean(bp_twist[:, j]), + ] + ) na_avg = np.array(na_avg) return na_avg @@ -371,8 +578,20 @@ def std(self): propeller, opening, shift, slide, rise, tilt, roll, twist]``. """ - bp_shear, bp_stretch, bp_stagger, bp_rise, bp_shift, bp_slide, bp_buckle, bp_prop, bp_open, bp_tilt, bp_roll,\ - bp_twist = [], [], [], [], [], [], [], [], [], [], [], [] + ( + bp_shear, + bp_stretch, + bp_stagger, + bp_rise, + bp_shift, + bp_slide, + bp_buckle, + bp_prop, + bp_open, + bp_tilt, + bp_roll, + bp_twist, + ) = ([], [], [], [], [], [], [], [], [], [], [], []) for i in range(len(self.profiles)): bp_shear.append(self.profiles.values()[i].Shear) bp_stretch.append(self.profiles.values()[i].Stretch) @@ -386,18 +605,52 @@ def std(self): bp_tilt.append(self.profiles.values()[i].Tilt) bp_roll.append(self.profiles.values()[i].Roll) bp_twist.append(self.profiles.values()[i].Twist) - bp_shear, bp_stretch, bp_stagger, bp_rise, bp_shift, bp_slide, bp_buckle, bp_prop, bp_open, bp_tilt, bp_roll,\ - bp_twist = np.array(bp_shear), np.array(bp_stretch), np.array(bp_stagger), np.array(bp_rise),\ - np.array(bp_shift), np.array(bp_slide), np.array(bp_buckle), np.array(bp_prop),\ - np.array(bp_open), np.array(bp_tilt), np.array(bp_roll), np.array(bp_twist) + ( + bp_shear, + bp_stretch, + bp_stagger, + bp_rise, + bp_shift, + bp_slide, + bp_buckle, + bp_prop, + bp_open, + bp_tilt, + bp_roll, + bp_twist, + ) = ( + np.array(bp_shear), + np.array(bp_stretch), + np.array(bp_stagger), + np.array(bp_rise), + np.array(bp_shift), + np.array(bp_slide), + np.array(bp_buckle), + np.array(bp_prop), + np.array(bp_open), + np.array(bp_tilt), + np.array(bp_roll), + np.array(bp_twist), + ) na_std = [] for j in range(len(bp_shear[0])): - na_std.append([ - np.std(bp_shear[:, j]), np.std(bp_stretch[:, j]), np.std(bp_stagger[:, j]), - np.std(bp_buckle[:j]), np.std(bp_prop[:, j]), np.std(bp_open[:, j]), np.std(bp_shift[:, j]), - np.std(bp_slide[:, j]), np.std(bp_rise[:, j]), np.std(bp_tilt[:, j]), np.std(bp_roll[:, j]), - np.std(bp_twist[:, j])]) + na_std.append( + [ + np.std(bp_shear[:, j]), + np.std(bp_stretch[:, j]), + np.std(bp_stagger[:, j]), + np.std(bp_buckle[:j]), + np.std(bp_prop[:, j]), + np.std(bp_open[:, j]), + np.std(bp_shift[:, j]), + np.std(bp_slide[:, j]), + np.std(bp_rise[:, j]), + np.std(bp_tilt[:, j]), + np.std(bp_roll[:, j]), + np.std(bp_twist[:, j]), + ] + ) na_std = np.array(na_std) return na_std @@ -417,13 +670,20 @@ def plot(self, **kwargs): na_avg, na_std = self.mean_std() for k in range(len(na_avg[0])): - ax = kwargs.pop('ax', plt.subplot(111)) + ax = kwargs.pop("ax", plt.subplot(111)) x = list(range(1, len(na_avg[:, k]) + 1)) - ax.errorbar(x, na_avg[:, k], yerr=na_std[:, k], fmt='-o') + ax.errorbar(x, na_avg[:, k], yerr=na_std[:, k], fmt="-o") ax.set_xlim(0, len(na_avg[:, k]) + 1) ax.set_xlabel(r"Nucleic Acid Number") param = self.profiles.values()[0].dtype.names[k] - if param in ["Shear", "Stretch", "Stagger", "Rise", "Shift", "Slide"]: + if param in [ + "Shear", + "Stretch", + "Stagger", + "Rise", + "Shift", + "Slide", + ]: ax.set_ylabel(r"{!s} ($\AA$)".format(param)) else: ax.set_ylabel("{0!s} (deg)".format((param))) @@ -487,9 +747,14 @@ class X3DNA(BaseX3DNA): .. _`X3DNA docs`: http://forum.x3dna.org/ """ - @deprecate(release="2.7.0", remove="3.0.0", - message=("X3DNA module is deprecated and will be removed in" - "MDAnalysis 3.0.0, see #3788")) + @deprecate( + release="2.7.0", + remove="3.0.0", + message=( + "X3DNA module is deprecated and will be removed in" + "MDAnalysis 3.0.0, see #3788" + ), + ) def __init__(self, filename, **kwargs): """Set up parameters to run X3DNA_ on PDB *filename*. @@ -521,8 +786,17 @@ def __init__(self, filename, **kwargs): """ # list of temporary files, to be cleaned up on __del__ self.tempfiles = [ - "auxiliary.par", "bestpairs.pdb", "bp_order.dat", "bp_helical.par", "cf_7methods.par", - "col_chains.scr", "col_helices.scr", "hel_regions.pdb", "ref_frames.dat", "hstacking.pdb", "stacking.pdb" + "auxiliary.par", + "bestpairs.pdb", + "bp_order.dat", + "bp_helical.par", + "cf_7methods.par", + "col_chains.scr", + "col_helices.scr", + "hel_regions.pdb", + "ref_frames.dat", + "hstacking.pdb", + "stacking.pdb", ] self.tempdirs = [] self.filename = filename @@ -531,28 +805,38 @@ def __init__(self, filename, **kwargs): # guess executables self.exe = {} - x3dna_exe_name = kwargs.pop('executable', 'xdna_ensemble') - self.x3dna_param = kwargs.pop('x3dna_param', True) - self.exe['xdna_ensemble'] = which(x3dna_exe_name) - if self.exe['xdna_ensemble'] is None: - errmsg = "X3DNA binary {x3dna_exe_name!r} not found.".format(**vars()) + x3dna_exe_name = kwargs.pop("executable", "xdna_ensemble") + self.x3dna_param = kwargs.pop("x3dna_param", True) + self.exe["xdna_ensemble"] = which(x3dna_exe_name) + if self.exe["xdna_ensemble"] is None: + errmsg = "X3DNA binary {x3dna_exe_name!r} not found.".format( + **vars() + ) logger.fatal(errmsg) - logger.fatal("%(x3dna_exe_name)r must be on the PATH or provided as keyword argument 'executable'.", - vars()) + logger.fatal( + "%(x3dna_exe_name)r must be on the PATH or provided as keyword argument 'executable'.", + vars(), + ) raise OSError(errno.ENOENT, errmsg) - x3dnapath = os.path.dirname(self.exe['xdna_ensemble']) + x3dnapath = os.path.dirname(self.exe["xdna_ensemble"]) self.logfile = kwargs.pop("logfile", "bp_step.par") if self.x3dna_param is False: - self.template = textwrap.dedent("""x3dna_ensemble analyze -b 355d.bps --one %(filename)r """) + self.template = textwrap.dedent( + """x3dna_ensemble analyze -b 355d.bps --one %(filename)r """ + ) else: - self.template = textwrap.dedent("""find_pair -s %(filename)r stdout |analyze stdin """) + self.template = textwrap.dedent( + """find_pair -s %(filename)r stdout |analyze stdin """ + ) # sanity checks for program, path in self.exe.items(): if path is None or which(path) is None: - logger.error("Executable %(program)r not found, should have been %(path)r.", - vars()) + logger.error( + "Executable %(program)r not found, should have been %(path)r.", + vars(), + ) # results self.profiles = OrderedDict() @@ -563,7 +847,7 @@ def run(self, **kwargs): x3dnaargs = vars(self).copy() x3dnaargs.update(kwargs) - x3dna_param = kwargs.pop('x3dna_param', self.x3dna_param) + x3dna_param = kwargs.pop("x3dna_param", self.x3dna_param) inp = self.template % x3dnaargs if inpname: @@ -571,18 +855,24 @@ def run(self, **kwargs): f.write(inp) logger.debug("Wrote X3DNA input file %r for inspection", inpname) - logger.info("Starting X3DNA on %(filename)r (trajectory: %(dcd)r)", x3dnaargs) - logger.debug("%s", self.exe['xdna_ensemble']) + logger.info( + "Starting X3DNA on %(filename)r (trajectory: %(dcd)r)", x3dnaargs + ) + logger.debug("%s", self.exe["xdna_ensemble"]) with open(outname, "w") as output: x3dna = subprocess.call([inp], shell=True) with open(outname, "r") as output: # X3DNA is not very good at setting returncodes so check ourselves for line in output: - if line.strip().startswith(('*** ERROR ***', 'ERROR')): + if line.strip().startswith(("*** ERROR ***", "ERROR")): x3dna.returncode = 255 break if x3dna.bit_length != 0: - logger.fatal("X3DNA Failure (%d). Check output %r", x3dna.bit_length, outname) + logger.fatal( + "X3DNA Failure (%d). Check output %r", + x3dna.bit_length, + outname, + ) logger.info("X3DNA finished: output file %(outname)r", vars()) def collect(self, **kwargs): @@ -607,10 +897,10 @@ def collect(self, **kwargs): """ # Shear Stretch Stagger Buckle Prop-Tw Opening Shift Slide Rise Tilt Roll Twist - #0123456789.0123456789.0123456789.0123456789.0123456789.0123456789.123456789.123456789.123456789.123456789.123456789.123456789.123456789. + # 0123456789.0123456789.0123456789.0123456789.0123456789.0123456789.123456789.123456789.123456789.123456789.123456789.123456789.123456789. # 11 22 33 44 - #T-A -0.033 -0.176 0.158 -12.177 -8.979 1.440 0.000 0.000 0.000 0.000 0.000 0.000 - #C-G -0.529 0.122 -0.002 -7.983 -10.083 -0.091 -0.911 1.375 3.213 -0.766 -4.065 41.492 + # T-A -0.033 -0.176 0.158 -12.177 -8.979 1.440 0.000 0.000 0.000 0.000 0.000 0.000 + # C-G -0.529 0.122 -0.002 -7.983 -10.083 -0.091 -0.911 1.375 3.213 -0.766 -4.065 41.492 # only parse bp_step.par x3dna_output = kwargs.pop("x3dnaout", self.logfile) @@ -619,20 +909,35 @@ def collect(self, **kwargs): logger.info("Collecting X3DNA profiles for run with id %s", run) length = 1 # length of trajectory --- is this really needed?? No... just for info - if '*' in self.filename: + if "*" in self.filename: import glob filenames = glob.glob(self.filename) length = len(filenames) if length == 0: - logger.error("Glob pattern %r did not find any files.", self.filename) - raise ValueError("Glob pattern {0!r} did not find any files.".format(self.filename)) - logger.info("Found %d input files based on glob pattern %s", length, self.filename) + logger.error( + "Glob pattern %r did not find any files.", self.filename + ) + raise ValueError( + "Glob pattern {0!r} did not find any files.".format( + self.filename + ) + ) + logger.info( + "Found %d input files based on glob pattern %s", + length, + self.filename, + ) # one recarray for each frame, indexed by frame number self.profiles = OrderedDict() - logger.info("Run %s: Reading %d X3DNA profiles from %r", run, length, x3dna_output) + logger.info( + "Run %s: Reading %d X3DNA profiles from %r", + run, + length, + x3dna_output, + ) x3dna_profile_no = 0 records = [] with open(x3dna_output, "r") as x3dna: @@ -644,22 +949,55 @@ def collect(self, **kwargs): read_data = True logger.debug("Started reading data") fields = line.split() - x3dna_profile_no = int(1) # useless int value code based off hole plugin + x3dna_profile_no = int( + 1 + ) # useless int value code based off hole plugin records = [] continue if read_data: if len(line.strip()) != 0: try: - Sequence, Shear, Stretch, Stagger, Buckle, Propeller, Opening, Shift, Slide, Rise, \ - Tilt, Roll, Twist = line.split() + ( + Sequence, + Shear, + Stretch, + Stagger, + Buckle, + Propeller, + Opening, + Shift, + Slide, + Rise, + Tilt, + Roll, + Twist, + ) = line.split() except: - logger.critical("Run %d: Problem parsing line %r", run, line.strip()) - logger.exception("Check input file %r.", x3dna_output) + logger.critical( + "Run %d: Problem parsing line %r", + run, + line.strip(), + ) + logger.exception( + "Check input file %r.", x3dna_output + ) raise records.append( - [float(Shear), float(Stretch), float(Stagger), float(Buckle), float(Propeller), - float(Opening), float(Shift), float(Slide), float(Rise), float(Tilt), float(Roll), - float(Twist)]) + [ + float(Shear), + float(Stretch), + float(Stagger), + float(Buckle), + float(Propeller), + float(Opening), + float(Shift), + float(Slide), + float(Rise), + float(Tilt), + float(Roll), + float(Twist), + ] + ) continue else: # end of records (empty line) @@ -669,34 +1007,67 @@ def collect(self, **kwargs): read_data = True logger.debug("Started reading data") fields = line.split() - x3dna_profile_no = int(1) # useless int value code based off hole plugin + x3dna_profile_no = int( + 1 + ) # useless int value code based off hole plugin records = [] continue if read_data: if len(line.strip()) != 0: try: - Sequence, Shift, Slide, Rise, Tilt, Roll, Twist = line.split() + ( + Sequence, + Shift, + Slide, + Rise, + Tilt, + Roll, + Twist, + ) = line.split() except: - logger.critical("Run %d: Problem parsing line %r", run, line.strip()) - logger.exception("Check input file %r.", x3dna_output) + logger.critical( + "Run %d: Problem parsing line %r", + run, + line.strip(), + ) + logger.exception( + "Check input file %r.", x3dna_output + ) raise records.append( - [float(Shift), float(Slide), float(Rise), float(Tilt), float(Roll), float(Twist)]) + [ + float(Shift), + float(Slide), + float(Rise), + float(Tilt), + float(Roll), + float(Twist), + ] + ) continue else: # end of records (empty line) read_data = False if self.x3dna_param is False: - frame_x3dna_output = np.rec.fromrecords(records, formats="f8,f8,f8,f8,f8,f8,f8,f8,f8,f8,f8,f8", - names="Shear,Stretch,Stagger,Buckle,Propeller,Opening," - "Shift,Slide,Rise,Tilt,Roll,Twist") + frame_x3dna_output = np.rec.fromrecords( + records, + formats="f8,f8,f8,f8,f8,f8,f8,f8,f8,f8,f8,f8", + names="Shear,Stretch,Stagger,Buckle,Propeller,Opening," + "Shift,Slide,Rise,Tilt,Roll,Twist", + ) else: - frame_x3dna_output = np.rec.fromrecords(records, formats="f8,f8,f8,f8,f8,f8", - names="Shift,Slide,Rise,Tilt,Roll,Twist") + frame_x3dna_output = np.rec.fromrecords( + records, + formats="f8,f8,f8,f8,f8,f8", + names="Shift,Slide,Rise,Tilt,Roll,Twist", + ) # store the profile self.profiles[x3dna_profile_no] = frame_x3dna_output - logger.debug("Collected X3DNA profile for frame %d (%d datapoints)", - x3dna_profile_no, len(frame_x3dna_output)) + logger.debug( + "Collected X3DNA profile for frame %d (%d datapoints)", + x3dna_profile_no, + len(frame_x3dna_output), + ) # save a profile for each frame (for debugging and scripted processing) # a tmp folder for each trajectory if outdir is not None: @@ -704,14 +1075,29 @@ def collect(self, **kwargs): os.system("rm -f tmp*.out") if not os.path.exists(rundir): os.makedirs(rundir) - frame_x3dna_txt = os.path.join(rundir, "bp_step_{0!s}_{1:04d}.dat.gz".format(run, x3dna_profile_no)) + frame_x3dna_txt = os.path.join( + rundir, + "bp_step_{0!s}_{1:04d}.dat.gz".format( + run, x3dna_profile_no + ), + ) np.savetxt(frame_x3dna_txt, frame_x3dna_output) - logger.debug("Finished with frame %d, saved as %r", x3dna_profile_no, frame_x3dna_txt) + logger.debug( + "Finished with frame %d, saved as %r", + x3dna_profile_no, + frame_x3dna_txt, + ) # if we get here then we haven't found anything interesting if len(self.profiles) == length: - logger.info("Collected X3DNA profiles for %d frames", len(self.profiles)) + logger.info( + "Collected X3DNA profiles for %d frames", len(self.profiles) + ) else: - logger.warning("Missing data: Found %d X3DNA profiles from %d frames.", len(self.profiles), length) + logger.warning( + "Missing data: Found %d X3DNA profiles from %d frames.", + len(self.profiles), + length, + ) def __del__(self): for f in self.tempfiles: @@ -736,9 +1122,15 @@ class X3DNAtraj(BaseX3DNA): .. deprecated:: 2.7.0 X3DNA will be removed in 3.0.0. """ - @deprecate(release="2.7.0", remove="3.0.0", - message=("X3DNA module is deprecated and will be removed in" - "MDAnalysis 3.0.0, see #3788")) + + @deprecate( + release="2.7.0", + remove="3.0.0", + message=( + "X3DNA module is deprecated and will be removed in" + "MDAnalysis 3.0.0, see #3788" + ), + ) def __init__(self, universe, **kwargs): """Set up the class. @@ -776,10 +1168,10 @@ def __init__(self, universe, **kwargs): self.universe = universe self.selection = kwargs.pop("selection", "nucleic") - self.x3dna_param = kwargs.pop('x3dna_param', True) - self.start = kwargs.pop('start', None) - self.stop = kwargs.pop('stop', None) - self.step = kwargs.pop('step', None) + self.x3dna_param = kwargs.pop("x3dna_param", True) + self.start = kwargs.pop("start", None) + self.stop = kwargs.pop("stop", None) + self.step = kwargs.pop("step", None) self.x3dna_kwargs = kwargs @@ -792,10 +1184,10 @@ def run(self, **kwargs): analyse part of the trajectory. The defaults are the values provided to the class constructor. """ - start = kwargs.pop('start', self.start) - stop = kwargs.pop('stop', self.stop) - step = kwargs.pop('step', self.step) - x3dna_param = kwargs.pop('x3dna_param', self.x3dna_param) + start = kwargs.pop("start", self.start) + stop = kwargs.pop("stop", self.stop) + step = kwargs.pop("step", self.step) + x3dna_param = kwargs.pop("x3dna_param", self.x3dna_param) x3dna_kw = self.x3dna_kwargs.copy() x3dna_kw.update(kwargs) @@ -818,7 +1210,8 @@ def run(self, **kwargs): pass if len(x3dna_profiles) != 1: err_msg = "Got {0} profiles ({1}) --- should be 1 (time step {2})".format( - len(x3dna_profiles), x3dna_profiles.keys(), ts) + len(x3dna_profiles), x3dna_profiles.keys(), ts + ) logger.error(err_msg) warnings.warn(err_msg) profiles[ts.frame] = x3dna_profiles.values()[0] @@ -826,7 +1219,7 @@ def run(self, **kwargs): def run_x3dna(self, pdbfile, **kwargs): """Run X3DNA on a single PDB file `pdbfile`.""" - kwargs['x3dna_param'] = self.x3dna_param + kwargs["x3dna_param"] = self.x3dna_param H = X3DNA(pdbfile, **kwargs) H.run() H.collect() diff --git a/package/MDAnalysis/analysis/lineardensity.py b/package/MDAnalysis/analysis/lineardensity.py index 8970d68d8a0..a0377b47b35 100644 --- a/package/MDAnalysis/analysis/lineardensity.py +++ b/package/MDAnalysis/analysis/lineardensity.py @@ -44,21 +44,26 @@ class Results(Results): the docstring for LinearDensity for details. The Results class is defined here to implement deprecation warnings for the user.""" - _deprecation_dict = {"pos": "mass_density", - "pos_std": "mass_density_stddev", - "char": "charge_density", - "char_std": "charge_density_stddev"} + _deprecation_dict = { + "pos": "mass_density", + "pos_std": "mass_density_stddev", + "char": "charge_density", + "char_std": "charge_density_stddev", + } def _deprecation_warning(self, key): warnings.warn( f"`{key}` is deprecated and will be removed in version 3.0.0. " f"Please use `{self._deprecation_dict[key]}` instead.", - DeprecationWarning) + DeprecationWarning, + ) def __getitem__(self, key): if key in self._deprecation_dict.keys(): self._deprecation_warning(key) - return super(Results, self).__getitem__(self._deprecation_dict[key]) + return super(Results, self).__getitem__( + self._deprecation_dict[key] + ) return super(Results, self).__getitem__(key) def __getattr__(self, attr): @@ -193,9 +198,10 @@ class LinearDensity(AnalysisBase): and :attr:`results.x.charge_density_stddev` instead. """ - def __init__(self, select, grouping='atoms', binsize=0.25, **kwargs): - super(LinearDensity, self).__init__(select.universe.trajectory, - **kwargs) + def __init__(self, select, grouping="atoms", binsize=0.25, **kwargs): + super(LinearDensity, self).__init__( + select.universe.trajectory, **kwargs + ) # allows use of run(parallel=True) self._ags = [select] self._universe = select.universe @@ -222,13 +228,17 @@ def __init__(self, select, grouping='atoms', binsize=0.25, **kwargs): self.nbins = bins.max() slices_vol = self.volume / bins - self.keys = ['mass_density', 'mass_density_stddev', - 'charge_density', 'charge_density_stddev'] + self.keys = [ + "mass_density", + "mass_density_stddev", + "charge_density", + "charge_density_stddev", + ] # Initialize results array with zeros for dim in self.results: - idx = self.results[dim]['dim'] - self.results[dim]['slice_volume'] = slices_vol[idx] + idx = self.results[dim]["dim"] + self.results[dim]["slice_volume"] = slices_vol[idx] for key in self.keys: self.results[dim][key] = np.zeros(self.nbins) @@ -249,7 +259,8 @@ def _single_frame(self): else: raise AttributeError( - f"{self.grouping} is not a valid value for grouping.") + f"{self.grouping} is not a valid value for grouping." + ) self.totalmass = np.sum(self.masses) @@ -257,37 +268,41 @@ def _single_frame(self): self._ags[0].wrap(compound=self.grouping) # Find position of atom/group of atoms - if self.grouping == 'atoms': + if self.grouping == "atoms": positions = self._ags[0].positions # faster for atoms else: # Centre of mass for residues, segments, fragments positions = self._ags[0].center_of_mass(compound=self.grouping) - for dim in ['x', 'y', 'z']: - idx = self.results[dim]['dim'] + for dim in ["x", "y", "z"]: + idx = self.results[dim]["dim"] - key = 'mass_density' - key_std = 'mass_density_stddev' + key = "mass_density" + key_std = "mass_density_stddev" # histogram for positions weighted on masses - hist, _ = np.histogram(positions[:, idx], - weights=self.masses, - bins=self.nbins, - range=(0.0, max(self.dimensions))) + hist, _ = np.histogram( + positions[:, idx], + weights=self.masses, + bins=self.nbins, + range=(0.0, max(self.dimensions)), + ) self.results[dim][key] += hist self.results[dim][key_std] += np.square(hist) - key = 'charge_density' - key_std = 'charge_density_stddev' + key = "charge_density" + key_std = "charge_density_stddev" # histogram for positions weighted on charges - hist, bin_edges = np.histogram(positions[:, idx], - weights=self.charges, - bins=self.nbins, - range=(0.0, max(self.dimensions))) + hist, bin_edges = np.histogram( + positions[:, idx], + weights=self.charges, + bins=self.nbins, + range=(0.0, max(self.dimensions)), + ) self.results[dim][key] += hist self.results[dim][key_std] += np.square(hist) - self.results[dim]['hist_bin_edges'] = bin_edges + self.results[dim]["hist_bin_edges"] = bin_edges def _conclude(self): avogadro = constants["N_Avogadro"] # unit: mol^{-1} @@ -296,9 +311,13 @@ def _conclude(self): k = avogadro * volume_conversion # Average results over the number of configurations - for dim in ['x', 'y', 'z']: - for key in ['mass_density', 'mass_density_stddev', - 'charge_density', 'charge_density_stddev']: + for dim in ["x", "y", "z"]: + for key in [ + "mass_density", + "mass_density_stddev", + "charge_density", + "charge_density_stddev", + ]: self.results[dim][key] /= self.n_frames # Compute standard deviation for the error # For certain tests in testsuite, floating point imprecision @@ -306,29 +325,35 @@ def _conclude(self): # radicand_mass and radicand_charge are therefore calculated first # and negative values set to 0 before the square root # is calculated. - radicand_mass = self.results[dim]['mass_density_stddev'] - \ - np.square(self.results[dim]['mass_density']) + radicand_mass = self.results[dim][ + "mass_density_stddev" + ] - np.square(self.results[dim]["mass_density"]) radicand_mass[radicand_mass < 0] = 0 - self.results[dim]['mass_density_stddev'] = np.sqrt(radicand_mass) + self.results[dim]["mass_density_stddev"] = np.sqrt(radicand_mass) - radicand_charge = self.results[dim]['charge_density_stddev'] - \ - np.square(self.results[dim]['charge_density']) + radicand_charge = self.results[dim][ + "charge_density_stddev" + ] - np.square(self.results[dim]["charge_density"]) radicand_charge[radicand_charge < 0] = 0 - self.results[dim]['charge_density_stddev'] = \ - np.sqrt(radicand_charge) + self.results[dim]["charge_density_stddev"] = np.sqrt( + radicand_charge + ) - for dim in ['x', 'y', 'z']: + for dim in ["x", "y", "z"]: # norming factor, units of mol^-1 cm^3 - norm = k * self.results[dim]['slice_volume'] + norm = k * self.results[dim]["slice_volume"] for key in self.keys: self.results[dim][key] /= norm # TODO: Remove in 3.0.0 - @deprecate(release="2.2.0", remove="3.0.0", - message="It will be replaced by a :meth:`_reduce` " - "method in the future") + @deprecate( + release="2.2.0", + remove="3.0.0", + message="It will be replaced by a :meth:`_reduce` " + "method in the future", + ) def _add_other_results(self, other): """For parallel analysis""" - for dim in ['x', 'y', 'z']: + for dim in ["x", "y", "z"]: for key in self.keys: self.results[dim][key] += other.results[dim][key] diff --git a/package/MDAnalysis/analysis/msd.py b/package/MDAnalysis/analysis/msd.py index 4515ed40983..8d0e7a39e1e 100644 --- a/package/MDAnalysis/analysis/msd.py +++ b/package/MDAnalysis/analysis/msd.py @@ -246,16 +246,20 @@ from ..core import groups from tqdm import tqdm -logger = logging.getLogger('MDAnalysis.analysis.msd') - -due.cite(Doi("10.21105/joss.00877"), - description="Mean Squared Displacements with tidynamics", - path="MDAnalysis.analysis.msd", - cite_module=True) -due.cite(Doi("10.1051/sfn/201112010"), - description="FCA fast correlation algorithm", - path="MDAnalysis.analysis.msd", - cite_module=True) +logger = logging.getLogger("MDAnalysis.analysis.msd") + +due.cite( + Doi("10.21105/joss.00877"), + description="Mean Squared Displacements with tidynamics", + path="MDAnalysis.analysis.msd", + cite_module=True, +) +due.cite( + Doi("10.1051/sfn/201112010"), + description="FCA fast correlation algorithm", + path="MDAnalysis.analysis.msd", + cite_module=True, +) del Doi @@ -297,7 +301,7 @@ class EinsteinMSD(AnalysisBase): .. versionadded:: 2.0.0 """ - def __init__(self, u, select='all', msd_type='xyz', fft=True, **kwargs): + def __init__(self, u, select="all", msd_type="xyz", fft=True, **kwargs): r""" Parameters ---------- @@ -314,8 +318,9 @@ def __init__(self, u, select='all', msd_type='xyz', fft=True, **kwargs): The tidynamics package is required for `fft=True`. """ if isinstance(u, groups.UpdatingAtomGroup): - raise TypeError("UpdatingAtomGroups are not valid for MSD " - "computation") + raise TypeError( + "UpdatingAtomGroups are not valid for MSD " "computation" + ) super(EinsteinMSD, self).__init__(u.universe.trajectory, **kwargs) @@ -337,18 +342,25 @@ def __init__(self, u, select='all', msd_type='xyz', fft=True, **kwargs): def _prepare(self): # self.n_frames only available here # these need to be zeroed prior to each run() call - self.results.msds_by_particle = np.zeros((self.n_frames, - self.n_particles)) + self.results.msds_by_particle = np.zeros( + (self.n_frames, self.n_particles) + ) self._position_array = np.zeros( - (self.n_frames, self.n_particles, self.dim_fac)) + (self.n_frames, self.n_particles, self.dim_fac) + ) # self.results.timeseries not set here def _parse_msd_type(self): - r""" Sets up the desired dimensionality of the MSD. - - """ - keys = {'x': [0], 'y': [1], 'z': [2], 'xy': [0, 1], - 'xz': [0, 2], 'yz': [1, 2], 'xyz': [0, 1, 2]} + r"""Sets up the desired dimensionality of the MSD.""" + keys = { + "x": [0], + "y": [1], + "z": [2], + "xy": [0, 1], + "xz": [0, 2], + "yz": [1, 2], + "xyz": [0, 1, 2], + } self.msd_type = self.msd_type.lower() @@ -356,19 +368,19 @@ def _parse_msd_type(self): self._dim = keys[self.msd_type] except KeyError: raise ValueError( - 'invalid msd_type: {} specified, please specify one of xyz, ' - 'xy, xz, yz, x, y, z'.format(self.msd_type)) + "invalid msd_type: {} specified, please specify one of xyz, " + "xy, xz, yz, x, y, z".format(self.msd_type) + ) self.dim_fac = len(self._dim) def _single_frame(self): - r""" Constructs array of positions for MSD calculation. - - """ + r"""Constructs array of positions for MSD calculation.""" # shape of position array set here, use span in last dimension # from this point on - self._position_array[self._frame_index] = ( - self.ag.positions[:, self._dim]) + self._position_array[self._frame_index] = self.ag.positions[ + :, self._dim + ] def _conclude(self): if self.fft: @@ -377,9 +389,7 @@ def _conclude(self): self._conclude_simple() def _conclude_simple(self): - r""" Calculates the MSD via the simple "windowed" algorithm. - - """ + r"""Calculates the MSD via the simple "windowed" algorithm.""" lagtimes = np.arange(1, self.n_frames) positions = self._position_array.astype(np.float64) for lag in tqdm(lagtimes): @@ -389,13 +399,12 @@ def _conclude_simple(self): self.results.timeseries = self.results.msds_by_particle.mean(axis=1) def _conclude_fft(self): # with FFT, np.float64 bit prescision required. - r""" Calculates the MSD via the FCA fast correlation algorithm. - - """ + r"""Calculates the MSD via the FCA fast correlation algorithm.""" try: import tidynamics except ImportError: - raise ImportError("""ERROR --- tidynamics was not found! + raise ImportError( + """ERROR --- tidynamics was not found! tidynamics is required to compute an FFT based MSD (default) @@ -403,10 +412,12 @@ def _conclude_fft(self): # with FFT, np.float64 bit prescision required. pip install tidynamics - or set fft=False""") + or set fft=False""" + ) positions = self._position_array.astype(np.float64) for n in tqdm(range(self.n_particles)): self.results.msds_by_particle[:, n] = tidynamics.msd( - positions[:, n, :]) + positions[:, n, :] + ) self.results.timeseries = self.results.msds_by_particle.mean(axis=1) diff --git a/package/MDAnalysis/analysis/nucleicacids.py b/package/MDAnalysis/analysis/nucleicacids.py index b0f5013e799..bf68a954cb3 100644 --- a/package/MDAnalysis/analysis/nucleicacids.py +++ b/package/MDAnalysis/analysis/nucleicacids.py @@ -170,20 +170,21 @@ class NucPairDist(AnalysisBase): @classmethod def get_supported_backends(cls): - return ('serial', 'multiprocessing', 'dask') + return ("serial", "multiprocessing", "dask") _s1: mda.AtomGroup _s2: mda.AtomGroup _n_sel: int - - def __init__(self, selection1: List[mda.AtomGroup], - selection2: List[mda.AtomGroup], - **kwargs) -> None: - super( - NucPairDist, - self).__init__( - selection1[0].universe.trajectory, - **kwargs) + + def __init__( + self, + selection1: List[mda.AtomGroup], + selection2: List[mda.AtomGroup], + **kwargs, + ) -> None: + super(NucPairDist, self).__init__( + selection1[0].universe.trajectory, **kwargs + ) if len(selection1) != len(selection2): raise ValueError("Selections must be same length") @@ -199,10 +200,15 @@ def __init__(self, selection1: List[mda.AtomGroup], @staticmethod def select_strand_atoms( - strand1: ResidueGroup, strand2: ResidueGroup, - a1_name: str, a2_name: str, g_name: str = 'G', - a_name: str = 'A', u_name: str = 'U', - t_name: str = 'T', c_name: str = 'C' + strand1: ResidueGroup, + strand2: ResidueGroup, + a1_name: str, + a2_name: str, + g_name: str = "G", + a_name: str = "A", + u_name: str = "U", + t_name: str = "T", + c_name: str = "C", ) -> Tuple[List[mda.AtomGroup], List[mda.AtomGroup]]: r""" A helper method for nucleic acid pair distance analyses. @@ -266,17 +272,18 @@ def select_strand_atoms( f"AtomGroup in {pair} is not a valid nucleic acid" ) - ag1 = pair[0].atoms.select_atoms(f'name {a1}') - ag2 = pair[1].atoms.select_atoms(f'name {a2}') + ag1 = pair[0].atoms.select_atoms(f"name {a1}") + ag2 = pair[1].atoms.select_atoms(f"name {a2}") if not all(len(ag) > 0 for ag in [ag1, ag2]): - err_info: Tuple[Residue, str] = (pair[0], a1) \ - if len(ag1) == 0 else (pair[1], a2) + err_info: Tuple[Residue, str] = ( + (pair[0], a1) if len(ag1) == 0 else (pair[1], a2) + ) raise ValueError( ( f"{err_info[0]} returns an empty AtomGroup" - "with selection string \"name {a2}\"" + 'with selection string "name {a2}"' ) ) @@ -291,22 +298,22 @@ def _prepare(self) -> None: ) def _single_frame(self) -> None: - dist: np.ndarray = calc_bonds( - self._s1.positions, self._s2.positions - ) + dist: np.ndarray = calc_bonds(self._s1.positions, self._s2.positions) self.results.distances[self._frame_index, :] = dist def _conclude(self) -> None: - self.results['pair_distances'] = self.results['distances'] + self.results["pair_distances"] = self.results["distances"] # TODO: remove pair_distances in 3.0.0 def _get_aggregator(self): - return ResultsGroup(lookup={ - 'distances': ResultsGroup.ndarray_vstack, - } + return ResultsGroup( + lookup={ + "distances": ResultsGroup.ndarray_vstack, + } ) + class WatsonCrickDist(NucPairDist): r""" Watson-Crick base pair distance for selected @@ -426,11 +433,19 @@ class WatsonCrickDist(NucPairDist): but it is **deprecated** and will be removed in release 3.0.0. """ - def __init__(self, strand1: ResidueClass, strand2: ResidueClass, - n1_name: str = 'N1', n3_name: str = "N3", - g_name: str = 'G', a_name: str = 'A', u_name: str = 'U', - t_name: str = 'T', c_name: str = 'C', - **kwargs) -> None: + def __init__( + self, + strand1: ResidueClass, + strand2: ResidueClass, + n1_name: str = "N1", + n3_name: str = "N3", + g_name: str = "G", + a_name: str = "A", + u_name: str = "U", + t_name: str = "T", + c_name: str = "C", + **kwargs, + ) -> None: def verify_strand(strand: ResidueClass) -> ResidueGroup: # Helper method to verify the strands @@ -456,11 +471,18 @@ def verify_strand(strand: ResidueClass) -> ResidueGroup: strand1: ResidueGroup = verify_strand(strand1) strand2: ResidueGroup = verify_strand(strand2) - strand_atomgroups: Tuple[List[mda.AtomGroup], List[mda.AtomGroup]] = \ + strand_atomgroups: Tuple[List[mda.AtomGroup], List[mda.AtomGroup]] = ( self.select_strand_atoms( - strand1, strand2, n1_name, n3_name, - g_name=g_name, a_name=a_name, - t_name=t_name, u_name=u_name, c_name=c_name + strand1, + strand2, + n1_name, + n3_name, + g_name=g_name, + a_name=a_name, + t_name=t_name, + u_name=u_name, + c_name=c_name, + ) ) super(WatsonCrickDist, self).__init__( @@ -531,17 +553,32 @@ class MinorPairDist(NucPairDist): .. versionadded:: 2.7.0 """ - def __init__(self, strand1: ResidueGroup, strand2: ResidueGroup, - o2_name: str = 'O2', c2_name: str = "C2", - g_name: str = 'G', a_name: str = 'A', u_name: str = 'U', - t_name: str = 'T', c_name: str = 'C', - **kwargs) -> None: - - selections: Tuple[List[mda.AtomGroup], List[mda.AtomGroup]] = \ + def __init__( + self, + strand1: ResidueGroup, + strand2: ResidueGroup, + o2_name: str = "O2", + c2_name: str = "C2", + g_name: str = "G", + a_name: str = "A", + u_name: str = "U", + t_name: str = "T", + c_name: str = "C", + **kwargs, + ) -> None: + + selections: Tuple[List[mda.AtomGroup], List[mda.AtomGroup]] = ( self.select_strand_atoms( - strand1, strand2, c2_name, o2_name, - g_name=g_name, a_name=a_name, - t_name=t_name, u_name=u_name, c_name=c_name + strand1, + strand2, + c2_name, + o2_name, + g_name=g_name, + a_name=a_name, + t_name=t_name, + u_name=u_name, + c_name=c_name, + ) ) super(MinorPairDist, self).__init__( @@ -614,17 +651,32 @@ class MajorPairDist(NucPairDist): .. versionadded:: 2.7.0 """ - def __init__(self, strand1: ResidueGroup, strand2: ResidueGroup, - n4_name: str = 'N4', o6_name: str = "O6", - g_name: str = 'G', a_name: str = 'A', u_name: str = 'U', - t_name: str = 'T', c_name: str = 'C', - **kwargs) -> None: - - selections: Tuple[List[mda.AtomGroup], List[mda.AtomGroup]] = \ + def __init__( + self, + strand1: ResidueGroup, + strand2: ResidueGroup, + n4_name: str = "N4", + o6_name: str = "O6", + g_name: str = "G", + a_name: str = "A", + u_name: str = "U", + t_name: str = "T", + c_name: str = "C", + **kwargs, + ) -> None: + + selections: Tuple[List[mda.AtomGroup], List[mda.AtomGroup]] = ( self.select_strand_atoms( - strand1, strand2, o6_name, n4_name, g_name=g_name, - a_name=a_name, t_name=t_name, u_name=u_name, - c_name=c_name + strand1, + strand2, + o6_name, + n4_name, + g_name=g_name, + a_name=a_name, + t_name=t_name, + u_name=u_name, + c_name=c_name, + ) ) super(MajorPairDist, self).__init__( diff --git a/package/MDAnalysis/analysis/nuclinfo.py b/package/MDAnalysis/analysis/nuclinfo.py index 39be7191d8a..a6772599142 100644 --- a/package/MDAnalysis/analysis/nuclinfo.py +++ b/package/MDAnalysis/analysis/nuclinfo.py @@ -130,13 +130,32 @@ def wc_pair(universe, i, bp, seg1="SYSTEM", seg2="SYSTEM"): .. versionadded:: 0.7.6 """ - if universe.select_atoms(" resid {0!s} ".format(i)).resnames[0] in ["DC", "DT", "U", "C", "T", "CYT", "THY", "URA"]: + if universe.select_atoms(" resid {0!s} ".format(i)).resnames[0] in [ + "DC", + "DT", + "U", + "C", + "T", + "CYT", + "THY", + "URA", + ]: a1, a2 = "N3", "N1" - if universe.select_atoms(" resid {0!s} ".format(i)).resnames[0] in ["DG", "DA", "A", "G", "ADE", "GUA"]: + if universe.select_atoms(" resid {0!s} ".format(i)).resnames[0] in [ + "DG", + "DA", + "A", + "G", + "ADE", + "GUA", + ]: a1, a2 = "N1", "N3" - wc_dist = universe.select_atoms("(segid {0!s} and resid {1!s} and name {2!s}) " - "or (segid {3!s} and resid {4!s} and name {5!s}) " - .format(seg1, i, a1, seg2, bp, a2)) + wc_dist = universe.select_atoms( + "(segid {0!s} and resid {1!s} and name {2!s}) " + "or (segid {3!s} and resid {4!s} and name {5!s}) ".format( + seg1, i, a1, seg2, bp, a2 + ) + ) wc = mdamath.norm(wc_dist[0].position - wc_dist[1].position) return wc @@ -172,13 +191,32 @@ def minor_pair(universe, i, bp, seg1="SYSTEM", seg2="SYSTEM"): .. versionadded:: 0.7.6 """ - if universe.select_atoms(" resid {0!s} ".format(i)).resnames[0] in ["DC", "DT", "U", "C", "T", "CYT", "THY", "URA"]: + if universe.select_atoms(" resid {0!s} ".format(i)).resnames[0] in [ + "DC", + "DT", + "U", + "C", + "T", + "CYT", + "THY", + "URA", + ]: a1, a2 = "O2", "C2" - if universe.select_atoms(" resid {0!s} ".format(i)).resnames[0] in ["DG", "DA", "A", "G", "ADE", "GUA"]: + if universe.select_atoms(" resid {0!s} ".format(i)).resnames[0] in [ + "DG", + "DA", + "A", + "G", + "ADE", + "GUA", + ]: a1, a2 = "C2", "O2" - c2o2_dist = universe.select_atoms("(segid {0!s} and resid {1!s} and name {2!s}) " - "or (segid {3!s} and resid {4!s} and name {5!s})" - .format(seg1, i, a1, seg2, bp, a2)) + c2o2_dist = universe.select_atoms( + "(segid {0!s} and resid {1!s} and name {2!s}) " + "or (segid {3!s} and resid {4!s} and name {5!s})".format( + seg1, i, a1, seg2, bp, a2 + ) + ) c2o2 = mdamath.norm(c2o2_dist[0].position - c2o2_dist[1].position) return c2o2 @@ -215,19 +253,48 @@ def major_pair(universe, i, bp, seg1="SYSTEM", seg2="SYSTEM"): .. versionadded:: 0.7.6 """ - if universe.select_atoms(" resid {0!s} ".format(i)).resnames[0] in ["DC", "DG", "C", "G", "CYT", "GUA"]: - if universe.select_atoms(" resid {0!s} ".format(i)).resnames[0] in ["DC", "C", "CYT"]: + if universe.select_atoms(" resid {0!s} ".format(i)).resnames[0] in [ + "DC", + "DG", + "C", + "G", + "CYT", + "GUA", + ]: + if universe.select_atoms(" resid {0!s} ".format(i)).resnames[0] in [ + "DC", + "C", + "CYT", + ]: a1, a2 = "N4", "O6" else: a1, a2 = "O6", "N4" - if universe.select_atoms(" resid {0!s} ".format(i)).resnames[0] in ["DT", "DA", "A", "T", "U", "ADE", "THY", "URA"]: - if universe.select_atoms(" resid {0!s} ".format(i)).resnames[0] in ["DT", "T", "THY", "U", "URA"]: + if universe.select_atoms(" resid {0!s} ".format(i)).resnames[0] in [ + "DT", + "DA", + "A", + "T", + "U", + "ADE", + "THY", + "URA", + ]: + if universe.select_atoms(" resid {0!s} ".format(i)).resnames[0] in [ + "DT", + "T", + "THY", + "U", + "URA", + ]: a1, a2 = "O4", "N6" else: a1, a2 = "N6", "O4" - no_dist = universe.select_atoms("(segid {0!s} and resid {1!s} and name {2!s}) " - "or (segid {3!s} and resid {4!s} and name {5!s}) " - .format(seg1, i, a1, seg2, bp, a2)) + no_dist = universe.select_atoms( + "(segid {0!s} and resid {1!s} and name {2!s}) " + "or (segid {3!s} and resid {4!s} and name {5!s}) ".format( + seg1, i, a1, seg2, bp, a2 + ) + ) major = mdamath.norm(no_dist[0].position - no_dist[1].position) return major @@ -255,11 +322,11 @@ def phase_cp(universe, seg, i): .. versionadded:: 0.7.6 """ - atom1 = universe.select_atoms(" atom {0!s} {1!s} O4\' ".format(seg, i)) - atom2 = universe.select_atoms(" atom {0!s} {1!s} C1\' ".format(seg, i)) - atom3 = universe.select_atoms(" atom {0!s} {1!s} C2\' ".format(seg, i)) - atom4 = universe.select_atoms(" atom {0!s} {1!s} C3\' ".format(seg, i)) - atom5 = universe.select_atoms(" atom {0!s} {1!s} C4\' ".format(seg, i)) + atom1 = universe.select_atoms(" atom {0!s} {1!s} O4' ".format(seg, i)) + atom2 = universe.select_atoms(" atom {0!s} {1!s} C1' ".format(seg, i)) + atom3 = universe.select_atoms(" atom {0!s} {1!s} C2' ".format(seg, i)) + atom4 = universe.select_atoms(" atom {0!s} {1!s} C3' ".format(seg, i)) + atom5 = universe.select_atoms(" atom {0!s} {1!s} C4' ".format(seg, i)) data1 = atom1.positions data2 = atom2.positions @@ -274,13 +341,21 @@ def phase_cp(universe, seg, i): r4 = data4 - r0 r5 = data5 - r0 - R1 = ((r1 * sin(2 * pi * 0.0 / 5.0)) + (r2 * sin(2 * pi * 1.0 / 5.0)) + - (r3 * sin(2 * pi * 2.0 / 5.0)) + (r4 * sin(2 * pi * 3.0 / 5.0)) + - (r5 * sin(2 * pi * 4.0 / 5.0))) - - R2 = ((r1 * cos(2 * pi * 0.0 / 5.0)) + (r2 * cos(2 * pi * 1.0 / 5.0)) + - (r3 * cos(2 * pi * 2.0 / 5.0)) + (r4 * cos(2 * pi * 3.0 / 5.0)) + - (r5 * cos(2 * pi * 4.0 / 5.0))) + R1 = ( + (r1 * sin(2 * pi * 0.0 / 5.0)) + + (r2 * sin(2 * pi * 1.0 / 5.0)) + + (r3 * sin(2 * pi * 2.0 / 5.0)) + + (r4 * sin(2 * pi * 3.0 / 5.0)) + + (r5 * sin(2 * pi * 4.0 / 5.0)) + ) + + R2 = ( + (r1 * cos(2 * pi * 0.0 / 5.0)) + + (r2 * cos(2 * pi * 1.0 / 5.0)) + + (r3 * cos(2 * pi * 2.0 / 5.0)) + + (r4 * cos(2 * pi * 3.0 / 5.0)) + + (r5 * cos(2 * pi * 4.0 / 5.0)) + ) x = np.cross(R1[0], R2[0]) n = x / sqrt(pow(x[0], 2) + pow(x[1], 2) + pow(x[2], 2)) @@ -291,15 +366,27 @@ def phase_cp(universe, seg, i): r4_d = np.dot(r4, n) r5_d = np.dot(r5, n) - D = ((r1_d * sin(4 * pi * 0.0 / 5.0)) + (r2_d * sin(4 * pi * 1.0 / 5.0)) - + (r3_d * sin(4 * pi * 2.0 / 5.0)) + (r4_d * sin(4 * pi * 3.0 / 5.0)) - + (r5_d * sin(4 * pi * 4.0 / 5.0))) * -1 * sqrt(2.0 / 5.0) - - C = ((r1_d * cos(4 * pi * 0.0 / 5.0)) + (r2_d * cos(4 * pi * 1.0 / 5.0)) - + (r3_d * cos(4 * pi * 2.0 / 5.0)) + (r4_d * cos(4 * pi * 3.0 / 5.0)) - + (r5_d * cos(4 * pi * 4.0 / 5.0))) * sqrt(2.0 / 5.0) - - phase_ang = (np.arctan2(D, C) + (pi / 2.)) * 180. / pi + D = ( + ( + (r1_d * sin(4 * pi * 0.0 / 5.0)) + + (r2_d * sin(4 * pi * 1.0 / 5.0)) + + (r3_d * sin(4 * pi * 2.0 / 5.0)) + + (r4_d * sin(4 * pi * 3.0 / 5.0)) + + (r5_d * sin(4 * pi * 4.0 / 5.0)) + ) + * -1 + * sqrt(2.0 / 5.0) + ) + + C = ( + (r1_d * cos(4 * pi * 0.0 / 5.0)) + + (r2_d * cos(4 * pi * 1.0 / 5.0)) + + (r3_d * cos(4 * pi * 2.0 / 5.0)) + + (r4_d * cos(4 * pi * 3.0 / 5.0)) + + (r5_d * cos(4 * pi * 4.0 / 5.0)) + ) * sqrt(2.0 / 5.0) + + phase_ang = (np.arctan2(D, C) + (pi / 2.0)) * 180.0 / pi return phase_ang % 360 @@ -325,30 +412,40 @@ def phase_as(universe, seg, i): .. versionadded:: 0.7.6 """ - angle1 = universe.select_atoms(" atom {0!s} {1!s} C1\' ".format(seg, i), - " atom {0!s} {1!s} C2\' ".format(seg, i), - " atom {0!s} {1!s} C3\' ".format(seg, i), - " atom {0!s} {1!s} C4\' ".format(seg, i)) - - angle2 = universe.select_atoms(" atom {0!s} {1!s} C2\' ".format(seg, i), - " atom {0!s} {1!s} C3\' ".format(seg, i), - " atom {0!s} {1!s} C4\' ".format(seg, i), - " atom {0!s} {1!s} O4\' ".format(seg, i)) - - angle3 = universe.select_atoms(" atom {0!s} {1!s} C3\' ".format(seg, i), - " atom {0!s} {1!s} C4\' ".format(seg, i), - " atom {0!s} {1!s} O4\' ".format(seg, i), - " atom {0!s} {1!s} C1\' ".format(seg, i)) - - angle4 = universe.select_atoms(" atom {0!s} {1!s} C4\' ".format(seg, i), - " atom {0!s} {1!s} O4\' ".format(seg, i), - " atom {0!s} {1!s} C1\' ".format(seg, i), - " atom {0!s} {1!s} C2\' ".format(seg, i)) - - angle5 = universe.select_atoms(" atom {0!s} {1!s} O4\' ".format(seg, i), - " atom {0!s} {1!s} C1\' ".format(seg, i), - " atom {0!s} {1!s} C2\' ".format(seg, i), - " atom {0!s} {1!s} C3\' ".format(seg, i)) + angle1 = universe.select_atoms( + " atom {0!s} {1!s} C1' ".format(seg, i), + " atom {0!s} {1!s} C2' ".format(seg, i), + " atom {0!s} {1!s} C3' ".format(seg, i), + " atom {0!s} {1!s} C4' ".format(seg, i), + ) + + angle2 = universe.select_atoms( + " atom {0!s} {1!s} C2' ".format(seg, i), + " atom {0!s} {1!s} C3' ".format(seg, i), + " atom {0!s} {1!s} C4' ".format(seg, i), + " atom {0!s} {1!s} O4' ".format(seg, i), + ) + + angle3 = universe.select_atoms( + " atom {0!s} {1!s} C3' ".format(seg, i), + " atom {0!s} {1!s} C4' ".format(seg, i), + " atom {0!s} {1!s} O4' ".format(seg, i), + " atom {0!s} {1!s} C1' ".format(seg, i), + ) + + angle4 = universe.select_atoms( + " atom {0!s} {1!s} C4' ".format(seg, i), + " atom {0!s} {1!s} O4' ".format(seg, i), + " atom {0!s} {1!s} C1' ".format(seg, i), + " atom {0!s} {1!s} C2' ".format(seg, i), + ) + + angle5 = universe.select_atoms( + " atom {0!s} {1!s} O4' ".format(seg, i), + " atom {0!s} {1!s} C1' ".format(seg, i), + " atom {0!s} {1!s} C2' ".format(seg, i), + " atom {0!s} {1!s} C3' ".format(seg, i), + ) data1 = angle1.dihedral.value() data2 = angle2.dihedral.value() @@ -356,19 +453,31 @@ def phase_as(universe, seg, i): data4 = angle4.dihedral.value() data5 = angle5.dihedral.value() - B = ((data1 * sin(2 * 2 * pi * (1 - 1.) / 5.)) - + (data2 * sin(2 * 2 * pi * (2 - 1.) / 5.)) - + (data3 * sin(2 * 2 * pi * (3 - 1.) / 5.)) - + (data4 * sin(2 * 2 * pi * (4 - 1.) / 5.)) - + (data5 * sin(2 * 2 * pi * (5 - 1.) / 5.))) * -2. / 5. - - A = ((data1 * cos(2 * 2 * pi * (1 - 1.) / 5.)) - + (data2 * cos(2 * 2 * pi * (2 - 1.) / 5.)) - + (data3 * cos(2 * 2 * pi * (3 - 1.) / 5.)) - + (data4 * cos(2 * 2 * pi * (4 - 1.) / 5.)) - + (data5 * cos(2 * 2 * pi * (5 - 1.) / 5.))) * 2. / 5. - - phase_ang = np.arctan2(B, A) * 180. / pi + B = ( + ( + (data1 * sin(2 * 2 * pi * (1 - 1.0) / 5.0)) + + (data2 * sin(2 * 2 * pi * (2 - 1.0) / 5.0)) + + (data3 * sin(2 * 2 * pi * (3 - 1.0) / 5.0)) + + (data4 * sin(2 * 2 * pi * (4 - 1.0) / 5.0)) + + (data5 * sin(2 * 2 * pi * (5 - 1.0) / 5.0)) + ) + * -2.0 + / 5.0 + ) + + A = ( + ( + (data1 * cos(2 * 2 * pi * (1 - 1.0) / 5.0)) + + (data2 * cos(2 * 2 * pi * (2 - 1.0) / 5.0)) + + (data3 * cos(2 * 2 * pi * (3 - 1.0) / 5.0)) + + (data4 * cos(2 * 2 * pi * (4 - 1.0) / 5.0)) + + (data5 * cos(2 * 2 * pi * (5 - 1.0) / 5.0)) + ) + * 2.0 + / 5.0 + ) + + phase_ang = np.arctan2(B, A) * 180.0 / pi return phase_ang % 360 @@ -401,44 +510,60 @@ def tors(universe, seg, i): .. versionadded:: 0.7.6 """ - a = universe.select_atoms(" atom {0!s} {1!s} O3\' ".format(seg, i - 1), - " atom {0!s} {1!s} P ".format(seg, i), - " atom {0!s} {1!s} O5\' ".format(seg, i), - " atom {0!s} {1!s} C5\' ".format(seg, i)) - - b = universe.select_atoms(" atom {0!s} {1!s} P ".format(seg, i), - " atom {0!s} {1!s} O5\' ".format(seg, i), - " atom {0!s} {1!s} C5\' ".format(seg, i), - " atom {0!s} {1!s} C4\' ".format(seg, i)) - - g = universe.select_atoms(" atom {0!s} {1!s} O5\' ".format(seg, i), - " atom {0!s} {1!s} C5\' ".format(seg, i), - " atom {0!s} {1!s} C4\' ".format(seg, i), - " atom {0!s} {1!s} C3\' ".format(seg, i)) - - d = universe.select_atoms(" atom {0!s} {1!s} C5\' ".format(seg, i), - " atom {0!s} {1!s} C4\' ".format(seg, i), - " atom {0!s} {1!s} C3\' ".format(seg, i), - " atom {0!s} {1!s} O3\' ".format(seg, i)) - - e = universe.select_atoms(" atom {0!s} {1!s} C4\' ".format(seg, i), - " atom {0!s} {1!s} C3\' ".format(seg, i), - " atom {0!s} {1!s} O3\' ".format(seg, i), - " atom {0!s} {1!s} P ".format(seg, i + 1)) - - z = universe.select_atoms(" atom {0!s} {1!s} C3\' ".format(seg, i), - " atom {0!s} {1!s} O3\' ".format(seg, i), - " atom {0!s} {1!s} P ".format(seg, i + 1), - " atom {0!s} {1!s} O5\' ".format(seg, i + 1)) - c = universe.select_atoms(" atom {0!s} {1!s} O4\' ".format(seg, i), - " atom {0!s} {1!s} C1\' ".format(seg, i), - " atom {0!s} {1!s} N9 ".format(seg, i), - " atom {0!s} {1!s} C4 ".format(seg, i)) + a = universe.select_atoms( + " atom {0!s} {1!s} O3' ".format(seg, i - 1), + " atom {0!s} {1!s} P ".format(seg, i), + " atom {0!s} {1!s} O5' ".format(seg, i), + " atom {0!s} {1!s} C5' ".format(seg, i), + ) + + b = universe.select_atoms( + " atom {0!s} {1!s} P ".format(seg, i), + " atom {0!s} {1!s} O5' ".format(seg, i), + " atom {0!s} {1!s} C5' ".format(seg, i), + " atom {0!s} {1!s} C4' ".format(seg, i), + ) + + g = universe.select_atoms( + " atom {0!s} {1!s} O5' ".format(seg, i), + " atom {0!s} {1!s} C5' ".format(seg, i), + " atom {0!s} {1!s} C4' ".format(seg, i), + " atom {0!s} {1!s} C3' ".format(seg, i), + ) + + d = universe.select_atoms( + " atom {0!s} {1!s} C5' ".format(seg, i), + " atom {0!s} {1!s} C4' ".format(seg, i), + " atom {0!s} {1!s} C3' ".format(seg, i), + " atom {0!s} {1!s} O3' ".format(seg, i), + ) + + e = universe.select_atoms( + " atom {0!s} {1!s} C4' ".format(seg, i), + " atom {0!s} {1!s} C3' ".format(seg, i), + " atom {0!s} {1!s} O3' ".format(seg, i), + " atom {0!s} {1!s} P ".format(seg, i + 1), + ) + + z = universe.select_atoms( + " atom {0!s} {1!s} C3' ".format(seg, i), + " atom {0!s} {1!s} O3' ".format(seg, i), + " atom {0!s} {1!s} P ".format(seg, i + 1), + " atom {0!s} {1!s} O5' ".format(seg, i + 1), + ) + c = universe.select_atoms( + " atom {0!s} {1!s} O4' ".format(seg, i), + " atom {0!s} {1!s} C1' ".format(seg, i), + " atom {0!s} {1!s} N9 ".format(seg, i), + " atom {0!s} {1!s} C4 ".format(seg, i), + ) if len(c) < 4: - c = universe.select_atoms(" atom {0!s} {1!s} O4\' ".format(seg, i), - " atom {0!s} {1!s} C1\' ".format(seg, i), - " atom {0!s} {1!s} N1 ".format(seg, i), - " atom {0!s} {1!s} C2 ".format(seg, i)) + c = universe.select_atoms( + " atom {0!s} {1!s} O4' ".format(seg, i), + " atom {0!s} {1!s} C1' ".format(seg, i), + " atom {0!s} {1!s} N1 ".format(seg, i), + " atom {0!s} {1!s} C2 ".format(seg, i), + ) alpha = a.dihedral.value() % 360 beta = b.dihedral.value() % 360 @@ -473,10 +598,12 @@ def tors_alpha(universe, seg, i): .. versionadded:: 0.7.6 """ - a = universe.select_atoms(" atom {0!s} {1!s} O3\' ".format(seg, i - 1), - " atom {0!s} {1!s} P ".format(seg, i), - " atom {0!s} {1!s} O5\' ".format(seg, i), - " atom {0!s} {1!s} C5\' ".format(seg, i)) + a = universe.select_atoms( + " atom {0!s} {1!s} O3' ".format(seg, i - 1), + " atom {0!s} {1!s} P ".format(seg, i), + " atom {0!s} {1!s} O5' ".format(seg, i), + " atom {0!s} {1!s} C5' ".format(seg, i), + ) alpha = a.dihedral.value() % 360 return alpha @@ -503,16 +630,18 @@ def tors_beta(universe, seg, i): .. versionadded:: 0.7.6 """ - b = universe.select_atoms(" atom {0!s} {1!s} P ".format(seg, i), - " atom {0!s} {1!s} O5\' ".format(seg, i), - " atom {0!s} {1!s} C5\' ".format(seg, i), - " atom {0!s} {1!s} C4\' ".format(seg, i)) + b = universe.select_atoms( + " atom {0!s} {1!s} P ".format(seg, i), + " atom {0!s} {1!s} O5' ".format(seg, i), + " atom {0!s} {1!s} C5' ".format(seg, i), + " atom {0!s} {1!s} C4' ".format(seg, i), + ) beta = b.dihedral.value() % 360 return beta def tors_gamma(universe, seg, i): - """ Gamma backbone dihedral + """Gamma backbone dihedral The dihedral is computed based on position atoms for resid `i`. @@ -533,10 +662,12 @@ def tors_gamma(universe, seg, i): .. versionadded:: 0.7.6 """ - g = universe.select_atoms(" atom {0!s} {1!s} O5\' ".format(seg, i), - " atom {0!s} {1!s} C5\' ".format(seg, i), - " atom {0!s} {1!s} C4\' ".format(seg, i), - " atom {0!s} {1!s} C3\' ".format(seg, i)) + g = universe.select_atoms( + " atom {0!s} {1!s} O5' ".format(seg, i), + " atom {0!s} {1!s} C5' ".format(seg, i), + " atom {0!s} {1!s} C4' ".format(seg, i), + " atom {0!s} {1!s} C3' ".format(seg, i), + ) gamma = g.dihedral.value() % 360 return gamma @@ -563,10 +694,12 @@ def tors_delta(universe, seg, i): .. versionadded:: 0.7.6 """ - d = universe.select_atoms(" atom {0!s} {1!s} C5\' ".format(seg, i), - " atom {0!s} {1!s} C4\' ".format(seg, i), - " atom {0!s} {1!s} C3\' ".format(seg, i), - " atom {0!s} {1!s} O3\' ".format(seg, i)) + d = universe.select_atoms( + " atom {0!s} {1!s} C5' ".format(seg, i), + " atom {0!s} {1!s} C4' ".format(seg, i), + " atom {0!s} {1!s} C3' ".format(seg, i), + " atom {0!s} {1!s} O3' ".format(seg, i), + ) delta = d.dihedral.value() % 360 return delta @@ -593,10 +726,12 @@ def tors_eps(universe, seg, i): .. versionadded:: 0.7.6 """ - e = universe.select_atoms(" atom {0!s} {1!s} C4\' ".format(seg, i), - " atom {0!s} {1!s} C3\' ".format(seg, i), - " atom {0!s} {1!s} O3\' ".format(seg, i), - " atom {0!s} {1!s} P ".format(seg, i + 1)) + e = universe.select_atoms( + " atom {0!s} {1!s} C4' ".format(seg, i), + " atom {0!s} {1!s} C3' ".format(seg, i), + " atom {0!s} {1!s} O3' ".format(seg, i), + " atom {0!s} {1!s} P ".format(seg, i + 1), + ) epsilon = e.dihedral.value() % 360 return epsilon @@ -623,10 +758,12 @@ def tors_zeta(universe, seg, i): .. versionadded:: 0.7.6 """ - z = universe.select_atoms(" atom {0!s} {1!s} C3\' ".format(seg, i), - " atom {0!s} {1!s} O3\' ".format(seg, i), - " atom {0!s} {1!s} P ".format(seg, i + 1), - " atom {0!s} {1!s} O5\' ".format(seg, i + 1)) + z = universe.select_atoms( + " atom {0!s} {1!s} C3' ".format(seg, i), + " atom {0!s} {1!s} O3' ".format(seg, i), + " atom {0!s} {1!s} P ".format(seg, i + 1), + " atom {0!s} {1!s} O5' ".format(seg, i + 1), + ) zeta = z.dihedral.value() % 360 return zeta @@ -653,15 +790,19 @@ def tors_chi(universe, seg, i): .. versionadded:: 0.7.6 """ - c = universe.select_atoms(" atom {0!s} {1!s} O4\' ".format(seg, i), - " atom {0!s} {1!s} C1\' ".format(seg, i), - " atom {0!s} {1!s} N9 ".format(seg, i), - " atom {0!s} {1!s} C4 ".format(seg, i)) + c = universe.select_atoms( + " atom {0!s} {1!s} O4' ".format(seg, i), + " atom {0!s} {1!s} C1' ".format(seg, i), + " atom {0!s} {1!s} N9 ".format(seg, i), + " atom {0!s} {1!s} C4 ".format(seg, i), + ) if len(c) < 4: - c = universe.select_atoms(" atom {0!s} {1!s} O4\' ".format(seg, i), - " atom {0!s} {1!s} C1\' ".format(seg, i), - " atom {0!s} {1!s} N1 ".format(seg, i), - " atom {0!s} {1!s} C2 ".format(seg, i)) + c = universe.select_atoms( + " atom {0!s} {1!s} O4' ".format(seg, i), + " atom {0!s} {1!s} C1' ".format(seg, i), + " atom {0!s} {1!s} N1 ".format(seg, i), + " atom {0!s} {1!s} C2 ".format(seg, i), + ) chi = c.dihedral.value() % 360 return chi @@ -691,22 +832,27 @@ def hydroxyl(universe, seg, i): .. versionadded:: 0.7.6 """ - h = universe.select_atoms("atom {0!s} {1!s} C1'".format(seg, i), - "atom {0!s} {1!s} C2'".format(seg, i), - "atom {0!s} {1!s} O2'".format(seg, i), - "atom {0!s} {1!s} H2'".format(seg, i)) + h = universe.select_atoms( + "atom {0!s} {1!s} C1'".format(seg, i), + "atom {0!s} {1!s} C2'".format(seg, i), + "atom {0!s} {1!s} O2'".format(seg, i), + "atom {0!s} {1!s} H2'".format(seg, i), + ) try: hydr = h.dihedral.value() % 360 except ValueError: - errmsg = (f"Resid {i} does not contain atoms C1', C2', O2', H2' but " - f"atoms {list(h.atoms)}") + errmsg = ( + f"Resid {i} does not contain atoms C1', C2', O2', H2' but " + f"atoms {list(h.atoms)}" + ) raise ValueError(errmsg) from None return hydr -def pseudo_dihe_baseflip(universe, bp1, bp2, i, - seg1="SYSTEM", seg2="SYSTEM", seg3="SYSTEM"): +def pseudo_dihe_baseflip( + universe, bp1, bp2, i, seg1="SYSTEM", seg2="SYSTEM", seg3="SYSTEM" +): """pseudo dihedral for flipped bases. Useful only for nucleic acid base flipping The dihedral is computed based on position atoms for resid `i` @@ -742,13 +888,25 @@ def pseudo_dihe_baseflip(universe, bp1, bp2, i, """ bf1 = universe.select_atoms( " ( segid {0!s} and resid {1!s} and nucleicbase ) " - "or ( segid {2!s} and resid {3!s} and nucleicbase ) " - .format( seg1, bp1, seg2, bp2)) - bf4 = universe.select_atoms("(segid {0!s} and resid {1!s} and nucleicbase) ".format(seg3, i)) - bf2 = universe.select_atoms("(segid {0!s} and resid {1!s} and nucleicsugar) ".format(seg2, bp2)) - bf3 = universe.select_atoms("(segid {0!s} and resid {1!s} and nucleicsugar) ".format(seg3, i)) - x = [bf1.center_of_mass(), bf2.center_of_mass(), - bf3.center_of_mass(), bf4.center_of_mass()] + "or ( segid {2!s} and resid {3!s} and nucleicbase ) ".format( + seg1, bp1, seg2, bp2 + ) + ) + bf4 = universe.select_atoms( + "(segid {0!s} and resid {1!s} and nucleicbase) ".format(seg3, i) + ) + bf2 = universe.select_atoms( + "(segid {0!s} and resid {1!s} and nucleicsugar) ".format(seg2, bp2) + ) + bf3 = universe.select_atoms( + "(segid {0!s} and resid {1!s} and nucleicsugar) ".format(seg3, i) + ) + x = [ + bf1.center_of_mass(), + bf2.center_of_mass(), + bf3.center_of_mass(), + bf4.center_of_mass(), + ] pseudo = mdamath.dihedral(x[0] - x[1], x[1] - x[2], x[2] - x[3]) pseudo = np.rad2deg(pseudo) % 360 return pseudo diff --git a/package/MDAnalysis/analysis/pca.py b/package/MDAnalysis/analysis/pca.py index cbf4cb588c8..fddbf7d0092 100644 --- a/package/MDAnalysis/analysis/pca.py +++ b/package/MDAnalysis/analysis/pca.py @@ -239,10 +239,18 @@ class PCA(AnalysisBase): incorrectly handle cases where the ``frame`` argument was passed. """ + _analysis_algorithm_is_parallelizable = False - def __init__(self, universe, select='all', align=False, mean=None, - n_components=None, **kwargs): + def __init__( + self, + universe, + select="all", + align=False, + mean=None, + n_components=None, + **kwargs, + ): super(PCA, self).__init__(universe.trajectory, **kwargs) self._u = universe @@ -268,15 +276,18 @@ def _prepare(self): else: self.mean = np.asarray(self._mean) if self.mean.shape[0] != self._n_atoms: - raise ValueError('Number of atoms in reference ({}) does ' - 'not match number of atoms in the ' - 'selection ({})'.format(self._n_atoms, - self.mean.shape[0])) + raise ValueError( + "Number of atoms in reference ({}) does " + "not match number of atoms in the " + "selection ({})".format(self._n_atoms, self.mean.shape[0]) + ) self._calc_mean = False if self.n_frames == 1: - raise ValueError('No covariance information can be gathered from a' - 'single trajectory frame.\n') + raise ValueError( + "No covariance information can be gathered from a" + "single trajectory frame.\n" + ) n_dim = self._n_atoms * 3 self.cov = np.zeros((n_dim, n_dim)) self._ref_atom_positions = self._reference.positions @@ -284,15 +295,20 @@ def _prepare(self): self._ref_atom_positions -= self._ref_cog if self._calc_mean: - for ts in ProgressBar(self._sliced_trajectory, - verbose=self._verbose, desc="Mean Calculation"): + for ts in ProgressBar( + self._sliced_trajectory, + verbose=self._verbose, + desc="Mean Calculation", + ): if self.align: mobile_cog = self._atoms.center_of_geometry() - mobile_atoms, old_rmsd = _fit_to(self._atoms.positions - mobile_cog, - self._ref_atom_positions, - self._atoms, - mobile_com=mobile_cog, - ref_com=self._ref_cog) + mobile_atoms, old_rmsd = _fit_to( + self._atoms.positions - mobile_cog, + self._ref_atom_positions, + self._atoms, + mobile_com=mobile_cog, + ref_com=self._ref_cog, + ) self.mean += self._atoms.positions self.mean /= self.n_frames @@ -301,11 +317,13 @@ def _prepare(self): def _single_frame(self): if self.align: mobile_cog = self._atoms.center_of_geometry() - mobile_atoms, old_rmsd = _fit_to(self._atoms.positions - mobile_cog, - self._ref_atom_positions, - self._atoms, - mobile_com=mobile_cog, - ref_com=self._ref_cog) + mobile_atoms, old_rmsd = _fit_to( + self._atoms.positions - mobile_cog, + self._ref_atom_positions, + self._atoms, + mobile_com=mobile_cog, + ref_com=self._ref_cog, + ) # now all structures are aligned to reference x = mobile_atoms.positions.ravel() else: @@ -324,25 +342,31 @@ def _conclude(self): @property def p_components(self): - wmsg = ("The `p_components` attribute was deprecated in " - "MDAnalysis 2.0.0 and will be removed in MDAnalysis 3.0.0. " - "Please use `results.p_components` instead.") + wmsg = ( + "The `p_components` attribute was deprecated in " + "MDAnalysis 2.0.0 and will be removed in MDAnalysis 3.0.0. " + "Please use `results.p_components` instead." + ) warnings.warn(wmsg, DeprecationWarning) return self.results.p_components @property def variance(self): - wmsg = ("The `variance` attribute was deprecated in " - "MDAnalysis 2.0.0 and will be removed in MDAnalysis 3.0.0. " - "Please use `results.variance` instead.") + wmsg = ( + "The `variance` attribute was deprecated in " + "MDAnalysis 2.0.0 and will be removed in MDAnalysis 3.0.0. " + "Please use `results.variance` instead." + ) warnings.warn(wmsg, DeprecationWarning) return self.results.variance @property def cumulated_variance(self): - wmsg = ("The `cumulated_variance` attribute was deprecated in " - "MDAnalysis 2.0.0 and will be removed in MDAnalysis 3.0.0. " - "Please use `results.cumulated_variance` instead.") + wmsg = ( + "The `cumulated_variance` attribute was deprecated in " + "MDAnalysis 2.0.0 and will be removed in MDAnalysis 3.0.0. " + "Please use `results.cumulated_variance` instead." + ) warnings.warn(wmsg, DeprecationWarning) return self.results.cumulated_variance @@ -356,13 +380,21 @@ def n_components(self, n): if n is None: n = len(self._variance) self.results.variance = self._variance[:n] - self.results.cumulated_variance = (np.cumsum(self._variance) / - np.sum(self._variance))[:n] + self.results.cumulated_variance = ( + np.cumsum(self._variance) / np.sum(self._variance) + )[:n] self.results.p_components = self._p_components[:, :n] self._n_components = n - def transform(self, atomgroup, n_components=None, start=None, stop=None, - step=None, verbose=False): + def transform( + self, + atomgroup, + n_components=None, + start=None, + stop=None, + step=None, + verbose=False, + ): """Apply the dimensionality reduction on a trajectory Parameters @@ -403,31 +435,37 @@ def transform(self, atomgroup, n_components=None, start=None, stop=None, on with ``verbose = True``, or off with ``verbose = False`` """ if not self._calculated: - raise ValueError('Call run() on the PCA before using transform') + raise ValueError("Call run() on the PCA before using transform") if isinstance(atomgroup, Universe): atomgroup = atomgroup.atoms if self._n_atoms != atomgroup.n_atoms: - raise ValueError('PCA has been fit for' - '{} atoms. Your atomgroup' - 'has {} atoms'.format(self._n_atoms, - atomgroup.n_atoms)) + raise ValueError( + "PCA has been fit for" + "{} atoms. Your atomgroup" + "has {} atoms".format(self._n_atoms, atomgroup.n_atoms) + ) if not (self._atoms.types == atomgroup.types).all(): - warnings.warn('Atom types do not match with types used to fit PCA') + warnings.warn("Atom types do not match with types used to fit PCA") traj = atomgroup.universe.trajectory start, stop, step = traj.check_slice_indices(start, stop, step) n_frames = len(range(start, stop, step)) - dim = (n_components if n_components is not None else - self.results.p_components.shape[1]) + dim = ( + n_components + if n_components is not None + else self.results.p_components.shape[1] + ) dot = np.zeros((n_frames, dim)) - for i, ts in tqdm(enumerate(traj[start:stop:step]), disable=not verbose, - total=len(traj[start:stop:step]) - ): + for i, ts in tqdm( + enumerate(traj[start:stop:step]), + disable=not verbose, + total=len(traj[start:stop:step]), + ): xyz = atomgroup.positions.ravel() - self._xmean dot[i] = np.dot(xyz, self._p_components[:, :dim]) @@ -547,20 +585,22 @@ def project_single_frame(self, components=None, group=None, anchor=None): .. versionadded:: 2.2.0 """ if not self._calculated: - raise ValueError('Call run() on the PCA before projecting') + raise ValueError("Call run() on the PCA before projecting") if group is not None: if anchor is None: - raise ValueError("'anchor' cannot be 'None'" + - " if 'group' is not 'None'") + raise ValueError( + "'anchor' cannot be 'None'" + " if 'group' is not 'None'" + ) anchors = group.select_atoms(anchor) anchors_res_ids = anchors.resindices if np.unique(anchors_res_ids).size != anchors_res_ids.size: raise ValueError("More than one 'anchor' found in residues") if not np.isin(group.resindices, anchors_res_ids).all(): - raise ValueError("Some residues in 'group'" + - " do not have an 'anchor'") + raise ValueError( + "Some residues in 'group'" + " do not have an 'anchor'" + ) if not anchors.issubset(self._atoms): raise ValueError("Some 'anchors' are not part of PCA class") @@ -568,18 +608,22 @@ def project_single_frame(self, components=None, group=None, anchor=None): # sure that extrapolation works on residues, not random atoms. non_pca = group.residues.atoms - self._atoms pca_res_indices, pca_res_counts = np.unique( - self._atoms.resindices, return_counts=True) + self._atoms.resindices, return_counts=True + ) non_pca_atoms = np.array([], dtype=int) for res in group.residues: # n_common is the number of pca atoms in a residue - n_common = pca_res_counts[np.where( - pca_res_indices == res.resindex)][0] - non_pca_atoms = np.append(non_pca_atoms, - res.atoms.n_atoms - n_common) + n_common = pca_res_counts[ + np.where(pca_res_indices == res.resindex) + ][0] + non_pca_atoms = np.append( + non_pca_atoms, res.atoms.n_atoms - n_common + ) # index_extrapolate records the anchor number for each non-PCA atom - index_extrapolate = np.repeat(np.arange(anchors.atoms.n_atoms), - non_pca_atoms) + index_extrapolate = np.repeat( + np.arange(anchors.atoms.n_atoms), non_pca_atoms + ) if components is None: components = np.arange(self.results.p_components.shape[1]) @@ -591,22 +635,29 @@ def wrapped(ts): xyz = self._atoms.positions.ravel() - self._xmean self._atoms.positions = np.reshape( - (np.dot(np.dot(xyz, self._p_components[:, components]), - self._p_components[:, components].T) - + self._xmean), (-1, 3) + ( + np.dot( + np.dot(xyz, self._p_components[:, components]), + self._p_components[:, components].T, + ) + + self._xmean + ), + (-1, 3), ) if group is not None: - non_pca.positions += (anchors.positions - - anchors_coords_old)[index_extrapolate] + non_pca.positions += (anchors.positions - anchors_coords_old)[ + index_extrapolate + ] return ts + return wrapped @due.dcite( - Doi('10.1002/(SICI)1097-0134(19990901)36:4<419::AID-PROT5>3.0.CO;2-U'), - Doi('10.1529/biophysj.104.052449'), + Doi("10.1002/(SICI)1097-0134(19990901)36:4<419::AID-PROT5>3.0.CO;2-U"), + Doi("10.1529/biophysj.104.052449"), description="RMSIP", - path='MDAnalysis.analysis.pca', + path="MDAnalysis.analysis.pca", ) def rmsip(self, other, n_components=None): """Compute the root mean square inner product between subspaces. @@ -667,23 +718,24 @@ def rmsip(self, other, n_components=None): try: a = self.results.p_components except AttributeError: - raise ValueError('Call run() on the PCA before using rmsip') + raise ValueError("Call run() on the PCA before using rmsip") try: b = other.results.p_components except AttributeError: if isinstance(other, type(self)): raise ValueError( - 'Call run() on the other PCA before using rmsip') + "Call run() on the other PCA before using rmsip" + ) else: - raise ValueError('other must be another PCA class') + raise ValueError("other must be another PCA class") return rmsip(a.T, b.T, n_components=n_components) @due.dcite( - Doi('10.1016/j.str.2007.12.011'), + Doi("10.1016/j.str.2007.12.011"), description="Cumulative overlap", - path='MDAnalysis.analysis.pca', + path="MDAnalysis.analysis.pca", ) def cumulative_overlap(self, other, i=0, n_components=None): """Compute the cumulative overlap of a vector in a subspace. @@ -722,16 +774,18 @@ def cumulative_overlap(self, other, i=0, n_components=None): a = self.results.p_components except AttributeError: raise ValueError( - 'Call run() on the PCA before using cumulative_overlap') + "Call run() on the PCA before using cumulative_overlap" + ) try: b = other.results.p_components except AttributeError: if isinstance(other, type(self)): raise ValueError( - 'Call run() on the other PCA before using cumulative_overlap') + "Call run() on the other PCA before using cumulative_overlap" + ) else: - raise ValueError('other must be another PCA class') + raise ValueError("other must be another PCA class") return cumulative_overlap(a.T, b.T, i=i, n_components=n_components) @@ -767,15 +821,18 @@ def cosine_content(pca_space, i): t = np.arange(len(pca_space)) T = len(pca_space) cos = np.cos(np.pi * t * (i + 1) / T) - return ((2.0 / T) * (scipy.integrate.simpson(cos*pca_space[:, i])) ** 2 / - scipy.integrate.simpson(pca_space[:, i] ** 2)) + return ( + (2.0 / T) + * (scipy.integrate.simpson(cos * pca_space[:, i])) ** 2 + / scipy.integrate.simpson(pca_space[:, i] ** 2) + ) @due.dcite( - Doi('10.1002/(SICI)1097-0134(19990901)36:4<419::AID-PROT5>3.0.CO;2-U'), - Doi('10.1529/biophysj.104.052449'), + Doi("10.1002/(SICI)1097-0134(19990901)36:4<419::AID-PROT5>3.0.CO;2-U"), + Doi("10.1529/biophysj.104.052449"), description="RMSIP", - path='MDAnalysis.analysis.pca', + path="MDAnalysis.analysis.pca", ) def rmsip(a, b, n_components=None): """Compute the root mean square inner product between subspaces. @@ -845,7 +902,7 @@ def rmsip(a, b, n_components=None): elif len(n_components) == 2: n_a, n_b = n_components else: - raise ValueError('Too many values provided for n_components') + raise ValueError("Too many values provided for n_components") if n_a is None: n_a = len(a) @@ -853,14 +910,14 @@ def rmsip(a, b, n_components=None): n_b = len(b) sip = np.matmul(a[:n_a], b[:n_b].T) ** 2 - msip = sip.sum()/n_a + msip = sip.sum() / n_a return msip**0.5 @due.dcite( - Doi('10.1016/j.str.2007.12.011'), + Doi("10.1016/j.str.2007.12.011"), description="Cumulative overlap", - path='MDAnalysis.analysis.pca', + path="MDAnalysis.analysis.pca", ) def cumulative_overlap(a, b, i=0, n_components=None): """Compute the cumulative overlap of a vector in a subspace. @@ -906,5 +963,5 @@ def cumulative_overlap(a, b, i=0, n_components=None): b = b[:n_components] b_norms = (b**2).sum(axis=1) ** 0.5 - o = np.abs(np.matmul(vec, b.T)) / (b_norms*vec_norm) + o = np.abs(np.matmul(vec, b.T)) / (b_norms * vec_norm) return (o**2).sum() ** 0.5 diff --git a/package/MDAnalysis/analysis/polymer.py b/package/MDAnalysis/analysis/polymer.py index a38cf68daac..9c3cc39dee2 100644 --- a/package/MDAnalysis/analysis/polymer.py +++ b/package/MDAnalysis/analysis/polymer.py @@ -45,7 +45,7 @@ logger = logging.getLogger(__name__) -@requires('bonds') +@requires("bonds") def sort_backbone(backbone): """Rearrange a linear AtomGroup into backbone order @@ -68,25 +68,30 @@ def sort_backbone(backbone): .. versionadded:: 0.20.0 """ if not backbone.n_fragments == 1: - raise ValueError("{} fragments found in backbone. " - "backbone must be a single contiguous AtomGroup" - "".format(backbone.n_fragments)) + raise ValueError( + "{} fragments found in backbone. " + "backbone must be a single contiguous AtomGroup" + "".format(backbone.n_fragments) + ) - branches = [at for at in backbone - if len(at.bonded_atoms & backbone) > 2] + branches = [at for at in backbone if len(at.bonded_atoms & backbone) > 2] if branches: # find which atom has too many bonds for easier debug raise ValueError( "Backbone is not linear. " "The following atoms have more than two bonds in backbone: {}." - "".format(','.join(str(a) for a in branches))) + "".format(",".join(str(a) for a in branches)) + ) - caps = [atom for atom in backbone - if len(atom.bonded_atoms & backbone) == 1] + caps = [ + atom for atom in backbone if len(atom.bonded_atoms & backbone) == 1 + ] if not caps: # cyclical structure - raise ValueError("Could not find starting point of backbone, " - "is the backbone cyclical?") + raise ValueError( + "Could not find starting point of backbone, " + "is the backbone cyclical?" + ) # arbitrarily choose one of the capping atoms to be the startpoint sorted_backbone = AtomGroup([caps[0]]) @@ -229,9 +234,11 @@ class PersistenceLength(AnalysisBase): :attr:`lb`, :attr:`lp`, :attr:`fit` are now stored in a :class:`MDAnalysis.analysis.base.Results` instance. """ + def __init__(self, atomgroups, **kwargs): super(PersistenceLength, self).__init__( - atomgroups[0].universe.trajectory, **kwargs) + atomgroups[0].universe.trajectory, **kwargs + ) self._atomgroups = atomgroups # Check that all chains are the same length @@ -256,30 +263,36 @@ def _single_frame(self): vecs /= np.sqrt((vecs * vecs).sum(axis=1))[:, None] inner_pr = np.inner(vecs, vecs) - for i in range(n-1): - self._results[:(n-1)-i] += inner_pr[i, i:] + for i in range(n - 1): + self._results[: (n - 1) - i] += inner_pr[i, i:] @property def lb(self): - wmsg = ("The `lb` attribute was deprecated in " - "MDAnalysis 2.0.0 and will be removed in MDAnalysis 3.0.0. " - "Please use `results.variance` instead.") + wmsg = ( + "The `lb` attribute was deprecated in " + "MDAnalysis 2.0.0 and will be removed in MDAnalysis 3.0.0. " + "Please use `results.variance` instead." + ) warnings.warn(wmsg, DeprecationWarning) return self.results.lb @property def lp(self): - wmsg = ("The `lp` attribute was deprecated in " - "MDAnalysis 2.0.0 and will be removed in MDAnalysis 3.0.0. " - "Please use `results.variance` instead.") + wmsg = ( + "The `lp` attribute was deprecated in " + "MDAnalysis 2.0.0 and will be removed in MDAnalysis 3.0.0. " + "Please use `results.variance` instead." + ) warnings.warn(wmsg, DeprecationWarning) return self.results.lp @property def fit(self): - wmsg = ("The `fit` attribute was deprecated in " - "MDAnalysis 2.0.0 and will be removed in MDAnalysis 3.0.0. " - "Please use `results.variance` instead.") + wmsg = ( + "The `fit` attribute was deprecated in " + "MDAnalysis 2.0.0 and will be removed in MDAnalysis 3.0.0. " + "Please use `results.variance` instead." + ) warnings.warn(wmsg, DeprecationWarning) return self.results.fit @@ -309,13 +322,15 @@ def _perform_fit(self): self.results.bond_autocorrelation except AttributeError: raise NoDataError("Use the run method first") from None - self.results.x = self.results.lb *\ - np.arange(len(self.results.bond_autocorrelation)) + self.results.x = self.results.lb * np.arange( + len(self.results.bond_autocorrelation) + ) - self.results.lp = fit_exponential_decay(self.results.x, - self.results.bond_autocorrelation) + self.results.lp = fit_exponential_decay( + self.results.x, self.results.bond_autocorrelation + ) - self.results.fit = np.exp(-self.results.x/self.results.lp) + self.results.fit = np.exp(-self.results.x / self.results.lp) def plot(self, ax=None): """Visualize the results and fit @@ -330,20 +345,21 @@ def plot(self, ax=None): ax : the axis that the graph was plotted on """ import matplotlib.pyplot as plt + if ax is None: fig, ax = plt.subplots() - ax.plot(self.results.x, - self.results.bond_autocorrelation, - 'ro', - label='Result') - ax.plot(self.results.x, - self.results.fit, - label='Fit') - ax.set_xlabel(r'x') - ax.set_ylabel(r'$C(x)$') + ax.plot( + self.results.x, + self.results.bond_autocorrelation, + "ro", + label="Result", + ) + ax.plot(self.results.x, self.results.fit, label="Fit") + ax.set_xlabel(r"x") + ax.set_ylabel(r"$C(x)$") ax.set_xlim(0.0, 40 * self.results.lb) - ax.legend(loc='best') + ax.legend(loc="best") return ax @@ -368,8 +384,9 @@ def fit_exponential_decay(x, y): This function assumes that data starts at 1.0 and decays to 0.0 """ + def expfunc(x, a): - return np.exp(-x/a) + return np.exp(-x / a) a = scipy.optimize.curve_fit(expfunc, x, y)[0][0] diff --git a/package/MDAnalysis/analysis/psa.py b/package/MDAnalysis/analysis/psa.py index b93ea90c64b..853d6ecf622 100644 --- a/package/MDAnalysis/analysis/psa.py +++ b/package/MDAnalysis/analysis/psa.py @@ -64,8 +64,10 @@ ) -wmsg = ('Deprecation in version 2.8.0:\n' - 'MDAnalysis.analysis.psa is deprecated in favour of the MDAKit ' - 'PathSimAnalysis (https://github.com/MDAnalysis/PathSimAnalysis) ' - 'and will be removed in MDAnalysis version 3.0.0') +wmsg = ( + "Deprecation in version 2.8.0:\n" + "MDAnalysis.analysis.psa is deprecated in favour of the MDAKit " + "PathSimAnalysis (https://github.com/MDAnalysis/PathSimAnalysis) " + "and will be removed in MDAnalysis version 3.0.0" +) warnings.warn(wmsg, category=DeprecationWarning) diff --git a/package/MDAnalysis/analysis/rdf.py b/package/MDAnalysis/analysis/rdf.py index 891be116ca5..02ac014d47c 100644 --- a/package/MDAnalysis/analysis/rdf.py +++ b/package/MDAnalysis/analysis/rdf.py @@ -217,24 +217,30 @@ class InterRDF(AnalysisBase): of the `results` attribute of :class:`~MDAnalysis.analysis.AnalysisBase`. """ - def __init__(self, - g1, - g2, - nbins=75, - range=(0.0, 15.0), - norm="rdf", - exclusion_block=None, - exclude_same=None, - **kwargs): + + def __init__( + self, + g1, + g2, + nbins=75, + range=(0.0, 15.0), + norm="rdf", + exclusion_block=None, + exclude_same=None, + **kwargs, + ): super(InterRDF, self).__init__(g1.universe.trajectory, **kwargs) self.g1 = g1 self.g2 = g2 self.norm = str(norm).lower() - self.rdf_settings = {'bins': nbins, - 'range': range} + self.rdf_settings = {"bins": nbins, "range": range} self._exclusion_block = exclusion_block - if exclude_same is not None and exclude_same not in ['residue', 'segment', 'chain']: + if exclude_same is not None and exclude_same not in [ + "residue", + "segment", + "chain", + ]: raise ValueError( "The exclude_same argument to InterRDF must be None, 'residue', 'segment' " "or 'chain'." @@ -243,12 +249,18 @@ def __init__(self, raise ValueError( "The exclude_same argument to InterRDF cannot be used with exclusion_block." ) - name_to_attr = {'residue': 'resindices', 'segment': 'segindices', 'chain': 'chainIDs'} + name_to_attr = { + "residue": "resindices", + "segment": "segindices", + "chain": "chainIDs", + } self.exclude_same = name_to_attr.get(exclude_same) - if self.norm not in ['rdf', 'density', 'none']: - raise ValueError(f"'{self.norm}' is an invalid norm. " - "Use 'rdf', 'density' or 'none'.") + if self.norm not in ["rdf", "density", "none"]: + raise ValueError( + f"'{self.norm}' is an invalid norm. " + "Use 'rdf', 'density' or 'none'." + ) def _prepare(self): # Empty histogram to store the RDF @@ -263,17 +275,19 @@ def _prepare(self): # Cumulative volume for rdf normalization self.volume_cum = 0 # Set the max range to filter the search radius - self._maxrange = self.rdf_settings['range'][1] + self._maxrange = self.rdf_settings["range"][1] def _single_frame(self): - pairs, dist = distances.capped_distance(self.g1.positions, - self.g2.positions, - self._maxrange, - box=self._ts.dimensions) + pairs, dist = distances.capped_distance( + self.g1.positions, + self.g2.positions, + self._maxrange, + box=self._ts.dimensions, + ) # Maybe exclude same molecule distances if self._exclusion_block is not None: - idxA = pairs[:, 0]//self._exclusion_block[0] - idxB = pairs[:, 1]//self._exclusion_block[1] + idxA = pairs[:, 0] // self._exclusion_block[0] + idxB = pairs[:, 1] // self._exclusion_block[1] mask = np.where(idxA != idxB)[0] dist = dist[mask] @@ -295,7 +309,7 @@ def _conclude(self): if self.norm in ["rdf", "density"]: # Volume in each radial shell vols = np.power(self.results.edges, 3) - norm *= 4/3 * np.pi * np.diff(vols) + norm *= 4 / 3 * np.pi * np.diff(vols) if self.norm == "rdf": # Number of each selection @@ -317,33 +331,41 @@ def _conclude(self): @property def edges(self): - wmsg = ("The `edges` attribute was deprecated in MDAnalysis 2.0.0 " - "and will be removed in MDAnalysis 3.0.0. Please use " - "`results.bins` instead") + wmsg = ( + "The `edges` attribute was deprecated in MDAnalysis 2.0.0 " + "and will be removed in MDAnalysis 3.0.0. Please use " + "`results.bins` instead" + ) warnings.warn(wmsg, DeprecationWarning) return self.results.edges @property def count(self): - wmsg = ("The `count` attribute was deprecated in MDAnalysis 2.0.0 " - "and will be removed in MDAnalysis 3.0.0. Please use " - "`results.bins` instead") + wmsg = ( + "The `count` attribute was deprecated in MDAnalysis 2.0.0 " + "and will be removed in MDAnalysis 3.0.0. Please use " + "`results.bins` instead" + ) warnings.warn(wmsg, DeprecationWarning) return self.results.count @property def bins(self): - wmsg = ("The `bins` attribute was deprecated in MDAnalysis 2.0.0 " - "and will be removed in MDAnalysis 3.0.0. Please use " - "`results.bins` instead") + wmsg = ( + "The `bins` attribute was deprecated in MDAnalysis 2.0.0 " + "and will be removed in MDAnalysis 3.0.0. Please use " + "`results.bins` instead" + ) warnings.warn(wmsg, DeprecationWarning) return self.results.bins @property def rdf(self): - wmsg = ("The `rdf` attribute was deprecated in MDAnalysis 2.0.0 " - "and will be removed in MDAnalysis 3.0.0. Please use " - "`results.rdf` instead") + wmsg = ( + "The `rdf` attribute was deprecated in MDAnalysis 2.0.0 " + "and will be removed in MDAnalysis 3.0.0. Please use " + "`results.rdf` instead" + ) warnings.warn(wmsg, DeprecationWarning) return self.results.rdf @@ -540,54 +562,69 @@ class InterRDF_s(AnalysisBase): .. deprecated:: 2.3.0 The `universe` parameter is superflous. """ - def __init__(self, - u, - ags, - nbins=75, - range=(0.0, 15.0), - norm="rdf", - density=False, - **kwargs): - super(InterRDF_s, self).__init__(ags[0][0].universe.trajectory, - **kwargs) - - warnings.warn("The `u` attribute is superflous and will be removed " - "in MDAnalysis 3.0.0.", DeprecationWarning) + + def __init__( + self, + u, + ags, + nbins=75, + range=(0.0, 15.0), + norm="rdf", + density=False, + **kwargs, + ): + super(InterRDF_s, self).__init__( + ags[0][0].universe.trajectory, **kwargs + ) + + warnings.warn( + "The `u` attribute is superflous and will be removed " + "in MDAnalysis 3.0.0.", + DeprecationWarning, + ) self.ags = ags self.norm = str(norm).lower() - self.rdf_settings = {'bins': nbins, - 'range': range} + self.rdf_settings = {"bins": nbins, "range": range} - if self.norm not in ['rdf', 'density', 'none']: - raise ValueError(f"'{self.norm}' is an invalid norm. " - "Use 'rdf', 'density' or 'none'.") + if self.norm not in ["rdf", "density", "none"]: + raise ValueError( + f"'{self.norm}' is an invalid norm. " + "Use 'rdf', 'density' or 'none'." + ) if density: - warnings.warn("The `density` attribute was deprecated in " - "MDAnalysis 2.3.0 and will be removed in " - "MDAnalysis 3.0.0. Please use `norm=density` " - "instead.", DeprecationWarning) + warnings.warn( + "The `density` attribute was deprecated in " + "MDAnalysis 2.3.0 and will be removed in " + "MDAnalysis 3.0.0. Please use `norm=density` " + "instead.", + DeprecationWarning, + ) self.norm = "density" def _prepare(self): count, edges = np.histogram([-1], **self.rdf_settings) - self.results.count = [np.zeros((ag1.n_atoms, ag2.n_atoms, len(count)), - dtype=np.float64) for ag1, ag2 in self.ags] + self.results.count = [ + np.zeros((ag1.n_atoms, ag2.n_atoms, len(count)), dtype=np.float64) + for ag1, ag2 in self.ags + ] self.results.edges = edges self.results.bins = 0.5 * (edges[:-1] + edges[1:]) if self.norm == "rdf": # Cumulative volume for rdf normalization self.volume_cum = 0 - self._maxrange = self.rdf_settings['range'][1] + self._maxrange = self.rdf_settings["range"][1] def _single_frame(self): for i, (ag1, ag2) in enumerate(self.ags): - pairs, dist = distances.capped_distance(ag1.positions, - ag2.positions, - self._maxrange, - box=self._ts.dimensions) + pairs, dist = distances.capped_distance( + ag1.positions, + ag2.positions, + self._maxrange, + box=self._ts.dimensions, + ) for j, (idx1, idx2) in enumerate(pairs): count, _ = np.histogram(dist[j], **self.rdf_settings) @@ -601,7 +638,7 @@ def _conclude(self): if self.norm in ["rdf", "density"]: # Volume in each radial shell vols = np.power(self.results.edges, 3) - norm *= 4/3 * np.pi * np.diff(vols) + norm *= 4 / 3 * np.pi * np.diff(vols) if self.norm == "rdf": # Average number density @@ -639,40 +676,50 @@ def get_cdf(self): @property def edges(self): - wmsg = ("The `edges` attribute was deprecated in MDAnalysis 2.0.0 " - "and will be removed in MDAnalysis 3.0.0. Please use " - "`results.bins` instead") + wmsg = ( + "The `edges` attribute was deprecated in MDAnalysis 2.0.0 " + "and will be removed in MDAnalysis 3.0.0. Please use " + "`results.bins` instead" + ) warnings.warn(wmsg, DeprecationWarning) return self.results.edges @property def count(self): - wmsg = ("The `count` attribute was deprecated in MDAnalysis 2.0.0 " - "and will be removed in MDAnalysis 3.0.0. Please use " - "`results.bins` instead") + wmsg = ( + "The `count` attribute was deprecated in MDAnalysis 2.0.0 " + "and will be removed in MDAnalysis 3.0.0. Please use " + "`results.bins` instead" + ) warnings.warn(wmsg, DeprecationWarning) return self.results.count @property def bins(self): - wmsg = ("The `bins` attribute was deprecated in MDAnalysis 2.0.0 " - "and will be removed in MDAnalysis 3.0.0. Please use " - "`results.bins` instead") + wmsg = ( + "The `bins` attribute was deprecated in MDAnalysis 2.0.0 " + "and will be removed in MDAnalysis 3.0.0. Please use " + "`results.bins` instead" + ) warnings.warn(wmsg, DeprecationWarning) return self.results.bins @property def rdf(self): - wmsg = ("The `rdf` attribute was deprecated in MDAnalysis 2.0.0 " - "and will be removed in MDAnalysis 3.0.0. Please use " - "`results.rdf` instead") + wmsg = ( + "The `rdf` attribute was deprecated in MDAnalysis 2.0.0 " + "and will be removed in MDAnalysis 3.0.0. Please use " + "`results.rdf` instead" + ) warnings.warn(wmsg, DeprecationWarning) return self.results.rdf @property def cdf(self): - wmsg = ("The `cdf` attribute was deprecated in MDAnalysis 2.0.0 " - "and will be removed in MDAnalysis 3.0.0. Please use " - "`results.cdf` instead") + wmsg = ( + "The `cdf` attribute was deprecated in MDAnalysis 2.0.0 " + "and will be removed in MDAnalysis 3.0.0. Please use " + "`results.cdf` instead" + ) warnings.warn(wmsg, DeprecationWarning) return self.results.cdf diff --git a/package/MDAnalysis/analysis/results.py b/package/MDAnalysis/analysis/results.py index 8aa2062d2bc..7708f3dd881 100644 --- a/package/MDAnalysis/analysis/results.py +++ b/package/MDAnalysis/analysis/results.py @@ -48,6 +48,7 @@ assert r.masses == list((*r1.masses, *r2.masses)) assert (r.vectors == np.vstack([r1.vectors, r2.vectors])).all() """ + from collections import UserDict import numpy as np from typing import Callable, Sequence @@ -100,7 +101,9 @@ class in `scikit-learn`_. def _validate_key(self, key): if key in dir(self): - raise AttributeError(f"'{key}' is a protected dictionary attribute") + raise AttributeError( + f"'{key}' is a protected dictionary attribute" + ) elif isinstance(key, str) and not key.isidentifier(): raise ValueError(f"'{key}' is not a valid attribute") @@ -125,13 +128,17 @@ def __getattr__(self, attr): try: return self[attr] except KeyError as err: - raise AttributeError(f"'Results' object has no attribute '{attr}'") from err + raise AttributeError( + f"'Results' object has no attribute '{attr}'" + ) from err def __delattr__(self, attr): try: del self[attr] except KeyError as err: - raise AttributeError(f"'Results' object has no attribute '{attr}'") from err + raise AttributeError( + f"'Results' object has no attribute '{attr}'" + ) from err def __getstate__(self): return self.data @@ -166,7 +173,7 @@ class ResultsGroup: obj1 = Results(mass=1) obj2 = Results(mass=3) assert {'mass': 2.0} == group.merge([obj1, obj2]) - + .. code-block:: python @@ -182,10 +189,12 @@ class ResultsGroup: def __init__(self, lookup: dict[str, Callable] = None): self._lookup = lookup - def merge(self, objects: Sequence[Results], require_all_aggregators: bool = True) -> Results: - """Merge multiple Results into a single Results instance. + def merge( + self, objects: Sequence[Results], require_all_aggregators: bool = True + ) -> Results: + """Merge multiple Results into a single Results instance. - Merge multiple :class:`Results` instances into a single one, using the + Merge multiple :class:`Results` instances into a single one, using the `lookup` dictionary to determine the appropriate aggregator functions for each named results attribute. If the resulting object only contains a single element, it just returns it without using any aggregators. @@ -213,7 +222,7 @@ def merge(self, objects: Sequence[Results], require_all_aggregators: bool = True if len(objects) == 1: merged_results = objects[0] return merged_results - + merged_results = Results() for key in objects[0].keys(): agg_function = self._lookup.get(key, None) diff --git a/package/MDAnalysis/analysis/rms.py b/package/MDAnalysis/analysis/rms.py index f33d1b761fb..55a1322a75e 100644 --- a/package/MDAnalysis/analysis/rms.py +++ b/package/MDAnalysis/analysis/rms.py @@ -172,7 +172,7 @@ from ..lib.util import asiterable, iterable, get_weights -logger = logging.getLogger('MDAnalysis.analysis.rmsd') +logger = logging.getLogger("MDAnalysis.analysis.rmsd") def rmsd(a, b, weights=None, center=False, superposition=False): @@ -258,7 +258,7 @@ def rmsd(a, b, weights=None, center=False, superposition=False): b = np.asarray(b, dtype=np.float64) N = b.shape[0] if a.shape != b.shape: - raise ValueError('a and b must have same shape') + raise ValueError("a and b must have same shape") # superposition only works if structures are centered if center or superposition: @@ -269,7 +269,7 @@ def rmsd(a, b, weights=None, center=False, superposition=False): if weights is not None: if len(weights) != len(a): - raise ValueError('weights must have same length as a and b') + raise ValueError("weights must have same length as a and b") # weights are constructed as relative to the mean weights = np.asarray(weights, dtype=np.float64) / np.mean(weights) @@ -277,8 +277,7 @@ def rmsd(a, b, weights=None, center=False, superposition=False): return qcp.CalcRMSDRotationalMatrix(a, b, N, None, weights) else: if weights is not None: - return np.sqrt(np.sum(weights[:, np.newaxis] - * ((a - b) ** 2)) / N) + return np.sqrt(np.sum(weights[:, np.newaxis] * ((a - b) ** 2)) / N) else: return np.sqrt(np.sum((a - b) ** 2) / N) @@ -307,28 +306,29 @@ def process_selection(select): """ if isinstance(select, str): - select = {'reference': str(select), 'mobile': str(select)} + select = {"reference": str(select), "mobile": str(select)} elif type(select) is tuple: try: - select = {'mobile': select[0], 'reference': select[1]} + select = {"mobile": select[0], "reference": select[1]} except IndexError: raise IndexError( "select must contain two selection strings " - "(reference, mobile)") from None + "(reference, mobile)" + ) from None elif type(select) is dict: # compatability hack to use new nomenclature try: - select['mobile'] - select['reference'] + select["mobile"] + select["reference"] except KeyError: raise KeyError( - "select dictionary must contain entries for keys " - "'mobile' and 'reference'." - ) from None + "select dictionary must contain entries for keys " + "'mobile' and 'reference'." + ) from None else: raise TypeError("'select' must be either a string, 2-tuple, or dict") - select['mobile'] = asiterable(select['mobile']) - select['reference'] = asiterable(select['reference']) + select["mobile"] = asiterable(select["mobile"]) + select["reference"] = asiterable(select["reference"]) return select @@ -362,16 +362,29 @@ class RMSD(AnalysisBase): introduced :meth:`get_supported_backends` allowing for parallel execution on ``multiprocessing`` and ``dask`` backends. """ + _analysis_algorithm_is_parallelizable = True @classmethod def get_supported_backends(cls): - return ('serial', 'multiprocessing', 'dask',) - - - def __init__(self, atomgroup, reference=None, select='all', - groupselections=None, weights=None, weights_groupselections=False, - tol_mass=0.1, ref_frame=0, **kwargs): + return ( + "serial", + "multiprocessing", + "dask", + ) + + def __init__( + self, + atomgroup, + reference=None, + select="all", + groupselections=None, + weights=None, + weights_groupselections=False, + tol_mass=0.1, + ref_frame=0, + **kwargs, + ): r"""Parameters ---------- atomgroup : AtomGroup or Universe @@ -512,49 +525,67 @@ def __init__(self, atomgroup, reference=None, select='all', `filename` keyword was removed. """ - super(RMSD, self).__init__(atomgroup.universe.trajectory, - **kwargs) + super(RMSD, self).__init__(atomgroup.universe.trajectory, **kwargs) self.atomgroup = atomgroup self.reference = reference if reference is not None else self.atomgroup select = process_selection(select) - self.groupselections = ([process_selection(s) for s in groupselections] - if groupselections is not None else []) + self.groupselections = ( + [process_selection(s) for s in groupselections] + if groupselections is not None + else [] + ) self.weights = weights self.tol_mass = tol_mass self.ref_frame = ref_frame self.weights_groupselections = weights_groupselections - self.ref_atoms = self.reference.select_atoms(*select['reference']) - self.mobile_atoms = self.atomgroup.select_atoms(*select['mobile']) + self.ref_atoms = self.reference.select_atoms(*select["reference"]) + self.mobile_atoms = self.atomgroup.select_atoms(*select["mobile"]) if len(self.ref_atoms) != len(self.mobile_atoms): - err = ("Reference and trajectory atom selections do " - "not contain the same number of atoms: " - "N_ref={0:d}, N_traj={1:d}".format(self.ref_atoms.n_atoms, - self.mobile_atoms.n_atoms)) + err = ( + "Reference and trajectory atom selections do " + "not contain the same number of atoms: " + "N_ref={0:d}, N_traj={1:d}".format( + self.ref_atoms.n_atoms, self.mobile_atoms.n_atoms + ) + ) logger.exception(err) raise SelectionError(err) - logger.info("RMS calculation " - "for {0:d} atoms.".format(len(self.ref_atoms))) - mass_mismatches = (np.absolute((self.ref_atoms.masses - - self.mobile_atoms.masses)) > - self.tol_mass) + logger.info( + "RMS calculation " "for {0:d} atoms.".format(len(self.ref_atoms)) + ) + mass_mismatches = ( + np.absolute((self.ref_atoms.masses - self.mobile_atoms.masses)) + > self.tol_mass + ) if np.any(mass_mismatches): # diagnostic output: logger.error("Atoms: reference | mobile") for ar, at in zip(self.ref_atoms, self.mobile_atoms): if ar.name != at.name: - logger.error("{0!s:>4} {1:3d} {2!s:>3} {3!s:>3} {4:6.3f}" - "| {5!s:>4} {6:3d} {7!s:>3} {8!s:>3}" - "{9:6.3f}".format(ar.segid, ar.resid, - ar.resname, ar.name, - ar.mass, at.segid, at.resid, - at.resname, at.name, - at.mass)) - errmsg = ("Inconsistent selections, masses differ by more than" - "{0:f}; mis-matching atoms" - "are shown above.".format(self.tol_mass)) + logger.error( + "{0!s:>4} {1:3d} {2!s:>3} {3!s:>3} {4:6.3f}" + "| {5!s:>4} {6:3d} {7!s:>3} {8!s:>3}" + "{9:6.3f}".format( + ar.segid, + ar.resid, + ar.resname, + ar.name, + ar.mass, + at.segid, + at.resid, + at.resname, + at.name, + at.mass, + ) + ) + errmsg = ( + "Inconsistent selections, masses differ by more than" + "{0:f}; mis-matching atoms" + "are shown above.".format(self.tol_mass) + ) logger.error(errmsg) raise SelectionError(errmsg) del mass_mismatches @@ -565,27 +596,38 @@ def __init__(self, atomgroup, reference=None, select='all', # *groupselections* groups each a dict with reference/mobile self._groupselections_atoms = [ { - 'reference': self.reference.universe.select_atoms(*s['reference']), - 'mobile': self.atomgroup.universe.select_atoms(*s['mobile']), + "reference": self.reference.universe.select_atoms( + *s["reference"] + ), + "mobile": self.atomgroup.universe.select_atoms(*s["mobile"]), } - for s in self.groupselections] + for s in self.groupselections + ] # sanity check - for igroup, (sel, atoms) in enumerate(zip(self.groupselections, - self._groupselections_atoms)): - if len(atoms['mobile']) != len(atoms['reference']): - logger.exception('SelectionError: Group Selection') + for igroup, (sel, atoms) in enumerate( + zip(self.groupselections, self._groupselections_atoms) + ): + if len(atoms["mobile"]) != len(atoms["reference"]): + logger.exception("SelectionError: Group Selection") raise SelectionError( "Group selection {0}: {1} | {2}: Reference and trajectory " "atom selections do not contain the same number of atoms: " "N_ref={3}, N_traj={4}".format( - igroup, sel['reference'], sel['mobile'], - len(atoms['reference']), len(atoms['mobile']))) + igroup, + sel["reference"], + sel["mobile"], + len(atoms["reference"]), + len(atoms["mobile"]), + ) + ) # check weights type - acceptable_dtypes = (np.dtype('float64'), np.dtype('int64')) - msg = ("weights should only be 'mass', None or 1D float array." - "For weights on groupselections, " - "use **weight_groupselections**") + acceptable_dtypes = (np.dtype("float64"), np.dtype("int64")) + msg = ( + "weights should only be 'mass', None or 1D float array." + "For weights on groupselections, " + "use **weight_groupselections**" + ) if iterable(self.weights): element_lens = [] @@ -605,42 +647,57 @@ def __init__(self, atomgroup, reference=None, select='all', if self.weights_groupselections: if len(self.weights_groupselections) != len(self.groupselections): - raise ValueError("Length of weights_groupselections is not equal to " - "length of groupselections ") - for weights, atoms, selection in zip(self.weights_groupselections, - self._groupselections_atoms, - self.groupselections): + raise ValueError( + "Length of weights_groupselections is not equal to " + "length of groupselections " + ) + for weights, atoms, selection in zip( + self.weights_groupselections, + self._groupselections_atoms, + self.groupselections, + ): try: if iterable(weights) or weights != "mass": - get_weights(atoms['mobile'], weights) + get_weights(atoms["mobile"], weights) except Exception as e: - raise type(e)(str(e) + ' happens in selection %s' % selection['mobile']) - + raise type(e)( + str(e) + + " happens in selection %s" % selection["mobile"] + ) def _prepare(self): self._n_atoms = self.mobile_atoms.n_atoms if not self.weights_groupselections: - if not iterable(self.weights): # apply 'mass' or 'None' to groupselections - self.weights_groupselections = [self.weights] * len(self.groupselections) + if not iterable( + self.weights + ): # apply 'mass' or 'None' to groupselections + self.weights_groupselections = [self.weights] * len( + self.groupselections + ) else: - self.weights_groupselections = [None] * len(self.groupselections) - - for igroup, (weights, atoms) in enumerate(zip(self.weights_groupselections, - self._groupselections_atoms)): - if str(weights) == 'mass': - self.weights_groupselections[igroup] = atoms['mobile'].masses + self.weights_groupselections = [None] * len( + self.groupselections + ) + + for igroup, (weights, atoms) in enumerate( + zip(self.weights_groupselections, self._groupselections_atoms) + ): + if str(weights) == "mass": + self.weights_groupselections[igroup] = atoms["mobile"].masses if weights is not None: - self.weights_groupselections[igroup] = np.asarray(self.weights_groupselections[igroup], - dtype=np.float64) / \ - np.mean(self.weights_groupselections[igroup]) + self.weights_groupselections[igroup] = np.asarray( + self.weights_groupselections[igroup], dtype=np.float64 + ) / np.mean(self.weights_groupselections[igroup]) # add the array of weights to weights_select self.weights_select = get_weights(self.mobile_atoms, self.weights) self.weights_ref = get_weights(self.ref_atoms, self.weights) if self.weights_select is not None: - self.weights_select = np.asarray(self.weights_select, dtype=np.float64) / \ - np.mean(self.weights_select) - self.weights_ref = np.asarray(self.weights_ref, dtype=np.float64) / \ - np.mean(self.weights_ref) + self.weights_select = np.asarray( + self.weights_select, dtype=np.float64 + ) / np.mean(self.weights_select) + self.weights_ref = np.asarray( + self.weights_ref, dtype=np.float64 + ) / np.mean(self.weights_ref) current_frame = self.reference.universe.trajectory.ts.frame @@ -653,10 +710,14 @@ def _prepare(self): # makes a copy self._ref_coordinates = self.ref_atoms.positions - self._ref_com if self._groupselections_atoms: - self._groupselections_ref_coords64 = [(self.reference. - select_atoms(*s['reference']). - positions.astype(np.float64)) for s in - self.groupselections] + self._groupselections_ref_coords64 = [ + ( + self.reference.select_atoms( + *s["reference"] + ).positions.astype(np.float64) + ) + for s in self.groupselections + ] finally: # Move back to the original frame self.reference.universe.trajectory[current_frame] @@ -674,20 +735,28 @@ def _prepare(self): else: self._rot = None - self.results.rmsd = np.zeros((self.n_frames, - 3 + len(self._groupselections_atoms))) + self.results.rmsd = np.zeros( + (self.n_frames, 3 + len(self._groupselections_atoms)) + ) - self._mobile_coordinates64 = self.mobile_atoms.positions.copy().astype(np.float64) + self._mobile_coordinates64 = self.mobile_atoms.positions.copy().astype( + np.float64 + ) def _get_aggregator(self): - return ResultsGroup(lookup={'rmsd': ResultsGroup.ndarray_vstack}) + return ResultsGroup(lookup={"rmsd": ResultsGroup.ndarray_vstack}) def _single_frame(self): - mobile_com = self.mobile_atoms.center(self.weights_select).astype(np.float64) + mobile_com = self.mobile_atoms.center(self.weights_select).astype( + np.float64 + ) self._mobile_coordinates64[:] = self.mobile_atoms.positions self._mobile_coordinates64 -= mobile_com - self.results.rmsd[self._frame_index, :2] = self._ts.frame, self._trajectory.time + self.results.rmsd[self._frame_index, :2] = ( + self._ts.frame, + self._trajectory.time, + ) if self._groupselections_atoms: # superimpose structures: MDAnalysis qcprot needs Nx3 coordinate @@ -696,9 +765,15 @@ def _single_frame(self): # left** so that we can easily use broadcasting and save one # expensive numpy transposition. - self.results.rmsd[self._frame_index, 2] = qcp.CalcRMSDRotationalMatrix( - self._ref_coordinates64, self._mobile_coordinates64, - self._n_atoms, self._rot, self.weights_select) + self.results.rmsd[self._frame_index, 2] = ( + qcp.CalcRMSDRotationalMatrix( + self._ref_coordinates64, + self._mobile_coordinates64, + self._n_atoms, + self._rot, + self.weights_select, + ) + ) self._R[:, :] = self._rot.reshape(3, 3) # Transform each atom in the trajectory (use inplace ops to @@ -713,24 +788,39 @@ def _single_frame(self): # 2) calculate secondary RMSDs (without any further # superposition) for igroup, (refpos, atoms) in enumerate( - zip(self._groupselections_ref_coords64, - self._groupselections_atoms), 3): + zip( + self._groupselections_ref_coords64, + self._groupselections_atoms, + ), + 3, + ): self.results.rmsd[self._frame_index, igroup] = rmsd( - refpos, atoms['mobile'].positions, - weights=self.weights_groupselections[igroup-3], - center=False, superposition=False) + refpos, + atoms["mobile"].positions, + weights=self.weights_groupselections[igroup - 3], + center=False, + superposition=False, + ) else: # only calculate RMSD by setting the Rmatrix to None (no need # to carry out the rotation as we already get the optimum RMSD) - self.results.rmsd[self._frame_index, 2] = qcp.CalcRMSDRotationalMatrix( - self._ref_coordinates64, self._mobile_coordinates64, - self._n_atoms, None, self.weights_select) + self.results.rmsd[self._frame_index, 2] = ( + qcp.CalcRMSDRotationalMatrix( + self._ref_coordinates64, + self._mobile_coordinates64, + self._n_atoms, + None, + self.weights_select, + ) + ) @property def rmsd(self): - wmsg = ("The `rmsd` attribute was deprecated in MDAnalysis 2.0.0 and " - "will be removed in MDAnalysis 3.0.0. Please use " - "`results.rmsd` instead.") + wmsg = ( + "The `rmsd` attribute was deprecated in MDAnalysis 2.0.0 and " + "will be removed in MDAnalysis 3.0.0. Please use " + "`results.rmsd` instead." + ) warnings.warn(wmsg, DeprecationWarning) return self.results.rmsd @@ -754,7 +844,7 @@ class RMSF(AnalysisBase): @classmethod def get_supported_backends(cls): - return ('serial',) + return ("serial",) def __init__(self, atomgroup, **kwargs): r"""Parameters @@ -885,7 +975,9 @@ def _prepare(self): def _single_frame(self): k = self._frame_index - self.sumsquares += (k / (k+1.0)) * (self.atomgroup.positions - self.mean) ** 2 + self.sumsquares += (k / (k + 1.0)) * ( + self.atomgroup.positions - self.mean + ) ** 2 self.mean = (k * self.mean + self.atomgroup.positions) / (k + 1) def _conclude(self): @@ -893,13 +985,17 @@ def _conclude(self): self.results.rmsf = np.sqrt(self.sumsquares.sum(axis=1) / (k + 1)) if not (self.results.rmsf >= 0).all(): - raise ValueError("Some RMSF values negative; overflow " + - "or underflow occurred") + raise ValueError( + "Some RMSF values negative; overflow " + + "or underflow occurred" + ) @property def rmsf(self): - wmsg = ("The `rmsf` attribute was deprecated in MDAnalysis 2.0.0 and " - "will be removed in MDAnalysis 3.0.0. Please use " - "`results.rmsd` instead.") + wmsg = ( + "The `rmsf` attribute was deprecated in MDAnalysis 2.0.0 and " + "will be removed in MDAnalysis 3.0.0. Please use " + "`results.rmsd` instead." + ) warnings.warn(wmsg, DeprecationWarning) return self.results.rmsf diff --git a/package/MDAnalysis/analysis/waterdynamics.py b/package/MDAnalysis/analysis/waterdynamics.py index 2c7a1c4bec3..aaeb1b296f3 100644 --- a/package/MDAnalysis/analysis/waterdynamics.py +++ b/package/MDAnalysis/analysis/waterdynamics.py @@ -49,8 +49,10 @@ ) -wmsg = ("Deprecation in version 2.8.0\n" - "MDAnalysis.analysis.waterdynamics is deprecated in favour of the " - "MDAKit waterdynamics (https://www.mdanalysis.org/waterdynamics/) " - "and will be removed in MDAnalysis version 3.0.0") +wmsg = ( + "Deprecation in version 2.8.0\n" + "MDAnalysis.analysis.waterdynamics is deprecated in favour of the " + "MDAKit waterdynamics (https://www.mdanalysis.org/waterdynamics/) " + "and will be removed in MDAnalysis version 3.0.0" +) warnings.warn(wmsg, category=DeprecationWarning) diff --git a/package/pyproject.toml b/package/pyproject.toml index 5853b74a210..337c71c28fe 100644 --- a/package/pyproject.toml +++ b/package/pyproject.toml @@ -135,6 +135,7 @@ tables\.py | MDAnalysis/visualization/.*\.py | MDAnalysis/lib/.*\.py^ | MDAnalysis/transformations/.*\.py +| MDAnalysis/analysis/.*\.py ) ''' extend-exclude = ''' @@ -182,6 +183,7 @@ __pycache__ | MDAnalysis/coordinates/chemfiles\.py | MDAnalysis/coordinates/memory\.py | MDAnalysis/core/universe\.py +| .*\.pyx ) ''' required-version = '24' diff --git a/testsuite/MDAnalysisTests/analysis/conftest.py b/testsuite/MDAnalysisTests/analysis/conftest.py index df17c05c064..5848c503165 100644 --- a/testsuite/MDAnalysisTests/analysis/conftest.py +++ b/testsuite/MDAnalysisTests/analysis/conftest.py @@ -47,107 +47,116 @@ def params_for_cls(cls, exclude: list[str] = None): ] params = [ - pytest.param({ - "backend": backend, - "n_workers": nproc - }, ) for backend in installed_backends for nproc in (2, ) + pytest.param( + {"backend": backend, "n_workers": nproc}, + ) + for backend in installed_backends + for nproc in (2,) if backend != "serial" ] params.extend([{"backend": "serial"}]) return params -@pytest.fixture(scope='module', params=params_for_cls(FrameAnalysis)) +@pytest.fixture(scope="module", params=params_for_cls(FrameAnalysis)) def client_FrameAnalysis(request): return request.param -@pytest.fixture(scope='module', params=params_for_cls(AnalysisBase)) +@pytest.fixture(scope="module", params=params_for_cls(AnalysisBase)) def client_AnalysisBase(request): return request.param -@pytest.fixture(scope='module', params=params_for_cls(AnalysisFromFunction)) +@pytest.fixture(scope="module", params=params_for_cls(AnalysisFromFunction)) def client_AnalysisFromFunction(request): return request.param -@pytest.fixture(scope='module', - params=params_for_cls(AnalysisFromFunction, - exclude=['multiprocessing'])) +@pytest.fixture( + scope="module", + params=params_for_cls(AnalysisFromFunction, exclude=["multiprocessing"]), +) def client_AnalysisFromFunctionAnalysisClass(request): return request.param -@pytest.fixture(scope='module', params=params_for_cls(IncompleteAnalysis)) +@pytest.fixture(scope="module", params=params_for_cls(IncompleteAnalysis)) def client_IncompleteAnalysis(request): return request.param -@pytest.fixture(scope='module', params=params_for_cls(OldAPIAnalysis)) +@pytest.fixture(scope="module", params=params_for_cls(OldAPIAnalysis)) def client_OldAPIAnalysis(request): return request.param # MDAnalysis.analysis.rms -@pytest.fixture(scope='module', params=params_for_cls(RMSD)) + +@pytest.fixture(scope="module", params=params_for_cls(RMSD)) def client_RMSD(request): return request.param -@pytest.fixture(scope='module', params=params_for_cls(RMSF)) +@pytest.fixture(scope="module", params=params_for_cls(RMSF)) def client_RMSF(request): return request.param # MDAnalysis.analysis.dihedrals -@pytest.fixture(scope='module', params=params_for_cls(Dihedral)) + +@pytest.fixture(scope="module", params=params_for_cls(Dihedral)) def client_Dihedral(request): return request.param -@pytest.fixture(scope='module', params=params_for_cls(Ramachandran)) +@pytest.fixture(scope="module", params=params_for_cls(Ramachandran)) def client_Ramachandran(request): return request.param -@pytest.fixture(scope='module', params=params_for_cls(Janin)) +@pytest.fixture(scope="module", params=params_for_cls(Janin)) def client_Janin(request): return request.param # MDAnalysis.analysis.gnm - -@pytest.fixture(scope='module', params=params_for_cls(GNMAnalysis)) + + +@pytest.fixture(scope="module", params=params_for_cls(GNMAnalysis)) def client_GNMAnalysis(request): return request.param # MDAnalysis.analysis.bat -@pytest.fixture(scope='module', params=params_for_cls(BAT)) + +@pytest.fixture(scope="module", params=params_for_cls(BAT)) def client_BAT(request): return request.param # MDAnalysis.analysis.dssp.dssp + @pytest.fixture(scope="module", params=params_for_cls(DSSP)) def client_DSSP(request): return request.param - + # MDAnalysis.analysis.hydrogenbonds - -@pytest.fixture(scope='module', params=params_for_cls(HydrogenBondAnalysis)) + + +@pytest.fixture(scope="module", params=params_for_cls(HydrogenBondAnalysis)) def client_HydrogenBondAnalysis(request): return request.param # MDAnalysis.analysis.nucleicacids + @pytest.fixture(scope="module", params=params_for_cls(NucPairDist)) def client_NucPairDist(request): return request.param @@ -155,6 +164,7 @@ def client_NucPairDist(request): # MDAnalysis.analysis.contacts + @pytest.fixture(scope="module", params=params_for_cls(Contacts)) def client_Contacts(request): return request.param @@ -162,6 +172,7 @@ def client_Contacts(request): # MDAnalysis.analysis.density -@pytest.fixture(scope='module', params=params_for_cls(DensityAnalysis)) + +@pytest.fixture(scope="module", params=params_for_cls(DensityAnalysis)) def client_DensityAnalysis(request): return request.param diff --git a/testsuite/MDAnalysisTests/analysis/test_align.py b/testsuite/MDAnalysisTests/analysis/test_align.py index 31455198bec..fbda36b1580 100644 --- a/testsuite/MDAnalysisTests/analysis/test_align.py +++ b/testsuite/MDAnalysisTests/analysis/test_align.py @@ -31,15 +31,23 @@ import pytest from MDAnalysis import SelectionError, SelectionWarning from MDAnalysisTests import executable_not_found -from MDAnalysisTests.datafiles import (PSF, DCD, CRD, FASTA, ALIGN_BOUND, - ALIGN_UNBOUND, PDB_helix) +from MDAnalysisTests.datafiles import ( + PSF, + DCD, + CRD, + FASTA, + ALIGN_BOUND, + ALIGN_UNBOUND, + PDB_helix, +) from numpy.testing import ( assert_equal, assert_array_equal, assert_allclose, ) -#Function for Parametrizing conditional raising + +# Function for Parametrizing conditional raising @contextmanager def does_not_raise(): yield @@ -50,10 +58,13 @@ class TestRotationMatrix: b = np.array([[0.1, 0.1, 0.1], [1.1, 1.1, 1.1]]) w = np.array([1.3, 2.3]) - @pytest.mark.parametrize('a, b, weights, expected', ( + @pytest.mark.parametrize( + "a, b, weights, expected", + ( (a, b, None, 0.15785647734415692), (a, b, w, 0.13424643502242328), - )) + ), + ) def test_rotation_matrix_input(self, a, b, weights, expected): rot, rmsd = align.rotation_matrix(a, b, weights) assert_equal(rot, np.eye(3)) @@ -68,11 +79,8 @@ def test_list_args(self): assert rmsd == pytest.approx(0.13424643502242328) def test_exception(self): - a = [[0.1, 0.2, 0.3], - [1.1, 1.1, 1.1], - [2, 2, 2]] - b = [[0.1, 0.1, 0.1], - [1.1, 1.1, 1.1]] + a = [[0.1, 0.2, 0.3], [1.1, 1.1, 1.1], [2, 2, 2]] + b = [[0.1, 0.1, 0.1], [1.1, 1.1, 1.1]] with pytest.raises(ValueError): align.rotation_matrix(a, b) @@ -91,20 +99,23 @@ def reference(): @staticmethod @pytest.fixture() def reference_small(reference): - return mda.Merge(reference.select_atoms( - "not name H* and not atom 4AKE 1 CA")) + return mda.Merge( + reference.select_atoms("not name H* and not atom 4AKE 1 CA") + ) @pytest.mark.parametrize("strict", (True, False)) - def test_match(self, universe, reference, strict, - selection="protein and backbone"): + def test_match( + self, universe, reference, strict, selection="protein and backbone" + ): ref = reference.select_atoms(selection) mobile = universe.select_atoms(selection) groups = align.get_matching_atoms(ref, mobile, strict=strict) assert_equal(groups[0].names, groups[1].names) @pytest.mark.parametrize("strict", (True, False)) - def test_nomatch_atoms_raise(self, universe, reference, - strict, selection="protein and backbone"): + def test_nomatch_atoms_raise( + self, universe, reference, strict, selection="protein and backbone" + ): # one atom less but same residues; with strict=False should try # to get selections (but current code fails, so we also raise SelectionError) ref = reference.select_atoms(selection).atoms[1:] @@ -115,11 +126,18 @@ def test_nomatch_atoms_raise(self, universe, reference, else: with pytest.warns(SelectionWarning): with pytest.raises(SelectionError): - groups = align.get_matching_atoms(ref, mobile, strict=strict) + groups = align.get_matching_atoms( + ref, mobile, strict=strict + ) @pytest.mark.parametrize("strict", (True, False)) - def test_nomatch_residues_raise_empty(self, universe, reference_small, - strict, selection="protein and backbone"): + def test_nomatch_residues_raise_empty( + self, + universe, + reference_small, + strict, + selection="protein and backbone", + ): # one atom less and all residues different: will currently create # empty selections with strict=False, see also # https://gist.github.com/orbeckst/2686badcd15031e6c946baf9164a683d @@ -131,55 +149,78 @@ def test_nomatch_residues_raise_empty(self, universe, reference_small, else: with pytest.warns(SelectionWarning): with pytest.raises(SelectionError): - groups = align.get_matching_atoms(ref, mobile, strict=strict) + groups = align.get_matching_atoms( + ref, mobile, strict=strict + ) def test_toggle_atom_mismatch_default_error(self, universe, reference): - selection = ('resname ALA and name CA', 'resname ALA and name O') + selection = ("resname ALA and name CA", "resname ALA and name O") with pytest.raises(SelectionError): rmsd = align.alignto(universe, reference, select=selection) def test_toggle_atom_mismatch_kwarg_error(self, universe, reference): - selection = ('resname ALA and name CA', 'resname ALA and name O') + selection = ("resname ALA and name CA", "resname ALA and name O") with pytest.raises(SelectionError): - rmsd = align.alignto(universe, reference, select=selection, match_atoms=True) + rmsd = align.alignto( + universe, reference, select=selection, match_atoms=True + ) def test_toggle_atom_nomatch(self, universe, reference): - selection = ('resname ALA and name CA', 'resname ALA and name O') - rmsd = align.alignto(universe, reference, select=selection, match_atoms=False) + selection = ("resname ALA and name CA", "resname ALA and name O") + rmsd = align.alignto( + universe, reference, select=selection, match_atoms=False + ) assert rmsd[0] > 0.01 def test_toggle_atom_nomatch_mismatch_atoms(self, universe, reference): # mismatching number of atoms, but same number of residues - u = universe.select_atoms('resname ALA and name CA') - u += universe.select_atoms('resname ALA and name O')[-1] - ref = reference.select_atoms('resname ALA and name CA') + u = universe.select_atoms("resname ALA and name CA") + u += universe.select_atoms("resname ALA and name O")[-1] + ref = reference.select_atoms("resname ALA and name CA") with pytest.raises(SelectionError): - align.alignto(u, ref, select='all', match_atoms=False) - - @pytest.mark.parametrize('subselection, expectation', [ - ('resname ALA and name CA', does_not_raise()), - (mda.Universe(PSF, DCD).select_atoms('resname ALA and name CA'), does_not_raise()), - (1234, pytest.raises(TypeError)), - ]) - def test_subselection_alignto(self, universe, reference, subselection, expectation): + align.alignto(u, ref, select="all", match_atoms=False) + + @pytest.mark.parametrize( + "subselection, expectation", + [ + ("resname ALA and name CA", does_not_raise()), + ( + mda.Universe(PSF, DCD).select_atoms("resname ALA and name CA"), + does_not_raise(), + ), + (1234, pytest.raises(TypeError)), + ], + ) + def test_subselection_alignto( + self, universe, reference, subselection, expectation + ): with expectation: - rmsd = align.alignto(universe, reference, subselection=subselection) + rmsd = align.alignto( + universe, reference, subselection=subselection + ) assert_allclose(rmsd[1], 0.0, rtol=0, atol=1.5e-9) def test_no_atom_masses(self, universe): - #if no masses are present - u = mda.Universe.empty(6, 2, atom_resindex=[0, 0, 0, 1, 1, 1], trajectory=True) + # if no masses are present + u = mda.Universe.empty( + 6, 2, atom_resindex=[0, 0, 0, 1, 1, 1], trajectory=True + ) with pytest.warns(SelectionWarning): align.get_matching_atoms(u.atoms, u.atoms) def test_one_universe_has_masses(self, universe): - u = mda.Universe.empty(6, 2, atom_resindex=[0, 0, 0, 1, 1, 1], trajectory=True) - ref = mda.Universe.empty(6, 2, atom_resindex=[0, 0, 0, 1, 1, 1], trajectory=True) - ref.add_TopologyAttr('masses') + u = mda.Universe.empty( + 6, 2, atom_resindex=[0, 0, 0, 1, 1, 1], trajectory=True + ) + ref = mda.Universe.empty( + 6, 2, atom_resindex=[0, 0, 0, 1, 1, 1], trajectory=True + ) + ref.add_TopologyAttr("masses") with pytest.warns(SelectionWarning): align.get_matching_atoms(u.atoms, ref.atoms) + class TestAlign(object): @staticmethod @pytest.fixture() @@ -193,45 +234,62 @@ def reference(): def test_rmsd(self, universe, reference): universe.trajectory[0] # ensure first frame - bb = universe.select_atoms('backbone') + bb = universe.select_atoms("backbone") first_frame = bb.positions universe.trajectory[-1] last_frame = bb.positions - assert_allclose(rms.rmsd(first_frame, first_frame), 0.0, rtol=0, atol=1.5e-5, - err_msg="error: rmsd(X,X) should be 0") + assert_allclose( + rms.rmsd(first_frame, first_frame), + 0.0, + rtol=0, + atol=1.5e-5, + err_msg="error: rmsd(X,X) should be 0", + ) # rmsd(A,B) = rmsd(B,A) should be exact but spurious failures in the # 9th decimal have been observed (see Issue 57 comment #1) so we relax # the test to 6 decimals. rmsd = rms.rmsd(first_frame, last_frame, superposition=True) assert_allclose( - rms.rmsd(last_frame, first_frame, superposition=True), rmsd, rtol=0, atol=1.5e-6, - err_msg="error: rmsd() is not symmetric") - assert_allclose(rmsd, 6.820321761927005, rtol=0, atol=1.5e-5, - err_msg="RMSD calculation between 1st and last AdK frame gave wrong answer") + rms.rmsd(last_frame, first_frame, superposition=True), + rmsd, + rtol=0, + atol=1.5e-6, + err_msg="error: rmsd() is not symmetric", + ) + assert_allclose( + rmsd, + 6.820321761927005, + rtol=0, + atol=1.5e-5, + err_msg="RMSD calculation between 1st and last AdK frame gave wrong answer", + ) # test masses as weights last_atoms_weight = universe.atoms.masses A = universe.trajectory[0] B = reference.trajectory[-1] - rmsd = align.alignto(universe, reference, weights='mass') - rmsd_sup_weight = rms.rmsd(A, B, weights=last_atoms_weight, center=True, - superposition=True) + rmsd = align.alignto(universe, reference, weights="mass") + rmsd_sup_weight = rms.rmsd( + A, B, weights=last_atoms_weight, center=True, superposition=True + ) assert_allclose(rmsd[1], rmsd_sup_weight, rtol=0, atol=1.5e-6) def test_rmsd_custom_mass_weights(self, universe, reference): last_atoms_weight = universe.atoms.masses A = universe.trajectory[0] B = reference.trajectory[-1] - rmsd = align.alignto(universe, reference, - weights=reference.atoms.masses) - rmsd_sup_weight = rms.rmsd(A, B, weights=last_atoms_weight, center=True, - superposition=True) + rmsd = align.alignto( + universe, reference, weights=reference.atoms.masses + ) + rmsd_sup_weight = rms.rmsd( + A, B, weights=last_atoms_weight, center=True, superposition=True + ) assert_allclose(rmsd[1], rmsd_sup_weight, rtol=0, atol=1.5e-6) def test_rmsd_custom_weights(self, universe, reference): weights = np.zeros(universe.atoms.n_atoms) - ca = universe.select_atoms('name CA') + ca = universe.select_atoms("name CA") weights[ca.indices] = 1 - rmsd = align.alignto(universe, reference, select='name CA') + rmsd = align.alignto(universe, reference, select="name CA") rmsd_weights = align.alignto(universe, reference, weights=weights) assert_allclose(rmsd[1], rmsd_weights[1], rtol=0, atol=1.5e-6) @@ -240,19 +298,23 @@ def test_AlignTraj_outfile_default(self, universe, reference, tmpdir): reference.trajectory[-1] x = align.AlignTraj(universe, reference) try: - assert os.path.basename(x.filename) == 'rmsfit_adk_dims.dcd' + assert os.path.basename(x.filename) == "rmsfit_adk_dims.dcd" finally: x._writer.close() - def test_AlignTraj_outfile_default_exists(self, universe, reference, tmpdir): + def test_AlignTraj_outfile_default_exists( + self, universe, reference, tmpdir + ): reference.trajectory[-1] - outfile = str(tmpdir.join('align_test.dcd')) + outfile = str(tmpdir.join("align_test.dcd")) align.AlignTraj(universe, reference, filename=outfile).run() fitted = mda.Universe(PSF, outfile) # ensure default file exists - with mda.Writer(str(tmpdir.join("rmsfit_align_test.dcd")), - n_atoms=fitted.atoms.n_atoms) as w: + with mda.Writer( + str(tmpdir.join("rmsfit_align_test.dcd")), + n_atoms=fitted.atoms.n_atoms, + ) as w: w.write(fitted.atoms) with tmpdir.as_cwd(): @@ -264,13 +326,13 @@ def test_AlignTraj_outfile_default_exists(self, universe, reference, tmpdir): def test_AlignTraj_step_works(self, universe, reference, tmpdir): reference.trajectory[-1] - outfile = str(tmpdir.join('align_test.dcd')) + outfile = str(tmpdir.join("align_test.dcd")) # this shouldn't throw an exception align.AlignTraj(universe, reference, filename=outfile).run(step=10) def test_AlignTraj_deprecated_attribute(self, universe, reference, tmpdir): reference.trajectory[-1] - outfile = str(tmpdir.join('align_test.dcd')) + outfile = str(tmpdir.join("align_test.dcd")) x = align.AlignTraj(universe, reference, filename=outfile).run(stop=2) wmsg = "The `rmsd` attribute was deprecated in MDAnalysis 2.0.0" @@ -279,7 +341,7 @@ def test_AlignTraj_deprecated_attribute(self, universe, reference, tmpdir): def test_AlignTraj(self, universe, reference, tmpdir): reference.trajectory[-1] - outfile = str(tmpdir.join('align_test.dcd')) + outfile = str(tmpdir.join("align_test.dcd")) x = align.AlignTraj(universe, reference, filename=outfile).run() fitted = mda.Universe(PSF, outfile) @@ -293,58 +355,84 @@ def test_AlignTraj(self, universe, reference, tmpdir): self._assert_rmsd(reference, fitted, -1, 0.0) def test_AlignTraj_weighted(self, universe, reference, tmpdir): - outfile = str(tmpdir.join('align_test.dcd')) - x = align.AlignTraj(universe, reference, - filename=outfile, weights='mass').run() + outfile = str(tmpdir.join("align_test.dcd")) + x = align.AlignTraj( + universe, reference, filename=outfile, weights="mass" + ).run() fitted = mda.Universe(PSF, outfile) assert_allclose(x.results.rmsd[0], 0, rtol=0, atol=1.5e-3) assert_allclose(x.results.rmsd[-1], 6.9033, rtol=0, atol=1.5e-3) - self._assert_rmsd(reference, fitted, 0, 0.0, - weights=universe.atoms.masses) - self._assert_rmsd(reference, fitted, -1, 6.929083032629219, - weights=universe.atoms.masses) + self._assert_rmsd( + reference, fitted, 0, 0.0, weights=universe.atoms.masses + ) + self._assert_rmsd( + reference, + fitted, + -1, + 6.929083032629219, + weights=universe.atoms.masses, + ) def test_AlignTraj_custom_weights(self, universe, reference, tmpdir): weights = np.zeros(universe.atoms.n_atoms) - ca = universe.select_atoms('name CA') + ca = universe.select_atoms("name CA") weights[ca.indices] = 1 - outfile = str(tmpdir.join('align_test.dcd')) + outfile = str(tmpdir.join("align_test.dcd")) - x = align.AlignTraj(universe, reference, - filename=outfile, select='name CA').run() - x_weights = align.AlignTraj(universe, reference, - filename=outfile, weights=weights).run() + x = align.AlignTraj( + universe, reference, filename=outfile, select="name CA" + ).run() + x_weights = align.AlignTraj( + universe, reference, filename=outfile, weights=weights + ).run() - assert_allclose(x.results.rmsd, x_weights.results.rmsd, rtol=0, atol=1.5e-7) + assert_allclose( + x.results.rmsd, x_weights.results.rmsd, rtol=0, atol=1.5e-7 + ) def test_AlignTraj_custom_mass_weights(self, universe, reference, tmpdir): - outfile = str(tmpdir.join('align_test.dcd')) - x = align.AlignTraj(universe, reference, - filename=outfile, - weights=reference.atoms.masses).run() + outfile = str(tmpdir.join("align_test.dcd")) + x = align.AlignTraj( + universe, + reference, + filename=outfile, + weights=reference.atoms.masses, + ).run() fitted = mda.Universe(PSF, outfile) assert_allclose(x.results.rmsd[0], 0, rtol=0, atol=1.5e-3) assert_allclose(x.results.rmsd[-1], 6.9033, rtol=0, atol=1.5e-3) - self._assert_rmsd(reference, fitted, 0, 0.0, - weights=universe.atoms.masses) - self._assert_rmsd(reference, fitted, -1, 6.929083032629219, - weights=universe.atoms.masses) + self._assert_rmsd( + reference, fitted, 0, 0.0, weights=universe.atoms.masses + ) + self._assert_rmsd( + reference, + fitted, + -1, + 6.929083032629219, + weights=universe.atoms.masses, + ) def test_AlignTraj_partial_fit(self, universe, reference, tmpdir): - outfile = str(tmpdir.join('align_test.dcd')) + outfile = str(tmpdir.join("align_test.dcd")) # fitting on a partial selection should still write the whole topology - align.AlignTraj(universe, reference, select='resid 1-20', - filename=outfile, weights='mass').run() + align.AlignTraj( + universe, + reference, + select="resid 1-20", + filename=outfile, + weights="mass", + ).run() mda.Universe(PSF, outfile) def test_AlignTraj_in_memory(self, universe, reference, tmpdir): - outfile = str(tmpdir.join('align_test.dcd')) + outfile = str(tmpdir.join("align_test.dcd")) reference.trajectory[-1] - x = align.AlignTraj(universe, reference, filename=outfile, - in_memory=True).run() + x = align.AlignTraj( + universe, reference, filename=outfile, in_memory=True + ).run() assert x.filename is None assert_allclose(x.results.rmsd[0], 6.9290, rtol=0, atol=1.5e-3) assert_allclose(x.results.rmsd[-1], 5.2797e-07, rtol=0, atol=1.5e-3) @@ -357,20 +445,31 @@ def test_AlignTraj_writer_kwargs(self, universe, reference, tmpdir): # Issue 4564 writer_kwargs = dict(precision=2) with tmpdir.as_cwd(): - aligner = align.AlignTraj(universe, reference, - select='protein and name CA', - filename='aligned_traj.xtc', - writer_kwargs=writer_kwargs, - in_memory=False).run() + aligner = align.AlignTraj( + universe, + reference, + select="protein and name CA", + filename="aligned_traj.xtc", + writer_kwargs=writer_kwargs, + in_memory=False, + ).run() assert_equal(aligner._writer.precision, 2) def _assert_rmsd(self, reference, fitted, frame, desired, weights=None): fitted.trajectory[frame] - rmsd = rms.rmsd(reference.atoms.positions, fitted.atoms.positions, - superposition=True) - assert_allclose(rmsd, desired, rtol = 0, atol=1.5e-5, - err_msg="frame {0:d} of fit does not have " - "expected RMSD".format(frame)) + rmsd = rms.rmsd( + reference.atoms.positions, + fitted.atoms.positions, + superposition=True, + ) + assert_allclose( + rmsd, + desired, + rtol=0, + atol=1.5e-5, + err_msg="frame {0:d} of fit does not have " + "expected RMSD".format(frame), + ) def test_alignto_checks_selections(self, universe, reference): """Testing that alignto() fails if selections do not @@ -396,15 +495,16 @@ def different_atoms(): def test_alignto_partial_universe(self, universe, reference): u_bound = mda.Universe(ALIGN_BOUND) u_free = mda.Universe(ALIGN_UNBOUND) - selection = 'segid B' + selection = "segid B" segB_bound = u_bound.select_atoms(selection) segB_free = u_free.select_atoms(selection) segB_free.translate(segB_bound.centroid() - segB_free.centroid()) align.alignto(u_free, u_bound, select=selection) - assert_allclose(segB_bound.positions, segB_free.positions, - rtol=0, atol=1.5e-3) + assert_allclose( + segB_bound.positions, segB_free.positions, rtol=0, atol=1.5e-3 + ) def _get_aligned_average_positions(ref_files, ref, select="all", **kwargs): @@ -412,9 +512,10 @@ def _get_aligned_average_positions(ref_files, ref, select="all", **kwargs): prealigner = align.AlignTraj(u, ref, select=select, **kwargs).run() ag = u.select_atoms(select) reference_coordinates = u.trajectory.timeseries(asel=ag).mean(axis=1) - rmsd = sum(prealigner.results.rmsd/len(u.trajectory)) + rmsd = sum(prealigner.results.rmsd / len(u.trajectory)) return reference_coordinates, rmsd + class TestAverageStructure(object): ref_files = (PSF, DCD) @@ -433,8 +534,10 @@ def test_average_structure_deprecated_attrs(self, universe, reference): wmsg = "The `universe` attribute was deprecated in MDAnalysis 2.0.0" with pytest.warns(DeprecationWarning, match=wmsg): - assert_equal(avg.universe.atoms.positions, - avg.results.universe.atoms.positions) + assert_equal( + avg.universe.atoms.positions, + avg.results.universe.atoms.positions, + ) wmsg = "The `positions` attribute was deprecated in MDAnalysis 2.0.0" with pytest.warns(DeprecationWarning, match=wmsg): @@ -447,34 +550,43 @@ def test_average_structure_deprecated_attrs(self, universe, reference): def test_average_structure(self, universe, reference): ref, rmsd = _get_aligned_average_positions(self.ref_files, reference) avg = align.AverageStructure(universe, reference).run() - assert_allclose(avg.results.universe.atoms.positions, ref, rtol=0, atol=1.5e-4) + assert_allclose( + avg.results.universe.atoms.positions, ref, rtol=0, atol=1.5e-4 + ) assert_allclose(avg.results.rmsd, rmsd, rtol=0, atol=1.5e-7) def test_average_structure_mass_weighted(self, universe, reference): - ref, rmsd = _get_aligned_average_positions(self.ref_files, reference, weights='mass') - avg = align.AverageStructure(universe, reference, weights='mass').run() - assert_allclose(avg.results.universe.atoms.positions, ref, - rtol=0, atol=1.5e-4) + ref, rmsd = _get_aligned_average_positions( + self.ref_files, reference, weights="mass" + ) + avg = align.AverageStructure(universe, reference, weights="mass").run() + assert_allclose( + avg.results.universe.atoms.positions, ref, rtol=0, atol=1.5e-4 + ) assert_allclose(avg.results.rmsd, rmsd, rtol=0, atol=1.5e-7) def test_average_structure_select(self, universe, reference): - select = 'protein and name CA and resid 3-5' - ref, rmsd = _get_aligned_average_positions(self.ref_files, reference, select=select) + select = "protein and name CA and resid 3-5" + ref, rmsd = _get_aligned_average_positions( + self.ref_files, reference, select=select + ) avg = align.AverageStructure(universe, reference, select=select).run() - assert_allclose(avg.results.universe.atoms.positions, ref, - rtol=0, atol=1.5e-4) + assert_allclose( + avg.results.universe.atoms.positions, ref, rtol=0, atol=1.5e-4 + ) assert_allclose(avg.results.rmsd, rmsd, rtol=0, atol=1.5e-7) def test_average_structure_no_ref(self, universe): ref, rmsd = _get_aligned_average_positions(self.ref_files, universe) avg = align.AverageStructure(universe).run() - assert_allclose(avg.results.universe.atoms.positions, ref, - rtol=0, atol=1.5e-4) + assert_allclose( + avg.results.universe.atoms.positions, ref, rtol=0, atol=1.5e-4 + ) assert_allclose(avg.results.rmsd, rmsd, rtol=0, atol=1.5e-7) def test_average_structure_no_msf(self, universe): avg = align.AverageStructure(universe).run() - assert not hasattr(avg, 'msf') + assert not hasattr(avg, "msf") def test_mismatch_atoms(self, universe): u = mda.Merge(universe.atoms[:10]) @@ -493,15 +605,20 @@ def test_average_structure_ref_frame(self, universe): universe.trajectory[0] ref, rmsd = _get_aligned_average_positions(self.ref_files, u) avg = align.AverageStructure(universe, ref_frame=ref_frame).run() - assert_allclose(avg.results.universe.atoms.positions, ref, - rtol=0, atol=1.5e-4) + assert_allclose( + avg.results.universe.atoms.positions, ref, rtol=0, atol=1.5e-4 + ) assert_allclose(avg.results.rmsd, rmsd, rtol=0, atol=1.5e-7) def test_average_structure_in_memory(self, universe): avg = align.AverageStructure(universe, in_memory=True).run() reference_coordinates = universe.trajectory.timeseries().mean(axis=1) - assert_allclose(avg.results.universe.atoms.positions, - reference_coordinates, rtol=0, atol=1.5e-4) + assert_allclose( + avg.results.universe.atoms.positions, + reference_coordinates, + rtol=0, + atol=1.5e-4, + ) assert avg.filename is None @@ -509,57 +626,65 @@ class TestAlignmentProcessing: seq = FASTA error_msg = "selection string has unexpected length" - @pytest.mark.skipif(HAS_BIOPYTHON, reason='biopython is installed') + @pytest.mark.skipif(HAS_BIOPYTHON, reason="biopython is installed") def test_importerror_biopython(self): errmsg = "The `fasta2select` method requires an installation" with pytest.raises(ImportError, match=errmsg): _ = align.fasta2select(self.seq, is_aligned=True) - @pytest.mark.skipif(not HAS_BIOPYTHON, reason='requires biopython') + @pytest.mark.skipif(not HAS_BIOPYTHON, reason="requires biopython") def test_fasta2select_aligned(self): """test align.fasta2select() on aligned FASTA (Issue 112)""" sel = align.fasta2select(self.seq, is_aligned=True) # length of the output strings, not residues or anything real... - assert len(sel['reference']) == 30623, self.error_msg - assert len(sel['mobile']) == 30623, self.error_msg + assert len(sel["reference"]) == 30623, self.error_msg + assert len(sel["mobile"]) == 30623, self.error_msg @pytest.mark.skipif( executable_not_found("clustalw2") or not HAS_BIOPYTHON, - reason="Test skipped because clustalw2 executable not found") + reason="Test skipped because clustalw2 executable not found", + ) def test_fasta2select_file(self, tmpdir): """test align.fasta2select() on a non-aligned FASTA with default filenames""" with tmpdir.as_cwd(): - sel = align.fasta2select(self.seq, is_aligned=False, - alnfilename=None, treefilename=None) - assert len(sel['reference']) == 23080, self.error_msg - assert len(sel['mobile']) == 23090, self.error_msg + sel = align.fasta2select( + self.seq, is_aligned=False, alnfilename=None, treefilename=None + ) + assert len(sel["reference"]) == 23080, self.error_msg + assert len(sel["mobile"]) == 23090, self.error_msg @pytest.mark.skipif( executable_not_found("clustalw2") or not HAS_BIOPYTHON, - reason="Test skipped because clustalw2 executable not found") + reason="Test skipped because clustalw2 executable not found", + ) def test_fasta2select_ClustalW(self, tmpdir): """MDAnalysis.analysis.align: test fasta2select() with ClustalW (Issue 113)""" - alnfile = str(tmpdir.join('alignmentprocessing.aln')) - treefile = str(tmpdir.join('alignmentprocessing.dnd')) - sel = align.fasta2select(self.seq, is_aligned=False, - alnfilename=alnfile, treefilename=treefile) + alnfile = str(tmpdir.join("alignmentprocessing.aln")) + treefile = str(tmpdir.join("alignmentprocessing.dnd")) + sel = align.fasta2select( + self.seq, + is_aligned=False, + alnfilename=alnfile, + treefilename=treefile, + ) # numbers computed from alignment with clustalw 2.1 on Mac OS X # [orbeckst] length of the output strings, not residues or anything # real... - assert len(sel['reference']) == 23080, self.error_msg - assert len(sel['mobile']) == 23090, self.error_msg + assert len(sel["reference"]) == 23080, self.error_msg + assert len(sel["mobile"]) == 23090, self.error_msg - @pytest.mark.skipif(not HAS_BIOPYTHON, reason='requires biopython') + @pytest.mark.skipif(not HAS_BIOPYTHON, reason="requires biopython") def test_fasta2select_resids(self, tmpdir): """test align.fasta2select() when resids provided (Issue #3124)""" resids = [x for x in range(705)] - sel = align.fasta2select(self.seq, is_aligned=True, - ref_resids=resids, target_resids=resids) + sel = align.fasta2select( + self.seq, is_aligned=True, ref_resids=resids, target_resids=resids + ) # length of the output strings, not residues or anything real... - assert len(sel['reference']) == 30621, self.error_msg - assert len(sel['mobile']) == 30621, self.error_msg + assert len(sel["reference"]) == 30621, self.error_msg + assert len(sel["mobile"]) == 30621, self.error_msg class TestSequenceAlignmentFunction: @@ -573,14 +698,14 @@ def atomgroups(): mobile = universe.select_atoms("resid 122-159") return reference, mobile - @pytest.mark.skipif(HAS_BIOPYTHON, reason='biopython installed') + @pytest.mark.skipif(HAS_BIOPYTHON, reason="biopython installed") def test_biopython_import_error(self, atomgroups): ref, mob = atomgroups errmsg = "The `sequence_alignment` method requires an installation of" with pytest.raises(ImportError, match=errmsg): align.sequence_alignment(mob, ref) - @pytest.mark.skipif(not HAS_BIOPYTHON, reason='requires biopython') + @pytest.mark.skipif(not HAS_BIOPYTHON, reason="requires biopython") @pytest.mark.filterwarnings("ignore:`sequence_alignment` is deprecated!") def test_sequence_alignment(self, atomgroups): reference, mobile = atomgroups @@ -589,18 +714,24 @@ def test_sequence_alignment(self, atomgroups): assert len(aln) == 5, "return value has wrong tuple size" seqA, seqB, score, begin, end = aln - assert_equal(seqA, reference.residues.sequence(format="string"), - err_msg="reference sequence mismatch") - assert mobile.residues.sequence( - format="string") in seqB, "mobile sequence mismatch" - assert score == pytest.approx(54.6) + assert_equal( + seqA, + reference.residues.sequence(format="string"), + err_msg="reference sequence mismatch", + ) + assert ( + mobile.residues.sequence(format="string") in seqB + ), "mobile sequence mismatch" + assert score == pytest.approx(54.6) assert_array_equal([begin, end], [0, reference.n_residues]) - @pytest.mark.skipif(not HAS_BIOPYTHON, reason='requires biopython') + @pytest.mark.skipif(not HAS_BIOPYTHON, reason="requires biopython") def test_sequence_alignment_deprecation(self, atomgroups): reference, mobile = atomgroups - wmsg = ("`sequence_alignment` is deprecated!\n" - "`sequence_alignment` will be removed in release 3.0.") + wmsg = ( + "`sequence_alignment` is deprecated!\n" + "`sequence_alignment` will be removed in release 3.0." + ) with pytest.warns(DeprecationWarning, match=wmsg): align.sequence_alignment(mobile, reference) @@ -630,14 +761,13 @@ def test_iterative_average_default(self, mobile): [10.54679871, 9.49505306, -8.61215292], [9.99500556, 9.16624224, -7.75231192], [9.83897407, 9.93134598, -9.29541129], - [11.45760169, 10.5857071, -8.13037669] + [11.45760169, 10.5857071, -8.13037669], ], atol=1e-5, ) def test_iterative_average_eps_high(self, mobile): - res = align.iterative_average(mobile, select="bynum 1:10", - eps=1e-5) + res = align.iterative_average(mobile, select="bynum 1:10", eps=1e-5) assert_allclose( res.results.positions, [ @@ -650,15 +780,15 @@ def test_iterative_average_eps_high(self, mobile): [10.54679871, 9.49505306, -8.61215292], [9.99500556, 9.16624224, -7.75231192], [9.83897407, 9.93134598, -9.29541129], - [11.45760169, 10.5857071, -8.13037669] + [11.45760169, 10.5857071, -8.13037669], ], atol=1e-5, ) def test_iterative_average_weights_mass(self, mobile, reference): - res = align.iterative_average(mobile, reference, - select="bynum 1:10", - niter=10, weights="mass") + res = align.iterative_average( + mobile, reference, select="bynum 1:10", niter=10, weights="mass" + ) assert_allclose( res.results.positions, [ @@ -671,19 +801,17 @@ def test_iterative_average_weights_mass(self, mobile, reference): [10.37499697, 9.13535837, -8.3732586], [9.83883314, 8.57939098, -7.6195549], [9.64405257, 9.55924307, -9.04315991], - [11.0678934, 10.27798773, -7.64881842] + [11.0678934, 10.27798773, -7.64881842], ], atol=1e-5, ) def test_iterative_average_convergence_failure(self, mobile, reference): with pytest.raises(RuntimeError): - _ = align.iterative_average(mobile, reference, - niter=1, eps=0) + _ = align.iterative_average(mobile, reference, niter=1, eps=0) def test_iterative_average_convergence_verbose(self, mobile, reference): - _ = align.iterative_average(mobile, select="bynum 1:10", - verbose=True) + _ = align.iterative_average(mobile, select="bynum 1:10", verbose=True) def test_alignto_reorder_atomgroups(): @@ -691,5 +819,5 @@ def test_alignto_reorder_atomgroups(): u = mda.Universe(PDB_helix) mobile = u.atoms[:4] ref = u.atoms[[3, 2, 1, 0]] - rmsd = align.alignto(mobile, ref, select='bynum 1-4') + rmsd = align.alignto(mobile, ref, select="bynum 1-4") assert_allclose(rmsd, (0.0, 0.0)) diff --git a/testsuite/MDAnalysisTests/analysis/test_atomicdistances.py b/testsuite/MDAnalysisTests/analysis/test_atomicdistances.py index 443173cff70..4697485470b 100644 --- a/testsuite/MDAnalysisTests/analysis/test_atomicdistances.py +++ b/testsuite/MDAnalysisTests/analysis/test_atomicdistances.py @@ -79,8 +79,9 @@ def ad_ag4(): @staticmethod @pytest.fixture() def expected_dist(ad_ag1, ad_ag2): - expected = np.zeros((len(ad_ag1.universe.trajectory), - ad_ag1.atoms.n_atoms)) + expected = np.zeros( + (len(ad_ag1.universe.trajectory), ad_ag1.atoms.n_atoms) + ) # calculate distances without PBCs using dist() for i, ts in enumerate(ad_ag1.universe.trajectory): @@ -90,8 +91,9 @@ def expected_dist(ad_ag1, ad_ag2): @staticmethod @pytest.fixture() def expected_pbc_dist(ad_ag1, ad_ag2): - expected = np.zeros((len(ad_ag1.universe.trajectory), - ad_ag1.atoms.n_atoms)) + expected = np.zeros( + (len(ad_ag1.universe.trajectory), ad_ag1.atoms.n_atoms) + ) # calculate distances with PBCs using dist() for i, ts in enumerate(ad_ag1.universe.trajectory): @@ -99,8 +101,8 @@ def expected_pbc_dist(ad_ag1, ad_ag2): return expected def test_ad_exceptions(self, ad_ag1, ad_ag3, ad_ag4): - '''Test exceptions raised when number of atoms do not - match or AtomGroups come from different trajectories.''' + """Test exceptions raised when number of atoms do not + match or AtomGroups come from different trajectories.""" # number of atoms do not match match_exp_atoms = "AtomGroups do not" @@ -114,22 +116,19 @@ def test_ad_exceptions(self, ad_ag1, ad_ag3, ad_ag4): # only need to test that this class correctly applies distance calcs # calc_bonds() is tested elsewhere - def test_ad_pairwise_dist(self, ad_ag1, ad_ag2, - expected_dist): - '''Ensure that pairwise distances between atoms are - correctly calculated without PBCs.''' - pairwise_no_pbc = (ad.AtomicDistances(ad_ag1, ad_ag2, - pbc=False).run()) + def test_ad_pairwise_dist(self, ad_ag1, ad_ag2, expected_dist): + """Ensure that pairwise distances between atoms are + correctly calculated without PBCs.""" + pairwise_no_pbc = ad.AtomicDistances(ad_ag1, ad_ag2, pbc=False).run() actual = pairwise_no_pbc.results # compare with expected values from dist() assert_allclose(actual, expected_dist) - def test_ad_pairwise_dist_pbc(self, ad_ag1, ad_ag2, - expected_pbc_dist): - '''Ensure that pairwise distances between atoms are - correctly calculated with PBCs.''' - pairwise_pbc = (ad.AtomicDistances(ad_ag1, ad_ag2).run()) + def test_ad_pairwise_dist_pbc(self, ad_ag1, ad_ag2, expected_pbc_dist): + """Ensure that pairwise distances between atoms are + correctly calculated with PBCs.""" + pairwise_pbc = ad.AtomicDistances(ad_ag1, ad_ag2).run() actual = pairwise_pbc.results # compare with expected values from dist() diff --git a/testsuite/MDAnalysisTests/analysis/test_backends.py b/testsuite/MDAnalysisTests/analysis/test_backends.py index a4c105e082a..471d367a603 100644 --- a/testsuite/MDAnalysisTests/analysis/test_backends.py +++ b/testsuite/MDAnalysisTests/analysis/test_backends.py @@ -51,22 +51,36 @@ def test_all_backends_give_correct_results(self, func, iterable, answer): for answ in backends_dict.values(): assert answ == answer - @pytest.mark.parametrize("backend_cls,params,warning_message", [ - (backends.BackendSerial, { - 'n_workers': 5 - }, "n_workers is ignored when executing with backend='serial'"), - ]) + @pytest.mark.parametrize( + "backend_cls,params,warning_message", + [ + ( + backends.BackendSerial, + {"n_workers": 5}, + "n_workers is ignored when executing with backend='serial'", + ), + ], + ) def test_get_warnings(self, backend_cls, params, warning_message): with pytest.warns(UserWarning, match=warning_message): backend_cls(**params) - @pytest.mark.parametrize("backend_cls,params,error_message", [ - pytest.param(backends.BackendDask, {'n_workers': 2}, - ("module 'dask' is missing. Please install 'dask': " - "https://docs.dask.org/en/stable/install.html"), - marks=pytest.mark.skipif(is_installed('dask'), - reason='dask is installed')) - ]) + @pytest.mark.parametrize( + "backend_cls,params,error_message", + [ + pytest.param( + backends.BackendDask, + {"n_workers": 2}, + ( + "module 'dask' is missing. Please install 'dask': " + "https://docs.dask.org/en/stable/install.html" + ), + marks=pytest.mark.skipif( + is_installed("dask"), reason="dask is installed" + ), + ) + ], + ) def test_get_errors(self, backend_cls, params, error_message): with pytest.raises(ValueError, match=error_message): backend_cls(**params) diff --git a/testsuite/MDAnalysisTests/analysis/test_base.py b/testsuite/MDAnalysisTests/analysis/test_base.py index 377d70602ba..4208b604de1 100644 --- a/testsuite/MDAnalysisTests/analysis/test_base.py +++ b/testsuite/MDAnalysisTests/analysis/test_base.py @@ -42,7 +42,8 @@ class FrameAnalysis(base.AnalysisBase): """Just grabs frame numbers of frames it goes over""" @classmethod - def get_supported_backends(cls): return ('serial', 'dask', 'multiprocessing') + def get_supported_backends(cls): + return ("serial", "dask", "multiprocessing") _analysis_algorithm_is_parallelizable = True @@ -60,7 +61,10 @@ def _conclude(self): self.found_frames = list(self.results.found_frames) def _get_aggregator(self): - return base.ResultsGroup({'found_frames': base.ResultsGroup.ndarray_hstack}) + return base.ResultsGroup( + {"found_frames": base.ResultsGroup.ndarray_hstack} + ) + class IncompleteAnalysis(base.AnalysisBase): def __init__(self, reader, **kwargs): @@ -80,42 +84,57 @@ def _prepare(self): self.results = base.Results() -@pytest.fixture(scope='module') +@pytest.fixture(scope="module") def u(): return mda.Universe(PSF, DCD) -@pytest.fixture(scope='module') +@pytest.fixture(scope="module") def u_xtc(): return mda.Universe(TPR, XTC) # dt = 100 -FRAMES_ERR = 'AnalysisBase.frames is incorrect' -TIMES_ERR = 'AnalysisBase.times is incorrect' +FRAMES_ERR = "AnalysisBase.frames is incorrect" +TIMES_ERR = "AnalysisBase.times is incorrect" + class Parallelizable(base.AnalysisBase): _analysis_algorithm_is_parallelizable = True + @classmethod - def get_supported_backends(cls): return ('multiprocessing', 'dask') - def _single_frame(self): pass + def get_supported_backends(cls): + return ("multiprocessing", "dask") + + def _single_frame(self): + pass + class SerialOnly(base.AnalysisBase): - def _single_frame(self): pass + def _single_frame(self): + pass + class ParallelizableWithDaskOnly(base.AnalysisBase): _analysis_algorithm_is_parallelizable = True + @classmethod - def get_supported_backends(cls): return ('dask',) - def _single_frame(self): pass + def get_supported_backends(cls): + return ("dask",) + + def _single_frame(self): + pass + class CustomSerialBackend(backends.BackendBase): def apply(self, func, computations): return [func(task) for task in computations] + class ManyWorkersBackend(backends.BackendBase): def apply(self, func, computations): return [func(task) for task in computations] + def test_incompatible_n_workers(u): backend = ManyWorkersBackend(n_workers=2) with pytest.raises(ValueError): @@ -126,91 +145,137 @@ def test_frame_values_incompatability(u): start, stop, step = 0, 4, 1 frames = [1, 2, 3, 4] - with pytest.raises(ValueError, - match="start/stop/step cannot be combined with frames"): + with pytest.raises( + ValueError, match="start/stop/step cannot be combined with frames" + ): FrameAnalysis(u.trajectory).run( - frames=frames, - start=start, - stop=stop, - step=step + frames=frames, start=start, stop=stop, step=step ) + def test_n_workers_conflict_raises_value_error(u): backend_instance = ManyWorkersBackend(n_workers=4) with pytest.raises(ValueError, match="n_workers specified twice"): FrameAnalysis(u.trajectory).run( - backend=backend_instance, - n_workers=1, - unsupported_backend=True + backend=backend_instance, n_workers=1, unsupported_backend=True ) -@pytest.mark.parametrize('run_class,backend,n_workers', [ - (Parallelizable, 'not-existing-backend', 2), - (Parallelizable, 'not-existing-backend', None), - (SerialOnly, 'not-existing-backend', 2), - (SerialOnly, 'not-existing-backend', None), - (SerialOnly, 'multiprocessing', 2), - (SerialOnly, 'dask', None), - (ParallelizableWithDaskOnly, 'multiprocessing', None), - (ParallelizableWithDaskOnly, 'multiprocessing', 2), -]) + +@pytest.mark.parametrize( + "run_class,backend,n_workers", + [ + (Parallelizable, "not-existing-backend", 2), + (Parallelizable, "not-existing-backend", None), + (SerialOnly, "not-existing-backend", 2), + (SerialOnly, "not-existing-backend", None), + (SerialOnly, "multiprocessing", 2), + (SerialOnly, "dask", None), + (ParallelizableWithDaskOnly, "multiprocessing", None), + (ParallelizableWithDaskOnly, "multiprocessing", 2), + ], +) def test_backend_configuration_fails(u, run_class, backend, n_workers): u = mda.Universe(TPR, XTC) # dt = 100 with pytest.raises(ValueError): - _ = run_class(u.trajectory).run(backend=backend, n_workers=n_workers, stop=0) + _ = run_class(u.trajectory).run( + backend=backend, n_workers=n_workers, stop=0 + ) + -@pytest.mark.parametrize('run_class,backend,n_workers', [ - (Parallelizable, CustomSerialBackend, 2), - (ParallelizableWithDaskOnly, CustomSerialBackend, 2), -]) -def test_backend_configuration_works_when_unsupported_backend(u, run_class, backend, n_workers): +@pytest.mark.parametrize( + "run_class,backend,n_workers", + [ + (Parallelizable, CustomSerialBackend, 2), + (ParallelizableWithDaskOnly, CustomSerialBackend, 2), + ], +) +def test_backend_configuration_works_when_unsupported_backend( + u, run_class, backend, n_workers +): u = mda.Universe(TPR, XTC) # dt = 100 backend_instance = backend(n_workers=n_workers) - _ = run_class(u.trajectory).run(backend=backend_instance, n_workers=n_workers, stop=0, unsupported_backend=True) + _ = run_class(u.trajectory).run( + backend=backend_instance, + n_workers=n_workers, + stop=0, + unsupported_backend=True, + ) + -@pytest.mark.parametrize('run_class,backend,n_workers', [ - (Parallelizable, CustomSerialBackend, 1), - (ParallelizableWithDaskOnly, CustomSerialBackend, 1), -]) +@pytest.mark.parametrize( + "run_class,backend,n_workers", + [ + (Parallelizable, CustomSerialBackend, 1), + (ParallelizableWithDaskOnly, CustomSerialBackend, 1), + ], +) def test_custom_backend_works(u, run_class, backend, n_workers): backend_instance = backend(n_workers=n_workers) u = mda.Universe(TPR, XTC) # dt = 100 - _ = run_class(u.trajectory).run(backend=backend_instance, n_workers=n_workers, unsupported_backend=True) - -@pytest.mark.parametrize('run_class,backend_instance,n_workers', [ - (Parallelizable, map, 1), - (SerialOnly, list, 1), - (ParallelizableWithDaskOnly, object, 1), -]) -def test_fails_incorrect_custom_backend(u, run_class, backend_instance, n_workers): + _ = run_class(u.trajectory).run( + backend=backend_instance, n_workers=n_workers, unsupported_backend=True + ) + + +@pytest.mark.parametrize( + "run_class,backend_instance,n_workers", + [ + (Parallelizable, map, 1), + (SerialOnly, list, 1), + (ParallelizableWithDaskOnly, object, 1), + ], +) +def test_fails_incorrect_custom_backend( + u, run_class, backend_instance, n_workers +): u = mda.Universe(TPR, XTC) # dt = 100 with pytest.raises(ValueError): - _ = run_class(u.trajectory).run(backend=backend_instance, n_workers=n_workers, unsupported_backend=True) + _ = run_class(u.trajectory).run( + backend=backend_instance, + n_workers=n_workers, + unsupported_backend=True, + ) with pytest.raises(ValueError): - _ = run_class(u.trajectory).run(backend=backend_instance, n_workers=n_workers) + _ = run_class(u.trajectory).run( + backend=backend_instance, n_workers=n_workers + ) + -@pytest.mark.parametrize('run_class,backend,n_workers', [ - (SerialOnly, CustomSerialBackend, 1), - (SerialOnly, 'multiprocessing', 1), - (SerialOnly, 'dask', 1), -]) +@pytest.mark.parametrize( + "run_class,backend,n_workers", + [ + (SerialOnly, CustomSerialBackend, 1), + (SerialOnly, "multiprocessing", 1), + (SerialOnly, "dask", 1), + ], +) def test_fails_for_unparallelizable(u, run_class, backend, n_workers): u = mda.Universe(TPR, XTC) # dt = 100 with pytest.raises(ValueError): if not isinstance(backend, str): backend_instance = backend(n_workers=n_workers) - _ = run_class(u.trajectory).run(backend=backend_instance, n_workers=n_workers, unsupported_backend=True) + _ = run_class(u.trajectory).run( + backend=backend_instance, + n_workers=n_workers, + unsupported_backend=True, + ) else: - _ = run_class(u.trajectory).run(backend=backend, n_workers=n_workers, unsupported_backend=True) - -@pytest.mark.parametrize('run_kwargs,frames', [ - ({}, np.arange(98)), - ({'start': 20}, np.arange(20, 98)), - ({'stop': 30}, np.arange(30)), - ({'step': 10}, np.arange(0, 98, 10)) -]) + _ = run_class(u.trajectory).run( + backend=backend, n_workers=n_workers, unsupported_backend=True + ) + + +@pytest.mark.parametrize( + "run_kwargs,frames", + [ + ({}, np.arange(98)), + ({"start": 20}, np.arange(20, 98)), + ({"stop": 30}, np.arange(30)), + ({"step": 10}, np.arange(0, 98, 10)), + ], +) def test_start_stop_step_parallel(u, run_kwargs, frames, client_FrameAnalysis): # client_FrameAnalysis is defined [here](testsuite/MDAnalysisTests/analysis/conftest.py), # and determines a set of parameters ('backend', 'n_workers'), taking only backends @@ -219,7 +284,7 @@ def test_start_stop_step_parallel(u, run_kwargs, frames, client_FrameAnalysis): assert an.n_frames == len(frames) assert_equal(an.found_frames, frames) assert_equal(an.frames, frames, err_msg=FRAMES_ERR) - assert_almost_equal(an.times, frames+1, decimal=4, err_msg=TIMES_ERR) + assert_almost_equal(an.times, frames + 1, decimal=4, err_msg=TIMES_ERR) def test_reset_n_parts_to_n_frames(u): @@ -228,36 +293,57 @@ def test_reset_n_parts_to_n_frames(u): https://github.com/MDAnalysis/mdanalysis/issues/4685 """ a = FrameAnalysis(u.trajectory) - with pytest.warns(UserWarning, match='Set `n_parts` to'): - a.run(backend='multiprocessing', - start=0, - stop=1, - n_workers=2, - n_parts=2) - - -@pytest.mark.parametrize('run_kwargs,frames', [ - ({}, np.arange(98)), - ({'start': 20}, np.arange(20, 98)), - ({'stop': 30}, np.arange(30)), - ({'step': 10}, np.arange(0, 98, 10)) -]) + with pytest.warns(UserWarning, match="Set `n_parts` to"): + a.run( + backend="multiprocessing", start=0, stop=1, n_workers=2, n_parts=2 + ) + + +@pytest.mark.parametrize( + "run_kwargs,frames", + [ + ({}, np.arange(98)), + ({"start": 20}, np.arange(20, 98)), + ({"stop": 30}, np.arange(30)), + ({"step": 10}, np.arange(0, 98, 10)), + ], +) def test_start_stop_step(u, run_kwargs, frames): an = FrameAnalysis(u.trajectory).run(**run_kwargs) assert an.n_frames == len(frames) assert_equal(an.found_frames, frames) assert_equal(an.frames, frames, err_msg=FRAMES_ERR) - assert_allclose(an.times, frames+1, rtol=0, atol=1.5e-4, err_msg=TIMES_ERR) + assert_allclose( + an.times, frames + 1, rtol=0, atol=1.5e-4, err_msg=TIMES_ERR + ) -@pytest.mark.parametrize('run_kwargs, frames', [ - ({'frames': [4, 5, 6, 7, 8, 9]}, np.arange(4, 10)), - ({'frames': [0, 2, 4, 6, 8]}, np.arange(0, 10, 2)), - ({'frames': [4, 6, 8]}, np.arange(4, 10, 2)), - ({'frames': [0, 3, 4, 3, 5]}, [0, 3, 4, 3, 5]), - ({'frames': [True, True, False, True, False, True, True, False, True, - False]}, (0, 1, 3, 5, 6, 8)), -]) +@pytest.mark.parametrize( + "run_kwargs, frames", + [ + ({"frames": [4, 5, 6, 7, 8, 9]}, np.arange(4, 10)), + ({"frames": [0, 2, 4, 6, 8]}, np.arange(0, 10, 2)), + ({"frames": [4, 6, 8]}, np.arange(4, 10, 2)), + ({"frames": [0, 3, 4, 3, 5]}, [0, 3, 4, 3, 5]), + ( + { + "frames": [ + True, + True, + False, + True, + False, + True, + True, + False, + True, + False, + ] + }, + (0, 1, 3, 5, 6, 8), + ), + ], +) def test_frame_slice(u_xtc, run_kwargs, frames): an = FrameAnalysis(u_xtc.trajectory).run(**run_kwargs) assert an.n_frames == len(frames) @@ -265,14 +351,32 @@ def test_frame_slice(u_xtc, run_kwargs, frames): assert_equal(an.frames, frames, err_msg=FRAMES_ERR) -@pytest.mark.parametrize('run_kwargs, frames', [ - ({'frames': [4, 5, 6, 7, 8, 9]}, np.arange(4, 10)), - ({'frames': [0, 2, 4, 6, 8]}, np.arange(0, 10, 2)), - ({'frames': [4, 6, 8]}, np.arange(4, 10, 2)), - ({'frames': [0, 3, 4, 3, 5]}, [0, 3, 4, 3, 5]), - ({'frames': [True, True, False, True, False, True, True, False, True, - False]}, (0, 1, 3, 5, 6, 8)), -]) +@pytest.mark.parametrize( + "run_kwargs, frames", + [ + ({"frames": [4, 5, 6, 7, 8, 9]}, np.arange(4, 10)), + ({"frames": [0, 2, 4, 6, 8]}, np.arange(0, 10, 2)), + ({"frames": [4, 6, 8]}, np.arange(4, 10, 2)), + ({"frames": [0, 3, 4, 3, 5]}, [0, 3, 4, 3, 5]), + ( + { + "frames": [ + True, + True, + False, + True, + False, + True, + True, + False, + True, + False, + ] + }, + (0, 1, 3, 5, 6, 8), + ), + ], +) def test_frame_slice_parallel(run_kwargs, frames, client_FrameAnalysis): u = mda.Universe(TPR, XTC) # dt = 100 an = FrameAnalysis(u.trajectory).run(**run_kwargs, **client_FrameAnalysis) @@ -281,25 +385,30 @@ def test_frame_slice_parallel(run_kwargs, frames, client_FrameAnalysis): assert_equal(an.frames, frames, err_msg=FRAMES_ERR) -@pytest.mark.parametrize('run_kwargs', [ - ({'start': 4, 'frames': [4, 5, 6, 7, 8, 9]}), - ({'stop': 6, 'frames': [0, 1, 2, 3, 4, 5]}), - ({'step': 2, 'frames': [0, 2, 4, 6, 8]}), - ({'start': 4, 'stop': 7, 'frames': [4, 5, 6]}), - ({'stop': 6, 'step': 2, 'frames': [0, 2, 4, 6]}), - ({'start': 4, 'step': 2, 'frames': [4, 6, 8]}), - ({'start': 0, 'stop': 0, 'step': 0, 'frames': [4, 6, 8]}), -]) +@pytest.mark.parametrize( + "run_kwargs", + [ + ({"start": 4, "frames": [4, 5, 6, 7, 8, 9]}), + ({"stop": 6, "frames": [0, 1, 2, 3, 4, 5]}), + ({"step": 2, "frames": [0, 2, 4, 6, 8]}), + ({"start": 4, "stop": 7, "frames": [4, 5, 6]}), + ({"stop": 6, "step": 2, "frames": [0, 2, 4, 6]}), + ({"start": 4, "step": 2, "frames": [4, 6, 8]}), + ({"start": 0, "stop": 0, "step": 0, "frames": [4, 6, 8]}), + ], +) def test_frame_fail(u, run_kwargs, client_FrameAnalysis): an = FrameAnalysis(u.trajectory) - msg = 'start/stop/step cannot be combined with frames' + msg = "start/stop/step cannot be combined with frames" with pytest.raises(ValueError, match=msg): an.run(**client_FrameAnalysis, **run_kwargs) + def test_parallelizable_transformations(): - # pick any transformation that would allow + # pick any transformation that would allow # for parallelizable attribute - from MDAnalysis.transformations import NoJump + from MDAnalysis.transformations import NoJump + u = mda.Universe(XTC, to_guess=()) u.trajectory.add_transformations(NoJump()) @@ -308,18 +417,18 @@ def test_parallelizable_transformations(): # test that parallel fails with pytest.raises(ValueError): - FrameAnalysis(u.trajectory).run(backend='multiprocessing') + FrameAnalysis(u.trajectory).run(backend="multiprocessing") def test_instance_serial_backend(u): # test that isinstance is checked and the correct ValueError raise appears - msg = 'Can not display progressbar with non-serial backend' + msg = "Can not display progressbar with non-serial backend" with pytest.raises(ValueError, match=msg): FrameAnalysis(u.trajectory).run( backend=backends.BackendMultiprocessing(n_workers=2), verbose=True, progressbar_kwargs={"leave": True}, - unsupported_backend=True + unsupported_backend=True, ) @@ -327,25 +436,31 @@ def test_frame_bool_fail(client_FrameAnalysis): u = mda.Universe(TPR, XTC) # dt = 100 an = FrameAnalysis(u.trajectory) frames = [True, True, False] - msg = 'boolean index did not match indexed array along (axis|dimension) 0' + msg = "boolean index did not match indexed array along (axis|dimension) 0" with pytest.raises(IndexError, match=msg): an.run(**client_FrameAnalysis, frames=frames) def test_rewind(client_FrameAnalysis): u = mda.Universe(TPR, XTC) # dt = 100 - an = FrameAnalysis(u.trajectory).run(**client_FrameAnalysis, frames=[0, 2, 3, 5, 9]) + an = FrameAnalysis(u.trajectory).run( + **client_FrameAnalysis, frames=[0, 2, 3, 5, 9] + ) assert_equal(u.trajectory.ts.frame, 0) def test_frames_times(client_FrameAnalysis): u = mda.Universe(TPR, XTC) # dt = 100 - an = FrameAnalysis(u.trajectory).run(start=1, stop=8, step=2, **client_FrameAnalysis) + an = FrameAnalysis(u.trajectory).run( + start=1, stop=8, step=2, **client_FrameAnalysis + ) frames = np.array([1, 3, 5, 7]) assert an.n_frames == len(frames) assert_equal(an.found_frames, frames) assert_equal(an.frames, frames, err_msg=FRAMES_ERR) - assert_allclose(an.times, frames*100, rtol=0, atol=1.5e-4, err_msg=TIMES_ERR) + assert_allclose( + an.times, frames * 100, rtol=0, atol=1.5e-4, err_msg=TIMES_ERR + ) def test_verbose(u): @@ -356,7 +471,7 @@ def test_verbose(u): def test_warn_nparts_nworkers(u): a = FrameAnalysis(u.trajectory) with pytest.warns(UserWarning): - a.run(backend='multiprocessing', n_workers=3, n_parts=2) + a.run(backend="multiprocessing", n_workers=3, n_parts=2) @pytest.mark.parametrize( @@ -364,8 +479,8 @@ def test_warn_nparts_nworkers(u): [ (base.AnalysisBase, False), (base.AnalysisFromFunction, True), - (FrameAnalysis, True) - ] + (FrameAnalysis, True), + ], ) def test_not_parallelizable(u, classname, is_parallelizable): assert classname._analysis_algorithm_is_parallelizable == is_parallelizable @@ -374,30 +489,34 @@ def test_not_parallelizable(u, classname, is_parallelizable): def test_verbose_progressbar(u, capsys): FrameAnalysis(u.trajectory).run() _, err = capsys.readouterr() - expected = '' - actual = err.strip().split('\r')[-1] + expected = "" + actual = err.strip().split("\r")[-1] assert actual == expected def test_verbose_progressbar_run(u, capsys): FrameAnalysis(u.trajectory).run(verbose=True) _, err = capsys.readouterr() - expected = u'100%|██████████' - actual = err.strip().split('\r')[-1] + expected = "100%|██████████" + actual = err.strip().split("\r")[-1] assert actual[:15] == expected + def test_verbose_progressbar_run_with_kwargs(u, capsys): FrameAnalysis(u.trajectory).run( - verbose=True, progressbar_kwargs={'desc': 'custom'}) + verbose=True, progressbar_kwargs={"desc": "custom"} + ) _, err = capsys.readouterr() - expected = u'custom: 100%|██████████' - actual = err.strip().split('\r')[-1] + expected = "custom: 100%|██████████" + actual = err.strip().split("\r")[-1] assert actual[:23] == expected def test_progressbar_multiprocessing(u): with pytest.raises(ValueError): - FrameAnalysis(u.trajectory).run(backend='multiprocessing', verbose=True) + FrameAnalysis(u.trajectory).run( + backend="multiprocessing", verbose=True + ) def test_incomplete_defined_analysis(u): @@ -413,7 +532,7 @@ def test_filter_baseanalysis_kwargs_VE(): def bad_f(mobile, verbose=2): pass - kwargs = {'step': 3, 'foo': None} + kwargs = {"step": 3, "foo": None} with pytest.raises(ValueError): base._filter_baseanalysis_kwargs(bad_f, kwargs) @@ -423,15 +542,15 @@ def test_filter_baseanalysis_kwargs(): def good_f(mobile, ref): pass - kwargs = {'step': 3, 'foo': None} + kwargs = {"step": 3, "foo": None} base_kwargs, kwargs = base._filter_baseanalysis_kwargs(good_f, kwargs) assert 2 == len(kwargs) - assert kwargs['foo'] == None + assert kwargs["foo"] == None assert len(base_kwargs) == 1 - assert base_kwargs['verbose'] is False + assert base_kwargs["verbose"] is False def simple_function(mobile): @@ -443,13 +562,18 @@ def test_results_type(u): assert type(an.results) == base.Results -@pytest.mark.parametrize('start, stop, step, nframes', [ - (None, None, 2, 49), - (None, 50, 2, 25), - (20, 50, 2, 15), - (20, 50, None, 30) -]) -def test_AnalysisFromFunction(u, start, stop, step, nframes, client_AnalysisFromFunction): +@pytest.mark.parametrize( + "start, stop, step, nframes", + [ + (None, None, 2, 49), + (None, 50, 2, 25), + (20, 50, 2, 15), + (20, 50, None, 30), + ], +) +def test_AnalysisFromFunction( + u, start, stop, step, nframes, client_AnalysisFromFunction +): # client_AnalysisFromFunction is defined [here](testsuite/MDAnalysisTests/analysis/conftest.py), # and determines a set of parameters ('backend', 'n_workers'), taking only backends # that are implemented for a given subclass, to run the test against. @@ -488,7 +612,7 @@ def mass_xyz(atomgroup1, atomgroup2, masses): def test_AnalysisFromFunction_args_content(u, client_AnalysisFromFunction): - protein = u.select_atoms('protein') + protein = u.select_atoms("protein") masses = protein.masses.reshape(-1, 1) another = mda.Universe(TPR, XTC).select_atoms("protein") ans = base.AnalysisFromFunction(mass_xyz, protein, another, masses) @@ -507,7 +631,9 @@ def test_analysis_class(client_AnalysisFromFunctionAnalysisClass): u = mda.Universe(PSF, DCD) step = 2 - ana = ana_class(u.atoms).run(step=step, **client_AnalysisFromFunctionAnalysisClass) + ana = ana_class(u.atoms).run( + step=step, **client_AnalysisFromFunctionAnalysisClass + ) results = [] for ts in u.trajectory[::step]: diff --git a/testsuite/MDAnalysisTests/analysis/test_bat.py b/testsuite/MDAnalysisTests/analysis/test_bat.py index f6bf24a56a8..704cf616cb7 100644 --- a/testsuite/MDAnalysisTests/analysis/test_bat.py +++ b/testsuite/MDAnalysisTests/analysis/test_bat.py @@ -28,8 +28,15 @@ import copy import MDAnalysis as mda -from MDAnalysisTests.datafiles import (PSF, DCD, mol2_comments_header, XYZ_mini, - BATArray, TPR, XTC) +from MDAnalysisTests.datafiles import ( + PSF, + DCD, + mol2_comments_header, + XYZ_mini, + BATArray, + TPR, + XTC, +) from MDAnalysis.analysis.bat import BAT @@ -48,7 +55,7 @@ def bat(self, selected_residues, client_BAT): @pytest.fixture def bat_npz(self, tmpdir, selected_residues, client_BAT): - filename = str(tmpdir / 'test_bat_IO.npy') + filename = str(tmpdir / "test_bat_IO.npy") R = BAT(selected_residues) R.run(**client_BAT) R.save(filename) @@ -56,13 +63,16 @@ def bat_npz(self, tmpdir, selected_residues, client_BAT): def test_bat_root_selection(self, selected_residues): R = BAT(selected_residues) - assert_equal(R._root.indices, [8, 2, 1], - err_msg="error: incorrect root atoms selected") + assert_equal( + R._root.indices, + [8, 2, 1], + err_msg="error: incorrect root atoms selected", + ) def test_bat_number_of_frames(self, bat): - assert_equal(len(bat), - 2, - err_msg="error: list is not length of trajectory") + assert_equal( + len(bat), 2, err_msg="error: list is not length of trajectory" + ) def test_bat_coordinates(self, bat): test_bat = np.load(BATArray) @@ -71,24 +81,35 @@ def test_bat_coordinates(self, bat): test_bat, rtol=0, atol=1.5e-5, - err_msg="error: BAT coordinates should match test values") + err_msg="error: BAT coordinates should match test values", + ) def test_bat_coordinates_single_frame(self, selected_residues, client_BAT): - bat = BAT(selected_residues).run(start=1, stop=2, **client_BAT).results.bat + bat = ( + BAT(selected_residues) + .run(start=1, stop=2, **client_BAT) + .results.bat + ) test_bat = [np.load(BATArray)[1]] assert_allclose( bat, test_bat, rtol=0, atol=1.5e-5, - err_msg="error: BAT coordinates should match test values") + err_msg="error: BAT coordinates should match test values", + ) def test_bat_reconstruction(self, selected_residues, bat): R = BAT(selected_residues) XYZ = R.Cartesian(bat[0]) - assert_allclose(XYZ, selected_residues.positions, rtol=0, atol=1.5e-5, - err_msg="error: Reconstructed Cartesian coordinates " + \ - "don't match original") + assert_allclose( + XYZ, + selected_residues.positions, + rtol=0, + atol=1.5e-5, + err_msg="error: Reconstructed Cartesian coordinates " + + "don't match original", + ) def test_bat_IO(self, bat_npz, selected_residues, bat): R2 = BAT(selected_residues, filename=bat_npz) @@ -98,7 +119,8 @@ def test_bat_IO(self, bat_npz, selected_residues, bat): test_bat, rtol=0, atol=1.5e-5, - err_msg="error: Loaded BAT coordinates should match test values") + err_msg="error: Loaded BAT coordinates should match test values", + ) def test_bat_nobonds(self): u = mda.Universe(XYZ_mini) @@ -107,28 +129,29 @@ def test_bat_nobonds(self): Z = BAT(u.atoms) def test_bat_bad_initial_atom(self, selected_residues): - errmsg = 'Initial atom is not a terminal atom' + errmsg = "Initial atom is not a terminal atom" with pytest.raises(ValueError, match=errmsg): - R = BAT(selected_residues, initial_atom = selected_residues[0]) + R = BAT(selected_residues, initial_atom=selected_residues[0]) def test_bat_disconnected_atom_group(self): u = mda.Universe(PSF, DCD) - selected_residues = u.select_atoms("resid 1-3") + \ - u.select_atoms("resid 5-7") - errmsg = 'Additional torsions not found.' + selected_residues = u.select_atoms("resid 1-3") + u.select_atoms( + "resid 5-7" + ) + errmsg = "Additional torsions not found." with pytest.raises(ValueError, match=errmsg): R = BAT(selected_residues) def test_bat_multifragments_atomgroup(self): u = mda.Universe(TPR, XTC) - errmsg = 'AtomGroup has more than one molecule' + errmsg = "AtomGroup has more than one molecule" with pytest.raises(ValueError, match=errmsg): - BAT(u.select_atoms('resname SOL')) + BAT(u.select_atoms("resname SOL")) def test_bat_incorrect_dims(self, bat_npz): u = mda.Universe(PSF, DCD) selected_residues = u.select_atoms("resid 1-3") - errmsg = 'Dimensions of array in loaded file' + errmsg = "Dimensions of array in loaded file" with pytest.raises(ValueError, match=errmsg): R = BAT(selected_residues, filename=bat_npz) @@ -137,6 +160,9 @@ def test_Cartesian_does_not_modify_input(self, selected_residues, bat): pre_transformation = copy.deepcopy(bat[0]) R.Cartesian(bat[0]) assert_allclose( - pre_transformation, bat[0], rtol=0, atol=1.5e-7, - err_msg="BAT.Cartesian modified input data" + pre_transformation, + bat[0], + rtol=0, + atol=1.5e-7, + err_msg="BAT.Cartesian modified input data", ) diff --git a/testsuite/MDAnalysisTests/analysis/test_contacts.py b/testsuite/MDAnalysisTests/analysis/test_contacts.py index 6b416e27f8e..dd0c4ab00c8 100644 --- a/testsuite/MDAnalysisTests/analysis/test_contacts.py +++ b/testsuite/MDAnalysisTests/analysis/test_contacts.py @@ -40,13 +40,13 @@ XTC, contacts_villin_folded, contacts_villin_unfolded, - contacts_file + contacts_file, ) def test_soft_cut_q(): # just check some of the extremal points - assert contacts.soft_cut_q([0], [0]) == .5 + assert contacts.soft_cut_q([0], [0]) == 0.5 assert_allclose(contacts.soft_cut_q([100], [0]), 0, rtol=0, atol=1.5e-7) assert_allclose(contacts.soft_cut_q([-100], [0]), 1, rtol=0, atol=1.5e-7) @@ -58,8 +58,10 @@ def test_soft_cut_q_folded(): # indices have been stored 1 indexed indices = contacts_data[:, :2].astype(int) - 1 - r = np.linalg.norm(u.atoms.positions[indices[:, 0]] - - u.atoms.positions[indices[:, 1]], axis=1) + r = np.linalg.norm( + u.atoms.positions[indices[:, 0]] - u.atoms.positions[indices[:, 1]], + axis=1, + ) r0 = contacts_data[:, 2] beta = 5.0 @@ -76,8 +78,10 @@ def test_soft_cut_q_unfolded(): # indices have been stored 1 indexed indices = contacts_data[:, :2].astype(int) - 1 - r = np.linalg.norm(u.atoms.positions[indices[:, 0]] - - u.atoms.positions[indices[:, 1]], axis=1) + r = np.linalg.norm( + u.atoms.positions[indices[:, 0]] - u.atoms.positions[indices[:, 1]], + axis=1, + ) r0 = contacts_data[:, 2] beta = 5.0 @@ -87,25 +91,25 @@ def test_soft_cut_q_unfolded(): assert_allclose(Q.mean(), 0.0, rtol=0, atol=1.5e-1) -@pytest.mark.parametrize('r, cutoff, expected_value', [ - ([1], 2, 1), - ([2], 1, 0), - ([2, 0.5], 1, 0.5), - ([2, 3], [3, 4], 1), - ([4, 5], [3, 4], 0) - -]) +@pytest.mark.parametrize( + "r, cutoff, expected_value", + [ + ([1], 2, 1), + ([2], 1, 0), + ([2, 0.5], 1, 0.5), + ([2, 3], [3, 4], 1), + ([4, 5], [3, 4], 0), + ], +) def test_hard_cut_q(r, cutoff, expected_value): # just check some extremal points assert contacts.hard_cut_q(r, cutoff) == expected_value -@pytest.mark.parametrize('r, r0, radius, expected_value', [ - ([1], None, 2, 1), - ([2], None, 1, 0), - ([2, 0.5], None, 1, 0.5) - -]) +@pytest.mark.parametrize( + "r, r0, radius, expected_value", + [([1], None, 2, 1), ([2], None, 1, 0), ([2, 0.5], None, 1, 0.5)], +) def test_radius_cut_q(r, r0, radius, expected_value): # check some extremal points assert contacts.radius_cut_q(r, r0, radius) == expected_value @@ -126,7 +130,7 @@ def test_contact_matrix(): def test_new_selection(): u = mda.Universe(PSF, DCD) - selections = ('all', ) + selections = ("all",) sel = contacts._new_selections(u, selections, -1)[0] u.trajectory[-1] assert_array_equal(sel.positions, u.atoms.positions) @@ -134,7 +138,7 @@ def test_new_selection(): def soft_cut(ref, u, selA, selB, radius=4.5, beta=5.0, lambda_constant=1.8): """ - Reference implementation for testing + Reference implementation for testing """ # reference groups A and B from selection strings refA, refB = ref.select_atoms(selA), ref.select_atoms(selB) @@ -171,8 +175,13 @@ def universe(): return mda.Universe(PSF, DCD) def _run_Contacts( - self, universe, client_Contacts, start=None, - stop=None, step=None, **kwargs + self, + universe, + client_Contacts, + start=None, + stop=None, + step=None, + **kwargs, ): acidic = universe.select_atoms(self.sel_acidic) basic = universe.select_atoms(self.sel_basic) @@ -181,13 +190,13 @@ def _run_Contacts( select=(self.sel_acidic, self.sel_basic), refgroup=(acidic, basic), radius=6.0, - **kwargs + **kwargs, ).run(**client_Contacts, start=start, stop=stop, step=step) @pytest.mark.parametrize("seltxt", [sel_acidic, sel_basic]) def test_select_valid_types(self, universe, seltxt): """Test if Contacts._get_atomgroup() can take both string and AtomGroup - as selections. + as selections. """ ag = universe.select_atoms(seltxt) @@ -197,8 +206,7 @@ def test_select_valid_types(self, universe, seltxt): assert ag_from_string == ag_from_ag def test_contacts_selections(self, universe, client_Contacts): - """Test if Contacts can take both string and AtomGroup as selections. - """ + """Test if Contacts can take both string and AtomGroup as selections.""" aga = universe.select_atoms(self.sel_acidic) agb = universe.select_atoms(self.sel_basic) @@ -207,8 +215,9 @@ def test_contacts_selections(self, universe, client_Contacts): ) csel = contacts.Contacts( - universe, select=(self.sel_acidic, self.sel_basic), - refgroup=(aga, agb) + universe, + select=(self.sel_acidic, self.sel_basic), + refgroup=(aga, agb), ) cag.run(**client_Contacts) @@ -247,8 +256,11 @@ def test_end_zero(self, universe, client_Contacts): def test_slicing(self, universe, client_Contacts): start, stop, step = 10, 30, 5 CA1 = self._run_Contacts( - universe, client_Contacts=client_Contacts, - start=start, stop=stop, step=step + universe, + client_Contacts=client_Contacts, + start=start, + stop=stop, + step=step, ) frames = np.arange(universe.trajectory.n_frames)[start:stop:step] assert len(CA1.results.timeseries) == len(frames) @@ -261,14 +273,15 @@ def test_villin_folded(self, client_Contacts): grF = f.select_atoms(sel) - q = contacts.Contacts(u, - select=(sel, sel), - refgroup=(grF, grF), - method="soft_cut") + q = contacts.Contacts( + u, select=(sel, sel), refgroup=(grF, grF), method="soft_cut" + ) q.run(**client_Contacts) results = soft_cut(f, u, sel, sel) - assert_allclose(q.results.timeseries[:, 1], results[:, 1], rtol=0, atol=1.5e-7) + assert_allclose( + q.results.timeseries[:, 1], results[:, 1], rtol=0, atol=1.5e-7 + ) def test_villin_unfolded(self, client_Contacts): # both folded @@ -278,39 +291,122 @@ def test_villin_unfolded(self, client_Contacts): grF = f.select_atoms(sel) - q = contacts.Contacts(u, - select=(sel, sel), - refgroup=(grF, grF), - method="soft_cut") + q = contacts.Contacts( + u, select=(sel, sel), refgroup=(grF, grF), method="soft_cut" + ) q.run(**client_Contacts) results = soft_cut(f, u, sel, sel) - assert_allclose(q.results.timeseries[:, 1], results[:, 1], rtol=0, atol=1.5e-7) + assert_allclose( + q.results.timeseries[:, 1], results[:, 1], rtol=0, atol=1.5e-7 + ) def test_hard_cut_method(self, universe, client_Contacts): ca = self._run_Contacts(universe, client_Contacts=client_Contacts) - expected = [1., 0.58252427, 0.52427184, 0.55339806, 0.54368932, - 0.54368932, 0.51456311, 0.46601942, 0.48543689, 0.52427184, - 0.46601942, 0.58252427, 0.51456311, 0.48543689, 0.48543689, - 0.48543689, 0.46601942, 0.51456311, 0.49514563, 0.49514563, - 0.45631068, 0.47572816, 0.49514563, 0.50485437, 0.53398058, - 0.50485437, 0.51456311, 0.51456311, 0.49514563, 0.49514563, - 0.54368932, 0.50485437, 0.48543689, 0.55339806, 0.45631068, - 0.46601942, 0.53398058, 0.53398058, 0.46601942, 0.52427184, - 0.45631068, 0.46601942, 0.47572816, 0.46601942, 0.45631068, - 0.47572816, 0.45631068, 0.48543689, 0.4368932, 0.4368932, - 0.45631068, 0.50485437, 0.41747573, 0.4368932, 0.51456311, - 0.47572816, 0.46601942, 0.46601942, 0.47572816, 0.47572816, - 0.46601942, 0.45631068, 0.44660194, 0.47572816, 0.48543689, - 0.47572816, 0.42718447, 0.40776699, 0.37864078, 0.42718447, - 0.45631068, 0.4368932, 0.4368932, 0.45631068, 0.4368932, - 0.46601942, 0.45631068, 0.48543689, 0.44660194, 0.44660194, - 0.44660194, 0.42718447, 0.45631068, 0.44660194, 0.48543689, - 0.48543689, 0.44660194, 0.4368932, 0.40776699, 0.41747573, - 0.48543689, 0.45631068, 0.46601942, 0.47572816, 0.51456311, - 0.45631068, 0.37864078, 0.42718447] + expected = [ + 1.0, + 0.58252427, + 0.52427184, + 0.55339806, + 0.54368932, + 0.54368932, + 0.51456311, + 0.46601942, + 0.48543689, + 0.52427184, + 0.46601942, + 0.58252427, + 0.51456311, + 0.48543689, + 0.48543689, + 0.48543689, + 0.46601942, + 0.51456311, + 0.49514563, + 0.49514563, + 0.45631068, + 0.47572816, + 0.49514563, + 0.50485437, + 0.53398058, + 0.50485437, + 0.51456311, + 0.51456311, + 0.49514563, + 0.49514563, + 0.54368932, + 0.50485437, + 0.48543689, + 0.55339806, + 0.45631068, + 0.46601942, + 0.53398058, + 0.53398058, + 0.46601942, + 0.52427184, + 0.45631068, + 0.46601942, + 0.47572816, + 0.46601942, + 0.45631068, + 0.47572816, + 0.45631068, + 0.48543689, + 0.4368932, + 0.4368932, + 0.45631068, + 0.50485437, + 0.41747573, + 0.4368932, + 0.51456311, + 0.47572816, + 0.46601942, + 0.46601942, + 0.47572816, + 0.47572816, + 0.46601942, + 0.45631068, + 0.44660194, + 0.47572816, + 0.48543689, + 0.47572816, + 0.42718447, + 0.40776699, + 0.37864078, + 0.42718447, + 0.45631068, + 0.4368932, + 0.4368932, + 0.45631068, + 0.4368932, + 0.46601942, + 0.45631068, + 0.48543689, + 0.44660194, + 0.44660194, + 0.44660194, + 0.42718447, + 0.45631068, + 0.44660194, + 0.48543689, + 0.48543689, + 0.44660194, + 0.4368932, + 0.40776699, + 0.41747573, + 0.48543689, + 0.45631068, + 0.46601942, + 0.47572816, + 0.51456311, + 0.45631068, + 0.37864078, + 0.42718447, + ] assert len(ca.results.timeseries) == len(expected) - assert_allclose(ca.results.timeseries[:, 1], expected, rtol=0, atol=1.5e-7) + assert_allclose( + ca.results.timeseries[:, 1], expected, rtol=0, atol=1.5e-7 + ) def test_radius_cut_method(self, universe, client_Contacts): acidic = universe.select_atoms(self.sel_acidic) @@ -320,7 +416,9 @@ def test_radius_cut_method(self, universe, client_Contacts): expected = [] for ts in universe.trajectory: r = contacts.distance_array(acidic.positions, basic.positions) - expected.append(contacts.radius_cut_q(r[initial_contacts], None, radius=6.0)) + expected.append( + contacts.radius_cut_q(r[initial_contacts], None, radius=6.0) + ) ca = self._run_Contacts( universe, client_Contacts=client_Contacts, method="radius_cut" @@ -333,23 +431,116 @@ def _is_any_closer(r, r0, dist=2.5): def test_own_method(self, universe, client_Contacts): ca = self._run_Contacts( - universe, client_Contacts=client_Contacts, - method=self._is_any_closer + universe, + client_Contacts=client_Contacts, + method=self._is_any_closer, ) - bound_expected = [1., 1., 0., 1., 1., 0., 0., 1., 0., 1., 1., 0., 0., - 1., 0., 0., 0., 0., 1., 1., 0., 0., 0., 1., 0., 1., - 0., 1., 1., 0., 1., 1., 1., 0., 0., 0., 0., 1., 0., - 0., 1., 0., 1., 1., 1., 0., 1., 0., 0., 1., 1., 1., - 0., 1., 0., 1., 1., 0., 0., 0., 1., 1., 1., 0., 0., - 1., 0., 1., 1., 1., 1., 1., 1., 0., 1., 1., 0., 1., - 0., 0., 1., 1., 0., 0., 1., 1., 1., 0., 1., 0., 0., - 1., 0., 1., 1., 1., 1., 1.] + bound_expected = [ + 1.0, + 1.0, + 0.0, + 1.0, + 1.0, + 0.0, + 0.0, + 1.0, + 0.0, + 1.0, + 1.0, + 0.0, + 0.0, + 1.0, + 0.0, + 0.0, + 0.0, + 0.0, + 1.0, + 1.0, + 0.0, + 0.0, + 0.0, + 1.0, + 0.0, + 1.0, + 0.0, + 1.0, + 1.0, + 0.0, + 1.0, + 1.0, + 1.0, + 0.0, + 0.0, + 0.0, + 0.0, + 1.0, + 0.0, + 0.0, + 1.0, + 0.0, + 1.0, + 1.0, + 1.0, + 0.0, + 1.0, + 0.0, + 0.0, + 1.0, + 1.0, + 1.0, + 0.0, + 1.0, + 0.0, + 1.0, + 1.0, + 0.0, + 0.0, + 0.0, + 1.0, + 1.0, + 1.0, + 0.0, + 0.0, + 1.0, + 0.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 0.0, + 1.0, + 1.0, + 0.0, + 1.0, + 0.0, + 0.0, + 1.0, + 1.0, + 0.0, + 0.0, + 1.0, + 1.0, + 1.0, + 0.0, + 1.0, + 0.0, + 0.0, + 1.0, + 0.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + ] assert_array_equal(ca.results.timeseries[:, 1], bound_expected) @staticmethod def _weird_own_method(r, r0): - return 'aaa' + return "aaa" def test_own_method_no_array_cast(self, universe, client_Contacts): with pytest.raises(ValueError): @@ -366,23 +557,59 @@ def test_non_callable_method(self, universe, client_Contacts): universe, client_Contacts=client_Contacts, method=2, stop=2 ) - @pytest.mark.parametrize("pbc,expected", [ - (True, [1., 0.43138152, 0.3989021, 0.43824337, 0.41948765, - 0.42223239, 0.41354071, 0.43641354, 0.41216834, 0.38334858]), - (False, [1., 0.42327791, 0.39192399, 0.40950119, 0.40902613, - 0.42470309, 0.41140143, 0.42897862, 0.41472684, 0.38574822]) - ]) + @pytest.mark.parametrize( + "pbc,expected", + [ + ( + True, + [ + 1.0, + 0.43138152, + 0.3989021, + 0.43824337, + 0.41948765, + 0.42223239, + 0.41354071, + 0.43641354, + 0.41216834, + 0.38334858, + ], + ), + ( + False, + [ + 1.0, + 0.42327791, + 0.39192399, + 0.40950119, + 0.40902613, + 0.42470309, + 0.41140143, + 0.42897862, + 0.41472684, + 0.38574822, + ], + ), + ], + ) def test_distance_box(self, pbc, expected, client_Contacts): u = mda.Universe(TPR, XTC) sel_basic = "(resname ARG LYS)" sel_acidic = "(resname ASP GLU)" acidic = u.select_atoms(sel_acidic) basic = u.select_atoms(sel_basic) - - r = contacts.Contacts(u, select=(sel_acidic, sel_basic), - refgroup=(acidic, basic), radius=6.0, pbc=pbc) + + r = contacts.Contacts( + u, + select=(sel_acidic, sel_basic), + refgroup=(acidic, basic), + radius=6.0, + pbc=pbc, + ) r.run(**client_Contacts) - assert_allclose(r.results.timeseries[:, 1], expected,rtol=0, atol=1.5e-7) + assert_allclose( + r.results.timeseries[:, 1], expected, rtol=0, atol=1.5e-7 + ) def test_warn_deprecated_attr(self, universe, client_Contacts): """Test for warning message emitted on using deprecated `timeseries` @@ -394,13 +621,14 @@ def test_warn_deprecated_attr(self, universe, client_Contacts): with pytest.warns(DeprecationWarning, match=wmsg): assert_equal(CA1.timeseries, CA1.results.timeseries) - @pytest.mark.parametrize("datafiles, expected", [((PSF, DCD), 0), - ([TPR, XTC], 41814)]) + @pytest.mark.parametrize( + "datafiles, expected", [((PSF, DCD), 0), ([TPR, XTC], 41814)] + ) def test_n_initial_contacts(self, datafiles, expected): """Test for n_initial_contacts attribute""" u = mda.Universe(*datafiles) - select = ('protein', 'not protein') - refgroup = (u.select_atoms('protein'), u.select_atoms('not protein')) + select = ("protein", "not protein") + refgroup = (u.select_atoms("protein"), u.select_atoms("not protein")) r = contacts.Contacts(u, select=select, refgroup=refgroup) assert_equal(r.n_initial_contacts, expected) @@ -408,49 +636,213 @@ def test_n_initial_contacts(self, datafiles, expected): def test_q1q2(client_Contacts): u = mda.Universe(PSF, DCD) - q1q2 = contacts.q1q2(u, 'name CA', radius=8) + q1q2 = contacts.q1q2(u, "name CA", radius=8) q1q2.run(**client_Contacts) - q1_expected = [1., 0.98092643, 0.97366031, 0.97275204, 0.97002725, - 0.97275204, 0.96276113, 0.96730245, 0.9582198, 0.96185286, - 0.95367847, 0.96276113, 0.9582198, 0.95186194, 0.95367847, - 0.95095368, 0.94187103, 0.95186194, 0.94277929, 0.94187103, - 0.9373297, 0.93642144, 0.93097184, 0.93914623, 0.93278837, - 0.93188011, 0.9373297, 0.93097184, 0.93188011, 0.92643052, - 0.92824705, 0.92915531, 0.92643052, 0.92461399, 0.92279746, - 0.92643052, 0.93278837, 0.93188011, 0.93369664, 0.9346049, - 0.9373297, 0.94096276, 0.9400545, 0.93642144, 0.9373297, - 0.9373297, 0.9400545, 0.93006358, 0.9400545, 0.93823797, - 0.93914623, 0.93278837, 0.93097184, 0.93097184, 0.92733878, - 0.92824705, 0.92279746, 0.92824705, 0.91825613, 0.92733878, - 0.92643052, 0.92733878, 0.93278837, 0.92733878, 0.92824705, - 0.93097184, 0.93278837, 0.93914623, 0.93097184, 0.9373297, - 0.92915531, 0.93188011, 0.93551317, 0.94096276, 0.93642144, - 0.93642144, 0.9346049, 0.93369664, 0.93369664, 0.93278837, - 0.93006358, 0.93278837, 0.93006358, 0.9346049, 0.92824705, - 0.93097184, 0.93006358, 0.93188011, 0.93278837, 0.93006358, - 0.92915531, 0.92824705, 0.92733878, 0.92643052, 0.93188011, - 0.93006358, 0.9346049, 0.93188011] - assert_allclose(q1q2.results.timeseries[:, 1], q1_expected, rtol=0, atol=1.5e-7) - - q2_expected = [0.94649446, 0.94926199, 0.95295203, 0.95110701, 0.94833948, - 0.95479705, 0.94926199, 0.9501845, 0.94926199, 0.95387454, - 0.95202952, 0.95110701, 0.94649446, 0.94095941, 0.94649446, - 0.9400369, 0.94464945, 0.95202952, 0.94741697, 0.94649446, - 0.94188192, 0.94188192, 0.93911439, 0.94464945, 0.9400369, - 0.94095941, 0.94372694, 0.93726937, 0.93819188, 0.93357934, - 0.93726937, 0.93911439, 0.93911439, 0.93450185, 0.93357934, - 0.93265683, 0.93911439, 0.94372694, 0.93911439, 0.94649446, - 0.94833948, 0.95110701, 0.95110701, 0.95295203, 0.94926199, - 0.95110701, 0.94926199, 0.94741697, 0.95202952, 0.95202952, - 0.95202952, 0.94741697, 0.94741697, 0.94926199, 0.94280443, - 0.94741697, 0.94833948, 0.94833948, 0.9400369, 0.94649446, - 0.94741697, 0.94926199, 0.95295203, 0.94926199, 0.9501845, - 0.95664207, 0.95756458, 0.96309963, 0.95756458, 0.96217712, - 0.95756458, 0.96217712, 0.96586716, 0.96863469, 0.96494465, - 0.97232472, 0.97140221, 0.9695572, 0.97416974, 0.9695572, - 0.96217712, 0.96771218, 0.9704797, 0.96771218, 0.9695572, - 0.97140221, 0.97601476, 0.97693727, 0.98154982, 0.98431734, - 0.97601476, 0.9797048, 0.98154982, 0.98062731, 0.98431734, - 0.98616236, 0.9898524, 1.] - assert_allclose(q1q2.results.timeseries[:, 2], q2_expected, rtol=0, atol=1.5e-7) + q1_expected = [ + 1.0, + 0.98092643, + 0.97366031, + 0.97275204, + 0.97002725, + 0.97275204, + 0.96276113, + 0.96730245, + 0.9582198, + 0.96185286, + 0.95367847, + 0.96276113, + 0.9582198, + 0.95186194, + 0.95367847, + 0.95095368, + 0.94187103, + 0.95186194, + 0.94277929, + 0.94187103, + 0.9373297, + 0.93642144, + 0.93097184, + 0.93914623, + 0.93278837, + 0.93188011, + 0.9373297, + 0.93097184, + 0.93188011, + 0.92643052, + 0.92824705, + 0.92915531, + 0.92643052, + 0.92461399, + 0.92279746, + 0.92643052, + 0.93278837, + 0.93188011, + 0.93369664, + 0.9346049, + 0.9373297, + 0.94096276, + 0.9400545, + 0.93642144, + 0.9373297, + 0.9373297, + 0.9400545, + 0.93006358, + 0.9400545, + 0.93823797, + 0.93914623, + 0.93278837, + 0.93097184, + 0.93097184, + 0.92733878, + 0.92824705, + 0.92279746, + 0.92824705, + 0.91825613, + 0.92733878, + 0.92643052, + 0.92733878, + 0.93278837, + 0.92733878, + 0.92824705, + 0.93097184, + 0.93278837, + 0.93914623, + 0.93097184, + 0.9373297, + 0.92915531, + 0.93188011, + 0.93551317, + 0.94096276, + 0.93642144, + 0.93642144, + 0.9346049, + 0.93369664, + 0.93369664, + 0.93278837, + 0.93006358, + 0.93278837, + 0.93006358, + 0.9346049, + 0.92824705, + 0.93097184, + 0.93006358, + 0.93188011, + 0.93278837, + 0.93006358, + 0.92915531, + 0.92824705, + 0.92733878, + 0.92643052, + 0.93188011, + 0.93006358, + 0.9346049, + 0.93188011, + ] + assert_allclose( + q1q2.results.timeseries[:, 1], q1_expected, rtol=0, atol=1.5e-7 + ) + + q2_expected = [ + 0.94649446, + 0.94926199, + 0.95295203, + 0.95110701, + 0.94833948, + 0.95479705, + 0.94926199, + 0.9501845, + 0.94926199, + 0.95387454, + 0.95202952, + 0.95110701, + 0.94649446, + 0.94095941, + 0.94649446, + 0.9400369, + 0.94464945, + 0.95202952, + 0.94741697, + 0.94649446, + 0.94188192, + 0.94188192, + 0.93911439, + 0.94464945, + 0.9400369, + 0.94095941, + 0.94372694, + 0.93726937, + 0.93819188, + 0.93357934, + 0.93726937, + 0.93911439, + 0.93911439, + 0.93450185, + 0.93357934, + 0.93265683, + 0.93911439, + 0.94372694, + 0.93911439, + 0.94649446, + 0.94833948, + 0.95110701, + 0.95110701, + 0.95295203, + 0.94926199, + 0.95110701, + 0.94926199, + 0.94741697, + 0.95202952, + 0.95202952, + 0.95202952, + 0.94741697, + 0.94741697, + 0.94926199, + 0.94280443, + 0.94741697, + 0.94833948, + 0.94833948, + 0.9400369, + 0.94649446, + 0.94741697, + 0.94926199, + 0.95295203, + 0.94926199, + 0.9501845, + 0.95664207, + 0.95756458, + 0.96309963, + 0.95756458, + 0.96217712, + 0.95756458, + 0.96217712, + 0.96586716, + 0.96863469, + 0.96494465, + 0.97232472, + 0.97140221, + 0.9695572, + 0.97416974, + 0.9695572, + 0.96217712, + 0.96771218, + 0.9704797, + 0.96771218, + 0.9695572, + 0.97140221, + 0.97601476, + 0.97693727, + 0.98154982, + 0.98431734, + 0.97601476, + 0.9797048, + 0.98154982, + 0.98062731, + 0.98431734, + 0.98616236, + 0.9898524, + 1.0, + ] + assert_allclose( + q1q2.results.timeseries[:, 2], q2_expected, rtol=0, atol=1.5e-7 + ) diff --git a/testsuite/MDAnalysisTests/analysis/test_data.py b/testsuite/MDAnalysisTests/analysis/test_data.py index 44853346c85..d3d570d6c46 100644 --- a/testsuite/MDAnalysisTests/analysis/test_data.py +++ b/testsuite/MDAnalysisTests/analysis/test_data.py @@ -23,9 +23,15 @@ from numpy.testing import assert_equal import pytest + def test_all_exports(): from MDAnalysis.analysis.data import filenames - missing = [name for name in dir(filenames) - if - not name.startswith('_') and name not in filenames.__all__ and name != 'absolute_import'] + + missing = [ + name + for name in dir(filenames) + if not name.startswith("_") + and name not in filenames.__all__ + and name != "absolute_import" + ] assert_equal(missing, [], err_msg="Variables need to be added to __all__.") diff --git a/testsuite/MDAnalysisTests/analysis/test_density.py b/testsuite/MDAnalysisTests/analysis/test_density.py index 68dbed6839d..3547ff9fe91 100644 --- a/testsuite/MDAnalysisTests/analysis/test_density.py +++ b/testsuite/MDAnalysisTests/analysis/test_density.py @@ -41,32 +41,35 @@ class TestDensity(object): nbins = 3, 4, 5 counts = 100 - Lmax = 10. + Lmax = 10.0 - @pytest.fixture(scope='class') + @pytest.fixture(scope="class") def bins(self): return [np.linspace(0, self.Lmax, n + 1) for n in self.nbins] @pytest.fixture() def h_and_edges(self, bins): return np.histogramdd( - self.Lmax * np.sin( - np.linspace(0, 1, self.counts * 3)).reshape(self.counts, 3), - bins=bins) + self.Lmax + * np.sin(np.linspace(0, 1, self.counts * 3)).reshape( + self.counts, 3 + ), + bins=bins, + ) @pytest.fixture() def D(self, h_and_edges): h, edges = h_and_edges - d = density.Density(h, edges, parameters={'isDensity': False}, - units={'length': 'A'}) + d = density.Density( + h, edges, parameters={"isDensity": False}, units={"length": "A"} + ) d.make_density() return d @pytest.fixture() def D1(self, h_and_edges): h, edges = h_and_edges - d = density.Density(h, edges, parameters={}, - units={}) + d = density.Density(h, edges, parameters={}, units={}) return d def test_shape(self, D): @@ -74,17 +77,19 @@ def test_shape(self, D): def test_edges(self, bins, D): for dim, (edges, fixture) in enumerate(zip(D.edges, bins)): - assert_almost_equal(edges, fixture, - err_msg="edges[{0}] mismatch".format(dim)) + assert_almost_equal( + edges, fixture, err_msg="edges[{0}] mismatch".format(dim) + ) def test_midpoints(self, bins, D): - midpoints = [0.5*(b[:-1] + b[1:]) for b in bins] + midpoints = [0.5 * (b[:-1] + b[1:]) for b in bins] for dim, (mp, fixture) in enumerate(zip(D.midpoints, midpoints)): - assert_almost_equal(mp, fixture, - err_msg="midpoints[{0}] mismatch".format(dim)) + assert_almost_equal( + mp, fixture, err_msg="midpoints[{0}] mismatch".format(dim) + ) def test_delta(self, D): - deltas = np.array([self.Lmax])/np.array(self.nbins) + deltas = np.array([self.Lmax]) / np.array(self.nbins) assert_almost_equal(D.delta, deltas) def test_grid(self, D): @@ -93,12 +98,12 @@ def test_grid(self, D): assert_almost_equal(D.grid.sum() * dV, self.counts) def test_origin(self, bins, D): - midpoints = [0.5*(b[:-1] + b[1:]) for b in bins] + midpoints = [0.5 * (b[:-1] + b[1:]) for b in bins] origin = [m[0] for m in midpoints] assert_almost_equal(D.origin, origin) def test_check_set_unit_keyerror(self, D): - units = {'weight': 'A'} + units = {"weight": "A"} with pytest.raises(ValueError): D._check_set_unit(units) @@ -108,23 +113,23 @@ def test_check_set_unit_attributeError(self, D): D._check_set_unit(units) def test_check_set_unit_nolength(self, D): - del D.units['length'] - units = {'density': 'A^{-3}'} + del D.units["length"] + units = {"density": "A^{-3}"} with pytest.raises(ValueError): D._check_set_unit(units) def test_check_set_density_none(self, D1): - units = {'density': None} + units = {"density": None} D1._check_set_unit(units) - assert D1.units['density'] is None + assert D1.units["density"] is None def test_check_set_density_not_in_units(self, D1): - del D1.units['density'] + del D1.units["density"] D1._check_set_unit({}) - assert D1.units['density'] is None + assert D1.units["density"] is None def test_parameters_isdensity(self, D): - with pytest.warns(UserWarning, match='Running make_density()'): + with pytest.warns(UserWarning, match="Running make_density()"): D.make_density() def test_check_convert_density_grid_not_density(self, D1): @@ -132,65 +137,64 @@ def test_check_convert_density_grid_not_density(self, D1): D1.convert_density() def test_check_convert_density_value_error(self, D): - unit = 'A^{-2}' + unit = "A^{-2}" with pytest.raises(ValueError, match="The name of the unit"): D.convert_density(unit) def test_check_convert_density_units_same_density_units(self, D): - unit = 'A^{-3}' + unit = "A^{-3}" D_orig = copy.deepcopy(D) D.convert_density(unit) - assert D.units['density'] == D_orig.units['density'] == unit + assert D.units["density"] == D_orig.units["density"] == unit assert_almost_equal(D.grid, D_orig.grid) def test_check_convert_density_units_density(self, D): - unit = 'nm^{-3}' + unit = "nm^{-3}" D_orig = copy.deepcopy(D) D.convert_density(unit) - assert D.units['density'] == 'nm^{-3}' + assert D.units["density"] == "nm^{-3}" assert_almost_equal(D.grid, 10**3 * D_orig.grid) def test_convert_length_same_length_units(self, D): - unit = 'A' + unit = "A" D_orig = copy.deepcopy(D) D.convert_length(unit) - assert D.units['length'] == D_orig.units['length'] == unit + assert D.units["length"] == D_orig.units["length"] == unit assert_almost_equal(D.grid, D_orig.grid) def test_convert_length_other_length_units(self, D): - unit = 'nm' + unit = "nm" D_orig = copy.deepcopy(D) D.convert_length(unit) - assert D.units['length'] == unit + assert D.units["length"] == unit assert_almost_equal(D.grid, D_orig.grid) def test_repr(self, D, D1): - assert str(D) == '' - assert str(D1) == '' + assert str(D) == "" + assert str(D1) == "" def test_check_convert_length_edges(self, D): D1 = copy.deepcopy(D) - unit = 'nm' + unit = "nm" D.convert_length(unit) for prev_edge, conv_edge in zip(D1.edges, D.edges): - assert_almost_equal(prev_edge, 10*conv_edge) + assert_almost_equal(prev_edge, 10 * conv_edge) def test_check_convert_density_edges(self, D): - unit = 'nm^{-3}' + unit = "nm^{-3}" D_orig = copy.deepcopy(D) D.convert_density(unit) for new_den, orig_den in zip(D.edges, D_orig.edges): assert_almost_equal(new_den, orig_den) - @pytest.mark.parametrize('dxtype', - ("float", "double", "int", "byte")) + @pytest.mark.parametrize("dxtype", ("float", "double", "int", "byte")) def test_export_types(self, D, dxtype, tmpdir, outfile="density.dx"): with tmpdir.as_cwd(): D.export(outfile, type=dxtype) dx = gridData.OpenDX.field(0) dx.read(outfile) - data = dx.components['data'] + data = dx.components["data"] assert data.type == dxtype @@ -198,35 +202,48 @@ class DensityParameters(object): topology = TPR trajectory = XTC delta = 2.0 - selections = {'none': "resname None", - 'static': "name OW", - 'dynamic': "name OW and around 4 (protein and resid 1-10)", - 'solute': "protein and not name H*", - } - references = {'static': - {'meandensity': 0.016764271713091212, }, - 'static_sliced': - {'meandensity': 0.016764270747693617, }, - 'static_defined': - {'meandensity': 0.0025000000000000005, }, - 'static_defined_unequal': - {'meandensity': 0.006125, }, - 'dynamic': - {'meandensity': 0.0012063418843728784, }, - 'notwithin': - {'meandensity': 0.015535385132107926, }, - } - cutoffs = {'notwithin': 4.0, } - gridcenters = {'static_defined': np.array([56.0, 45.0, 35.0]), - 'error1': np.array([56.0, 45.0]), - 'error2': [56.0, 45.0, "MDAnalysis"], - } + selections = { + "none": "resname None", + "static": "name OW", + "dynamic": "name OW and around 4 (protein and resid 1-10)", + "solute": "protein and not name H*", + } + references = { + "static": { + "meandensity": 0.016764271713091212, + }, + "static_sliced": { + "meandensity": 0.016764270747693617, + }, + "static_defined": { + "meandensity": 0.0025000000000000005, + }, + "static_defined_unequal": { + "meandensity": 0.006125, + }, + "dynamic": { + "meandensity": 0.0012063418843728784, + }, + "notwithin": { + "meandensity": 0.015535385132107926, + }, + } + cutoffs = { + "notwithin": 4.0, + } + gridcenters = { + "static_defined": np.array([56.0, 45.0, 35.0]), + "error1": np.array([56.0, 45.0]), + "error2": [56.0, 45.0, "MDAnalysis"], + } precision = 5 - outfile = 'density.dx' + outfile = "density.dx" @pytest.fixture() def universe(self): - return mda.Universe(self.topology, self.trajectory, tpr_resid_from_one=False) + return mda.Universe( + self.topology, self.trajectory, tpr_resid_from_one=False + ) class TestDensityAnalysis(DensityParameters): @@ -237,37 +254,42 @@ def check_DensityAnalysis( tmpdir, client_DensityAnalysis, runargs=None, - **kwargs + **kwargs, ): runargs = runargs if runargs else {} with tmpdir.as_cwd(): D = density.DensityAnalysis(ag, delta=self.delta, **kwargs).run( **runargs, **client_DensityAnalysis ) - assert_almost_equal(D.results.density.grid.mean(), ref_meandensity, - err_msg="mean density does not match") + assert_almost_equal( + D.results.density.grid.mean(), + ref_meandensity, + err_msg="mean density does not match", + ) D.results.density.export(self.outfile) D2 = density.Density(self.outfile) assert_almost_equal( - D.results.density.grid, D2.grid, decimal=self.precision, - err_msg="DX export failed: different grid sizes" + D.results.density.grid, + D2.grid, + decimal=self.precision, + err_msg="DX export failed: different grid sizes", ) @pytest.mark.parametrize("mode", ("static", "dynamic")) def test_run(self, mode, universe, tmpdir, client_DensityAnalysis): - updating = (mode == "dynamic") + updating = mode == "dynamic" self.check_DensityAnalysis( universe.select_atoms(self.selections[mode], updating=updating), - self.references[mode]['meandensity'], + self.references[mode]["meandensity"], tmpdir=tmpdir, client_DensityAnalysis=client_DensityAnalysis, ) def test_sliced(self, universe, tmpdir, client_DensityAnalysis): self.check_DensityAnalysis( - universe.select_atoms(self.selections['static']), - self.references['static_sliced']['meandensity'], + universe.select_atoms(self.selections["static"]), + self.references["static_sliced"]["meandensity"], tmpdir=tmpdir, client_DensityAnalysis=client_DensityAnalysis, runargs=dict(start=1, stop=-1, step=2), @@ -278,11 +300,11 @@ def test_userdefn_eqbox(self, universe, tmpdir, client_DensityAnalysis): # Do not need to see UserWarning that box is too small warnings.simplefilter("ignore") self.check_DensityAnalysis( - universe.select_atoms(self.selections['static']), - self.references['static_defined']['meandensity'], + universe.select_atoms(self.selections["static"]), + self.references["static_defined"]["meandensity"], tmpdir=tmpdir, client_DensityAnalysis=client_DensityAnalysis, - gridcenter=self.gridcenters['static_defined'], + gridcenter=self.gridcenters["static_defined"], xdim=10.0, ydim=10.0, zdim=10.0, @@ -290,11 +312,11 @@ def test_userdefn_eqbox(self, universe, tmpdir, client_DensityAnalysis): def test_userdefn_neqbox(self, universe, tmpdir, client_DensityAnalysis): self.check_DensityAnalysis( - universe.select_atoms(self.selections['static']), - self.references['static_defined_unequal']['meandensity'], + universe.select_atoms(self.selections["static"]), + self.references["static_defined_unequal"]["meandensity"], tmpdir=tmpdir, client_DensityAnalysis=client_DensityAnalysis, - gridcenter=self.gridcenters['static_defined'], + gridcenter=self.gridcenters["static_defined"], xdim=10.0, ydim=15.0, zdim=20.0, @@ -312,8 +334,10 @@ def test_userdefn_boxshape(self, universe, client_DensityAnalysis): assert D.results.density.grid.shape == (8, 12, 17) def test_warn_userdefn_padding(self, universe, client_DensityAnalysis): - regex = (r"Box padding \(currently set at 1\.0\) is not used " - r"in user defined grids\.") + regex = ( + r"Box padding \(currently set at 1\.0\) is not used " + r"in user defined grids\." + ) with pytest.warns(UserWarning, match=regex): D = density.DensityAnalysis( universe.select_atoms(self.selections["static"]), @@ -326,8 +350,10 @@ def test_warn_userdefn_padding(self, universe, client_DensityAnalysis): ).run(step=5, **client_DensityAnalysis) def test_warn_userdefn_smallgrid(self, universe, client_DensityAnalysis): - regex = ("Atom selection does not fit grid --- " - "you may want to define a larger box") + regex = ( + "Atom selection does not fit grid --- " + "you may want to define a larger box" + ) with pytest.warns(UserWarning, match=regex): D = density.DensityAnalysis( universe.select_atoms(self.selections["static"]), @@ -343,7 +369,9 @@ def test_ValueError_userdefn_gridcenter_shape( self, universe, client_DensityAnalysis ): # Test len(gridcenter) != 3 - with pytest.raises(ValueError, match="Gridcenter must be a 3D coordinate"): + with pytest.raises( + ValueError, match="Gridcenter must be a 3D coordinate" + ): D = density.DensityAnalysis( universe.select_atoms(self.selections["static"]), delta=self.delta, @@ -357,7 +385,9 @@ def test_ValueError_userdefn_gridcenter_type( self, universe, client_DensityAnalysis ): # Test gridcenter includes non-numeric strings - with pytest.raises(ValueError, match="Gridcenter must be a 3D coordinate"): + with pytest.raises( + ValueError, match="Gridcenter must be a 3D coordinate" + ): D = density.DensityAnalysis( universe.select_atoms(self.selections["static"]), delta=self.delta, @@ -371,7 +401,7 @@ def test_ValueError_userdefn_gridcenter_missing( self, universe, client_DensityAnalysis ): # Test no gridcenter provided when grid dimensions are given - regex = ("Gridcenter or grid dimensions are not provided") + regex = "Gridcenter or grid dimensions are not provided" with pytest.raises(ValueError, match=regex): D = density.DensityAnalysis( universe.select_atoms(self.selections["static"]), @@ -381,10 +411,13 @@ def test_ValueError_userdefn_gridcenter_missing( zdim=10.0, ).run(step=5, **client_DensityAnalysis) - def test_ValueError_userdefn_xdim_type(self, universe, - client_DensityAnalysis): + def test_ValueError_userdefn_xdim_type( + self, universe, client_DensityAnalysis + ): # Test xdim != int or float - with pytest.raises(ValueError, match="xdim, ydim, and zdim must be numbers"): + with pytest.raises( + ValueError, match="xdim, ydim, and zdim must be numbers" + ): D = density.DensityAnalysis( universe.select_atoms(self.selections["static"]), delta=self.delta, @@ -394,10 +427,11 @@ def test_ValueError_userdefn_xdim_type(self, universe, gridcenter=self.gridcenters["static_defined"], ).run(step=5, **client_DensityAnalysis) - def test_ValueError_userdefn_xdim_nanvalue(self, universe, - client_DensityAnalysis): + def test_ValueError_userdefn_xdim_nanvalue( + self, universe, client_DensityAnalysis + ): # Test xdim set to NaN value - regex = ("Gridcenter or grid dimensions have NaN element") + regex = "Gridcenter or grid dimensions have NaN element" with pytest.raises(ValueError, match=regex): D = density.DensityAnalysis( universe.select_atoms(self.selections["static"]), @@ -409,10 +443,12 @@ def test_ValueError_userdefn_xdim_nanvalue(self, universe, ).run(step=5, **client_DensityAnalysis) def test_warn_noatomgroup(self, universe, client_DensityAnalysis): - regex = ("No atoms in AtomGroup at input time frame. " - "This may be intended; please ensure that " - "your grid selection covers the atomic " - "positions you wish to capture.") + regex = ( + "No atoms in AtomGroup at input time frame. " + "This may be intended; please ensure that " + "your grid selection covers the atomic " + "positions you wish to capture." + ) with pytest.warns(UserWarning, match=regex): D = density.DensityAnalysis( universe.select_atoms(self.selections["none"]), @@ -425,20 +461,24 @@ def test_warn_noatomgroup(self, universe, client_DensityAnalysis): ).run(step=5, **client_DensityAnalysis) def test_ValueError_noatomgroup(self, universe, client_DensityAnalysis): - with pytest.raises(ValueError, match="No atoms in AtomGroup at input" - " time frame. Grid for density" - " could not be automatically" - " generated. If this is" - " expected, a user" - " defined grid will " - "need to be provided instead."): + with pytest.raises( + ValueError, + match="No atoms in AtomGroup at input" + " time frame. Grid for density" + " could not be automatically" + " generated. If this is" + " expected, a user" + " defined grid will " + "need to be provided instead.", + ): D = density.DensityAnalysis( universe.select_atoms(self.selections["none"]) ).run(step=5, **client_DensityAnalysis) def test_warn_results_deprecated(self, universe, client_DensityAnalysis): D = density.DensityAnalysis( - universe.select_atoms(self.selections['static'])) + universe.select_atoms(self.selections["static"]) + ) D.run(stop=1, **client_DensityAnalysis) wmsg = "The `density` attribute was deprecated in MDAnalysis 2.0.0" with pytest.warns(DeprecationWarning, match=wmsg): @@ -451,27 +491,30 @@ def test_density_analysis_conversion_default_unit(self): D.run() D.results.density.convert_density() + class TestGridImport(object): - @block_import('gridData') + @block_import("gridData") def test_absence_griddata(self): - sys.modules.pop('MDAnalysis.analysis.density', None) + sys.modules.pop("MDAnalysis.analysis.density", None) # if gridData package is missing an ImportError should be raised # at the module level of MDAnalysis.analysis.density with pytest.raises(ImportError): import MDAnalysis.analysis.density def test_presence_griddata(self): - sys.modules.pop('MDAnalysis.analysis.density', None) + sys.modules.pop("MDAnalysis.analysis.density", None) # no ImportError exception is raised when gridData is properly # imported by MDAnalysis.analysis.density # mock gridData in case there are testing scenarios where # it is not available mock = Mock() - with patch.dict('sys.modules', {'gridData': mock}): + with patch.dict("sys.modules", {"gridData": mock}): try: import MDAnalysis.analysis.density except ImportError: - pytest.fail(msg='''MDAnalysis.analysis.density should not raise - an ImportError if gridData is available.''') + pytest.fail( + msg="""MDAnalysis.analysis.density should not raise + an ImportError if gridData is available.""" + ) diff --git a/testsuite/MDAnalysisTests/analysis/test_dielectric.py b/testsuite/MDAnalysisTests/analysis/test_dielectric.py index a1a5ccc5062..45d8e722cdb 100644 --- a/testsuite/MDAnalysisTests/analysis/test_dielectric.py +++ b/testsuite/MDAnalysisTests/analysis/test_dielectric.py @@ -41,7 +41,7 @@ def test_broken_molecules(self, ag): ag.wrap() eps = DielectricConstant(ag, make_whole=False).run() - assert_allclose(eps.results['eps_mean'], 721.711, rtol=1e-03) + assert_allclose(eps.results["eps_mean"], 721.711, rtol=1e-03) def test_broken_repaired_molecules(self, ag): # cut molecules apart @@ -50,21 +50,23 @@ def test_broken_repaired_molecules(self, ag): ag.wrap() eps = DielectricConstant(ag, make_whole=True).run() - assert_allclose(eps.results['eps_mean'], 5.088, rtol=1e-03) + assert_allclose(eps.results["eps_mean"], 5.088, rtol=1e-03) def test_temperature(self, ag): eps = DielectricConstant(ag, temperature=100).run() - assert_allclose(eps.results['eps_mean'], 9.621, rtol=1e-03) + assert_allclose(eps.results["eps_mean"], 9.621, rtol=1e-03) def test_non_charges(self): u = mda.Universe(DCD_TRICLINIC, to_guess=()) - with pytest.raises(NoDataError, - match="No charges defined given atomgroup."): + with pytest.raises( + NoDataError, match="No charges defined given atomgroup." + ): DielectricConstant(u.atoms).run() def test_non_neutral(self, ag): - with pytest.raises(NotImplementedError, - match="Analysis for non-neutral systems or"): + with pytest.raises( + NotImplementedError, match="Analysis for non-neutral systems or" + ): DielectricConstant(ag[:-1]).run() def test_free_charges(self, ag): diff --git a/testsuite/MDAnalysisTests/analysis/test_diffusionmap.py b/testsuite/MDAnalysisTests/analysis/test_diffusionmap.py index 11271fd8f4c..99e978b3ff8 100644 --- a/testsuite/MDAnalysisTests/analysis/test_diffusionmap.py +++ b/testsuite/MDAnalysisTests/analysis/test_diffusionmap.py @@ -28,17 +28,17 @@ from numpy.testing import assert_array_almost_equal, assert_allclose -@pytest.fixture(scope='module') +@pytest.fixture(scope="module") def u(): return MDAnalysis.Universe(PDB, XTC) -@pytest.fixture(scope='module') +@pytest.fixture(scope="module") def dist(u): - return diffusionmap.DistanceMatrix(u, select='backbone') + return diffusionmap.DistanceMatrix(u, select="backbone") -@pytest.fixture(scope='module') +@pytest.fixture(scope="module") def dmap(dist): d_map = diffusionmap.DiffusionMap(dist) d_map.run() @@ -53,57 +53,74 @@ def test_eg(dist, dmap): def test_dist_weights(u): - backbone = u.select_atoms('backbone') + backbone = u.select_atoms("backbone") weights_atoms = np.ones(len(backbone.atoms)) - dist = diffusionmap.DistanceMatrix(u, - select='backbone', - weights=weights_atoms) + dist = diffusionmap.DistanceMatrix( + u, select="backbone", weights=weights_atoms + ) dist.run(step=3) dmap = diffusionmap.DiffusionMap(dist) dmap.run() assert_array_almost_equal(dmap.eigenvalues, [1, 1, 1, 1], 4) - assert_array_almost_equal(dmap._eigenvectors, - ([[0, 0, 1, 0], - [0, 0, 0, 1], - [-.707, -.707, 0, 0], - [.707, -.707, 0, 0]]), 2) + assert_array_almost_equal( + dmap._eigenvectors, + ( + [ + [0, 0, 1, 0], + [0, 0, 0, 1], + [-0.707, -0.707, 0, 0], + [0.707, -0.707, 0, 0], + ] + ), + 2, + ) def test_dist_weights_frames(u): - backbone = u.select_atoms('backbone') + backbone = u.select_atoms("backbone") weights_atoms = np.ones(len(backbone.atoms)) - dist = diffusionmap.DistanceMatrix(u, - select='backbone', - weights=weights_atoms) + dist = diffusionmap.DistanceMatrix( + u, select="backbone", weights=weights_atoms + ) frames = np.arange(len(u.trajectory)) dist.run(frames=frames[::3]) dmap = diffusionmap.DiffusionMap(dist) dmap.run() assert_array_almost_equal(dmap.eigenvalues, [1, 1, 1, 1], 4) - assert_array_almost_equal(dmap._eigenvectors, - ([[0, 0, 1, 0], - [0, 0, 0, 1], - [-.707, -.707, 0, 0], - [.707, -.707, 0, 0]]), 2) + assert_array_almost_equal( + dmap._eigenvectors, + ( + [ + [0, 0, 1, 0], + [0, 0, 0, 1], + [-0.707, -0.707, 0, 0], + [0.707, -0.707, 0, 0], + ] + ), + 2, + ) + def test_distvalues_ag_universe(u): - dist_universe = diffusionmap.DistanceMatrix(u, select='backbone').run() - ag = u.select_atoms('backbone') + dist_universe = diffusionmap.DistanceMatrix(u, select="backbone").run() + ag = u.select_atoms("backbone") dist_ag = diffusionmap.DistanceMatrix(ag).run() - assert_allclose(dist_universe.results.dist_matrix, - dist_ag.results.dist_matrix) + assert_allclose( + dist_universe.results.dist_matrix, dist_ag.results.dist_matrix + ) def test_distvalues_ag_select(u): - dist_universe = diffusionmap.DistanceMatrix(u, select='backbone').run() - ag = u.select_atoms('protein') - dist_ag = diffusionmap.DistanceMatrix(ag, select='backbone').run() - assert_allclose(dist_universe.results.dist_matrix, - dist_ag.results.dist_matrix) - + dist_universe = diffusionmap.DistanceMatrix(u, select="backbone").run() + ag = u.select_atoms("protein") + dist_ag = diffusionmap.DistanceMatrix(ag, select="backbone").run() + assert_allclose( + dist_universe.results.dist_matrix, dist_ag.results.dist_matrix + ) + def test_different_steps(u): - dmap = diffusionmap.DiffusionMap(u, select='backbone') + dmap = diffusionmap.DiffusionMap(u, select="backbone") dmap.run(step=3) assert dmap._eigenvectors.shape == (4, 4) @@ -118,7 +135,7 @@ def test_transform(u, dmap): def test_long_traj(u): - with pytest.warns(UserWarning, match='The distance matrix is very large'): + with pytest.warns(UserWarning, match="The distance matrix is very large"): dmap = diffusionmap.DiffusionMap(u) dmap._dist_matrix.run(stop=1) dmap._dist_matrix.n_frames = 5001 @@ -126,20 +143,21 @@ def test_long_traj(u): def test_updating_atomgroup(u): - with pytest.warns(UserWarning, match='U must be a static AtomGroup'): - resid_select = 'around 5 resname ALA' + with pytest.warns(UserWarning, match="U must be a static AtomGroup"): + resid_select = "around 5 resname ALA" ag = u.select_atoms(resid_select, updating=True) dmap = diffusionmap.DiffusionMap(ag) dmap.run() + def test_not_universe_atomgroup_error(u): trj_only = u.trajectory - with pytest.raises(ValueError, match='U is not a Universe or AtomGroup'): + with pytest.raises(ValueError, match="U is not a Universe or AtomGroup"): diffusionmap.DiffusionMap(trj_only) def test_DistanceMatrix_attr_warning(u): - dist = diffusionmap.DistanceMatrix(u, select='backbone').run(step=3) + dist = diffusionmap.DistanceMatrix(u, select="backbone").run(step=3) wmsg = f"The `dist_matrix` attribute was deprecated in MDAnalysis 2.0.0" with pytest.warns(DeprecationWarning, match=wmsg): assert getattr(dist, "dist_matrix") is dist.results.dist_matrix diff --git a/testsuite/MDAnalysisTests/analysis/test_dihedrals.py b/testsuite/MDAnalysisTests/analysis/test_dihedrals.py index 767345bda7f..82625dfb934 100644 --- a/testsuite/MDAnalysisTests/analysis/test_dihedrals.py +++ b/testsuite/MDAnalysisTests/analysis/test_dihedrals.py @@ -26,10 +26,19 @@ import pytest import MDAnalysis as mda -from MDAnalysisTests.datafiles import (GRO, XTC, TPR, DihedralArray, - DihedralsArray, RamaArray, GLYRamaArray, - JaninArray, LYSJaninArray, PDB_rama, - PDB_janin) +from MDAnalysisTests.datafiles import ( + GRO, + XTC, + TPR, + DihedralArray, + DihedralsArray, + RamaArray, + GLYRamaArray, + JaninArray, + LYSJaninArray, + PDB_rama, + PDB_janin, +) import MDAnalysis.analysis.dihedrals from MDAnalysis.analysis.dihedrals import Dihedral, Ramachandran, Janin @@ -39,10 +48,11 @@ class TestDihedral(object): @pytest.fixture() def atomgroup(self): u = mda.Universe(GRO, XTC) - ag = u.select_atoms("(resid 4 and name N CA C) or (resid 5 and name N)") + ag = u.select_atoms( + "(resid 4 and name N CA C) or (resid 5 and name N)" + ) return ag - def test_dihedral(self, atomgroup, client_Dihedral): # client_Dihedral is defined in testsuite/analysis/conftest.py # among with other testing fixtures. During testing, it will @@ -53,25 +63,39 @@ def test_dihedral(self, atomgroup, client_Dihedral): dihedral = Dihedral([atomgroup]).run(**client_Dihedral) test_dihedral = np.load(DihedralArray) - assert_allclose(dihedral.results.angles, test_dihedral, rtol=0, atol=1.5e-5, - err_msg="error: dihedral angles should " - "match test values") + assert_allclose( + dihedral.results.angles, + test_dihedral, + rtol=0, + atol=1.5e-5, + err_msg="error: dihedral angles should " "match test values", + ) def test_dihedral_single_frame(self, atomgroup, client_Dihedral): - dihedral = Dihedral([atomgroup]).run(start=5, stop=6, **client_Dihedral) + dihedral = Dihedral([atomgroup]).run( + start=5, stop=6, **client_Dihedral + ) test_dihedral = [np.load(DihedralArray)[5]] - assert_allclose(dihedral.results.angles, test_dihedral, rtol=0, atol=1.5e-5, - err_msg="error: dihedral angles should " - "match test vales") + assert_allclose( + dihedral.results.angles, + test_dihedral, + rtol=0, + atol=1.5e-5, + err_msg="error: dihedral angles should " "match test vales", + ) def test_atomgroup_list(self, atomgroup, client_Dihedral): dihedral = Dihedral([atomgroup, atomgroup]).run(**client_Dihedral) test_dihedral = np.load(DihedralsArray) - assert_allclose(dihedral.results.angles, test_dihedral, rtol=0, atol=1.5e-5, - err_msg="error: dihedral angles should " - "match test values") + assert_allclose( + dihedral.results.angles, + test_dihedral, + rtol=0, + atol=1.5e-5, + err_msg="error: dihedral angles should " "match test values", + ) def test_enough_atoms(self, atomgroup, client_Dihedral): with pytest.raises(ValueError): @@ -97,42 +121,64 @@ def rama_ref_array(self): def test_ramachandran(self, universe, rama_ref_array, client_Ramachandran): rama = Ramachandran(universe.select_atoms("protein")).run( - **client_Ramachandran) - - assert_allclose(rama.results.angles, rama_ref_array, rtol=0, atol=1.5e-5, - err_msg="error: dihedral angles should " - "match test values") - - def test_ramachandran_single_frame(self, universe, rama_ref_array, client_Ramachandran): + **client_Ramachandran + ) + + assert_allclose( + rama.results.angles, + rama_ref_array, + rtol=0, + atol=1.5e-5, + err_msg="error: dihedral angles should " "match test values", + ) + + def test_ramachandran_single_frame( + self, universe, rama_ref_array, client_Ramachandran + ): rama = Ramachandran(universe.select_atoms("protein")).run( - start=5, stop=6, **client_Ramachandran) - - assert_allclose(rama.results.angles[0], rama_ref_array[5], rtol=0, atol=1.5e-5, - err_msg="error: dihedral angles should " - "match test values") - - def test_ramachandran_residue_selections(self, universe, client_Ramachandran): + start=5, stop=6, **client_Ramachandran + ) + + assert_allclose( + rama.results.angles[0], + rama_ref_array[5], + rtol=0, + atol=1.5e-5, + err_msg="error: dihedral angles should " "match test values", + ) + + def test_ramachandran_residue_selections( + self, universe, client_Ramachandran + ): rama = Ramachandran(universe.select_atoms("resname GLY")).run( - **client_Ramachandran) + **client_Ramachandran + ) test_rama = np.load(GLYRamaArray) - assert_allclose(rama.results.angles, test_rama, rtol=0, atol=1.5e-5, - err_msg="error: dihedral angles should " - "match test values") + assert_allclose( + rama.results.angles, + test_rama, + rtol=0, + atol=1.5e-5, + err_msg="error: dihedral angles should " "match test values", + ) def test_outside_protein_length(self, universe, client_Ramachandran): with pytest.raises(ValueError): - rama = Ramachandran(universe.select_atoms("resid 220"), - check_protein=True).run(**client_Ramachandran) + rama = Ramachandran( + universe.select_atoms("resid 220"), check_protein=True + ).run(**client_Ramachandran) def test_outside_protein_unchecked(self, universe, client_Ramachandran): - rama = Ramachandran(universe.select_atoms("resid 220"), - check_protein=False).run(**client_Ramachandran) + rama = Ramachandran( + universe.select_atoms("resid 220"), check_protein=False + ).run(**client_Ramachandran) def test_protein_ends(self, universe): with pytest.warns(UserWarning) as record: - rama = Ramachandran(universe.select_atoms("protein"), - check_protein=True).run() + rama = Ramachandran( + universe.select_atoms("protein"), check_protein=True + ).run() assert len(record) == 1 def test_None_removal(self): @@ -141,9 +187,14 @@ def test_None_removal(self): rama = Ramachandran(u.select_atoms("protein").residues[1:-1]) def test_plot(self, universe): - ax = Ramachandran(universe.select_atoms("resid 5-10")).run().plot(ref=True) - assert isinstance(ax, matplotlib.axes.Axes), \ - "Ramachandran.plot() did not return and Axes instance" + ax = ( + Ramachandran(universe.select_atoms("resid 5-10")) + .run() + .plot(ref=True) + ) + assert isinstance( + ax, matplotlib.axes.Axes + ), "Ramachandran.plot() did not return and Axes instance" def test_ramachandran_attr_warning(self, universe): rama = Ramachandran(universe.select_atoms("protein")).run(stop=2) @@ -178,24 +229,36 @@ def _test_janin(self, u, ref_array, client_Janin): janin = Janin(u.select_atoms("protein")).run(**client_Janin) # Test precision lowered to account for platform differences with osx - assert_allclose(janin.results.angles, ref_array, rtol=0, atol=1.5e-3, - err_msg="error: dihedral angles should " - "match test values") + assert_allclose( + janin.results.angles, + ref_array, + rtol=0, + atol=1.5e-3, + err_msg="error: dihedral angles should " "match test values", + ) def test_janin_single_frame(self, universe, janin_ref_array): janin = Janin(universe.select_atoms("protein")).run(start=5, stop=6) - assert_allclose(janin.results.angles[0], janin_ref_array[5], rtol=0, atol=1.5e-3, - err_msg="error: dihedral angles should " - "match test values") + assert_allclose( + janin.results.angles[0], + janin_ref_array[5], + rtol=0, + atol=1.5e-3, + err_msg="error: dihedral angles should " "match test values", + ) def test_janin_residue_selections(self, universe, client_Janin): janin = Janin(universe.select_atoms("resname LYS")).run(**client_Janin) test_janin = np.load(LYSJaninArray) - assert_allclose(janin.results.angles, test_janin, rtol=0, atol=1.5e-3, - err_msg="error: dihedral angles should " - "match test values") + assert_allclose( + janin.results.angles, + test_janin, + rtol=0, + atol=1.5e-3, + err_msg="error: dihedral angles should " "match test values", + ) def test_outside_protein_length(self, universe): with pytest.raises(ValueError): @@ -208,13 +271,17 @@ def test_remove_residues(self, universe): def test_atom_selection(self): with pytest.raises(ValueError): u = mda.Universe(PDB_janin) - janin = Janin(u.select_atoms("protein and not resname ALA CYS GLY " - "PRO SER THR VAL")) + janin = Janin( + u.select_atoms( + "protein and not resname ALA CYS GLY " "PRO SER THR VAL" + ) + ) def test_plot(self, universe): ax = Janin(universe.select_atoms("resid 5-10")).run().plot(ref=True) - assert isinstance(ax, matplotlib.axes.Axes), \ - "Ramachandran.plot() did not return and Axes instance" + assert isinstance( + ax, matplotlib.axes.Axes + ), "Ramachandran.plot() did not return and Axes instance" def test_janin_attr_warning(self, universe): janin = Janin(universe.select_atoms("protein")).run(stop=2) @@ -226,13 +293,14 @@ def test_janin_attr_warning(self, universe): # tests for parallelization + @pytest.mark.parametrize( "classname,is_parallelizable", [ (MDAnalysis.analysis.dihedrals.Dihedral, True), (MDAnalysis.analysis.dihedrals.Ramachandran, True), (MDAnalysis.analysis.dihedrals.Janin, True), - ] + ], ) def test_class_is_parallelizable(classname, is_parallelizable): assert classname._analysis_algorithm_is_parallelizable == is_parallelizable @@ -241,13 +309,31 @@ def test_class_is_parallelizable(classname, is_parallelizable): @pytest.mark.parametrize( "classname,backends", [ - (MDAnalysis.analysis.dihedrals.Dihedral, - ('serial', 'multiprocessing', 'dask',)), - (MDAnalysis.analysis.dihedrals.Ramachandran, - ('serial', 'multiprocessing', 'dask',)), - (MDAnalysis.analysis.dihedrals.Janin, - ('serial', 'multiprocessing', 'dask',)), - ] + ( + MDAnalysis.analysis.dihedrals.Dihedral, + ( + "serial", + "multiprocessing", + "dask", + ), + ), + ( + MDAnalysis.analysis.dihedrals.Ramachandran, + ( + "serial", + "multiprocessing", + "dask", + ), + ), + ( + MDAnalysis.analysis.dihedrals.Janin, + ( + "serial", + "multiprocessing", + "dask", + ), + ), + ], ) def test_supported_backends(classname, backends): assert classname.get_supported_backends() == backends diff --git a/testsuite/MDAnalysisTests/analysis/test_distances.py b/testsuite/MDAnalysisTests/analysis/test_distances.py index 8e3a14f8224..90fe8d75e8e 100644 --- a/testsuite/MDAnalysisTests/analysis/test_distances.py +++ b/testsuite/MDAnalysisTests/analysis/test_distances.py @@ -29,8 +29,13 @@ import MDAnalysis.analysis.distances -from numpy.testing import (assert_equal, assert_array_equal, assert_almost_equal, - assert_array_almost_equal,assert_allclose) +from numpy.testing import ( + assert_equal, + assert_array_equal, + assert_almost_equal, + assert_array_almost_equal, + assert_allclose, +) import numpy as np @@ -38,75 +43,92 @@ class TestContactMatrix(object): @staticmethod @pytest.fixture() def coord(): - return np.array([[1, 1, 1], - [5, 5, 5], - [1.1, 1.1, 1.1], - [11, 11, 11], # neighboring image with pbc - [21, 21, 21]], # non neighboring image with pbc - dtype=np.float32) - + return np.array( + [ + [1, 1, 1], + [5, 5, 5], + [1.1, 1.1, 1.1], + [11, 11, 11], # neighboring image with pbc + [21, 21, 21], + ], # non neighboring image with pbc + dtype=np.float32, + ) + @staticmethod @pytest.fixture() def box(): return np.array([10, 10, 10, 90, 90, 90], dtype=np.float32) - + @staticmethod @pytest.fixture() def shape(): return 5, 5 - + @staticmethod @pytest.fixture() def res_no_pbc(): - return np.array([[1, 0, 1, 0, 0], - [0, 1, 0, 0, 0], - [1, 0, 1, 0, 0], - [0, 0, 0, 1, 0], - [0, 0, 0, 0, 1]], dtype=bool) - + return np.array( + [ + [1, 0, 1, 0, 0], + [0, 1, 0, 0, 0], + [1, 0, 1, 0, 0], + [0, 0, 0, 1, 0], + [0, 0, 0, 0, 1], + ], + dtype=bool, + ) + @staticmethod @pytest.fixture() def res_pbc(): - return np.array([[1, 0, 1, 1, 1], - [0, 1, 0, 0, 0], - [1, 0, 1, 1, 1], - [1, 0, 1, 1, 1], - [1, 0, 1, 1, 1]], dtype=bool) + return np.array( + [ + [1, 0, 1, 1, 1], + [0, 1, 0, 0, 0], + [1, 0, 1, 1, 1], + [1, 0, 1, 1, 1], + [1, 0, 1, 1, 1], + ], + dtype=bool, + ) def test_np(self, coord, shape, res_no_pbc): contacts = MDAnalysis.analysis.distances.contact_matrix( coord, cutoff=1, returntype="numpy" ) - assert contacts.shape == shape, \ - "wrong shape (should be {0})".format(shape) + assert contacts.shape == shape, "wrong shape (should be {0})".format( + shape + ) assert_equal(contacts, res_no_pbc) def test_sparse(self, coord, shape, res_no_pbc): contacts = MDAnalysis.analysis.distances.contact_matrix( coord, cutoff=1.5, returntype="sparse" ) - assert contacts.shape == shape, \ - "wrong shape (should be {0})".format(shape) + assert contacts.shape == shape, "wrong shape (should be {0})".format( + shape + ) assert_equal(contacts.toarray(), res_no_pbc) def test_box_numpy(self, coord, box, shape, res_pbc): contacts = MDAnalysis.analysis.distances.contact_matrix( coord, box=box, cutoff=1 ) - assert contacts.shape == shape, \ - "wrong shape (should be {0})".format(shape) + assert contacts.shape == shape, "wrong shape (should be {0})".format( + shape + ) assert_equal(contacts, res_pbc) def test_box_sparse(self, coord, box, shape, res_pbc): contacts = MDAnalysis.analysis.distances.contact_matrix( - coord, box=box, cutoff=1, returntype='sparse' + coord, box=box, cutoff=1, returntype="sparse" + ) + assert contacts.shape == shape, "wrong shape (should be {0})".format( + shape ) - assert contacts.shape == shape, \ - "wrong shape (should be {0})".format(shape) assert_equal(contacts.toarray(), res_pbc) - class TestDist(object): @staticmethod @@ -126,13 +148,13 @@ def ag2(): @pytest.fixture() def box(): return np.array([8, 8, 8, 90, 90, 90], dtype=np.float32) - + @staticmethod @pytest.fixture() def expected(ag, ag2): - - return np.diag(scipy.spatial.distance.cdist( - ag.positions, ag2.positions) + + return np.diag( + scipy.spatial.distance.cdist(ag.positions, ag2.positions) ) @staticmethod @@ -141,38 +163,37 @@ def expected_box(ag, ag2, box): rp = np.abs(ag.positions - ag2.positions) box_2d = box[np.newaxis, 0:3] - rp = np.where(rp > box_2d / 2, box_2d - rp, rp) + rp = np.where(rp > box_2d / 2, box_2d - rp, rp) return np.sqrt(np.square(rp).sum(axis=1)) def test_pairwise_dist(self, ag, ag2, expected): - '''Ensure that pairwise distances between atoms are - correctly calculated.''' + """Ensure that pairwise distances between atoms are + correctly calculated.""" actual = MDAnalysis.analysis.distances.dist(ag, ag2)[2] assert_allclose(actual, expected) def test_pairwise_dist_box(self, ag, ag2, expected_box, box): - '''Ensure that pairwise distances between atoms are - correctly calculated.''' + """Ensure that pairwise distances between atoms are + correctly calculated.""" actual = MDAnalysis.analysis.distances.dist(ag, ag2, 0, box)[2] assert_allclose(actual, expected_box, rtol=1e-05, atol=10) def test_pairwise_dist_offset_effect(self, ag, ag2, expected): - '''Test that feeding in offsets to dist() doesn't alter - pairwise distance matrix.''' - actual = MDAnalysis.analysis.distances.dist( - ag, ag2, offset=229)[2] + """Test that feeding in offsets to dist() doesn't alter + pairwise distance matrix.""" + actual = MDAnalysis.analysis.distances.dist(ag, ag2, offset=229)[2] assert_allclose(actual, expected) def test_offset_calculation(self, ag, ag2): - '''Test that offsets fed to dist() are correctly calculated.''' - actual = MDAnalysis.analysis.distances.dist(ag, ag2, - offset=33)[:2] - assert_equal(actual, np.array([ag.atoms.resids + 33, - ag2.atoms.resids + 33])) + """Test that offsets fed to dist() are correctly calculated.""" + actual = MDAnalysis.analysis.distances.dist(ag, ag2, offset=33)[:2] + assert_equal( + actual, np.array([ag.atoms.resids + 33, ag2.atoms.resids + 33]) + ) def test_mismatch_exception(self, ag, ag2, expected): - '''A ValueError should be raised if the two atomgroups - don't have the same number of atoms.''' + """A ValueError should be raised if the two atomgroups + don't have the same number of atoms.""" with pytest.raises(ValueError): MDAnalysis.analysis.distances.dist(ag[:8], ag2) @@ -202,36 +223,30 @@ def group(u): @pytest.fixture() def expected(self, group, ag, ag2): - distance_matrix_1 = scipy.spatial.distance.cdist(group.positions, - ag.positions) + distance_matrix_1 = scipy.spatial.distance.cdist( + group.positions, ag.positions + ) mask_1 = np.unique(np.where(distance_matrix_1 <= self.distance)[0]) group_filtered = group[mask_1] - distance_matrix_2 = scipy.spatial.distance.cdist(group_filtered.positions, - ag2.positions) + distance_matrix_2 = scipy.spatial.distance.cdist( + group_filtered.positions, ag2.positions + ) mask_2 = np.unique(np.where(distance_matrix_2 <= self.distance)[0]) return group_filtered[mask_2].indices def test_between_simple_case_indices_only(self, group, ag, ag2, expected): - '''Test MDAnalysis.analysis.distances.between() for + """Test MDAnalysis.analysis.distances.between() for a simple input case. Checks atom indices of returned AtomGroup against sorted expected index - values.''' + values.""" actual = MDAnalysis.analysis.distances.between( - group, - ag, - ag2, - self.distance + group, ag, ag2, self.distance ).indices assert_equal(actual, expected) - @pytest.mark.parametrize('dists', [5.9, 0.0]) + @pytest.mark.parametrize("dists", [5.9, 0.0]) def test_between_return_type(self, dists, group, ag, ag2): - '''Test that MDAnalysis.analysis.distances.between() - returns an AtomGroup even when the returned group is empty.''' - actual = MDAnalysis.analysis.distances.between( - group, - ag, - ag2, - dists - ) + """Test that MDAnalysis.analysis.distances.between() + returns an AtomGroup even when the returned group is empty.""" + actual = MDAnalysis.analysis.distances.between(group, ag, ag2, dists) assert isinstance(actual, MDAnalysis.core.groups.AtomGroup) diff --git a/testsuite/MDAnalysisTests/analysis/test_dssp.py b/testsuite/MDAnalysisTests/analysis/test_dssp.py index f7a1e118931..ee43a400dff 100644 --- a/testsuite/MDAnalysisTests/analysis/test_dssp.py +++ b/testsuite/MDAnalysisTests/analysis/test_dssp.py @@ -9,7 +9,9 @@ # Files that match glob pattern '????.pdb.gz' and matching '????.pdb.dssp' files, # containing the secondary structure assignment string, will be tested automatically. -@pytest.mark.parametrize("pdb_filename", glob.glob(f"{DSSP_FOLDER}/?????.pdb.gz")) +@pytest.mark.parametrize( + "pdb_filename", glob.glob(f"{DSSP_FOLDER}/?????.pdb.gz") +) def test_file_guess_hydrogens(pdb_filename, client_DSSP): u = mda.Universe(pdb_filename) with open(f"{pdb_filename.rstrip('.gz')}.dssp", "r") as fin: @@ -27,7 +29,9 @@ def test_trajectory(client_DSSP): last_frame = "".join(run.results.dssp[-1]) avg_frame = "".join(translate(run.results.dssp_ndarray.mean(axis=0))) - assert first_frame[:10] != last_frame[:10] == avg_frame[:10] == "-EEEEEE---" + assert ( + first_frame[:10] != last_frame[:10] == avg_frame[:10] == "-EEEEEE---" + ) protein = mda.Universe(TPR, XTC).select_atoms("protein") run = DSSP(protein).run(**client_DSSP, stop=10) @@ -39,7 +43,9 @@ def test_atomgroup(client_DSSP): last_frame = "".join(run.results.dssp[-1]) avg_frame = "".join(translate(run.results.dssp_ndarray.mean(axis=0))) - assert first_frame[:10] != last_frame[:10] == avg_frame[:10] == "-EEEEEE---" + assert ( + first_frame[:10] != last_frame[:10] == avg_frame[:10] == "-EEEEEE---" + ) def test_trajectory_with_hydrogens(client_DSSP): @@ -49,10 +55,14 @@ def test_trajectory_with_hydrogens(client_DSSP): last_frame = "".join(run.results.dssp[-1]) avg_frame = "".join(translate(run.results.dssp_ndarray.mean(axis=0))) - assert first_frame[:10] == last_frame[:10] == avg_frame[:10] == "-EEEEEE---" + assert ( + first_frame[:10] == last_frame[:10] == avg_frame[:10] == "-EEEEEE---" + ) -@pytest.mark.parametrize("pdb_filename", glob.glob(f"{DSSP_FOLDER}/2xdgA.pdb.gz")) +@pytest.mark.parametrize( + "pdb_filename", glob.glob(f"{DSSP_FOLDER}/2xdgA.pdb.gz") +) def test_trajectory_without_hydrogen_fails(pdb_filename, client_DSSP): u = mda.Universe(pdb_filename) with pytest.raises(ValueError): @@ -62,8 +72,9 @@ def test_trajectory_without_hydrogen_fails(pdb_filename, client_DSSP): @pytest.mark.parametrize( "pdb_filename", glob.glob(f"{DSSP_FOLDER}/1mr1D_failing.pdb.gz") ) -def test_trajectory_with_uneven_number_of_atoms_fails(pdb_filename, - client_DSSP): +def test_trajectory_with_uneven_number_of_atoms_fails( + pdb_filename, client_DSSP +): u = mda.Universe(pdb_filename) with pytest.raises(ValueError): DSSP(u, guess_hydrogens=True).run(**client_DSSP) diff --git a/testsuite/MDAnalysisTests/analysis/test_encore.py b/testsuite/MDAnalysisTests/analysis/test_encore.py index 948575adfff..707bfcea7fb 100644 --- a/testsuite/MDAnalysisTests/analysis/test_encore.py +++ b/testsuite/MDAnalysisTests/analysis/test_encore.py @@ -52,14 +52,15 @@ def test_moved_to_mdakit_warning(): with pytest.warns(DeprecationWarning, match=wmsg): reload(encore) + class TestEncore(object): - @pytest.fixture(scope='class') + @pytest.fixture(scope="class") def ens1_template(self): template = mda.Universe(PSF, DCD) template.transfer_to_memory(step=5) return template - @pytest.fixture(scope='class') + @pytest.fixture(scope="class") def ens2_template(self): template = mda.Universe(PSF, DCD2) template.transfer_to_memory(step=5) @@ -69,21 +70,23 @@ def ens2_template(self): def ens1(self, ens1_template): return mda.Universe( ens1_template.filename, - ens1_template.trajectory.timeseries(order='fac'), - format=mda.coordinates.memory.MemoryReader) + ens1_template.trajectory.timeseries(order="fac"), + format=mda.coordinates.memory.MemoryReader, + ) @pytest.fixture() def ens2(self, ens2_template): return mda.Universe( ens2_template.filename, - ens2_template.trajectory.timeseries(order='fac'), - format=mda.coordinates.memory.MemoryReader) + ens2_template.trajectory.timeseries(order="fac"), + format=mda.coordinates.memory.MemoryReader, + ) def test_triangular_matrix(self): scalar = 2 size = 3 expected_value = 1.984 - filename = tempfile.mktemp()+".npz" + filename = tempfile.mktemp() + ".npz" triangular_matrix = encore.utils.TriangularMatrix(size=size) @@ -91,130 +94,191 @@ def test_triangular_matrix(self): err_msg = ( "Data error in TriangularMatrix: read/write are not consistent" - ) + ) assert_equal(triangular_matrix[0, 1], expected_value, err_msg) - assert_equal(triangular_matrix[0,1], triangular_matrix[1,0], - err_msg="Data error in TriangularMatrix: matrix non symmetrical") + assert_equal( + triangular_matrix[0, 1], + triangular_matrix[1, 0], + err_msg="Data error in TriangularMatrix: matrix non symmetrical", + ) triangular_matrix.savez(filename) - triangular_matrix_2 = encore.utils.TriangularMatrix(size = size, loadfile = filename) - assert_equal(triangular_matrix_2[0,1], expected_value, - err_msg="Data error in TriangularMatrix: loaded matrix non symmetrical") + triangular_matrix_2 = encore.utils.TriangularMatrix( + size=size, loadfile=filename + ) + assert_equal( + triangular_matrix_2[0, 1], + expected_value, + err_msg="Data error in TriangularMatrix: loaded matrix non symmetrical", + ) - triangular_matrix_3 = encore.utils.TriangularMatrix(size = size) + triangular_matrix_3 = encore.utils.TriangularMatrix(size=size) triangular_matrix_3.loadz(filename) - assert_equal(triangular_matrix_3[0,1], expected_value, - err_msg="Data error in TriangularMatrix: loaded matrix non symmetrical") + assert_equal( + triangular_matrix_3[0, 1], + expected_value, + err_msg="Data error in TriangularMatrix: loaded matrix non symmetrical", + ) incremented_triangular_matrix = triangular_matrix + scalar - assert_equal(incremented_triangular_matrix[0,1], expected_value + scalar, - err_msg="Error in TriangularMatrix: addition of scalar gave" - "inconsistent results") + assert_equal( + incremented_triangular_matrix[0, 1], + expected_value + scalar, + err_msg="Error in TriangularMatrix: addition of scalar gave" + "inconsistent results", + ) triangular_matrix += scalar - assert_equal(triangular_matrix[0,1], expected_value + scalar, - err_msg="Error in TriangularMatrix: addition of scalar gave" - "inconsistent results") + assert_equal( + triangular_matrix[0, 1], + expected_value + scalar, + err_msg="Error in TriangularMatrix: addition of scalar gave" + "inconsistent results", + ) multiplied_triangular_matrix_2 = triangular_matrix_2 * scalar - assert_equal(multiplied_triangular_matrix_2[0,1], expected_value * scalar, - err_msg="Error in TriangularMatrix: multiplication by scalar gave" - "inconsistent results") + assert_equal( + multiplied_triangular_matrix_2[0, 1], + expected_value * scalar, + err_msg="Error in TriangularMatrix: multiplication by scalar gave" + "inconsistent results", + ) triangular_matrix_2 *= scalar - assert_equal(triangular_matrix_2[0,1], expected_value * scalar, - err_msg="Error in TriangularMatrix: multiplication by scalar gave\ -inconsistent results") + assert_equal( + triangular_matrix_2[0, 1], + expected_value * scalar, + err_msg="Error in TriangularMatrix: multiplication by scalar gave\ +inconsistent results", + ) - @pytest.mark.xfail(os.name == 'nt', - reason="Not yet supported on Windows.") + @pytest.mark.xfail(os.name == "nt", reason="Not yet supported on Windows.") def test_parallel_calculation(self): - arguments = [tuple([i]) for i in np.arange(0,100)] + arguments = [tuple([i]) for i in np.arange(0, 100)] - parallel_calculation = encore.utils.ParallelCalculation(function=function, - n_jobs=4, - args=arguments) + parallel_calculation = encore.utils.ParallelCalculation( + function=function, n_jobs=4, args=arguments + ) results = parallel_calculation.run() for i, r in enumerate(results): assert_equal( r[1], - arguments[i][0]**2, - err_msg="Unexpected results from ParallelCalculation") + arguments[i][0] ** 2, + err_msg="Unexpected results from ParallelCalculation", + ) def test_rmsd_matrix_with_superimposition(self, ens1): - conf_dist_matrix = encore.confdistmatrix.conformational_distance_matrix( - ens1, - encore.confdistmatrix.set_rmsd_matrix_elements, - select="name CA", - pairwise_align=True, - weights='mass', - n_jobs=1) + conf_dist_matrix = ( + encore.confdistmatrix.conformational_distance_matrix( + ens1, + encore.confdistmatrix.set_rmsd_matrix_elements, + select="name CA", + pairwise_align=True, + weights="mass", + n_jobs=1, + ) + ) reference = rms.RMSD(ens1, select="name CA") reference.run() err_msg = ( "Calculated RMSD values differ from " - "the reference implementation") + "the reference implementation" + ) for i, rmsd in enumerate(reference.results.rmsd): - assert_allclose(conf_dist_matrix[0, i], rmsd[2], rtol=0, atol=1.5e-3, err_msg=err_msg) + assert_allclose( + conf_dist_matrix[0, i], + rmsd[2], + rtol=0, + atol=1.5e-3, + err_msg=err_msg, + ) def test_rmsd_matrix_with_superimposition_custom_weights(self, ens1): - conf_dist_matrix = encore.confdistmatrix.conformational_distance_matrix( - ens1, - encore.confdistmatrix.set_rmsd_matrix_elements, - select="name CA", - pairwise_align=True, - weights='mass', - n_jobs=1) + conf_dist_matrix = ( + encore.confdistmatrix.conformational_distance_matrix( + ens1, + encore.confdistmatrix.set_rmsd_matrix_elements, + select="name CA", + pairwise_align=True, + weights="mass", + n_jobs=1, + ) + ) - conf_dist_matrix_custom = encore.confdistmatrix.conformational_distance_matrix( - ens1, - encore.confdistmatrix.set_rmsd_matrix_elements, - select="name CA", - pairwise_align=True, - weights=(ens1.select_atoms('name CA').masses, ens1.select_atoms('name CA').masses), - n_jobs=1) + conf_dist_matrix_custom = ( + encore.confdistmatrix.conformational_distance_matrix( + ens1, + encore.confdistmatrix.set_rmsd_matrix_elements, + select="name CA", + pairwise_align=True, + weights=( + ens1.select_atoms("name CA").masses, + ens1.select_atoms("name CA").masses, + ), + n_jobs=1, + ) + ) for i in range(conf_dist_matrix_custom.size): - assert_allclose(conf_dist_matrix_custom[0, i], conf_dist_matrix[0, i], rtol=0, atol=1.5e-7) + assert_allclose( + conf_dist_matrix_custom[0, i], + conf_dist_matrix[0, i], + rtol=0, + atol=1.5e-7, + ) def test_rmsd_matrix_without_superimposition(self, ens1): selection_string = "name CA" selection = ens1.select_atoms(selection_string) reference_rmsd = [] - coordinates = ens1.trajectory.timeseries(selection, order='fac') + coordinates = ens1.trajectory.timeseries(selection, order="fac") for coord in coordinates: - reference_rmsd.append(rms.rmsd(coordinates[0], coord, superposition=False)) + reference_rmsd.append( + rms.rmsd(coordinates[0], coord, superposition=False) + ) confdist_matrix = encore.confdistmatrix.conformational_distance_matrix( ens1, encore.confdistmatrix.set_rmsd_matrix_elements, select=selection_string, pairwise_align=False, - weights='mass', - n_jobs=1) + weights="mass", + n_jobs=1, + ) print(repr(confdist_matrix.as_array()[0, :])) - assert_allclose(confdist_matrix.as_array()[0,:], reference_rmsd, rtol=0, atol=1.5e-3, - err_msg="calculated RMSD values differ from reference") + assert_allclose( + confdist_matrix.as_array()[0, :], + reference_rmsd, + rtol=0, + atol=1.5e-3, + err_msg="calculated RMSD values differ from reference", + ) def test_ensemble_superimposition(self): aligned_ensemble1 = mda.Universe(PSF, DCD) - align.AlignTraj(aligned_ensemble1, aligned_ensemble1, - select="name CA", - in_memory=True).run() + align.AlignTraj( + aligned_ensemble1, + aligned_ensemble1, + select="name CA", + in_memory=True, + ).run() aligned_ensemble2 = mda.Universe(PSF, DCD) - align.AlignTraj(aligned_ensemble2, aligned_ensemble2, - select="name *", - in_memory=True).run() - - rmsfs1 = rms.RMSF(aligned_ensemble1.select_atoms('name *')) + align.AlignTraj( + aligned_ensemble2, + aligned_ensemble2, + select="name *", + in_memory=True, + ).run() + + rmsfs1 = rms.RMSF(aligned_ensemble1.select_atoms("name *")) rmsfs1.run() - rmsfs2 = rms.RMSF(aligned_ensemble2.select_atoms('name *')) + rmsfs2 = rms.RMSF(aligned_ensemble2.select_atoms("name *")) rmsfs2.run() assert sum(rmsfs1.results.rmsf) > sum(rmsfs2.results.rmsf), ( @@ -224,18 +288,24 @@ def test_ensemble_superimposition(self): def test_ensemble_superimposition_to_reference_non_weighted(self): aligned_ensemble1 = mda.Universe(PSF, DCD) - align.AlignTraj(aligned_ensemble1, aligned_ensemble1, - select="name CA", - in_memory=True).run() + align.AlignTraj( + aligned_ensemble1, + aligned_ensemble1, + select="name CA", + in_memory=True, + ).run() aligned_ensemble2 = mda.Universe(PSF, DCD) - align.AlignTraj(aligned_ensemble2, aligned_ensemble2, - select="name *", - in_memory=True).run() - - rmsfs1 = rms.RMSF(aligned_ensemble1.select_atoms('name *')) + align.AlignTraj( + aligned_ensemble2, + aligned_ensemble2, + select="name *", + in_memory=True, + ).run() + + rmsfs1 = rms.RMSF(aligned_ensemble1.select_atoms("name *")) rmsfs1.run() - rmsfs2 = rms.RMSF(aligned_ensemble2.select_atoms('name *')) + rmsfs2 = rms.RMSF(aligned_ensemble2.select_atoms("name *")) rmsfs2.run() assert sum(rmsfs1.results.rmsf) > sum(rmsfs2.results.rmsf), ( @@ -244,64 +314,276 @@ def test_ensemble_superimposition_to_reference_non_weighted(self): ) def test_covariance_matrix(self, ens1): - reference_cov = np.array([ - [12.9122,-5.2692,3.9016,10.0663,-5.3309,3.8923,8.5037,-5.2017,2.6941], - [-5.2692,4.1087,-2.4101,-4.5485,3.3954,-2.3245,-3.7343,2.8415,-1.6223], - [3.9016,-2.4101,3.1800,3.4453,-2.6860,2.2438,2.7751,-2.2523,1.6084], - [10.0663,-4.5485,3.4453,8.8608,-4.6727,3.3641,7.0106,-4.4986,2.2604], - [-5.3309,3.3954,-2.6860,-4.6727,4.4627,-2.4233,-3.8304,3.0367,-1.6942], - [3.8923,-2.3245,2.2438,3.3641,-2.4233,2.6193,2.6908,-2.0252,1.5775], - [8.5037,-3.7343,2.7751,7.0106,-3.8304,2.6908,6.2861,-3.7138,1.8701], - [-5.2017,2.8415,-2.2523,-4.4986,3.0367,-2.0252,-3.7138,3.3999,-1.4166], - [2.6941,-1.6223,1.6084,2.2604,-1.6942,1.5775,1.8701,-1.4166,1.4664]]) - - covariance = encore.covariance.covariance_matrix(ens1, - select="name CA and resnum 1:3", - estimator=encore.covariance.shrinkage_covariance_estimator) - assert_allclose(covariance, reference_cov, rtol=0, atol=1.5e-4, - err_msg="Covariance matrix from covariance estimation not as expected") + reference_cov = np.array( + [ + [ + 12.9122, + -5.2692, + 3.9016, + 10.0663, + -5.3309, + 3.8923, + 8.5037, + -5.2017, + 2.6941, + ], + [ + -5.2692, + 4.1087, + -2.4101, + -4.5485, + 3.3954, + -2.3245, + -3.7343, + 2.8415, + -1.6223, + ], + [ + 3.9016, + -2.4101, + 3.1800, + 3.4453, + -2.6860, + 2.2438, + 2.7751, + -2.2523, + 1.6084, + ], + [ + 10.0663, + -4.5485, + 3.4453, + 8.8608, + -4.6727, + 3.3641, + 7.0106, + -4.4986, + 2.2604, + ], + [ + -5.3309, + 3.3954, + -2.6860, + -4.6727, + 4.4627, + -2.4233, + -3.8304, + 3.0367, + -1.6942, + ], + [ + 3.8923, + -2.3245, + 2.2438, + 3.3641, + -2.4233, + 2.6193, + 2.6908, + -2.0252, + 1.5775, + ], + [ + 8.5037, + -3.7343, + 2.7751, + 7.0106, + -3.8304, + 2.6908, + 6.2861, + -3.7138, + 1.8701, + ], + [ + -5.2017, + 2.8415, + -2.2523, + -4.4986, + 3.0367, + -2.0252, + -3.7138, + 3.3999, + -1.4166, + ], + [ + 2.6941, + -1.6223, + 1.6084, + 2.2604, + -1.6942, + 1.5775, + 1.8701, + -1.4166, + 1.4664, + ], + ] + ) + + covariance = encore.covariance.covariance_matrix( + ens1, + select="name CA and resnum 1:3", + estimator=encore.covariance.shrinkage_covariance_estimator, + ) + assert_allclose( + covariance, + reference_cov, + rtol=0, + atol=1.5e-4, + err_msg="Covariance matrix from covariance estimation not as expected", + ) def test_covariance_matrix_with_reference(self, ens1): - reference_cov = np.array([ - [39.0760,-28.5383,29.7761,37.9330,-35.5251,18.9421,30.4334,-31.4829,12.8712], - [-28.5383,24.1827,-25.5676,-29.0183,30.3511,-15.9598,-22.9298,26.1086,-10.8693], - [29.7761,-25.5676,28.9796,30.7607,-32.8739,17.7072,24.1689,-28.3557,12.1190], - [37.9330,-29.0183,30.7607,37.6532,-36.4537,19.2865,29.9841,-32.1404,12.9998], - [-35.5251,30.3511,-32.8739,-36.4537,38.5711,-20.1190,-28.7652,33.2857,-13.6963], - [18.9421,-15.9598,17.7072,19.2865,-20.1190,11.4059,15.1244,-17.2695,7.8205], - [30.4334,-22.9298,24.1689,29.9841,-28.7652,15.1244,24.0514,-25.4106,10.2863], - [-31.4829,26.1086,-28.3557,-32.1404,33.2857,-17.2695,-25.4106,29.1773,-11.7530], - [12.8712,-10.8693,12.1190,12.9998,-13.6963,7.8205,10.2863,-11.7530,5.5058]]) - - covariance = encore.covariance.covariance_matrix(ens1, - select="name CA and resnum 1:3", - estimator=encore.covariance.shrinkage_covariance_estimator, - reference=ens1) + reference_cov = np.array( + [ + [ + 39.0760, + -28.5383, + 29.7761, + 37.9330, + -35.5251, + 18.9421, + 30.4334, + -31.4829, + 12.8712, + ], + [ + -28.5383, + 24.1827, + -25.5676, + -29.0183, + 30.3511, + -15.9598, + -22.9298, + 26.1086, + -10.8693, + ], + [ + 29.7761, + -25.5676, + 28.9796, + 30.7607, + -32.8739, + 17.7072, + 24.1689, + -28.3557, + 12.1190, + ], + [ + 37.9330, + -29.0183, + 30.7607, + 37.6532, + -36.4537, + 19.2865, + 29.9841, + -32.1404, + 12.9998, + ], + [ + -35.5251, + 30.3511, + -32.8739, + -36.4537, + 38.5711, + -20.1190, + -28.7652, + 33.2857, + -13.6963, + ], + [ + 18.9421, + -15.9598, + 17.7072, + 19.2865, + -20.1190, + 11.4059, + 15.1244, + -17.2695, + 7.8205, + ], + [ + 30.4334, + -22.9298, + 24.1689, + 29.9841, + -28.7652, + 15.1244, + 24.0514, + -25.4106, + 10.2863, + ], + [ + -31.4829, + 26.1086, + -28.3557, + -32.1404, + 33.2857, + -17.2695, + -25.4106, + 29.1773, + -11.7530, + ], + [ + 12.8712, + -10.8693, + 12.1190, + 12.9998, + -13.6963, + 7.8205, + 10.2863, + -11.7530, + 5.5058, + ], + ] + ) + + covariance = encore.covariance.covariance_matrix( + ens1, + select="name CA and resnum 1:3", + estimator=encore.covariance.shrinkage_covariance_estimator, + reference=ens1, + ) err_msg = ( - "Covariance matrix from covariance estimation not as expected" - ) - assert_allclose(covariance, reference_cov, rtol=0, atol=1.5e-4, err_msg=err_msg) + "Covariance matrix from covariance estimation not as expected" + ) + assert_allclose( + covariance, reference_cov, rtol=0, atol=1.5e-4, err_msg=err_msg + ) def test_hes_to_self(self, ens1): results, details = encore.hes([ens1, ens1]) result_value = results[0, 1] - expected_value = 0. - assert_allclose(result_value, expected_value, rtol=0, atol=1.5e-7, - err_msg="Harmonic Ensemble Similarity to itself\ - not zero:{0:f}".format(result_value)) + expected_value = 0.0 + assert_allclose( + result_value, + expected_value, + rtol=0, + atol=1.5e-7, + err_msg="Harmonic Ensemble Similarity to itself\ + not zero:{0:f}".format( + result_value + ), + ) def test_hes(self, ens1, ens2): - results, details = encore.hes([ens1, ens2], weights='mass') + results, details = encore.hes([ens1, ens2], weights="mass") result_value = results[0, 1] - min_bound = 1E5 - assert result_value > min_bound, "Unexpected value for Harmonic " \ - "Ensemble Similarity: {0:f}. Expected {1:f}.".format(result_value, min_bound) + min_bound = 1e5 + assert result_value > min_bound, ( + "Unexpected value for Harmonic " + "Ensemble Similarity: {0:f}. Expected {1:f}.".format( + result_value, min_bound + ) + ) def test_hes_custom_weights(self, ens1, ens2): - results, details = encore.hes([ens1, ens2], weights='mass') - results_custom, details_custom = encore.hes([ens1, ens2], - weights=(ens1.select_atoms('name CA').masses, - ens2.select_atoms('name CA').masses)) + results, details = encore.hes([ens1, ens2], weights="mass") + results_custom, details_custom = encore.hes( + [ens1, ens2], + weights=( + ens1.select_atoms("name CA").masses, + ens2.select_atoms("name CA").masses, + ), + ) result_value = results[0, 1] result_value_custom = results_custom[0, 1] assert_allclose(result_value, result_value_custom, rtol=0, atol=1.5e-7) @@ -310,90 +592,154 @@ def test_hes_align(self, ens1, ens2): # This test is massively sensitive! # Get 5260 when masses were float32? results, details = encore.hes([ens1, ens2], align=True) - result_value = results[0,1] + result_value = results[0, 1] expected_value = 2047.05 - assert_allclose(result_value, expected_value, rtol=0, atol=1.5e3, - err_msg="Unexpected value for Harmonic Ensemble Similarity: {0:f}. Expected {1:f}.".format(result_value, expected_value)) + assert_allclose( + result_value, + expected_value, + rtol=0, + atol=1.5e3, + err_msg="Unexpected value for Harmonic Ensemble Similarity: {0:f}. Expected {1:f}.".format( + result_value, expected_value + ), + ) def test_ces_to_self(self, ens1): - results, details = \ - encore.ces([ens1, ens1], - clustering_method=encore.AffinityPropagationNative(preference = -3.0)) - result_value = results[0,1] - expected_value = 0. - assert_allclose(result_value, expected_value, rtol=0, atol=1.5e-7, - err_msg="ClusteringEnsemble Similarity to itself not zero: {0:f}".format(result_value)) + results, details = encore.ces( + [ens1, ens1], + clustering_method=encore.AffinityPropagationNative( + preference=-3.0 + ), + ) + result_value = results[0, 1] + expected_value = 0.0 + assert_allclose( + result_value, + expected_value, + rtol=0, + atol=1.5e-7, + err_msg="ClusteringEnsemble Similarity to itself not zero: {0:f}".format( + result_value + ), + ) def test_ces(self, ens1, ens2): results, details = encore.ces([ens1, ens2]) - result_value = results[0,1] + result_value = results[0, 1] expected_value = 0.51 - assert_allclose(result_value, expected_value, rtol=0, atol=1.5e-2, - err_msg="Unexpected value for Cluster Ensemble Similarity: {0:f}. Expected {1:f}.".format(result_value, expected_value)) + assert_allclose( + result_value, + expected_value, + rtol=0, + atol=1.5e-2, + err_msg="Unexpected value for Cluster Ensemble Similarity: {0:f}. Expected {1:f}.".format( + result_value, expected_value + ), + ) def test_dres_to_self(self, ens1): results, details = encore.dres([ens1, ens1]) - result_value = results[0,1] - expected_value = 0. - assert_allclose(result_value, expected_value, rtol=0, atol=1.5e-2, - err_msg="Dim. Reduction Ensemble Similarity to itself not zero: {0:f}".format(result_value)) + result_value = results[0, 1] + expected_value = 0.0 + assert_allclose( + result_value, + expected_value, + rtol=0, + atol=1.5e-2, + err_msg="Dim. Reduction Ensemble Similarity to itself not zero: {0:f}".format( + result_value + ), + ) def test_dres(self, ens1, ens2): - results, details = encore.dres([ens1, ens2], select="name CA and resnum 1-10") - result_value = results[0,1] + results, details = encore.dres( + [ens1, ens2], select="name CA and resnum 1-10" + ) + result_value = results[0, 1] upper_bound = 0.6 - assert result_value < upper_bound, "Unexpected value for Dim. " \ - "reduction Ensemble Similarity: {0:f}. Expected {1:f}.".format(result_value, upper_bound) + assert result_value < upper_bound, ( + "Unexpected value for Dim. " + "reduction Ensemble Similarity: {0:f}. Expected {1:f}.".format( + result_value, upper_bound + ) + ) @pytest.mark.xfail # sporadically fails, see Issue #2158 def test_dres_without_superimposition(self, ens1, ens2): distance_matrix = encore.get_distance_matrix( - encore.merge_universes([ens1, ens2]), - superimpose=False) - results, details = encore.dres([ens1, ens2], - distance_matrix = distance_matrix) - result_value = results[0,1] + encore.merge_universes([ens1, ens2]), superimpose=False + ) + results, details = encore.dres( + [ens1, ens2], distance_matrix=distance_matrix + ) + result_value = results[0, 1] expected_value = 0.68 - assert_allclose(result_value, expected_value, rtol=0, atol=1.5e-1, - err_msg="Unexpected value for Dim. reduction Ensemble Similarity: {0:f}. Expected {1:f}.".format(result_value, expected_value)) + assert_allclose( + result_value, + expected_value, + rtol=0, + atol=1.5e-1, + err_msg="Unexpected value for Dim. reduction Ensemble Similarity: {0:f}. Expected {1:f}.".format( + result_value, expected_value + ), + ) def test_ces_convergence(self, ens1): - expected_values = [0.3443593, 0.1941854, 0.06857104, 0.] + expected_values = [0.3443593, 0.1941854, 0.06857104, 0.0] results = encore.ces_convergence(ens1, 5) - for i,ev in enumerate(expected_values): - assert_allclose(ev, results[i], rtol=0, atol=1.5e-2, - err_msg="Unexpected value for Clustering Ensemble similarity in convergence estimation") + for i, ev in enumerate(expected_values): + assert_allclose( + ev, + results[i], + rtol=0, + atol=1.5e-2, + err_msg="Unexpected value for Clustering Ensemble similarity in convergence estimation", + ) def test_dres_convergence(self, ens1): # Due to encore.dres_convergence() involving random numbers, the # following assertion is allowed to fail once. This significantly # reduces the probability of a random test failure. - expected_values = [0.3, 0.] + expected_values = [0.3, 0.0] results = encore.dres_convergence(ens1, 10) try: - assert_allclose(results[:,0], expected_values, rtol=0, atol=1.5e-1) + assert_allclose( + results[:, 0], expected_values, rtol=0, atol=1.5e-1 + ) except AssertionError: # Random test failure is very rare, but repeating the failed test # just once would only assert that the test passes with 50% # probability. To be a little safer, we raise a warning and repeat # the test 10 times: - warnings.warn(message="Test 'test_dres_convergence' failed, " - "repeating test 10 times.", - category=RuntimeWarning) + warnings.warn( + message="Test 'test_dres_convergence' failed, " + "repeating test 10 times.", + category=RuntimeWarning, + ) for i in range(10): results = encore.dres_convergence(ens1, 10) - assert_allclose(results[:,0], expected_values, rtol=0, atol=1.5e-1, - err_msg="Unexpected value for Dim. " - "reduction Ensemble similarity in " - "convergence estimation") + assert_allclose( + results[:, 0], + expected_values, + rtol=0, + atol=1.5e-1, + err_msg="Unexpected value for Dim. " + "reduction Ensemble similarity in " + "convergence estimation", + ) @pytest.mark.xfail # sporadically fails, see Issue #2158 def test_hes_error_estimation(self, ens1): expected_average = 10 expected_stdev = 12 - averages, stdevs = encore.hes([ens1, ens1], estimate_error = True, bootstrapping_samples=10, select="name CA and resnum 1-10") - average = averages[0,1] - stdev = stdevs[0,1] + averages, stdevs = encore.hes( + [ens1, ens1], + estimate_error=True, + bootstrapping_samples=10, + select="name CA and resnum 1-10", + ) + average = averages[0, 1] + stdev = stdevs[0, 1] err_msg = ( "Unexpected average value for bootstrapped samples in Harmonic" " Ensemble similarity" @@ -402,69 +748,87 @@ def test_hes_error_estimation(self, ens1): "Unexpected standard deviation for bootstrapped samples in" " Harmonic Ensemble similarity" ) - assert_allclose(average, expected_average, rtol=0, atol=1.5e2, err_msg=err_msg) - assert_allclose(stdev, expected_stdev, rtol=0, atol=1.5e2, err_msg=error_msg) + assert_allclose( + average, expected_average, rtol=0, atol=1.5e2, err_msg=err_msg + ) + assert_allclose( + stdev, expected_stdev, rtol=0, atol=1.5e2, err_msg=error_msg + ) def test_ces_error_estimation(self, ens1): expected_average = 0.03 expected_stdev = 0.31 - averages, stdevs = encore.ces([ens1, ens1], - estimate_error = True, - bootstrapping_samples=10, - clustering_method=encore.AffinityPropagationNative(preference=-2.0), - select="name CA and resnum 1-10") - average = averages[0,1] - stdev = stdevs[0,1] - - assert_allclose(average, expected_average, rtol=0, atol=1.5e-1, - err_msg="Unexpected average value for bootstrapped samples in Clustering Ensemble similarity") - assert_allclose(stdev, expected_stdev, rtol=0, atol=1.5, - err_msg="Unexpected standard deviation for bootstrapped samples in Clustering Ensemble similarity") + averages, stdevs = encore.ces( + [ens1, ens1], + estimate_error=True, + bootstrapping_samples=10, + clustering_method=encore.AffinityPropagationNative( + preference=-2.0 + ), + select="name CA and resnum 1-10", + ) + average = averages[0, 1] + stdev = stdevs[0, 1] + + assert_allclose( + average, + expected_average, + rtol=0, + atol=1.5e-1, + err_msg="Unexpected average value for bootstrapped samples in Clustering Ensemble similarity", + ) + assert_allclose( + stdev, + expected_stdev, + rtol=0, + atol=1.5, + err_msg="Unexpected standard deviation for bootstrapped samples in Clustering Ensemble similarity", + ) def test_ces_error_estimation_ensemble_bootstrap(self, ens1): # Error estimation using a method that does not take a distance # matrix as input, and therefore relies on bootstrapping the ensembles # instead - pytest.importorskip('sklearn') + pytest.importorskip("sklearn") expected_average = 0.03 expected_stdev = 0.02 - averages, stdevs = encore.ces([ens1, ens1], - estimate_error = True, - bootstrapping_samples=10, - clustering_method=encore.KMeans(n_clusters=2), - select="name CA and resnum 1-10") + averages, stdevs = encore.ces( + [ens1, ens1], + estimate_error=True, + bootstrapping_samples=10, + clustering_method=encore.KMeans(n_clusters=2), + select="name CA and resnum 1-10", + ) average = averages[0, 1] stdev = stdevs[0, 1] err_msg = ( "Unexpected average value for bootstrapped samples in" - " Clustering Ensemble similarity") + " Clustering Ensemble similarity" + ) assert_allclose( - average, - expected_average, - rtol = 0, - atol = 1.5e-1, - err_msg=err_msg) + average, expected_average, rtol=0, atol=1.5e-1, err_msg=err_msg + ) error_msg = ( "Unexpected standard deviation for bootstrapped samples in" " Clustering Ensemble similarity" - ) + ) assert_allclose( - stdev, - expected_stdev, - rtol=0, - atol=1.5e-1, - err_msg=error_msg) + stdev, expected_stdev, rtol=0, atol=1.5e-1, err_msg=error_msg + ) def test_dres_error_estimation(self, ens1): average_upper_bound = 0.3 stdev_upper_bound = 0.2 - averages, stdevs = encore.dres([ens1, ens1], estimate_error = True, - bootstrapping_samples=10, - select="name CA and resnum 1-10") - average = averages[0,1] - stdev = stdevs[0,1] + averages, stdevs = encore.dres( + [ens1, ens1], + estimate_error=True, + bootstrapping_samples=10, + select="name CA and resnum 1-10", + ) + average = averages[0, 1] + stdev = stdevs[0, 1] err_msg = ( "Unexpected average value for bootstrapped samples in Dim. " "reduction Ensemble similarity" @@ -478,141 +842,160 @@ def test_dres_error_estimation(self, ens1): class TestEncoreClustering(object): - @pytest.fixture(scope='class') + @pytest.fixture(scope="class") def ens1_template(self): template = mda.Universe(PSF, DCD) template.transfer_to_memory(step=5) return template - @pytest.fixture(scope='class') + @pytest.fixture(scope="class") def ens2_template(self): template = mda.Universe(PSF, DCD2) template.transfer_to_memory(step=5) return template - @pytest.fixture(scope='class') + @pytest.fixture(scope="class") def cc(self): return encore.ClusterCollection([1, 1, 1, 3, 3, 5, 5, 5]) - @pytest.fixture(scope='class') + @pytest.fixture(scope="class") def cluster(self): return encore.Cluster(elem_list=np.array([0, 1, 2]), centroid=1) @pytest.fixture() def ens1(self, ens1_template): return mda.Universe( - ens1_template.filename, - ens1_template.trajectory.timeseries(order='fac'), - format=mda.coordinates.memory.MemoryReader) + ens1_template.filename, + ens1_template.trajectory.timeseries(order="fac"), + format=mda.coordinates.memory.MemoryReader, + ) @pytest.fixture() def ens2(self, ens2_template): return mda.Universe( ens2_template.filename, - ens2_template.trajectory.timeseries(order='fac'), - format=mda.coordinates.memory.MemoryReader) + ens2_template.trajectory.timeseries(order="fac"), + format=mda.coordinates.memory.MemoryReader, + ) def test_clustering_one_ensemble(self, ens1): cluster_collection = encore.cluster(ens1) expected_value = 7 - assert len(cluster_collection) == expected_value, "Unexpected " \ - "results: {0}".format(cluster_collection) + assert ( + len(cluster_collection) == expected_value + ), "Unexpected " "results: {0}".format(cluster_collection) def test_clustering_two_ensembles(self, ens1, ens2): cluster_collection = encore.cluster([ens1, ens2]) expected_value = 14 - assert len(cluster_collection) == expected_value, "Unexpected " \ - "results: {0}".format(cluster_collection) - - @pytest.mark.xfail(platform.machine() == "arm64" and platform.system() == "Darwin", - reason="see gh-3599") + assert ( + len(cluster_collection) == expected_value + ), "Unexpected " "results: {0}".format(cluster_collection) + + @pytest.mark.xfail( + platform.machine() == "arm64" and platform.system() == "Darwin", + reason="see gh-3599", + ) def test_clustering_three_ensembles_two_identical(self, ens1, ens2): cluster_collection = encore.cluster([ens1, ens2, ens1]) expected_value = 40 - assert len(cluster_collection) == expected_value, "Unexpected result:" \ - " {0}".format(cluster_collection) + assert ( + len(cluster_collection) == expected_value + ), "Unexpected result:" " {0}".format(cluster_collection) def test_clustering_two_methods(self, ens1): cluster_collection = encore.cluster( [ens1], - method=[encore.AffinityPropagationNative(), - encore.AffinityPropagationNative()]) - assert len(cluster_collection[0]) == len(cluster_collection[1]), \ - "Unexpected result: {0}".format(cluster_collection) + method=[ + encore.AffinityPropagationNative(), + encore.AffinityPropagationNative(), + ], + ) + assert len(cluster_collection[0]) == len( + cluster_collection[1] + ), "Unexpected result: {0}".format(cluster_collection) def test_clustering_AffinityPropagationNative_direct(self, ens1): method = encore.AffinityPropagationNative() distance_matrix = encore.get_distance_matrix(ens1) cluster_assignment = method(distance_matrix) expected_value = 7 - assert len(set(cluster_assignment)) == expected_value, \ - "Unexpected result: {0}".format(cluster_assignment) + assert ( + len(set(cluster_assignment)) == expected_value + ), "Unexpected result: {0}".format(cluster_assignment) def test_clustering_AffinityPropagation_direct(self, ens1): - pytest.importorskip('sklearn') + pytest.importorskip("sklearn") method = encore.AffinityPropagation(random_state=0) distance_matrix = encore.get_distance_matrix(ens1) cluster_assignment = method(distance_matrix) expected_value = 7 - assert len(set(cluster_assignment)) == expected_value, \ - "Unexpected result: {0}".format(cluster_assignment) + assert ( + len(set(cluster_assignment)) == expected_value + ), "Unexpected result: {0}".format(cluster_assignment) def test_clustering_KMeans_direct(self, ens1): - pytest.importorskip('sklearn') + pytest.importorskip("sklearn") clusters = 10 method = encore.KMeans(clusters) - coordinates = ens1.trajectory.timeseries(order='fac') - coordinates = np.reshape(coordinates, - (coordinates.shape[0], -1)) + coordinates = ens1.trajectory.timeseries(order="fac") + coordinates = np.reshape(coordinates, (coordinates.shape[0], -1)) cluster_assignment = method(coordinates) - assert len(set(cluster_assignment)) == clusters, \ - "Unexpected result: {0}".format(cluster_assignment) + assert ( + len(set(cluster_assignment)) == clusters + ), "Unexpected result: {0}".format(cluster_assignment) def test_clustering_DBSCAN_direct(self, ens1): - pytest.importorskip('sklearn') + pytest.importorskip("sklearn") method = encore.DBSCAN(eps=0.5, min_samples=2) distance_matrix = encore.get_distance_matrix(ens1) cluster_assignment = method(distance_matrix) expected_value = 2 - assert len(set(cluster_assignment)) == expected_value, \ - "Unexpected result: {0}".format(cluster_assignment) + assert ( + len(set(cluster_assignment)) == expected_value + ), "Unexpected result: {0}".format(cluster_assignment) def test_clustering_two_different_methods(self, ens1): - pytest.importorskip('sklearn') + pytest.importorskip("sklearn") cluster_collection = encore.cluster( [ens1], - method=[encore.AffinityPropagation(preference=-7.5, - random_state=0), - encore.DBSCAN(min_samples=2)]) - assert len(cluster_collection[0]) == len(cluster_collection[1]), \ - "Unexpected result: {0}".format(cluster_collection) + method=[ + encore.AffinityPropagation(preference=-7.5, random_state=0), + encore.DBSCAN(min_samples=2), + ], + ) + assert len(cluster_collection[0]) == len( + cluster_collection[1] + ), "Unexpected result: {0}".format(cluster_collection) def test_clustering_method_w_no_distance_matrix(self, ens1): - pytest.importorskip('sklearn') - cluster_collection = encore.cluster( - [ens1], - method=encore.KMeans(10)) - assert len(cluster_collection) == 10, \ - "Unexpected result: {0}".format(cluster_collection) + pytest.importorskip("sklearn") + cluster_collection = encore.cluster([ens1], method=encore.KMeans(10)) + assert len(cluster_collection) == 10, "Unexpected result: {0}".format( + cluster_collection + ) def test_clustering_two_methods_one_w_no_distance_matrix(self, ens1): - pytest.importorskip('sklearn') + pytest.importorskip("sklearn") cluster_collection = encore.cluster( [ens1], - method=[encore.KMeans(17), - encore.AffinityPropagationNative()]) - assert len(cluster_collection[0]) == len(cluster_collection[0]), \ - "Unexpected result: {0}".format(cluster_collection) + method=[encore.KMeans(17), encore.AffinityPropagationNative()], + ) + assert len(cluster_collection[0]) == len( + cluster_collection[0] + ), "Unexpected result: {0}".format(cluster_collection) def test_sklearn_affinity_propagation(self, ens1): - pytest.importorskip('sklearn') + pytest.importorskip("sklearn") cc1 = encore.cluster([ens1]) - cc2 = encore.cluster([ens1], - method=encore.AffinityPropagation(random_state=0)) - assert len(cc1) == len(cc2), \ - "Native and sklearn implementations of affinity "\ - "propagation don't agree: mismatch in number of "\ - "clusters: {0} {1}".format(len(cc1), len(cc2)) + cc2 = encore.cluster( + [ens1], method=encore.AffinityPropagation(random_state=0) + ) + assert len(cc1) == len(cc2), ( + "Native and sklearn implementations of affinity " + "propagation don't agree: mismatch in number of " + "clusters: {0} {1}".format(len(cc1), len(cc2)) + ) def test_ClusterCollection_init(self, cc): err_msg = "ClusterCollection was not constructed correctly" @@ -631,24 +1014,31 @@ def test_ClusterCollection_get_ids(self, cc): assert_equal( cc.get_ids(), [0, 1, 2], - err_msg="ClusterCollection ids aren't as expected") + err_msg="ClusterCollection ids aren't as expected", + ) def test_ClusterCollection_get_centroids(self, cc): assert_equal( - cc.get_centroids(), [1, 3, 5], - err_msg="ClusterCollection centroids aren't as expected") + cc.get_centroids(), + [1, 3, 5], + err_msg="ClusterCollection centroids aren't as expected", + ) + def test_cluster_add_metadata(self, cluster): - metadata = cluster.elements*10 - cluster.add_metadata('test', metadata) + metadata = cluster.elements * 10 + cluster.add_metadata("test", metadata) assert_equal( - cluster.metadata['test'], + cluster.metadata["test"], metadata, - err_msg="Cluster metadata isn't as expected") + err_msg="Cluster metadata isn't as expected", + ) metadata = np.append(metadata, 9) - error_message = ("Size of metadata is not equal to the " - "number of cluster elements") + error_message = ( + "Size of metadata is not equal to the " + "number of cluster elements" + ) with pytest.raises(TypeError, match=error_message): - cluster.add_metadata('test2', metadata) + cluster.add_metadata("test2", metadata) def test_empty_cluster(self): empty_cluster = encore.Cluster() @@ -663,11 +1053,14 @@ def test_centroid_not_in_elements(self): encore.Cluster([38, 39, 40, 41, 42, 43], 99) def test_metadata_size_error(self): - error_message = ('Size of metadata having label "label" is ' - 'not equal to the number of cluster elements') + error_message = ( + 'Size of metadata having label "label" is ' + "not equal to the number of cluster elements" + ) with pytest.raises(TypeError, match=error_message): - encore.Cluster(np.array([1, 1, 1]), 1, None, - {"label": [1, 1, 1, 1]}) + encore.Cluster( + np.array([1, 1, 1]), 1, None, {"label": [1, 1, 1, 1]} + ) def test_cluster_iteration(self, cluster): test = [] @@ -676,7 +1069,7 @@ def test_cluster_iteration(self, cluster): assert_equal(cluster.elements, test) def test_cluster_len(self, cluster): - assert(cluster.size == len(cluster)) + assert cluster.size == len(cluster) def test_cluster_repr(self): repr_message = "" @@ -685,6 +1078,7 @@ def test_cluster_repr(self): repr_message = "" assert_equal(repr(cluster), repr_message) + class TestEncoreClusteringSklearn(object): """The tests in this class were duplicated from the affinity propagation tests in scikit-learn""" @@ -693,75 +1087,79 @@ class TestEncoreClusteringSklearn(object): @pytest.fixture() def distance_matrix(self): - X = np.array([[8.73101582, 8.85617874], - [11.61311169, 11.58774351], - [10.86083514, 11.06253959], - [9.45576027, 8.50606967], - [11.30441509, 11.04867001], - [8.63708065, 9.02077816], - [8.34792066, 9.1851129], - [11.06197897, 11.15126501], - [11.24563175, 9.36888267], - [10.83455241, 8.70101808], - [11.49211627, 11.48095194], - [10.6448857, 10.20768141], - [10.491806, 9.38775868], - [11.08330999, 9.39065561], - [10.83872922, 9.48897803], - [11.37890079, 8.93799596], - [11.70562094, 11.16006288], - [10.95871246, 11.1642394], - [11.59763163, 10.91793669], - [11.05761743, 11.5817094], - [8.35444086, 8.91490389], - [8.79613913, 8.82477028], - [11.00420001, 9.7143482], - [11.90790185, 10.41825373], - [11.39149519, 11.89635728], - [8.31749192, 9.78031016], - [11.59530088, 9.75835567], - [11.17754529, 11.13346973], - [11.01830341, 10.92512646], - [11.75326028, 8.46089638], - [11.74702358, 9.36241786], - [10.53075064, 9.77744847], - [8.67474149, 8.30948696], - [11.05076484, 9.16079575], - [8.79567794, 8.52774713], - [11.18626498, 8.38550253], - [10.57169895, 9.42178069], - [8.65168114, 8.76846013], - [11.12522708, 10.6583617], - [8.87537899, 9.02246614], - [9.29163622, 9.05159316], - [11.38003537, 10.93945712], - [8.74627116, 8.85490353], - [10.65550973, 9.76402598], - [8.49888186, 9.31099614], - [8.64181338, 9.154761], - [10.84506927, 10.8790789], - [8.98872711, 9.17133275], - [11.7470232, 10.60908885], - [10.89279865, 9.32098256], - [11.14254656, 9.28262927], - [9.02660689, 9.12098876], - [9.16093666, 8.72607596], - [11.47151183, 8.92803007], - [11.76917681, 9.59220592], - [9.97880407, 11.26144744], - [8.58057881, 8.43199283], - [10.53394006, 9.36033059], - [11.34577448, 10.70313399], - [9.07097046, 8.83928763]]) - - XX = np.einsum('ij,ij->i', X, X)[:, np.newaxis] + X = np.array( + [ + [8.73101582, 8.85617874], + [11.61311169, 11.58774351], + [10.86083514, 11.06253959], + [9.45576027, 8.50606967], + [11.30441509, 11.04867001], + [8.63708065, 9.02077816], + [8.34792066, 9.1851129], + [11.06197897, 11.15126501], + [11.24563175, 9.36888267], + [10.83455241, 8.70101808], + [11.49211627, 11.48095194], + [10.6448857, 10.20768141], + [10.491806, 9.38775868], + [11.08330999, 9.39065561], + [10.83872922, 9.48897803], + [11.37890079, 8.93799596], + [11.70562094, 11.16006288], + [10.95871246, 11.1642394], + [11.59763163, 10.91793669], + [11.05761743, 11.5817094], + [8.35444086, 8.91490389], + [8.79613913, 8.82477028], + [11.00420001, 9.7143482], + [11.90790185, 10.41825373], + [11.39149519, 11.89635728], + [8.31749192, 9.78031016], + [11.59530088, 9.75835567], + [11.17754529, 11.13346973], + [11.01830341, 10.92512646], + [11.75326028, 8.46089638], + [11.74702358, 9.36241786], + [10.53075064, 9.77744847], + [8.67474149, 8.30948696], + [11.05076484, 9.16079575], + [8.79567794, 8.52774713], + [11.18626498, 8.38550253], + [10.57169895, 9.42178069], + [8.65168114, 8.76846013], + [11.12522708, 10.6583617], + [8.87537899, 9.02246614], + [9.29163622, 9.05159316], + [11.38003537, 10.93945712], + [8.74627116, 8.85490353], + [10.65550973, 9.76402598], + [8.49888186, 9.31099614], + [8.64181338, 9.154761], + [10.84506927, 10.8790789], + [8.98872711, 9.17133275], + [11.7470232, 10.60908885], + [10.89279865, 9.32098256], + [11.14254656, 9.28262927], + [9.02660689, 9.12098876], + [9.16093666, 8.72607596], + [11.47151183, 8.92803007], + [11.76917681, 9.59220592], + [9.97880407, 11.26144744], + [8.58057881, 8.43199283], + [10.53394006, 9.36033059], + [11.34577448, 10.70313399], + [9.07097046, 8.83928763], + ] + ) + + XX = np.einsum("ij,ij->i", X, X)[:, np.newaxis] YY = XX.T distances = np.dot(X, X.T) distances *= -2 distances += XX distances += YY np.maximum(distances, 0, out=distances) - distances.flat[::distances.shape[0] + 1] = 0.0 + distances.flat[:: distances.shape[0] + 1] = 0.0 dimension = len(distances) distance_matrix = encore.utils.TriangularMatrix(len(distances)) @@ -771,24 +1169,27 @@ def distance_matrix(self): return distance_matrix def test_one(self, distance_matrix): - preference = -float(np.median(distance_matrix.as_array()) * 10.) - clustering_method = encore.AffinityPropagationNative(preference=preference) - ccs = encore.cluster(None, - distance_matrix=distance_matrix, - method=clustering_method) - assert self.n_clusters == len(ccs), \ - "Basic clustering test failed to give the right"\ - "number of clusters: {0} vs {1}".format(self.n_clusters, len(ccs)) + preference = -float(np.median(distance_matrix.as_array()) * 10.0) + clustering_method = encore.AffinityPropagationNative( + preference=preference + ) + ccs = encore.cluster( + None, distance_matrix=distance_matrix, method=clustering_method + ) + assert self.n_clusters == len(ccs), ( + "Basic clustering test failed to give the right" + "number of clusters: {0} vs {1}".format(self.n_clusters, len(ccs)) + ) class TestEncoreDimensionalityReduction(object): - @pytest.fixture(scope='class') + @pytest.fixture(scope="class") def ens1_template(self): template = mda.Universe(PSF, DCD) template.transfer_to_memory(step=5) return template - @pytest.fixture(scope='class') + @pytest.fixture(scope="class") def ens2_template(self): template = mda.Universe(PSF, DCD2) template.transfer_to_memory(step=5) @@ -798,126 +1199,168 @@ def ens2_template(self): def ens1(self, ens1_template): return mda.Universe( ens1_template.filename, - ens1_template.trajectory.timeseries(order='fac'), - format=mda.coordinates.memory.MemoryReader) + ens1_template.trajectory.timeseries(order="fac"), + format=mda.coordinates.memory.MemoryReader, + ) @pytest.fixture() def ens2(self, ens2_template): return mda.Universe( ens2_template.filename, - ens2_template.trajectory.timeseries(order='fac'), - format=mda.coordinates.memory.MemoryReader) + ens2_template.trajectory.timeseries(order="fac"), + format=mda.coordinates.memory.MemoryReader, + ) def test_dimensionality_reduction_one_ensemble(self, ens1): dimension = 2 coordinates, details = encore.reduce_dimensionality(ens1) - assert_equal(coordinates.shape[0], dimension, - err_msg="Unexpected result in dimensionality reduction: {0}".format(coordinates)) + assert_equal( + coordinates.shape[0], + dimension, + err_msg="Unexpected result in dimensionality reduction: {0}".format( + coordinates + ), + ) def test_dimensionality_reduction_two_ensembles(self, ens1, ens2): dimension = 2 - coordinates, details = \ - encore.reduce_dimensionality([ens1, ens2]) - assert_equal(coordinates.shape[0], dimension, - err_msg="Unexpected result in dimensionality reduction: {0}".format(coordinates)) - - - def test_dimensionality_reduction_three_ensembles_two_identical(self, - ens1, ens2): - coordinates, details = \ - encore.reduce_dimensionality([ens1, ens2, ens1]) - coordinates_ens1 = coordinates[:,np.where(details["ensemble_membership"]==1)] - coordinates_ens3 = coordinates[:,np.where(details["ensemble_membership"]==3)] - assert_allclose(coordinates_ens1, coordinates_ens3, rtol=0, atol=1.5, - err_msg="Unexpected result in dimensionality reduction: {0}".format(coordinates)) + coordinates, details = encore.reduce_dimensionality([ens1, ens2]) + assert_equal( + coordinates.shape[0], + dimension, + err_msg="Unexpected result in dimensionality reduction: {0}".format( + coordinates + ), + ) + def test_dimensionality_reduction_three_ensembles_two_identical( + self, ens1, ens2 + ): + coordinates, details = encore.reduce_dimensionality([ens1, ens2, ens1]) + coordinates_ens1 = coordinates[ + :, np.where(details["ensemble_membership"] == 1) + ] + coordinates_ens3 = coordinates[ + :, np.where(details["ensemble_membership"] == 3) + ] + assert_allclose( + coordinates_ens1, + coordinates_ens3, + rtol=0, + atol=1.5, + err_msg="Unexpected result in dimensionality reduction: {0}".format( + coordinates + ), + ) def test_dimensionality_reduction_specified_dimension(self, ens1, ens2): dimension = 3 coordinates, details = encore.reduce_dimensionality( [ens1, ens2], - method=encore.StochasticProximityEmbeddingNative(dimension=dimension)) - assert_equal(coordinates.shape[0], dimension, - err_msg="Unexpected result in dimensionality reduction: {0}".format(coordinates)) - + method=encore.StochasticProximityEmbeddingNative( + dimension=dimension + ), + ) + assert_equal( + coordinates.shape[0], + dimension, + err_msg="Unexpected result in dimensionality reduction: {0}".format( + coordinates + ), + ) def test_dimensionality_reduction_SPENative_direct(self, ens1): dimension = 2 method = encore.StochasticProximityEmbeddingNative(dimension=dimension) distance_matrix = encore.get_distance_matrix(ens1) coordinates, details = method(distance_matrix) - assert_equal(coordinates.shape[0], dimension, - err_msg="Unexpected result in dimensionality reduction: {0}".format( - coordinates)) + assert_equal( + coordinates.shape[0], + dimension, + err_msg="Unexpected result in dimensionality reduction: {0}".format( + coordinates + ), + ) def test_dimensionality_reduction_PCA_direct(self, ens1): - pytest.importorskip('sklearn') + pytest.importorskip("sklearn") dimension = 2 method = encore.PrincipalComponentAnalysis(dimension=dimension) - coordinates = ens1.trajectory.timeseries(order='fac') - coordinates = np.reshape(coordinates, - (coordinates.shape[0], -1)) + coordinates = ens1.trajectory.timeseries(order="fac") + coordinates = np.reshape(coordinates, (coordinates.shape[0], -1)) coordinates, details = method(coordinates) - assert_equal(coordinates.shape[0], dimension, - err_msg="Unexpected result in dimensionality reduction: {0}".format( - coordinates)) - + assert_equal( + coordinates.shape[0], + dimension, + err_msg="Unexpected result in dimensionality reduction: {0}".format( + coordinates + ), + ) def test_dimensionality_reduction_different_method(self, ens1, ens2): - pytest.importorskip('sklearn') + pytest.importorskip("sklearn") dimension = 3 - coordinates, details = \ - encore.reduce_dimensionality( - [ens1, ens2], - method=encore.PrincipalComponentAnalysis(dimension=dimension)) - assert_equal(coordinates.shape[0], dimension, - err_msg="Unexpected result in dimensionality reduction: {0}".format(coordinates)) - + coordinates, details = encore.reduce_dimensionality( + [ens1, ens2], + method=encore.PrincipalComponentAnalysis(dimension=dimension), + ) + assert_equal( + coordinates.shape[0], + dimension, + err_msg="Unexpected result in dimensionality reduction: {0}".format( + coordinates + ), + ) def test_dimensionality_reduction_two_methods(self, ens1, ens2): - dims = [2,3] - coordinates, details = \ - encore.reduce_dimensionality( - [ens1, ens2], - method=[encore.StochasticProximityEmbeddingNative(dims[0]), - encore.StochasticProximityEmbeddingNative(dims[1])]) + dims = [2, 3] + coordinates, details = encore.reduce_dimensionality( + [ens1, ens2], + method=[ + encore.StochasticProximityEmbeddingNative(dims[0]), + encore.StochasticProximityEmbeddingNative(dims[1]), + ], + ) assert_equal(coordinates[1].shape[0], dims[1]) def test_dimensionality_reduction_two_different_methods(self, ens1, ens2): - pytest.importorskip('sklearn') - dims = [2,3] - coordinates, details = \ - encore.reduce_dimensionality( - [ens1, ens2], - method=[encore.StochasticProximityEmbeddingNative(dims[0]), - encore.PrincipalComponentAnalysis(dims[1])]) + pytest.importorskip("sklearn") + dims = [2, 3] + coordinates, details = encore.reduce_dimensionality( + [ens1, ens2], + method=[ + encore.StochasticProximityEmbeddingNative(dims[0]), + encore.PrincipalComponentAnalysis(dims[1]), + ], + ) assert_equal(coordinates[1].shape[0], dims[1]) class TestEncoreConfDistMatrix(object): def test_get_distance_matrix(self): # Issue #1324 - u = mda.Universe(TPR,XTC) + u = mda.Universe(TPR, XTC) dm = confdistmatrix.get_distance_matrix(u) + class TestEncoreImportWarnings(object): - @block_import('sklearn') + @block_import("sklearn") def _check_sklearn_import_warns(self, package, recwarn): for mod in list(sys.modules): # list as we're changing as we iterate - if 'encore' in mod: + if "encore" in mod: sys.modules.pop(mod, None) - warnings.simplefilter('always') + warnings.simplefilter("always") # assert_warns(ImportWarning, importlib.import_module, package) importlib.import_module(package) assert recwarn.pop(ImportWarning) def test_import_warnings(self, recwarn): for mod in list(sys.modules): # list as we're changing as we iterate - if 'encore' in mod: + if "encore" in mod: sys.modules.pop(mod, None) for pkg in ( - 'MDAnalysis.analysis.encore.dimensionality_reduction.DimensionalityReductionMethod', - 'MDAnalysis.analysis.encore.clustering.ClusteringMethod', + "MDAnalysis.analysis.encore.dimensionality_reduction.DimensionalityReductionMethod", + "MDAnalysis.analysis.encore.clustering.ClusteringMethod", ): self._check_sklearn_import_warns(pkg, recwarn) # This is a quickfix! Convert this to a parametrize call in future. diff --git a/testsuite/MDAnalysisTests/analysis/test_gnm.py b/testsuite/MDAnalysisTests/analysis/test_gnm.py index e69ac7056fe..36241d3e854 100644 --- a/testsuite/MDAnalysisTests/analysis/test_gnm.py +++ b/testsuite/MDAnalysisTests/analysis/test_gnm.py @@ -39,16 +39,27 @@ def universe(): def test_gnm(universe, tmpdir, client_GNMAnalysis): - output = os.path.join(str(tmpdir), 'output.txt') + output = os.path.join(str(tmpdir), "output.txt") gnm = mda.analysis.gnm.GNMAnalysis(universe, ReportVector=output) gnm.run(**client_GNMAnalysis) result = gnm.results assert len(result.times) == 10 assert_almost_equal(gnm.results.times, np.arange(0, 1000, 100), decimal=4) - assert_almost_equal(gnm.results.eigenvalues, - [2.0287113e-15, 4.1471575e-15, 1.8539533e-15, 4.3810359e-15, - 3.9607304e-15, 4.1289113e-15, 2.5501084e-15, 4.0498182e-15, - 4.2058769e-15, 3.9839431e-15]) + assert_almost_equal( + gnm.results.eigenvalues, + [ + 2.0287113e-15, + 4.1471575e-15, + 1.8539533e-15, + 4.3810359e-15, + 3.9607304e-15, + 4.1289113e-15, + 2.5501084e-15, + 4.0498182e-15, + 4.2058769e-15, + 3.9839431e-15, + ], + ) def test_gnm_run_step(universe, client_GNMAnalysis): @@ -57,26 +68,234 @@ def test_gnm_run_step(universe, client_GNMAnalysis): result = gnm.results assert len(result.times) == 4 assert_almost_equal(gnm.results.times, np.arange(0, 1200, 300), decimal=4) - assert_almost_equal(gnm.results.eigenvalues, - [2.0287113e-15, 4.3810359e-15, 2.5501084e-15, 3.9839431e-15]) + assert_almost_equal( + gnm.results.eigenvalues, + [2.0287113e-15, 4.3810359e-15, 2.5501084e-15, 3.9839431e-15], + ) def test_generate_kirchoff(universe): gnm = mda.analysis.gnm.GNMAnalysis(universe) gen = gnm.generate_kirchoff() - assert_almost_equal(gen[0], - [7,-1,-1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0,-1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0,-1,-1,-1, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,-1, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]) + assert_almost_equal( + gen[0], + [ + 7, + -1, + -1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + -1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + -1, + -1, + -1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + -1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + ], + ) def test_gnm_SVD_fail(universe): @@ -93,25 +312,228 @@ def test_closeContactGNMAnalysis(universe, client_GNMAnalysis): gnm.run(stop=2, **client_GNMAnalysis) result = gnm.results assert len(result.times) == 2 - assert_almost_equal(gnm.results.times, (0, 100), decimal=4) - assert_almost_equal(gnm.results.eigenvalues, [0.1502614, 0.1426407]) + assert_almost_equal(gnm.results.times, (0, 100), decimal=4) + assert_almost_equal(gnm.results.eigenvalues, [0.1502614, 0.1426407]) gen = gnm.generate_kirchoff() - assert_almost_equal(gen[0], - [16.326744128018923, -2.716098853586913, -1.94736842105263, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, - 0.0, 0.0, 0.0, 0.0, -0.05263157894736842, 0.0, 0.0, 0.0, -3.3541953679557905, 0.0, -1.4210526315789465, 0.0, 0.0, 0.0, 0.0, - 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, - 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, - 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, - 0.0, 0.0, 0.0, -1.0423368771244421, -1.3006649542861801, -0.30779350562554625, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, - 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, -0.927172649945531, -0.7509392614826383, - 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, - 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, - 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, - 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, - 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, - 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, - 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, - 0.0, 0.0, -2.263157894736841, -0.24333213169614382]) + assert_almost_equal( + gen[0], + [ + 16.326744128018923, + -2.716098853586913, + -1.94736842105263, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + -0.05263157894736842, + 0.0, + 0.0, + 0.0, + -3.3541953679557905, + 0.0, + -1.4210526315789465, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + -1.0423368771244421, + -1.3006649542861801, + -0.30779350562554625, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + -0.927172649945531, + -0.7509392614826383, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + -2.263157894736841, + -0.24333213169614382, + ], + ) def test_closeContactGNMAnalysis_weights_None(universe, client_GNMAnalysis): @@ -120,17 +542,224 @@ def test_closeContactGNMAnalysis_weights_None(universe, client_GNMAnalysis): result = gnm.results assert len(result.times) == 2 assert_almost_equal(gnm.results.times, (0, 100), decimal=4) - assert_almost_equal(gnm.results.eigenvalues, [2.4328739, 2.2967251]) + assert_almost_equal(gnm.results.eigenvalues, [2.4328739, 2.2967251]) gen = gnm.generate_kirchoff() - assert_almost_equal(gen[0], - [303.0, -58.0, -37.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, -1.0, - 0.0, 0.0, 0.0, -67.0, 0.0, -27.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, - 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, - 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, -17.0, -15.0, - -6.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, - 0.0, 0.0, 0.0, -14.0, -15.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, - 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, - 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, - 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, - 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, - 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, -43.0, -3.0]) + assert_almost_equal( + gen[0], + [ + 303.0, + -58.0, + -37.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + -1.0, + 0.0, + 0.0, + 0.0, + -67.0, + 0.0, + -27.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + -17.0, + -15.0, + -6.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + -14.0, + -15.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + -43.0, + -3.0, + ], + ) diff --git a/testsuite/MDAnalysisTests/analysis/test_helix_analysis.py b/testsuite/MDAnalysisTests/analysis/test_helix_analysis.py index dbde56fde4e..3a291050d46 100644 --- a/testsuite/MDAnalysisTests/analysis/test_helix_analysis.py +++ b/testsuite/MDAnalysisTests/analysis/test_helix_analysis.py @@ -28,10 +28,16 @@ import MDAnalysis as mda from MDAnalysis.analysis import helix_analysis as hel -from MDAnalysisTests.datafiles import (GRO, XTC, PSF, DCD, PDB_small, - HELANAL_BENDING_MATRIX, - HELANAL_BENDING_MATRIX_SUBSET, - XYZ) +from MDAnalysisTests.datafiles import ( + GRO, + XTC, + PSF, + DCD, + PDB_small, + HELANAL_BENDING_MATRIX, + HELANAL_BENDING_MATRIX_SUBSET, + XYZ, +) # reference data from old helix analysis of a single PDB file: # data = MDAnalysis.analysis.helanal.helanal_main(PDB_small, @@ -39,28 +45,69 @@ # keys are renamed and local screw angles now use a different # algorithm HELANAL_SINGLE_DATA = { - 'global_tilts': np.rad2deg(1.3309656332019535), - 'local_heights summary': np.array([1.5286051, 0.19648294, 0.11384312], - dtype=np.float32), - 'local_bends': - np.array([3.44526005, 4.85425806, 4.69548464, 2.39473653, - 3.56172442, 3.97128344, 3.41563916, 1.86140978, - 5.22997046, 5.41381264, 27.49601364, 39.69839478, - 35.05921936, 21.78928566, 9.8632431, 8.80066967, - 5.5344553, 6.14356709, 10.15450764, 11.07686138, - 9.23541832], dtype=np.float32), - 'local_nres_per_turn summary': np.array([3.64864163, 0.152694, - 0.1131402]), - 'local_twists summary': np.array([98.83011627, 4.08171701, 3.07253003], - dtype=np.float32), - 'local_twists': - np.array([97.23709869, 99.09676361, 97.25350952, 101.76019287, - 100.42689514, 97.08784485, 97.07430267, 98.33553314, - 97.86578369, 95.45792389, 97.10089111, 95.26415253, - 87.93136597, 108.38458252, 95.27167511, 104.01931763, - 100.7199707, 101.48034668, 99.64170074, 94.78946686, - 102.50147247, 97.25154877, 104.54204559, 101.42829895], - dtype=np.float32), + "global_tilts": np.rad2deg(1.3309656332019535), + "local_heights summary": np.array( + [1.5286051, 0.19648294, 0.11384312], dtype=np.float32 + ), + "local_bends": np.array( + [ + 3.44526005, + 4.85425806, + 4.69548464, + 2.39473653, + 3.56172442, + 3.97128344, + 3.41563916, + 1.86140978, + 5.22997046, + 5.41381264, + 27.49601364, + 39.69839478, + 35.05921936, + 21.78928566, + 9.8632431, + 8.80066967, + 5.5344553, + 6.14356709, + 10.15450764, + 11.07686138, + 9.23541832, + ], + dtype=np.float32, + ), + "local_nres_per_turn summary": np.array([3.64864163, 0.152694, 0.1131402]), + "local_twists summary": np.array( + [98.83011627, 4.08171701, 3.07253003], dtype=np.float32 + ), + "local_twists": np.array( + [ + 97.23709869, + 99.09676361, + 97.25350952, + 101.76019287, + 100.42689514, + 97.08784485, + 97.07430267, + 98.33553314, + 97.86578369, + 95.45792389, + 97.10089111, + 95.26415253, + 87.93136597, + 108.38458252, + 95.27167511, + 104.01931763, + 100.7199707, + 101.48034668, + 99.64170074, + 94.78946686, + 102.50147247, + 97.25154877, + 104.54204559, + 101.42829895, + ], + dtype=np.float32, + ), } @@ -113,8 +160,9 @@ def test_local_screw_angles_plane_circle(): """ angdeg = np.arange(0, 360, 12, dtype=np.int32) angrad = np.deg2rad(angdeg, dtype=np.float64) - xyz = np.array([[np.cos(a), np.sin(a), 0] for a in angrad], - dtype=np.float64) + xyz = np.array( + [[np.cos(a), np.sin(a), 0] for a in angrad], dtype=np.float64 + ) xyz[15, 1] = 0 # because np.sin(np.deg2rad(180)) = 1e-16 ?! screw = hel.local_screw_angles([1, 0, 0], [0, 1, 0], xyz) correct = np.zeros_like(angdeg) @@ -130,8 +178,9 @@ def test_local_screw_angles_ortho_circle(): """ angdeg = np.arange(0, 360, 12, dtype=np.int32) angrad = np.deg2rad(angdeg, dtype=np.float64) - xyz = np.array([[np.cos(a), np.sin(a), 0] for a in angrad], - dtype=np.float64) + xyz = np.array( + [[np.cos(a), np.sin(a), 0] for a in angrad], dtype=np.float64 + ) xyz[15, 1] = 0 # because np.sin(np.deg2rad(180)) = 1e-16 ?! screw = hel.local_screw_angles([1, 0, 0], [0, 0, 1], xyz) correct = np.zeros_like(angdeg) @@ -197,57 +246,70 @@ def zigzag(): x x x x x """ n_atoms = 100 - u = mda.Universe.empty(100, atom_resindex=np.arange(n_atoms), - trajectory=True) - xyz = np.array(list(zip([1, -1]*(n_atoms//2), # x \in {0, 1} - [0]*n_atoms, # y == 0 - range(n_atoms)))) # z rises continuously + u = mda.Universe.empty( + 100, atom_resindex=np.arange(n_atoms), trajectory=True + ) + xyz = np.array( + list( + zip( + [1, -1] * (n_atoms // 2), # x \in {0, 1} + [0] * n_atoms, # y == 0 + range(n_atoms), + ) + ) + ) # z rises continuously u.load_new(xyz) return u -@pytest.mark.parametrize('ref_axis,screw_angles', [ - # input vectors zigzag between [-1, 0, 0] and [1, 0, 0] - # global axis is z-axis - ([0, 0, 1], [180, 0]), # calculated to x-z plane - ([1, 0, 0], [180, 0]), # calculated to x-z plane - ([-1, 0, 0], [0, 180]), # calculated to x-z plane upside down - ([0, 1, 0], [90, -90]), # calculated to y-z plane - ([0, -1, 0], [-90, 90]), # calculated to x-z plane upside down - # calculated to diagonal xy-z plane rotating around - ([1, 1, 0], [135, -45]), - ([-1, 1, 0], [45, -135]), - ([1, -1, 0], [-135, 45]), - ([-1, -1, 0], [-45, 135]), - # calculated to diagonal xyz-z plane w/o z contribution - ([1, 1, 1], [135, -45]), - ([1, 1, -1], [135, -45]), - ([1, -1, 1], [-135, 45]), - ([-1, 1, 1], [45, -135]), - ([-1, -1, 1], [-45, 135]), - ([-1, -1, -1], [-45, 135]), -]) +@pytest.mark.parametrize( + "ref_axis,screw_angles", + [ + # input vectors zigzag between [-1, 0, 0] and [1, 0, 0] + # global axis is z-axis + ([0, 0, 1], [180, 0]), # calculated to x-z plane + ([1, 0, 0], [180, 0]), # calculated to x-z plane + ([-1, 0, 0], [0, 180]), # calculated to x-z plane upside down + ([0, 1, 0], [90, -90]), # calculated to y-z plane + ([0, -1, 0], [-90, 90]), # calculated to x-z plane upside down + # calculated to diagonal xy-z plane rotating around + ([1, 1, 0], [135, -45]), + ([-1, 1, 0], [45, -135]), + ([1, -1, 0], [-135, 45]), + ([-1, -1, 0], [-45, 135]), + # calculated to diagonal xyz-z plane w/o z contribution + ([1, 1, 1], [135, -45]), + ([1, 1, -1], [135, -45]), + ([1, -1, 1], [-135, 45]), + ([-1, 1, 1], [45, -135]), + ([-1, -1, 1], [-45, 135]), + ([-1, -1, -1], [-45, 135]), + ], +) def test_helix_analysis_zigzag(zigzag, ref_axis, screw_angles): - properties = hel.helix_analysis(zigzag.atoms.positions, - ref_axis=ref_axis) - assert_almost_equal(properties['local_twists'], 180, decimal=4) - assert_almost_equal(properties['local_nres_per_turn'], 2, decimal=4) - assert_almost_equal(properties['global_axis'], - [0, 0, -1], decimal=4) + properties = hel.helix_analysis(zigzag.atoms.positions, ref_axis=ref_axis) + assert_almost_equal(properties["local_twists"], 180, decimal=4) + assert_almost_equal(properties["local_nres_per_turn"], 2, decimal=4) + assert_almost_equal(properties["global_axis"], [0, 0, -1], decimal=4) # all 0 vectors - assert_almost_equal(properties['local_axes'], 0, decimal=4) - assert_almost_equal(properties['local_bends'], 0, decimal=4) - assert_almost_equal(properties['all_bends'], 0, decimal=4) - assert_almost_equal(properties['local_heights'], 0, decimal=4) - assert_almost_equal(properties['local_helix_directions'][0::2], - [[-1, 0, 0]]*49, decimal=4) - assert_almost_equal(properties['local_helix_directions'][1::2], - [[1, 0, 0]]*49, decimal=4) + assert_almost_equal(properties["local_axes"], 0, decimal=4) + assert_almost_equal(properties["local_bends"], 0, decimal=4) + assert_almost_equal(properties["all_bends"], 0, decimal=4) + assert_almost_equal(properties["local_heights"], 0, decimal=4) + assert_almost_equal( + properties["local_helix_directions"][0::2], + [[-1, 0, 0]] * 49, + decimal=4, + ) + assert_almost_equal( + properties["local_helix_directions"][1::2], [[1, 0, 0]] * 49, decimal=4 + ) origins = zigzag.atoms.positions[1:-1].copy() origins[:, 0] = 0 - assert_almost_equal(properties['local_origins'], origins, decimal=4) - assert_almost_equal(properties['local_screw_angles'], - screw_angles*49, decimal=4) + assert_almost_equal(properties["local_origins"], origins, decimal=4) + assert_almost_equal( + properties["local_screw_angles"], screw_angles * 49, decimal=4 + ) def square_oct(n_rep=10): @@ -258,13 +320,20 @@ def square_oct(n_rep=10): x-coordinates increase continuously. """ # square-octagon-square-octagon - sq2 = 0.5 ** 0.5 + sq2 = 0.5**0.5 square = [(1, 0), (0, 1), (-1, 0), (0, -1)] - octagon = [(1, 0), (sq2, sq2), (0, 1), (-sq2, sq2), - (-1, 0), (-sq2, -sq2), (0, -1), (sq2, -sq2)] - shapes = (square+octagon)*n_rep - xyz = np.array(list(zip(np.arange(len(shapes)), - *zip(*shapes)))) + octagon = [ + (1, 0), + (sq2, sq2), + (0, 1), + (-sq2, sq2), + (-1, 0), + (-sq2, -sq2), + (0, -1), + (sq2, -sq2), + ] + shapes = (square + octagon) * n_rep + xyz = np.array(list(zip(np.arange(len(shapes)), *zip(*shapes)))) n_atoms = len(xyz) u = mda.Universe.empty(n_atoms, trajectory=True) u.load_new(xyz) @@ -276,64 +345,82 @@ def test_helix_analysis_square_oct(): u = square_oct(n_rep=n_rep) n_atoms = len(u.atoms) - properties = hel.helix_analysis(u.atoms.positions, - ref_axis=[0, 0, 1]) + properties = hel.helix_analysis(u.atoms.positions, ref_axis=[0, 0, 1]) twist_trans = [102.76438, 32.235607] - twists = ([90]*2 + twist_trans + [45]*6 + twist_trans[::-1]) * n_rep - assert_almost_equal(properties['local_twists'], twists[:n_atoms-3], - decimal=4) + twists = ([90] * 2 + twist_trans + [45] * 6 + twist_trans[::-1]) * n_rep + assert_almost_equal( + properties["local_twists"], twists[: n_atoms - 3], decimal=4 + ) res_trans = [3.503159, 11.167775] - res = ([4]*2 + res_trans + [8]*6 + res_trans[::-1]) * n_rep - assert_almost_equal(properties['local_nres_per_turn'], res[:n_atoms-3], - decimal=4) - assert_almost_equal(properties['global_axis'], - [-1, 0, 0], decimal=3) - assert_almost_equal(properties['local_axes']-[1, 0, 0], 0, decimal=4) - assert_almost_equal(properties['local_bends'], 0, decimal=4) - assert_almost_equal(properties['all_bends'], 0, decimal=4) - assert_almost_equal(properties['local_heights'], 1, decimal=4) - - loc_rot = [[0., 0., 1.], - [0., -1., 0.], - [0., 0., -1.], - [0., 0.97528684, 0.2209424], # the transition square->oct - [0., 0.70710677, 0.70710677], # 0.5 ** 0.5 - [0., 0., 1.], - [0., -0.70710677, 0.70710677], - [0., -1., 0.], - [0., -0.70710677, -0.70710677], - [0., 0., -1.], - [0., 0.70710677, -0.70710677], - [0., 0.97528684, -0.2209424]] * n_rep - assert_almost_equal(properties['local_helix_directions'], - loc_rot[:n_atoms-2], decimal=4) + res = ([4] * 2 + res_trans + [8] * 6 + res_trans[::-1]) * n_rep + assert_almost_equal( + properties["local_nres_per_turn"], res[: n_atoms - 3], decimal=4 + ) + assert_almost_equal(properties["global_axis"], [-1, 0, 0], decimal=3) + assert_almost_equal(properties["local_axes"] - [1, 0, 0], 0, decimal=4) + assert_almost_equal(properties["local_bends"], 0, decimal=4) + assert_almost_equal(properties["all_bends"], 0, decimal=4) + assert_almost_equal(properties["local_heights"], 1, decimal=4) + + loc_rot = [ + [0.0, 0.0, 1.0], + [0.0, -1.0, 0.0], + [0.0, 0.0, -1.0], + [0.0, 0.97528684, 0.2209424], # the transition square->oct + [0.0, 0.70710677, 0.70710677], # 0.5 ** 0.5 + [0.0, 0.0, 1.0], + [0.0, -0.70710677, 0.70710677], + [0.0, -1.0, 0.0], + [0.0, -0.70710677, -0.70710677], + [0.0, 0.0, -1.0], + [0.0, 0.70710677, -0.70710677], + [0.0, 0.97528684, -0.2209424], + ] * n_rep + assert_almost_equal( + properties["local_helix_directions"], loc_rot[: n_atoms - 2], decimal=4 + ) origins = u.atoms.positions.copy()[1:-1] - origins[:, 1:] = ([[0., 0.], # square - [0., 0.], - [0., -0.33318555], # square -> octagon - [-1.7878988, -0.6315732], # square -> octagon - [0., 0.], # octagon - [0., 0.], - [0., 0.], - [0., 0.], - [0., 0.], - [0., 0.], - [-1.3141878, 1.3141878], # octagon -> square - [0.34966463, 0.14732757]]*n_rep)[:len(origins)] - assert_almost_equal(properties['local_origins'], origins, - decimal=4) + origins[:, 1:] = ( + [ + [0.0, 0.0], # square + [0.0, 0.0], + [0.0, -0.33318555], # square -> octagon + [-1.7878988, -0.6315732], # square -> octagon + [0.0, 0.0], # octagon + [0.0, 0.0], + [0.0, 0.0], + [0.0, 0.0], + [0.0, 0.0], + [0.0, 0.0], + [-1.3141878, 1.3141878], # octagon -> square + [0.34966463, 0.14732757], + ] + * n_rep + )[: len(origins)] + assert_almost_equal(properties["local_origins"], origins, decimal=4) # calculated to the x-y plane # all input vectors (loc_rot) are in y-z plane - screw = [0, 90, 180, # square - -77.236, # transition - -45, 0, 45, 90, 135, 180, -135, # octagon - -102.764]*n_rep + screw = [ + 0, + 90, + 180, # square + -77.236, # transition + -45, + 0, + 45, + 90, + 135, + 180, + -135, # octagon + -102.764, + ] * n_rep # not quite 0, comes out as 1.32e-06 - assert_almost_equal(properties['local_screw_angles'], screw[:-2], - decimal=3) + assert_almost_equal( + properties["local_screw_angles"], screw[:-2], decimal=3 + ) class TestHELANAL(object): @@ -341,47 +428,58 @@ class TestHELANAL(object): @pytest.fixture() def psf_ca(self): u = mda.Universe(PSF, DCD) - ag = u.select_atoms('name CA') + ag = u.select_atoms("name CA") return ag @pytest.fixture() def helanal(self, psf_ca): - ha = hel.HELANAL(psf_ca, select='resnum 161-187', - flatten_single_helix=True) + ha = hel.HELANAL( + psf_ca, select="resnum 161-187", flatten_single_helix=True + ) return ha.run(start=10, stop=80) def test_regression_summary(self, helanal): - bends = helanal.results.summary['all_bends'] + bends = helanal.results.summary["all_bends"] old_helanal = read_bending_matrix(HELANAL_BENDING_MATRIX_SUBSET) - assert_almost_equal(np.triu(bends['mean'], 1), old_helanal['Mean'], - decimal=1) - assert_almost_equal(np.triu(bends['sample_sd'], 1), old_helanal['SD'], - decimal=1) - assert_almost_equal(np.triu(bends['abs_dev'], 1), old_helanal['ABDEV'], - decimal=1) + assert_almost_equal( + np.triu(bends["mean"], 1), old_helanal["Mean"], decimal=1 + ) + assert_almost_equal( + np.triu(bends["sample_sd"], 1), old_helanal["SD"], decimal=1 + ) + assert_almost_equal( + np.triu(bends["abs_dev"], 1), old_helanal["ABDEV"], decimal=1 + ) def test_regression_values(self): u = mda.Universe(PDB_small) - ha = hel.HELANAL(u, select='name CA and resnum 161-187', - flatten_single_helix=True) + ha = hel.HELANAL( + u, select="name CA and resnum 161-187", flatten_single_helix=True + ) ha.run() for key, value in HELANAL_SINGLE_DATA.items(): - if 'summary' in key: + if "summary" in key: data = ha.results[key.split()[0]] - calculated = [data.mean(), data.std(ddof=1), - np.fabs(data-data.mean()).mean()] + calculated = [ + data.mean(), + data.std(ddof=1), + np.fabs(data - data.mean()).mean(), + ] else: calculated = ha.results[key][0] - msg = 'Mismatch between calculated and reference {}' - assert_almost_equal(calculated, value, - decimal=4, - err_msg=msg.format(key)) + msg = "Mismatch between calculated and reference {}" + assert_almost_equal( + calculated, value, decimal=4, err_msg=msg.format(key) + ) def test_multiple_selections(self, psf_ca): - ha = hel.HELANAL(psf_ca, flatten_single_helix=True, - select=('resnum 30-40', 'resnum 60-80')) + ha = hel.HELANAL( + psf_ca, + flatten_single_helix=True, + select=("resnum 30-40", "resnum 60-80"), + ) ha.run() n_frames = len(psf_ca.universe.trajectory) assert len(ha.atomgroups) == 2 @@ -393,23 +491,23 @@ def test_multiple_selections(self, psf_ca): def test_universe_from_origins(self, helanal): u = helanal.universe_from_origins() assert isinstance(u, mda.Universe) - assert len(u.atoms) == len(helanal.atomgroups[0])-2 + assert len(u.atoms) == len(helanal.atomgroups[0]) - 2 assert len(u.trajectory) == 70 def test_universe_from_origins_except(self, psf_ca): - ha = hel.HELANAL(psf_ca, select='resnum 161-187') - with pytest.raises(ValueError, match=r'before universe_from_origins'): + ha = hel.HELANAL(psf_ca, select="resnum 161-187") + with pytest.raises(ValueError, match=r"before universe_from_origins"): u = ha.universe_from_origins() def test_multiple_atoms_per_residues(self): u = mda.Universe(XYZ) with pytest.warns(UserWarning) as rec: - ha = hel.HELANAL(u, select='name H') + ha = hel.HELANAL(u, select="name H") assert len(rec) == 1 - assert 'multiple atoms' in rec[0].message.args[0] + assert "multiple atoms" in rec[0].message.args[0] def test_residue_gaps_split(self, psf_ca): - sel = 'resid 6:50 or resid 100:130 or resid 132:148' + sel = "resid 6:50 or resid 100:130 or resid 132:148" with pytest.warns(UserWarning) as rec: ha = hel.HELANAL(psf_ca, select=sel).run() assert len(ha.atomgroups) == 3 @@ -418,52 +516,55 @@ def test_residue_gaps_split(self, psf_ca): assert len(ha.atomgroups[2]) == 17 assert len(rec) == 1 warnmsg = rec[0].message.args[0] - assert 'has gaps in the residues' in warnmsg - assert 'Splitting into 3 helices' in warnmsg + assert "has gaps in the residues" in warnmsg + assert "Splitting into 3 helices" in warnmsg def test_residue_gaps_no_split(self, psf_ca): - sel = 'resid 6:50 or resid 100:130 or resid 132:148' + sel = "resid 6:50 or resid 100:130 or resid 132:148" with pytest.warns(UserWarning) as rec: - ha = hel.HELANAL(psf_ca, select=sel, - split_residue_sequences=False) + ha = hel.HELANAL(psf_ca, select=sel, split_residue_sequences=False) ha.run() assert len(ha.atomgroups) == 1 - assert len(ha.atomgroups[0]) == 45+31+17 + assert len(ha.atomgroups[0]) == 45 + 31 + 17 assert len(rec) == 1 warnmsg = rec[0].message.args[0] - assert 'has gaps in the residues' in warnmsg - assert 'Splitting into' not in warnmsg + assert "has gaps in the residues" in warnmsg + assert "Splitting into" not in warnmsg def test_len_groups_short(self, psf_ca): - sel = 'resnum 161-168' - with pytest.warns(UserWarning, match='Fewer than 9 atoms found'): + sel = "resnum 161-168" + with pytest.warns(UserWarning, match="Fewer than 9 atoms found"): ha = hel.HELANAL(psf_ca, select=sel) assert len(ha.atomgroups) < 9 - @pytest.mark.parametrize('ref_axis,screw_angles', [ - # input vectors zigzag between [-1, 0, 0] and [1, 0, 0] - # global axis is z-axis - ([0, 0, 1], [180, 0]), # calculated to x-z plane - ([1, 0, 0], [180, 0]), # calculated to x-z plane - ([-1, 0, 0], [0, 180]), # calculated to x-z plane upside down - ([0, 1, 0], [90, -90]), # calculated to y-z plane - ([0, -1, 0], [-90, 90]), # calculated to x-z plane upside down - # calculated to diagonal xy-z plane rotating around - ([1, 1, 0], [135, -45]), - ([-1, 1, 0], [45, -135]), - ([1, -1, 0], [-135, 45]), - ([-1, -1, 0], [-45, 135]), - # calculated to diagonal xyz-z plane w/o z contribution - ([1, 1, 1], [135, -45]), - ([1, 1, -1], [135, -45]), - ([1, -1, 1], [-135, 45]), - ([-1, 1, 1], [45, -135]), - ([-1, -1, 1], [-45, 135]), - ([-1, -1, -1], [-45, 135]), - ]) + @pytest.mark.parametrize( + "ref_axis,screw_angles", + [ + # input vectors zigzag between [-1, 0, 0] and [1, 0, 0] + # global axis is z-axis + ([0, 0, 1], [180, 0]), # calculated to x-z plane + ([1, 0, 0], [180, 0]), # calculated to x-z plane + ([-1, 0, 0], [0, 180]), # calculated to x-z plane upside down + ([0, 1, 0], [90, -90]), # calculated to y-z plane + ([0, -1, 0], [-90, 90]), # calculated to x-z plane upside down + # calculated to diagonal xy-z plane rotating around + ([1, 1, 0], [135, -45]), + ([-1, 1, 0], [45, -135]), + ([1, -1, 0], [-135, 45]), + ([-1, -1, 0], [-45, 135]), + # calculated to diagonal xyz-z plane w/o z contribution + ([1, 1, 1], [135, -45]), + ([1, 1, -1], [135, -45]), + ([1, -1, 1], [-135, 45]), + ([-1, 1, 1], [45, -135]), + ([-1, -1, 1], [-45, 135]), + ([-1, -1, -1], [-45, 135]), + ], + ) def test_helanal_zigzag(self, zigzag, ref_axis, screw_angles): - ha = hel.HELANAL(zigzag, select="all", ref_axis=ref_axis, - flatten_single_helix=True).run() + ha = hel.HELANAL( + zigzag, select="all", ref_axis=ref_axis, flatten_single_helix=True + ).run() assert_almost_equal(ha.results.local_twists, 180, decimal=4) assert_almost_equal(ha.results.local_nres_per_turn, 2, decimal=4) assert_almost_equal(ha.results.global_axis, [[0, 0, -1]], decimal=4) @@ -472,21 +573,28 @@ def test_helanal_zigzag(self, zigzag, ref_axis, screw_angles): assert_almost_equal(ha.results.local_bends, 0, decimal=4) assert_almost_equal(ha.results.all_bends, 0, decimal=4) assert_almost_equal(ha.results.local_heights, 0, decimal=4) - assert_almost_equal(ha.results.local_helix_directions[0][0::2], - [[-1, 0, 0]]*49, decimal=4) - assert_almost_equal(ha.results.local_helix_directions[0][1::2], - [[1, 0, 0]]*49, decimal=4) + assert_almost_equal( + ha.results.local_helix_directions[0][0::2], + [[-1, 0, 0]] * 49, + decimal=4, + ) + assert_almost_equal( + ha.results.local_helix_directions[0][1::2], + [[1, 0, 0]] * 49, + decimal=4, + ) origins = zigzag.atoms.positions[1:-1].copy() origins[:, 0] = 0 assert_almost_equal(ha.results.local_origins[0], origins, decimal=4) - assert_almost_equal(ha.results.local_screw_angles[0], - screw_angles*49, decimal=4) + assert_almost_equal( + ha.results.local_screw_angles[0], screw_angles * 49, decimal=4 + ) def test_vector_of_best_fit(): line = np.random.rand(3) unit = line / np.linalg.norm(line) - points = line*np.arange(1000)[:, np.newaxis] + points = line * np.arange(1000)[:, np.newaxis] noise = np.random.normal(size=(1000, 3)) data = points + noise diff --git a/testsuite/MDAnalysisTests/analysis/test_hydrogenbondautocorrel.py b/testsuite/MDAnalysisTests/analysis/test_hydrogenbondautocorrel.py index 6a4970edba1..97eeb6cc016 100644 --- a/testsuite/MDAnalysisTests/analysis/test_hydrogenbondautocorrel.py +++ b/testsuite/MDAnalysisTests/analysis/test_hydrogenbondautocorrel.py @@ -23,8 +23,10 @@ import pytest from MDAnalysisTests.datafiles import ( - TRZ, TRZ_psf, - waterPSF, waterDCD, + TRZ, + TRZ_psf, + waterPSF, + waterDCD, XYZ_mini, ) from numpy.testing import assert_almost_equal @@ -33,8 +35,10 @@ from importlib import reload import MDAnalysis as mda -from MDAnalysis.analysis.hydrogenbonds import (HydrogenBondAutoCorrel as HBAC, - find_hydrogen_donors) +from MDAnalysis.analysis.hydrogenbonds import ( + HydrogenBondAutoCorrel as HBAC, + find_hydrogen_donors, +) class TestHydrogenBondAutocorrel(object): @@ -44,118 +48,166 @@ def u(self): @pytest.fixture() def hydrogens(self, u): - return u.atoms.select_atoms('name Hn') + return u.atoms.select_atoms("name Hn") @pytest.fixture() def nitrogens(self, u): - return u.atoms.select_atoms('name N') + return u.atoms.select_atoms("name N") @pytest.fixture() def oxygens(self, u): - return u.atoms.select_atoms('name O') + return u.atoms.select_atoms("name O") # regression tests for different conditions def test_continuous(self, u, hydrogens, oxygens, nitrogens): - hbond = HBAC(u, - hydrogens=hydrogens, - acceptors=oxygens, - donors=nitrogens, - bond_type='continuous', - sample_time=0.06, + hbond = HBAC( + u, + hydrogens=hydrogens, + acceptors=oxygens, + donors=nitrogens, + bond_type="continuous", + sample_time=0.06, ) hbond.run() assert_almost_equal( - hbond.solution['results'], - np.array([ 1. , 0.92668623, 0.83137828, - 0.74486804, 0.67741936, 0.60263932], - dtype=np.float32) + hbond.solution["results"], + np.array( + [ + 1.0, + 0.92668623, + 0.83137828, + 0.74486804, + 0.67741936, + 0.60263932, + ], + dtype=np.float32, + ), ) def test_continuous_excl(self, u, hydrogens, oxygens, nitrogens): - hbond = HBAC(u, - hydrogens=hydrogens, - acceptors=oxygens, - donors=nitrogens, - bond_type='continuous', - exclusions=(np.arange(len(hydrogens)), np.array( - range(len(oxygens)))), - sample_time=0.06, + hbond = HBAC( + u, + hydrogens=hydrogens, + acceptors=oxygens, + donors=nitrogens, + bond_type="continuous", + exclusions=( + np.arange(len(hydrogens)), + np.array(range(len(oxygens))), + ), + sample_time=0.06, ) hbond.run() assert_almost_equal( - hbond.solution['results'], - np.array([ 1. , 0.92668623, 0.83137828, - 0.74486804, 0.67741936, 0.60263932], - dtype=np.float32) + hbond.solution["results"], + np.array( + [ + 1.0, + 0.92668623, + 0.83137828, + 0.74486804, + 0.67741936, + 0.60263932, + ], + dtype=np.float32, + ), ) - def test_intermittent(self, u, hydrogens, oxygens, nitrogens): - hbond = HBAC(u, - hydrogens=hydrogens, - acceptors=oxygens, - donors=nitrogens, - bond_type='intermittent', - sample_time=0.06, + hbond = HBAC( + u, + hydrogens=hydrogens, + acceptors=oxygens, + donors=nitrogens, + bond_type="intermittent", + sample_time=0.06, ) hbond.run() assert_almost_equal( - hbond.solution['results'], - np.array([ 1. , 0.92668623, 0.84310848, - 0.79325515, 0.76392961, 0.72287393], - dtype=np.float32) + hbond.solution["results"], + np.array( + [ + 1.0, + 0.92668623, + 0.84310848, + 0.79325515, + 0.76392961, + 0.72287393, + ], + dtype=np.float32, + ), ) - def test_intermittent_timecut(self, u, hydrogens, oxygens, nitrogens): - hbond = HBAC(u, - hydrogens=hydrogens, - acceptors=oxygens, - donors=nitrogens, - bond_type='intermittent', - time_cut=0.01, # time cut at traj.dt == continuous - sample_time=0.06, + hbond = HBAC( + u, + hydrogens=hydrogens, + acceptors=oxygens, + donors=nitrogens, + bond_type="intermittent", + time_cut=0.01, # time cut at traj.dt == continuous + sample_time=0.06, ) hbond.run() assert_almost_equal( - hbond.solution['results'], - np.array([ 1. , 0.92668623, 0.83137828, - 0.74486804, 0.67741936, 0.60263932], - dtype=np.float32) + hbond.solution["results"], + np.array( + [ + 1.0, + 0.92668623, + 0.83137828, + 0.74486804, + 0.67741936, + 0.60263932, + ], + dtype=np.float32, + ), ) def test_intermittent_excl(self, u, hydrogens, oxygens, nitrogens): - hbond = HBAC(u, - hydrogens=hydrogens, - acceptors=oxygens, - donors=nitrogens, - bond_type='intermittent', - exclusions=(np.arange(len(hydrogens)), np.array( - range(len(oxygens)))), - sample_time=0.06, + hbond = HBAC( + u, + hydrogens=hydrogens, + acceptors=oxygens, + donors=nitrogens, + bond_type="intermittent", + exclusions=( + np.arange(len(hydrogens)), + np.array(range(len(oxygens))), + ), + sample_time=0.06, ) hbond.run() assert_almost_equal( - hbond.solution['results'], - np.array([ 1. , 0.92668623, 0.84310848, - 0.79325515, 0.76392961, 0.72287393], - dtype=np.float32) + hbond.solution["results"], + np.array( + [ + 1.0, + 0.92668623, + 0.84310848, + 0.79325515, + 0.76392961, + 0.72287393, + ], + dtype=np.float32, + ), ) # For `solve` the test trajectories aren't long enough # So spoof the results and check that solver finds solution def test_solve_continuous(self, u, hydrogens, oxygens, nitrogens): - hbond = HBAC(u, - hydrogens=hydrogens, - acceptors=oxygens, - donors=nitrogens, - bond_type='continuous', - sample_time=0.06, + hbond = HBAC( + u, + hydrogens=hydrogens, + acceptors=oxygens, + donors=nitrogens, + bond_type="continuous", + sample_time=0.06, ) def actual_function_cont(t): @@ -163,24 +215,26 @@ def actual_function_cont(t): A2 = 0.25 tau1 = 0.5 tau2 = 0.1 - return A1 * np.exp(-t/tau1) + A2 * np.exp(-t/tau2) - hbond.solution['time'] = time = np.arange(0, 0.06, 0.001) - hbond.solution['results'] = actual_function_cont(time) + return A1 * np.exp(-t / tau1) + A2 * np.exp(-t / tau2) + + hbond.solution["time"] = time = np.arange(0, 0.06, 0.001) + hbond.solution["results"] = actual_function_cont(time) hbond.solve() assert_almost_equal( - hbond.solution['fit'], + hbond.solution["fit"], np.array([0.75, 0.5, 0.1]), ) def test_solve_intermittent(self, u, hydrogens, oxygens, nitrogens): - hbond = HBAC(u, - hydrogens=hydrogens, - acceptors=oxygens, - donors=nitrogens, - bond_type='intermittent', - sample_time=0.06, + hbond = HBAC( + u, + hydrogens=hydrogens, + acceptors=oxygens, + donors=nitrogens, + bond_type="intermittent", + sample_time=0.06, ) def actual_function_int(t): @@ -190,69 +244,86 @@ def actual_function_int(t): tau1 = 5 tau2 = 1 tau3 = 0.1 - return A1 * np.exp(-t/tau1) + A2 * np.exp(-t/tau2) + A3 * np.exp(-t/tau3) - hbond.solution['time'] = time = np.arange(0, 6.0, 0.01) - hbond.solution['results'] = actual_function_int(time) + return ( + A1 * np.exp(-t / tau1) + + A2 * np.exp(-t / tau2) + + A3 * np.exp(-t / tau3) + ) + + hbond.solution["time"] = time = np.arange(0, 6.0, 0.01) + hbond.solution["results"] = actual_function_int(time) hbond.solve() assert_almost_equal( - hbond.solution['fit'], + hbond.solution["fit"], np.array([0.33, 0.33, 5, 1, 0.1]), ) # setup errors def test_wronglength_DA(self, u, hydrogens, oxygens, nitrogens): with pytest.raises(ValueError): - HBAC(u, - hydrogens=hydrogens[:-1], - acceptors=oxygens, - donors=nitrogens, - bond_type='intermittent', - exclusions=(np.arange(len(hydrogens)), np.array( - range(len(oxygens)))), - sample_time=0.06, - ) + HBAC( + u, + hydrogens=hydrogens[:-1], + acceptors=oxygens, + donors=nitrogens, + bond_type="intermittent", + exclusions=( + np.arange(len(hydrogens)), + np.array(range(len(oxygens))), + ), + sample_time=0.06, + ) def test_exclusions(self, u, hydrogens, oxygens, nitrogens): - excl_list = (np.array(range(len(hydrogens))), np.array( - range(len(oxygens)))) + excl_list = ( + np.array(range(len(hydrogens))), + np.array(range(len(oxygens))), + ) excl_list2 = excl_list[0], excl_list[1][:-1] with pytest.raises(ValueError): - HBAC(u, - hydrogens=hydrogens, - acceptors=oxygens, - donors=nitrogens, - bond_type='intermittent', - exclusions=excl_list2, - sample_time=0.06, - ) + HBAC( + u, + hydrogens=hydrogens, + acceptors=oxygens, + donors=nitrogens, + bond_type="intermittent", + exclusions=excl_list2, + sample_time=0.06, + ) def test_bond_type_VE(self, u, hydrogens, oxygens, nitrogens): with pytest.raises(ValueError): - HBAC(u, - hydrogens=hydrogens, - acceptors=oxygens, - donors=nitrogens, - bond_type='marzipan', - exclusions=(np.arange(len(hydrogens)), np.array(range( - len(oxygens)))), - sample_time=0.06, - ) + HBAC( + u, + hydrogens=hydrogens, + acceptors=oxygens, + donors=nitrogens, + bond_type="marzipan", + exclusions=( + np.arange(len(hydrogens)), + np.array(range(len(oxygens))), + ), + sample_time=0.06, + ) def test_solve_before_run_VE(self, u, hydrogens, oxygens, nitrogens): - hbond = HBAC(u, - hydrogens=hydrogens, - acceptors=oxygens, - donors=nitrogens, - bond_type='continuous', - sample_time=0.06, + hbond = HBAC( + u, + hydrogens=hydrogens, + acceptors=oxygens, + donors=nitrogens, + bond_type="continuous", + sample_time=0.06, ) with pytest.raises(ValueError): hbond.solve() - @mock.patch('MDAnalysis.coordinates.TRZ.TRZReader._read_frame') - def test_unslicable_traj_VE(self, mock_read, u, hydrogens, oxygens, nitrogens): + @mock.patch("MDAnalysis.coordinates.TRZ.TRZReader._read_frame") + def test_unslicable_traj_VE( + self, mock_read, u, hydrogens, oxygens, nitrogens + ): mock_read.side_effect = TypeError with pytest.raises(ValueError): @@ -261,17 +332,18 @@ def test_unslicable_traj_VE(self, mock_read, u, hydrogens, oxygens, nitrogens): hydrogens=hydrogens, acceptors=oxygens, donors=nitrogens, - bond_type='continuous', - sample_time=0.06 - ) + bond_type="continuous", + sample_time=0.06, + ) def test_repr(self, u, hydrogens, oxygens, nitrogens): - hbond = HBAC(u, - hydrogens=hydrogens, - acceptors=oxygens, - donors=nitrogens, - bond_type='continuous', - sample_time=0.06, + hbond = HBAC( + u, + hydrogens=hydrogens, + acceptors=oxygens, + donors=nitrogens, + bond_type="continuous", + sample_time=0.06, ) assert isinstance(repr(hbond), str) @@ -279,7 +351,7 @@ def test_repr(self, u, hydrogens, oxygens, nitrogens): def test_find_donors(): u = mda.Universe(waterPSF, waterDCD) - H = u.select_atoms('name H*') + H = u.select_atoms("name H*") D = find_hydrogen_donors(H) diff --git a/testsuite/MDAnalysisTests/analysis/test_hydrogenbondautocorrel_deprecated.py b/testsuite/MDAnalysisTests/analysis/test_hydrogenbondautocorrel_deprecated.py index 7a372d53c54..e1822b62fa0 100644 --- a/testsuite/MDAnalysisTests/analysis/test_hydrogenbondautocorrel_deprecated.py +++ b/testsuite/MDAnalysisTests/analysis/test_hydrogenbondautocorrel_deprecated.py @@ -23,8 +23,10 @@ import pytest from MDAnalysisTests.datafiles import ( - TRZ, TRZ_psf, - waterPSF, waterDCD, + TRZ, + TRZ_psf, + waterPSF, + waterDCD, XYZ_mini, ) from numpy.testing import assert_almost_equal @@ -36,10 +38,12 @@ from MDAnalysis.analysis import hbonds from MDAnalysis.analysis.hbonds import HydrogenBondAutoCorrel as HBAC + @pytest.fixture(scope="module") def u_water(): return mda.Universe(waterPSF, waterDCD) + class TestHydrogenBondAutocorrel(object): @pytest.fixture() def u(self): @@ -47,118 +51,166 @@ def u(self): @pytest.fixture() def hydrogens(self, u): - return u.atoms.select_atoms('name Hn') + return u.atoms.select_atoms("name Hn") @pytest.fixture() def nitrogens(self, u): - return u.atoms.select_atoms('name N') + return u.atoms.select_atoms("name N") @pytest.fixture() def oxygens(self, u): - return u.atoms.select_atoms('name O') + return u.atoms.select_atoms("name O") # regression tests for different conditions def test_continuous(self, u, hydrogens, oxygens, nitrogens): - hbond = HBAC(u, - hydrogens=hydrogens, - acceptors=oxygens, - donors=nitrogens, - bond_type='continuous', - sample_time=0.06, + hbond = HBAC( + u, + hydrogens=hydrogens, + acceptors=oxygens, + donors=nitrogens, + bond_type="continuous", + sample_time=0.06, ) hbond.run() assert_almost_equal( - hbond.solution['results'], - np.array([ 1. , 0.92668623, 0.83137828, - 0.74486804, 0.67741936, 0.60263932], - dtype=np.float32) + hbond.solution["results"], + np.array( + [ + 1.0, + 0.92668623, + 0.83137828, + 0.74486804, + 0.67741936, + 0.60263932, + ], + dtype=np.float32, + ), ) def test_continuous_excl(self, u, hydrogens, oxygens, nitrogens): - hbond = HBAC(u, - hydrogens=hydrogens, - acceptors=oxygens, - donors=nitrogens, - bond_type='continuous', - exclusions=(np.arange(len(hydrogens)), np.array( - range(len(oxygens)))), - sample_time=0.06, + hbond = HBAC( + u, + hydrogens=hydrogens, + acceptors=oxygens, + donors=nitrogens, + bond_type="continuous", + exclusions=( + np.arange(len(hydrogens)), + np.array(range(len(oxygens))), + ), + sample_time=0.06, ) hbond.run() assert_almost_equal( - hbond.solution['results'], - np.array([ 1. , 0.92668623, 0.83137828, - 0.74486804, 0.67741936, 0.60263932], - dtype=np.float32) + hbond.solution["results"], + np.array( + [ + 1.0, + 0.92668623, + 0.83137828, + 0.74486804, + 0.67741936, + 0.60263932, + ], + dtype=np.float32, + ), ) - def test_intermittent(self, u, hydrogens, oxygens, nitrogens): - hbond = HBAC(u, - hydrogens=hydrogens, - acceptors=oxygens, - donors=nitrogens, - bond_type='intermittent', - sample_time=0.06, + hbond = HBAC( + u, + hydrogens=hydrogens, + acceptors=oxygens, + donors=nitrogens, + bond_type="intermittent", + sample_time=0.06, ) hbond.run() assert_almost_equal( - hbond.solution['results'], - np.array([ 1. , 0.92668623, 0.84310848, - 0.79325515, 0.76392961, 0.72287393], - dtype=np.float32) + hbond.solution["results"], + np.array( + [ + 1.0, + 0.92668623, + 0.84310848, + 0.79325515, + 0.76392961, + 0.72287393, + ], + dtype=np.float32, + ), ) - def test_intermittent_timecut(self, u, hydrogens, oxygens, nitrogens): - hbond = HBAC(u, - hydrogens=hydrogens, - acceptors=oxygens, - donors=nitrogens, - bond_type='intermittent', - time_cut=0.01, # time cut at traj.dt == continuous - sample_time=0.06, + hbond = HBAC( + u, + hydrogens=hydrogens, + acceptors=oxygens, + donors=nitrogens, + bond_type="intermittent", + time_cut=0.01, # time cut at traj.dt == continuous + sample_time=0.06, ) hbond.run() assert_almost_equal( - hbond.solution['results'], - np.array([ 1. , 0.92668623, 0.83137828, - 0.74486804, 0.67741936, 0.60263932], - dtype=np.float32) + hbond.solution["results"], + np.array( + [ + 1.0, + 0.92668623, + 0.83137828, + 0.74486804, + 0.67741936, + 0.60263932, + ], + dtype=np.float32, + ), ) def test_intermittent_excl(self, u, hydrogens, oxygens, nitrogens): - hbond = HBAC(u, - hydrogens=hydrogens, - acceptors=oxygens, - donors=nitrogens, - bond_type='intermittent', - exclusions=(np.arange(len(hydrogens)), np.array( - range(len(oxygens)))), - sample_time=0.06, + hbond = HBAC( + u, + hydrogens=hydrogens, + acceptors=oxygens, + donors=nitrogens, + bond_type="intermittent", + exclusions=( + np.arange(len(hydrogens)), + np.array(range(len(oxygens))), + ), + sample_time=0.06, ) hbond.run() assert_almost_equal( - hbond.solution['results'], - np.array([ 1. , 0.92668623, 0.84310848, - 0.79325515, 0.76392961, 0.72287393], - dtype=np.float32) + hbond.solution["results"], + np.array( + [ + 1.0, + 0.92668623, + 0.84310848, + 0.79325515, + 0.76392961, + 0.72287393, + ], + dtype=np.float32, + ), ) # For `solve` the test trajectories aren't long enough # So spoof the results and check that solver finds solution def test_solve_continuous(self, u, hydrogens, oxygens, nitrogens): - hbond = HBAC(u, - hydrogens=hydrogens, - acceptors=oxygens, - donors=nitrogens, - bond_type='continuous', - sample_time=0.06, + hbond = HBAC( + u, + hydrogens=hydrogens, + acceptors=oxygens, + donors=nitrogens, + bond_type="continuous", + sample_time=0.06, ) def actual_function_cont(t): @@ -166,24 +218,26 @@ def actual_function_cont(t): A2 = 0.25 tau1 = 0.5 tau2 = 0.1 - return A1 * np.exp(-t/tau1) + A2 * np.exp(-t/tau2) - hbond.solution['time'] = time = np.arange(0, 0.06, 0.001) - hbond.solution['results'] = actual_function_cont(time) + return A1 * np.exp(-t / tau1) + A2 * np.exp(-t / tau2) + + hbond.solution["time"] = time = np.arange(0, 0.06, 0.001) + hbond.solution["results"] = actual_function_cont(time) hbond.solve() assert_almost_equal( - hbond.solution['fit'], + hbond.solution["fit"], np.array([0.75, 0.5, 0.1]), ) def test_solve_intermittent(self, u, hydrogens, oxygens, nitrogens): - hbond = HBAC(u, - hydrogens=hydrogens, - acceptors=oxygens, - donors=nitrogens, - bond_type='intermittent', - sample_time=0.06, + hbond = HBAC( + u, + hydrogens=hydrogens, + acceptors=oxygens, + donors=nitrogens, + bond_type="intermittent", + sample_time=0.06, ) def actual_function_int(t): @@ -193,69 +247,86 @@ def actual_function_int(t): tau1 = 5 tau2 = 1 tau3 = 0.1 - return A1 * np.exp(-t/tau1) + A2 * np.exp(-t/tau2) + A3 * np.exp(-t/tau3) - hbond.solution['time'] = time = np.arange(0, 6.0, 0.01) - hbond.solution['results'] = actual_function_int(time) + return ( + A1 * np.exp(-t / tau1) + + A2 * np.exp(-t / tau2) + + A3 * np.exp(-t / tau3) + ) + + hbond.solution["time"] = time = np.arange(0, 6.0, 0.01) + hbond.solution["results"] = actual_function_int(time) hbond.solve() assert_almost_equal( - hbond.solution['fit'], + hbond.solution["fit"], np.array([0.33, 0.33, 5, 1, 0.1]), ) # setup errors def test_wronglength_DA(self, u, hydrogens, oxygens, nitrogens): with pytest.raises(ValueError): - HBAC(u, - hydrogens=hydrogens[:-1], - acceptors=oxygens, - donors=nitrogens, - bond_type='intermittent', - exclusions=(np.arange(len(hydrogens)), np.array( - range(len(oxygens)))), - sample_time=0.06, - ) + HBAC( + u, + hydrogens=hydrogens[:-1], + acceptors=oxygens, + donors=nitrogens, + bond_type="intermittent", + exclusions=( + np.arange(len(hydrogens)), + np.array(range(len(oxygens))), + ), + sample_time=0.06, + ) def test_exclusions(self, u, hydrogens, oxygens, nitrogens): - excl_list = (np.array(range(len(hydrogens))), np.array( - range(len(oxygens)))) + excl_list = ( + np.array(range(len(hydrogens))), + np.array(range(len(oxygens))), + ) excl_list2 = excl_list[0], excl_list[1][:-1] with pytest.raises(ValueError): - HBAC(u, - hydrogens=hydrogens, - acceptors=oxygens, - donors=nitrogens, - bond_type='intermittent', - exclusions=excl_list2, - sample_time=0.06, - ) + HBAC( + u, + hydrogens=hydrogens, + acceptors=oxygens, + donors=nitrogens, + bond_type="intermittent", + exclusions=excl_list2, + sample_time=0.06, + ) def test_bond_type_VE(self, u, hydrogens, oxygens, nitrogens): with pytest.raises(ValueError): - HBAC(u, - hydrogens=hydrogens, - acceptors=oxygens, - donors=nitrogens, - bond_type='marzipan', - exclusions=(np.arange(len(hydrogens)), np.array(range( - len(oxygens)))), - sample_time=0.06, - ) + HBAC( + u, + hydrogens=hydrogens, + acceptors=oxygens, + donors=nitrogens, + bond_type="marzipan", + exclusions=( + np.arange(len(hydrogens)), + np.array(range(len(oxygens))), + ), + sample_time=0.06, + ) def test_solve_before_run_VE(self, u, hydrogens, oxygens, nitrogens): - hbond = HBAC(u, - hydrogens=hydrogens, - acceptors=oxygens, - donors=nitrogens, - bond_type='continuous', - sample_time=0.06, + hbond = HBAC( + u, + hydrogens=hydrogens, + acceptors=oxygens, + donors=nitrogens, + bond_type="continuous", + sample_time=0.06, ) with pytest.raises(ValueError): hbond.solve() - @mock.patch('MDAnalysis.coordinates.TRZ.TRZReader._read_frame') - def test_unslicable_traj_VE(self, mock_read, u, hydrogens, oxygens, nitrogens): + @mock.patch("MDAnalysis.coordinates.TRZ.TRZReader._read_frame") + def test_unslicable_traj_VE( + self, mock_read, u, hydrogens, oxygens, nitrogens + ): mock_read.side_effect = TypeError with pytest.raises(ValueError): @@ -264,38 +335,40 @@ def test_unslicable_traj_VE(self, mock_read, u, hydrogens, oxygens, nitrogens): hydrogens=hydrogens, acceptors=oxygens, donors=nitrogens, - bond_type='continuous', - sample_time=0.06 - ) + bond_type="continuous", + sample_time=0.06, + ) def test_repr(self, u, hydrogens, oxygens, nitrogens): - hbond = HBAC(u, - hydrogens=hydrogens, - acceptors=oxygens, - donors=nitrogens, - bond_type='continuous', - sample_time=0.06, + hbond = HBAC( + u, + hydrogens=hydrogens, + acceptors=oxygens, + donors=nitrogens, + bond_type="continuous", + sample_time=0.06, ) assert isinstance(repr(hbond), str) def test_deprecation_warning(self, u, hydrogens, oxygens, nitrogens): - wmsg = ('`HydrogenBondAutoCorrel` is deprecated!\n' - '`HydrogenBondAutoCorrel` will be removed in release 3.0.0.\n' - 'The class was moved to MDAnalysis.analysis.hbonds.hbond_autocorrel.') + wmsg = ( + "`HydrogenBondAutoCorrel` is deprecated!\n" + "`HydrogenBondAutoCorrel` will be removed in release 3.0.0.\n" + "The class was moved to MDAnalysis.analysis.hbonds.hbond_autocorrel." + ) with pytest.warns(DeprecationWarning, match=wmsg): HBAC( u, hydrogens=hydrogens, acceptors=oxygens, donors=nitrogens, - bond_type='continuous', - sample_time=0.06 + bond_type="continuous", + sample_time=0.06, ) - def test_find_donors(u_water): - H = u_water.select_atoms('name H*') + H = u_water.select_atoms("name H*") D = hbonds.find_hydrogen_donors(H) @@ -313,19 +386,21 @@ def test_donors_nobonds(): def test_find_hydrogen_donors_deprecation_warning(u_water): - H = u_water.select_atoms('name H*') - wmsg = ('`find_hydrogen_donors` is deprecated!\n' - '`find_hydrogen_donors` will be removed in release 3.0.0.\n' - 'The function was moved to MDAnalysis.analysis.hbonds.hbond_autocorrel.') + H = u_water.select_atoms("name H*") + wmsg = ( + "`find_hydrogen_donors` is deprecated!\n" + "`find_hydrogen_donors` will be removed in release 3.0.0.\n" + "The function was moved to MDAnalysis.analysis.hbonds.hbond_autocorrel." + ) with pytest.warns(DeprecationWarning, match=wmsg): hbonds.find_hydrogen_donors(H) def test_moved_module_warning(): - wmsg = ("This module was moved to " - "MDAnalysis.analysis.hydrogenbonds.hbond_autocorrel; " - "hbonds.hbond_autocorrel will be removed in 3.0.0.") + wmsg = ( + "This module was moved to " + "MDAnalysis.analysis.hydrogenbonds.hbond_autocorrel; " + "hbonds.hbond_autocorrel will be removed in 3.0.0." + ) with pytest.warns(DeprecationWarning, match=wmsg): reload(hbonds.hbond_autocorrel) - - diff --git a/testsuite/MDAnalysisTests/analysis/test_hydrogenbonds_analysis.py b/testsuite/MDAnalysisTests/analysis/test_hydrogenbonds_analysis.py index bef6b03331d..a731dd6ec93 100644 --- a/testsuite/MDAnalysisTests/analysis/test_hydrogenbonds_analysis.py +++ b/testsuite/MDAnalysisTests/analysis/test_hydrogenbonds_analysis.py @@ -25,13 +25,18 @@ import pytest import copy -from numpy.testing import (assert_allclose, assert_equal, - assert_array_almost_equal, assert_array_equal, - assert_almost_equal) +from numpy.testing import ( + assert_allclose, + assert_equal, + assert_array_almost_equal, + assert_array_equal, + assert_almost_equal, +) import MDAnalysis from MDAnalysis.analysis.hydrogenbonds.hbond_analysis import ( - HydrogenBondAnalysis) + HydrogenBondAnalysis, +) from MDAnalysis.exceptions import NoDataError from MDAnalysisTests.datafiles import waterPSF, waterDCD @@ -39,20 +44,20 @@ class TestHydrogenBondAnalysisTIP3P(object): @staticmethod - @pytest.fixture(scope='class') + @pytest.fixture(scope="class") def universe(): return MDAnalysis.Universe(waterPSF, waterDCD) kwargs = { - 'donors_sel': 'name OH2', - 'hydrogens_sel': 'name H1 H2', - 'acceptors_sel': 'name OH2', - 'd_h_cutoff': 1.2, - 'd_a_cutoff': 3.0, - 'd_h_a_angle_cutoff': 120.0 + "donors_sel": "name OH2", + "hydrogens_sel": "name H1 H2", + "acceptors_sel": "name OH2", + "d_h_cutoff": 1.2, + "d_a_cutoff": 3.0, + "d_h_a_angle_cutoff": 120.0, } - @pytest.fixture(scope='class') + @pytest.fixture(scope="class") def h(self, universe, client_HydrogenBondAnalysis): h = HydrogenBondAnalysis(universe, **self.kwargs) h.run(**client_HydrogenBondAnalysis) @@ -64,18 +69,22 @@ def test_hbond_analysis(self, h): assert len(h.results.hbonds) == 32 reference = { - 'distance': {'mean': 2.7627309, 'std': 0.0905052}, - 'angle': {'mean': 158.9038039, 'std': 12.0362826}, + "distance": {"mean": 2.7627309, "std": 0.0905052}, + "angle": {"mean": 158.9038039, "std": 12.0362826}, } - assert_allclose(np.mean(h.results.hbonds[:, 4]), - reference['distance']['mean']) - assert_allclose(np.std(h.results.hbonds[:, 4]), - reference['distance']['std']) - assert_allclose(np.mean(h.results.hbonds[:, 5]), - reference['angle']['mean']) - assert_allclose(np.std(h.results.hbonds[:, 5]), - reference['angle']['std']) + assert_allclose( + np.mean(h.results.hbonds[:, 4]), reference["distance"]["mean"] + ) + assert_allclose( + np.std(h.results.hbonds[:, 4]), reference["distance"]["std"] + ) + assert_allclose( + np.mean(h.results.hbonds[:, 5]), reference["angle"]["mean"] + ) + assert_allclose( + np.std(h.results.hbonds[:, 5]), reference["angle"]["std"] + ) def test_count_by_time(self, h): @@ -100,7 +109,7 @@ def test_count_by_ids(self, h, universe): unique_hbonds = h.count_by_ids() most_common_hbond_ids = [12, 14, 9] - assert_equal(unique_hbonds[0,:3], most_common_hbond_ids) + assert_equal(unique_hbonds[0, :3], most_common_hbond_ids) # count_by_ids() returns raw counts # convert to fraction of time that bond was observed @@ -117,16 +126,16 @@ def test_hbonds_deprecated_attr(self, h): class TestHydrogenBondAnalysisIdeal(object): kwargs = { - 'donors_sel': 'name O', - 'hydrogens_sel': 'name H1 H2', - 'acceptors_sel': 'name O', - 'd_h_cutoff': 1.2, - 'd_a_cutoff': 3.0, - 'd_h_a_angle_cutoff': 120.0 + "donors_sel": "name O", + "hydrogens_sel": "name H1 H2", + "acceptors_sel": "name O", + "d_h_cutoff": 1.2, + "d_a_cutoff": 3.0, + "d_h_a_angle_cutoff": 120.0, } @staticmethod - @pytest.fixture(scope='class') + @pytest.fixture(scope="class") def universe(): # create two water molecules """ @@ -138,53 +147,56 @@ def universe(): """ n_residues = 2 u = MDAnalysis.Universe.empty( - n_atoms=n_residues*3, + n_atoms=n_residues * 3, n_residues=n_residues, atom_resindex=np.repeat(range(n_residues), 3), residue_segindex=[0] * n_residues, trajectory=True, # necessary for adding coordinates - ) + ) - u.add_TopologyAttr('name', ['O', 'H1', 'H2'] * n_residues) - u.add_TopologyAttr('type', ['O', 'H', 'H'] * n_residues) - u.add_TopologyAttr('resname', ['SOL'] * n_residues) - u.add_TopologyAttr('resid', list(range(1, n_residues + 1))) - u.add_TopologyAttr('id', list(range(1, (n_residues * 3) + 1))) + u.add_TopologyAttr("name", ["O", "H1", "H2"] * n_residues) + u.add_TopologyAttr("type", ["O", "H", "H"] * n_residues) + u.add_TopologyAttr("resname", ["SOL"] * n_residues) + u.add_TopologyAttr("resid", list(range(1, n_residues + 1))) + u.add_TopologyAttr("id", list(range(1, (n_residues * 3) + 1))) # Atomic coordinates with a single hydrogen bond between O1-H2---O2 - pos1 = np.array([[0, 0, 0], # O1 - [-0.249, -0.968, 0], # H1 - [1, 0, 0], # H2 - [2.5, 0, 0], # O2 - [3., 0, 0], # H3 - [2.250, 0.968, 0] # H4 - ]) + pos1 = np.array( + [ + [0, 0, 0], # O1 + [-0.249, -0.968, 0], # H1 + [1, 0, 0], # H2 + [2.5, 0, 0], # O2 + [3.0, 0, 0], # H3 + [2.250, 0.968, 0], # H4 + ] + ) # Atomic coordinates with no hydrogen bonds - pos2 = np.array([[0, 0, 0], # O1 - [-0.249, -0.968, 0], # H1 - [1, 0, 0], # H2 - [4.5, 0, 0], # O2 - [5., 0, 0], # H3 - [4.250, 0.968, 0] # H4 - ]) - - coordinates = np.empty((3, # number of frames - u.atoms.n_atoms, - 3)) + pos2 = np.array( + [ + [0, 0, 0], # O1 + [-0.249, -0.968, 0], # H1 + [1, 0, 0], # H2 + [4.5, 0, 0], # O2 + [5.0, 0, 0], # H3 + [4.250, 0.968, 0], # H4 + ] + ) + + coordinates = np.empty((3, u.atoms.n_atoms, 3)) # number of frames coordinates[0] = pos1 coordinates[1] = pos2 coordinates[2] = pos1 - u.load_new(coordinates, order='fac') + u.load_new(coordinates, order="fac") return u @staticmethod - @pytest.fixture(scope='class') + @pytest.fixture(scope="class") def hydrogen_bonds(universe, client_HydrogenBondAnalysis): h = HydrogenBondAnalysis( - universe, - **TestHydrogenBondAnalysisIdeal.kwargs + universe, **TestHydrogenBondAnalysisIdeal.kwargs ) h.run(**client_HydrogenBondAnalysis) return h @@ -200,42 +212,48 @@ def test_count_by_type(self, hydrogen_bonds): def test_no_bond_info_exception(self, universe): kwargs = { - 'donors_sel': None, - 'hydrogens_sel': None, - 'acceptors_sel': None, - 'd_h_cutoff': 1.2, - 'd_a_cutoff': 3.0, - 'd_h_a_angle_cutoff': 120.0 + "donors_sel": None, + "hydrogens_sel": None, + "acceptors_sel": None, + "d_h_cutoff": 1.2, + "d_a_cutoff": 3.0, + "d_h_a_angle_cutoff": 120.0, } u = universe.copy() n_residues = 2 - u.add_TopologyAttr('mass', [15.999, 1.008, 1.008] * n_residues) - u.add_TopologyAttr('charge', [-1.04, 0.52, 0.52] * n_residues) + u.add_TopologyAttr("mass", [15.999, 1.008, 1.008] * n_residues) + u.add_TopologyAttr("charge", [-1.04, 0.52, 0.52] * n_residues) with pytest.raises(NoDataError, match="no bond information"): h = HydrogenBondAnalysis(u, **kwargs) def test_no_bond_donor_sel(self, universe): kwargs = { - 'donors_sel': "type O", - 'hydrogens_sel': None, - 'acceptors_sel': None, - 'd_h_cutoff': 1.2, - 'd_a_cutoff': 3.0, - 'd_h_a_angle_cutoff': 120.0 + "donors_sel": "type O", + "hydrogens_sel": None, + "acceptors_sel": None, + "d_h_cutoff": 1.2, + "d_a_cutoff": 3.0, + "d_h_a_angle_cutoff": 120.0, } u = universe.copy() n_residues = 2 - u.add_TopologyAttr('mass', [15.999, 1.008, 1.008] * n_residues) - u.add_TopologyAttr('charge', [-1.04, 0.52, 0.52] * n_residues) + u.add_TopologyAttr("mass", [15.999, 1.008, 1.008] * n_residues) + u.add_TopologyAttr("charge", [-1.04, 0.52, 0.52] * n_residues) h = HydrogenBondAnalysis(u, **kwargs) donors = u.select_atoms(h.guess_donors()) def test_first_hbond(self, hydrogen_bonds): assert len(hydrogen_bonds.results.hbonds) == 2 - frame_no, donor_index, hydrogen_index, acceptor_index, da_dst, angle =\ - hydrogen_bonds.results.hbonds[0] + ( + frame_no, + donor_index, + hydrogen_index, + acceptor_index, + da_dst, + angle, + ) = hydrogen_bonds.results.hbonds[0] assert_equal(donor_index, 0) assert_equal(hydrogen_index, 2) assert_equal(acceptor_index, 3) @@ -243,7 +261,7 @@ def test_first_hbond(self, hydrogen_bonds): assert_almost_equal(angle, 180) def test_count_by_time(self, hydrogen_bonds): - ref_times = np.array([0, 1, 2]) # u.trajectory.dt is 1 + ref_times = np.array([0, 1, 2]) # u.trajectory.dt is 1 ref_counts = np.array([1, 0, 1]) counts = hydrogen_bonds.count_by_time() @@ -266,8 +284,9 @@ def test_no_attr_hbonds(self, universe): with pytest.raises(NoDataError, match=".hbonds attribute is None"): hbonds.lifetime(tau_max=2, intermittency=1) - def test_logging_step_not_1(self, universe, caplog, - client_HydrogenBondAnalysis): + def test_logging_step_not_1( + self, universe, caplog, client_HydrogenBondAnalysis + ): hbonds = HydrogenBondAnalysis(universe, **self.kwargs) # using step 2 hbonds.run(**client_HydrogenBondAnalysis, step=2) @@ -275,24 +294,25 @@ def test_logging_step_not_1(self, universe, caplog, caplog.set_level(logging.WARNING) hbonds.lifetime(tau_max=2, intermittency=1) - warning = ("Autocorrelation: Hydrogen bonds were computed with " - "step > 1.") + warning = ( + "Autocorrelation: Hydrogen bonds were computed with " "step > 1." + ) assert any(warning in rec.getMessage() for rec in caplog.records) class TestHydrogenBondAnalysisNoRes(TestHydrogenBondAnalysisIdeal): kwargs = { - 'donors_sel': 'type O', -# 'hydrogens_sel': 'type H H', - 'acceptors_sel': 'type O', - 'd_h_cutoff': 1.2, - 'd_a_cutoff': 3.0, - 'd_h_a_angle_cutoff': 120.0 + "donors_sel": "type O", + # 'hydrogens_sel': 'type H H', + "acceptors_sel": "type O", + "d_h_cutoff": 1.2, + "d_a_cutoff": 3.0, + "d_h_a_angle_cutoff": 120.0, } @staticmethod - @pytest.fixture(scope='class', autouse=True) + @pytest.fixture(scope="class", autouse=True) def universe(): # create two water molecules """ @@ -304,52 +324,55 @@ def universe(): """ n_residues = 2 u = MDAnalysis.Universe.empty( - n_atoms=n_residues*3, + n_atoms=n_residues * 3, n_residues=n_residues, atom_resindex=np.repeat(range(n_residues), 3), residue_segindex=[0] * n_residues, trajectory=True, # necessary for adding coordinates - ) + ) - u.add_TopologyAttr('type', ['O', 'H', 'H'] * n_residues) - u.add_TopologyAttr('id', list(range(1, (n_residues * 3) + 1))) - u.add_TopologyAttr('mass', [15.999, 1.008, 1.008] * n_residues) - u.add_TopologyAttr('charge', [-1.04, 0.52, 0.52] * n_residues) + u.add_TopologyAttr("type", ["O", "H", "H"] * n_residues) + u.add_TopologyAttr("id", list(range(1, (n_residues * 3) + 1))) + u.add_TopologyAttr("mass", [15.999, 1.008, 1.008] * n_residues) + u.add_TopologyAttr("charge", [-1.04, 0.52, 0.52] * n_residues) # Atomic coordinates with a single hydrogen bond between O1-H2---O2 - pos1 = np.array([[0, 0, 0], # O1 - [-0.249, -0.968, 0], # H1 - [1, 0, 0], # H2 - [2.5, 0, 0], # O2 - [3., 0, 0], # H3 - [2.250, 0.968, 0] # H4 - ]) + pos1 = np.array( + [ + [0, 0, 0], # O1 + [-0.249, -0.968, 0], # H1 + [1, 0, 0], # H2 + [2.5, 0, 0], # O2 + [3.0, 0, 0], # H3 + [2.250, 0.968, 0], # H4 + ] + ) # Atomic coordinates with no hydrogen bonds - pos2 = np.array([[0, 0, 0], # O1 - [-0.249, -0.968, 0], # H1 - [1, 0, 0], # H2 - [4.5, 0, 0], # O2 - [5., 0, 0], # H3 - [4.250, 0.968, 0] # H4 - ]) - - coordinates = np.empty((3, # number of frames - u.atoms.n_atoms, - 3)) + pos2 = np.array( + [ + [0, 0, 0], # O1 + [-0.249, -0.968, 0], # H1 + [1, 0, 0], # H2 + [4.5, 0, 0], # O2 + [5.0, 0, 0], # H3 + [4.250, 0.968, 0], # H4 + ] + ) + + coordinates = np.empty((3, u.atoms.n_atoms, 3)) # number of frames coordinates[0] = pos1 coordinates[1] = pos2 coordinates[2] = pos1 - u.load_new(coordinates, order='fac') + u.load_new(coordinates, order="fac") return u @staticmethod - @pytest.fixture(scope='class') + @pytest.fixture(scope="class") def hydrogen_bonds(universe, client_HydrogenBondAnalysis): h = HydrogenBondAnalysis( - universe, - **TestHydrogenBondAnalysisNoRes.kwargs + universe, **TestHydrogenBondAnalysisNoRes.kwargs ) h.run(**client_HydrogenBondAnalysis) return h @@ -359,26 +382,30 @@ def test_no_hydrogen_bonds(self, universe): tmp_kwargs["d_h_a_angle_cutoff"] = 50 hbonds = HydrogenBondAnalysis(universe, **tmp_kwargs) - with pytest.warns(UserWarning, - match=("No hydrogen bonds were found given angle " - "of 50 between Donor, type O, and Acceptor," - " type O.")): + with pytest.warns( + UserWarning, + match=( + "No hydrogen bonds were found given angle " + "of 50 between Donor, type O, and Acceptor," + " type O." + ), + ): hbonds.run(step=1) class TestHydrogenBondAnalysisBetween(object): kwargs = { - 'donors_sel': 'name O P', - 'hydrogens_sel': 'name H1 H2 PH', - 'acceptors_sel': 'name O P', - 'd_h_cutoff': 1.2, - 'd_a_cutoff': 3.0, - 'd_h_a_angle_cutoff': 120.0 + "donors_sel": "name O P", + "hydrogens_sel": "name H1 H2 PH", + "acceptors_sel": "name O P", + "d_h_cutoff": 1.2, + "d_a_cutoff": 3.0, + "d_h_a_angle_cutoff": 120.0, } @staticmethod - @pytest.fixture(scope='class') + @pytest.fixture(scope="class") def universe(): # create two water molecules and two "protein" molecules # P1-PH1 are the two atoms that comprise the toy protein PROT1 @@ -393,55 +420,52 @@ def universe(): n_sol_residues = 2 n_prot_residues = 2 u = MDAnalysis.Universe.empty( - n_atoms=n_sol_residues*3 + n_prot_residues*2, + n_atoms=n_sol_residues * 3 + n_prot_residues * 2, n_residues=n_residues, atom_resindex=[0, 0, 0, 1, 1, 1, 2, 2, 3, 3], residue_segindex=[0, 0, 1, 1], trajectory=True, # necessary for adding coordinates - ) - - u.add_TopologyAttr( - 'name', - ['O', 'H1', 'H2'] * n_sol_residues + ['P', 'PH'] * n_prot_residues ) + u.add_TopologyAttr( - 'type', - ['O', 'H', 'H'] * n_sol_residues + ['P', 'PH'] * n_prot_residues + "name", + ["O", "H1", "H2"] * n_sol_residues + ["P", "PH"] * n_prot_residues, ) u.add_TopologyAttr( - 'resname', - ['SOL'] * n_sol_residues + ['PROT'] * n_prot_residues + "type", + ["O", "H", "H"] * n_sol_residues + ["P", "PH"] * n_prot_residues, ) u.add_TopologyAttr( - 'resid', - list(range(1, n_residues + 1)) + "resname", ["SOL"] * n_sol_residues + ["PROT"] * n_prot_residues ) + u.add_TopologyAttr("resid", list(range(1, n_residues + 1))) u.add_TopologyAttr( - 'id', - list(range(1, (n_sol_residues * 3 + n_prot_residues * 2) + 1)) + "id", + list(range(1, (n_sol_residues * 3 + n_prot_residues * 2) + 1)), ) # Atomic coordinates with hydrogen bonds between: # O1-H2---O2 # O2-H3---P1 # P1-PH1---P2 - pos = np.array([[0, 0, 0], # O1 - [-0.249, -0.968, 0], # H1 - [1, 0, 0], # H2 - [2.5, 0, 0], # O2 - [3., 0, 0], # H3 - [2.250, 0.968, 0], # H4 - [5.5, 0, 0], # P1 - [6.5, 0, 0], # PH1 - [8.5, 0, 0], # P2 - [9.5, 0, 0], # PH2 - ]) - - coordinates = np.empty((1, # number of frames - u.atoms.n_atoms, - 3)) + pos = np.array( + [ + [0, 0, 0], # O1 + [-0.249, -0.968, 0], # H1 + [1, 0, 0], # H2 + [2.5, 0, 0], # O2 + [3.0, 0, 0], # H3 + [2.250, 0.968, 0], # H4 + [5.5, 0, 0], # P1 + [6.5, 0, 0], # PH1 + [8.5, 0, 0], # P2 + [9.5, 0, 0], # PH2 + ] + ) + + coordinates = np.empty((1, u.atoms.n_atoms, 3)) # number of frames coordinates[0] = pos - u.load_new(coordinates, order='fac') + u.load_new(coordinates, order="fac") return u @@ -454,29 +478,27 @@ def test_between_all(self, universe, client_HydrogenBondAnalysis): expected_hbond_indices = [ [0, 2, 3], # water-water [3, 4, 6], # protein-water - [6, 7, 8] # protein-protein + [6, 7, 8], # protein-protein ] expected_hbond_distances = [2.5, 3.0, 3.0] - assert_array_equal(hbonds.results.hbonds[:, 1:4], - expected_hbond_indices) + assert_array_equal( + hbonds.results.hbonds[:, 1:4], expected_hbond_indices + ) assert_allclose(hbonds.results.hbonds[:, 4], expected_hbond_distances) def test_between_PW(self, universe, client_HydrogenBondAnalysis): # Find only protein-water hydrogen bonds hbonds = HydrogenBondAnalysis( - universe, - between=["resname PROT", "resname SOL"], - **self.kwargs + universe, between=["resname PROT", "resname SOL"], **self.kwargs ) hbonds.run(**client_HydrogenBondAnalysis) # indices of [donor, hydrogen, acceptor] for each hydrogen bond - expected_hbond_indices = [ - [3, 4, 6] # protein-water - ] + expected_hbond_indices = [[3, 4, 6]] # protein-water expected_hbond_distances = [3.0] - assert_array_equal(hbonds.results.hbonds[:, 1:4], - expected_hbond_indices) + assert_array_equal( + hbonds.results.hbonds[:, 1:4], expected_hbond_indices + ) assert_allclose(hbonds.results.hbonds[:, 4], expected_hbond_distances) def test_between_PW_PP(self, universe, client_HydrogenBondAnalysis): @@ -486,42 +508,46 @@ def test_between_PW_PP(self, universe, client_HydrogenBondAnalysis): universe, between=[ ["resname PROT", "resname SOL"], - ["resname PROT", "resname PROT"] + ["resname PROT", "resname PROT"], ], - **self.kwargs + **self.kwargs, ) hbonds.run(**client_HydrogenBondAnalysis) # indices of [donor, hydrogen, acceptor] for each hydrogen bond expected_hbond_indices = [ [3, 4, 6], # protein-water - [6, 7, 8] # protein-protein + [6, 7, 8], # protein-protein ] expected_hbond_distances = [3.0, 3.0] - assert_array_equal(hbonds.results.hbonds[:, 1:4], - expected_hbond_indices) + assert_array_equal( + hbonds.results.hbonds[:, 1:4], expected_hbond_indices + ) assert_allclose(hbonds.results.hbonds[:, 4], expected_hbond_distances) -class TestHydrogenBondAnalysisTIP3P_GuessAcceptors_GuessHydrogens_UseTopology_(TestHydrogenBondAnalysisTIP3P): +class TestHydrogenBondAnalysisTIP3P_GuessAcceptors_GuessHydrogens_UseTopology_( + TestHydrogenBondAnalysisTIP3P +): """Uses the same distance and cutoff hydrogen bond criteria as :class:`TestHydrogenBondAnalysisTIP3P`, so the results are identical, but the hydrogens and acceptors are guessed whilst the donor-hydrogen pairs are determined via the topology. """ + kwargs = { - 'donors_sel': None, - 'hydrogens_sel': None, - 'acceptors_sel': None, - 'd_a_cutoff': 3.0, - 'd_h_a_angle_cutoff': 120.0 + "donors_sel": None, + "hydrogens_sel": None, + "acceptors_sel": None, + "d_a_cutoff": 3.0, + "d_h_a_angle_cutoff": 120.0, } def test_no_hydrogens(self, universe, client_HydrogenBondAnalysis): # If no hydrogens are identified at a given frame, check an # empty donor atom group is created test_kwargs = TestHydrogenBondAnalysisTIP3P.kwargs.copy() - test_kwargs['donors_sel'] = None # use topology to find pairs - test_kwargs['hydrogens_sel'] = "name H" # no atoms have name H + test_kwargs["donors_sel"] = None # use topology to find pairs + test_kwargs["hydrogens_sel"] = "name H" # no atoms have name H h = HydrogenBondAnalysis(universe, **test_kwargs) h.run(**client_HydrogenBondAnalysis) @@ -532,24 +558,23 @@ def test_no_hydrogens(self, universe, client_HydrogenBondAnalysis): class TestHydrogenBondAnalysisTIP3P_GuessDonors_NoTopology(object): - """Guess the donor atoms involved in hydrogen bonds using the partial charges of the atoms. - """ + """Guess the donor atoms involved in hydrogen bonds using the partial charges of the atoms.""" @staticmethod - @pytest.fixture(scope='class') + @pytest.fixture(scope="class") def universe(): return MDAnalysis.Universe(waterPSF, waterDCD) kwargs = { - 'donors_sel': None, - 'hydrogens_sel': None, - 'acceptors_sel': None, - 'd_h_cutoff': 1.2, - 'd_a_cutoff': 3.0, - 'd_h_a_angle_cutoff': 120.0 + "donors_sel": None, + "hydrogens_sel": None, + "acceptors_sel": None, + "d_h_cutoff": 1.2, + "d_a_cutoff": 3.0, + "d_h_a_angle_cutoff": 120.0, } - @pytest.fixture(scope='class') + @pytest.fixture(scope="class") def h(self, universe): h = HydrogenBondAnalysis(universe, **self.kwargs) return h @@ -557,7 +582,7 @@ def h(self, universe): def test_guess_donors(self, h): ref_donors = "(resname TIP3 and name OH2)" - donors = h.guess_donors(select='all', max_charge=-0.5) + donors = h.guess_donors(select="all", max_charge=-0.5) assert donors == ref_donors @@ -568,41 +593,40 @@ class TestHydrogenBondAnalysisTIP3P_GuessHydrogens_NoTopology(object): """ @staticmethod - @pytest.fixture(scope='class') + @pytest.fixture(scope="class") def universe(): return MDAnalysis.Universe(waterPSF, waterDCD) kwargs = { - 'donors_sel': None, - 'hydrogens_sel': None, - 'acceptors_sel': None, - 'd_h_cutoff': 1.2, - 'd_a_cutoff': 3.0, - 'd_h_a_angle_cutoff': 120.0 + "donors_sel": None, + "hydrogens_sel": None, + "acceptors_sel": None, + "d_h_cutoff": 1.2, + "d_a_cutoff": 3.0, + "d_h_a_angle_cutoff": 120.0, } - @pytest.fixture(scope='class') + @pytest.fixture(scope="class") def h(self, universe): h = HydrogenBondAnalysis(universe, **self.kwargs) return h def test_guess_hydrogens(self, h): - ref_hydrogens = "(resname TIP3 and name H1) or (resname TIP3 and name H2)" - hydrogens = h.guess_hydrogens(select='all') + ref_hydrogens = ( + "(resname TIP3 and name H1) or (resname TIP3 and name H2)" + ) + hydrogens = h.guess_hydrogens(select="all") assert hydrogens == ref_hydrogens pytest.mark.parametrize( "min_mass, max_mass, min_charge", - [ - (1.05, 1.10, 0.30), - (0.90, 0.95, 0.30), - (0.90, 1.10, 1.00) - ] + [(1.05, 1.10, 0.30), (0.90, 0.95, 0.30), (0.90, 1.10, 1.00)], ) + def test_guess_hydrogens_empty_selection(self, h): - hydrogens = h.guess_hydrogens(select='all', min_charge=1.0) + hydrogens = h.guess_hydrogens(select="all", min_charge=1.0) assert hydrogens == "" def test_guess_hydrogens_min_max_mass(self, h): @@ -611,7 +635,8 @@ def test_guess_hydrogens_min_max_mass(self, h): with pytest.raises(ValueError, match=errmsg): - h.guess_hydrogens(select='all', min_mass=1.1, max_mass=0.9) + h.guess_hydrogens(select="all", min_mass=1.1, max_mass=0.9) + class TestHydrogenBondAnalysisTIP3PStartStep(object): """Uses the same distance and cutoff hydrogen bond criteria as :class:`TestHydrogenBondAnalysisTIP3P` but starting @@ -619,20 +644,20 @@ class TestHydrogenBondAnalysisTIP3PStartStep(object): """ @staticmethod - @pytest.fixture(scope='class') + @pytest.fixture(scope="class") def universe(): return MDAnalysis.Universe(waterPSF, waterDCD) kwargs = { - 'donors_sel': 'name OH2', - 'hydrogens_sel': 'name H1 H2', - 'acceptors_sel': 'name OH2', - 'd_h_cutoff': 1.2, - 'd_a_cutoff': 3.0, - 'd_h_a_angle_cutoff': 120.0 + "donors_sel": "name OH2", + "hydrogens_sel": "name H1 H2", + "acceptors_sel": "name OH2", + "d_h_cutoff": 1.2, + "d_a_cutoff": 3.0, + "d_h_a_angle_cutoff": 120.0, } - @pytest.fixture(scope='class') + @pytest.fixture(scope="class") def h(self, universe, client_HydrogenBondAnalysis): h = HydrogenBondAnalysis(universe, **self.kwargs) h.run(**client_HydrogenBondAnalysis, start=1, step=2) @@ -644,22 +669,34 @@ def test_hbond_analysis(self, h): assert len(h.results.hbonds) == 15 reference = { - 'distance': {'mean': 2.73942464, 'std': 0.05867924}, - 'angle': {'mean': 157.07768079, 'std': 9.72636682}, + "distance": {"mean": 2.73942464, "std": 0.05867924}, + "angle": {"mean": 157.07768079, "std": 9.72636682}, } - assert_allclose(np.mean(h.results.hbonds[:, 4]), - reference['distance']['mean']) - assert_allclose(np.std(h.results.hbonds[:, 4]), - reference['distance']['std']) - assert_allclose(np.mean(h.results.hbonds[:, 5]), - reference['angle']['mean']) - assert_allclose(np.std(h.results.hbonds[:, 5]), - reference['angle']['std']) + assert_allclose( + np.mean(h.results.hbonds[:, 4]), reference["distance"]["mean"] + ) + assert_allclose( + np.std(h.results.hbonds[:, 4]), reference["distance"]["std"] + ) + assert_allclose( + np.mean(h.results.hbonds[:, 5]), reference["angle"]["mean"] + ) + assert_allclose( + np.std(h.results.hbonds[:, 5]), reference["angle"]["std"] + ) def test_count_by_time(self, h): - ref_times = np.array([0.04, 0.08, 0.12, 0.16, 0.20, ]) + ref_times = np.array( + [ + 0.04, + 0.08, + 0.12, + 0.16, + 0.20, + ] + ) ref_counts = np.array([2, 4, 4, 2, 3]) counts = h.count_by_time() @@ -678,29 +715,32 @@ def test_count_by_type(self, h): class TestHydrogenBondAnalysisEmptySelections: @staticmethod - @pytest.fixture(scope='class') + @pytest.fixture(scope="class") def universe(): return MDAnalysis.Universe(waterPSF, waterDCD) - msg = ("{} is an empty selection string - no hydrogen bonds will " - "be found. This may be intended, but please check your " - "selection." - ) + msg = ( + "{} is an empty selection string - no hydrogen bonds will " + "be found. This may be intended, but please check your " + "selection." + ) - @pytest.mark.parametrize('seltype', - ['donors_sel', 'hydrogens_sel', 'acceptors_sel']) + @pytest.mark.parametrize( + "seltype", ["donors_sel", "hydrogens_sel", "acceptors_sel"] + ) def test_empty_sel(self, universe, seltype): - sel_kwarg = {seltype: ' '} + sel_kwarg = {seltype: " "} with pytest.warns(UserWarning, match=self.msg.format(seltype)): HydrogenBondAnalysis(universe, **sel_kwarg) def test_hbond_analysis(self, universe, client_HydrogenBondAnalysis): - h = HydrogenBondAnalysis(universe, donors_sel=' ', hydrogens_sel=' ', - acceptors_sel=' ') + h = HydrogenBondAnalysis( + universe, donors_sel=" ", hydrogens_sel=" ", acceptors_sel=" " + ) h.run(**client_HydrogenBondAnalysis) - assert h.donors_sel == '' - assert h.hydrogens_sel == '' - assert h.acceptors_sel == '' + assert h.donors_sel == "" + assert h.hydrogens_sel == "" + assert h.acceptors_sel == "" assert h.results.hbonds.size == 0 diff --git a/testsuite/MDAnalysisTests/analysis/test_leaflet.py b/testsuite/MDAnalysisTests/analysis/test_leaflet.py index 0c4839f36b5..e968bbf8c9c 100644 --- a/testsuite/MDAnalysisTests/analysis/test_leaflet.py +++ b/testsuite/MDAnalysisTests/analysis/test_leaflet.py @@ -43,14 +43,14 @@ def lipid_heads(universe): return universe.select_atoms(LIPID_HEAD_STRING) -@pytest.mark.skipif(HAS_NX, reason='networkx is installed') +@pytest.mark.skipif(HAS_NX, reason="networkx is installed") def test_optional_nx(): errmsg = "The LeafletFinder class requires an installation of networkx" with pytest.raises(ImportError, match=errmsg): _ = LeafletFinder(universe, lipid_heads, pbc=True) -@pytest.mark.skipif(not HAS_NX, reason='needs networkx') +@pytest.mark.skipif(not HAS_NX, reason="needs networkx") class TestLeafletFinder: @staticmethod def lines2one(lines): @@ -61,13 +61,22 @@ def test_leaflet_finder(self, universe, lipid_heads): lfls = LeafletFinder(universe, lipid_heads, pbc=True) top_heads, bottom_heads = lfls.groups() # Make top be... on top. - if top_heads.center_of_geometry()[2] < bottom_heads.center_of_geometry()[2]: - top_heads,bottom_heads = (bottom_heads,top_heads) - assert_equal(top_heads.indices, np.arange(1,2150,12), - err_msg="Found wrong leaflet lipids") - assert_equal(bottom_heads.indices, np.arange(2521,4670,12), - err_msg="Found wrong leaflet lipids") - + if ( + top_heads.center_of_geometry()[2] + < bottom_heads.center_of_geometry()[2] + ): + top_heads, bottom_heads = (bottom_heads, top_heads) + assert_equal( + top_heads.indices, + np.arange(1, 2150, 12), + err_msg="Found wrong leaflet lipids", + ) + assert_equal( + bottom_heads.indices, + np.arange(2521, 4670, 12), + err_msg="Found wrong leaflet lipids", + ) + def test_string_vs_atomgroup_proper(self, universe, lipid_heads): lfls_ag = LeafletFinder(universe, lipid_heads, pbc=True) lfls_string = LeafletFinder(universe, LIPID_HEAD_STRING, pbc=True) @@ -75,52 +84,58 @@ def test_string_vs_atomgroup_proper(self, universe, lipid_heads): groups_string = lfls_string.groups() assert_equal(groups_string[0].indices, groups_ag[0].indices) assert_equal(groups_string[1].indices, groups_ag[1].indices) - + def test_optimize_cutoff(self, universe, lipid_heads): cutoff, N = optimize_cutoff(universe, lipid_heads, pbc=True) assert N == 2 assert_almost_equal(cutoff, 10.5, decimal=4) - + def test_pbc_on_off(self, universe, lipid_heads): lfls_pbc_on = LeafletFinder(universe, lipid_heads, pbc=True) lfls_pbc_off = LeafletFinder(universe, lipid_heads, pbc=False) assert lfls_pbc_on.graph.size() > lfls_pbc_off.graph.size() - + def test_pbc_on_off_difference(self, universe, lipid_heads): import networkx lfls_pbc_on = LeafletFinder(universe, lipid_heads, cutoff=7, pbc=True) - lfls_pbc_off = LeafletFinder(universe, lipid_heads, cutoff=7, pbc=False) + lfls_pbc_off = LeafletFinder( + universe, lipid_heads, cutoff=7, pbc=False + ) pbc_on_graph = lfls_pbc_on.graph pbc_off_graph = lfls_pbc_off.graph diff_graph = networkx.difference(pbc_on_graph, pbc_off_graph) - assert_equal(set(diff_graph.edges), {(69, 153), (73, 79), - (206, 317), (313, 319)}) - + assert_equal( + set(diff_graph.edges), + {(69, 153), (73, 79), (206, 317), (313, 319)}, + ) + @pytest.mark.parametrize("sparse", [True, False, None]) def test_sparse_on_off_none(self, universe, lipid_heads, sparse): - lfls_ag = LeafletFinder(universe, lipid_heads, cutoff=15.0, pbc=True, - sparse=sparse) + lfls_ag = LeafletFinder( + universe, lipid_heads, cutoff=15.0, pbc=True, sparse=sparse + ) assert_almost_equal(len(lfls_ag.graph.edges), 1903, decimal=4) - + def test_cutoff_update(self, universe, lipid_heads): lfls_ag = LeafletFinder(universe, lipid_heads, cutoff=15.0, pbc=True) lfls_ag.update(cutoff=1.0) assert_almost_equal(lfls_ag.cutoff, 1.0, decimal=4) assert_almost_equal(len(lfls_ag.groups()), 360, decimal=4) - + def test_cutoff_update_default(self, universe, lipid_heads): lfls_ag = LeafletFinder(universe, lipid_heads, cutoff=15.0, pbc=True) lfls_ag.update() assert_almost_equal(lfls_ag.cutoff, 15.0, decimal=4) assert_almost_equal(len(lfls_ag.groups()), 2, decimal=4) - + def test_write_selection(self, universe, lipid_heads, tmpdir): lfls_ag = LeafletFinder(universe, lipid_heads, cutoff=15.0, pbc=True) with tmpdir.as_cwd(): - filename = lfls_ag.write_selection('leaflet.vmd') - expected_output = self.lines2one([ - """# leaflets based on select= cutoff=15.000000 + filename = lfls_ag.write_selection("leaflet.vmd") + expected_output = self.lines2one( + [ + """# leaflets based on select= cutoff=15.000000 # MDAnalysis VMD selection atomselect macro leaflet_1 {index 1 13 25 37 49 61 73 85 \\ 97 109 121 133 145 157 169 181 \\ @@ -170,10 +185,17 @@ def test_write_selection(self, universe, lipid_heads, tmpdir): 4537 4549 4561 4573 4585 4597 4609 4621 \\ 4633 4645 4657 4669 } - """]) - - assert self.lines2one(open('leaflet.vmd').readlines()) == expected_output - + """ + ] + ) + + assert ( + self.lines2one(open("leaflet.vmd").readlines()) + == expected_output + ) + def test_component_index_is_not_none(self, universe, lipid_heads): lfls_ag = LeafletFinder(universe, lipid_heads, cutoff=15.0, pbc=True) - assert_almost_equal(len(lfls_ag.groups(component_index=0)), 180, decimal=4) + assert_almost_equal( + len(lfls_ag.groups(component_index=0)), 180, decimal=4 + ) diff --git a/testsuite/MDAnalysisTests/analysis/test_lineardensity.py b/testsuite/MDAnalysisTests/analysis/test_lineardensity.py index 2b6ce161cb6..9f5963938bb 100644 --- a/testsuite/MDAnalysisTests/analysis/test_lineardensity.py +++ b/testsuite/MDAnalysisTests/analysis/test_lineardensity.py @@ -35,7 +35,7 @@ def test_invalid_grouping(): """Invalid groupings raise AttributeError""" universe = mda.Universe(waterPSF, waterDCD) - sel_string = 'all' + sel_string = "all" selection = universe.select_atoms(sel_string) with pytest.raises(AttributeError): # centroid is attribute of AtomGroup, but not valid here @@ -44,55 +44,128 @@ def test_invalid_grouping(): # test data for grouping='atoms' -expected_masses_atoms = np.array([15.9994, 1.008, 1.008, 15.9994, 1.008, 1.008, - 15.9994, 1.008, 1.008, 15.9994, 1.008, 1.008, - 15.9994, 1.008, 1.008]) -expected_charges_atoms = np.array([-0.834, 0.417, 0.417, -0.834, 0.417, - 0.417, -0.834, 0.417, 0.417, -0.834, - 0.417, 0.417, -0.834, 0.417, 0.417]) -expected_xmass_atoms = np.array([0., 0., 0., 0.00723323, 0.00473288, 0., - 0., 0., 0., 0.]) -expected_xcharge_atoms = np.array([0., 0., 0., 2.21582311e-05, - -2.21582311e-05, 0., 0., 0., 0., 0.]) +expected_masses_atoms = np.array( + [ + 15.9994, + 1.008, + 1.008, + 15.9994, + 1.008, + 1.008, + 15.9994, + 1.008, + 1.008, + 15.9994, + 1.008, + 1.008, + 15.9994, + 1.008, + 1.008, + ] +) +expected_charges_atoms = np.array( + [ + -0.834, + 0.417, + 0.417, + -0.834, + 0.417, + 0.417, + -0.834, + 0.417, + 0.417, + -0.834, + 0.417, + 0.417, + -0.834, + 0.417, + 0.417, + ] +) +expected_xmass_atoms = np.array( + [0.0, 0.0, 0.0, 0.00723323, 0.00473288, 0.0, 0.0, 0.0, 0.0, 0.0] +) +expected_xcharge_atoms = np.array( + [0.0, 0.0, 0.0, 2.21582311e-05, -2.21582311e-05, 0.0, 0.0, 0.0, 0.0, 0.0] +) # test data for grouping='residues' -expected_masses_residues = np.array([18.0154, 18.0154, 18.0154, 18.0154, - 18.0154]) +expected_masses_residues = np.array( + [18.0154, 18.0154, 18.0154, 18.0154, 18.0154] +) expected_charges_residues = np.array([0, 0, 0, 0, 0]) -expected_xmass_residues = np.array([0., 0., 0., 0.00717967, 0.00478644, - 0., 0., 0., 0., 0.]) -expected_xcharge_residues = np.array([0., 0., 0., 0., 0., 0., 0., 0., 0., 0.]) +expected_xmass_residues = np.array( + [0.0, 0.0, 0.0, 0.00717967, 0.00478644, 0.0, 0.0, 0.0, 0.0, 0.0] +) +expected_xcharge_residues = np.array( + [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0] +) # test data for grouping='segments' expected_masses_segments = np.array([90.0770]) expected_charges_segments = np.array([0]) -expected_xmass_segments = np.array([0., 0., 0., 0.01196611, 0., - 0., 0., 0., 0., 0.]) -expected_xcharge_segments = np.array([0., 0., 0., 0., 0., 0., 0., 0., 0., 0.]) +expected_xmass_segments = np.array( + [0.0, 0.0, 0.0, 0.01196611, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0] +) +expected_xcharge_segments = np.array( + [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0] +) # test data for grouping='fragments' -expected_masses_fragments = np.array([18.0154, 18.0154, 18.0154, 18.0154, - 18.0154]) +expected_masses_fragments = np.array( + [18.0154, 18.0154, 18.0154, 18.0154, 18.0154] +) expected_charges_fragments = np.array([0, 0, 0, 0, 0]) -expected_xmass_fragments = np.array([0., 0., 0., 0.00717967, 0.00478644, - 0., 0., 0., 0., 0.]) -expected_xcharge_fragments = np.array([0., 0., 0., 0., 0., 0., 0., 0., 0., 0.]) - - -@pytest.mark.parametrize("grouping, expected_masses, expected_charges, expected_xmass, expected_xcharge", [ - ("atoms", expected_masses_atoms, expected_charges_atoms, - expected_xmass_atoms, expected_xcharge_atoms), - ("residues", expected_masses_residues, expected_charges_residues, - expected_xmass_residues, expected_xcharge_residues), - ("segments", expected_masses_segments, expected_charges_segments, - expected_xmass_segments, expected_xcharge_segments), - ("fragments", expected_masses_fragments, expected_charges_fragments, - expected_xmass_fragments, expected_xcharge_fragments) -]) -def test_lineardensity(grouping, expected_masses, expected_charges, - expected_xmass, expected_xcharge): +expected_xmass_fragments = np.array( + [0.0, 0.0, 0.0, 0.00717967, 0.00478644, 0.0, 0.0, 0.0, 0.0, 0.0] +) +expected_xcharge_fragments = np.array( + [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0] +) + + +@pytest.mark.parametrize( + "grouping, expected_masses, expected_charges, expected_xmass, expected_xcharge", + [ + ( + "atoms", + expected_masses_atoms, + expected_charges_atoms, + expected_xmass_atoms, + expected_xcharge_atoms, + ), + ( + "residues", + expected_masses_residues, + expected_charges_residues, + expected_xmass_residues, + expected_xcharge_residues, + ), + ( + "segments", + expected_masses_segments, + expected_charges_segments, + expected_xmass_segments, + expected_xcharge_segments, + ), + ( + "fragments", + expected_masses_fragments, + expected_charges_fragments, + expected_xmass_fragments, + expected_xcharge_fragments, + ), + ], +) +def test_lineardensity( + grouping, + expected_masses, + expected_charges, + expected_xmass, + expected_xcharge, +): universe = mda.Universe(waterPSF, waterDCD) - sel_string = 'all' + sel_string = "all" selection = universe.select_atoms(sel_string) ld = LinearDensity(selection, grouping, binsize=5).run() assert_allclose(ld.masses, expected_masses) @@ -105,26 +178,30 @@ def test_lineardensity(grouping, expected_masses, expected_charges, @pytest.fixture(scope="module") def testing_Universe(): """Generate a universe for testing whether LinearDensity works with - updating atom groups. Also used for parallel analysis test.""" + updating atom groups. Also used for parallel analysis test.""" n_atoms = 3 - u = mda.Universe.empty(n_atoms=n_atoms, - n_residues=n_atoms, - n_segments=n_atoms, - atom_resindex=np.arange(n_atoms), - residue_segindex=np.arange(n_atoms)) + u = mda.Universe.empty( + n_atoms=n_atoms, + n_residues=n_atoms, + n_segments=n_atoms, + atom_resindex=np.arange(n_atoms), + residue_segindex=np.arange(n_atoms), + ) for attr in ["charges", "masses"]: u.add_TopologyAttr(attr, values=np.ones(n_atoms)) - coords = np.array([ - [[1., 1., 1.], [1., 2., 1.], [2., 1., 1.]], - [[1., 1., 2.], [1., 2., 1.], [2., 1., 1.]], - [[1., 1., 3.], [1., 2., 1.], [2., 1., 1.]], - [[1., 1., 4.], [1., 2., 1.], [2., 1., 1.]], - [[1., 1., 5.], [1., 2., 1.], [2., 1., 1.]] - ]) + coords = np.array( + [ + [[1.0, 1.0, 1.0], [1.0, 2.0, 1.0], [2.0, 1.0, 1.0]], + [[1.0, 1.0, 2.0], [1.0, 2.0, 1.0], [2.0, 1.0, 1.0]], + [[1.0, 1.0, 3.0], [1.0, 2.0, 1.0], [2.0, 1.0, 1.0]], + [[1.0, 1.0, 4.0], [1.0, 2.0, 1.0], [2.0, 1.0, 1.0]], + [[1.0, 1.0, 5.0], [1.0, 2.0, 1.0], [2.0, 1.0, 1.0]], + ] + ) - u.trajectory = get_reader_for(coords)(coords, order='fac', n_atoms=n_atoms) + u.trajectory = get_reader_for(coords)(coords, order="fac", n_atoms=n_atoms) for ts in u.trajectory: ts.dimensions = np.array([2, 2, 6, 90, 90, 90]) @@ -133,7 +210,7 @@ def testing_Universe(): def test_updating_atomgroup(testing_Universe): - expected_z_pos = np.array([0., 0.91329641, 0.08302695, 0., 0., 0.]) + expected_z_pos = np.array([0.0, 0.91329641, 0.08302695, 0.0, 0.0, 0.0]) u = testing_Universe selection = u.select_atoms("prop z < 3", updating=True) ld = LinearDensity(selection, binsize=1).run() @@ -143,27 +220,31 @@ def test_updating_atomgroup(testing_Universe): assert_allclose(ld.results.x.hist_bin_edges, expected_bin_edges) -testdict = {"pos": "mass_density", - "pos_std": "mass_density_stddev", - "char": "charge_density", - "char_std": "charge_density_stddev"} +testdict = { + "pos": "mass_density", + "pos_std": "mass_density_stddev", + "char": "charge_density", + "char_std": "charge_density_stddev", +} # TODO: Remove in 3.0.0 def test_old_name_deprecations(): universe = mda.Universe(waterPSF, waterDCD) - sel_string = 'all' + sel_string = "all" selection = universe.select_atoms(sel_string) ld = LinearDensity(selection, binsize=5).run() with pytest.warns(DeprecationWarning): assert_allclose(ld.results.x.pos, ld.results.x.mass_density) assert_allclose(ld.results.x.pos_std, ld.results.x.mass_density_stddev) assert_allclose(ld.results.x.char, ld.results.x.charge_density) - assert_allclose(ld.results.x.char_std, - ld.results.x.charge_density_stddev) + assert_allclose( + ld.results.x.char_std, ld.results.x.charge_density_stddev + ) for key in testdict.keys(): - assert_allclose(ld.results["x"][key], - ld.results["x"][testdict[key]]) + assert_allclose( + ld.results["x"][key], ld.results["x"][testdict[key]] + ) # Check that no DeprecationWarning is raised with new attributes with no_deprecated_call(): @@ -185,8 +266,13 @@ def test_parallel_analysis(testing_Universe): ld1 = LinearDensity(selection1, binsize=1).run() ld2 = LinearDensity(selection2, binsize=1).run() ld_whole = LinearDensity(selection_whole, binsize=1).run() - with pytest.warns(DeprecationWarning, - match="`_add_other_results` is deprecated!"): + with pytest.warns( + DeprecationWarning, match="`_add_other_results` is deprecated!" + ): ld1._add_other_results(ld2) - assert_allclose(ld1.results.z.mass_density, ld_whole.results.z.mass_density) - assert_allclose(ld1.results.x.mass_density, ld_whole.results.x.mass_density) + assert_allclose( + ld1.results.z.mass_density, ld_whole.results.z.mass_density + ) + assert_allclose( + ld1.results.x.mass_density, ld_whole.results.x.mass_density + ) diff --git a/testsuite/MDAnalysisTests/analysis/test_msd.py b/testsuite/MDAnalysisTests/analysis/test_msd.py index 3b96e40c61a..757fe6552cf 100644 --- a/testsuite/MDAnalysisTests/analysis/test_msd.py +++ b/testsuite/MDAnalysisTests/analysis/test_msd.py @@ -25,7 +25,7 @@ from MDAnalysis.analysis.msd import EinsteinMSD as MSD import MDAnalysis as mda -from numpy.testing import (assert_almost_equal, assert_equal) +from numpy.testing import assert_almost_equal, assert_equal import numpy as np from MDAnalysisTests.datafiles import PSF, DCD, RANDOM_WALK, RANDOM_WALK_TOPO @@ -34,38 +34,38 @@ import pytest -@pytest.fixture(scope='module') +@pytest.fixture(scope="module") def SELECTION(): - selection = 'backbone and name CA and resid 1-10' + selection = "backbone and name CA and resid 1-10" return selection -@pytest.fixture(scope='module') +@pytest.fixture(scope="module") def u(): return mda.Universe(PSF, DCD) -@pytest.fixture(scope='module') +@pytest.fixture(scope="module") def NSTEP(): nstep = 5000 return nstep -@pytest.fixture(scope='module') +@pytest.fixture(scope="module") def random_walk_u(): # 100x100 return mda.Universe(RANDOM_WALK_TOPO, RANDOM_WALK) -@pytest.fixture(scope='module') +@pytest.fixture(scope="module") def msd(u, SELECTION): # non fft msd - m = MSD(u, SELECTION, msd_type='xyz', fft=False) + m = MSD(u, SELECTION, msd_type="xyz", fft=False) m.run() return m -@pytest.fixture(scope='module') +@pytest.fixture(scope="module") def step_traj(NSTEP): # constant velocity x = np.arange(NSTEP) traj = np.vstack([x, x, x]).T @@ -75,7 +75,7 @@ def step_traj(NSTEP): # constant velocity return u -@block_import('tidynamics') +@block_import("tidynamics") def test_notidynamics(u, SELECTION): with pytest.raises(ImportError, match="tidynamics was not found"): u = mda.Universe(PSF, DCD) @@ -86,7 +86,7 @@ def test_notidynamics(u, SELECTION): def characteristic_poly(n, d): # polynomial that describes unit step traj MSD x = np.arange(0, n) - y = d*x*x + y = d * x * x return y @@ -98,65 +98,86 @@ def test_selection_works(self, msd): def test_ag_accepted(self, u): ag = u.select_atoms("resid 1") - m = MSD(ag, msd_type='xyz', fft=False) + m = MSD(ag, msd_type="xyz", fft=False) def test_updating_ag_rejected(self, u): updating_ag = u.select_atoms("around 3.5 resid 1", updating=True) errmsg = "UpdatingAtomGroups are not valid" with pytest.raises(TypeError, match=errmsg): - m = MSD(updating_ag, msd_type='xyz', fft=False) + m = MSD(updating_ag, msd_type="xyz", fft=False) - @pytest.mark.parametrize('msdtype', ['foo', 'bar', 'yx', 'zyx']) + @pytest.mark.parametrize("msdtype", ["foo", "bar", "yx", "zyx"]) def test_msdtype_error(self, u, SELECTION, msdtype): errmsg = f"invalid msd_type: {msdtype}" with pytest.raises(ValueError, match=errmsg): m = MSD(u, SELECTION, msd_type=msdtype) - @pytest.mark.parametrize("dim, dim_factor", [ - ('xyz', 3), ('xy', 2), ('xz', 2), ('yz', 2), ('x', 1), ('y', 1), - ('z', 1) - ]) - def test_simple_step_traj_all_dims(self, step_traj, NSTEP, dim, - dim_factor): + @pytest.mark.parametrize( + "dim, dim_factor", + [ + ("xyz", 3), + ("xy", 2), + ("xz", 2), + ("yz", 2), + ("x", 1), + ("y", 1), + ("z", 1), + ], + ) + def test_simple_step_traj_all_dims( + self, step_traj, NSTEP, dim, dim_factor + ): # testing the "simple" algorithm on constant velocity trajectory # should fit the polynomial y=dim_factor*x**2 - m_simple = MSD(step_traj, 'all', msd_type=dim, fft=False) + m_simple = MSD(step_traj, "all", msd_type=dim, fft=False) m_simple.run() poly = characteristic_poly(NSTEP, dim_factor) assert_almost_equal(m_simple.results.timeseries, poly, decimal=4) - @pytest.mark.parametrize("dim, dim_factor", [ - ('xyz', 3), ('xy', 2), ('xz', 2), ('yz', 2), ('x', 1), ('y', 1), - ('z', 1) - ]) - def test_simple_start_stop_step_all_dims(self, step_traj, NSTEP, dim, - dim_factor): + @pytest.mark.parametrize( + "dim, dim_factor", + [ + ("xyz", 3), + ("xy", 2), + ("xz", 2), + ("yz", 2), + ("x", 1), + ("y", 1), + ("z", 1), + ], + ) + def test_simple_start_stop_step_all_dims( + self, step_traj, NSTEP, dim, dim_factor + ): # testing the "simple" algorithm on constant velocity trajectory # test start stop step is working correctly - m_simple = MSD(step_traj, 'all', msd_type=dim, fft=False) + m_simple = MSD(step_traj, "all", msd_type=dim, fft=False) m_simple.run(start=10, stop=1000, step=10) poly = characteristic_poly(NSTEP, dim_factor) # polynomial must take offset start into account - assert_almost_equal(m_simple.results.timeseries, poly[0:990:10], - decimal=4) + assert_almost_equal( + m_simple.results.timeseries, poly[0:990:10], decimal=4 + ) def test_random_walk_u_simple(self, random_walk_u): # regress against random_walk test data - msd_rw = MSD(random_walk_u, 'all', msd_type='xyz', fft=False) + msd_rw = MSD(random_walk_u, "all", msd_type="xyz", fft=False) msd_rw.run() norm = np.linalg.norm(msd_rw.results.timeseries) val = 3932.39927487146 assert_almost_equal(norm, val, decimal=5) -@pytest.mark.skipif(import_not_available("tidynamics"), - reason="Test skipped because tidynamics not found") +@pytest.mark.skipif( + import_not_available("tidynamics"), + reason="Test skipped because tidynamics not found", +) class TestMSDFFT(object): - @pytest.fixture(scope='class') + @pytest.fixture(scope="class") def msd_fft(self, u, SELECTION): # fft msd - m = MSD(u, SELECTION, msd_type='xyz', fft=True) + m = MSD(u, SELECTION, msd_type="xyz", fft=True) m.run() return m @@ -172,7 +193,7 @@ def test_fft_vs_simple_default_per_particle(self, msd, msd_fft): per_particle_fft = msd_fft.results.msds_by_particle assert_almost_equal(per_particle_simple, per_particle_fft, decimal=4) - @pytest.mark.parametrize("dim", ['xyz', 'xy', 'xz', 'yz', 'x', 'y', 'z']) + @pytest.mark.parametrize("dim", ["xyz", "xy", "xz", "yz", "x", "y", "z"]) def test_fft_vs_simple_all_dims(self, u, SELECTION, dim): # check fft and simple give same result for each dimensionality m_simple = MSD(u, SELECTION, msd_type=dim, fft=False) @@ -183,7 +204,7 @@ def test_fft_vs_simple_all_dims(self, u, SELECTION, dim): timeseries_fft = m_fft.results.timeseries assert_almost_equal(timeseries_simple, timeseries_fft, decimal=4) - @pytest.mark.parametrize("dim", ['xyz', 'xy', 'xz', 'yz', 'x', 'y', 'z']) + @pytest.mark.parametrize("dim", ["xyz", "xy", "xz", "yz", "x", "y", "z"]) def test_fft_vs_simple_all_dims_per_particle(self, u, SELECTION, dim): # check fft and simple give same result for each particle in each # dimension @@ -195,40 +216,58 @@ def test_fft_vs_simple_all_dims_per_particle(self, u, SELECTION, dim): per_particle_fft = m_fft.results.msds_by_particle assert_almost_equal(per_particle_simple, per_particle_fft, decimal=4) - @pytest.mark.parametrize("dim, dim_factor", [ - ('xyz', 3), ('xy', 2), ('xz', 2), ('yz', 2), ('x', 1), ('y', 1), - ('z', 1) - ]) + @pytest.mark.parametrize( + "dim, dim_factor", + [ + ("xyz", 3), + ("xy", 2), + ("xz", 2), + ("yz", 2), + ("x", 1), + ("y", 1), + ("z", 1), + ], + ) def test_fft_step_traj_all_dims(self, step_traj, NSTEP, dim, dim_factor): # testing the fft algorithm on constant velocity trajectory # this should fit the polynomial y=dim_factor*x**2 # fft based tests require a slight decrease in expected prescision # primarily due to roundoff in fft(ifft()) calls. # relative accuracy expected to be around ~1e-12 - m_simple = MSD(step_traj, 'all', msd_type=dim, fft=True) + m_simple = MSD(step_traj, "all", msd_type=dim, fft=True) m_simple.run() poly = characteristic_poly(NSTEP, dim_factor) # this was relaxed from decimal=4 for numpy=1.13 test assert_almost_equal(m_simple.results.timeseries, poly, decimal=3) - @pytest.mark.parametrize("dim, dim_factor", [( - 'xyz', 3), ('xy', 2), ('xz', 2), ('yz', 2), ('x', 1), ('y', 1), - ('z', 1) - ]) - def test_fft_start_stop_step_all_dims(self, step_traj, NSTEP, dim, - dim_factor): + @pytest.mark.parametrize( + "dim, dim_factor", + [ + ("xyz", 3), + ("xy", 2), + ("xz", 2), + ("yz", 2), + ("x", 1), + ("y", 1), + ("z", 1), + ], + ) + def test_fft_start_stop_step_all_dims( + self, step_traj, NSTEP, dim, dim_factor + ): # testing the fft algorithm on constant velocity trajectory # test start stop step is working correctly - m_simple = MSD(step_traj, 'all', msd_type=dim, fft=True) + m_simple = MSD(step_traj, "all", msd_type=dim, fft=True) m_simple.run(start=10, stop=1000, step=10) poly = characteristic_poly(NSTEP, dim_factor) # polynomial must take offset start into account - assert_almost_equal(m_simple.results.timeseries, poly[0:990:10], - decimal=3) + assert_almost_equal( + m_simple.results.timeseries, poly[0:990:10], decimal=3 + ) def test_random_walk_u_fft(self, random_walk_u): # regress against random_walk test data - msd_rw = MSD(random_walk_u, 'all', msd_type='xyz', fft=True) + msd_rw = MSD(random_walk_u, "all", msd_type="xyz", fft=True) msd_rw.run() norm = np.linalg.norm(msd_rw.results.timeseries) val = 3932.39927487146 diff --git a/testsuite/MDAnalysisTests/analysis/test_nucleicacids.py b/testsuite/MDAnalysisTests/analysis/test_nucleicacids.py index ce2ae5e4864..e695a605691 100644 --- a/testsuite/MDAnalysisTests/analysis/test_nucleicacids.py +++ b/testsuite/MDAnalysisTests/analysis/test_nucleicacids.py @@ -27,15 +27,19 @@ from pytest import approx -from MDAnalysis.analysis.nucleicacids import (NucPairDist, WatsonCrickDist, - MajorPairDist, MinorPairDist) +from MDAnalysis.analysis.nucleicacids import ( + NucPairDist, + WatsonCrickDist, + MajorPairDist, + MinorPairDist, +) from MDAnalysisTests.datafiles import RNA_PSF, RNA_PDB from MDAnalysis.core.groups import ResidueGroup -@pytest.fixture(scope='module') +@pytest.fixture(scope="module") def u(): return mda.Universe(RNA_PSF, RNA_PDB) @@ -51,10 +55,10 @@ def test_empty_ag_error(strand): strand2 = ResidueGroup([strand.residues[1]]) with pytest.raises(ValueError, match="returns an empty AtomGroup"): - NucPairDist.select_strand_atoms(strand1, strand2, 'UNK1', 'O2') + NucPairDist.select_strand_atoms(strand1, strand2, "UNK1", "O2") -@pytest.fixture(scope='module') +@pytest.fixture(scope="module") def wc_rna(strand, client_NucPairDist): strand1 = ResidueGroup([strand.residues[0], strand.residues[21]]) strand2 = ResidueGroup([strand.residues[1], strand.residues[22]]) diff --git a/testsuite/MDAnalysisTests/analysis/test_nuclinfo.py b/testsuite/MDAnalysisTests/analysis/test_nuclinfo.py index ea6c3e03fbf..61577599a00 100644 --- a/testsuite/MDAnalysisTests/analysis/test_nuclinfo.py +++ b/testsuite/MDAnalysisTests/analysis/test_nuclinfo.py @@ -30,147 +30,212 @@ ) -@pytest.fixture(scope='module') +@pytest.fixture(scope="module") def u(): return mda.Universe(RNA_PSF, RNA_PDB) -@pytest.mark.parametrize('i, bp, seg1, seg2, expected_value', ( - ( 1, 2, 'RNAA', 'RNAA', 4.3874702), - (22, 23, 'RNAA', 'RNAA', 4.1716404), -)) +@pytest.mark.parametrize( + "i, bp, seg1, seg2, expected_value", + ( + (1, 2, "RNAA", "RNAA", 4.3874702), + (22, 23, "RNAA", "RNAA", 4.1716404), + ), +) def test_wc_pair(u, i, bp, seg1, seg2, expected_value): val = nuclinfo.wc_pair(u, i, bp, seg1=seg1, seg2=seg2) assert_almost_equal(val, expected_value, decimal=3) -@pytest.mark.parametrize('i, bp, seg1, seg2, expected_value', ( - ( 3, 17, 'RNAA', 'RNAA', 15.06506), - (20, 5, 'RNAA', 'RNAA', 3.219116), -)) +@pytest.mark.parametrize( + "i, bp, seg1, seg2, expected_value", + ( + (3, 17, "RNAA", "RNAA", 15.06506), + (20, 5, "RNAA", "RNAA", 3.219116), + ), +) def test_minor_pair(u, i, bp, seg1, seg2, expected_value): val = nuclinfo.minor_pair(u, i, bp, seg1=seg1, seg2=seg2) assert_almost_equal(val, expected_value, decimal=3) -@pytest.mark.parametrize('i, bp, seg1, seg2, expected_value', ( - (2, 12, 'RNAA', 'RNAA', 26.884272), - (5, 9, 'RNAA', 'RNAA', 13.578535), -)) +@pytest.mark.parametrize( + "i, bp, seg1, seg2, expected_value", + ( + (2, 12, "RNAA", "RNAA", 26.884272), + (5, 9, "RNAA", "RNAA", 13.578535), + ), +) def test_major_pair(u, i, bp, seg1, seg2, expected_value): val = nuclinfo.major_pair(u, i, bp, seg1=seg1, seg2=seg2) assert_almost_equal(val, expected_value, decimal=3) -@pytest.mark.parametrize('seg, i, expected_value', ( - ('RNAA', 9, 3.16497), - ('RNAA', 21, 22.07721), -)) +@pytest.mark.parametrize( + "seg, i, expected_value", + ( + ("RNAA", 9, 3.16497), + ("RNAA", 21, 22.07721), + ), +) def test_phase_cp(u, seg, i, expected_value): val = nuclinfo.phase_cp(u, seg=seg, i=i) assert_almost_equal(val, expected_value, decimal=3) -@pytest.mark.parametrize('seg, i, expected_value', ( - ('RNAA', 1, 359.57580), - ('RNAA', 11, 171.71645), -)) +@pytest.mark.parametrize( + "seg, i, expected_value", + ( + ("RNAA", 1, 359.57580), + ("RNAA", 11, 171.71645), + ), +) def test_phase_as(u, seg, i, expected_value): val = nuclinfo.phase_as(u, seg=seg, i=i) assert_almost_equal(val, expected_value, decimal=3) -@pytest.mark.parametrize('seg, i, expected_value', ( - ('RNAA', 5, [302.203802, 179.043077, 35.271411, 79.499729, 201.000393, - 282.14321 , 210.709327]), - ('RNAA', 21, [280.388619, 185.12919 , 56.616215, 64.87354 , 187.153367, - 279.340915, 215.332144]), -)) +@pytest.mark.parametrize( + "seg, i, expected_value", + ( + ( + "RNAA", + 5, + [ + 302.203802, + 179.043077, + 35.271411, + 79.499729, + 201.000393, + 282.14321, + 210.709327, + ], + ), + ( + "RNAA", + 21, + [ + 280.388619, + 185.12919, + 56.616215, + 64.87354, + 187.153367, + 279.340915, + 215.332144, + ], + ), + ), +) def test_tors(u, seg, i, expected_value): val = nuclinfo.tors(u, seg=seg, i=i) assert_allclose(val, expected_value, rtol=1e-03) -@pytest.mark.parametrize('seg, i, expected_value', ( - ('RNAA', 6, 279.15103), - ('RNAA', 18, 298.09936), -)) +@pytest.mark.parametrize( + "seg, i, expected_value", + ( + ("RNAA", 6, 279.15103), + ("RNAA", 18, 298.09936), + ), +) def test_tors_alpha(u, seg, i, expected_value): val = nuclinfo.tors_alpha(u, seg=seg, i=i) assert_almost_equal(val, expected_value, decimal=3) -@pytest.mark.parametrize('seg, i, expected_value', ( - ('RNAA', 7, 184.20501), - ('RNAA', 15, 169.70042), -)) +@pytest.mark.parametrize( + "seg, i, expected_value", + ( + ("RNAA", 7, 184.20501), + ("RNAA", 15, 169.70042), + ), +) def test_tors_beta(u, seg, i, expected_value): val = nuclinfo.tors_beta(u, seg=seg, i=i) assert_almost_equal(val, expected_value, decimal=3) -@pytest.mark.parametrize('seg, i, expected_value', ( - ('RNAA', 7, 52.72022), - ('RNAA', 15, 54.59684), -)) +@pytest.mark.parametrize( + "seg, i, expected_value", + ( + ("RNAA", 7, 52.72022), + ("RNAA", 15, 54.59684), + ), +) def test_tors_gamma(u, seg, i, expected_value): val = nuclinfo.tors_gamma(u, seg=seg, i=i) assert_almost_equal(val, expected_value, decimal=3) -@pytest.mark.parametrize('seg, i, expected_value', ( - ('RNAA', 7, 84.80554), - ('RNAA', 15, 82.00043), -)) +@pytest.mark.parametrize( + "seg, i, expected_value", + ( + ("RNAA", 7, 84.80554), + ("RNAA", 15, 82.00043), + ), +) def test_tors_delta(u, seg, i, expected_value): val = nuclinfo.tors_delta(u, seg=seg, i=i) assert_almost_equal(val, expected_value, decimal=3) -@pytest.mark.parametrize('seg, i, expected_value', ( - ('RNAA', 7, 200.40990), - ('RNAA', 15, 210.96953), -)) +@pytest.mark.parametrize( + "seg, i, expected_value", + ( + ("RNAA", 7, 200.40990), + ("RNAA", 15, 210.96953), + ), +) def test_tors_eps(u, seg, i, expected_value): val = nuclinfo.tors_eps(u, seg=seg, i=i) assert_almost_equal(val, expected_value, decimal=3) -@pytest.mark.parametrize('seg, i, expected_value', ( - ('RNAA', 7, 297.84736), - ('RNAA', 15, 330.24898) -)) +@pytest.mark.parametrize( + "seg, i, expected_value", (("RNAA", 7, 297.84736), ("RNAA", 15, 330.24898)) +) def test_tors_zeta(u, seg, i, expected_value): val = nuclinfo.tors_zeta(u, seg=seg, i=i) assert_almost_equal(val, expected_value, decimal=3) -@pytest.mark.parametrize('seg, i, expected_value', ( - ('RNAA', 1, 178.37435), - ('RNAA', 2, 202.03418), - ('RNAA', 7, 200.91674), - ('RNAA', 15, 209.32109), -)) +@pytest.mark.parametrize( + "seg, i, expected_value", + ( + ("RNAA", 1, 178.37435), + ("RNAA", 2, 202.03418), + ("RNAA", 7, 200.91674), + ("RNAA", 15, 209.32109), + ), +) def test_tors_chi(u, seg, i, expected_value): val = nuclinfo.tors_chi(u, seg=seg, i=i) assert_almost_equal(val, expected_value, decimal=3) -@pytest.mark.parametrize('seg, i, expected_value', ( - ('RNAA', 20, 103.07024), - ('RNAA', 5, 156.62223), - ('RNAA', 7 , 77.94538), - ('RNAA', 15, 130.18539), -)) +@pytest.mark.parametrize( + "seg, i, expected_value", + ( + ("RNAA", 20, 103.07024), + ("RNAA", 5, 156.62223), + ("RNAA", 7, 77.94538), + ("RNAA", 15, 130.18539), + ), +) def test_hydroxyl(u, seg, i, expected_value): val = nuclinfo.hydroxyl(u, seg=seg, i=i) assert_almost_equal(val, expected_value, decimal=3) -@pytest.mark.parametrize('bp1, bp2, i, seg1, seg2, seg3, expected_value', ( - (16, 2, 3, 'RNAA', 'RNAA', 'RNAA', 314.69804), - (8, 9, 10, 'RNAA', 'RNAA', 'RNAA', 34.50106), -)) -def test_pseudo_dihe_baseflip(u, bp1, bp2, i, seg1, seg2, seg3, expected_value): +@pytest.mark.parametrize( + "bp1, bp2, i, seg1, seg2, seg3, expected_value", + ( + (16, 2, 3, "RNAA", "RNAA", "RNAA", 314.69804), + (8, 9, 10, "RNAA", "RNAA", "RNAA", 34.50106), + ), +) +def test_pseudo_dihe_baseflip( + u, bp1, bp2, i, seg1, seg2, seg3, expected_value +): val = nuclinfo.pseudo_dihe_baseflip(u, bp1, bp2, i, seg1, seg2, seg3) assert_almost_equal(val, expected_value, decimal=3) diff --git a/testsuite/MDAnalysisTests/analysis/test_pca.py b/testsuite/MDAnalysisTests/analysis/test_pca.py index 19dca6cf3b0..663aa5e68ff 100644 --- a/testsuite/MDAnalysisTests/analysis/test_pca.py +++ b/testsuite/MDAnalysisTests/analysis/test_pca.py @@ -24,44 +24,58 @@ import MDAnalysis as mda from MDAnalysis.analysis import align import MDAnalysis.analysis.pca -from MDAnalysis.analysis.pca import (PCA, cosine_content, - rmsip, cumulative_overlap) +from MDAnalysis.analysis.pca import ( + PCA, + cosine_content, + rmsip, + cumulative_overlap, +) -from numpy.testing import (assert_almost_equal, assert_equal, - assert_array_almost_equal, assert_allclose,) +from numpy.testing import ( + assert_almost_equal, + assert_equal, + assert_array_almost_equal, + assert_allclose, +) -from MDAnalysisTests.datafiles import (PSF, DCD, RANDOM_WALK, RANDOM_WALK_TOPO, - waterPSF, waterDCD) +from MDAnalysisTests.datafiles import ( + PSF, + DCD, + RANDOM_WALK, + RANDOM_WALK_TOPO, + waterPSF, + waterDCD, +) import pytest -SELECTION = 'backbone and name CA and resid 1-10' +SELECTION = "backbone and name CA and resid 1-10" -@pytest.fixture(scope='module') +@pytest.fixture(scope="module") def u(): return mda.Universe(PSF, DCD) -@pytest.fixture(scope='function') +@pytest.fixture(scope="function") def u_fresh(): # each test gets a fresh universe return mda.Universe(PSF, DCD) -@pytest.fixture(scope='module') +@pytest.fixture(scope="module") def u_aligned(): u = mda.Universe(PSF, DCD, in_memory=True) align.AlignTraj(u, u, select=SELECTION).run() return u -@pytest.fixture(scope='module') +@pytest.fixture(scope="module") def pca(u): u.transfer_to_memory() return PCA(u, select=SELECTION).run() -@pytest.fixture(scope='module') +@pytest.fixture(scope="module") def pca_aligned(u): # run on a copy so positions in u are unchanged u_copy = u.copy() @@ -85,15 +99,17 @@ def test_cum_var(pca): def test_pcs(pca): - assert_equal(pca.results.p_components.shape, (pca._n_atoms * 3, - pca._n_atoms * 3)) + assert_equal( + pca.results.p_components.shape, (pca._n_atoms * 3, pca._n_atoms * 3) + ) def test_pcs_n_components(u): pca = PCA(u, select=SELECTION).run() - assert_equal(pca.n_components, pca._n_atoms*3) - assert_equal(pca.results.p_components.shape, (pca._n_atoms * 3, - pca._n_atoms * 3)) + assert_equal(pca.n_components, pca._n_atoms * 3) + assert_equal( + pca.results.p_components.shape, (pca._n_atoms * 3, pca._n_atoms * 3) + ) pca.n_components = 10 assert_equal(pca.n_components, 10) assert_equal(pca.results.p_components.shape, (pca._n_atoms * 3, 10)) @@ -102,27 +118,27 @@ def test_pcs_n_components(u): def test_different_steps(pca, u): atoms = u.select_atoms(SELECTION) dot = pca.transform(atoms, start=5, stop=7, step=1) - assert_equal(dot.shape, (2, atoms.n_atoms*3)) + assert_equal(dot.shape, (2, atoms.n_atoms * 3)) def test_transform_different_atoms(pca, u): - atoms = u.select_atoms('backbone and name N and resid 1-10') + atoms = u.select_atoms("backbone and name N and resid 1-10") with pytest.warns(UserWarning): pca.transform(atoms, start=5, stop=7, step=1) def test_transform_rerun(u): - atoms = u.select_atoms('bynum 1-10') + atoms = u.select_atoms("bynum 1-10") u.transfer_to_memory() - pca = PCA(u, select='bynum 1-10').run(stop=5) + pca = PCA(u, select="bynum 1-10").run(stop=5) dot = pca.transform(atoms) assert_equal(dot.shape, (98, atoms.n_atoms * 3)) def test_pca_not_run(u): - atoms = u.select_atoms('bynum 1-10') + atoms = u.select_atoms("bynum 1-10") u.transfer_to_memory() - pca = PCA(u, select='bynum 1-10') + pca = PCA(u, select="bynum 1-10") with pytest.raises(ValueError): dot = pca.transform(atoms, stop=5) @@ -137,7 +153,7 @@ def test_no_frames(u): def test_can_run_frames(u): atoms = u.select_atoms(SELECTION) u.transfer_to_memory() - PCA(u, select=SELECTION).run(frames=[0,1]) + PCA(u, select=SELECTION).run(frames=[0, 1]) def test_can_run_frames(u): @@ -168,37 +184,39 @@ def test_project_no_pca_run(u, pca): pca_class = PCA(u, select=SELECTION) with pytest.raises(ValueError) as exc: pca_class.project_single_frame() - assert 'Call run() on the PCA before projecting' in str(exc.value) + assert "Call run() on the PCA before projecting" in str(exc.value) def test_project_none_anchor(u, pca): - group = u.select_atoms('resnum 1') + group = u.select_atoms("resnum 1") with pytest.raises(ValueError) as exc: func = pca.project_single_frame(0, group=group, anchor=None) - assert ("'anchor' cannot be 'None'" + - " if 'group' is not 'None'") in str(exc.value) + assert ("'anchor' cannot be 'None'" + " if 'group' is not 'None'") in str( + exc.value + ) def test_project_more_anchor(u, pca): - group = u.select_atoms('resnum 1') + group = u.select_atoms("resnum 1") with pytest.raises(ValueError) as exc: - project = pca.project_single_frame(0, group=group, anchor='backbone') + project = pca.project_single_frame(0, group=group, anchor="backbone") assert "More than one 'anchor' found in residues" in str(exc.value) def test_project_less_anchor(u, pca): - group = u.select_atoms('all') + group = u.select_atoms("all") with pytest.raises(ValueError) as exc: - project = pca.project_single_frame(0, group=group, anchor='name CB') - assert ("Some residues in 'group'" + - " do not have an 'anchor'") in str(exc.value) + project = pca.project_single_frame(0, group=group, anchor="name CB") + assert ("Some residues in 'group'" + " do not have an 'anchor'") in str( + exc.value + ) def test_project_invalid_anchor(u): - pca = PCA(u, select='name CA').run() - group = u.select_atoms('all') + pca = PCA(u, select="name CA").run() + group = u.select_atoms("all") with pytest.raises(ValueError) as exc: - project = pca.project_single_frame(0, group=group, anchor='name N') + project = pca.project_single_frame(0, group=group, anchor="name N") assert "Some 'anchors' are not part of PCA class" in str(exc.value) @@ -227,8 +245,7 @@ def test_project_reconstruct_whole(u, u_fresh): @pytest.mark.parametrize( - ("n1", "n2"), - [(0, 0), (0, [0]), ([0, 1], [0, 1]), (0, 1), (1, 0)] + ("n1", "n2"), [(0, 0), (0, [0]), ([0, 1], [0, 1]), (0, 1), (1, 0)] ) def test_project_twice_projection(u_fresh, n1, n2): # Two succesive projections are applied. The second projection does nothing @@ -252,17 +269,14 @@ def test_project_twice_projection(u_fresh, n1, n2): def test_project_extrapolate_translation(u_fresh): # when the projection is extended to non-PCA atoms, # non-PCA atoms' coordinates will be conserved relative to the anchor atom - pca = PCA(u_fresh, select='resnum 1 and backbone').run() - sel = 'resnum 1 and name CA CB CG' + pca = PCA(u_fresh, select="resnum 1 and backbone").run() + sel = "resnum 1 and name CA CB CG" group = u_fresh.select_atoms(sel) - project = pca.project_single_frame(0, group=group, - anchor='name CA') + project = pca.project_single_frame(0, group=group, anchor="name CA") - distances_original = ( - mda.lib.distances.self_distance_array(group.positions) - ) - distances_new = ( - mda.lib.distances.self_distance_array(project(group).positions) + distances_original = mda.lib.distances.self_distance_array(group.positions) + distances_new = mda.lib.distances.self_distance_array( + project(group).positions ) assert_allclose(distances_original, distances_new, rtol=1e-05) @@ -273,7 +287,7 @@ def test_cosine_content(): pca_random = PCA(rand).run() dot = pca_random.transform(rand.atoms) content = cosine_content(dot, 0) - assert_almost_equal(content, .99, 1) + assert_almost_equal(content, 0.99, 1) def test_mean_shape(pca_aligned, u): @@ -285,19 +299,17 @@ def test_mean_shape(pca_aligned, u): def test_calculate_mean(pca_aligned, u, u_aligned): ag = u_aligned.select_atoms(SELECTION) coords = u_aligned.trajectory.coordinate_array[:, ag.ix] - assert_almost_equal(pca_aligned.mean, coords.mean( - axis=0), decimal=5) + assert_almost_equal(pca_aligned.mean, coords.mean(axis=0), decimal=5) def test_given_mean(pca, u): - pca = PCA(u, select=SELECTION, align=False, - mean=pca.mean).run() + pca = PCA(u, select=SELECTION, align=False, mean=pca.mean).run() assert_almost_equal(pca.cov, pca.cov, decimal=5) def test_wrong_num_given_mean(u): wrong_mean = [[0, 0, 0], [1, 1, 1]] - with pytest.raises(ValueError, match='Number of atoms in'): + with pytest.raises(ValueError, match="Number of atoms in"): pca = PCA(u, select=SELECTION, mean=wrong_mean).run() @@ -316,22 +328,24 @@ def test_pca_rmsip_self(pca): def test_rmsip_ortho(pca): - value = rmsip(pca.results.p_components[:, :10].T, - pca.results.p_components[:, 10:20].T) + value = rmsip( + pca.results.p_components[:, :10].T, + pca.results.p_components[:, 10:20].T, + ) assert_almost_equal(value, 0.0) def test_pytest_too_many_components(pca): with pytest.raises(ValueError) as exc: pca.rmsip(pca, n_components=(1, 2, 3)) - assert 'Too many values' in str(exc.value) + assert "Too many values" in str(exc.value) def test_asymmetric_rmsip(pca): a = pca.rmsip(pca, n_components=(10, 4)) b = pca.rmsip(pca, n_components=(4, 10)) - assert abs(a-b) > 0.1, 'RMSIP should be asymmetric' + assert abs(a - b) > 0.1, "RMSIP should be asymmetric" assert_almost_equal(b, 1.0) @@ -346,51 +360,47 @@ def test_cumulative_overlap_ortho(pca): assert_almost_equal(value, 0.0) -@pytest.mark.parametrize( - 'method', ['rmsip', - 'cumulative_overlap']) +@pytest.mark.parametrize("method", ["rmsip", "cumulative_overlap"]) def test_compare_not_run_other(u, pca, method): pca2 = PCA(u) func = getattr(pca, method) with pytest.raises(ValueError) as exc: func(pca2) - assert 'Call run()' in str(exc.value) + assert "Call run()" in str(exc.value) -@pytest.mark.parametrize( - 'method', ['rmsip', - 'cumulative_overlap']) +@pytest.mark.parametrize("method", ["rmsip", "cumulative_overlap"]) def test_compare_not_run_self(u, pca, method): pca2 = PCA(u) func = getattr(pca2, method) with pytest.raises(ValueError) as exc: func(pca) - assert 'Call run()' in str(exc.value) + assert "Call run()" in str(exc.value) -@pytest.mark.parametrize( - 'method', ['rmsip', - 'cumulative_overlap']) +@pytest.mark.parametrize("method", ["rmsip", "cumulative_overlap"]) def test_compare_wrong_class(u, pca, method): func = getattr(pca, method) with pytest.raises(ValueError) as exc: func(3) - assert 'must be another PCA class' in str(exc.value) + assert "must be another PCA class" in str(exc.value) -@pytest.mark.parametrize("attr", ("p_components", "variance", - "cumulated_variance")) +@pytest.mark.parametrize( + "attr", ("p_components", "variance", "cumulated_variance") +) def test_pca_attr_warning(u, attr): pca = PCA(u, select=SELECTION).run(stop=2) wmsg = f"The `{attr}` attribute was deprecated in MDAnalysis 2.0.0" with pytest.warns(DeprecationWarning, match=wmsg): getattr(pca, attr) is pca.results[attr] + @pytest.mark.parametrize( "classname,is_parallelizable", [ (MDAnalysis.analysis.pca.PCA, False), - ] + ], ) def test_class_is_parallelizable(classname, is_parallelizable): assert classname._analysis_algorithm_is_parallelizable == is_parallelizable @@ -399,9 +409,8 @@ def test_class_is_parallelizable(classname, is_parallelizable): @pytest.mark.parametrize( "classname,backends", [ - (MDAnalysis.analysis.pca.PCA, ('serial',)), - ] + (MDAnalysis.analysis.pca.PCA, ("serial",)), + ], ) def test_supported_backends(classname, backends): assert classname.get_supported_backends() == backends - diff --git a/testsuite/MDAnalysisTests/analysis/test_persistencelength.py b/testsuite/MDAnalysisTests/analysis/test_persistencelength.py index 5d8790ab3db..3cbee2952cf 100644 --- a/testsuite/MDAnalysisTests/analysis/test_persistencelength.py +++ b/testsuite/MDAnalysisTests/analysis/test_persistencelength.py @@ -45,8 +45,7 @@ def u(): @staticmethod @pytest.fixture() def p(u): - ags = [r.atoms.select_atoms('name C* N*') - for r in u.residues] + ags = [r.atoms.select_atoms("name C* N*") for r in u.residues] p = polymer.PersistenceLength(ags) return p @@ -69,17 +68,19 @@ def test_lb(self, p_run): def test_fit(self, p_run): assert_almost_equal(p_run.results.lp, 6.504, 3) - assert len(p_run.results.fit) == len(p_run.results.bond_autocorrelation) + assert len(p_run.results.fit) == len( + p_run.results.bond_autocorrelation + ) def test_raise_NoDataError(self, p): - #Ensure that a NoDataError is raised if perform_fit() + # Ensure that a NoDataError is raised if perform_fit() # is called before the run() method of AnalysisBase with pytest.raises(NoDataError): p._perform_fit() def test_plot_ax_return(self, p_run): - '''Ensure that a matplotlib axis object is - returned when plot() is called.''' + """Ensure that a matplotlib axis object is + returned when plot() is called.""" actual = p_run.plot() expected = matplotlib.axes.Axes assert isinstance(actual, expected) @@ -125,7 +126,7 @@ def test_fit_noisy(self): class TestSortBackbone(object): @staticmethod - @pytest.fixture(scope='class') + @pytest.fixture(scope="class") def u(): return mda.Universe(TRZ_psf, TRZ) @@ -160,10 +161,9 @@ def test_branches(self, u): def test_circular(self): u = mda.Universe.empty(6, trajectory=True) # circular structure - bondlist = [(0, 1), (1, 2), (2, 3), - (3, 4), (4, 5), (5, 0)] + bondlist = [(0, 1), (1, 2), (2, 3), (3, 4), (4, 5), (5, 0)] u.add_TopologyAttr(Bonds(bondlist)) with pytest.raises(ValueError) as ex: polymer.sort_backbone(u.atoms) - assert 'cyclical' in str(ex.value) + assert "cyclical" in str(ex.value) diff --git a/testsuite/MDAnalysisTests/analysis/test_rdf.py b/testsuite/MDAnalysisTests/analysis/test_rdf.py index 90adef77e3a..d3507e56734 100644 --- a/testsuite/MDAnalysisTests/analysis/test_rdf.py +++ b/testsuite/MDAnalysisTests/analysis/test_rdf.py @@ -33,7 +33,7 @@ @pytest.fixture() def u(): u = mda.Universe(two_water_gro, in_memory=True) - u.add_TopologyAttr('chainIDs', u.atoms.resids) + u.add_TopologyAttr("chainIDs", u.atoms.resids) return u @@ -43,8 +43,8 @@ def sels(u): # (NOTE: requires in-memory coordinates to make them permanent) for at, (x, y) in zip(u.atoms, zip([1] * 3 + [2] * 3, [2, 1, 3] * 2)): at.position = x, y, 0.0 - s1 = u.select_atoms('name OW') - s2 = u.select_atoms('name HW1 HW2') + s1 = u.select_atoms("name OW") + s2 = u.select_atoms("name HW1 HW2") return s1, s2 @@ -96,10 +96,9 @@ def test_exclusion(sels): assert rdf.results.count.sum() == 4 -@pytest.mark.parametrize("attr, count", [ - ("residue", 8), - ("segment", 0), - ("chain", 8)]) +@pytest.mark.parametrize( + "attr, count", [("residue", 8), ("segment", 0), ("chain", 8)] +) def test_ignore_same_residues(sels, attr, count): # should see two distances with 4 counts each s1, s2 = sels @@ -110,13 +109,18 @@ def test_ignore_same_residues(sels, attr, count): def test_ignore_same_residues_fails(sels): s1, s2 = sels - with pytest.raises(ValueError, match="The exclude_same argument to InterRDF must be"): + with pytest.raises( + ValueError, match="The exclude_same argument to InterRDF must be" + ): InterRDF(s2, s2, exclude_same="unsupported").run() - with pytest.raises(ValueError, match="The exclude_same argument to InterRDF cannot be used with"): + with pytest.raises( + ValueError, + match="The exclude_same argument to InterRDF cannot be used with", + ): InterRDF(s2, s2, exclude_same="residue", exclusion_block=tuple()).run() - - + + @pytest.mark.parametrize("attr", ("rdf", "bins", "edges", "count")) def test_rdf_attr_warning(sels, attr): s1, s2 = sels @@ -126,18 +130,18 @@ def test_rdf_attr_warning(sels, attr): getattr(rdf, attr) is rdf.results[attr] -@pytest.mark.parametrize("norm, value", [ - ("density", 1.956823), - ("rdf", 244602.88385), - ("none", 4)]) +@pytest.mark.parametrize( + "norm, value", [("density", 1.956823), ("rdf", 244602.88385), ("none", 4)] +) def test_norm(sels, norm, value): s1, s2 = sels rdf = InterRDF(s1, s2, norm=norm).run() assert_allclose(max(rdf.results.rdf), value) -@pytest.mark.parametrize("norm, norm_required", [ - ("Density", "density"), (None, "none")]) +@pytest.mark.parametrize( + "norm, norm_required", [("Density", "density"), (None, "none")] +) def test_norm_values(sels, norm, norm_required): s1, s2 = sels rdf = InterRDF(s1, s2, norm=norm).run() diff --git a/testsuite/MDAnalysisTests/analysis/test_rdf_s.py b/testsuite/MDAnalysisTests/analysis/test_rdf_s.py index 49e2a66bd5b..5c6e8b6031a 100644 --- a/testsuite/MDAnalysisTests/analysis/test_rdf_s.py +++ b/testsuite/MDAnalysisTests/analysis/test_rdf_s.py @@ -30,23 +30,24 @@ from MDAnalysisTests.datafiles import GRO_MEMPROT, XTC_MEMPROT -@pytest.fixture(scope='module') +@pytest.fixture(scope="module") def u(): return mda.Universe(GRO_MEMPROT, XTC_MEMPROT) -@pytest.fixture(scope='module') +@pytest.fixture(scope="module") def sels(u): - s1 = u.select_atoms('name ZND and resid 289') + s1 = u.select_atoms("name ZND and resid 289") s2 = u.select_atoms( - '(name OD1 or name OD2) and resid 51 and sphzone 5.0 (resid 289)') - s3 = u.select_atoms('name ZND and (resid 291 or resid 292)') - s4 = u.select_atoms('(name OD1 or name OD2) and sphzone 5.0 (resid 291)') + "(name OD1 or name OD2) and resid 51 and sphzone 5.0 (resid 289)" + ) + s3 = u.select_atoms("name ZND and (resid 291 or resid 292)") + s4 = u.select_atoms("(name OD1 or name OD2) and sphzone 5.0 (resid 291)") ags = [[s1, s2], [s3, s4]] return ags -@pytest.fixture(scope='module') +@pytest.fixture(scope="module") def rdf(u, sels): return InterRDF_s(u, sels).run() @@ -100,22 +101,27 @@ def test_double_run(rdf): def test_cdf(rdf): rdf.get_cdf() - ref = rdf.results.count[0][0][0].sum()/rdf.n_frames + ref = rdf.results.count[0][0][0].sum() / rdf.n_frames assert rdf.results.cdf[0][0][0][-1] == ref -@pytest.mark.parametrize("density, value", [ - (None, 26551.55088100731), # default, like False (no kwarg, see below) - (False, 26551.55088100731), - (True, 0.021915460340071267)]) +@pytest.mark.parametrize( + "density, value", + [ + (None, 26551.55088100731), # default, like False (no kwarg, see below) + (False, 26551.55088100731), + (True, 0.021915460340071267), + ], +) def test_density(u, sels, density, value): - kwargs = {'density': density} if density is not None else {} + kwargs = {"density": density} if density is not None else {} rdf = InterRDF_s(u, sels, **kwargs).run() assert_almost_equal(max(rdf.results.rdf[0][0][0]), value) if not density: - s1 = u.select_atoms('name ZND and resid 289') + s1 = u.select_atoms("name ZND and resid 289") s2 = u.select_atoms( - 'name OD1 and resid 51 and sphzone 5.0 (resid 289)') + "name OD1 and resid 51 and sphzone 5.0 (resid 289)" + ) rdf_ref = InterRDF(s1, s2).run() assert_almost_equal(rdf_ref.results.rdf, rdf.results.rdf[0][0][0]) @@ -125,23 +131,29 @@ def test_overwrite_norm(u, sels): assert rdf.norm == "density" -@pytest.mark.parametrize("norm, value", [ - ("density", 0.021915460340071267), - ("rdf", 26551.55088100731), - ("none", 0.6)]) +@pytest.mark.parametrize( + "norm, value", + [ + ("density", 0.021915460340071267), + ("rdf", 26551.55088100731), + ("none", 0.6), + ], +) def test_norm(u, sels, norm, value): rdf = InterRDF_s(u, sels, norm=norm).run() assert_allclose(max(rdf.results.rdf[0][0][0]), value) if norm == "rdf": - s1 = u.select_atoms('name ZND and resid 289') + s1 = u.select_atoms("name ZND and resid 289") s2 = u.select_atoms( - 'name OD1 and resid 51 and sphzone 5.0 (resid 289)') + "name OD1 and resid 51 and sphzone 5.0 (resid 289)" + ) rdf_ref = InterRDF(s1, s2).run() assert_almost_equal(rdf_ref.results.rdf, rdf.results.rdf[0][0][0]) -@pytest.mark.parametrize("norm, norm_required", [ - ("Density", "density"), (None, "none")]) +@pytest.mark.parametrize( + "norm, norm_required", [("Density", "density"), (None, "none")] +) def test_norm_values(u, sels, norm, norm_required): rdf = InterRDF_s(u, sels, norm=norm).run() assert rdf.norm == norm_required diff --git a/testsuite/MDAnalysisTests/analysis/test_results.py b/testsuite/MDAnalysisTests/analysis/test_results.py index e3d8fa6ca95..adfbc4b5063 100644 --- a/testsuite/MDAnalysisTests/analysis/test_results.py +++ b/testsuite/MDAnalysisTests/analysis/test_results.py @@ -156,7 +156,9 @@ def merger(self): @pytest.mark.parametrize("n", [1, 2, 5, 14]) def test_all_results(self, results_0, results_1, merger, n): - objects = [obj for obj, _ in zip(cycle([results_0, results_1]), range(n))] + objects = [ + obj for obj, _ in zip(cycle([results_0, results_1]), range(n)) + ] arr = [i for _, i in zip(range(n), cycle([0, 1]))] answers = { @@ -169,14 +171,19 @@ def test_all_results(self, results_0, results_1, merger, n): results = merger.merge(objects) for attr, merged_value in results.items(): - assert_equal(merged_value, answers.get(attr), err_msg=f"{attr=}, {merged_value=}, {arr=}, {objects=}") + assert_equal( + merged_value, + answers.get(attr), + err_msg=f"{attr=}, {merged_value=}, {arr=}, {objects=}", + ) def test_missing_aggregator(self, results_0, results_1, merger): original_float_lookup = merger._lookup.get("float") merger._lookup["float"] = None - with pytest.raises(ValueError, - match="No aggregation function for key='float'"): + with pytest.raises( + ValueError, match="No aggregation function for key='float'" + ): merger.merge([results_0, results_1], require_all_aggregators=True) merger._lookup["float"] = original_float_lookup diff --git a/testsuite/MDAnalysisTests/analysis/test_rms.py b/testsuite/MDAnalysisTests/analysis/test_rms.py index deb46885c82..f39baa68f12 100644 --- a/testsuite/MDAnalysisTests/analysis/test_rms.py +++ b/testsuite/MDAnalysisTests/analysis/test_rms.py @@ -61,17 +61,20 @@ def u2(self): @pytest.fixture() def p_first(self, u): u.trajectory[0] - return u.select_atoms('protein') + return u.select_atoms("protein") @pytest.fixture() def p_last(self, u2): u2.trajectory[-1] - return u2.select_atoms('protein') + return u2.select_atoms("protein") # internal test def test_p_frames(self, p_first, p_last): # check that these fixtures are really different - assert p_first.universe.trajectory.ts.frame != p_last.universe.trajectory.ts.frame + assert ( + p_first.universe.trajectory.ts.frame + != p_last.universe.trajectory.ts.frame + ) assert not np.allclose(p_first.positions, p_last.positions) def test_no_center(self, a, b): @@ -83,14 +86,12 @@ def test_center(self, a, b): assert_almost_equal(rmsd, 0.0) def test_list(self, a, b): - rmsd = rms.rmsd(a.tolist(), - b.tolist(), - center=False) + rmsd = rms.rmsd(a.tolist(), b.tolist(), center=False) assert_almost_equal(rmsd, 1.0) - @pytest.mark.parametrize('dtype', [np.float32, np.float64]) + @pytest.mark.parametrize("dtype", [np.float32, np.float64]) def test_superposition(self, a, b, u, dtype): - bb = u.atoms.select_atoms('backbone') + bb = u.atoms.select_atoms("backbone") a = bb.positions.copy() u.trajectory[-1] b = bb.positions.copy() @@ -110,20 +111,29 @@ def test_weights(self, a, b): def test_weights_and_superposition_1(self, u): weights = np.ones(len(u.trajectory[0])) - weighted = rms.rmsd(u.trajectory[0], u.trajectory[1], - weights=weights, superposition=True) - firstCoords = rms.rmsd(u.trajectory[0], u.trajectory[1], - superposition=True) + weighted = rms.rmsd( + u.trajectory[0], + u.trajectory[1], + weights=weights, + superposition=True, + ) + firstCoords = rms.rmsd( + u.trajectory[0], u.trajectory[1], superposition=True + ) assert_almost_equal(weighted, firstCoords, decimal=5) def test_weights_and_superposition_2(self, u): weights = np.zeros(len(u.trajectory[0])) weights[:100] = 1 - weighted = rms.rmsd(u.trajectory[0], u.trajectory[-1], - weights=weights, superposition=True) - firstCoords = rms.rmsd(u.trajectory[0][:100], - u.trajectory[-1][:100], - superposition=True) + weighted = rms.rmsd( + u.trajectory[0], + u.trajectory[-1], + weights=weights, + superposition=True, + ) + firstCoords = rms.rmsd( + u.trajectory[0][:100], u.trajectory[-1][:100], superposition=True + ) # very close to zero, change significant decimal places to 5 assert_almost_equal(weighted, firstCoords, decimal=5) @@ -162,7 +172,7 @@ def universe(self): @pytest.fixture() def outfile(self, tmpdir): - return os.path.join(str(tmpdir), 'rmsd.txt') + return os.path.join(str(tmpdir), "rmsd.txt") @pytest.fixture() def correct_values(self): @@ -178,13 +188,11 @@ def correct_values_mass_add_ten(self): @pytest.fixture() def correct_values_group(self): - return [[0, 1, 0, 0, 0], - [49, 50, 4.7857, 4.7048, 4.6924]] + return [[0, 1, 0, 0, 0], [49, 50, 4.7857, 4.7048, 4.6924]] @pytest.fixture() def correct_values_backbone_group(self): - return [[0, 1, 0, 0, 0], - [49, 50, 4.6997, 1.9154, 2.7139]] + return [[0, 1, 0, 0, 0], [49, 50, 4.6997, 1.9154, 2.7139]] def test_rmsd(self, universe, correct_values, client_RMSD): # client_RMSD is defined in testsuite/analysis/conftest.py @@ -192,197 +200,276 @@ def test_rmsd(self, universe, correct_values, client_RMSD): # collect all possible backends and reasonable number of workers # for a given AnalysisBase subclass, and extend the tests # to run with all of them. - RMSD = MDAnalysis.analysis.rms.RMSD(universe, select='name CA') + RMSD = MDAnalysis.analysis.rms.RMSD(universe, select="name CA") RMSD.run(step=49, **client_RMSD) - assert_almost_equal(RMSD.results.rmsd, correct_values, 4, - err_msg="error: rmsd profile should match" + - "test values") + assert_almost_equal( + RMSD.results.rmsd, + correct_values, + 4, + err_msg="error: rmsd profile should match" + "test values", + ) def test_rmsd_frames(self, universe, correct_values, client_RMSD): - RMSD = MDAnalysis.analysis.rms.RMSD(universe, select='name CA') + RMSD = MDAnalysis.analysis.rms.RMSD(universe, select="name CA") RMSD.run(frames=[0, 49], **client_RMSD) - assert_almost_equal(RMSD.results.rmsd, correct_values, 4, - err_msg="error: rmsd profile should match" + - "test values") - - def test_rmsd_unicode_selection(self, universe, correct_values, client_RMSD): - RMSD = MDAnalysis.analysis.rms.RMSD(universe, select=u'name CA') + assert_almost_equal( + RMSD.results.rmsd, + correct_values, + 4, + err_msg="error: rmsd profile should match" + "test values", + ) + + def test_rmsd_unicode_selection( + self, universe, correct_values, client_RMSD + ): + RMSD = MDAnalysis.analysis.rms.RMSD(universe, select="name CA") RMSD.run(step=49, **client_RMSD) - assert_almost_equal(RMSD.results.rmsd, correct_values, 4, - err_msg="error: rmsd profile should match" + - "test values") + assert_almost_equal( + RMSD.results.rmsd, + correct_values, + 4, + err_msg="error: rmsd profile should match" + "test values", + ) def test_rmsd_atomgroup_selections(self, universe, client_RMSD): # see Issue #1684 - R1 = MDAnalysis.analysis.rms.RMSD(universe.atoms, - select="resid 1-30").run(**client_RMSD) - R2 = MDAnalysis.analysis.rms.RMSD(universe.atoms.select_atoms("name CA"), - select="resid 1-30").run(**client_RMSD) + R1 = MDAnalysis.analysis.rms.RMSD( + universe.atoms, select="resid 1-30" + ).run(**client_RMSD) + R2 = MDAnalysis.analysis.rms.RMSD( + universe.atoms.select_atoms("name CA"), select="resid 1-30" + ).run(**client_RMSD) assert not np.allclose(R1.results.rmsd[:, 2], R2.results.rmsd[:, 2]) def test_rmsd_single_frame(self, universe, client_RMSD): - RMSD = MDAnalysis.analysis.rms.RMSD(universe, select='name CA', - ).run(start=5, stop=6, **client_RMSD) + RMSD = MDAnalysis.analysis.rms.RMSD( + universe, + select="name CA", + ).run(start=5, stop=6, **client_RMSD) single_frame = [[5, 6, 0.91544906]] - assert_almost_equal(RMSD.results.rmsd, single_frame, 4, - err_msg="error: rmsd profile should match" + - "test values") + assert_almost_equal( + RMSD.results.rmsd, + single_frame, + 4, + err_msg="error: rmsd profile should match" + "test values", + ) def test_mass_weighted(self, universe, correct_values, client_RMSD): # mass weighting the CA should give the same answer as weighing # equally because all CA have the same mass - RMSD = MDAnalysis.analysis.rms.RMSD(universe, select='name CA', - weights='mass').run(step=49, **client_RMSD) + RMSD = MDAnalysis.analysis.rms.RMSD( + universe, select="name CA", weights="mass" + ).run(step=49, **client_RMSD) - assert_almost_equal(RMSD.results.rmsd, correct_values, 4, - err_msg="error: rmsd profile should match" - "test values") + assert_almost_equal( + RMSD.results.rmsd, + correct_values, + 4, + err_msg="error: rmsd profile should match" "test values", + ) def test_custom_weighted(self, universe, correct_values_mass, client_RMSD): - RMSD = MDAnalysis.analysis.rms.RMSD(universe, weights="mass").run(step=49, **client_RMSD) + RMSD = MDAnalysis.analysis.rms.RMSD(universe, weights="mass").run( + step=49, **client_RMSD + ) - assert_almost_equal(RMSD.results.rmsd, correct_values_mass, 4, - err_msg="error: rmsd profile should match" - "test values") + assert_almost_equal( + RMSD.results.rmsd, + correct_values_mass, + 4, + err_msg="error: rmsd profile should match" "test values", + ) def test_weights_mass_is_mass_weighted(self, universe, client_RMSD): - RMSD_mass = MDAnalysis.analysis.rms.RMSD(universe, - weights="mass").run(step=49, **client_RMSD) - RMSD_cust = MDAnalysis.analysis.rms.RMSD(universe, - weights=universe.atoms.masses).run(step=49, **client_RMSD) - assert_almost_equal(RMSD_mass.results.rmsd, RMSD_cust.results.rmsd, 4, - err_msg="error: rmsd profiles should match for 'mass' " - "and universe.atoms.masses") - - def test_custom_weighted_list(self, universe, correct_values_mass, client_RMSD): + RMSD_mass = MDAnalysis.analysis.rms.RMSD(universe, weights="mass").run( + step=49, **client_RMSD + ) + RMSD_cust = MDAnalysis.analysis.rms.RMSD( + universe, weights=universe.atoms.masses + ).run(step=49, **client_RMSD) + assert_almost_equal( + RMSD_mass.results.rmsd, + RMSD_cust.results.rmsd, + 4, + err_msg="error: rmsd profiles should match for 'mass' " + "and universe.atoms.masses", + ) + + def test_custom_weighted_list( + self, universe, correct_values_mass, client_RMSD + ): weights = universe.atoms.masses - RMSD = MDAnalysis.analysis.rms.RMSD(universe, - weights=list(weights)).run(step=49, **client_RMSD) - assert_almost_equal(RMSD.results.rmsd, correct_values_mass, 4, - err_msg="error: rmsd profile should match" + - "test values") - - def test_custom_groupselection_weights_applied_1D_array(self, universe, client_RMSD): - RMSD = MDAnalysis.analysis.rms.RMSD(universe, - select='backbone', - groupselections=['name CA and resid 1-5', 'name CA and resid 1'], - weights=None, - weights_groupselections=[[1, 0, 0, 0, 0], None]).run(step=49, - **client_RMSD - ) - - assert_almost_equal(RMSD.results.rmsd.T[3], RMSD.results.rmsd.T[4], 4, - err_msg="error: rmsd profile should match " - "for applied weight array and selected resid") - - def test_custom_groupselection_weights_applied_mass(self, universe, correct_values_mass, client_RMSD): - RMSD = MDAnalysis.analysis.rms.RMSD(universe, - select='backbone', - groupselections=['all', 'all'], - weights=None, - weights_groupselections=['mass', - universe.atoms.masses]).run(step=49, - **client_RMSD - ) - - assert_almost_equal(RMSD.results.rmsd.T[3], RMSD.results.rmsd.T[4], 4, - err_msg="error: rmsd profile should match " - "between applied mass and universe.atoms.masses") + RMSD = MDAnalysis.analysis.rms.RMSD( + universe, weights=list(weights) + ).run(step=49, **client_RMSD) + assert_almost_equal( + RMSD.results.rmsd, + correct_values_mass, + 4, + err_msg="error: rmsd profile should match" + "test values", + ) + + def test_custom_groupselection_weights_applied_1D_array( + self, universe, client_RMSD + ): + RMSD = MDAnalysis.analysis.rms.RMSD( + universe, + select="backbone", + groupselections=["name CA and resid 1-5", "name CA and resid 1"], + weights=None, + weights_groupselections=[[1, 0, 0, 0, 0], None], + ).run(step=49, **client_RMSD) + + assert_almost_equal( + RMSD.results.rmsd.T[3], + RMSD.results.rmsd.T[4], + 4, + err_msg="error: rmsd profile should match " + "for applied weight array and selected resid", + ) + + def test_custom_groupselection_weights_applied_mass( + self, universe, correct_values_mass, client_RMSD + ): + RMSD = MDAnalysis.analysis.rms.RMSD( + universe, + select="backbone", + groupselections=["all", "all"], + weights=None, + weights_groupselections=["mass", universe.atoms.masses], + ).run(step=49, **client_RMSD) + + assert_almost_equal( + RMSD.results.rmsd.T[3], + RMSD.results.rmsd.T[4], + 4, + err_msg="error: rmsd profile should match " + "between applied mass and universe.atoms.masses", + ) def test_rmsd_scalar_weights_raises_ValueError(self, universe): with pytest.raises(ValueError): - RMSD = MDAnalysis.analysis.rms.RMSD( - universe, weights=42) + RMSD = MDAnalysis.analysis.rms.RMSD(universe, weights=42) def test_rmsd_string_weights_raises_ValueError(self, universe): with pytest.raises(ValueError): - RMSD = MDAnalysis.analysis.rms.RMSD( - universe, weights="Jabberwock") + RMSD = MDAnalysis.analysis.rms.RMSD(universe, weights="Jabberwock") def test_rmsd_mismatched_weights_raises_ValueError(self, universe): with pytest.raises(ValueError): RMSD = MDAnalysis.analysis.rms.RMSD( - universe, weights=universe.atoms.masses[:-1]) + universe, weights=universe.atoms.masses[:-1] + ) - def test_rmsd_misuse_weights_for_groupselection_raises_TypeError(self, universe): + def test_rmsd_misuse_weights_for_groupselection_raises_TypeError( + self, universe + ): with pytest.raises(TypeError): RMSD = MDAnalysis.analysis.rms.RMSD( - universe, groupselections=['all'], - weights=[universe.atoms.masses, universe.atoms.masses[:-1]]) - - def test_rmsd_mismatched_weights_in_groupselection_raises_ValueError(self, universe): + universe, + groupselections=["all"], + weights=[universe.atoms.masses, universe.atoms.masses[:-1]], + ) + + def test_rmsd_mismatched_weights_in_groupselection_raises_ValueError( + self, universe + ): with pytest.raises(ValueError): RMSD = MDAnalysis.analysis.rms.RMSD( - universe, groupselections=['all'], + universe, + groupselections=["all"], weights=universe.atoms.masses, - weights_groupselections = [universe.atoms.masses[:-1]]) + weights_groupselections=[universe.atoms.masses[:-1]], + ) def test_rmsd_list_of_weights_wrong_length(self, universe): with pytest.raises(ValueError): RMSD = MDAnalysis.analysis.rms.RMSD( - universe, groupselections=['backbone', 'name CA'], - weights='mass', - weights_groupselections=[None]) - - def test_rmsd_group_selections(self, universe, correct_values_group, client_RMSD): - RMSD = MDAnalysis.analysis.rms.RMSD(universe, - groupselections=['backbone', 'name CA'] - ).run(step=49, **client_RMSD) - assert_almost_equal(RMSD.results.rmsd, correct_values_group, 4, - err_msg="error: rmsd profile should match" - "test values") - - def test_rmsd_backbone_and_group_selection(self, universe, - correct_values_backbone_group, - client_RMSD): + universe, + groupselections=["backbone", "name CA"], + weights="mass", + weights_groupselections=[None], + ) + + def test_rmsd_group_selections( + self, universe, correct_values_group, client_RMSD + ): + RMSD = MDAnalysis.analysis.rms.RMSD( + universe, groupselections=["backbone", "name CA"] + ).run(step=49, **client_RMSD) + assert_almost_equal( + RMSD.results.rmsd, + correct_values_group, + 4, + err_msg="error: rmsd profile should match" "test values", + ) + + def test_rmsd_backbone_and_group_selection( + self, universe, correct_values_backbone_group, client_RMSD + ): RMSD = MDAnalysis.analysis.rms.RMSD( universe, reference=universe, select="backbone", - groupselections=['backbone and resid 1:10', - 'backbone and resid 10:20']).run(step=49, **client_RMSD) + groupselections=[ + "backbone and resid 1:10", + "backbone and resid 10:20", + ], + ).run(step=49, **client_RMSD) assert_almost_equal( - RMSD.results.rmsd, correct_values_backbone_group, 4, - err_msg="error: rmsd profile should match test values") + RMSD.results.rmsd, + correct_values_backbone_group, + 4, + err_msg="error: rmsd profile should match test values", + ) def test_ref_length_unequal_len(self, universe): reference = MDAnalysis.Universe(PSF, DCD) reference.atoms = reference.atoms[:-1] with pytest.raises(SelectionError): - RMSD = MDAnalysis.analysis.rms.RMSD(universe, - reference=reference) + RMSD = MDAnalysis.analysis.rms.RMSD(universe, reference=reference) def test_mass_mismatches(self, universe): reference = MDAnalysis.Universe(PSF, DCD) reference.atoms.masses = 10 with pytest.raises(SelectionError): - RMSD = MDAnalysis.analysis.rms.RMSD(universe, - reference=reference) + RMSD = MDAnalysis.analysis.rms.RMSD(universe, reference=reference) - def test_ref_mobile_mass_mismapped(self, universe,correct_values_mass_add_ten, client_RMSD): + def test_ref_mobile_mass_mismapped( + self, universe, correct_values_mass_add_ten, client_RMSD + ): reference = MDAnalysis.Universe(PSF, DCD) universe.atoms.masses = universe.atoms.masses + 10 - RMSD = MDAnalysis.analysis.rms.RMSD(universe, - reference=reference, - select='all', - weights='mass', - tol_mass=100) + RMSD = MDAnalysis.analysis.rms.RMSD( + universe, + reference=reference, + select="all", + weights="mass", + tol_mass=100, + ) RMSD.run(step=49, **client_RMSD) - assert_almost_equal(RMSD.results.rmsd, correct_values_mass_add_ten, 4, - err_msg="error: rmsd profile should match " - "between true values and calculated values") + assert_almost_equal( + RMSD.results.rmsd, + correct_values_mass_add_ten, + 4, + err_msg="error: rmsd profile should match " + "between true values and calculated values", + ) def test_group_selections_unequal_len(self, universe): reference = MDAnalysis.Universe(PSF, DCD) - reference.atoms[0].residue.resname = 'NOTMET' + reference.atoms[0].residue.resname = "NOTMET" with pytest.raises(SelectionError): - RMSD = MDAnalysis.analysis.rms.RMSD(universe, - reference=reference, - groupselections=['resname MET', 'type NH3']) + RMSD = MDAnalysis.analysis.rms.RMSD( + universe, + reference=reference, + groupselections=["resname MET", "type NH3"], + ) def test_rmsd_attr_warning(self, universe, client_RMSD): - RMSD = MDAnalysis.analysis.rms.RMSD( - universe, select='name CA').run(stop=2, **client_RMSD) + RMSD = MDAnalysis.analysis.rms.RMSD(universe, select="name CA").run( + stop=2, **client_RMSD + ) wmsg = "The `rmsd` attribute was deprecated in MDAnalysis 2.0.0" with pytest.warns(DeprecationWarning, match=wmsg): @@ -395,23 +482,29 @@ def universe(self): return mda.Universe(GRO, XTC) def test_rmsf(self, universe, client_RMSF): - rmsfs = rms.RMSF(universe.select_atoms('name CA')) + rmsfs = rms.RMSF(universe.select_atoms("name CA")) rmsfs.run(**client_RMSF) test_rmsfs = np.load(rmsfArray) - assert_almost_equal(rmsfs.results.rmsf, test_rmsfs, 5, - err_msg="error: rmsf profile should match test " - "values") + assert_almost_equal( + rmsfs.results.rmsf, + test_rmsfs, + 5, + err_msg="error: rmsf profile should match test " "values", + ) def test_rmsf_single_frame(self, universe, client_RMSF): - rmsfs = rms.RMSF(universe.select_atoms('name CA')).run(start=5, stop=6, **client_RMSF) + rmsfs = rms.RMSF(universe.select_atoms("name CA")).run( + start=5, stop=6, **client_RMSF + ) - assert_almost_equal(rmsfs.results.rmsf, 0, 5, - err_msg="error: rmsfs should all be zero") + assert_almost_equal( + rmsfs.results.rmsf, 0, 5, err_msg="error: rmsfs should all be zero" + ) def test_rmsf_identical_frames(self, universe, tmpdir, client_RMSF): - outfile = os.path.join(str(tmpdir), 'rmsf.xtc') + outfile = os.path.join(str(tmpdir), "rmsf.xtc") # write a dummy trajectory of all the same frame with mda.Writer(outfile, universe.atoms.n_atoms) as W: @@ -419,13 +512,16 @@ def test_rmsf_identical_frames(self, universe, tmpdir, client_RMSF): W.write(universe) universe = mda.Universe(GRO, outfile) - rmsfs = rms.RMSF(universe.select_atoms('name CA')) + rmsfs = rms.RMSF(universe.select_atoms("name CA")) rmsfs.run(**client_RMSF) - assert_almost_equal(rmsfs.results.rmsf, 0, 5, - err_msg="error: rmsfs should all be 0") + assert_almost_equal( + rmsfs.results.rmsf, 0, 5, err_msg="error: rmsfs should all be 0" + ) def test_rmsf_attr_warning(self, universe, client_RMSF): - rmsfs = rms.RMSF(universe.select_atoms('name CA')).run(stop=2, **client_RMSF) + rmsfs = rms.RMSF(universe.select_atoms("name CA")).run( + stop=2, **client_RMSF + ) wmsg = "The `rmsf` attribute was deprecated in MDAnalysis 2.0.0" with pytest.warns(DeprecationWarning, match=wmsg): @@ -437,7 +533,7 @@ def test_rmsf_attr_warning(self, universe, client_RMSF): [ (MDAnalysis.analysis.rms.RMSD, True), (MDAnalysis.analysis.rms.RMSF, False), - ] + ], ) def test_class_is_parallelizable(classname, is_parallelizable): assert classname._analysis_algorithm_is_parallelizable == is_parallelizable @@ -446,9 +542,16 @@ def test_class_is_parallelizable(classname, is_parallelizable): @pytest.mark.parametrize( "classname,backends", [ - (MDAnalysis.analysis.rms.RMSD, ('serial', 'multiprocessing', 'dask',)), - (MDAnalysis.analysis.rms.RMSF, ('serial',)), - ] + ( + MDAnalysis.analysis.rms.RMSD, + ( + "serial", + "multiprocessing", + "dask", + ), + ), + (MDAnalysis.analysis.rms.RMSF, ("serial",)), + ], ) def test_supported_backends(classname, backends): assert classname.get_supported_backends() == backends diff --git a/testsuite/MDAnalysisTests/analysis/test_wbridge.py b/testsuite/MDAnalysisTests/analysis/test_wbridge.py index 39fe372409c..2b53d2f1d35 100644 --- a/testsuite/MDAnalysisTests/analysis/test_wbridge.py +++ b/testsuite/MDAnalysisTests/analysis/test_wbridge.py @@ -2,157 +2,160 @@ from collections import defaultdict from numpy.testing import ( - assert_equal, assert_array_equal,) + assert_equal, + assert_array_equal, +) import pytest import MDAnalysis from MDAnalysis.analysis.hydrogenbonds.wbridge_analysis import ( - WaterBridgeAnalysis, ) + WaterBridgeAnalysis, +) class TestWaterBridgeAnalysis(object): @staticmethod - @pytest.fixture(scope='class') + @pytest.fixture(scope="class") def universe_empty(): - '''A universe with no hydrogen bonds''' - grofile = '''Test gro file + """A universe with no hydrogen bonds""" + grofile = """Test gro file 5 1ALA N 1 0.000 0.000 0.000 1ALA H 2 0.100 0.000 0.000 2SOL OW 3 3.000 0.000 0.000 4ALA H 4 0.500 0.000 0.000 4ALA N 5 0.600 0.000 0.000 - 1.0 1.0 1.0''' - u = MDAnalysis.Universe(StringIO(grofile), format='gro') + 1.0 1.0 1.0""" + u = MDAnalysis.Universe(StringIO(grofile), format="gro") return u @staticmethod - @pytest.fixture(scope='class') + @pytest.fixture(scope="class") def universe_DA(): - '''A universe with one hydrogen bond acceptor bonding to a hydrogen bond - donor''' - grofile = '''Test gro file + """A universe with one hydrogen bond acceptor bonding to a hydrogen bond + donor""" + grofile = """Test gro file 3 1ALA N 1 0.000 0.000 0.000 1ALA H 2 0.100 0.000 0.000 4ALA O 3 0.300 0.000 0.000 - 1.0 1.0 1.0''' - u = MDAnalysis.Universe(StringIO(grofile), format='gro') + 1.0 1.0 1.0""" + u = MDAnalysis.Universe(StringIO(grofile), format="gro") return u @staticmethod - @pytest.fixture(scope='class') + @pytest.fixture(scope="class") def universe_DA_PBC(): - '''A universe with one hydrogen bond acceptor bonding to a hydrogen bond - donor but in a PBC condition''' - grofile = '''Test gro file + """A universe with one hydrogen bond acceptor bonding to a hydrogen bond + donor but in a PBC condition""" + grofile = """Test gro file 3 1ALA N 1 0.800 0.000 0.000 1ALA H 2 0.900 0.000 0.000 4ALA O 3 0.100 0.000 0.000 - 1.0 1.0 1.0''' - u = MDAnalysis.Universe(StringIO(grofile), format='gro') + 1.0 1.0 1.0""" + u = MDAnalysis.Universe(StringIO(grofile), format="gro") return u @staticmethod - @pytest.fixture(scope='class') + @pytest.fixture(scope="class") def universe_AD(): - '''A universe with one hydrogen bond donor bonding to a hydrogen bond - acceptor''' - grofile = '''Test gro file + """A universe with one hydrogen bond donor bonding to a hydrogen bond + acceptor""" + grofile = """Test gro file 3 1ALA O 1 0.000 0.000 0.000 4ALA H 2 0.200 0.000 0.000 4ALA N 3 0.300 0.000 0.000 - 1.0 1.0 1.0''' - u = MDAnalysis.Universe(StringIO(grofile), format='gro') + 1.0 1.0 1.0""" + u = MDAnalysis.Universe(StringIO(grofile), format="gro") return u @staticmethod - @pytest.fixture(scope='class') + @pytest.fixture(scope="class") def universe_loop(): - '''A universe with one hydrogen bond acceptor bonding to a water which - bonds back to the first hydrogen bond acceptor and thus form a loop''' - grofile = '''Test gro file + """A universe with one hydrogen bond acceptor bonding to a water which + bonds back to the first hydrogen bond acceptor and thus form a loop""" + grofile = """Test gro file 5 1ALA O 1 0.000 0.001 0.000 2SOL OW 2 0.300 0.001 0.000 2SOL HW1 3 0.200 0.002 0.000 2SOL HW2 4 0.200 0.000 0.000 4ALA O 5 0.600 0.000 0.000 - 1.0 1.0 1.0''' - u = MDAnalysis.Universe(StringIO(grofile), format='gro') + 1.0 1.0 1.0""" + u = MDAnalysis.Universe(StringIO(grofile), format="gro") return u @staticmethod - @pytest.fixture(scope='class') + @pytest.fixture(scope="class") def universe_DWA(): - '''A universe with one hydrogen bond donor bonding to a hydrogen bond - acceptor through a water''' - grofile = '''Test gro file + """A universe with one hydrogen bond donor bonding to a hydrogen bond + acceptor through a water""" + grofile = """Test gro file 5 1ALA N 1 0.000 0.000 0.000 1ALA H 2 0.100 0.000 0.000 2SOL OW 3 0.300 0.000 0.000 2SOL HW2 4 0.400 0.000 0.000 4ALA O 5 0.600 0.000 0.000 - 1.0 1.0 1.0''' - u = MDAnalysis.Universe(StringIO(grofile), format='gro') + 1.0 1.0 1.0""" + u = MDAnalysis.Universe(StringIO(grofile), format="gro") return u @staticmethod - @pytest.fixture(scope='class') + @pytest.fixture(scope="class") def universe_DWD(): - '''A universe with one hydrogen bond donor bonding to a hydrogen bond - donor through a water''' - grofile = '''Test gro file + """A universe with one hydrogen bond donor bonding to a hydrogen bond + donor through a water""" + grofile = """Test gro file 5 1ALA N 1 0.000 0.000 0.000 1ALA H 2 0.100 0.000 0.000 2SOL OW 3 0.300 0.000 0.000 4ALA H 4 0.500 0.000 0.000 4ALA N 5 0.600 0.000 0.000 - 1.0 1.0 1.0''' - u = MDAnalysis.Universe(StringIO(grofile), format='gro') + 1.0 1.0 1.0""" + u = MDAnalysis.Universe(StringIO(grofile), format="gro") return u @staticmethod - @pytest.fixture(scope='class') + @pytest.fixture(scope="class") def universe_AWA(): - '''A universe with two hydrogen bond acceptor are joined by a water''' - grofile = '''Test gro file + """A universe with two hydrogen bond acceptor are joined by a water""" + grofile = """Test gro file 5 1ALA O 1 0.000 0.000 0.000 2SOL OW 2 0.300 0.000 0.000 2SOL HW1 3 0.200 0.000 0.000 2SOL HW2 4 0.400 0.000 0.000 4ALA O 5 0.600 0.000 0.000 - 1.0 1.0 1.0''' - u = MDAnalysis.Universe(StringIO(grofile), format='gro') + 1.0 1.0 1.0""" + u = MDAnalysis.Universe(StringIO(grofile), format="gro") return u @staticmethod - @pytest.fixture(scope='class') + @pytest.fixture(scope="class") def universe_AWD(): - '''A universe with one hydrogen bond acceptor bonding to a hydrogen - bond donor through a water''' - grofile = '''Test gro file + """A universe with one hydrogen bond acceptor bonding to a hydrogen + bond donor through a water""" + grofile = """Test gro file 5 1ALA O 1 0.000 0.000 0.000 2SOL OW 2 0.300 0.000 0.000 2SOL HW1 3 0.200 0.000 0.000 4ALA H 4 0.500 0.000 0.000 4ALA N 5 0.600 0.000 0.000 - 1.0 1.0 1.0''' - u = MDAnalysis.Universe(StringIO(grofile), format='gro') + 1.0 1.0 1.0""" + u = MDAnalysis.Universe(StringIO(grofile), format="gro") return u @staticmethod - @pytest.fixture(scope='class') + @pytest.fixture(scope="class") def universe_AWWA(): - '''A universe with one hydrogen bond acceptor bonding to a hydrogen bond - acceptor through two waters''' - grofile = '''Test gro file + """A universe with one hydrogen bond acceptor bonding to a hydrogen bond + acceptor through two waters""" + grofile = """Test gro file 7 1ALA O 1 0.000 0.000 0.000 2SOL OW 2 0.300 0.000 0.000 @@ -161,16 +164,16 @@ def universe_AWWA(): 3SOL OW 5 0.600 0.000 0.000 3SOL HW1 6 0.700 0.000 0.000 4ALA O 7 0.900 0.000 0.000 - 1.0 1.0 1.0''' - u = MDAnalysis.Universe(StringIO(grofile), format='gro') + 1.0 1.0 1.0""" + u = MDAnalysis.Universe(StringIO(grofile), format="gro") return u @staticmethod - @pytest.fixture(scope='class') + @pytest.fixture(scope="class") def universe_AWWWA(): - '''A universe with one hydrogen bond acceptor bonding to a hydrogen bond - acceptor through three waters''' - grofile = '''Test gro file + """A universe with one hydrogen bond acceptor bonding to a hydrogen bond + acceptor through three waters""" + grofile = """Test gro file 9 1ALA O 1 0.000 0.000 0.000 2SOL OW 2 0.300 0.000 0.000 @@ -181,16 +184,16 @@ def universe_AWWWA(): 4SOL OW 7 0.900 0.000 0.000 4SOL HW1 8 1.000 0.000 0.000 5ALA O 9 1.200 0.000 0.000 - 10.0 10.0 10.0''' - u = MDAnalysis.Universe(StringIO(grofile), format='gro') + 10.0 10.0 10.0""" + u = MDAnalysis.Universe(StringIO(grofile), format="gro") return u @staticmethod - @pytest.fixture(scope='class') + @pytest.fixture(scope="class") def universe_AWWWWA(): - '''A universe with one hydrogen bond acceptor bonding to a hydrogen bond - acceptor through three waters''' - grofile = '''Test gro file + """A universe with one hydrogen bond acceptor bonding to a hydrogen bond + acceptor through three waters""" + grofile = """Test gro file 11 1ALA O 1 0.000 0.000 0.000 2SOL OW 2 0.300 0.000 0.000 @@ -203,16 +206,16 @@ def universe_AWWWWA(): 5SOL OW 9 1.200 0.000 0.000 5SOL HW1 10 1.300 0.000 0.000 6ALA O 11 1.400 0.000 0.000 - 10.0 10.0 10.0''' - u = MDAnalysis.Universe(StringIO(grofile), format='gro') + 10.0 10.0 10.0""" + u = MDAnalysis.Universe(StringIO(grofile), format="gro") return u @staticmethod - @pytest.fixture(scope='class') + @pytest.fixture(scope="class") def universe_branch(): - '''A universe with one hydrogen bond acceptor bonding to two hydrogen - bond acceptor in selection 2''' - grofile = '''Test gro file + """A universe with one hydrogen bond acceptor bonding to two hydrogen + bond acceptor in selection 2""" + grofile = """Test gro file 9 1ALA O 1 0.000 0.000 0.000 2SOL OW 2 0.300 0.000 0.000 @@ -223,16 +226,16 @@ def universe_branch(): 3SOL HW2 7 0.600 0.100 0.000 4ALA O 8 0.900 0.000 0.000 5ALA O 9 0.600 0.300 0.000 - 1.0 1.0 1.0''' - u = MDAnalysis.Universe(StringIO(grofile), format='gro') + 1.0 1.0 1.0""" + u = MDAnalysis.Universe(StringIO(grofile), format="gro") return u @staticmethod - @pytest.fixture(scope='class') + @pytest.fixture(scope="class") def universe_AWA_AWWA(): - '''A universe with one hydrogen bond acceptors are bonded through one or - two water''' - grofile = '''Test gro file + """A universe with one hydrogen bond acceptors are bonded through one or + two water""" + grofile = """Test gro file 12 1ALA O 1 0.000 0.000 0.000 2SOL OW 2 0.300 0.000 0.000 @@ -246,15 +249,15 @@ def universe_AWA_AWWA(): 7SOL OW 10 0.600 1.000 0.000 7SOL HW1 11 0.700 1.000 0.000 8ALA O 12 0.900 1.000 0.000 - 1.0 1.0 1.0''' - u = MDAnalysis.Universe(StringIO(grofile), format='gro') + 1.0 1.0 1.0""" + u = MDAnalysis.Universe(StringIO(grofile), format="gro") return u @staticmethod - @pytest.fixture(scope='class') + @pytest.fixture(scope="class") def wb_multiframe(): - '''A water bridge object with multipley frames''' - grofile = '''Test gro file + """A water bridge object with multipley frames""" + grofile = """Test gro file 13 1ALA O 1 0.000 0.000 0.000 1ALA H 2 0.000 0.000 0.000 @@ -269,113 +272,164 @@ def wb_multiframe(): 5SOL HW1 11 1.300 0.000 0.000 6ALA H 12 1.400 0.000 0.000 6ALA O 13 1.400 0.000 0.000 - 10.0 10.0 10.0''' - u = MDAnalysis.Universe(StringIO(grofile), format='gro') - wb = WaterBridgeAnalysis(u, 'protein and (resid 1)', 'protein and (resid 4)', - order=4) + 10.0 10.0 10.0""" + u = MDAnalysis.Universe(StringIO(grofile), format="gro") + wb = WaterBridgeAnalysis( + u, "protein and (resid 1)", "protein and (resid 4)", order=4 + ) # Build an dummy WaterBridgeAnalysis object for testing wb.results.network = [] wb.results.network.append({(1, 0, 12, None, 2.0, 180.0): None}) wb.results.network.append({(0, None, 12, 13, 2.0, 180.0): None}) - wb.results.network.append({(1, 0, 3, None, 2.0, 180.0): - {(4, 2, 12, None, 2.0, 180.0): None}}) - wb.results.network.append({(0, None, 3, 2, 2.0, 180.0): - {(4, 2, 5, None, 2.0, 180.0): - {(5, None, 11, 12, 2.0, 180.0): None}}}) + wb.results.network.append( + {(1, 0, 3, None, 2.0, 180.0): {(4, 2, 12, None, 2.0, 180.0): None}} + ) + wb.results.network.append( + { + (0, None, 3, 2, 2.0, 180.0): { + (4, 2, 5, None, 2.0, 180.0): { + (5, None, 11, 12, 2.0, 180.0): None + } + } + } + ) wb.timesteps = range(len(wb.results.network)) return wb def test_nodata(self, universe_DA): - '''Test if the funtions can run when there is no data. - This is achieved by not runing the run() first.''' - wb = WaterBridgeAnalysis(universe_DA, 'protein and (resid 1)', - 'protein and (resid 4)', order=0) + """Test if the funtions can run when there is no data. + This is achieved by not runing the run() first.""" + wb = WaterBridgeAnalysis( + universe_DA, + "protein and (resid 1)", + "protein and (resid 4)", + order=0, + ) wb.generate_table() assert_equal(wb.timesteps_by_type(), None) assert_equal(wb.count_by_time(), None) assert_equal(wb.count_by_type(), None) def test_selection_type_error(self, universe_DA): - '''Test the case when the wrong selection1_type is given''' + """Test the case when the wrong selection1_type is given""" try: - wb = WaterBridgeAnalysis(universe_DA, 'protein and (resid 1)', - 'protein and (resid 4)', order=0, selection1_type='aaa') + wb = WaterBridgeAnalysis( + universe_DA, + "protein and (resid 1)", + "protein and (resid 4)", + order=0, + selection1_type="aaa", + ) except ValueError: pass else: raise pytest.fail("selection_type aaa should rasie error") def test_distance_type_error(self, universe_DA): - '''Test the case when the wrong selection1_type is given''' - with pytest.raises(ValueError, match="Only 'hydrogen' and 'heavy' are allowed for option `distance_type'"): - WaterBridgeAnalysis(universe_DA, 'protein and (resid 1)', - 'protein and (resid 4)', order=0, - selection1_type='aaa', distance_type='aaa') + """Test the case when the wrong selection1_type is given""" + with pytest.raises( + ValueError, + match="Only 'hydrogen' and 'heavy' are allowed for option `distance_type'", + ): + WaterBridgeAnalysis( + universe_DA, + "protein and (resid 1)", + "protein and (resid 4)", + order=0, + selection1_type="aaa", + distance_type="aaa", + ) def test_selection2_type_error(self, universe_DA): - '''Test the case when the wrong selection1_type is given''' - with pytest.raises(ValueError, match="`selection2_type` is not a keyword argument."): - WaterBridgeAnalysis(universe_DA, 'protein and (resid 1)', - 'protein and (resid 4)', order=0, - selection1_type='aaa', selection2_type='aaa') - + """Test the case when the wrong selection1_type is given""" + with pytest.raises( + ValueError, match="`selection2_type` is not a keyword argument." + ): + WaterBridgeAnalysis( + universe_DA, + "protein and (resid 1)", + "protein and (resid 4)", + order=0, + selection1_type="aaa", + selection2_type="aaa", + ) def test_empty_selection(self, universe_DA): - '''Test the case when selection yields empty result''' - wb = WaterBridgeAnalysis(universe_DA, 'protein and (resid 9)', - 'protein and (resid 10)', order=0) + """Test the case when selection yields empty result""" + wb = WaterBridgeAnalysis( + universe_DA, + "protein and (resid 9)", + "protein and (resid 10)", + order=0, + ) wb.run() assert wb.results.network == [{}] def test_loop(self, universe_loop): - '''Test if loop can be handled correctly''' - wb = WaterBridgeAnalysis(universe_loop, 'protein and (resid 1)', - 'protein and (resid 1 or resid 4)') + """Test if loop can be handled correctly""" + wb = WaterBridgeAnalysis( + universe_loop, + "protein and (resid 1)", + "protein and (resid 1 or resid 4)", + ) wb.run() assert_equal(len(wb.results.network[0].keys()), 2) - @pytest.mark.parametrize('distance_type', ["hydrogen", "heavy"]) + @pytest.mark.parametrize("distance_type", ["hydrogen", "heavy"]) def test_donor_accepter(self, universe_DA, distance_type): - '''Test zeroth order donor to acceptor hydrogen bonding''' - wb = WaterBridgeAnalysis(universe_DA, 'protein and (resid 1)', - 'protein and (resid 4)', - order=0, - update_selection=True, - debug=True, - distance_type=distance_type) + """Test zeroth order donor to acceptor hydrogen bonding""" + wb = WaterBridgeAnalysis( + universe_DA, + "protein and (resid 1)", + "protein and (resid 4)", + order=0, + update_selection=True, + debug=True, + distance_type=distance_type, + ) wb.run(verbose=False) network = wb.results.network[0] assert_equal(list(network.keys())[0][:4], (1, 0, 2, None)) - @pytest.mark.parametrize('distance_type', ["hydrogen", "heavy"]) + @pytest.mark.parametrize("distance_type", ["hydrogen", "heavy"]) def test_donor_accepter_pbc(self, universe_DA_PBC, distance_type): - '''Test zeroth order donor to acceptor hydrogen bonding in PBC conditions''' - wb = WaterBridgeAnalysis(universe_DA_PBC, - 'protein and (resid 1)', - 'protein and (resid 4)', - order=0, - pbc=True, - distance_type=distance_type) + """Test zeroth order donor to acceptor hydrogen bonding in PBC conditions""" + wb = WaterBridgeAnalysis( + universe_DA_PBC, + "protein and (resid 1)", + "protein and (resid 4)", + order=0, + pbc=True, + distance_type=distance_type, + ) wb.run(verbose=False) network = wb.results.network[0] assert_equal(list(network.keys())[0][:4], (1, 0, 2, None)) - @pytest.mark.parametrize('distance_type', ["hydrogen", "heavy"]) + @pytest.mark.parametrize("distance_type", ["hydrogen", "heavy"]) def test_accepter_donor(self, universe_AD, distance_type): - '''Test zeroth order acceptor to donor hydrogen bonding''' - wb = WaterBridgeAnalysis(universe_AD, 'protein and (resid 1)', - 'protein and (resid 4)', order=0, - distance_type=distance_type) + """Test zeroth order acceptor to donor hydrogen bonding""" + wb = WaterBridgeAnalysis( + universe_AD, + "protein and (resid 1)", + "protein and (resid 4)", + order=0, + distance_type=distance_type, + ) wb.run(verbose=False) network = wb.results.network[0] assert_equal(list(network.keys())[0][:4], (0, None, 1, 2)) - @pytest.mark.parametrize('distance_type', ["hydrogen", "heavy"]) + @pytest.mark.parametrize("distance_type", ["hydrogen", "heavy"]) def test_acceptor_water_accepter(self, universe_AWA, distance_type): - '''Test case where the hydrogen bond acceptor from selection 1 form - water bridge with hydrogen bond acceptor from selection 2''' - wb = WaterBridgeAnalysis(universe_AWA, 'protein and (resid 1)', - 'protein and (resid 4)', distance_type=distance_type) + """Test case where the hydrogen bond acceptor from selection 1 form + water bridge with hydrogen bond acceptor from selection 2""" + wb = WaterBridgeAnalysis( + universe_AWA, + "protein and (resid 1)", + "protein and (resid 4)", + distance_type=distance_type, + ) wb.run(verbose=False) network = wb.results.network[0] assert_equal(list(network.keys())[0][:4], (0, None, 2, 1)) @@ -383,12 +437,16 @@ def test_acceptor_water_accepter(self, universe_AWA, distance_type): assert_equal(list(second.keys())[0][:4], (3, 1, 4, None)) assert_equal(second[list(second.keys())[0]], None) - @pytest.mark.parametrize('distance_type', ["hydrogen", "heavy"]) + @pytest.mark.parametrize("distance_type", ["hydrogen", "heavy"]) def test_donor_water_accepter(self, universe_DWA, distance_type): - '''Test case where the hydrogen bond donor from selection 1 form - water bridge with hydrogen bond acceptor from selection 2''' - wb = WaterBridgeAnalysis(universe_DWA, 'protein and (resid 1)', - 'protein and (resid 4)', distance_type=distance_type) + """Test case where the hydrogen bond donor from selection 1 form + water bridge with hydrogen bond acceptor from selection 2""" + wb = WaterBridgeAnalysis( + universe_DWA, + "protein and (resid 1)", + "protein and (resid 4)", + distance_type=distance_type, + ) wb.run(verbose=False) network = wb.results.network[0] assert_equal(list(network.keys())[0][:4], (1, 0, 2, None)) @@ -396,12 +454,16 @@ def test_donor_water_accepter(self, universe_DWA, distance_type): assert_equal(list(second.keys())[0][:4], (3, 2, 4, None)) assert_equal(second[list(second.keys())[0]], None) - @pytest.mark.parametrize('distance_type', ["hydrogen", "heavy"]) + @pytest.mark.parametrize("distance_type", ["hydrogen", "heavy"]) def test_acceptor_water_donor(self, universe_AWD, distance_type): - '''Test case where the hydrogen bond acceptor from selection 1 form - water bridge with hydrogen bond donor from selection 2''' - wb = WaterBridgeAnalysis(universe_AWD, 'protein and (resid 1)', - 'protein and (resid 4)', distance_type=distance_type) + """Test case where the hydrogen bond acceptor from selection 1 form + water bridge with hydrogen bond donor from selection 2""" + wb = WaterBridgeAnalysis( + universe_AWD, + "protein and (resid 1)", + "protein and (resid 4)", + distance_type=distance_type, + ) wb.run(verbose=False) network = wb.results.network[0] assert_equal(list(network.keys())[0][:4], (0, None, 2, 1)) @@ -409,12 +471,16 @@ def test_acceptor_water_donor(self, universe_AWD, distance_type): assert_equal(list(second.keys())[0][:4], (1, None, 3, 4)) assert_equal(second[list(second.keys())[0]], None) - @pytest.mark.parametrize('distance_type', ["hydrogen", "heavy"]) + @pytest.mark.parametrize("distance_type", ["hydrogen", "heavy"]) def test_donor_water_donor(self, universe_DWD, distance_type): - '''Test case where the hydrogen bond donor from selection 1 form - water bridge with hydrogen bond donor from selection 2''' - wb = WaterBridgeAnalysis(universe_DWD, 'protein and (resid 1)', - 'protein and (resid 4)', distance_type=distance_type) + """Test case where the hydrogen bond donor from selection 1 form + water bridge with hydrogen bond donor from selection 2""" + wb = WaterBridgeAnalysis( + universe_DWD, + "protein and (resid 1)", + "protein and (resid 4)", + distance_type=distance_type, + ) wb.run(verbose=False) network = wb.results.network[0] assert_equal(list(network.keys())[0][:4], (1, 0, 2, None)) @@ -423,38 +489,44 @@ def test_donor_water_donor(self, universe_DWD, distance_type): assert_equal(second[list(second.keys())[0]], None) def test_empty(self, universe_empty): - '''Test case where no water bridge exists''' - wb = WaterBridgeAnalysis(universe_empty, 'protein', 'protein') + """Test case where no water bridge exists""" + wb = WaterBridgeAnalysis(universe_empty, "protein", "protein") wb.run(verbose=False) assert_equal(wb.results.network[0], defaultdict(dict)) def test_same_selection(self, universe_DWA): - ''' + """ This test tests that if the selection 1 and selection 2 are both protein. However, the protein only forms one hydrogen bond with the water. This entry won't be included. - ''' - wb = WaterBridgeAnalysis(universe_DWA, 'protein and resid 1', - 'protein and resid 1') + """ + wb = WaterBridgeAnalysis( + universe_DWA, "protein and resid 1", "protein and resid 1" + ) wb.run(verbose=False) assert_equal(wb.results.network[0], defaultdict(dict)) - @pytest.mark.parametrize('distance_type', ["hydrogen", "heavy"]) + @pytest.mark.parametrize("distance_type", ["hydrogen", "heavy"]) def test_acceptor_2water_accepter(self, universe_AWWA, distance_type): - '''Test case where the hydrogen bond acceptor from selection 1 form second order - water bridge with hydrogen bond acceptor from selection 2''' + """Test case where the hydrogen bond acceptor from selection 1 form second order + water bridge with hydrogen bond acceptor from selection 2""" # test first order - wb = WaterBridgeAnalysis(universe_AWWA, 'protein and (resid 1)', - 'protein and (resid 4)', - distance_type=distance_type) + wb = WaterBridgeAnalysis( + universe_AWWA, + "protein and (resid 1)", + "protein and (resid 4)", + distance_type=distance_type, + ) wb.run(verbose=False) assert_equal(wb.results.network[0], defaultdict(dict)) # test second order - wb = WaterBridgeAnalysis(universe_AWWA, - 'protein and (resid 1)', - 'protein and (resid 4)', - order=2, - distance_type=distance_type) + wb = WaterBridgeAnalysis( + universe_AWWA, + "protein and (resid 1)", + "protein and (resid 4)", + order=2, + distance_type=distance_type, + ) wb.run(verbose=False) network = wb.results.network[0] assert_equal(list(network.keys())[0][:4], (0, None, 2, 1)) @@ -464,9 +536,13 @@ def test_acceptor_2water_accepter(self, universe_AWWA, distance_type): assert_equal(list(third.keys())[0][:4], (5, 4, 6, None)) assert_equal(third[list(third.keys())[0]], None) # test third order - wb = WaterBridgeAnalysis(universe_AWWA, 'protein and (resid 1)', - 'protein and (resid 4)', order=3, - distance_type=distance_type) + wb = WaterBridgeAnalysis( + universe_AWWA, + "protein and (resid 1)", + "protein and (resid 4)", + order=3, + distance_type=distance_type, + ) wb.run(verbose=False) network = wb.results.network[0] assert_equal(list(network.keys())[0][:4], (0, None, 2, 1)) @@ -476,19 +552,27 @@ def test_acceptor_2water_accepter(self, universe_AWWA, distance_type): assert_equal(list(third.keys())[0][:4], (5, 4, 6, None)) assert_equal(third[list(third.keys())[0]], None) - @pytest.mark.parametrize('distance_type', ["hydrogen", "heavy"]) + @pytest.mark.parametrize("distance_type", ["hydrogen", "heavy"]) def test_acceptor_3water_accepter(self, universe_AWWWA, distance_type): - '''Test case where the hydrogen bond acceptor from selection 1 form third order - water bridge with hydrogen bond acceptor from selection 2''' - wb = WaterBridgeAnalysis(universe_AWWWA, 'protein and (resid 1)', - 'protein and (resid 5)', order=2, - distance_type=distance_type) + """Test case where the hydrogen bond acceptor from selection 1 form third order + water bridge with hydrogen bond acceptor from selection 2""" + wb = WaterBridgeAnalysis( + universe_AWWWA, + "protein and (resid 1)", + "protein and (resid 5)", + order=2, + distance_type=distance_type, + ) wb.run(verbose=False) assert_equal(wb.results.network[0], defaultdict(dict)) - wb = WaterBridgeAnalysis(universe_AWWWA, 'protein and (resid 1)', - 'protein and (resid 5)', order=3, - distance_type=distance_type) + wb = WaterBridgeAnalysis( + universe_AWWWA, + "protein and (resid 1)", + "protein and (resid 5)", + order=3, + distance_type=distance_type, + ) wb.run(verbose=False) network = wb.results.network[0] assert_equal(list(network.keys())[0][:4], (0, None, 2, 1)) @@ -500,9 +584,13 @@ def test_acceptor_3water_accepter(self, universe_AWWWA, distance_type): assert_equal(list(fourth.keys())[0][:4], (7, 6, 8, None)) assert_equal(fourth[list(fourth.keys())[0]], None) - wb = WaterBridgeAnalysis(universe_AWWWA, 'protein and (resid 1)', - 'protein and (resid 5)', order=4, - distance_type=distance_type) + wb = WaterBridgeAnalysis( + universe_AWWWA, + "protein and (resid 1)", + "protein and (resid 5)", + order=4, + distance_type=distance_type, + ) wb.run(verbose=False) network = wb.results.network[0] assert_equal(list(network.keys())[0][:4], (0, None, 2, 1)) @@ -514,19 +602,27 @@ def test_acceptor_3water_accepter(self, universe_AWWWA, distance_type): assert_equal(list(fourth.keys())[0][:4], (7, 6, 8, None)) assert_equal(fourth[list(fourth.keys())[0]], None) - @pytest.mark.parametrize('distance_type', ["hydrogen", "heavy"]) + @pytest.mark.parametrize("distance_type", ["hydrogen", "heavy"]) def test_acceptor_4water_accepter(self, universe_AWWWWA, distance_type): - '''Test case where the hydrogen bond acceptor from selection 1 form fourth order - water bridge with hydrogen bond acceptor from selection 2''' - wb = WaterBridgeAnalysis(universe_AWWWWA, 'protein and (resid 1)', - 'protein and (resid 6)', order=3, - distance_type=distance_type) + """Test case where the hydrogen bond acceptor from selection 1 form fourth order + water bridge with hydrogen bond acceptor from selection 2""" + wb = WaterBridgeAnalysis( + universe_AWWWWA, + "protein and (resid 1)", + "protein and (resid 6)", + order=3, + distance_type=distance_type, + ) wb.run(verbose=False) assert_equal(wb.results.network[0], defaultdict(dict)) - wb = WaterBridgeAnalysis(universe_AWWWWA, 'protein and (resid 1)', - 'protein and (resid 6)', order=4, - distance_type=distance_type) + wb = WaterBridgeAnalysis( + universe_AWWWWA, + "protein and (resid 1)", + "protein and (resid 6)", + order=4, + distance_type=distance_type, + ) wb.run(verbose=False) network = wb.results.network[0] assert_equal(list(network.keys())[0][:4], (0, None, 2, 1)) @@ -540,9 +636,13 @@ def test_acceptor_4water_accepter(self, universe_AWWWWA, distance_type): assert_equal(list(fifth.keys())[0][:4], (9, 8, 10, None)) assert_equal(fifth[list(fifth.keys())[0]], None) - wb = WaterBridgeAnalysis(universe_AWWWWA, 'protein and (resid 1)', - 'protein and (resid 6)', order=5, - distance_type=distance_type) + wb = WaterBridgeAnalysis( + universe_AWWWWA, + "protein and (resid 1)", + "protein and (resid 6)", + order=5, + distance_type=distance_type, + ) wb.run(verbose=False) network = wb.results.network[0] assert_equal(list(network.keys())[0][:4], (0, None, 2, 1)) @@ -556,55 +656,89 @@ def test_acceptor_4water_accepter(self, universe_AWWWWA, distance_type): assert_equal(list(fifth.keys())[0][:4], (9, 8, 10, None)) assert_equal(fifth[list(fifth.keys())[0]], None) - @pytest.mark.parametrize('distance_type', ["hydrogen", "heavy"]) + @pytest.mark.parametrize("distance_type", ["hydrogen", "heavy"]) def test_acceptor_22water_accepter(self, universe_branch, distance_type): - '''Test case where the hydrogen bond acceptor from selection 1 form a second order + """Test case where the hydrogen bond acceptor from selection 1 form a second order water bridge with hydrogen bond acceptor from selection 2 - and the last water is linked to two residues in selection 2''' - wb = WaterBridgeAnalysis(universe_branch, 'protein and (resid 1)', - 'protein and (resid 4 or resid 5)', order=2, - distance_type=distance_type) + and the last water is linked to two residues in selection 2""" + wb = WaterBridgeAnalysis( + universe_branch, + "protein and (resid 1)", + "protein and (resid 4 or resid 5)", + order=2, + distance_type=distance_type, + ) wb.run(verbose=False) network = wb.results.network[0] assert_equal(list(network.keys())[0][:4], (0, None, 2, 1)) second = network[list(network.keys())[0]] assert_equal(list(second.keys())[0][:4], (3, 1, 4, None)) third = second[list(second.keys())[0]] - assert_equal([(5, 4, 7, None), (6, 4, 8, None)], - sorted([key[:4] for key in list(third.keys())])) + assert_equal( + [(5, 4, 7, None), (6, 4, 8, None)], + sorted([key[:4] for key in list(third.keys())]), + ) def test_timeseries_wba(self, universe_branch): - '''Test if the time series data is correctly generated in water bridge analysis format''' - wb = WaterBridgeAnalysis(universe_branch, 'protein and (resid 1)', - 'protein and (resid 4 or resid 5)', order=2) - wb.output_format = 'sele1_sele2' + """Test if the time series data is correctly generated in water bridge analysis format""" + wb = WaterBridgeAnalysis( + universe_branch, + "protein and (resid 1)", + "protein and (resid 4 or resid 5)", + order=2, + ) + wb.output_format = "sele1_sele2" wb.run(verbose=False) timeseries = sorted(wb.results.timeseries[0]) - assert_equal(timeseries[0][:4], (0, 2, ('ALA', 1, 'O'), ('SOL', 2, 'HW1'))) - assert_equal(timeseries[1][:4], (3, 4, ('SOL', 2, 'HW2'), ('SOL', 3, 'OW'))) - assert_equal(timeseries[2][:4], (5, 7, ('SOL', 3, 'HW1'), ('ALA', 4, 'O'))) - assert_equal(timeseries[3][:4], (6, 8, ('SOL', 3, 'HW2'), ('ALA', 5, 'O'))) + assert_equal( + timeseries[0][:4], (0, 2, ("ALA", 1, "O"), ("SOL", 2, "HW1")) + ) + assert_equal( + timeseries[1][:4], (3, 4, ("SOL", 2, "HW2"), ("SOL", 3, "OW")) + ) + assert_equal( + timeseries[2][:4], (5, 7, ("SOL", 3, "HW1"), ("ALA", 4, "O")) + ) + assert_equal( + timeseries[3][:4], (6, 8, ("SOL", 3, "HW2"), ("ALA", 5, "O")) + ) def test_timeseries_hba(self, universe_branch): - '''Test if the time series data is correctly generated in hydrogen bond analysis format''' - wb = WaterBridgeAnalysis(universe_branch, 'protein and (resid 1)', - 'protein and (resid 4 or resid 5)', order=2) - wb.output_format = 'donor_acceptor' + """Test if the time series data is correctly generated in hydrogen bond analysis format""" + wb = WaterBridgeAnalysis( + universe_branch, + "protein and (resid 1)", + "protein and (resid 4 or resid 5)", + order=2, + ) + wb.output_format = "donor_acceptor" wb.run(verbose=False) timeseries = sorted(wb.results.timeseries[0]) - assert_equal(timeseries[0][:4], (2, 0, ('SOL', 2, 'HW1'), ('ALA', 1, 'O'))) - assert_equal(timeseries[1][:4], (3, 4, ('SOL', 2, 'HW2'), ('SOL', 3, 'OW'))) - assert_equal(timeseries[2][:4], (5, 7, ('SOL', 3, 'HW1'), ('ALA', 4, 'O'))) - assert_equal(timeseries[3][:4], (6, 8, ('SOL', 3, 'HW2'), ('ALA', 5, 'O'))) + assert_equal( + timeseries[0][:4], (2, 0, ("SOL", 2, "HW1"), ("ALA", 1, "O")) + ) + assert_equal( + timeseries[1][:4], (3, 4, ("SOL", 2, "HW2"), ("SOL", 3, "OW")) + ) + assert_equal( + timeseries[2][:4], (5, 7, ("SOL", 3, "HW1"), ("ALA", 4, "O")) + ) + assert_equal( + timeseries[3][:4], (6, 8, ("SOL", 3, "HW2"), ("ALA", 5, "O")) + ) - @pytest.mark.parametrize('distance_type', ["hydrogen", "heavy"]) + @pytest.mark.parametrize("distance_type", ["hydrogen", "heavy"]) def test_acceptor_12water_accepter(self, universe_AWA_AWWA, distance_type): - '''Test of independent first order and second can be recognised correctely''' - wb = WaterBridgeAnalysis(universe_AWA_AWWA, 'protein and (resid 1 or resid 5)', - 'protein and (resid 4 or resid 8)', order=1, - distance_type=distance_type) + """Test of independent first order and second can be recognised correctely""" + wb = WaterBridgeAnalysis( + universe_AWA_AWWA, + "protein and (resid 1 or resid 5)", + "protein and (resid 4 or resid 8)", + order=1, + distance_type=distance_type, + ) wb.run(verbose=False) network = wb.results.network[0] assert_equal(list(network.keys())[0][:4], (0, None, 2, 1)) @@ -612,174 +746,294 @@ def test_acceptor_12water_accepter(self, universe_AWA_AWWA, distance_type): assert_equal(list(second.keys())[0][:4], (3, 1, 4, None)) assert_equal(second[list(second.keys())[0]], None) network = wb.results.network[0] - wb = WaterBridgeAnalysis(universe_AWA_AWWA, 'protein and (resid 1 or resid 5)', - 'protein and (resid 4 or resid 8)', order=2, - distance_type=distance_type) + wb = WaterBridgeAnalysis( + universe_AWA_AWWA, + "protein and (resid 1 or resid 5)", + "protein and (resid 4 or resid 8)", + order=2, + distance_type=distance_type, + ) wb.run(verbose=False) network = wb.results.network[0] - assert_equal([(0, None, 2, 1), (5, None, 7, 6)], - sorted([key[:4] for key in list(network.keys())])) + assert_equal( + [(0, None, 2, 1), (5, None, 7, 6)], + sorted([key[:4] for key in list(network.keys())]), + ) def test_count_by_type_single_link(self, universe_DWA): - ''' + """ This test tests the simplest water bridge to see if count_by_type() works. - ''' - wb = WaterBridgeAnalysis(universe_DWA, 'protein and (resid 1)', - 'protein and (resid 4)') + """ + wb = WaterBridgeAnalysis( + universe_DWA, "protein and (resid 1)", "protein and (resid 4)" + ) wb.run(verbose=False) - assert_equal(wb.count_by_type(), [(1, 4, 'ALA', 1, 'H', 'ALA', 4, 'O', 1.)]) + assert_equal( + wb.count_by_type(), [(1, 4, "ALA", 1, "H", "ALA", 4, "O", 1.0)] + ) def test_count_by_type_multiple_link(self, universe_AWA_AWWA): - ''' + """ This test tests if count_by_type() can give the correct result for more than 1 links. - ''' - wb = WaterBridgeAnalysis(universe_AWA_AWWA, 'protein and (resid 1 or resid 5)', - 'protein and (resid 4 or resid 8)', order=2) + """ + wb = WaterBridgeAnalysis( + universe_AWA_AWWA, + "protein and (resid 1 or resid 5)", + "protein and (resid 4 or resid 8)", + order=2, + ) wb.run(verbose=False) - assert_equal(sorted(wb.count_by_type()), - [[0, 4, 'ALA', 1, 'O', 'ALA', 4, 'O', 1.0], - [5, 11, 'ALA', 5, 'O', 'ALA', 8, 'O', 1.0]]) - + assert_equal( + sorted(wb.count_by_type()), + [ + [0, 4, "ALA", 1, "O", "ALA", 4, "O", 1.0], + [5, 11, "ALA", 5, "O", "ALA", 8, "O", 1.0], + ], + ) def test_count_by_type_multiple_frame(self, wb_multiframe): - ''' + """ This test tests if count_by_type() works in multiply situations. :return: - ''' - result = [[0, 11, 'ALA', 1, 'O', 'ALA', 6, 'H', 0.25], - [0, 12, 'ALA', 1, 'O', 'ALA', 6, 'O', 0.25], - [1, 12, 'ALA', 1, 'H', 'ALA', 6, 'O', 0.5]] + """ + result = [ + [0, 11, "ALA", 1, "O", "ALA", 6, "H", 0.25], + [0, 12, "ALA", 1, "O", "ALA", 6, "O", 0.25], + [1, 12, "ALA", 1, "H", "ALA", 6, "O", 0.5], + ] assert_equal(sorted(wb_multiframe.count_by_type()), result) def test_count_by_type_filter(self, wb_multiframe): - ''' + """ This test tests if modifying analysis_func allows some results to be filtered out in count_by_type(). :return: - ''' + """ + def analysis(current, output, u): - sele1_index, sele1_heavy_index, atom2, heavy_atom2, dist, angle = current[0] - atom1, heavy_atom1, sele2_index, sele2_heavy_index, dist, angle = current[-1] + sele1_index, sele1_heavy_index, atom2, heavy_atom2, dist, angle = ( + current[0] + ) + atom1, heavy_atom1, sele2_index, sele2_heavy_index, dist, angle = ( + current[-1] + ) sele1 = u.atoms[sele1_index] sele2 = u.atoms[sele2_index] - (s1_resname, s1_resid, s1_name) = (sele1.resname, sele1.resid, sele1.name) - (s2_resname, s2_resid, s2_name) = (sele2.resname, sele2.resid, sele2.name) - - key = (sele1_index, sele2_index, s1_resname, s1_resid, s1_name, s2_resname, s2_resid, s2_name) - if s2_name == 'H': + (s1_resname, s1_resid, s1_name) = ( + sele1.resname, + sele1.resid, + sele1.name, + ) + (s2_resname, s2_resid, s2_name) = ( + sele2.resname, + sele2.resid, + sele2.name, + ) + + key = ( + sele1_index, + sele2_index, + s1_resname, + s1_resid, + s1_name, + s2_resname, + s2_resid, + s2_name, + ) + if s2_name == "H": output[key] += 1 - result = [((0, 11, 'ALA', 1, 'O', 'ALA', 6, 'H'), 0.25)] - assert_equal(sorted(wb_multiframe.count_by_type(analysis_func=analysis)), result) + + result = [((0, 11, "ALA", 1, "O", "ALA", 6, "H"), 0.25)] + assert_equal( + sorted(wb_multiframe.count_by_type(analysis_func=analysis)), result + ) def test_count_by_type_merge(self, wb_multiframe): - ''' + """ This test tests if modifying analysis_func allows some same residue to be merged in count_by_type(). - ''' + """ + def analysis(current, output, u): - sele1_index, sele1_heavy_index, atom2, heavy_atom2, dist, angle = current[0] - atom1, heavy_atom1, sele2_index, sele2_heavy_index, dist, angle = current[-1] + sele1_index, sele1_heavy_index, atom2, heavy_atom2, dist, angle = ( + current[0] + ) + atom1, heavy_atom1, sele2_index, sele2_heavy_index, dist, angle = ( + current[-1] + ) sele1 = u.atoms[sele1_index] sele2 = u.atoms[sele2_index] - (s1_resname, s1_resid, s1_name) = (sele1.resname, sele1.resid, sele1.name) - (s2_resname, s2_resid, s2_name) = (sele2.resname, sele2.resid, sele2.name) + (s1_resname, s1_resid, s1_name) = ( + sele1.resname, + sele1.resid, + sele1.name, + ) + (s2_resname, s2_resid, s2_name) = ( + sele2.resname, + sele2.resid, + sele2.name, + ) key = (s1_resname, s1_resid, s2_resname, s2_resid) output[key] = 1 - result = [(('ALA', 1, 'ALA', 6), 1.0)] - assert_equal(sorted(wb_multiframe.count_by_type(analysis_func=analysis)), result) + + result = [(("ALA", 1, "ALA", 6), 1.0)] + assert_equal( + sorted(wb_multiframe.count_by_type(analysis_func=analysis)), result + ) def test_count_by_type_order(self, wb_multiframe): - ''' + """ This test tests if modifying analysis_func allows the order of water bridge to be separated in count_by_type(). :return: - ''' + """ + def analysis(current, output, u): - sele1_index, sele1_heavy_index, atom2, heavy_atom2, dist, angle = current[0] - atom1, heavy_atom1, sele2_index, sele2_heavy_index, dist, angle = current[-1] + sele1_index, sele1_heavy_index, atom2, heavy_atom2, dist, angle = ( + current[0] + ) + atom1, heavy_atom1, sele2_index, sele2_heavy_index, dist, angle = ( + current[-1] + ) sele1 = u.atoms[sele1_index] sele2 = u.atoms[sele2_index] - (s1_resname, s1_resid, s1_name) = (sele1.resname, sele1.resid, sele1.name) - (s2_resname, s2_resid, s2_name) = (sele2.resname, sele2.resid, sele2.name) - key = (s1_resname, s1_resid, s2_resname, s2_resid, len(current)-1) + (s1_resname, s1_resid, s1_name) = ( + sele1.resname, + sele1.resid, + sele1.name, + ) + (s2_resname, s2_resid, s2_name) = ( + sele2.resname, + sele2.resid, + sele2.name, + ) + key = ( + s1_resname, + s1_resid, + s2_resname, + s2_resid, + len(current) - 1, + ) output[key] = 1 - result = [(('ALA', 1, 'ALA', 6, 0), 0.5), - (('ALA', 1, 'ALA', 6, 1), 0.25), - (('ALA', 1, 'ALA', 6, 2), 0.25)] - assert_equal(sorted(wb_multiframe.count_by_type(analysis_func=analysis)), result) + + result = [ + (("ALA", 1, "ALA", 6, 0), 0.5), + (("ALA", 1, "ALA", 6, 1), 0.25), + (("ALA", 1, "ALA", 6, 2), 0.25), + ] + assert_equal( + sorted(wb_multiframe.count_by_type(analysis_func=analysis)), result + ) def test_count_by_time(self, wb_multiframe): - ''' + """ This test tests if count_by_times() works. :return: - ''' - assert_equal(wb_multiframe.count_by_time(), [(0, 1), (1, 1), (2, 1), (3, 1)]) - + """ + assert_equal( + wb_multiframe.count_by_time(), [(0, 1), (1, 1), (2, 1), (3, 1)] + ) def test_count_by_time_weight(self, universe_AWA_AWWA): - ''' + """ This test tests if modyfing the analysis_func allows the weight to be changed in count_by_type(). :return: - ''' - wb = WaterBridgeAnalysis(universe_AWA_AWWA, 'protein and (resid 1 or resid 5)', - 'protein and (resid 4 or resid 8)', order=2) + """ + wb = WaterBridgeAnalysis( + universe_AWA_AWWA, + "protein and (resid 1 or resid 5)", + "protein and (resid 4 or resid 8)", + order=2, + ) wb.run(verbose=False) + def analysis(current, output, u): - sele1_index, sele1_heavy_index, atom2, heavy_atom2, dist, angle = current[0] - atom1, heavy_atom1, sele2_index, sele2_heavy_index, dist, angle = current[-1] + sele1_index, sele1_heavy_index, atom2, heavy_atom2, dist, angle = ( + current[0] + ) + atom1, heavy_atom1, sele2_index, sele2_heavy_index, dist, angle = ( + current[-1] + ) sele1 = u.atoms[sele1_index] sele2 = u.atoms[sele2_index] - (s1_resname, s1_resid, s1_name) = (sele1.resname, sele1.resid, sele1.name) - (s2_resname, s2_resid, s2_name) = (sele2.resname, sele2.resid, sele2.name) + (s1_resname, s1_resid, s1_name) = ( + sele1.resname, + sele1.resid, + sele1.name, + ) + (s2_resname, s2_resid, s2_name) = ( + sele2.resname, + sele2.resid, + sele2.name, + ) key = (s1_resname, s1_resid, s2_resname, s2_resid) - output[key] += len(current)-1 - assert_equal(wb.count_by_time(analysis_func=analysis), [(0,3), ]) + output[key] += len(current) - 1 + + assert_equal( + wb.count_by_time(analysis_func=analysis), + [ + (0, 3), + ], + ) def test_count_by_time_empty(self, universe_AWA_AWWA): - ''' + """ See if count_by_time() can handle zero well. :return: - ''' - wb = WaterBridgeAnalysis(universe_AWA_AWWA, 'protein and (resid 1 or resid 5)', - 'protein and (resid 4 or resid 8)', order=2) + """ + wb = WaterBridgeAnalysis( + universe_AWA_AWWA, + "protein and (resid 1 or resid 5)", + "protein and (resid 4 or resid 8)", + order=2, + ) wb.run(verbose=False) + def analysis(current, output, u): pass - assert_equal(wb.count_by_time(analysis_func=analysis), [(0,0), ]) + + assert_equal( + wb.count_by_time(analysis_func=analysis), + [ + (0, 0), + ], + ) def test_generate_table_hba(self, wb_multiframe): - '''Test generate table using hydrogen bond analysis format''' - table = wb_multiframe.generate_table(output_format='donor_acceptor') + """Test generate table using hydrogen bond analysis format""" + table = wb_multiframe.generate_table(output_format="donor_acceptor") assert_array_equal( sorted(table.donor_resid), [1, 1, 2, 2, 2, 6, 6], ) def test_generate_table_s1s2(self, wb_multiframe): - '''Test generate table using hydrogen bond analysis format''' - table = wb_multiframe.generate_table(output_format='sele1_sele2') + """Test generate table using hydrogen bond analysis format""" + table = wb_multiframe.generate_table(output_format="sele1_sele2") assert_array_equal( sorted(table.sele1_resid), [1, 1, 1, 1, 2, 2, 3], ) def test_timesteps_by_type(self, wb_multiframe): - '''Test the timesteps_by_type function''' + """Test the timesteps_by_type function""" timesteps = sorted(wb_multiframe.timesteps_by_type()) - assert_array_equal(timesteps[3], [1, 12, 'ALA', 1, 'H', 'ALA', 6, 'O', 0, 2]) + assert_array_equal( + timesteps[3], [1, 12, "ALA", 1, "H", "ALA", 6, "O", 0, 2] + ) def test_duplicate_water(self): - '''A case #3119 where + """A case #3119 where Acceptor···H−O···H-Donor | H···O-H will be recognised as 3rd order water bridge. - ''' - grofile = '''Test gro file + """ + grofile = """Test gro file 7 1LEU O 1 1.876 0.810 1.354 117SOL HW1 2 1.853 0.831 1.162 @@ -788,16 +1042,21 @@ def test_duplicate_water(self): 135SOL OW 5 1.924 0.713 0.845 1LEU H 6 1.997 0.991 1.194 1LEU N 7 2.041 1.030 1.274 - 2.22092 2.22092 2.22092''' - u = MDAnalysis.Universe(StringIO(grofile), format='gro') - wb = WaterBridgeAnalysis(u, 'resname LEU and name O', - 'resname LEU and name N H', order=4) + 2.22092 2.22092 2.22092""" + u = MDAnalysis.Universe(StringIO(grofile), format="gro") + wb = WaterBridgeAnalysis( + u, "resname LEU and name O", "resname LEU and name N H", order=4 + ) wb.run() assert len(wb.results.timeseries[0]) == 2 def test_warn_results_deprecated(self, universe_DA): - wb = WaterBridgeAnalysis(universe_DA, 'protein and (resid 9)', - 'protein and (resid 10)', order=0) + wb = WaterBridgeAnalysis( + universe_DA, + "protein and (resid 9)", + "protein and (resid 10)", + order=0, + ) wb.run() wmsg = "The `network` attribute was deprecated in MDAnalysis 2.0.0" diff --git a/testsuite/pyproject.toml b/testsuite/pyproject.toml index 9c660c95482..e0cf7e900d0 100644 --- a/testsuite/pyproject.toml +++ b/testsuite/pyproject.toml @@ -162,6 +162,7 @@ setup\.py | MDAnalysisTests/auxiliary/.*\.py | MDAnalysisTests/lib/.*\.py | MDAnalysisTests/transformations/.*\.py +| MDAnalysisTests/analysis/.*\.py ) ''' extend-exclude = ''' From 2419fbe882e0f60fd5ab9236fabecb8cdbb52f0c Mon Sep 17 00:00:00 2001 From: Rocco Meli Date: Sun, 22 Dec 2024 22:57:05 +0100 Subject: [PATCH 2/5] start fixes --- .../hydrogenbonds/wbridge_analysis.py | 10 +- .../MDAnalysisTests/analysis/test_base.py | 13 +- .../MDAnalysisTests/analysis/test_contacts.py | 489 +++--------------- 3 files changed, 91 insertions(+), 421 deletions(-) diff --git a/package/MDAnalysis/analysis/hydrogenbonds/wbridge_analysis.py b/package/MDAnalysis/analysis/hydrogenbonds/wbridge_analysis.py index b8aef4f4185..027c0b71255 100644 --- a/package/MDAnalysis/analysis/hydrogenbonds/wbridge_analysis.py +++ b/package/MDAnalysis/analysis/hydrogenbonds/wbridge_analysis.py @@ -706,15 +706,17 @@ def analysis(current, output, u, **kwargs): Will be removed in MDAnalysis 3.0.0. Please use :attr:`results.timeseries` instead. """ -from collections import defaultdict import logging import warnings +from collections import defaultdict + import numpy as np -from ..base import AnalysisBase +from MDAnalysis import MissingDataWarning, NoDataError, SelectionError +from MDAnalysis.lib.distances import calc_angles, capped_distance from MDAnalysis.lib.NeighborSearch import AtomNeighborSearch -from MDAnalysis.lib.distances import capped_distance, calc_angles -from MDAnalysis import NoDataError, MissingDataWarning, SelectionError + +from ..base import AnalysisBase logger = logging.getLogger("MDAnalysis.analysis.WaterBridgeAnalysis") diff --git a/testsuite/MDAnalysisTests/analysis/test_base.py b/testsuite/MDAnalysisTests/analysis/test_base.py index 4208b604de1..e369c4c6021 100644 --- a/testsuite/MDAnalysisTests/analysis/test_base.py +++ b/testsuite/MDAnalysisTests/analysis/test_base.py @@ -20,22 +20,17 @@ # MDAnalysis: A Toolkit for the Analysis of Molecular Dynamics Simulations. # J. Comput. Chem. 32 (2011), 2319--2327, doi:10.1002/jcc.21787 # -from collections import UserDict import pickle - -import pytest - -import numpy as np - -from numpy.testing import assert_equal, assert_allclose +from collections import UserDict import MDAnalysis as mda import numpy as np import pytest -from MDAnalysis.analysis import base, backends +from MDAnalysis.analysis import backends, base +from numpy.testing import assert_allclose, assert_almost_equal, assert_equal + from MDAnalysisTests.datafiles import DCD, PSF, TPR, XTC from MDAnalysisTests.util import no_deprecated_call -from numpy.testing import assert_almost_equal, assert_equal class FrameAnalysis(base.AnalysisBase): diff --git a/testsuite/MDAnalysisTests/analysis/test_contacts.py b/testsuite/MDAnalysisTests/analysis/test_contacts.py index dd0c4ab00c8..4bfe2699d23 100644 --- a/testsuite/MDAnalysisTests/analysis/test_contacts.py +++ b/testsuite/MDAnalysisTests/analysis/test_contacts.py @@ -21,27 +21,17 @@ # J. Comput. Chem. 32 (2011), 2319--2327, doi:10.1002/jcc.21787 # import warnings + import MDAnalysis as mda +import numpy as np import pytest from MDAnalysis.analysis import contacts from MDAnalysis.analysis.distances import distance_array +from numpy.testing import assert_allclose, assert_array_equal, assert_equal -from numpy.testing import ( - assert_equal, - assert_array_equal, - assert_allclose, -) -import numpy as np - -from MDAnalysisTests.datafiles import ( - PSF, - DCD, - TPR, - XTC, - contacts_villin_folded, - contacts_villin_unfolded, - contacts_file, -) +from MDAnalysisTests.datafiles import (DCD, PSF, TPR, XTC, contacts_file, + contacts_villin_folded, + contacts_villin_unfolded) def test_soft_cut_q(): @@ -303,106 +293,30 @@ def test_villin_unfolded(self, client_Contacts): def test_hard_cut_method(self, universe, client_Contacts): ca = self._run_Contacts(universe, client_Contacts=client_Contacts) + # fmt: off expected = [ - 1.0, - 0.58252427, - 0.52427184, - 0.55339806, - 0.54368932, - 0.54368932, - 0.51456311, - 0.46601942, - 0.48543689, - 0.52427184, - 0.46601942, - 0.58252427, - 0.51456311, - 0.48543689, - 0.48543689, - 0.48543689, - 0.46601942, - 0.51456311, - 0.49514563, - 0.49514563, - 0.45631068, - 0.47572816, - 0.49514563, - 0.50485437, - 0.53398058, - 0.50485437, - 0.51456311, - 0.51456311, - 0.49514563, - 0.49514563, - 0.54368932, - 0.50485437, - 0.48543689, - 0.55339806, - 0.45631068, - 0.46601942, - 0.53398058, - 0.53398058, - 0.46601942, - 0.52427184, - 0.45631068, - 0.46601942, - 0.47572816, - 0.46601942, - 0.45631068, - 0.47572816, - 0.45631068, - 0.48543689, - 0.4368932, - 0.4368932, - 0.45631068, - 0.50485437, - 0.41747573, - 0.4368932, - 0.51456311, - 0.47572816, - 0.46601942, - 0.46601942, - 0.47572816, - 0.47572816, - 0.46601942, - 0.45631068, - 0.44660194, - 0.47572816, - 0.48543689, - 0.47572816, - 0.42718447, - 0.40776699, - 0.37864078, - 0.42718447, - 0.45631068, - 0.4368932, - 0.4368932, - 0.45631068, - 0.4368932, - 0.46601942, - 0.45631068, - 0.48543689, - 0.44660194, - 0.44660194, - 0.44660194, - 0.42718447, - 0.45631068, - 0.44660194, - 0.48543689, - 0.48543689, - 0.44660194, - 0.4368932, - 0.40776699, - 0.41747573, - 0.48543689, - 0.45631068, - 0.46601942, - 0.47572816, - 0.51456311, - 0.45631068, - 0.37864078, - 0.42718447, + 1., 0.58252427, 0.52427184, 0.55339806, 0.54368932, + 0.54368932, 0.51456311, 0.46601942, 0.48543689, 0.52427184, + 0.46601942, 0.58252427, 0.51456311, 0.48543689, 0.48543689, + 0.48543689, 0.46601942, 0.51456311, 0.49514563, 0.49514563, + 0.45631068, 0.47572816, 0.49514563, 0.50485437, 0.53398058, + 0.50485437, 0.51456311, 0.51456311, 0.49514563, 0.49514563, + 0.54368932, 0.50485437, 0.48543689, 0.55339806, 0.45631068, + 0.46601942, 0.53398058, 0.53398058, 0.46601942, 0.52427184, + 0.45631068, 0.46601942, 0.47572816, 0.46601942, 0.45631068, + 0.47572816, 0.45631068, 0.48543689, 0.4368932, 0.4368932, + 0.45631068, 0.50485437, 0.41747573, 0.4368932, 0.51456311, + 0.47572816, 0.46601942, 0.46601942, 0.47572816, 0.47572816, + 0.46601942, 0.45631068, 0.44660194, 0.47572816, 0.48543689, + 0.47572816, 0.42718447, 0.40776699, 0.37864078, 0.42718447, + 0.45631068, 0.4368932, 0.4368932, 0.45631068, 0.4368932, + 0.46601942, 0.45631068, 0.48543689, 0.44660194, 0.44660194, + 0.44660194, 0.42718447, 0.45631068, 0.44660194, 0.48543689, + 0.48543689, 0.44660194, 0.4368932, 0.40776699, 0.41747573, + 0.48543689, 0.45631068, 0.46601942, 0.47572816, 0.51456311, + 0.45631068, 0.37864078, 0.42718447, ] + # fmt: on assert len(ca.results.timeseries) == len(expected) assert_allclose( ca.results.timeseries[:, 1], expected, rtol=0, atol=1.5e-7 @@ -436,106 +350,17 @@ def test_own_method(self, universe, client_Contacts): method=self._is_any_closer, ) + # fmt: off bound_expected = [ - 1.0, - 1.0, - 0.0, - 1.0, - 1.0, - 0.0, - 0.0, - 1.0, - 0.0, - 1.0, - 1.0, - 0.0, - 0.0, - 1.0, - 0.0, - 0.0, - 0.0, - 0.0, - 1.0, - 1.0, - 0.0, - 0.0, - 0.0, - 1.0, - 0.0, - 1.0, - 0.0, - 1.0, - 1.0, - 0.0, - 1.0, - 1.0, - 1.0, - 0.0, - 0.0, - 0.0, - 0.0, - 1.0, - 0.0, - 0.0, - 1.0, - 0.0, - 1.0, - 1.0, - 1.0, - 0.0, - 1.0, - 0.0, - 0.0, - 1.0, - 1.0, - 1.0, - 0.0, - 1.0, - 0.0, - 1.0, - 1.0, - 0.0, - 0.0, - 0.0, - 1.0, - 1.0, - 1.0, - 0.0, - 0.0, - 1.0, - 0.0, - 1.0, - 1.0, - 1.0, - 1.0, - 1.0, - 1.0, - 0.0, - 1.0, - 1.0, - 0.0, - 1.0, - 0.0, - 0.0, - 1.0, - 1.0, - 0.0, - 0.0, - 1.0, - 1.0, - 1.0, - 0.0, - 1.0, - 0.0, - 0.0, - 1.0, - 0.0, - 1.0, - 1.0, - 1.0, - 1.0, - 1.0, + 1., 1., 0., 1., 1., 0., 0., 1., 0., 1., 1., 0., 0., 1., 0., 0., + 0., 0., 1., 1., 0., 0., 0., 1., 0., 1., 0., 1., 1., 0., 1., 1., + 1., 0., 0., 0., 0., 1., 0., 0., 1., 0., 1., 1., 1., 0., 1., 0., + 0., 1., 1., 1., 0., 1., 0., 1., 1., 0., 0., 0., 1., 1., 1., 0., + 0., 1., 0., 1., 1., 1., 1., 1., 1., 0., 1., 1., 0., 1., 0., 0., + 1., 1., 0., 0., 1., 1., 1., 0., 1., 0., 0., 1., 0., 1., 1., 1., + 1., 1., ] + # fmt: on assert_array_equal(ca.results.timeseries[:, 1], bound_expected) @staticmethod @@ -639,210 +464,58 @@ def test_q1q2(client_Contacts): q1q2 = contacts.q1q2(u, "name CA", radius=8) q1q2.run(**client_Contacts) + # fmt: off q1_expected = [ - 1.0, - 0.98092643, - 0.97366031, - 0.97275204, - 0.97002725, - 0.97275204, - 0.96276113, - 0.96730245, - 0.9582198, - 0.96185286, - 0.95367847, - 0.96276113, - 0.9582198, - 0.95186194, - 0.95367847, - 0.95095368, - 0.94187103, - 0.95186194, - 0.94277929, - 0.94187103, - 0.9373297, - 0.93642144, - 0.93097184, - 0.93914623, - 0.93278837, - 0.93188011, - 0.9373297, - 0.93097184, - 0.93188011, - 0.92643052, - 0.92824705, - 0.92915531, - 0.92643052, - 0.92461399, - 0.92279746, - 0.92643052, - 0.93278837, - 0.93188011, - 0.93369664, - 0.9346049, - 0.9373297, - 0.94096276, - 0.9400545, - 0.93642144, - 0.9373297, - 0.9373297, - 0.9400545, - 0.93006358, - 0.9400545, - 0.93823797, - 0.93914623, - 0.93278837, - 0.93097184, - 0.93097184, - 0.92733878, - 0.92824705, - 0.92279746, - 0.92824705, - 0.91825613, - 0.92733878, - 0.92643052, - 0.92733878, - 0.93278837, - 0.92733878, - 0.92824705, - 0.93097184, - 0.93278837, - 0.93914623, - 0.93097184, - 0.9373297, - 0.92915531, - 0.93188011, - 0.93551317, - 0.94096276, - 0.93642144, - 0.93642144, - 0.9346049, - 0.93369664, - 0.93369664, - 0.93278837, - 0.93006358, - 0.93278837, - 0.93006358, - 0.9346049, - 0.92824705, - 0.93097184, - 0.93006358, - 0.93188011, - 0.93278837, - 0.93006358, - 0.92915531, - 0.92824705, - 0.92733878, - 0.92643052, - 0.93188011, - 0.93006358, - 0.9346049, - 0.93188011, + 1.0, 0.98092643, 0.97366031, 0.97275204, 0.97002725, + 0.97275204, 0.96276113, 0.96730245, 0.9582198, 0.96185286, + 0.95367847, 0.96276113, 0.9582198, 0.95186194, 0.95367847, + 0.95095368, 0.94187103, 0.95186194, 0.94277929, 0.94187103, + 0.9373297, 0.93642144, 0.93097184, 0.93914623, 0.93278837, + 0.93188011, 0.9373297, 0.93097184, 0.93188011, 0.92643052, + 0.92824705, 0.92915531, 0.92643052, 0.92461399, 0.92279746, + 0.92643052, 0.93278837, 0.93188011, 0.93369664, 0.9346049, + 0.9373297, 0.94096276, 0.9400545, 0.93642144, 0.9373297, + 0.9373297, 0.9400545, 0.93006358, 0.9400545, 0.93823797, + 0.93914623, 0.93278837, 0.93097184, 0.93097184, 0.92733878, + 0.92824705, 0.92279746, 0.92824705, 0.91825613, 0.92733878, + 0.92643052, 0.92733878, 0.93278837, 0.92733878, 0.92824705, + 0.93097184, 0.93278837, 0.93914623, 0.93097184, 0.9373297, + 0.92915531, 0.93188011, 0.93551317, 0.94096276, 0.93642144, + 0.93642144, 0.9346049, 0.93369664, 0.93369664, 0.93278837, + 0.93006358, 0.93278837, 0.93006358, 0.9346049, 0.92824705, + 0.93097184, 0.93006358, 0.93188011, 0.93278837, 0.93006358, + 0.92915531, 0.92824705, 0.92733878, 0.92643052, 0.93188011, + 0.93006358, 0.9346049, 0.93188011, ] + # fmt: on assert_allclose( q1q2.results.timeseries[:, 1], q1_expected, rtol=0, atol=1.5e-7 ) + # fmt: off q2_expected = [ - 0.94649446, - 0.94926199, - 0.95295203, - 0.95110701, - 0.94833948, - 0.95479705, - 0.94926199, - 0.9501845, - 0.94926199, - 0.95387454, - 0.95202952, - 0.95110701, - 0.94649446, - 0.94095941, - 0.94649446, - 0.9400369, - 0.94464945, - 0.95202952, - 0.94741697, - 0.94649446, - 0.94188192, - 0.94188192, - 0.93911439, - 0.94464945, - 0.9400369, - 0.94095941, - 0.94372694, - 0.93726937, - 0.93819188, - 0.93357934, - 0.93726937, - 0.93911439, - 0.93911439, - 0.93450185, - 0.93357934, - 0.93265683, - 0.93911439, - 0.94372694, - 0.93911439, - 0.94649446, - 0.94833948, - 0.95110701, - 0.95110701, - 0.95295203, - 0.94926199, - 0.95110701, - 0.94926199, - 0.94741697, - 0.95202952, - 0.95202952, - 0.95202952, - 0.94741697, - 0.94741697, - 0.94926199, - 0.94280443, - 0.94741697, - 0.94833948, - 0.94833948, - 0.9400369, - 0.94649446, - 0.94741697, - 0.94926199, - 0.95295203, - 0.94926199, - 0.9501845, - 0.95664207, - 0.95756458, - 0.96309963, - 0.95756458, - 0.96217712, - 0.95756458, - 0.96217712, - 0.96586716, - 0.96863469, - 0.96494465, - 0.97232472, - 0.97140221, - 0.9695572, - 0.97416974, - 0.9695572, - 0.96217712, - 0.96771218, - 0.9704797, - 0.96771218, - 0.9695572, - 0.97140221, - 0.97601476, - 0.97693727, - 0.98154982, - 0.98431734, - 0.97601476, - 0.9797048, - 0.98154982, - 0.98062731, - 0.98431734, - 0.98616236, - 0.9898524, - 1.0, + 0.94649446, 0.94926199, 0.95295203, 0.95110701, 0.94833948, + 0.95479705, 0.94926199, 0.9501845, 0.94926199, 0.95387454, + 0.95202952, 0.95110701, 0.94649446, 0.94095941, 0.94649446, + 0.9400369, 0.94464945, 0.95202952, 0.94741697, 0.94649446, + 0.94188192, 0.94188192, 0.93911439, 0.94464945, 0.9400369, + 0.94095941, 0.94372694, 0.93726937, 0.93819188, 0.93357934, + 0.93726937, 0.93911439, 0.93911439, 0.93450185, 0.93357934, + 0.93265683, 0.93911439, 0.94372694, 0.93911439, 0.94649446, + 0.94833948, 0.95110701, 0.95110701, 0.95295203, 0.94926199, + 0.95110701, 0.94926199, 0.94741697, 0.95202952, 0.95202952, + 0.95202952, 0.94741697, 0.94741697, 0.94926199, 0.94280443, + 0.94741697, 0.94833948, 0.94833948, 0.9400369, 0.94649446, + 0.94741697, 0.94926199, 0.95295203, 0.94926199, 0.9501845, + 0.95664207, 0.95756458, 0.96309963, 0.95756458, 0.96217712, + 0.95756458, 0.96217712, 0.96586716, 0.96863469, 0.96494465, + 0.97232472, 0.97140221, 0.9695572, 0.97416974, 0.9695572, + 0.96217712, 0.96771218, 0.9704797, 0.96771218, 0.9695572, + 0.97140221, 0.97601476, 0.97693727, 0.98154982, 0.98431734, + 0.97601476, 0.9797048, 0.98154982, 0.98062731, 0.98431734, + 0.98616236, 0.9898524, 1.0, ] + # fmt: on assert_allclose( q1q2.results.timeseries[:, 2], q2_expected, rtol=0, atol=1.5e-7 ) From dd499178a29bb93c8a59ac5f32946433502048f7 Mon Sep 17 00:00:00 2001 From: Rocco Meli Date: Fri, 27 Dec 2024 15:58:13 +0100 Subject: [PATCH 3/5] update --- .../MDAnalysisTests/analysis/test_contacts.py | 12 +- .../MDAnalysisTests/analysis/test_gnm.py | 476 ++---------------- 2 files changed, 53 insertions(+), 435 deletions(-) diff --git a/testsuite/MDAnalysisTests/analysis/test_contacts.py b/testsuite/MDAnalysisTests/analysis/test_contacts.py index 4bfe2699d23..7f4a5b69ecc 100644 --- a/testsuite/MDAnalysisTests/analysis/test_contacts.py +++ b/testsuite/MDAnalysisTests/analysis/test_contacts.py @@ -29,9 +29,15 @@ from MDAnalysis.analysis.distances import distance_array from numpy.testing import assert_allclose, assert_array_equal, assert_equal -from MDAnalysisTests.datafiles import (DCD, PSF, TPR, XTC, contacts_file, - contacts_villin_folded, - contacts_villin_unfolded) +from MDAnalysisTests.datafiles import ( + DCD, + PSF, + TPR, + XTC, + contacts_file, + contacts_villin_folded, + contacts_villin_unfolded, +) def test_soft_cut_q(): diff --git a/testsuite/MDAnalysisTests/analysis/test_gnm.py b/testsuite/MDAnalysisTests/analysis/test_gnm.py index 36241d3e854..6114ad66f1d 100644 --- a/testsuite/MDAnalysisTests/analysis/test_gnm.py +++ b/testsuite/MDAnalysisTests/analysis/test_gnm.py @@ -25,10 +25,9 @@ import MDAnalysis as mda import MDAnalysis.analysis.gnm - -from numpy.testing import assert_almost_equal import numpy as np import pytest +from numpy.testing import assert_almost_equal from MDAnalysisTests.datafiles import GRO, XTC @@ -77,225 +76,33 @@ def test_gnm_run_step(universe, client_GNMAnalysis): def test_generate_kirchoff(universe): gnm = mda.analysis.gnm.GNMAnalysis(universe) gen = gnm.generate_kirchoff() + # fmt: off assert_almost_equal( gen[0], [ - 7, - -1, - -1, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - -1, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - -1, - -1, - -1, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - -1, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - ], + 16.326744128018923, -2.716098853586913, -1.94736842105263, 0.0, + 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, + 0.0, 0.0, -0.05263157894736842, 0.0, 0.0, 0.0, -3.3541953679557905, + 0.0, -1.4210526315789465, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, + 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, + 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, + 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, + 0.0, 0.0, 0.0, 0.0, 0.0, -1.0423368771244421, -1.3006649542861801, + -0.30779350562554625, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, + 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, + -0.927172649945531, -0.7509392614826383, 0.0, 0.0, 0.0, 0.0, 0.0, + 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, + 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, + 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, + 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, + 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, + 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, + 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, + 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, + -2.263157894736841, -0.24333213169614382 + ] ) + # fmt: on def test_gnm_SVD_fail(universe): @@ -315,225 +122,30 @@ def test_closeContactGNMAnalysis(universe, client_GNMAnalysis): assert_almost_equal(gnm.results.times, (0, 100), decimal=4) assert_almost_equal(gnm.results.eigenvalues, [0.1502614, 0.1426407]) gen = gnm.generate_kirchoff() + # fmt: off assert_almost_equal( gen[0], [ - 16.326744128018923, - -2.716098853586913, - -1.94736842105263, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - -0.05263157894736842, - 0.0, - 0.0, - 0.0, - -3.3541953679557905, - 0.0, - -1.4210526315789465, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - -1.0423368771244421, - -1.3006649542861801, - -0.30779350562554625, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - -0.927172649945531, - -0.7509392614826383, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - -2.263157894736841, - -0.24333213169614382, - ], + 303.0, -58.0, -37.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, + 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, -1.0,0.0, 0.0, 0.0, -67.0, 0.0, + -27.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, + 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, + 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, + 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, + 0.0, -17.0, -15.0, -6.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, + 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, + 0.0, -14.0, -15.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, + 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, + 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, + 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, + 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, + 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, + 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, + 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, + 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, -43.0, -3.0 + ] ) + # fmt: on def test_closeContactGNMAnalysis_weights_None(universe, client_GNMAnalysis): From d3de2defcefe9e1cafcaaafada43d2adeeaf4bb7 Mon Sep 17 00:00:00 2001 From: Rocco Meli Date: Fri, 27 Dec 2024 16:04:43 +0100 Subject: [PATCH 4/5] format off/on --- .../MDAnalysisTests/analysis/test_encore.py | 256 +++------------- .../MDAnalysisTests/analysis/test_gnm.py | 282 +++--------------- 2 files changed, 78 insertions(+), 460 deletions(-) diff --git a/testsuite/MDAnalysisTests/analysis/test_encore.py b/testsuite/MDAnalysisTests/analysis/test_encore.py index 707bfcea7fb..9204352f480 100644 --- a/testsuite/MDAnalysisTests/analysis/test_encore.py +++ b/testsuite/MDAnalysisTests/analysis/test_encore.py @@ -20,27 +20,25 @@ # MDAnalysis: A Toolkit for the Analysis of Molecular Dynamics Simulations. # J. Comput. Chem. 32 (2011), 2319--2327, doi:10.1002/jcc.21787 # -import MDAnalysis as mda -import MDAnalysis.analysis.encore as encore - import importlib -import tempfile -import numpy as np -import sys import os -import warnings import platform +import sys +import tempfile +import warnings from importlib import reload +import MDAnalysis as mda +import MDAnalysis.analysis.align as align +import MDAnalysis.analysis.encore as encore +import MDAnalysis.analysis.encore.confdistmatrix as confdistmatrix +import MDAnalysis.analysis.rms as rms +import numpy as np import pytest -from numpy.testing import assert_equal, assert_allclose +from numpy.testing import assert_allclose, assert_equal -from MDAnalysisTests.datafiles import DCD, DCD2, PSF, TPR, XTC from MDAnalysisTests import block_import - -import MDAnalysis.analysis.rms as rms -import MDAnalysis.analysis.align as align -import MDAnalysis.analysis.encore.confdistmatrix as confdistmatrix +from MDAnalysisTests.datafiles import DCD, DCD2, PSF, TPR, XTC def function(x): @@ -314,109 +312,19 @@ def test_ensemble_superimposition_to_reference_non_weighted(self): ) def test_covariance_matrix(self, ens1): - reference_cov = np.array( - [ - [ - 12.9122, - -5.2692, - 3.9016, - 10.0663, - -5.3309, - 3.8923, - 8.5037, - -5.2017, - 2.6941, - ], - [ - -5.2692, - 4.1087, - -2.4101, - -4.5485, - 3.3954, - -2.3245, - -3.7343, - 2.8415, - -1.6223, - ], - [ - 3.9016, - -2.4101, - 3.1800, - 3.4453, - -2.6860, - 2.2438, - 2.7751, - -2.2523, - 1.6084, - ], - [ - 10.0663, - -4.5485, - 3.4453, - 8.8608, - -4.6727, - 3.3641, - 7.0106, - -4.4986, - 2.2604, - ], - [ - -5.3309, - 3.3954, - -2.6860, - -4.6727, - 4.4627, - -2.4233, - -3.8304, - 3.0367, - -1.6942, - ], - [ - 3.8923, - -2.3245, - 2.2438, - 3.3641, - -2.4233, - 2.6193, - 2.6908, - -2.0252, - 1.5775, - ], - [ - 8.5037, - -3.7343, - 2.7751, - 7.0106, - -3.8304, - 2.6908, - 6.2861, - -3.7138, - 1.8701, - ], - [ - -5.2017, - 2.8415, - -2.2523, - -4.4986, - 3.0367, - -2.0252, - -3.7138, - 3.3999, - -1.4166, - ], - [ - 2.6941, - -1.6223, - 1.6084, - 2.2604, - -1.6942, - 1.5775, - 1.8701, - -1.4166, - 1.4664, - ], - ] - ) + # fmt: off + reference_cov = np.array([ + [12.9122,-5.2692,3.9016,10.0663,-5.3309,3.8923,8.5037,-5.2017,2.6941], + [-5.2692,4.1087,-2.4101,-4.5485,3.3954,-2.3245,-3.7343,2.8415,-1.6223], + [3.9016,-2.4101,3.1800,3.4453,-2.6860,2.2438,2.7751,-2.2523,1.6084], + [10.0663,-4.5485,3.4453,8.8608,-4.6727,3.3641,7.0106,-4.4986,2.2604], + [-5.3309,3.3954,-2.6860,-4.6727,4.4627,-2.4233,-3.8304,3.0367,-1.6942], + [3.8923,-2.3245,2.2438,3.3641,-2.4233,2.6193,2.6908,-2.0252,1.5775], + [8.5037,-3.7343,2.7751,7.0106,-3.8304,2.6908,6.2861,-3.7138,1.8701], + [-5.2017,2.8415,-2.2523,-4.4986,3.0367,-2.0252,-3.7138,3.3999,-1.4166], + [2.6941,-1.6223,1.6084,2.2604,-1.6942,1.5775,1.8701,-1.4166,1.4664] + ]) + # fmt: on covariance = encore.covariance.covariance_matrix( ens1, @@ -432,109 +340,19 @@ def test_covariance_matrix(self, ens1): ) def test_covariance_matrix_with_reference(self, ens1): - reference_cov = np.array( - [ - [ - 39.0760, - -28.5383, - 29.7761, - 37.9330, - -35.5251, - 18.9421, - 30.4334, - -31.4829, - 12.8712, - ], - [ - -28.5383, - 24.1827, - -25.5676, - -29.0183, - 30.3511, - -15.9598, - -22.9298, - 26.1086, - -10.8693, - ], - [ - 29.7761, - -25.5676, - 28.9796, - 30.7607, - -32.8739, - 17.7072, - 24.1689, - -28.3557, - 12.1190, - ], - [ - 37.9330, - -29.0183, - 30.7607, - 37.6532, - -36.4537, - 19.2865, - 29.9841, - -32.1404, - 12.9998, - ], - [ - -35.5251, - 30.3511, - -32.8739, - -36.4537, - 38.5711, - -20.1190, - -28.7652, - 33.2857, - -13.6963, - ], - [ - 18.9421, - -15.9598, - 17.7072, - 19.2865, - -20.1190, - 11.4059, - 15.1244, - -17.2695, - 7.8205, - ], - [ - 30.4334, - -22.9298, - 24.1689, - 29.9841, - -28.7652, - 15.1244, - 24.0514, - -25.4106, - 10.2863, - ], - [ - -31.4829, - 26.1086, - -28.3557, - -32.1404, - 33.2857, - -17.2695, - -25.4106, - 29.1773, - -11.7530, - ], - [ - 12.8712, - -10.8693, - 12.1190, - 12.9998, - -13.6963, - 7.8205, - 10.2863, - -11.7530, - 5.5058, - ], - ] - ) + # fmt: off + reference_cov = np.array([ + [39.0760,-28.5383,29.7761,37.9330,-35.5251,18.9421,30.4334,-31.4829,12.8712], + [-28.5383,24.1827,-25.5676,-29.0183,30.3511,-15.9598,-22.9298,26.1086,-10.8693], + [29.7761,-25.5676,28.9796,30.7607,-32.8739,17.7072,24.1689,-28.3557,12.1190], + [37.9330,-29.0183,30.7607,37.6532,-36.4537,19.2865,29.9841,-32.1404,12.9998], + [-35.5251,30.3511,-32.8739,-36.4537,38.5711,-20.1190,-28.7652,33.2857,-13.6963], + [18.9421,-15.9598,17.7072,19.2865,-20.1190,11.4059,15.1244,-17.2695,7.8205], + [30.4334,-22.9298,24.1689,29.9841,-28.7652,15.1244,24.0514,-25.4106,10.2863], + [-31.4829,26.1086,-28.3557,-32.1404,33.2857,-17.2695,-25.4106,29.1773,-11.7530], + [12.8712,-10.8693,12.1190,12.9998,-13.6963,7.8205,10.2863,-11.7530,5.5058] + ]) + # fmt: on covariance = encore.covariance.covariance_matrix( ens1, diff --git a/testsuite/MDAnalysisTests/analysis/test_gnm.py b/testsuite/MDAnalysisTests/analysis/test_gnm.py index 6114ad66f1d..0283ca66b6b 100644 --- a/testsuite/MDAnalysisTests/analysis/test_gnm.py +++ b/testsuite/MDAnalysisTests/analysis/test_gnm.py @@ -80,26 +80,18 @@ def test_generate_kirchoff(universe): assert_almost_equal( gen[0], [ - 16.326744128018923, -2.716098853586913, -1.94736842105263, 0.0, - 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, - 0.0, 0.0, -0.05263157894736842, 0.0, 0.0, 0.0, -3.3541953679557905, - 0.0, -1.4210526315789465, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, - 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, - 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, - 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, - 0.0, 0.0, 0.0, 0.0, 0.0, -1.0423368771244421, -1.3006649542861801, - -0.30779350562554625, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, - 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, - -0.927172649945531, -0.7509392614826383, 0.0, 0.0, 0.0, 0.0, 0.0, - 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, - 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, - 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, - 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, - 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, - 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, - 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, - 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, - -2.263157894736841, -0.24333213169614382 + 7,-1,-1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0,-1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0,-1,-1,-1, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,-1, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ] ) # fmt: on @@ -126,23 +118,26 @@ def test_closeContactGNMAnalysis(universe, client_GNMAnalysis): assert_almost_equal( gen[0], [ - 303.0, -58.0, -37.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, - 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, -1.0,0.0, 0.0, 0.0, -67.0, 0.0, - -27.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, + 16.326744128018923, -2.716098853586913, -1.94736842105263, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, + 0.0, 0.0, -0.05263157894736842, 0.0, 0.0, 0.0, -3.3541953679557905, + 0.0, -1.4210526315789465, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, - 0.0, -17.0, -15.0, -6.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, - 0.0, -14.0, -15.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, + 0.0, 0.0, 0.0, 0.0, 0.0, -1.0423368771244421, -1.3006649542861801, + -0.30779350562554625, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, + -0.927172649945531, -0.7509392614826383, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, - 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, -43.0, -3.0 + 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, + 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, + -2.263157894736841, -0.24333213169614382 ] ) # fmt: on @@ -156,222 +151,27 @@ def test_closeContactGNMAnalysis_weights_None(universe, client_GNMAnalysis): assert_almost_equal(gnm.results.times, (0, 100), decimal=4) assert_almost_equal(gnm.results.eigenvalues, [2.4328739, 2.2967251]) gen = gnm.generate_kirchoff() + # fmt: off assert_almost_equal( gen[0], [ - 303.0, - -58.0, - -37.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - -1.0, - 0.0, - 0.0, - 0.0, - -67.0, - 0.0, - -27.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - -17.0, - -15.0, - -6.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - -14.0, - -15.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - -43.0, - -3.0, - ], + 303.0, -58.0, -37.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, + 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, -1.0,0.0, 0.0, 0.0, -67.0, 0.0, + -27.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, + 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, + 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, + 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, + 0.0, -17.0, -15.0, -6.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, + 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, + 0.0, -14.0, -15.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, + 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, + 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, + 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, + 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, + 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, + 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, + 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, + 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, -43.0, -3.0 + ] ) + # fmt: on From 56ea87533c98a98c0806b0f938ac69e01684d0c1 Mon Sep 17 00:00:00 2001 From: Rocco Meli Date: Fri, 27 Dec 2024 16:14:47 +0100 Subject: [PATCH 5/5] update formatting x3dna --- package/MDAnalysis/analysis/legacy/x3dna.py | 234 ++++---------------- 1 file changed, 49 insertions(+), 185 deletions(-) diff --git a/package/MDAnalysis/analysis/legacy/x3dna.py b/package/MDAnalysis/analysis/legacy/x3dna.py index 9ace5edf233..2365c181133 100644 --- a/package/MDAnalysis/analysis/legacy/x3dna.py +++ b/package/MDAnalysis/analysis/legacy/x3dna.py @@ -117,24 +117,23 @@ .. autoexception:: ApplicationError """ +import errno import glob +import logging import os -import errno -import shutil -import warnings import os.path +import shutil import subprocess import tempfile import textwrap +import warnings from collections import OrderedDict -import numpy as np import matplotlib.pyplot as plt +import numpy as np from MDAnalysis import ApplicationError -from MDAnalysis.lib.util import which, realpath, asiterable, deprecate - -import logging +from MDAnalysis.lib.util import asiterable, deprecate, realpath, which logger = logging.getLogger("MDAnalysis.analysis.x3dna") @@ -178,20 +177,10 @@ def mean_std_from_x3dnaPickle(profile): category=DeprecationWarning, ) if profile.x3dna_param is False: - ( - bp_shear, - bp_stretch, - bp_stagger, - bp_rise, - bp_shift, - bp_slide, - bp_buckle, - bp_prop, - bp_open, - bp_tilt, - bp_roll, - bp_twist, - ) = ([], [], [], [], [], [], [], [], [], [], [], []) + # fmt: off + bp_shear, bp_stretch, bp_stagger, bp_rise, bp_shift, bp_slide, bp_buckle, bp_prop, bp_open, bp_tilt, bp_roll,\ + bp_twist = [], [], [], [], [], [], [], [], [], [], [], [] + # fmt: on for i in range(len(profile)): bp_shear.append(profile.values()[i].Shear) bp_stretch.append(profile.values()[i].Stretch) @@ -205,33 +194,12 @@ def mean_std_from_x3dnaPickle(profile): bp_tilt.append(profile.values()[i].Tilt) bp_roll.append(profile.values()[i].Roll) bp_twist.append(profile.values()[i].Twist) - ( - bp_shear, - bp_stretch, - bp_stagger, - bp_rise, - bp_shift, - bp_slide, - bp_buckle, - bp_prop, - bp_open, - bp_tilt, - bp_roll, - bp_twist, - ) = ( - np.array(bp_shear), - np.array(bp_stretch), - np.array(bp_stagger), - np.array(bp_rise), - np.array(bp_shift), - np.array(bp_slide), - np.array(bp_buckle), - np.array(bp_prop), - np.array(bp_open), - np.array(bp_tilt), - np.array(bp_roll), - np.array(bp_twist), - ) + # fmt: off + bp_shear, bp_stretch, bp_stagger, bp_rise, bp_shift, bp_slide, bp_buckle, bp_prop, bp_open, bp_tilt, bp_roll,\ + bp_twist = np.array(bp_shear), np.array(bp_stretch), np.array(bp_stagger), np.array(bp_rise),\ + np.array(bp_shift), np.array(bp_slide), np.array(bp_buckle), np.array(bp_prop), \ + np.array(bp_open), np.array(bp_tilt), np.array(bp_roll), np.array(bp_twist) + # fmt: on na_avg, na_std = [], [] for j in range(len(bp_shear[0])): na_avg.append( @@ -267,20 +235,9 @@ def mean_std_from_x3dnaPickle(profile): ] ) else: - bp_rise, bp_shift, bp_slide, bp_tilt, bp_roll, bp_twist = ( - [], - [], - [], - [], - [], - [], - [], - [], - [], - [], - [], - [], - ) + # fmt: off + bp_rise, bp_shift, bp_slide, bp_tilt, bp_roll, bp_twist = [], [], [], [], [], [], [], [], [], [], [], [] + # fmt: on for i in range(len(profile)): # print i bp_rise.append(profile.values()[i].Rise) @@ -389,20 +346,10 @@ def mean_std(self): roll, twist]``. """ - ( - bp_shear, - bp_stretch, - bp_stagger, - bp_rise, - bp_shift, - bp_slide, - bp_buckle, - bp_prop, - bp_open, - bp_tilt, - bp_roll, - bp_twist, - ) = ([], [], [], [], [], [], [], [], [], [], [], []) + # fmt: off + bp_shear, bp_stretch, bp_stagger, bp_rise, bp_shift, bp_slide, bp_buckle, bp_prop, bp_open, bp_tilt, bp_roll,\ + bp_twist = [], [], [], [], [], [], [], [], [], [], [], [] + # fmt: on for i in range(len(self.profiles)): bp_shear.append(self.profiles.values()[i].Shear) bp_stretch.append(self.profiles.values()[i].Stretch) @@ -416,33 +363,12 @@ def mean_std(self): bp_tilt.append(self.profiles.values()[i].Tilt) bp_roll.append(self.profiles.values()[i].Roll) bp_twist.append(self.profiles.values()[i].Twist) - ( - bp_shear, - bp_stretch, - bp_stagger, - bp_rise, - bp_shift, - bp_slide, - bp_buckle, - bp_prop, - bp_open, - bp_tilt, - bp_roll, - bp_twist, - ) = ( - np.array(bp_shear), - np.array(bp_stretch), - np.array(bp_stagger), - np.array(bp_rise), - np.array(bp_shift), - np.array(bp_slide), - np.array(bp_buckle), - np.array(bp_prop), - np.array(bp_open), - np.array(bp_tilt), - np.array(bp_roll), - np.array(bp_twist), - ) + # fmt: off + bp_shear, bp_stretch, bp_stagger, bp_rise, bp_shift, bp_slide, bp_buckle, bp_prop, bp_open, bp_tilt, bp_roll,\ + bp_twist = np.array(bp_shear), np.array(bp_stretch), np.array(bp_stagger), np.array(bp_rise),\ + np.array(bp_shift), np.array(bp_slide), np.array(bp_buckle), np.array(bp_prop),\ + np.array(bp_open), np.array(bp_tilt), np.array(bp_roll), np.array(bp_twist) + # fmt: on na_avg, na_std = [], [] for j in range(len(bp_shear[0])): na_avg.append( @@ -491,20 +417,10 @@ def mean(self): shift, slide, rise, tilt, roll, twist]``. """ - ( - bp_shear, - bp_stretch, - bp_stagger, - bp_rise, - bp_shift, - bp_slide, - bp_buckle, - bp_prop, - bp_open, - bp_tilt, - bp_roll, - bp_twist, - ) = ([], [], [], [], [], [], [], [], [], [], [], []) + # fmt: off + bp_shear, bp_stretch, bp_stagger, bp_rise, bp_shift, bp_slide, bp_buckle, bp_prop, bp_open, bp_tilt, bp_roll,\ + bp_twist = [], [], [], [], [], [], [], [], [], [], [], [] + # fmt: on for i in range(len(self.profiles)): bp_shear.append(self.profiles.values()[i].Shear) bp_stretch.append(self.profiles.values()[i].Stretch) @@ -518,33 +434,12 @@ def mean(self): bp_tilt.append(self.profiles.values()[i].Tilt) bp_roll.append(self.profiles.values()[i].Roll) bp_twist.append(self.profiles.values()[i].Twist) - ( - bp_shear, - bp_stretch, - bp_stagger, - bp_rise, - bp_shift, - bp_slide, - bp_buckle, - bp_prop, - bp_open, - bp_tilt, - bp_roll, - bp_twist, - ) = ( - np.array(bp_shear), - np.array(bp_stretch), - np.array(bp_stagger), - np.array(bp_rise), - np.array(bp_shift), - np.array(bp_slide), - np.array(bp_buckle), - np.array(bp_prop), - np.array(bp_open), - np.array(bp_tilt), - np.array(bp_roll), - np.array(bp_twist), - ) + # fmt: off + bp_shear, bp_stretch, bp_stagger, bp_rise, bp_shift, bp_slide, bp_buckle, bp_prop, bp_open, bp_tilt, bp_roll,\ + bp_twist = np.array(bp_shear), np.array(bp_stretch), np.array(bp_stagger), np.array(bp_rise),\ + np.array(bp_shift), np.array(bp_slide), np.array(bp_buckle), np.array(bp_prop),\ + np.array(bp_open), np.array(bp_tilt), np.array(bp_roll), np.array(bp_twist) + # fmt: on na_avg = [] for j in range(len(bp_shear[0])): @@ -578,20 +473,10 @@ def std(self): propeller, opening, shift, slide, rise, tilt, roll, twist]``. """ - ( - bp_shear, - bp_stretch, - bp_stagger, - bp_rise, - bp_shift, - bp_slide, - bp_buckle, - bp_prop, - bp_open, - bp_tilt, - bp_roll, - bp_twist, - ) = ([], [], [], [], [], [], [], [], [], [], [], []) + # fmt: off + bp_shear, bp_stretch, bp_stagger, bp_rise, bp_shift, bp_slide, bp_buckle, bp_prop, bp_open, bp_tilt, bp_roll,\ + bp_twist = [], [], [], [], [], [], [], [], [], [], [], [] + # fmt: on for i in range(len(self.profiles)): bp_shear.append(self.profiles.values()[i].Shear) bp_stretch.append(self.profiles.values()[i].Stretch) @@ -605,33 +490,12 @@ def std(self): bp_tilt.append(self.profiles.values()[i].Tilt) bp_roll.append(self.profiles.values()[i].Roll) bp_twist.append(self.profiles.values()[i].Twist) - ( - bp_shear, - bp_stretch, - bp_stagger, - bp_rise, - bp_shift, - bp_slide, - bp_buckle, - bp_prop, - bp_open, - bp_tilt, - bp_roll, - bp_twist, - ) = ( - np.array(bp_shear), - np.array(bp_stretch), - np.array(bp_stagger), - np.array(bp_rise), - np.array(bp_shift), - np.array(bp_slide), - np.array(bp_buckle), - np.array(bp_prop), - np.array(bp_open), - np.array(bp_tilt), - np.array(bp_roll), - np.array(bp_twist), - ) + # fmt: off + bp_shear, bp_stretch, bp_stagger, bp_rise, bp_shift, bp_slide, bp_buckle, bp_prop, bp_open, bp_tilt, bp_roll,\ + bp_twist = np.array(bp_shear), np.array(bp_stretch), np.array(bp_stagger), np.array(bp_rise),\ + np.array(bp_shift), np.array(bp_slide), np.array(bp_buckle), np.array(bp_prop),\ + np.array(bp_open), np.array(bp_tilt), np.array(bp_roll), np.array(bp_twist) + # fmt: on na_std = [] for j in range(len(bp_shear[0])):