diff --git a/package/MDAnalysis/core/groups.py b/package/MDAnalysis/core/groups.py index 0637c201582..8502f1e9ff7 100644 --- a/package/MDAnalysis/core/groups.py +++ b/package/MDAnalysis/core/groups.py @@ -90,6 +90,7 @@ """ from six.moves import zip +from six import string_types from collections import namedtuple import numpy as np @@ -382,6 +383,10 @@ def __add__(self, other): Group with elements of `self` and `other` concatenated """ + if not isinstance(other, (ComponentBase, GroupBase)): # sanity check + raise TypeError("unsupported operand type(s) for +:" + " '{}' and '{}'".format(type(self).__name__, + type(other).__name__)) if self.level != other.level: raise TypeError("Can't add different level objects") if self._u is not other._u: @@ -975,6 +980,15 @@ class AtomGroup(GroupBase): .. SeeAlso:: :class:`MDAnalysis.core.universe.Universe` """ + def __getitem__(self, item): + # u.atoms['HT1'] access, otherwise default + if isinstance(item, string_types): + try: + return self._get_named_atom(item) + except (AttributeError, selection.SelectionError): + pass + return super(AtomGroup, self).__getitem__(item) + def __getattr__(self, attr): # is this a known attribute failure? if attr in ('fragments',): # TODO: Generalise this to cover many attributes @@ -1844,6 +1858,10 @@ def __add__(self, other): Group with elements of `self` and `other` concatenated """ + if not isinstance(other, (ComponentBase, GroupBase)): # sanity check + raise TypeError("unsupported operand type(s) for +:" + " '{}' and '{}'".format(type(self).__name__, + type(other).__name__)) if self.level != other.level: raise TypeError('Can only add {0}s or {1}s (not {2}s/{3}s)' ' to {0}'.format(self.level.singular.__name__, diff --git a/package/MDAnalysis/core/topologyattrs.py b/package/MDAnalysis/core/topologyattrs.py index 9f1fd08d512..3c3dae794d3 100644 --- a/package/MDAnalysis/core/topologyattrs.py +++ b/package/MDAnalysis/core/topologyattrs.py @@ -32,21 +32,131 @@ """ from six.moves import zip, range + +import Bio.Seq +import Bio.SeqRecord +import Bio.Alphabet from collections import defaultdict +import functools import itertools import numpy as np from . import flags -from ..lib.util import cached, convert_aa_code +from ..lib.util import cached, convert_aa_code, iterable from ..lib import transformations, mdamath from ..exceptions import NoDataError, SelectionError from .topologyobjects import TopologyGroup from . import selection -from .groups import (GroupBase, Atom, Residue, Segment, +from .groups import (ComponentBase, GroupBase, + Atom, Residue, Segment, AtomGroup, ResidueGroup, SegmentGroup) -_LENGTH_VALUEERROR = ("Setting {group} with wrong sized array. " - "Length {group}: {lengroup}, length values: {lenvalues}") + +def _check_length(func): + """Wrapper which checks the length of inputs to set_X + + Eg: + + @_check_length + def set_X(self, group, values): + + Will check the length of *values* compared to *group* before proceeding with + anything in the *set_X* method. + + Pseudo code for the check: + + if group in (Atom, Residue, Segment): + values must be single values, ie int, float or string + else: + values must be single value OR same length as group + + """ + _SINGLE_VALUE_ERROR = ("Setting {cls} {attrname} with wrong sized input. " + "Must use single value, length of supplied values: {lenvalues}.") + # Eg "Setting Residue resid with wrong sized input. Must use single value, length of supplied + # values: 2." + + _GROUP_VALUE_ERROR = ("Setting {group} {attrname} with wrong sized array. " + "Length {group}: {lengroup}, length of supplied values: {lenvalues}.") + # Eg "Setting AtomGroup masses with wrong sized array. Length AtomGroup: 100, length of + # supplied values: 50." + + def _attr_len(values): + # quasi len measurement + # strings, floats, ints are len 0, ie not iterable + # other iterables are just len'd + if iterable(values): + return len(values) + else: + return 0 # special case + + @functools.wraps(func) + def wrapper(attr, group, values): + val_len = _attr_len(values) + + if isinstance(group, ComponentBase): + if not val_len == 0: + raise ValueError(_SINGLE_VALUE_ERROR.format( + cls=group.__class__.__name__, attrname=attr.singular, + lenvalues=val_len)) + else: + if not (val_len == 0 or val_len == len(group)): + raise ValueError(_GROUP_VALUE_ERROR.format( + group=group.__class__.__name__, attrname=attr.attrname, + lengroup=len(group), lenvalues=val_len)) + # if everything went OK, continue with the function + return func(attr, group, values) + + return wrapper + + +def _wronglevel_error(attr, group): + """Generate an error for setting attr at wrong level + + attr : TopologyAttr that was accessed + group : Offending Component/Group + + Eg: + setting mass of residue, gets called with attr=Masses, group=residue + + raises a NotImplementedError with: + 'Cannot set masses from Residue. Use 'Residue.atoms.masses' + + Mainly used to ensure consistent and helpful error messages + """ + if isinstance(group, (Atom, AtomGroup)): + group_level = 1 + elif isinstance(group, (Residue, ResidueGroup)): + group_level = 2 + elif isinstance(group, (Segment, SegmentGroup)): + group_level = 3 + + # What level to go to before trying to set this attr + if isinstance(attr, AtomAttr): + corr_classes = ('atoms', 'atom') + attr_level = 1 + elif isinstance(attr, ResidueAttr): + corr_classes = ('residues', 'residue') + attr_level = 2 + elif isinstance(attr, SegmentAttr): + corr_classes = ('segments', 'segment') + attr_level = 3 + + if isinstance(group, ComponentBase) and (attr_level > group_level): + # ie going downards use plurals, going upwards use singulars + # Residue.atom!s!.mass!es! but Atom.segment!!.segid!! + correct = corr_classes[1] + attrname = attr.singular + else: + correct = corr_classes[0] + attrname = attr.attrname + + err_msg = "Cannot set {attr} from {cls}. Use '{cls}.{correct}.{attr} = '" + # eg "Cannot set masses from Residue. 'Use Residue.atoms.masses = '" + + return NotImplementedError(err_msg.format( + attr=attrname, cls=group.__class__.__name__, correct=correct, + )) class TopologyAttr(object): @@ -109,7 +219,6 @@ def is_guessed(self): def get_atoms(self, ag): """Get atom attributes for a given AtomGroup""" - # aix = ag.indices raise NoDataError def set_atoms(self, ag, values): @@ -245,6 +354,7 @@ class AtomAttr(TopologyAttr): def get_atoms(self, ag): return self.values[ag._ix] + @_check_length def set_atoms(self, ag, values): self.values[ag._ix] = values @@ -257,6 +367,9 @@ def get_residues(self, rg): aixs = self.top.tt.residues2atoms_2d(rg._ix) return [self.values[aix] for aix in aixs] + def set_residues(self, rg, values): + raise _wronglevel_error(self, rg) + def get_segments(self, sg): """By default, the values for each atom present in the set of residues are returned in a single array. This behavior can be overriden in child @@ -266,6 +379,9 @@ def get_segments(self, sg): aixs = self.top.tt.segments2atoms_2d(sg._ix) return [self.values[aix] for aix in aixs] + def set_segments(self, sg, values): + raise _wronglevel_error(self, sg) + # TODO: update docs to property doc class Atomids(AtomAttr): @@ -891,9 +1007,13 @@ def get_atoms(self, ag): rix = self.top.tt.atoms2residues(ag._ix) return self.values[rix] + def set_atoms(self, ag, values): + raise _wronglevel_error(self, ag) + def get_residues(self, rg): return self.values[rg._ix] + @_check_length def set_residues(self, rg, values): self.values[rg._ix] = values @@ -906,6 +1026,9 @@ def get_segments(self, sg): rixs = self.top.tt.segments2residues_2d(sg._ix) return [self.values[rix] for rix in rixs] + def set_segments(self, sg, values): + raise _wronglevel_error(self, sg) + # TODO: update docs to property doc class Resids(ResidueAttr): @@ -1004,37 +1127,36 @@ def sequence(self, **kwargs): Bio.SeqIO.write([record1, record2, ...], "multi.fasta", "fasta") - :Keywords: - *format* - - - ``"string"``: return sequence as a string of 1-letter codes - - ``"Seq"``: return a :class:`Bio.Seq.Seq` instance - - ``"SeqRecord"``: return a :class:`Bio.SeqRecord.SeqRecord` - instance - - Default is ``"SeqRecord"`` - - *id* - Sequence ID for SeqRecord (should be different for different - sequences) - *name* - Name of the protein. - *description* - Short description of the sequence. - *kwargs* - Any other keyword arguments that are understood by - :class:`Bio.SeqRecord.SeqRecord`. - - :Raises: :exc:`ValueError` if a residue name cannot be converted to a - 1-letter IUPAC protein amino acid code; make sure to only - select protein residues. Raises :exc:`TypeError` if an unknown - *format* is selected. + Parameters + ---------- + format : string, optional + - ``"string"``: return sequence as a string of 1-letter codes + - ``"Seq"``: return a :class:`Bio.Seq.Seq` instance + - ``"SeqRecord"``: return a :class:`Bio.SeqRecord.SeqRecord` + instance + + Default is ``"SeqRecord"`` + id : optional + Sequence ID for SeqRecord (should be different for different + sequences) + name : optional + Name of the protein. + description : optional + Short description of the sequence. + kwargs : optional + Any other keyword arguments that are understood by + class:`Bio.SeqRecord.SeqRecord`. + + Raises + ------ + :exc:`ValueError` if a residue name cannot be converted to a + 1-letter IUPAC protein amino acid code; make sure to only + select protein residues. + + :exc:`TypeError` if an unknown *format* is selected. .. versionadded:: 0.9.0 """ - import Bio.Seq - import Bio.SeqRecord - import Bio.Alphabet formats = ('string', 'Seq', 'SeqRecord') format = kwargs.pop("format", "SeqRecord") @@ -1086,13 +1208,20 @@ def get_atoms(self, ag): six = self.top.tt.atoms2segments(ag._ix) return self.values[six] + def set_atoms(self, ag, values): + raise _wronglevel_error(self, ag) + def get_residues(self, rg): six = self.top.tt.residues2segments(rg._ix) return self.values[six] + def set_residues(self, rg, values): + raise _wronglevel_error(self, rg) + def get_segments(self, sg): return self.values[sg._ix] + @_check_length def set_segments(self, sg, values): self.values[sg._ix] = values @@ -1184,6 +1313,9 @@ def _bondDict(self): bd[a].append((b, t, g, o)) return bd + def set_atoms(self, ag): + return NotImplementedError("Cannot set bond information") + def get_atoms(self, ag): try: unique_bonds = set(itertools.chain( diff --git a/package/MDAnalysis/core/universe.py b/package/MDAnalysis/core/universe.py index 5b30475c01b..00aa263746f 100644 --- a/package/MDAnalysis/core/universe.py +++ b/package/MDAnalysis/core/universe.py @@ -64,14 +64,14 @@ import sys from .. import _ANCHOR_UNIVERSES +from ..exceptions import NoDataError from ..lib import util -from ..lib.util import cached from ..lib.log import ProgressMeter, _set_verbose -from ..exceptions import NoDataError +from ..lib.util import cached from . import groups +from ._get_readers import get_reader_for, get_parser_for from .groups import (GroupBase, Atom, Residue, Segment, AtomGroup, ResidueGroup, SegmentGroup) -from ._get_readers import get_reader_for, get_parser_for from .topology import Topology from .topologyattrs import AtomAttr, ResidueAttr, SegmentAttr @@ -158,7 +158,7 @@ class Universe(object): in_memory After reading in the trajectory, transfer it to an in-memory representations, which allow for manipulation of coordinates. - in_memory_frame_interval + in_memory_step Only read every nth frame into in-memory representation. Attributes @@ -387,11 +387,11 @@ def load_new(self, filename, format=None, in_memory=False, **kwargs): trj_n_atoms=self.trajectory.n_atoms)) if in_memory: - self.transfer_to_memory(kwargs.get("in_memory_frame_interval", 1)) + self.transfer_to_memory(step=kwargs.get("in_memory_step", 1)) return filename, self.trajectory.format - def transfer_to_memory(self, frame_interval=1, verbose=None, quiet=None): + def transfer_to_memory(self, start=None, stop=None, step=None, verbose=None, quiet=None): """Transfer the trajectory to in memory representation. Replaces the current trajectory reader object with one of type @@ -400,7 +400,11 @@ def transfer_to_memory(self, frame_interval=1, verbose=None, quiet=None): Parameters ---------- - frame_interval : int, optional + start: int, optional + start reading from the nth frame. + stop: int, optional + read upto and excluding the nth frame. + step : int, optional Read in every nth frame. [1] verbose : bool, optional Will print the progress of loading trajectory to memory, if @@ -419,14 +423,14 @@ def transfer_to_memory(self, frame_interval=1, verbose=None, quiet=None): # trajectory file formats try: coordinates = self.trajectory.timeseries( - self.atoms, format='fac', step=frame_interval) + self.atoms, start=start, stop=stop, step=step, format='fac') # if the Timeseries extraction fails, # fall back to a slower approach except AttributeError: pm = ProgressMeter(self.trajectory.n_frames, - interval=frame_interval, verbose=verbose) + interval=step, verbose=verbose) coordinates = [] # TODO: use pre-allocated array - for ts in self.trajectory[::frame_interval]: + for ts in self.trajectory[start:stop:step]: coordinates.append(np.copy(ts.positions)) pm.echo(ts.frame) coordinates = np.array(coordinates) diff --git a/package/doc/sphinx/source/documentation_pages/overview.rst b/package/doc/sphinx/source/documentation_pages/overview.rst index f0afff09433..faa5f225114 100644 --- a/package/doc/sphinx/source/documentation_pages/overview.rst +++ b/package/doc/sphinx/source/documentation_pages/overview.rst @@ -1,3 +1,5 @@ +.. _overview-label: + ========================== Overview over MDAnalysis ========================== @@ -134,10 +136,7 @@ as described in :ref:`selection-commands-label`. Examples ======== -The easiest way to get started with MDAnalysis is to read this -introduction and the chapter on :ref:`selection-commands-label` and then -explore the package interactively in IPython_ or another interactive -Python interpreter. +The easiest way to get started with MDAnalysis is to read this introduction and the chapters on :ref:`topology-label` and :ref:`selection-commands-label`, then explore the package interactively in IPython_ or another interactive Python interpreter. Included trajectories --------------------- diff --git a/package/doc/sphinx/source/documentation_pages/selections.rst b/package/doc/sphinx/source/documentation_pages/selections.rst index 5d70cdcbded..77851225050 100644 --- a/package/doc/sphinx/source/documentation_pages/selections.rst +++ b/package/doc/sphinx/source/documentation_pages/selections.rst @@ -2,7 +2,7 @@ .. _selection-commands-label: ==================== - Selection Commands + Selection commands ==================== Once you have the :meth:`~MDAnalysis.core.universe.Universe` object, you can diff --git a/package/doc/sphinx/source/documentation_pages/topology.rst b/package/doc/sphinx/source/documentation_pages/topology.rst new file mode 100644 index 00000000000..ecf61b0af17 --- /dev/null +++ b/package/doc/sphinx/source/documentation_pages/topology.rst @@ -0,0 +1,15 @@ +.. -*- coding: utf-8 -*- +.. _topology-label: + +===================== + The topology system +===================== + +As shown briefly in :ref:`overview-label`, the :class:`~MDAnalysis.core.universe.Universe` class is the primary object and core interface to molecular dynamics data in MDAnalysis. +When loading topology information from a file, as with :: + + >>> from MDAnalysis import Universe + >>> from MDAnalysis.tests.datafiles import PSF + >>> u = Universe(PSF) + +the file is read, the contents parsed, and a :class:`~MDAnalysis.core.topology.Topology` object is constructed from these contents. diff --git a/package/doc/sphinx/source/index.rst b/package/doc/sphinx/source/index.rst index 1830fe69a18..27b33473d65 100644 --- a/package/doc/sphinx/source/index.rst +++ b/package/doc/sphinx/source/index.rst @@ -75,6 +75,7 @@ Contents :numbered: ./documentation_pages/overview + ./documentation_pages/topology ./documentation_pages/selections ./documentation_pages/analysis_modules ./documentation_pages/topology_modules diff --git a/testsuite/AUTHORS b/testsuite/AUTHORS index da60d4f7dba..6745b837268 100644 --- a/testsuite/AUTHORS +++ b/testsuite/AUTHORS @@ -71,6 +71,9 @@ Chronological list of authors - Fiona B. Naughton - Robert Delgado +2017 + - Utkarsh Bansal + External code ------------- diff --git a/testsuite/CHANGELOG b/testsuite/CHANGELOG index 6421abfa8d3..77800f0b95f 100644 --- a/testsuite/CHANGELOG +++ b/testsuite/CHANGELOG @@ -14,7 +14,7 @@ and https://github.com/MDAnalysis/mdanalysis/wiki/UnitTests ------------------------------------------------------------------------------ ??/??/16 jbarnoud, orbeckst, fiona-naughton, manuel.nuno.melo, richardjgowers - tyler.je.reddy + tyler.je.reddy, utkbansal * 0.16 - added two unit tests for MDAnalysis.analysis.polymer diff --git a/testsuite/MDAnalysisTests/__init__.py b/testsuite/MDAnalysisTests/__init__.py index 3d49b801276..6d830740c2d 100644 --- a/testsuite/MDAnalysisTests/__init__.py +++ b/testsuite/MDAnalysisTests/__init__.py @@ -128,19 +128,10 @@ # code won't be run again under coverage's watch. See Issue 344. import os -from contextlib import contextmanager +import sys # We get our nose from the plugins so that version-checking needs only be done there. from MDAnalysisTests.plugins import nose, loaded_plugins -import sys -import importlib -# This is a de facto test for numpy's version, since we don't actually need assert_ here. -# Should we be clean about this and just call distutils to compare version strings? -try: - from numpy.testing import assert_ -except ImportError: - raise ImportError("""numpy>=1.5 is required to run the test suite. Please install it first. """ - """(For example, try "easy_install 'numpy>=1.5'").""") # Any tests that plot with matplotlib need to run with the simple agg backend because # on Travis there is no DISPLAY set @@ -150,7 +141,14 @@ except ImportError: pass -from MDAnalysisTests.util import block_import +from MDAnalysisTests.util import ( + block_import, + executable_not_found, + module_not_found, + parser_not_found, + in_dir, +) +from MDAnalysisTests.core.util import make_Universe def run(*args, **kwargs): """Test-running function that loads plugins, sets up arguments, and calls `nose.run_exit()`""" @@ -175,77 +173,3 @@ def run(*args, **kwargs): return nose.run_exit(*args, **kwargs) -def executable_not_found(*args): - """Return ``True`` if none of the executables in args can be found. - - ``False`` otherwise (i.e. at least one was found). - - To be used as the argument of:: - - @dec.skipif(executable_not_found("binary_name"), msg="skip test because binary_name not available") - """ - # This must come here so that MDAnalysis isn't imported prematurely, - # which spoils coverage accounting (see Issue 344). - import MDAnalysis.lib.util - for name in args: - if MDAnalysis.lib.util.which(name) is not None: - return False - return True - - -def module_not_found(module): - try: - importlib.import_module(module) - except ImportError: - return True - else: - return False - - -def parser_not_found(parser_name): - """Return ``True`` if the parser of the given name cannot be found. - - This allows to skip a test when the parser is unavailable (e.g in python 3 - when the parser is not compatible). - - To be used as the argument of:: - - @dec.skipif(parser_not_found('DCD'), - 'DCD parser is not available. Are you on python 3?') - """ - import MDAnalysis.coordinates - try: - getattr(MDAnalysis.coordinates, parser_name) - except AttributeError: - return True - else: - return False - -@contextmanager -def in_dir(dirname): - """Context manager for safely changing directories. - - Arguments - --------- - dirname : string - directory to change into - - Example - ------- - Change into a temporary directory and always change back to the - current one:: - - with in_dir("/tmp") as tmpdir: - # do stuff - - SeeAlso - ------- - The :mod:`tmpdir` module provides functionality such as :func:`tmpdir.in_tmpdir` - to create temporary directories that are automatically deleted once they are no - longer used. - """ - - old_path = os.getcwd() - os.chdir(dirname) - yield dirname - os.chdir(old_path) diff --git a/testsuite/MDAnalysisTests/coordinates/test_crd.py b/testsuite/MDAnalysisTests/coordinates/test_crd.py index 6ae52facbb5..c76f80fb26f 100644 --- a/testsuite/MDAnalysisTests/coordinates/test_crd.py +++ b/testsuite/MDAnalysisTests/coordinates/test_crd.py @@ -10,8 +10,8 @@ import MDAnalysis as mda from MDAnalysisTests.datafiles import CRD -from MDAnalysisTests import tempdir -from MDAnalysisTests.core.groupbase import make_Universe +from MDAnalysisTests import tempdir, make_Universe + class TestCRDWriter(object): def setUp(self): diff --git a/testsuite/MDAnalysisTests/coordinates/test_gro.py b/testsuite/MDAnalysisTests/coordinates/test_gro.py index d409b94d273..fbae2d2e046 100644 --- a/testsuite/MDAnalysisTests/coordinates/test_gro.py +++ b/testsuite/MDAnalysisTests/coordinates/test_gro.py @@ -15,8 +15,7 @@ ) from MDAnalysisTests.coordinates.reference import RefAdK from MDAnalysisTests.coordinates.base import BaseTimestepTest -from MDAnalysisTests import tempdir -from MDAnalysisTests.core.groupbase import make_Universe +from MDAnalysisTests import tempdir, make_Universe class TestGROReader(TestCase, RefAdK): diff --git a/testsuite/MDAnalysisTests/coordinates/test_lammps.py b/testsuite/MDAnalysisTests/coordinates/test_lammps.py index e543c15d584..07c1f2ccff3 100644 --- a/testsuite/MDAnalysisTests/coordinates/test_lammps.py +++ b/testsuite/MDAnalysisTests/coordinates/test_lammps.py @@ -7,14 +7,13 @@ from numpy.testing import (assert_equal, assert_almost_equal, assert_raises, assert_, assert_array_almost_equal) -from MDAnalysisTests import tempdir +from MDAnalysisTests import tempdir, make_Universe from MDAnalysisTests.coordinates.reference import ( RefLAMMPSData, RefLAMMPSDataMini, RefLAMMPSDataDCD, ) from MDAnalysisTests.datafiles import ( LAMMPScnt, LAMMPShyd, LAMMPSdata, LAMMPSdata_mini ) -from MDAnalysisTests.core.groupbase import make_Universe def test_datareader_ValueError(): diff --git a/testsuite/MDAnalysisTests/coordinates/test_mol2.py b/testsuite/MDAnalysisTests/coordinates/test_mol2.py index 0406653f0b8..8d825d61d20 100644 --- a/testsuite/MDAnalysisTests/coordinates/test_mol2.py +++ b/testsuite/MDAnalysisTests/coordinates/test_mol2.py @@ -34,8 +34,7 @@ ) from MDAnalysis import Universe import MDAnalysis as mda -from MDAnalysisTests import tempdir -from MDAnalysisTests.core.groupbase import make_Universe +from MDAnalysisTests import tempdir, make_Universe class TestMol2(TestCase): diff --git a/testsuite/MDAnalysisTests/coordinates/test_netcdf.py b/testsuite/MDAnalysisTests/coordinates/test_netcdf.py index 75323453f4f..a8b9fd42aee 100644 --- a/testsuite/MDAnalysisTests/coordinates/test_netcdf.py +++ b/testsuite/MDAnalysisTests/coordinates/test_netcdf.py @@ -9,15 +9,12 @@ assert_array_equal, assert_almost_equal, assert_raises, dec) from unittest import TestCase -from MDAnalysisTests import module_not_found from MDAnalysisTests.datafiles import (PRMncdf, NCDF, PFncdf_Top, PFncdf_Trj, GRO, TRR, XYZ_mini) from MDAnalysisTests.coordinates.test_trj import _TRJReaderTest from MDAnalysisTests.coordinates.reference import (RefVGV, RefTZ2) -from MDAnalysisTests import tempdir -from MDAnalysisTests.util import block_import -from MDAnalysisTests.core.groupbase import make_Universe +from MDAnalysisTests import module_not_found, tempdir, block_import, make_Universe diff --git a/testsuite/MDAnalysisTests/coordinates/test_pdb.py b/testsuite/MDAnalysisTests/coordinates/test_pdb.py index df9cc99b7d3..7ddfcd50e12 100644 --- a/testsuite/MDAnalysisTests/coordinates/test_pdb.py +++ b/testsuite/MDAnalysisTests/coordinates/test_pdb.py @@ -20,8 +20,8 @@ PDB_cm, PDB_cm_gz, PDB_cm_bz2, PDB_mc, PDB_mc_gz, PDB_mc_bz2) from MDAnalysisTests.plugins.knownfailure import knownfailure -from MDAnalysisTests import parser_not_found, tempdir -from MDAnalysisTests.core.groupbase import make_Universe +from MDAnalysisTests import parser_not_found, tempdir, make_Universe + class TestPDBReader(_SingleFrameReader): def setUp(self): diff --git a/testsuite/MDAnalysisTests/coordinates/test_pdbqt.py b/testsuite/MDAnalysisTests/coordinates/test_pdbqt.py index d2ce29f743a..3e7c371f404 100644 --- a/testsuite/MDAnalysisTests/coordinates/test_pdbqt.py +++ b/testsuite/MDAnalysisTests/coordinates/test_pdbqt.py @@ -21,7 +21,7 @@ # import MDAnalysis as mda -from MDAnalysis.tests.datafiles import PDBQT_input, PDBQT_querypdb +from MDAnalysisTests.datafiles import PDBQT_input, PDBQT_querypdb from MDAnalysis.lib.NeighborSearch import AtomNeighborSearch from numpy.testing import ( @@ -32,8 +32,7 @@ ) import os -from MDAnalysisTests import tempdir -from MDAnalysisTests.core.groupbase import make_Universe +from MDAnalysisTests import tempdir, make_Universe class TestPDBQT(object): diff --git a/testsuite/MDAnalysisTests/coordinates/test_pqr.py b/testsuite/MDAnalysisTests/coordinates/test_pqr.py index 2a5ee062c33..c6ec7b5eb59 100644 --- a/testsuite/MDAnalysisTests/coordinates/test_pqr.py +++ b/testsuite/MDAnalysisTests/coordinates/test_pqr.py @@ -8,11 +8,10 @@ assert_warns, ) -from MDAnalysisTests.coordinates.reference import (RefAdKSmall) +from MDAnalysisTests.coordinates.reference import RefAdKSmall from MDAnalysisTests.coordinates.base import _SingleFrameReader -from MDAnalysisTests.datafiles import (PQR) -from MDAnalysisTests import tempdir -from MDAnalysisTests.core.groupbase import make_Universe +from MDAnalysisTests.datafiles import PQR +from MDAnalysisTests import tempdir, make_Universe class TestPQRReader(_SingleFrameReader): diff --git a/testsuite/MDAnalysisTests/coordinates/test_xyz.py b/testsuite/MDAnalysisTests/coordinates/test_xyz.py index 80f8417d5a0..2a577ab3ad7 100644 --- a/testsuite/MDAnalysisTests/coordinates/test_xyz.py +++ b/testsuite/MDAnalysisTests/coordinates/test_xyz.py @@ -15,8 +15,8 @@ from MDAnalysisTests.datafiles import COORDINATES_XYZ, COORDINATES_XYZ_BZ2 from MDAnalysisTests.coordinates.base import (BaseReaderTest, BaseReference, BaseWriterTest) -from MDAnalysisTests.core.groupbase import make_Universe, FakeReader -from MDAnalysisTests import tempdir +from MDAnalysisTests import tempdir, make_Universe + class XYZReference(BaseReference): def __init__(self): @@ -115,8 +115,7 @@ def tearDown(self): del self.tmpdir def test_no_names(self): - u = make_Universe() - u.trajectory = FakeReader() + u = make_Universe(trajectory=True) w = XYZWriter(self.outfile) w.write(u.trajectory.ts) @@ -126,8 +125,7 @@ def test_no_names(self): assert_(all(u2.atoms.names == 'X')) def test_single_name(self): - u = make_Universe() - u.trajectory = FakeReader() + u = make_Universe(trajectory=True) w = XYZWriter(self.outfile, atoms='ABC') w.write(u.trajectory.ts) @@ -137,8 +135,7 @@ def test_single_name(self): assert_(all(u2.atoms.names == 'ABC')) def test_list_names(self): - u = make_Universe() - u.trajectory = FakeReader() + u = make_Universe(trajectory=True) names = ['A', 'B', 'C', 'D', 'E'] * 25 diff --git a/testsuite/MDAnalysisTests/core/test_atom.py b/testsuite/MDAnalysisTests/core/test_atom.py new file mode 100644 index 00000000000..964271c7575 --- /dev/null +++ b/testsuite/MDAnalysisTests/core/test_atom.py @@ -0,0 +1,150 @@ +# -*- Mode: python; tab-width: 4; indent-tabs-mode:nil; coding:utf-8 -*- +# vim: tabstop=4 expandtab shiftwidth=4 softtabstop=4 fileencoding=utf-8 +# +# MDAnalysis --- http://www.mdanalysis.org +# Copyright (c) 2006-2016 The MDAnalysis Development Team and contributors +# (see the file AUTHORS for the full list of names) +# +# Released under the GNU Public Licence, v2 or any higher version +# +# Please cite your use of MDAnalysis in published work: +# +# R. J. Gowers, M. Linke, J. Barnoud, T. J. E. Reddy, M. N. Melo, S. L. Seyler, +# D. L. Dotson, J. Domanski, S. Buchoux, I. M. Kenney, and O. Beckstein. +# MDAnalysis: A Python package for the rapid analysis of molecular dynamics +# simulations. In S. Benthall and S. Rostrup editors, Proceedings of the 15th +# Python in Science Conference, pages 102-109, Austin, TX, 2016. SciPy. +# +# N. Michaud-Agrawal, E. J. Denning, T. B. Woolf, and O. Beckstein. +# MDAnalysis: A Toolkit for the Analysis of Molecular Dynamics Simulations. +# J. Comput. Chem. 32 (2011), 2319--2327, doi:10.1002/jcc.21787 +# + +import numpy as np +from numpy.testing import ( + dec, + raises, + assert_almost_equal, + assert_equal, + assert_raises, +) + +import MDAnalysis as mda +from MDAnalysis import NoDataError + +from MDAnalysisTests.datafiles import ( + PSF, DCD, + XYZ_mini, +) +from MDAnalysisTests import parser_not_found + + +class TestAtom(object): + # Legacy tests from before 363 + """Tests of Atom.""" + + @dec.skipif(parser_not_found('DCD'), + 'DCD parser not available. Are you using python 3?') + def setUp(self): + """Set up the standard AdK system in implicit solvent.""" + self.universe = mda.Universe(PSF, DCD) + self.atom = self.universe.atoms[1000] # Leu67:CG + self.known_pos = np.array([3.94543672, -12.4060812, -7.26820087], + dtype=np.float32) + + def tearDown(self): + del self.universe + del self.atom + del self.known_pos + + def test_attributes_names(self): + a = self.atom + assert_equal(a.name, 'CG') + assert_equal(a.resname, 'LEU') + + def test_setting_attribute_name(self): + self.atom.name = 'AA' + assert_equal(self.atom.name, 'AA') + + def test_setting_attribute_type(self): + self.atom.type = 'Z' + assert_equal(self.atom.type, 'Z') + + def test_setting_attribute_mass(self): + self.atom.mass = 13 + assert_equal(self.atom.mass, 13) + + def test_setting_attributes_charge(self): + self.atom.charge = 6 + assert_equal(self.atom.charge, 6) + + def test_attributes_positions(self): + a = self.atom + # new position property (mutable) + assert_almost_equal(a.position, self.known_pos) + pos = a.position + 3.14 + a.position = pos + assert_almost_equal(a.position, pos) + + def test_atom_selection(self): + asel = self.universe.select_atoms('atom 4AKE 67 CG').atoms[0] + assert_equal(self.atom, asel) + + def test_hierarchy(self): + u = self.universe + a = self.atom + assert_equal(a.segment, u.s4AKE) + assert_equal(a.residue, u.residues[66]) + + def test_bad_add(self): + def bad_add(): + return self.atom + 1 + + assert_raises(TypeError, bad_add) + + def test_add_AG(self): + ag = self.universe.atoms[:2] + + ag2 = self.atom + ag + + for at in [self.atom, ag[0], ag[1]]: + assert_equal(at in ag2, True) + + def test_no_velo(self): + def lookup_velo(): + return self.atom.velocity + + assert_raises(NoDataError, lookup_velo) + + def test_bonded_atoms(self): + at = self.universe.atoms[0] + ref = [b.partner(at) for b in at.bonds] + assert_equal(ref, list(at.bonded_atoms)) + + @raises(AttributeError) + def test_undefined_occupancy(self): + self.universe.atoms[0].occupancy + + +class TestAtomNoForceNoVel(object): + def setUp(self): + self.u = mda.Universe(XYZ_mini) + self.a = self.u.atoms[0] + + def tearDown(self): + del self.u + + def test_velocity_fail(self): + assert_raises(NoDataError, getattr, self.a, 'velocity') + + def test_force_fail(self): + assert_raises(NoDataError, getattr, self.a, 'force') + + def test_velocity_set_fail(self): + assert_raises(NoDataError, setattr, self.a, 'velocity', + [1.0, 1.0, 1.0]) + + def test_force_set_fail(self): + assert_raises(NoDataError, setattr, self.a, 'force', [1.0, 1.0, 1.0]) + + diff --git a/testsuite/MDAnalysisTests/core/test_atomgroup.py b/testsuite/MDAnalysisTests/core/test_atomgroup.py index 8cace8dbabc..499e69f28bd 100644 --- a/testsuite/MDAnalysisTests/core/test_atomgroup.py +++ b/testsuite/MDAnalysisTests/core/test_atomgroup.py @@ -21,18 +21,23 @@ # from glob import glob +import itertools +import os from os import path import numpy as np import warnings from numpy.testing import ( + dec, assert_, + assert_array_equal, + assert_almost_equal, assert_raises, assert_equal, assert_array_almost_equal, raises ) - +from nose.plugins.attrib import attr import MDAnalysis as mda from MDAnalysis.lib import transformations @@ -43,9 +48,12 @@ ImproperDihedral, ) -from MDAnalysisTests.datafiles import (PSF, DCD) -from MDAnalysisTests.core.groupbase import make_Universe -from MDAnalysisTests import tempdir +from MDAnalysisTests.datafiles import ( + PSF, DCD, + TRZ_psf, TRZ, + two_water_gro, +) +from MDAnalysisTests import parser_not_found, tempdir, make_Universe # I want to catch all warnings in the tests. If this is not set at the start it # could cause test that check for warnings to fail. @@ -190,6 +198,116 @@ def test_bogus_kwarg_pdb(self): with assert_raises(TypeError): self.u.atoms.write('dummy.pdb', bogus="what?") +class _WriteAtoms(object): + """Set up the standard AdK system in implicit solvent.""" + ext = None # override to test various output writers + precision = 3 + + @dec.skipif(parser_not_found('DCD'), + 'DCD parser not available. Are you using python 3?') + def setUp(self): + self.universe = mda.Universe(PSF, DCD) + suffix = '.' + self.ext + self.tempdir = tempdir.TempDir() + self.outfile = os.path.join(self.tempdir.name, 'writeatoms' + suffix) + + def tearDown(self): + del self.universe + del self.tempdir + + def universe_from_tmp(self): + return mda.Universe(self.outfile, convert_units=True) + + def test_write_atoms(self): + self.universe.atoms.write(self.outfile) + u2 = self.universe_from_tmp() + assert_array_almost_equal( + self.universe.atoms.positions, u2.atoms.positions, + self.precision, + err_msg=("atom coordinate mismatch between original and {0!s} file" + "".format(self.ext))) + + def test_write_empty_atomgroup(self): + sel = self.universe.select_atoms('name doesntexist') + assert_raises(IndexError, sel.write, self.outfile) + + def test_write_selection(self): + CA = self.universe.select_atoms('name CA') + CA.write(self.outfile) + u2 = self.universe_from_tmp() + # check EVERYTHING, otherwise we might get false positives! + CA2 = u2.atoms + assert_equal(len(u2.atoms), len(CA.atoms), + "written CA selection does not match original selection") + assert_almost_equal( + CA2.positions, CA.positions, self.precision, + err_msg="CA coordinates do not agree with original") + + def test_write_Residue(self): + G = self.universe.s4AKE.ARG[-2].atoms # 2nd but last Arg + G.write(self.outfile) + u2 = self.universe_from_tmp() + # check EVERYTHING, otherwise we might get false positives! + G2 = u2.atoms + assert_equal( + len(u2.atoms), len(G.atoms), + "written R206 Residue does not match original ResidueGroup") + assert_almost_equal( + G2.positions, G.positions, self.precision, + err_msg="Residue R206 coordinates do not agree with original") + + def test_write_ResidueGroup(self): + G = self.universe.s4AKE.LEU.atoms + G.write(self.outfile) + u2 = self.universe_from_tmp() + G2 = u2.atoms + assert_equal( + len(u2.atoms), len(G.atoms), + "written LEU ResidueGroup does not match original ResidueGroup") + assert_almost_equal( + G2.positions, G.positions, self.precision, + err_msg="ResidueGroup LEU coordinates do not agree with original") + + def test_write_Segment(self): + G = self.universe.s4AKE.atoms + G.write(self.outfile) + u2 = self.universe_from_tmp() + G2 = u2.atoms + assert_equal(len(u2.atoms), len(G.atoms), + "written s4AKE segment does not match original segment") + assert_almost_equal( + G2.positions, G.positions, self.precision, + err_msg="segment s4AKE coordinates do not agree with original") + + def test_write_Universe(self): + U = self.universe + with mda.Writer(self.outfile) as W: + W.write(U) + u2 = self.universe_from_tmp() + assert_equal( + len(u2.atoms), len(U.atoms), + "written 4AKE universe does not match original universe in size") + assert_almost_equal( + u2.atoms.positions, U.atoms.positions, self.precision, + err_msg=("written universe 4AKE coordinates do not" + " agree with original")) + + +class TestWritePDB(_WriteAtoms): + ext = "pdb" + precision = 3 + + +class TestWriteGRO(_WriteAtoms): + ext = "gro" + precision = 2 + + def test_flag_convert_length(self): + assert_equal(mda.core.flags['convert_lengths'], True, + "The flag convert_lengths SHOULD be True by default! " + "(If it is not then this might indicate a race condition" + " in the testing suite.)") + class TestAtomGroupTransformations(object): def setUp(self): @@ -393,3 +511,779 @@ def test_split_VE(self): ag = self.universe.atoms[:40] assert_raises(ValueError, ag.split, 'something') + + +class TestWrap(object): + @dec.skipif(parser_not_found('TRZ'), + 'TRZ parser not available. Are you using python 3?') + def setUp(self): + self.u = mda.Universe(TRZ_psf, TRZ) + self.ag = self.u.atoms[:100] + + def tearDown(self): + del self.u + del self.ag + + def test_wrap_comp_fail(self): + assert_raises(ValueError, self.ag.wrap, compound='strawberries') + + def test_wrap_cent_fail(self): + assert_raises(ValueError, self.ag.wrap, compound='residues', center='avacado') + + def test_wrap_box_fail(self): + assert_raises(ValueError, self.ag.wrap, box=np.array([0, 1])) + + def _in_box(self, coords): + """Check that a set of coordinates are 0.0 <= r <= box""" + box = self.u.dimensions[:3] + + return (coords >= 0.0).all() and (coords <= box).all() + + def test_wrap_atoms(self): + ag = self.u.atoms[100:200] + ag.wrap(compound='atoms') + + assert_equal(self._in_box(ag.positions), True) + + def test_wrap_group(self): + ag = self.u.atoms[:100] + ag.wrap(compound='group') + + cen = ag.center_of_mass() + + assert_equal(self._in_box(cen), True) + + def test_wrap_residues(self): + ag = self.u.atoms[300:400] + ag.wrap(compound='residues') + + cen = np.vstack([r.atoms.center_of_mass() for r in ag.residues]) + + assert_equal(self._in_box(cen), True) + + def test_wrap_segments(self): + ag = self.u.atoms[1000:1200] + ag.wrap(compound='segments') + + cen = np.vstack([s.atoms.center_of_mass() for s in ag.segments]) + + assert_equal(self._in_box(cen), True) + + def test_wrap_fragments(self): + ag = self.u.atoms[:250] + ag.wrap(compound='fragments') + + cen = np.vstack([f.center_of_mass() for f in ag.fragments]) + + assert_equal(self._in_box(cen), True) + + +class TestAtomGroupProperties(object): + """Test working with the properties of Atoms via AtomGroups + + Check that: + - getting properties from AG matches the Atom values + - setting properties from AG changes the Atom + - setting the property on Atom changes AG + """ + @staticmethod + def get_new(att_type): + """Return enough values to change the small g""" + if att_type == 'string': + return ['A', 'B', 'C', 'D', 'E', 'F'] + elif att_type == 'float': + return np.array([0.001, 0.002, 0.003, 0.005, 0.012, 0.025], dtype=np.float32) + elif att_type == 'int': + return [4, 6, 8, 1, 5, 4] + + def _check_ag_matches_atom(self, att, atts, ag): + """Checking Atomgroup property matches Atoms""" + # Check that accessing via AtomGroup is identical to doing + # a list comprehension over AG + ref = [getattr(atom, att) for atom in ag] + + assert_equal(ref, getattr(ag, atts), + err_msg="AtomGroup doesn't match Atoms for property: {0}".format(att)) + + def _change_atom_check_ag(self, att, atts, vals, ag): + """Changing Atom, checking AtomGroup matches this""" + # Set attributes via Atoms + for atom, val in zip(ag, vals): + setattr(atom, att, val) + # Check that AtomGroup returns new values + other = getattr(ag, atts) + + assert_equal(vals, other, + err_msg="Change to Atoms not reflected in AtomGroup for property: {0}".format(att)) + + @dec.skipif(parser_not_found('DCD'), + 'DCD parser not available. Are you using python 3?') + def test_attributes(self): + u = make_Universe(('names', 'resids', 'segids', 'types', 'altLocs', + 'charges', 'masses', 'radii', 'bfactors', + 'occupancies')) + u.atoms.occupancies = 1.0 + master = u.atoms + idx = [0, 1, 4, 7, 11, 14] + ag = master[idx] + + for att, atts, att_type in ( + ('name', 'names', 'string'), + ('type', 'types', 'string'), + ('altLoc', 'altLocs', 'string'), + ('charge', 'charges', 'float'), + ('mass', 'masses', 'float'), + ('radius', 'radii', 'float'), + ('bfactor', 'bfactors', 'float'), + ('occupancy', 'occupancies', 'float') + ): + vals = self.get_new(att_type) + yield self._check_ag_matches_atom, att, atts, ag + yield self._change_atom_check_ag, att, atts, vals, ag + + +class TestOrphans(object): + """Test moving Universes out of scope and having A/AG persist + + Atoms and AtomGroups from other scopes should work, namely: + - should have access to Universe + - should be able to use the Reader (coordinates) + """ + def test_atom(self): + u = mda.Universe(two_water_gro) + + def getter(): + u2 = mda.Universe(two_water_gro) + return u2.atoms[1] + + atom = getter() + + assert_(atom is not u.atoms[1]) + assert_(len(atom.universe.atoms) == len(u.atoms)) + assert_array_almost_equal(atom.position, u.atoms[1].position) + + def test_atomgroup(self): + u = mda.Universe(two_water_gro) + + def getter(): + u2 = mda.Universe(two_water_gro) + return u2.atoms[:4] + + ag = getter() + ag2 = u.atoms[:4] + assert_(ag is not ag2) + assert_(len(ag.universe.atoms) == len(u.atoms)) + assert_array_almost_equal(ag.positions, ag2.positions) + + +class TestCrossUniverse(object): + """Test behaviour when we mix Universes""" + def _check_badadd(self, a, b): + def add(x, y): + return x + y + assert_raises(ValueError, add, a, b) + + def test_add_mixed_universes(self): + # Issue #532 + # Checks that adding objects from different universes + # doesn't proceed quietly. + u1 = mda.Universe(two_water_gro) + u2 = mda.Universe(two_water_gro) + + A = [u1.atoms[:2], u1.atoms[3]] + B = [u2.atoms[:3], u2.atoms[0]] + + # Checks Atom to Atom, Atom to AG, AG to Atom and AG to AG + for x, y in itertools.product(A, B): + yield self._check_badadd, x, y + + def test_adding_empty_ags(self): + # Check that empty AtomGroups don't trip up on the Universe check + u = mda.Universe(two_water_gro) + + assert_(len(u.atoms[[]] + u.atoms[:3]) == 3) + assert_(len(u.atoms[:3] + u.atoms[[]]) == 3) + + +class TestDihedralSelections(object): + @dec.skipif(parser_not_found('DCD'), + 'DCD parser not available. Are you using python 3?') + def setUp(self): + self.universe = mda.Universe(PSF, DCD) + self.dih_prec = 2 + + def tearDown(self): + del self.universe + del self.dih_prec + + def test_phi_selection(self): + phisel = self.universe.s4AKE.r10.phi_selection() + assert_equal(phisel.names, ['C', 'N', 'CA', 'C']) + assert_equal(phisel.residues.resids, [9, 10]) + assert_equal(phisel.residues.resnames, ['PRO', 'GLY']) + + def test_psi_selection(self): + psisel = self.universe.s4AKE.r10.psi_selection() + assert_equal(psisel.names, ['N', 'CA', 'C', 'N']) + assert_equal(psisel.residues.resids, [10, 11]) + assert_equal(psisel.residues.resnames, ['GLY', 'ALA']) + + def test_omega_selection(self): + osel = self.universe.s4AKE.r8.omega_selection() + assert_equal(osel.names, ['CA', 'C', 'N', 'CA']) + assert_equal(osel.residues.resids, [8, 9]) + assert_equal(osel.residues.resnames, ['ALA', 'PRO']) + + def test_chi1_selection(self): + sel = self.universe.s4AKE.r13.chi1_selection() # LYS + assert_equal(sel.names, ['N', 'CA', 'CB', 'CG']) + assert_equal(sel.residues.resids, [13]) + assert_equal(sel.residues.resnames, ['LYS']) + + def test_phi_sel_fail(self): + sel = self.universe.residues[0].phi_selection() + assert_equal(sel, None) + + def test_psi_sel_fail(self): + sel = self.universe.residues[-1].psi_selection() + assert_equal(sel, None) + + def test_omega_sel_fail(self): + sel = self.universe.residues[-1].omega_selection() + assert_equal(sel, None) + + def test_ch1_sel_fail(self): + sel = self.universe.s4AKE.r8.chi1_selection() + assert_equal(sel, None) # ALA + + def test_dihedral_phi(self): + u = self.universe + phisel = u.s4AKE.r10.phi_selection() + assert_almost_equal(phisel.dihedral.value(), -168.57384, self.dih_prec) + + def test_dihedral_psi(self): + u = self.universe + psisel = u.s4AKE.r10.psi_selection() + assert_almost_equal(psisel.dihedral.value(), -30.064838, self.dih_prec) + + def test_dihedral_omega(self): + u = self.universe + osel = u.s4AKE.r8.omega_selection() + assert_almost_equal(osel.dihedral.value(), -179.93439, self.dih_prec) + + def test_dihedral_chi1(self): + u = self.universe + sel = u.s4AKE.r13.chi1_selection() # LYS + assert_almost_equal(sel.dihedral.value(), -58.428127, self.dih_prec) + + +class TestPBCFlag(object): + @dec.skipif(parser_not_found('TRZ'), + 'TRZ parser not available. Are you using python 3?') + def setUp(self): + self.prec = 3 + self.universe = mda.Universe(TRZ_psf, TRZ) + self.ref_noPBC = { + 'COG': np.array([4.23789883, 0.62429816, 2.43123484], dtype=np.float32), + 'COM': np.array([4.1673783, 0.70507009, 2.21175832]), + 'ROG': 119.30368949900134, 'Shape': 0.6690026954813445, + 'Asph': 0.5305456387833748, + 'MOI': np.array([ + [152117.06620921, 55149.54042136, -26630.46034023], + [55149.54042136, 72869.64061494, 21998.1778074], + [-26630.46034023, 21998.1778074, 162388.70002471]]), + 'BBox': np.array([[-75.74159241, -144.86634827, -94.47974396], [95.83090973, 115.11561584, 88.09812927]], + dtype=np.float32), + 'BSph': (173.40482, np.array([4.23789883, 0.62429816, 2.43123484], dtype=np.float32)), + 'PAxes': np.array([ + [0.46294889, -0.85135849, 0.24671249], + [0.40611024, 0.45112859, 0.7947059], + [-0.78787867, -0.26771575, 0.55459488]]) + } + self.ref_PBC = { + 'COG': np.array([26.82960892, 31.5592289, 30.98238945], dtype=np.float32), + 'COM': np.array([26.67781143, 31.2104336, 31.19796289]), + 'ROG': 27.713008969174918, 'Shape': 0.0017390512580463542, + 'Asph': 0.020601215358731016, + 'MOI': np.array([ + [7333.79167791, -211.8997285, -721.50785456], + [-211.8997285, 7059.07470427, -91.32156884], + [-721.50785456, -91.32156884, 6509.31735029]]), + 'BBox': np.array( + [[1.45964116e-01, 1.85623169e-02, 4.31785583e-02], [5.53314018e+01, 5.54227829e+01, 5.54158211e+01]], + dtype=np.float32), + 'BSph': (47.923367, np.array([26.82960892, 31.5592289, 30.98238945], dtype=np.float32)), + 'PAxes': np.array([ + [-0.50622389, -0.18364489, -0.84262206], + [-0.07520116, -0.96394227, 0.25526473], + [-0.85911708, 0.19258726, 0.4741603]]) + } + self.ag = self.universe.residues[0:3] + + def tearDown(self): + mda.core.flags['use_pbc'] = False + del self.universe + del self.ref_noPBC + del self.ref_PBC + del self.ag + + def test_flag(self): + # Test default setting of flag + assert_equal(mda.core.flags['use_pbc'], False) + + def test_default(self): + # Test regular behaviour + assert_almost_equal(self.ag.center_of_geometry(), self.ref_noPBC['COG'], self.prec) + assert_almost_equal(self.ag.center_of_mass(), self.ref_noPBC['COM'], self.prec) + assert_almost_equal(self.ag.radius_of_gyration(), self.ref_noPBC['ROG'], self.prec) + assert_almost_equal(self.ag.shape_parameter(), self.ref_noPBC['Shape'], self.prec) + assert_almost_equal(self.ag.asphericity(), self.ref_noPBC['Asph'], self.prec) + assert_almost_equal(self.ag.moment_of_inertia(), self.ref_noPBC['MOI'], self.prec) + assert_almost_equal(self.ag.bbox(), self.ref_noPBC['BBox'], self.prec) + assert_almost_equal(self.ag.bsphere()[0], self.ref_noPBC['BSph'][0], self.prec) + assert_almost_equal(self.ag.bsphere()[1], self.ref_noPBC['BSph'][1], self.prec) + assert_almost_equal(self.ag.principal_axes(), self.ref_noPBC['PAxes'], self.prec) + + def test_pbcflag(self): + # Test using ag method flag + assert_almost_equal(self.ag.center_of_geometry(pbc=True), self.ref_PBC['COG'], self.prec) + assert_almost_equal(self.ag.center_of_mass(pbc=True), self.ref_PBC['COM'], self.prec) + assert_almost_equal(self.ag.radius_of_gyration(pbc=True), self.ref_PBC['ROG'], self.prec) + assert_almost_equal(self.ag.shape_parameter(pbc=True), self.ref_PBC['Shape'], self.prec) + assert_almost_equal(self.ag.asphericity(pbc=True), self.ref_PBC['Asph'], self.prec) + assert_almost_equal(self.ag.moment_of_inertia(pbc=True), self.ref_PBC['MOI'], self.prec) + assert_almost_equal(self.ag.bbox(pbc=True), self.ref_PBC['BBox'], self.prec) + assert_almost_equal(self.ag.bsphere(pbc=True)[0], self.ref_PBC['BSph'][0], self.prec) + assert_almost_equal(self.ag.bsphere(pbc=True)[1], self.ref_PBC['BSph'][1], self.prec) + assert_almost_equal(self.ag.principal_axes(pbc=True), self.ref_PBC['PAxes'], self.prec) + + def test_usepbc_flag(self): + # Test using the core.flags flag + mda.core.flags['use_pbc'] = True + assert_almost_equal(self.ag.center_of_geometry(), self.ref_PBC['COG'], self.prec) + assert_almost_equal(self.ag.center_of_mass(), self.ref_PBC['COM'], self.prec) + assert_almost_equal(self.ag.radius_of_gyration(), self.ref_PBC['ROG'], self.prec) + assert_almost_equal(self.ag.shape_parameter(), self.ref_PBC['Shape'], self.prec) + assert_almost_equal(self.ag.asphericity(), self.ref_PBC['Asph'], self.prec) + assert_almost_equal(self.ag.moment_of_inertia(), self.ref_PBC['MOI'], self.prec) + assert_almost_equal(self.ag.bbox(), self.ref_PBC['BBox'], self.prec) + assert_almost_equal(self.ag.bsphere()[0], self.ref_PBC['BSph'][0], self.prec) + assert_almost_equal(self.ag.bsphere()[1], self.ref_PBC['BSph'][1], self.prec) + assert_almost_equal(self.ag.principal_axes(), self.ref_PBC['PAxes'], self.prec) + mda.core.flags['use_pbc'] = False + + def test_override_flag(self): + # Test using the core.flags flag, then overriding + mda.core.flags['use_pbc'] = True + assert_almost_equal(self.ag.center_of_geometry(pbc=False), self.ref_noPBC['COG'], self.prec) + assert_almost_equal(self.ag.center_of_mass(pbc=False), self.ref_noPBC['COM'], self.prec) + assert_almost_equal(self.ag.radius_of_gyration(pbc=False), self.ref_noPBC['ROG'], self.prec) + assert_almost_equal(self.ag.shape_parameter(pbc=False), self.ref_noPBC['Shape'], self.prec) + assert_almost_equal(self.ag.asphericity(pbc=False), self.ref_noPBC['Asph'], self.prec) + assert_almost_equal(self.ag.moment_of_inertia(pbc=False), self.ref_noPBC['MOI'], self.prec) + assert_almost_equal(self.ag.bbox(pbc=False), self.ref_noPBC['BBox'], self.prec) + assert_almost_equal(self.ag.bsphere(pbc=False)[0], self.ref_noPBC['BSph'][0], self.prec) + assert_almost_equal(self.ag.bsphere(pbc=False)[1], self.ref_noPBC['BSph'][1], self.prec) + assert_almost_equal(self.ag.principal_axes(pbc=False), self.ref_noPBC['PAxes'], self.prec) + mda.core.flags['use_pbc'] = False + + +@dec.skipif(parser_not_found('DCD'), + 'DCD parser not available. Are you using python 3?') +def test_instantselection_termini(): + """Test that instant selections work, even for residues that are also termini (Issue 70)""" + universe = mda.Universe(PSF, DCD) + assert_equal(universe.residues[20].CA.name, 'CA', "CA of MET21 is not selected correctly") + del universe + + +class TestAtomGroup(object): + """Tests of AtomGroup; selections are tested separately. + + These are from before the big topology rework (aka #363) but are still valid. + There is likely lots of duplication between here and other tests. + """ + @dec.skipif(parser_not_found('DCD'), + 'DCD parser not available. Are you using python 3?') + def setUp(self): + """Set up the standard AdK system in implicit solvent.""" + self.universe = mda.Universe(PSF, DCD) + self.ag = self.universe.atoms # prototypical AtomGroup + self.dih_prec = 2 + + def test_getitem_int(self): + assert_equal(self.universe.atoms[0].ix, self.universe.atoms.ix[0]) + + def test_getitem_slice(self): + assert_array_equal(self.universe.atoms[0:4].ix, + self.universe.atoms.ix[:4]) + + def test_getitem_slice2(self): + assert_equal(self.universe.atoms[0:8:2].ix, + self.universe.atoms.ix[0:8:2]) + + def test_getitem_str(self): + ag1 = self.universe.atoms['HT1'] + # select_atoms always returns an AtomGroup even if single result + ag2 = self.universe.select_atoms('name HT1')[0] + assert_equal(ag1, ag2) + + def test_getitem_IE(self): + d = {'A': 1} + assert_raises(IndexError, self.universe.atoms.__getitem__, d) + + def test_bad_make(self): + assert_raises(TypeError, mda.core.groups.AtomGroup, ['these', 'are', 'not', 'atoms']) + + def test_n_atoms(self): + assert_equal(self.ag.n_atoms, 3341) + + def test_n_residues(self): + assert_equal(self.ag.n_residues, 214) + + def test_n_segments(self): + assert_equal(self.ag.n_segments, 1) + + def test_resids_dim(self): + assert_equal(len(self.ag.resids), len(self.ag)) + + def test_resnums_dim(self): + assert_equal(len(self.ag.resnums), len(self.ag)) + + def test_segids_dim(self): + assert_equal(len(self.ag.segids), len(self.ag)) + + def test_len(self): + """testing that len(atomgroup) == atomgroup.n_atoms""" + assert_equal(len(self.ag), self.ag.n_atoms, "len and n_atoms disagree") + + def test_center_of_geometry(self): + assert_array_almost_equal(self.ag.center_of_geometry(), + np.array([-0.04223963, 0.0141824, -0.03505163], dtype=np.float32)) + + def test_center_of_mass(self): + assert_array_almost_equal(self.ag.center_of_mass(), + np.array([-0.01094035, 0.05727601, -0.12885778])) + + def test_coordinates(self): + assert_array_almost_equal( + self.ag.positions[1000:2000:200], + np.array([[3.94543672, -12.4060812, -7.26820087], + [13.21632767, 5.879035, -14.67914867], + [12.07735443, -9.00604534, 4.09301519], + [11.35541916, 7.0690732, -0.32511973], + [-13.26763439, 4.90658951, 10.6880455]], + dtype=np.float32)) + + def test_principal_axes(self): + assert_array_almost_equal( + self.ag.principal_axes(), + np.array([[-9.99925632e-01, 1.21546132e-02, 9.98264877e-04], + [1.20986911e-02, 9.98951474e-01, -4.41539838e-02], + [1.53389276e-03, 4.41386224e-02, 9.99024239e-01]])) + + def test_total_charge(self): + assert_almost_equal(self.ag.total_charge(), -4.0, decimal=4) + + def test_total_mass(self): + assert_almost_equal(self.ag.total_mass(), 23582.043) + + def test_indices_ndarray(self): + assert_equal(isinstance(self.ag.indices, np.ndarray), True) + + def test_indices(self): + assert_array_equal(self.ag.indices[:5], np.array([0, 1, 2, 3, 4])) + + def test_resids_ndarray(self): + assert_equal(isinstance(self.ag.resids, np.ndarray), True) + + def test_resids(self): + assert_array_equal(self.ag.residues.resids, np.arange(1, 215)) + + def test_resnums_ndarray(self): + assert_equal(isinstance(self.ag.residues.resnums, np.ndarray), True) + + def test_resnums(self): + assert_array_equal(self.ag.residues.resnums, np.arange(1, 215)) + + def test_resnames_ndarray(self): + assert_equal(isinstance(self.ag.residues.resnames, np.ndarray), True) + + def test_resnames(self): + resnames = self.ag.residues.resnames + assert_array_equal(resnames[0:3], np.array(["MET", "ARG", "ILE"])) + + def test_names_ndarray(self): + assert_equal(isinstance(self.ag.names, np.ndarray), True) + + def test_names(self): + names = self.ag.names + assert_array_equal(names[0:3], np.array(["N", "HT1", "HT2"])) + + def test_segids_ndarray(self): + assert_equal(isinstance(self.ag.segids, np.ndarray), True) + + def test_segids(self): + segids = self.ag.segids + assert_array_equal(segids[0], np.array(["4AKE"])) + + def test_masses_ndarray(self): + assert_equal(isinstance(self.ag.masses, np.ndarray), True) + + def test_masses(self): + masses = self.ag.masses + assert_array_equal(masses[0:3], np.array([14.007, 1.008, 1.008])) + + def test_charges_ndarray(self): + assert_equal(isinstance(self.ag.charges, np.ndarray), True) + + def test_charges(self): + assert_array_almost_equal(self.ag.charges[1000:2000:200], + np.array([-0.09, 0.09, -0.47, 0.51, 0.09])) + + def test_bad_add_AG(self): + def bad_add(): + return self.ag + [1, 2, 3] + assert_raises(TypeError, bad_add) + + def test_bool_false(self): + # Issue #304 + ag = self.universe.atoms[[]] + assert_equal(bool(ag), False) + + def test_bool_true(self): + # Issue #304 + ag = self.universe.atoms[:3] + assert_equal(bool(ag), True) + + def test_repr(self): + # Should make sure that the user facing info stays as expected + assert_equal(repr(self.ag), "") + + def test_set_resnum_single(self): + ag = self.universe.atoms[:3] + ag.residues.resnums = 5 + for at in ag: + assert_equal(at.resnum, 5) + assert_equal(all(ag.resnums == 5), True) + + def test_set_resname_single(self): + ag = self.universe.atoms[:3] + new = 'abc' + ag.residues.resnames = new + for at in ag: + assert_equal(at.resname, new) + assert_equal(all(ag.resnames == new), True) + + def test_packintobox_badshape(self): + ag = self.universe.atoms[:10] + box = np.zeros(9, dtype=np.float32).reshape(3, 3) + assert_raises(ValueError, ag.pack_into_box, box=box) + + def test_packintobox_noshape(self): + ag = self.universe.atoms[:10] + assert_raises(ValueError, ag.pack_into_box) + + def test_packintobox(self): + """test AtomGroup.pack_into_box(): Tests application of periodic boundary + conditions on coordinates + + Reference system doesn't have dimensions, so an arbitrary box is + imposed on the system + + """ + u = self.universe + u.trajectory.rewind() # just to make sure... + ag = u.atoms[1000:2000:200] + # Provide arbitrary box + ag.pack_into_box(box=np.array([5., 5., 5.], dtype=np.float32)) + assert_array_almost_equal( + ag.positions, + np.array([[3.94543672, 2.5939188, 2.73179913], + [3.21632767, 0.879035, 0.32085133], + [2.07735443, 0.99395466, 4.09301519], + [1.35541916, 2.0690732, 4.67488003], + [1.73236561, 4.90658951, 0.6880455]], dtype=np.float32)) + + def test_residues(self): + u = self.universe + assert_equal(u.residues[100].atoms.ix, + u.select_atoms('resname ILE and resid 101').atoms.ix, + "Direct selection from residue group does not match " + "expected I101.") + + def test_segments(self): + u = self.universe + assert_equal(len(u.segments.s4AKE.atoms), + len(u.select_atoms('segid 4AKE').atoms), + "Direct selection of segment 4AKE from segments failed.") + + def test_index_integer(self): + u = self.universe + a = u.atoms[100] + assert_(isinstance(a, mda.core.groups.Atom), "integer index did not return Atom") + + def test_index_slice(self): + u = self.universe + a = u.atoms[100:200:10] + assert_(isinstance(a, mda.core.groups.AtomGroup), + "slice index did not return AtomGroup") + + def test_index_slice_empty(self): + u = self.universe + assert_array_equal(u.atoms[0:0], [], + "making an empty AtomGroup failed") + + def test_index_advancedslice(self): + u = self.universe + aslice = [0, 10, 20, -1, 10] + ag = u.atoms[aslice] + assert_(isinstance(ag, mda.core.groups.AtomGroup), + "advanced slicing does not produce a AtomGroup") + assert_equal(ag[1], ag[-1], "advanced slicing does not preserve order") + + def test_boolean_indexing(self): + # index an array with a sequence of bools + # issue #282 + sel = np.array([True, False, True]) + ag = self.universe.atoms[10:13] + ag2 = ag[sel] + assert_equal(len(ag2), 2) + for at in [ag[0], ag[2]]: + assert_equal(at in ag2, True) + + def test_boolean_indexing_2(self): + # index an array with a sequence of bools + # issue #282 + sel = [True, False, True] + ag = self.universe.atoms[10:13] + ag2 = ag[sel] + assert_equal(len(ag2), 2) + for at in [ag[0], ag[2]]: + assert_equal(at in ag2, True) + + def test_bool_IE(self): + # indexing with empty list doesn't run foul of bool check + sel = [] + ag = self.universe.atoms[10:30] + ag2 = ag[sel] + assert_equal(len(ag2), 0) + + def test_dihedral_ValueError(self): + """test that AtomGroup.dihedral() raises ValueError if not exactly + 4 atoms given""" + nodih = self.universe.select_atoms("resid 3:10") + assert_raises(ValueError, getattr, nodih, 'dihedral') + nodih = self.universe.select_atoms("resid 3:5") + assert_raises(ValueError, getattr, nodih, 'dihedral') + + def test_improper(self): + u = self.universe + u.trajectory.rewind() # just to make sure... + peptbond = u.select_atoms("atom 4AKE 20 C", "atom 4AKE 21 CA", + "atom 4AKE 21 N", "atom 4AKE 21 HN") + assert_almost_equal(peptbond.improper.value(), 168.52952575683594, + self.dih_prec, + "Peptide bond improper dihedral for M21 " + "calculated wrongly.") + + def test_dihedral_equals_improper(self): + u = self.universe + u.trajectory.rewind() # just to make sure... + peptbond = u.select_atoms("atom 4AKE 20 C", "atom 4AKE 21 CA", + "atom 4AKE 21 N", "atom 4AKE 21 HN") + assert_equal(peptbond.improper.value(), peptbond.dihedral.value(), + "improper() and proper dihedral() give different results") + + def test_bond(self): + self.universe.trajectory.rewind() # just to make sure... + sel2 = self.universe.s4AKE.r98.atoms.select_atoms("name OE1", "name OE2") + assert_almost_equal(sel2.bond.value(), 2.1210737228393555, 3, + "distance of Glu98 OE1--OE2 wrong") + + def test_bond_pbc(self): + self.universe.trajectory.rewind() + sel2 = self.universe.s4AKE.r98.atoms.select_atoms("name OE1", "name OE2") + assert_almost_equal(sel2.bond.value(pbc=True), 2.1210737228393555, 3, + "distance of Glu98 OE1--OE2 wrong") + + def test_bond_ValueError(self): + ag = self.universe.atoms[:4] + assert_raises(ValueError, getattr, ag, 'bond') + + def test_angle(self): + self.universe.trajectory.rewind() # just to make sure... + sel3 = self.universe.s4AKE.r98.atoms.select_atoms("name OE1", "name CD", + "name OE2") + assert_almost_equal(sel3.angle.value(), 117.46187591552734, 3, + "angle of Glu98 OE1-CD-OE2 wrong") + + def test_angle_ValueError(self): + ag = self.universe.atoms[:2] + assert_raises(ValueError, getattr, ag, 'angle') + + def test_shape_parameter(self): + s = self.universe.s4AKE.atoms.shape_parameter() + assert_almost_equal(s, 0.00240753939086033, 6) + + def test_asphericity(self): + a = self.universe.s4AKE.atoms.asphericity() + assert_almost_equal(a, 0.020227504542775828, 6) + + def test_positions(self): + ag = self.universe.select_atoms("bynum 12:42") + pos = ag.positions + 3.14 + ag.positions = pos + # should work + assert_almost_equal(ag.positions, pos, + err_msg="failed to update atoms 12:42 position " + "to new position") + + def set_badarr(pos=pos): + # create wrong size array + badarr = np.random.random((pos.shape[0] - 1, pos.shape[1] - 1)) + ag.positions = badarr + assert_raises(ValueError, set_badarr) + + def test_set_names(self): + ag = self.universe.atoms[:2] + names = ['One', 'Two'] + ag.names = names + for a, b in zip(ag, names): + assert_equal(a.name, b) + + @attr("issue") + def test_nonexistent_instantselector_raises_AttributeError(self): + def access_nonexistent_instantselector(): + self.universe.atoms.NO_SUCH_ATOM + assert_raises(AttributeError, access_nonexistent_instantselector) + + def test_atom_order(self): + assert_equal(self.universe.atoms.indices, + sorted(self.universe.atoms.indices)) + + +class TestAtomGroupTimestep(object): + """Tests the AtomGroup.ts attribute (partial timestep)""" + + @dec.skipif(parser_not_found('TRZ'), + 'TRZ parser not available. Are you using python 3?') + def setUp(self): + self.universe = mda.Universe(TRZ_psf, TRZ) + self.prec = 6 + + def tearDown(self): + del self.universe + del self.prec + + def test_partial_timestep(self): + ag = self.universe.select_atoms('name Ca') + idx = ag.indices + + assert_equal(len(ag.ts._pos), len(ag)) + + for ts in self.universe.trajectory[0:20:5]: + assert_array_almost_equal(ts.positions[idx], ag.ts.positions, self.prec, + err_msg="Partial timestep coordinates wrong") + assert_array_almost_equal(ts.velocities[idx], ag.ts.velocities, self.prec, + err_msg="Partial timestep coordinates wrong") diff --git a/testsuite/MDAnalysisTests/core/test_atomselections.py b/testsuite/MDAnalysisTests/core/test_atomselections.py index c632f9ae60c..687a39048df 100644 --- a/testsuite/MDAnalysisTests/core/test_atomselections.py +++ b/testsuite/MDAnalysisTests/core/test_atomselections.py @@ -44,7 +44,6 @@ from MDAnalysis.core.selection import Parser from MDAnalysis import SelectionError -from MDAnalysisTests.core.groupbase import make_Universe from MDAnalysis.tests.datafiles import ( PSF, DCD, PRMpbc, TRJpbc_bz2, @@ -54,7 +53,7 @@ PDB_full, PDB_icodes, ) -from MDAnalysisTests import parser_not_found +from MDAnalysisTests import parser_not_found, make_Universe class TestSelectionsCHARMM(object): diff --git a/testsuite/MDAnalysisTests/core/test_fragments.py b/testsuite/MDAnalysisTests/core/test_fragments.py index afa27b53791..3efa2f75f72 100644 --- a/testsuite/MDAnalysisTests/core/test_fragments.py +++ b/testsuite/MDAnalysisTests/core/test_fragments.py @@ -28,11 +28,11 @@ assert_array_equal, ) -from MDAnalysisTests.core.groupbase import make_Universe from MDAnalysis.core.topologyattrs import Bonds from MDAnalysis.core import groups from MDAnalysis import NoDataError +from MDAnalysisTests import make_Universe # Also used in topology/test_guessers def make_starshape(): diff --git a/testsuite/MDAnalysisTests/core/test_group_traj_access.py b/testsuite/MDAnalysisTests/core/test_group_traj_access.py index c847e316ed2..94c0d54265b 100644 --- a/testsuite/MDAnalysisTests/core/test_group_traj_access.py +++ b/testsuite/MDAnalysisTests/core/test_group_traj_access.py @@ -31,8 +31,8 @@ ) from nose.plugins.attrib import attr -from MDAnalysisTests.core.groupbase import make_Universe -from MDAnalysis.tests.datafiles import ( +from MDAnalysisTests import make_Universe +from MDAnalysisTests.datafiles import ( COORDINATES_XYZ, COORDINATES_TRR, GRO, TRR, GRO_velocity, PDB_xvf, TRR_xvf diff --git a/testsuite/MDAnalysisTests/core/test_groups.py b/testsuite/MDAnalysisTests/core/test_groups.py index 5332b1d5d81..7193f300063 100644 --- a/testsuite/MDAnalysisTests/core/test_groups.py +++ b/testsuite/MDAnalysisTests/core/test_groups.py @@ -8,7 +8,7 @@ ) import operator -from MDAnalysisTests.core.groupbase import make_Universe +from MDAnalysisTests import make_Universe from MDAnalysis.core import groups diff --git a/testsuite/MDAnalysisTests/core/test_requires.py b/testsuite/MDAnalysisTests/core/test_requires.py index 4d59391594e..727c550d072 100644 --- a/testsuite/MDAnalysisTests/core/test_requires.py +++ b/testsuite/MDAnalysisTests/core/test_requires.py @@ -10,7 +10,7 @@ from MDAnalysis.core.groups import requires from MDAnalysis import NoDataError -from MDAnalysisTests.core.groupbase import make_Universe +from MDAnalysisTests import make_Universe class TestRequires(object): diff --git a/testsuite/MDAnalysisTests/core/test_residue.py b/testsuite/MDAnalysisTests/core/test_residue.py new file mode 100644 index 00000000000..52ea99c50f3 --- /dev/null +++ b/testsuite/MDAnalysisTests/core/test_residue.py @@ -0,0 +1,58 @@ +# -*- Mode: python; tab-width: 4; indent-tabs-mode:nil; coding:utf-8 -*- +# vim: tabstop=4 expandtab shiftwidth=4 softtabstop=4 fileencoding=utf-8 +# +# MDAnalysis --- http://www.mdanalysis.org +# Copyright (c) 2006-2016 The MDAnalysis Development Team and contributors +# (see the file AUTHORS for the full list of names) +# +# Released under the GNU Public Licence, v2 or any higher version +# +# Please cite your use of MDAnalysis in published work: +# +# R. J. Gowers, M. Linke, J. Barnoud, T. J. E. Reddy, M. N. Melo, S. L. Seyler, +# D. L. Dotson, J. Domanski, S. Buchoux, I. M. Kenney, and O. Beckstein. +# MDAnalysis: A Python package for the rapid analysis of molecular dynamics +# simulations. In S. Benthall and S. Rostrup editors, Proceedings of the 15th +# Python in Science Conference, pages 102-109, Austin, TX, 2016. SciPy. +# +# N. Michaud-Agrawal, E. J. Denning, T. B. Woolf, and O. Beckstein. +# MDAnalysis: A Toolkit for the Analysis of Molecular Dynamics Simulations. +# J. Comput. Chem. 32 (2011), 2319--2327, doi:10.1002/jcc.21787 +# +from numpy.testing import ( + dec, + assert_, + assert_equal, +) + +import MDAnalysis as mda + +from MDAnalysisTests import parser_not_found +from MDAnalysisTests.datafiles import PSF, DCD + + +class TestResidue(object): + # Legacy tests from before 363 + @dec.skipif(parser_not_found('DCD'), + 'DCD parser not available. Are you using python 3?') + def setUp(self): + self.universe = mda.Universe(PSF, DCD) + self.res = self.universe.residues[100] + + def test_type(self): + assert_(isinstance(self.res, mda.core.groups.Residue)) + assert_equal(self.res.resname, "ILE") + assert_equal(self.res.resid, 101) + + def test_index(self): + atom = self.res.atoms[2] + assert_(isinstance(atom, mda.core.groups.Atom)) + assert_equal(atom.name, "CA") + assert_equal(atom.index, 1522) + assert_equal(atom.resid, 101) + + def test_atom_order(self): + assert_equal(self.res.atoms.indices, + sorted(self.res.atoms.indices)) + + diff --git a/testsuite/MDAnalysisTests/core/test_residuegroup.py b/testsuite/MDAnalysisTests/core/test_residuegroup.py new file mode 100644 index 00000000000..2dcffdef6f0 --- /dev/null +++ b/testsuite/MDAnalysisTests/core/test_residuegroup.py @@ -0,0 +1,266 @@ +# -*- Mode: python; tab-width: 4; indent-tabs-mode:nil; coding:utf-8 -*- +# vim: tabstop=4 expandtab shiftwidth=4 softtabstop=4 fileencoding=utf-8 +# +# MDAnalysis --- http://www.mdanalysis.org +# Copyright (c) 2006-2016 The MDAnalysis Development Team and contributors +# (see the file AUTHORS for the full list of names) +# +# Released under the GNU Public Licence, v2 or any higher version +# +# Please cite your use of MDAnalysis in published work: +# +# R. J. Gowers, M. Linke, J. Barnoud, T. J. E. Reddy, M. N. Melo, S. L. Seyler, +# D. L. Dotson, J. Domanski, S. Buchoux, I. M. Kenney, and O. Beckstein. +# MDAnalysis: A Python package for the rapid analysis of molecular dynamics +# simulations. In S. Benthall and S. Rostrup editors, Proceedings of the 15th +# Python in Science Conference, pages 102-109, Austin, TX, 2016. SciPy. +# +# N. Michaud-Agrawal, E. J. Denning, T. B. Woolf, and O. Beckstein. +# MDAnalysis: A Toolkit for the Analysis of Molecular Dynamics Simulations. +# J. Comput. Chem. 32 (2011), 2319--2327, doi:10.1002/jcc.21787 +# + +import numpy as np +from numpy.testing import ( + dec, + assert_, + assert_equal, + assert_raises, +) +from unittest import skip + +import MDAnalysis as mda + +from MDAnalysisTests.datafiles import PSF, DCD +from MDAnalysisTests import parser_not_found + + +class TestSequence(object): + # all tests are done with the AdK system (PSF and DCD) sequence: + # http://www.uniprot.org/uniprot/P69441.fasta + # >sp|P69441|KAD_ECOLI Adenylate kinase OS=Escherichia coli (strain K12) GN=adk PE=1 SV=1 + ref_adk_sequence = ( + "MRIILLGAPGAGKGTQAQFIMEKYGIPQISTGDMLRAAVKSGSELGKQAKDIMDAGKLVT" + "DELVIALVKERIAQEDCRNGFLLDGFPRTIPQADAMKEAGINVDYVLEFDVPDELIVDRI" + "VGRRVHAPSGRVYHVKFNPPKVEGKDDVTGEELTTRKDDQEETVRKRLVEYHQMTAPLIG" + "YYSKEAEAGNTKYAKVDGTKPVAEVRADLEKILG" + ) + + def setUp(self): + self.u = mda.Universe(PSF, DCD) + + def tearDown(self): + del self.u + + def test_string(self): + p = self.u.select_atoms("protein") + assert_equal(p.residues.sequence(format="string"), + self.ref_adk_sequence) + + def test_SeqRecord(self): + p = self.u.select_atoms("protein") + s = p.residues.sequence(format="SeqRecord", + id="P69441", name="KAD_ECOLI Adenylate kinase", + description="EcAdK from pdb 4AKE") + assert_equal(s.id, "P69441") + assert_equal(s.seq.tostring(), self.ref_adk_sequence) + + def test_SeqRecord_default(self): + p = self.u.select_atoms("protein") + s = p.residues.sequence(id="P69441", name="KAD_ECOLI Adenylate kinase", + description="EcAdK from pdb 4AKE") + assert_equal(s.id, "P69441") + assert_equal(s.seq.tostring(), self.ref_adk_sequence) + + def test_Seq(self): + p = self.u.select_atoms("protein") + s = p.residues.sequence(format="Seq") + assert_equal(s.tostring(), self.ref_adk_sequence) + + def test_nonIUPACresname_VE(self): + """test_sequence_nonIUPACresname: non recognized amino acids raise + ValueError""" + # fake non-IUPAC residue name for this test + residues = self.u.select_atoms("resname MET").residues + residues.resnames = "MSE" + + def wrong_res(): + self.u.residues.sequence() + + assert_raises(ValueError, wrong_res) + + def test_format_TE(self): + assert_raises(TypeError, self.u.residues.sequence, format='chicken') + + +class TestResidueGroup(object): + # Legacy tests from before 363 + @dec.skipif(parser_not_found('DCD'), + 'DCD parser not available. Are you using python 3?') + def setUp(self): + """Set up the standard AdK system in implicit solvent.""" + self.universe = mda.Universe(PSF, DCD) + self.rg = self.universe.residues + + def test_newResidueGroup(self): + """test that slicing a ResidueGroup returns a new ResidueGroup + (Issue 135)""" + rg = self.universe.atoms.residues + newrg = rg[10:20:2] + assert_(isinstance(newrg, mda.core.groups.ResidueGroup), + "Failed to make a new ResidueGroup: type mismatch") + + def test_n_atoms(self): + assert_equal(self.rg.n_atoms, 3341) + + def test_n_residues(self): + assert_equal(self.rg.n_residues, 214) + + def test_resids_dim(self): + assert_equal(len(self.rg.resids), len(self.rg)) + + def test_resnums_dim(self): + assert_equal(len(self.rg.resnums), len(self.rg)) + + def test_segids_dim(self): + assert_equal(len(self.rg.segids), len(self.rg)) + + def test_len(self): + """testing that len(residuegroup) == residuegroup.n_residues""" + assert_equal(len(self.rg), self.rg.n_residues, + "len and n_residues disagree") + + def test_set_resids(self): + rg = self.universe.select_atoms("bynum 12:42").residues + resid = 999 + rg.resids = resid + # check individual atoms + for at in rg.atoms: + assert_equal(a.resid, resid, + err_msg="failed to set_resid atoms 12:42 to same resid") + # check residues + assert_equal(rg.resids, resid * np.ones(rg.n_residues), + err_msg="failed to set_resid of residues belonging to " + "atoms 12:42 to same resid") + + def test_set_resids(self): + """test_set_resid: set ResidueGroup resids on a per-residue basis""" + rg = self.universe.select_atoms("resid 10:18").residues + resids = np.array(rg.resids) + 1000 + rg.resids = resids + # check individual atoms + for r, resid in zip(rg, resids): + for at in r.atoms: + assert_equal(at.resid, resid, + err_msg="failed to set_resid residues 10:18 to same " + "resid in residue {0}\n" + "(resids = {1}\nresidues = {2})".format(r, resids, rg)) + assert_equal(rg.resids, resids, + err_msg="failed to set_resid of residues belonging to " + "residues 10:18 to new resids") + + def test_set_resids_updates_self(self): + rg = self.universe.select_atoms("resid 10:18").residues + resids = np.array(rg.resids) + 1000 + rg.resids = resids + assert_equal(rg.resids, resids, + err_msg="old selection was not changed in place " + "after set_resid") + + def test_set_resnum_single(self): + rg = self.universe.residues[:3] + new = 22 + rg.resnums = new + + assert_equal(all(rg.resnums == new), True) + for r in rg: + assert_equal(r.resnum, new) + + def test_set_resnum_many(self): + rg = self.universe.residues[:3] + new = [22, 23, 24] + rg.resnums = new + + assert_equal(all(rg.resnums == new), True) + for r, v in zip(rg, new): + assert_equal(r.resnum, v) + + def test_set_resnum_ValueError(self): + rg = self.universe.residues[:3] + new = [22, 23, 24, 25] + + assert_raises(ValueError, setattr, rg, 'resnums', new) + + # INVALID: no `set_resnames` method; use `resnames` property directly + @skip + def test_set_resname_single(self): + rg = self.universe.residues[:3] + new = 'newname' + + rg.set_resnames(new) + assert_equal(all(rg.resnames == new), True) + for r in rg: + assert_equal(r.name, new) + + # INVALID: no `set_resnames` method; use `resnames` property directly + @skip + def test_set_resname_many(self): + rg = self.universe.residues[:3] + new = ['a', 'b', 'c'] + rg.set_resnames(new) + + assert_equal(all(rg.resnames == new), True) + for r, v in zip(rg, new): + assert_equal(r.name, v) + + # INVALID: no `set_resnames` method; use `resnames` property directly + @skip + def test_set_resname_ValueError(self): + rg = self.universe.residues[:3] + new = ['a', 'b', 'c', 'd'] + + assert_raises(ValueError, rg.set_resnames, new) + + # INVALID: no `set_resids` method; also, residues are not mergeable + # by setting resids; resids are not necessarily unique; atoms must + # have their resindex set to change residue membership + @skip + def test_merge_residues(self): + rg = self.universe.select_atoms("resid 12:14").residues + nres_old = self.universe.atoms.n_residues + natoms_old = rg.n_atoms + rg.set_resids(12) # merge all into one with resid 12 + nres_new = self.universe.atoms.n_residues + r_merged = self.universe.select_atoms("resid 12:14").residues + natoms_new = self.universe.select_atoms("resid 12").n_atoms + assert_equal(len(r_merged), 1, err_msg="set_resid failed to merge " + "residues: merged = {0}".format(r_merged)) + assert_equal(nres_new, nres_old - 2, + err_msg="set_resid failed to merge residues: " + "merged = {0}".format(r_merged)) + assert_equal(natoms_new, natoms_old, err_msg="set_resid lost atoms " + "on merge".format(r_merged)) + + assert_equal(self.universe.residues.n_residues, + self.universe.atoms.n_residues, + err_msg="Universe.residues and Universe.atoms.n_residues " + "do not agree after residue " + "merge.") + + # INVALID: no `set_masses` method; use `masses` property directly + @skip + def test_set_masses(self): + rg = self.universe.select_atoms("bynum 12:42 and name H*").residues + mass = 2.0 + rg.set_masses(mass) + # check individual atoms + assert_equal([a.mass for a in rg.atoms], + mass * np.ones(rg.n_atoms), + err_msg="failed to set_mass H* atoms in resid 12:42 to {0}".format(mass)) + + # VALID + def test_atom_order(self): + assert_equal(self.universe.residues.atoms.indices, + sorted(self.universe.residues.atoms.indices)) + + diff --git a/testsuite/MDAnalysisTests/core/test_segment.py b/testsuite/MDAnalysisTests/core/test_segment.py new file mode 100644 index 00000000000..92aa21ae946 --- /dev/null +++ b/testsuite/MDAnalysisTests/core/test_segment.py @@ -0,0 +1,83 @@ +# -*- Mode: python; tab-width: 4; indent-tabs-mode:nil; coding:utf-8 -*- +# vim: tabstop=4 expandtab shiftwidth=4 softtabstop=4 fileencoding=utf-8 +# +# MDAnalysis --- http://www.mdanalysis.org +# Copyright (c) 2006-2016 The MDAnalysis Development Team and contributors +# (see the file AUTHORS for the full list of names) +# +# Released under the GNU Public Licence, v2 or any higher version +# +# Please cite your use of MDAnalysis in published work: +# +# R. J. Gowers, M. Linke, J. Barnoud, T. J. E. Reddy, M. N. Melo, S. L. Seyler, +# D. L. Dotson, J. Domanski, S. Buchoux, I. M. Kenney, and O. Beckstein. +# MDAnalysis: A Python package for the rapid analysis of molecular dynamics +# simulations. In S. Benthall and S. Rostrup editors, Proceedings of the 15th +# Python in Science Conference, pages 102-109, Austin, TX, 2016. SciPy. +# +# N. Michaud-Agrawal, E. J. Denning, T. B. Woolf, and O. Beckstein. +# MDAnalysis: A Toolkit for the Analysis of Molecular Dynamics Simulations. +# J. Comput. Chem. 32 (2011), 2319--2327, doi:10.1002/jcc.21787 +# + +from numpy.testing import ( + dec, + assert_, + assert_equal, +) +from nose.plugins.attrib import attr + +import MDAnalysis as mda + +from MDAnalysisTests import parser_not_found, make_Universe +from MDAnalysis.tests.datafiles import PSF, DCD + + +class TestSegment(object): + def setUp(self): + self.universe = make_Universe(('segids',)) + self.sB = self.universe.segments[1] + + def test_type(self): + assert_(isinstance(self.sB, mda.core.groups.Segment)) + assert_equal(self.sB.segid, "SegB") + + def test_index(self): + s = self.sB + res = s.residues[3] + assert_(isinstance(res, mda.core.groups.Residue)) + + def test_slicing(self): + res = self.sB.residues[:3] + assert_equal(len(res), 3) + assert_(isinstance(res, mda.core.groups.ResidueGroup)) + + def test_advanced_slicing(self): + res = self.sB.residues[[2, 1, 0, 2]] + assert_equal(len(res), 4) + assert_(isinstance(res, mda.core.groups.ResidueGroup)) + + def test_atom_order(self): + assert_equal(self.universe.segments[0].atoms.indices, + sorted(self.universe.segments[0].atoms.indices)) + +@attr("issue") +@dec.skipif(parser_not_found('DCD'), + 'DCD parser not available. Are you using python 3?') +def test_generated_residueselection(): + """Test that a generated residue group always returns a ResidueGroup (Issue 47) + unless there is a single residue (Issue 363 change)""" + universe = mda.Universe(PSF, DCD) + # only a single Cys in AdK + cys = universe.s4AKE.CYS + assert_(isinstance(cys, mda.core.groups.Residue), + "Single Cys77 is NOT returned as a single Residue (Issue 47)") + + # multiple Met + met = universe.s4AKE.MET + assert_(isinstance(met, mda.core.groups.ResidueGroup), + "Met selection does not return a ResidueGroup") + + del universe + + diff --git a/testsuite/MDAnalysisTests/core/test_segmentgroup.py b/testsuite/MDAnalysisTests/core/test_segmentgroup.py new file mode 100644 index 00000000000..f09b2355a05 --- /dev/null +++ b/testsuite/MDAnalysisTests/core/test_segmentgroup.py @@ -0,0 +1,89 @@ +# -*- Mode: python; tab-width: 4; indent-tabs-mode:nil; coding:utf-8 -*- +# vim: tabstop=4 expandtab shiftwidth=4 softtabstop=4 fileencoding=utf-8 +# +# MDAnalysis --- http://www.mdanalysis.org +# Copyright (c) 2006-2016 The MDAnalysis Development Team and contributors +# (see the file AUTHORS for the full list of names) +# +# Released under the GNU Public Licence, v2 or any higher version +# +# Please cite your use of MDAnalysis in published work: +# +# R. J. Gowers, M. Linke, J. Barnoud, T. J. E. Reddy, M. N. Melo, S. L. Seyler, +# D. L. Dotson, J. Domanski, S. Buchoux, I. M. Kenney, and O. Beckstein. +# MDAnalysis: A Python package for the rapid analysis of molecular dynamics +# simulations. In S. Benthall and S. Rostrup editors, Proceedings of the 15th +# Python in Science Conference, pages 102-109, Austin, TX, 2016. SciPy. +# +# N. Michaud-Agrawal, E. J. Denning, T. B. Woolf, and O. Beckstein. +# MDAnalysis: A Toolkit for the Analysis of Molecular Dynamics Simulations. +# J. Comput. Chem. 32 (2011), 2319--2327, doi:10.1002/jcc.21787 +# + +from numpy.testing import ( + dec, + assert_, + assert_equal, +) +from unittest import skip + +import MDAnalysis as mda + +from MDAnalysisTests.datafiles import PSF, DCD +from MDAnalysisTests import parser_not_found + + +class TestSegmentGroup(object): + # Legacy tests from before 363 + @dec.skipif(parser_not_found('DCD'), + 'DCD parser not available. Are you using python 3?') + def setUp(self): + """Set up the standard AdK system in implicit solvent.""" + self.universe = mda.Universe(PSF, DCD) + self.g = self.universe.atoms.segments + + def test_newSegmentGroup(self): + """test that slicing a SegmentGroup returns a new SegmentGroup (Issue 135)""" + g = self.universe.atoms.segments + newg = g[:] + assert_(isinstance(newg, mda.core.groups.SegmentGroup)) + assert_equal(len(newg), len(g)) + + def test_n_atoms(self): + assert_equal(self.g.n_atoms, 3341) + + def test_n_residues(self): + assert_equal(self.g.n_residues, 214) + + def test_resids_dim(self): + assert_equal(len(self.g.resids), len(self.g)) + for seg, resids in zip(self.g, self.g.resids): + assert_(len(resids) == len(seg.residues)) + assert_equal(seg.residues.resids, resids) + + def test_resnums_dim(self): + assert_equal(len(self.g.resnums), len(self.g)) + for seg, resnums in zip(self.g, self.g.resnums): + assert_(len(resnums) == len(seg.residues)) + assert_equal(seg.residues.resnums, resnums) + + def test_segids_dim(self): + assert_equal(len(self.g.segids), len(self.g)) + + def test_set_segids(self): + s = self.universe.select_atoms('all').segments + s.segids = 'ADK' + assert_equal(self.universe.segments.segids, ['ADK'], + err_msg="failed to set_segid on segments") + + def test_set_segid_updates_self(self): + g = self.universe.select_atoms("resid 10:18").segments + g.segids = 'ADK' + assert_equal(g.segids, ['ADK'], + err_msg="old selection was not changed in place after set_segid") + + def test_atom_order(self): + assert_equal(self.universe.segments.atoms.indices, + sorted(self.universe.segments.atoms.indices)) + + diff --git a/testsuite/MDAnalysisTests/core/test_topology.py b/testsuite/MDAnalysisTests/core/test_topology.py index b6117261393..503be6b89db 100644 --- a/testsuite/MDAnalysisTests/core/test_topology.py +++ b/testsuite/MDAnalysisTests/core/test_topology.py @@ -15,7 +15,7 @@ ) import numpy as np -from MDAnalysisTests.core.groupbase import make_Universe +from MDAnalysisTests import make_Universe from MDAnalysis.core.topology import ( Topology, diff --git a/testsuite/MDAnalysisTests/core/test_topologyattrs.py b/testsuite/MDAnalysisTests/core/test_topologyattrs.py index a9e60948e5a..1b828628794 100644 --- a/testsuite/MDAnalysisTests/core/test_topologyattrs.py +++ b/testsuite/MDAnalysisTests/core/test_topologyattrs.py @@ -33,7 +33,7 @@ from nose.tools import raises from MDAnalysisTests.plugins.knownfailure import knownfailure from MDAnalysisTests.datafiles import PSF, DCD -from MDAnalysisTests.core.groupbase import make_Universe +from MDAnalysisTests import make_Universe import MDAnalysis as mda import MDAnalysis.core.topologyattrs as tpattrs @@ -91,6 +91,13 @@ class TestAtomAttr(TopologyAttrMixin): values = np.array([7, 3, 69, 9993, 84, 194, 263, 501, 109, 5873]) attrclass = tpattrs.AtomAttr + @staticmethod + def test_set_atom_VE(): + u = make_Universe(('names',)) + at = u.atoms[0] + + assert_raises(ValueError, setattr, at, 'name', ['oopsy', 'daisies']) + def test_get_atoms(self): result = self.attr.get_atoms(DummyGroup([2, 1])) @@ -98,10 +105,22 @@ def test_get_atoms(self): assert_array_equal(result, self.values[[2, 1]]) - def test_set_atoms(self): - self.attr.set_atoms(DummyGroup([3, 7]), np.array([23, 504])) - assert_array_equal(self.attr.get_atoms(DummyGroup([3, 7])), - np.array([23, 504])) + def test_set_atoms_singular(self): + # set len 2 Group to len 1 value + dg = DummyGroup([3, 7]) + self.attr.set_atoms(dg, 567) + assert_array_equal(self.attr.get_atoms(dg), np.array([567, 567])) + + def test_set_atoms_plural(self): + # set len 2 Group to len 2 values + dg = DummyGroup([3, 7]) + self.attr.set_atoms(dg, np.array([23, 504])) + assert_array_equal(self.attr.get_atoms(dg), np.array([23, 504])) + + def test_set_atoms_VE(self): + # set len 2 Group to wrong length values + dg = DummyGroup([3, 7]) + assert_raises(ValueError, self.attr.set_atoms, dg, np.array([6, 7, 8, 9])) def test_get_residues(self): """Unless overriden by child class, this should yield values for all @@ -187,6 +206,13 @@ class TestResidueAttr(TopologyAttrMixin): values = np.array([15.2, 395.6, 0.1, 9.8]) attrclass = tpattrs.ResidueAttr + @staticmethod + def test_set_residue_VE(): + u = make_Universe(('resnames',)) + res = u.residues[0] + + assert_raises(ValueError, setattr, res, 'resname', ['wrong', 'length']) + def test_get_atoms(self): assert_array_equal(self.attr.get_atoms(DummyGroup([7, 3, 9])), self.values[[3, 2, 2]]) @@ -195,11 +221,23 @@ def test_get_residues(self): assert_array_equal(self.attr.get_residues(DummyGroup([1, 2, 1, 3])), self.values[[1, 2, 1, 3]]) - def test_set_residues(self): + def test_set_residues_singular(self): + dg = DummyGroup([3, 0, 1]) + self.attr.set_residues(dg, 2) + + assert_array_almost_equal(self.attr.get_residues(dg), + np.array([2, 2, 2])) + + def test_set_residues_plural(self): self.attr.set_residues(DummyGroup([3, 0, 1]), - np.array([23, 504, 0.0002])) + np.array([23, 504, 2])) assert_array_almost_equal(self.attr.get_residues(DummyGroup([3, 0, 1])), - np.array([23, 504, 0.0002])) + np.array([23, 504, 2])) + + def test_set_residues_VE(self): + dg = DummyGroup([3, 0, 1]) + + assert_raises(ValueError, self.attr.set_residues, dg, np.array([4.5, 5.2])) def test_get_segments(self): """Unless overriden by child class, this should yield values for all @@ -292,6 +330,13 @@ class TestSegmentAttr(TopologyAttrMixin): values = np.array([-0.19, 500]) attrclass = tpattrs.SegmentAttr + @staticmethod + def test_set_segment_VE(): + u = make_Universe(('segids',)) + seg = u.segments[0] + + assert_raises(ValueError, setattr, seg, 'segid', [1, 2, 3]) + def test_get_atoms(self): assert_array_equal(self.attr.get_atoms(DummyGroup([2, 4, 1])), self.values[[1, 1, 0]]) @@ -308,11 +353,19 @@ def test_get_segments(self): assert_array_equal(self.attr.get_segments(DummyGroup([1, 0, 0])), self.values[[1, 0, 0]]) - def test_set_segments(self): - self.attr.set_segments(DummyGroup([0, 1]), - np.array([23, -0.0002])) - assert_array_equal(self.attr.get_segments(DummyGroup([1, 0, 1])), - np.array([-0.0002, 23, -0.0002])) + def test_set_segments_singular(self): + dg = DummyGroup([0, 1]) + self.attr.set_segments(dg, 0.45) + assert_array_equal(self.attr.get_segments(dg), np.array([0.45, 0.45])) + + def test_set_segments_plural(self): + dg = DummyGroup([0, 1]) + self.attr.set_segments(dg, np.array([23, -0.0002])) + assert_array_equal(self.attr.get_segments(dg), np.array([23, -0.0002])) + + def test_set_segments_VE(self): + dg = DummyGroup([0, 1]) + assert_raises(ValueError, self.attr.set_segments, dg, np.array([4, 5, 6, 7])) class TestAttr(object): @@ -343,3 +396,58 @@ def test_align_principal_axes_with_x(self): # is that big. assert_(np.allclose(np.abs(self.ag.principal_axes()), np.eye(3), rtol=0, atol=0.5)) + + +class TestCrossLevelAttributeSetting(object): + """ + + Can only get attributes belonging to higher level objects + + Atom.resid works! + ResidueGroup.names = ['a', 'b', 'c'] doesn't work, Atom is below Residue + + Setting any attribute we can get should only work if they are the same level. + + Atom.resid = 4 should fail because resid belongs to Residue not Atom + """ + @staticmethod + def _check_crosslevel_fail(item, attr): + assert_raises(NotImplementedError, setattr, item, attr, 1.0) + + def test_set_crosslevel(self): + u = make_Universe(('names', 'resids', 'segids')) + + # component and group in each level + atomlevel = (u.atoms[0], u.atoms[:10]) + residuelevel = (u.residues[0], u.residues[:5]) + segmentlevel = (u.segments[0], u.segments[:2]) + levels = {0:atomlevel, 1:residuelevel, 2:segmentlevel} + + atomattr = 'names' + residueattr = 'resids' + segmentattr = 'segids' + attrs = {0:atomattr, 1:residueattr, 2:segmentattr} + + # loop over Atom, Residue, Segment level + for level_idx, level in levels.items(): + # loop over an Attribute native to each level + for attr_idx, attr in attrs.items(): + if level_idx == attr_idx: + # if we're on the same level, then this should work + # ie Atom.mass = 12.0 is OK! + continue + component, group = level + # eg 'name', 'names' = 'names', 'names'[:-1] + singular_attr, plural_attr = attr[:-1], attr + + # eg check ResidueGroup.names = 'newvalue' raises NIE + # or ResidueGroup.segids = 'newvalue' raises NIE + yield self._check_crosslevel_fail, group, plural_attr + + if attr_idx < level_idx: + # Segment.resid doesn't even exist as an attribute + # so we don't have to check that setting fails + # Atom.segid does exist as attribute, + # but will fail to be set + continue + yield self._check_crosslevel_fail, component, singular_attr diff --git a/testsuite/MDAnalysisTests/core/test_universe.py b/testsuite/MDAnalysisTests/core/test_universe.py index f5a062bbf20..f1eaef489c7 100644 --- a/testsuite/MDAnalysisTests/core/test_universe.py +++ b/testsuite/MDAnalysisTests/core/test_universe.py @@ -35,15 +35,19 @@ ) from nose.plugins.attrib import attr -from MDAnalysisTests.core.groupbase import make_Universe +from MDAnalysisTests import make_Universe from MDAnalysisTests.datafiles import ( PSF, DCD, PSF_BAD, PDB_small, + GRO, TRR, + two_water_gro, two_water_gro_nonames, + TRZ, TRZ_psf, ) from MDAnalysisTests import parser_not_found import MDAnalysis as mda +import MDAnalysis.coordinates from MDAnalysis.topology.base import TopologyReader @@ -235,3 +239,266 @@ def test_set_dimensions(self): box = np.array([10, 11, 12, 90, 90, 90]) u.dimensions = np.array([10, 11, 12, 90, 90, 90]) assert_allclose(u.dimensions, box) + + +class TestGuessBonds(object): + """Test the AtomGroup methed guess_bonds + + This needs to be done both from Universe creation (via kwarg) and AtomGroup + + It needs to: + - work if all atoms are in vdwradii table + - fail properly if not + - work again if vdwradii are passed. + """ + def setUp(self): + self.vdw = {'A':1.05, 'B':0.4} + + def tearDown(self): + del self.vdw + + def _check_universe(self, u): + """Verify that the Universe is created correctly""" + assert_equal(len(u.bonds), 4) + assert_equal(len(u.angles), 2) + assert_equal(len(u.dihedrals), 0) + assert_equal(len(u.atoms[0].bonds), 2) + assert_equal(len(u.atoms[1].bonds), 1) + assert_equal(len(u.atoms[2].bonds), 1) + assert_equal(len(u.atoms[3].bonds), 2) + assert_equal(len(u.atoms[4].bonds), 1) + assert_equal(len(u.atoms[5].bonds), 1) + assert_('guess_bonds' in u.kwargs) + + def test_universe_guess_bonds(self): + """Test that making a Universe with guess_bonds works""" + u = mda.Universe(two_water_gro, guess_bonds=True) + self._check_universe(u) + assert_(u.kwargs['guess_bonds'] is True) + + def test_universe_guess_bonds_no_vdwradii(self): + """Make a Universe that has atoms with unknown vdwradii.""" + assert_raises(ValueError, mda.Universe, two_water_gro_nonames, guess_bonds=True) + + def test_universe_guess_bonds_with_vdwradii(self): + """Unknown atom types, but with vdw radii here to save the day""" + u = mda.Universe(two_water_gro_nonames, guess_bonds=True, + vdwradii=self.vdw) + self._check_universe(u) + assert_(u.kwargs['guess_bonds'] is True) + assert_equal(self.vdw, u.kwargs['vdwradii']) + + def test_universe_guess_bonds_off(self): + u = mda.Universe(two_water_gro_nonames, guess_bonds=False) + + for attr in ('bonds', 'angles', 'dihedrals'): + assert_(not hasattr(u, attr)) + assert_(u.kwargs['guess_bonds'] is False) + + def _check_atomgroup(self, ag, u): + """Verify that the AtomGroup made bonds correctly, + and that the Universe got all this info + """ + assert_equal(len(ag.bonds), 2) + assert_equal(len(ag.angles), 1) + assert_equal(len(ag.dihedrals), 0) + assert_equal(len(u.bonds), 2) + assert_equal(len(u.angles), 1) + assert_equal(len(u.dihedrals), 0) + assert_equal(len(u.atoms[0].bonds), 2) + assert_equal(len(u.atoms[1].bonds), 1) + assert_equal(len(u.atoms[2].bonds), 1) + assert_equal(len(u.atoms[3].bonds), 0) + assert_equal(len(u.atoms[4].bonds), 0) + assert_equal(len(u.atoms[5].bonds), 0) + + def test_atomgroup_guess_bonds(self): + """Test an atomgroup doing guess bonds""" + u = mda.Universe(two_water_gro) + + ag = u.atoms[:3] + ag.guess_bonds() + self._check_atomgroup(ag, u) + + def test_atomgroup_guess_bonds_no_vdwradii(self): + u = mda.Universe(two_water_gro_nonames) + + ag = u.atoms[:3] + assert_raises(ValueError, ag.guess_bonds) + + def test_atomgroup_guess_bonds_with_vdwradii(self): + u = mda.Universe(two_water_gro_nonames) + + ag = u.atoms[:3] + ag.guess_bonds(vdwradii=self.vdw) + self._check_atomgroup(ag, u) + + +class TestInMemoryUniverse(object): + @staticmethod + @dec.skipif(parser_not_found('DCD'), + 'DCD parser not available. Are you using python 3?') + def test_reader_w_timeseries(): + universe = mda.Universe(PSF, DCD, in_memory=True) + assert_equal(universe.trajectory.timeseries(universe.atoms).shape, + (3341, 98, 3), + err_msg="Unexpected shape of trajectory timeseries") + + @staticmethod + def test_reader_wo_timeseries(): + universe = mda.Universe(GRO, TRR, in_memory=True) + assert_equal(universe.trajectory.timeseries(universe.atoms).shape, + (47681, 10, 3), + err_msg="Unexpected shape of trajectory timeseries") + + @staticmethod + @dec.skipif(parser_not_found('DCD'), + 'DCD parser not available. Are you using python 3?') + def test_reader_w_timeseries_frame_interval(): + universe = mda.Universe(PSF, DCD, in_memory=True, + in_memory_step=10) + assert_equal(universe.trajectory.timeseries(universe.atoms).shape, + (3341, 10, 3), + err_msg="Unexpected shape of trajectory timeseries") + + @staticmethod + def test_reader_wo_timeseries_frame_interval(): + universe = mda.Universe(GRO, TRR, in_memory=True, + in_memory_step=3) + assert_equal(universe.trajectory.timeseries(universe.atoms).shape, + (47681, 4, 3), + err_msg="Unexpected shape of trajectory timeseries") + + @staticmethod + @dec.skipif(parser_not_found('DCD'), + 'DCD parser not available. Are you using python 3?') + def test_existing_universe(): + universe = mda.Universe(PDB_small, DCD) + universe.transfer_to_memory() + assert_equal(universe.trajectory.timeseries(universe.atoms).shape, + (3341, 98, 3), + err_msg="Unexpected shape of trajectory timeseries") + + @staticmethod + @dec.skipif(parser_not_found('DCD'), + 'DCD parser not available. Are you using python 3?') + def test_frame_interval_convention(): + universe1 = mda.Universe(PSF, DCD) + array1 = universe1.trajectory.timeseries(skip=10) + universe2 = mda.Universe(PSF, DCD, in_memory=True, + in_memory_step=10) + array2 = universe2.trajectory.timeseries() + assert_equal(array1, array2, + err_msg="Unexpected differences between arrays.") + + @staticmethod + def test_slicing_with_start_stop(): + universe = MDAnalysis.Universe(PDB_small, DCD) + # Skip only the last frame + universe.transfer_to_memory(start=10, stop=20) + assert_equal(universe.trajectory.timeseries(universe.atoms).shape, + (3341, 10, 3), + err_msg="Unexpected shape of trajectory timeseries") + + @staticmethod + def test_slicing_without_start(): + universe = MDAnalysis.Universe(PDB_small, DCD) + # Skip only the last frame + universe.transfer_to_memory(stop=10) + assert_equal(universe.trajectory.timeseries(universe.atoms).shape, + (3341, 10, 3), + err_msg="Unexpected shape of trajectory timeseries") + + @staticmethod + def test_slicing_without_stop(): + universe = MDAnalysis.Universe(PDB_small, DCD) + # Skip only the last frame + universe.transfer_to_memory(start=10) + print(universe.trajectory.timeseries(universe.atoms).shape) + assert_equal(universe.trajectory.timeseries(universe.atoms).shape, + (3341, 88, 3), + err_msg="Unexpected shape of trajectory timeseries") + + @staticmethod + def test_slicing_step_without_start_stop(): + universe = MDAnalysis.Universe(PDB_small, DCD) + # Skip only the last frame + universe.transfer_to_memory(step=2) + print(universe.trajectory.timeseries(universe.atoms).shape) + assert_equal(universe.trajectory.timeseries(universe.atoms).shape, + (3341, 49, 3), + err_msg="Unexpected shape of trajectory timeseries") + + @staticmethod + def test_slicing_step_with_start_stop(): + universe = MDAnalysis.Universe(PDB_small, DCD) + # Skip only the last frame + universe.transfer_to_memory(start=10, stop=30, step=2) + print(universe.trajectory.timeseries(universe.atoms).shape) + assert_equal(universe.trajectory.timeseries(universe.atoms).shape, + (3341, 10, 3), + err_msg="Unexpected shape of trajectory timeseries") + + @staticmethod + def test_slicing_negative_start(): + universe = MDAnalysis.Universe(PDB_small, DCD) + # Skip only the last frame + universe.transfer_to_memory(start=-10) + print(universe.trajectory.timeseries(universe.atoms).shape) + assert_equal(universe.trajectory.timeseries(universe.atoms).shape, + (3341, 10, 3), + err_msg="Unexpected shape of trajectory timeseries") + + @staticmethod + def test_slicing_negative_stop(): + universe = MDAnalysis.Universe(PDB_small, DCD) + # Skip only the last frame + universe.transfer_to_memory(stop=-20) + print(universe.trajectory.timeseries(universe.atoms).shape) + assert_equal(universe.trajectory.timeseries(universe.atoms).shape, + (3341, 78, 3), + err_msg="Unexpected shape of trajectory timeseries") + + +class TestCustomReaders(object): + """ + Can pass a reader as kwarg on Universe creation + """ + @dec.skipif(parser_not_found('TRZ'), + 'TRZ parser not available. Are you using python 3?') + def test_custom_reader(self): + # check that reader passing works + u = mda.Universe(TRZ_psf, TRZ, format=MDAnalysis.coordinates.TRZ.TRZReader) + assert_equal(len(u.atoms), 8184) + + def test_custom_reader_singleframe(self): + T = MDAnalysis.topology.GROParser.GROParser + R = MDAnalysis.coordinates.GRO.GROReader + u = mda.Universe(two_water_gro, two_water_gro, + topology_format=T, format=R) + assert_equal(len(u.atoms), 6) + + def test_custom_reader_singleframe_2(self): + # Same as before, but only one argument to Universe + T = MDAnalysis.topology.GROParser.GROParser + R = MDAnalysis.coordinates.GRO.GROReader + u = mda.Universe(two_water_gro, + topology_format=T, format=R) + assert_equal(len(u.atoms), 6) + + @dec.skipif(parser_not_found('TRZ'), + 'TRZ parser not available. Are you using python 3?') + def test_custom_parser(self): + # topology reader passing works + u = mda.Universe(TRZ_psf, TRZ, topology_format=MDAnalysis.topology.PSFParser.PSFParser) + assert_equal(len(u.atoms), 8184) + + @dec.skipif(parser_not_found('TRZ'), + 'TRZ parser not available. Are you using python 3?') + def test_custom_both(self): + # use custom for both + u = mda.Universe(TRZ_psf, TRZ, format=MDAnalysis.coordinates.TRZ.TRZReader, + topology_format=MDAnalysis.topology.PSFParser.PSFParser) + assert_equal(len(u.atoms), 8184) + + diff --git a/testsuite/MDAnalysisTests/core/test_updating_atomgroup.py b/testsuite/MDAnalysisTests/core/test_updating_atomgroup.py index 63864babefa..f3de972ad3d 100644 --- a/testsuite/MDAnalysisTests/core/test_updating_atomgroup.py +++ b/testsuite/MDAnalysisTests/core/test_updating_atomgroup.py @@ -31,7 +31,7 @@ from MDAnalysisTests.datafiles import PSF, GRO, XTC -from MDAnalysisTests.core.groupbase import make_Universe +from MDAnalysisTests import make_Universe import MDAnalysis import MDAnalysis as mda diff --git a/testsuite/MDAnalysisTests/core/groupbase.py b/testsuite/MDAnalysisTests/core/util.py similarity index 63% rename from testsuite/MDAnalysisTests/core/groupbase.py rename to testsuite/MDAnalysisTests/core/util.py index ad5b4a6d14b..b22522d5b0b 100644 --- a/testsuite/MDAnalysisTests/core/groupbase.py +++ b/testsuite/MDAnalysisTests/core/util.py @@ -21,7 +21,7 @@ # """Mock Universe and Topology for testing purposes - +Avoids import of MDAnalysis at base level because of Issue #344 """ from __future__ import division @@ -32,12 +32,6 @@ import string import itertools -import MDAnalysis as mda -from MDAnalysis.core.topology import Topology -from MDAnalysis.coordinates.base import SingleFrameReader -from MDAnalysis.core import groups -import MDAnalysis.core.topologyattrs as ta - # Dimensions of the standard mock Universe # 5 atoms per residues, 5 residues per segment _N_ATOMS = 125 @@ -46,26 +40,65 @@ _ATOMS_PER_RES = _N_ATOMS // _N_RESIDUES _RESIDUES_PER_SEG = _N_RESIDUES // _N_SEGMENTS + def make_Universe(extras=None, size=None, trajectory=False, velocities=False, forces=False): - """Make a dummy reference Universe""" + """Make a dummy reference Universe + + u = make_Universe(('masses', 'charges')) + + Creates a lightweight Universe with only masses and charges. + + Preferable for testing core components because: + * minimises dependencies within the package + * very fast compared to a "real" Universe + + Parameters + ---------- + extras : list of strings + extra attributes to add to Universe + size : tuple of int + dimensions of the Universe (n_atoms, n_residues, n_segments) + trajectory : bool + create a fake Reader object attached to Universe + velocities : bool + if the fake Reader provides velocities + force : bool + if the fake Reader provides forces + + Returns + ------- + MDAnalysis.core.universe.Universe object + + """ + import MDAnalysis as mda + u = mda.Universe(make_topology(extras, size=size)) if trajectory: - u.trajectory = FakeReader(len(u.atoms), velocities, forces) + u.trajectory = make_FakeReader(len(u.atoms), velocities, forces) return u def make_topology(extras=None, size=None): """Reference topology system - extras - attributes to add to the Universe - size - tuple of natoms, nres, nseg, by default: - 125 atoms, 25 residue, 5 segments + Parameters + ---------- + extras : list of strings + attributes to add to the Universe + size : tuple of natoms, nres, nseg + by default: (125 atoms, 25 residue, 5 segments) + + Returns + ------- + MDAnalysis.core.topology.Topology object """ + from MDAnalysis.core.topology import Topology + if extras is None: extras = [] if size is None: size = _N_ATOMS, _N_RESIDUES, _N_SEGMENTS - attrs = [_menu[extra](size) for extra in extras] + attrs = [_MENU[extra](size) for extra in extras] return Topology(size[0], size[1], size[2], attrs=attrs, @@ -76,21 +109,26 @@ def make_topology(extras=None, size=None): def make_altLocs(size): """AltLocs cycling through A B C D E""" + import MDAnalysis.core.topologyattrs as ta + na, nr, ns = size alts = itertools.cycle(('A', 'B', 'C', 'D', 'E')) return ta.AltLocs(np.array(['{}'.format(next(alts)) for _ in range(na)], dtype=object)) def make_bfactors(size): + import MDAnalysis.core.topologyattrs as ta na, nr, ns = size return ta.Bfactors(np.tile(np.array([1.0, 2, 3, 4, 5]), nr)) def make_tempfactors(size): + import MDAnalysis.core.topologyattrs as ta na, nr, ns = size return ta.Tempfactors(np.tile(np.array([1.0, 2, 3, 4, 5]), nr)) def make_charges(size): """Atom charges (-1.5, -0.5, 0.0, 0.5, 1.5) repeated""" + import MDAnalysis.core.topologyattrs as ta na, nr, ns = size charges = itertools.cycle([-1.5, -0.5, 0.0, 0.5, 1.5]) return ta.Charges(np.array([next(charges) @@ -98,18 +136,21 @@ def make_charges(size): def make_resnames(size): """Creates residues named RsA RsB ... """ + import MDAnalysis.core.topologyattrs as ta na, nr, ns = size return ta.Resnames(np.array(['Rs{}'.format(string.uppercase[i]) for i in range(nr)], dtype=object)) def make_segids(size): """Segids SegA -> SegY""" + import MDAnalysis.core.topologyattrs as ta na, nr, ns = size return ta.Segids(np.array(['Seg{}'.format(string.uppercase[i]) for i in range(ns)], dtype=object)) def make_types(size): """Atoms are given types TypeA -> TypeE on a loop""" + import MDAnalysis.core.topologyattrs as ta na, nr, ns = size types = itertools.cycle(string.uppercase[:5]) return ta.Atomtypes(np.array( @@ -118,6 +159,7 @@ def make_types(size): def make_names(size): """Atom names AAA -> ZZZ (all unique)""" + import MDAnalysis.core.topologyattrs as ta na, nr, ns = size # produces, AAA, AAB, AAC, ABA etc names = itertools.product(*[string.uppercase] * 3) @@ -126,20 +168,24 @@ def make_names(size): for _ in range(na)], dtype=object)) def make_occupancies(size): + import MDAnalysis.core.topologyattrs as ta na, nr, ns = size return ta.Occupancies(np.tile(np.array([1.0, 2, 3, 4, 5]), nr)) def make_radii(size): + import MDAnalysis.core.topologyattrs as ta na, nr, ns = size return ta.Radii(np.tile(np.array([1.0, 2, 3, 4, 5]), nr)) def make_serials(size): """Serials go from 10 to size+10""" + import MDAnalysis.core.topologyattrs as ta na, nr, ns = size return ta.Atomids(np.arange(na) + 10) def make_masses(size): """Atom masses (5.1, 4.2, 3.3, 1.5, 0.5) repeated""" + import MDAnalysis.core.topologyattrs as ta na, nr, ns = size masses = itertools.cycle([5.1, 4.2, 3.3, 1.5, 0.5]) return ta.Masses(np.array([next(masses) @@ -147,16 +193,18 @@ def make_masses(size): def make_resnums(size): """Resnums 1 and upwards""" + import MDAnalysis.core.topologyattrs as ta na, nr, ns = size return ta.Resnums(np.arange(nr, dtype=np.int64) + 1) def make_resids(size): """Resids 1 and upwards""" + import MDAnalysis.core.topologyattrs as ta na, nr, ns = size return ta.Resids(np.arange(nr, dtype=np.int64) + 1) # Available extra TopologyAttrs to a dummy Universe -_menu = { +_MENU = { # Atoms 'altLocs': make_altLocs, 'bfactors': make_bfactors, @@ -176,20 +224,24 @@ def make_resids(size): 'segids': make_segids, } -class FakeReader(SingleFrameReader): - def __init__(self, n_atoms=None, velocities=False, forces=False): - self.n_atoms = n_atoms if not n_atoms is None else _N_ATOMS - self.filename = 'FakeReader' - self.n_frames = 1 - self._read_first_frame(velocities, forces) - - def _read_first_frame(self, velocities=False, forces=False): - ts = self.ts = self._Timestep(self.n_atoms, positions=True, - velocities=velocities, forces=forces) - ts.positions = np.arange(3 * self.n_atoms).reshape(self.n_atoms, 3) - if velocities: - ts.velocities = np.arange(3 * self.n_atoms).reshape(self.n_atoms, 3) + 100 - if forces: - ts.forces = np.arange(3 * self.n_atoms).reshape(self.n_atoms, 3) + 10000 - ts.frame = 0 - +def make_FakeReader(n_atoms=None, velocities=False, forces=False): + from MDAnalysis.coordinates.base import SingleFrameReader + + class FakeReader(SingleFrameReader): + def __init__(self, n_atoms=None, velocities=False, forces=False): + self.n_atoms = n_atoms if not n_atoms is None else _N_ATOMS + self.filename = 'FakeReader' + self.n_frames = 1 + self._read_first_frame(velocities, forces) + + def _read_first_frame(self, velocities=False, forces=False): + ts = self.ts = self._Timestep(self.n_atoms, positions=True, + velocities=velocities, forces=forces) + ts.positions = np.arange(3 * self.n_atoms).reshape(self.n_atoms, 3) + if velocities: + ts.velocities = np.arange(3 * self.n_atoms).reshape(self.n_atoms, 3) + 100 + if forces: + ts.forces = np.arange(3 * self.n_atoms).reshape(self.n_atoms, 3) + 10000 + ts.frame = 0 + + return FakeReader(n_atoms, velocities, forces) diff --git a/testsuite/MDAnalysisTests/test_atomgroup.py b/testsuite/MDAnalysisTests/test_atomgroup.py deleted file mode 100644 index 6c72026ec24..00000000000 --- a/testsuite/MDAnalysisTests/test_atomgroup.py +++ /dev/null @@ -1,2462 +0,0 @@ -# -*- Mode: python; tab-width: 4; indent-tabs-mode:nil; coding:utf-8 -*- -# vim: tabstop=4 expandtab shiftwidth=4 softtabstop=4 fileencoding=utf-8 -# -# MDAnalysis --- http://www.mdanalysis.org -# Copyright (c) 2006-2016 The MDAnalysis Development Team and contributors -# (see the file AUTHORS for the full list of names) -# -# Released under the GNU Public Licence, v2 or any higher version -# -# Please cite your use of MDAnalysis in published work: -# -# R. J. Gowers, M. Linke, J. Barnoud, T. J. E. Reddy, M. N. Melo, S. L. Seyler, -# D. L. Dotson, J. Domanski, S. Buchoux, I. M. Kenney, and O. Beckstein. -# MDAnalysis: A Python package for the rapid analysis of molecular dynamics -# simulations. In S. Benthall and S. Rostrup editors, Proceedings of the 15th -# Python in Science Conference, pages 102-109, Austin, TX, 2016. SciPy. -# -# N. Michaud-Agrawal, E. J. Denning, T. B. Woolf, and O. Beckstein. -# MDAnalysis: A Toolkit for the Analysis of Molecular Dynamics Simulations. -# J. Comput. Chem. 32 (2011), 2319--2327, doi:10.1002/jcc.21787 -# -from six.moves import zip - -import MDAnalysis -from MDAnalysis.tests.datafiles import (PSF, DCD, PDB_small, GRO, TRR, - TRZ, TRZ_psf, PSF_notop, - XYZ_mini, two_water_gro, - two_water_gro_nonames, - COORDINATES_XYZ, COORDINATES_TRR) -import MDAnalysis.core.groups -from MDAnalysis.core.groups import Atom, AtomGroup -from MDAnalysis import NoDataError - -import numpy as np -from numpy.testing import (TestCase, dec, raises, assert_equal, - assert_almost_equal, assert_raises, assert_, - assert_array_almost_equal, assert_array_equal, - assert_allclose) -from nose.plugins.attrib import attr - -from unittest import skip - -import os -import itertools - -from MDAnalysisTests import parser_not_found, tempdir -from MDAnalysisTests.core.groupbase import make_Universe - - -class TestAtom(TestCase): - """Tests of Atom.""" - - # VALID - @dec.skipif(parser_not_found('DCD'), - 'DCD parser not available. Are you using python 3?') - def setUp(self): - """Set up the standard AdK system in implicit solvent.""" - self.universe = MDAnalysis.Universe(PSF, DCD) - self.atom = self.universe.atoms[1000] # Leu67:CG - self.known_pos = np.array([3.94543672, -12.4060812, -7.26820087], - dtype=np.float32) - - # VALID - def tearDown(self): - del self.universe - del self.atom - del self.known_pos - - # VALID - def test_attributes_names(self): - a = self.atom - assert_equal(a.name, 'CG') - assert_equal(a.resname, 'LEU') - - def test_setting_attribute_name(self): - self.atom.name = 'AA' - assert_equal(self.atom.name, 'AA') - - def test_setting_attribute_type(self): - self.atom.type = 'Z' - assert_equal(self.atom.type, 'Z') - - #INVALID - @skip - def test_setting_attribute_mass(self): - assert_equal(self.atom.mass, 13) - - def test_setting_attributes_charge(self): - self.atom.charge = 6 - assert_equal(self.atom.charge, 6) - - # VALID - def test_attributes_positions(self): - a = self.atom - # new position property (mutable) - assert_almost_equal(a.position, self.known_pos) - pos = a.position + 3.14 - a.position = pos - assert_almost_equal(a.position, pos) - - # VALID: does this selection style generalize under new topology system" - def test_atom_selection(self): - asel = self.universe.select_atoms('atom 4AKE 67 CG').atoms[0] - assert_equal(self.atom, asel) - - # VALID - def test_hierarchy(self): - u = self.universe - a = self.atom - assert_equal(a.segment, u.s4AKE) - assert_equal(a.residue, u.residues[66]) - - # VALID: shows we need to raise TypError here - @skip - def test_bad_add(self): - def bad_add(): - return self.atom + 1 - - assert_raises(TypeError, bad_add) - - # VALID - def test_add_AG(self): - ag = self.universe.atoms[:2] - - ag2 = self.atom + ag - - for at in [self.atom, ag[0], ag[1]]: - assert_equal(at in ag2, True) - - # VALID - def test_no_velo(self): - def lookup_velo(): - return self.atom.velocity - - assert_raises(NoDataError, lookup_velo) - - def test_bonded_atoms(self): - at = self.universe.atoms[0] - ref = [b.partner(at) for b in at.bonds] - assert_equal(ref, list(at.bonded_atoms)) - - # VALID - @raises(AttributeError) - def test_undefined_occupancy(self): - self.universe.atoms[0].occupancy - - # INVALID: Atom objects are created when doing, e.g. self.universe.atoms[0]. - # attributes are not propagated to tables of attributes in this way - @skip - def test_set_undefined_occupancy(self): - self.universe.atoms[0].occupancy = .5 - assert_equal(self.universe.atoms[0].occupancy, .5) - assert_equal(self.universe.atoms[1].occupancy, 1) - - -# VALID: these attributes exist always and are pulled from trajectory; -# they give NoDataError if the reader doesn't give this information -class TestAtomNoForceNoVel(TestCase): - def setUp(self): - self.u = MDAnalysis.Universe(XYZ_mini) - self.a = self.u.atoms[0] - - def tearDown(self): - del self.u - - def test_velocity_fail(self): - assert_raises(NoDataError, getattr, self.a, 'velocity') - - def test_force_fail(self): - assert_raises(NoDataError, getattr, self.a, 'force') - - def test_velocity_set_fail(self): - assert_raises(NoDataError, setattr, self.a, 'velocity', - [1.0, 1.0, 1.0]) - - def test_force_set_fail(self): - assert_raises(NoDataError, setattr, self.a, 'force', [1.0, 1.0, 1.0]) - - -class TestAtomGroup(TestCase): - """Tests of AtomGroup; selections are tested separately.""" - # all tests are done with the AdK system (PSF and DCD) sequence: - # http://www.uniprot.org/uniprot/P69441.fasta - # >sp|P69441|KAD_ECOLI Adenylate kinase OS=Escherichia coli (strain K12) GN=adk PE=1 SV=1 - ref_adk_sequence = ( - "MRIILLGAPGAGKGTQAQFIMEKYGIPQISTGDMLRAAVKSGSELGKQAKDIMDAGKLVT" - "DELVIALVKERIAQEDCRNGFLLDGFPRTIPQADAMKEAGINVDYVLEFDVPDELIVDRI" - "VGRRVHAPSGRVYHVKFNPPKVEGKDDVTGEELTTRKDDQEETVRKRLVEYHQMTAPLIG" - "YYSKEAEAGNTKYAKVDGTKPVAEVRADLEKILG" - ) - - # VALID - @dec.skipif(parser_not_found('DCD'), - 'DCD parser not available. Are you using python 3?') - def setUp(self): - """Set up the standard AdK system in implicit solvent.""" - self.universe = MDAnalysis.Universe(PSF, DCD) - self.ag = self.universe.atoms # prototypical AtomGroup - self.dih_prec = 2 - - # INVALID: cannot use class in core to build AtomGroups for any Universe - @skip - def test_newAtomGroup(self): - newag = MDAnalysis.core.groups.AtomGroup(self.ag[1000:2000:200]) - assert_equal(type(newag), type(self.ag), - "Failed to make a new AtomGroup: type mismatch") - assert_equal(newag.n_atoms, len(self.ag[1000:2000:200])) - assert_equal(newag.n_residues, 5) - # check any special method - assert_almost_equal(newag.total_mass(), 40.044999999999995) - - - def test_getitem_int(self): - assert_equal(self.universe.atoms[0].ix, self.universe.atoms.ix[0]) - - def test_getitem_slice(self): - assert_array_equal(self.universe.atoms[0:4].ix, - self.universe.atoms.ix[:4]) - - def test_getitem_slice2(self): - assert_equal(self.universe.atoms[0:8:2].ix, - self.universe.atoms.ix[0:8:2]) - - # INVALID: we don't support getitem with names anymore. - # it could be supported by making the Atomnames topologyattr transplant - # a wrapped __getitem__ onto AtomGroup - @skip - def test_getitem_str(self): - ag1 = self.universe.atoms['HT1'] - # select_atoms always returns an AtomGroup even if single result - ag2 = self.universe.select_atoms('name HT1')[0] - assert_equal(ag1, ag2) - - # INVALID: can't use `AtomGroup` class directly; - # getitem with list works fine though - @skip - def test_getitem_list(self): - sel = [0, 1, 4] - ag1 = self.universe.atoms[sel] - ag2 = AtomGroup([self.universe.atoms[i] for i in sel]) - assert_equal(ag1._atoms, ag2._atoms) - - # INVALID: can't use `AtomGroup` class directly; - # getitem with array works fine though - @skip - def test_getitem_nparray(self): - sel = np.arange(5) - ag1 = self.universe.atoms[sel] - ag2 = AtomGroup([self.universe.atoms[i] for i in sel]) - assert_equal(ag1._atoms, ag2._atoms) - - # VALID: we should raise TypeError for this; currently get IndexError - @skip - def test_getitem_TE(self): - d = {'A': 1} - raises(TypeError, self.universe.atoms.__getitem__, d) - - # INVALID: can't build AtomGroup directly like this - @skip - def test_bad_make(self): - raises(TypeError, AtomGroup, ['these', 'are', 'not', 'atoms']) - - # VALID - def test_n_atoms(self): - assert_equal(self.ag.n_atoms, 3341) - - # VALID - def test_n_residues(self): - assert_equal(self.ag.n_residues, 214) - - # VALID - def test_n_segments(self): - assert_equal(self.ag.n_segments, 1) - - # VALID - def test_resids_dim(self): - assert_equal(len(self.ag.resids), len(self.ag)) - - # INVALID: this topology doesn't include resnums, so we don't have any - @skip - def test_resnums_dim(self): - assert_equal(len(self.ag.resnums), len(self.ag)) - - # VALID - def test_segids_dim(self): - assert_equal(len(self.ag.segids), len(self.ag)) - - # VALID - def test_len(self): - """testing that len(atomgroup) == atomgroup.n_atoms""" - assert_equal(len(self.ag), self.ag.n_atoms, "len and n_atoms disagree") - - # VALID - def test_center_of_geometry(self): - assert_array_almost_equal(self.ag.center_of_geometry(), - np.array([-0.04223963, 0.0141824, - -0.03505163], dtype=np.float32)) - - # INVALID: AtomGroup only has center_of_mass method if topology has masses - @skip - def test_center_of_mass(self): - assert_array_almost_equal(self.ag.center_of_mass(), - np.array([-0.01094035, 0.05727601, - -0.12885778])) - - # VALID - def test_coordinates(self): - assert_array_almost_equal( - self.ag.positions[1000:2000:200], - np.array([[3.94543672, -12.4060812, -7.26820087], - [13.21632767, 5.879035, -14.67914867], - [12.07735443, -9.00604534, 4.09301519], - [11.35541916, 7.0690732, -0.32511973], - [-13.26763439, 4.90658951, 10.6880455]], - dtype=np.float32)) - - # VALID - def test_principal_axes(self): - assert_array_almost_equal( - self.ag.principal_axes(), - np.array([[-9.99925632e-01, 1.21546132e-02, 9.98264877e-04], - [1.20986911e-02, 9.98951474e-01, -4.41539838e-02], - [1.53389276e-03, 4.41386224e-02, 9.99024239e-01]])) - - # VALID: need to set precision to correct within 5 decimal points - @skip - def test_total_charge(self): - assert_almost_equal(self.ag.total_charge(), -4.0) - - # VALID: need to set precision to correct within 2 decimal points - # perhaps we should use a higher precision float for masses in - # PSF parser - @skip - def test_total_mass(self): - assert_almost_equal(self.ag.total_mass(), 23582.043) - - # VALID - def test_indices_ndarray(self): - assert_equal(isinstance(self.ag.indices, np.ndarray), True) - - # VALID - def test_indices(self): - assert_array_equal(self.ag.indices[:5], np.array([0, 1, 2, 3, 4])) - - # VALID - def test_resids_ndarray(self): - assert_equal(isinstance(self.ag.resids, np.ndarray), True) - - # VALID - def test_resids(self): - assert_array_equal(self.ag.residues.resids, np.arange(1, 215)) - - # INVALID: this topology doesn't give resnums, so no such property exists - @skip - def test_resnums_ndarray(self): - assert_equal(isinstance(self.ag.residues.resnums, np.ndarray), True) - - # INVALID: see above - @skip - def test_resnums(self): - assert_array_equal(self.ag.residues.resnums, np.arange(1, 215)) - - # VALID - def test_resnames_ndarray(self): - assert_equal(isinstance(self.ag.residues.resnames, np.ndarray), True) - - # VALID - def test_resnames(self): - resnames = self.ag.residues.resnames - assert_array_equal(resnames[0:3], np.array(["MET", "ARG", "ILE"])) - - # VALID - def test_names_ndarray(self): - assert_equal(isinstance(self.ag.names, np.ndarray), True) - - # VALID - def test_names(self): - names = self.ag.names - assert_array_equal(names[0:3], np.array(["N", "HT1", "HT2"])) - - # VALID - def test_segids_ndarray(self): - assert_equal(isinstance(self.ag.segids, np.ndarray), True) - - # VALID - def test_segids(self): - segids = self.ag.segids - assert_array_equal(segids[0], np.array(["4AKE"])) - - # INVALID: this topology doesn't give masses - @skip - def test_masses_ndarray(self): - assert_equal(isinstance(self.ag.masses, np.ndarray), True) - - # INVALID: this topology doesn't give masses - @skip - def test_masses(self): - masses = self.ag.masses - assert_array_equal(masses[0:3], np.array([14.007, 1.008, 1.008])) - - # VALID - def test_charges_ndarray(self): - assert_equal(isinstance(self.ag.charges, np.ndarray), True) - - # VALID - def test_charges(self): - assert_array_almost_equal(self.ag.charges[1000:2000:200], - np.array([-0.09, 0.09, -0.47, 0.51, 0.09])) - - # INVALID: this topology doesn't give atom radii - @skip - def test_radii_ndarray(self): - assert_equal(isinstance(self.ag.radii, np.ndarray), True) - - # INVALID: this topology doesn't give atom radii - @skip - def test_radii(self): - radii = self.ag.radii - assert_array_equal(radii[0:3], np.array([None, None, None])) - - # INVALID: this topology doesn't give bfactors - @skip - def test_bfactors_ndarray(self): - assert_equal(isinstance(self.ag.bfactors, np.ndarray), True) - - # INVALID: this topology doesn't give bfactors - @skip - def test_bfactors(self): - bfactors = self.ag.bfactors # property, not method! - assert_array_equal(bfactors[0:3], np.array([None, None, None])) - - # INVALID: we give AttributeError since occupancies attribute not present - # if topology doesn't provide them - @skip - def test_occupancies(self): - assert_raises(NoDataError, getattr, self.ag, 'occupancies') - self.ag.occupancies = 0.25 - assert_array_almost_equal(self.ag.occupancies, - np.ones(len(self.ag)) * 0.25) - - # INVALID: AtomGroup no longer gets a sequence method; only in ResidueGroup - # if resnames in topology - @skip - def test_sequence_from_atoms(self): - p = self.universe.select_atoms("protein") - assert_equal(p.sequence(format="string"), - p.residues.sequence(format="string"), - err_msg="sequence() yields different results for " - "residues and atoms") - - # VALID - def test_sequence_string(self): - p = self.universe.select_atoms("protein") - assert_equal(p.residues.sequence(format="string"), - self.ref_adk_sequence) - - # VALID - def test_sequence_SeqRecord(self): - p = self.universe.select_atoms("protein") - s = p.residues.sequence(format="SeqRecord", - id="P69441", name="KAD_ECOLI Adenylate kinase", - description="EcAdK from pdb 4AKE") - assert_equal(s.id, "P69441") - assert_equal(s.seq.tostring(), self.ref_adk_sequence) - - # VALID - def test_sequence_SeqRecord_default(self): - p = self.universe.select_atoms("protein") - s = p.residues.sequence(id="P69441", name="KAD_ECOLI Adenylate kinase", - description="EcAdK from pdb 4AKE") - assert_equal(s.id, "P69441") - assert_equal(s.seq.tostring(), self.ref_adk_sequence) - - # VALID - def test_sequence_Seq(self): - p = self.universe.select_atoms("protein") - s = p.residues.sequence(format="Seq") - assert_equal(s.tostring(), self.ref_adk_sequence) - - # INVALID: currently raises AttributeError - @skip - def test_sequence_nonIUPACresname(self): - """test_sequence_nonIUPACresname: non recognized amino acids raise - ValueError""" - # fake non-IUPAC residue name for this test - residues = self.universe.select_atoms("resname MET").residues - residues.resnames = "MSE" - - def wrong_res(): - self.universe.atoms.sequence() - - assert_raises(ValueError, wrong_res) - - # INVALID: cannot create Atoms without a Universe - @skip - def test_no_uni_1(self): - at1 = Atom(1, 'dave', 'C', 'a', 1, 1, 0.1, 0.0) - at2 = Atom(2, 'dave', 'C', 'a', 1, 1, 0.1, 0.0) - ag = AtomGroup([at1, at2]) - assert_raises(NoDataError, getattr, ag, 'universe') - - # INVALID: cannot create AtomGroups without a Universe - @skip - def test_no_uni_2(self): - ag = AtomGroup([]) - assert_raises(NoDataError, getattr, ag, 'universe') - - # VALID: AtomGroup should raise TypeError when adding with non-AtomGroup - @skip - def test_bad_add_AG(self): - def bad_add(): - return self.ag + [1, 2, 3] - assert_raises(TypeError, bad_add) - - # INVALID: cannot create AtomGroup without a Universe - @skip - def test_bool_false(self): - # Issue #304 - ag = AtomGroup([]) - assert_equal(bool(ag), False) - - # VALID - def test_bool_true(self): - # Issue #304 - ag = self.universe.atoms[:3] - assert_equal(bool(ag), True) - - # VALID - def test_repr(self): - # Should make sure that the user facing info stays as expected - assert_equal(repr(self.ag), "") - - # INVALID: set resnums with `ag.residues.resnums = new` - # also resnums won't be present with this topology, perhaps - # Issue 202 following 4 tests - @skip - def test_set_resnum_single(self): - ag = self.universe.atoms[:3] - new = 5 - ag.set_resnums(new) - for at in ag: - assert_equal(at.resnum, new) - assert_equal(all(ag.resnums == new), True) - - # INVALID: set resnums with `ag.residues.resnums = new` - # also resnums won't be present with this topology, perhaps - @skip - def test_set_resnum_many(self): - ag = self.universe.atoms[:3] - new = [22, 23, 24] - ag.set_resnums(new) - for at, v in zip(ag, new): - assert_equal(at.resnum, v) - assert_equal(all(ag.resnums == new), True) - - def test_set_resname_single(self): - ag = self.universe.atoms[:3] - new = 'abc' - ag.residues.resnames = new - for at in ag: - assert_equal(at.resname, new) - assert_equal(all(ag.resnames == new), True) - - # INVALID: set resnames with `ag.residues.resnames = new` - @skip - def test_set_resname_many(self): - ag = self.universe.atoms[:3] - new = ['aa', 'bb', 'cc'] - ag.set_resnames(new) - for at, v in zip(ag, new): - assert_equal(at.resname, v) - assert_equal(all(ag.resnames == new), True) - - # TODO: add all other methods except select_atoms(), see - # test_atomselections.py - # INVALID: cannot create Atoms like this; must come from a Universe - @skip - def test_set_charges(self): - # Charges are initially 0 - at1 = Atom(1, 'dave', 'C', 'a', 1, 1, 0.1, 0.0) - at2 = Atom(2, 'dave', 'C', 'a', 1, 1, 0.1, 0.0) - ag = AtomGroup([at1, at2]) - - charges = [1.0, 2.0] - ag.set_charges(charges) - for at, val in zip(ag, charges): - assert_equal(at.charge, val) - - # INVALID: cannot create Atoms like this; must come from a Universe - @skip - def test_set_radii(self): - at1 = Atom(1, 'dave', 'C', 'a', 1, 1, 0.1, 0.0) - at2 = Atom(2, 'dave', 'C', 'a', 1, 1, 0.1, 0.0) - ag = AtomGroup([at1, at2]) - - radii = [1.0, 2.0] - ag.set_radii(radii) - for at, val in zip(ag, radii): - assert_equal(at.radius, val) - - # INVALID: cannot create Atoms like this; must come from a Universe - @skip - def test_set_bfactors(self): - at1 = Atom(1, 'dave', 'C', 'a', 1, 1, 0.1, 0.0) - at2 = Atom(2, 'dave', 'C', 'a', 1, 1, 0.1, 0.0) - ag = AtomGroup([at1, at2]) - - bfacs = [1.0, 2.0] - ag.set_bfactors(bfacs) - for at, val in zip(ag, bfacs): - assert_equal(at.bfactor, val) - - # add new methods here... - # VALID - def test_packintobox_badshape(self): - ag = self.universe.atoms[:10] - box = np.zeros(9, dtype=np.float32).reshape(3, 3) - - def badpack(a): - return a.pack_into_box(box=box) - - assert_raises(ValueError, badpack, ag) - - # VALID - def test_packintobox_noshape(self): - ag = self.universe.atoms[:10] - - def badpack(a): - return a.pack_into_box() - - assert_raises(ValueError, badpack, ag) - - # VALID - def test_packintobox(self): - """test AtomGroup.pack_into_box(): Tests application of periodic boundary - conditions on coordinates - - Reference system doesn't have dimensions, so an arbitrary box is - imposed on the system - - """ - u = self.universe - u.trajectory.rewind() # just to make sure... - ag = u.atoms[1000:2000:200] - # Provide arbitrary box - ag.pack_into_box(box=np.array([5., 5., 5.], dtype=np.float32)) - assert_array_almost_equal( - ag.positions, - np.array([[3.94543672, 2.5939188, 2.73179913], - [3.21632767, 0.879035, 0.32085133], - [2.07735443, 0.99395466, 4.09301519], - [1.35541916, 2.0690732, 4.67488003], - [1.73236561, 4.90658951, 0.6880455]], dtype=np.float32)) - - # INVALID: no `_atoms` property for ResidueGroup or AtomGroup; use `.atoms` - # which won't work because they are compared by reference in the default. - # But is also doesn't work to use `ix` as they will differ. - @skip - def test_residues(self): - u = self.universe - assert_equal(u.residues[100]._atoms, - u.select_atoms('resname ILE and resid 101')._atoms, - "Direct selection from residue group does not match " - "expected I101.") - - def test_segments(self): - u = self.universe - assert_equal(len(u.segments.s4AKE.atoms), - len(u.select_atoms('segid 4AKE').atoms), - "Direct selection of segment 4AKE from segments failed.") - - # VALID - def test_index_integer(self): - u = self.universe - a = u.atoms[100] - assert_(isinstance(a, Atom), "integer index did not return Atom") - - # VALID - def test_index_slice(self): - u = self.universe - a = u.atoms[100:200:10] - assert_(isinstance(a, AtomGroup), - "slice index did not return AtomGroup") - - # VALID - def test_index_slice_empty(self): - u = self.universe - assert_array_equal(u.atoms[0:0], [], - "making an empty AtomGroup failed") - - # VALID: - def test_index_advancedslice(self): - u = self.universe - aslice = [0, 10, 20, -1, 10] - ag = u.atoms[aslice] - assert_(isinstance(ag, AtomGroup), - "advanced slicing does not produce a AtomGroup") - assert_equal(ag[1], ag[-1], "advanced slicing does not preserve order") - - # VALID - def test_boolean_indexing(self): - # index an array with a sequence of bools - # issue #282 - sel = np.array([True, False, True]) - ag = self.universe.atoms[10:13] - ag2 = ag[sel] - assert_equal(len(ag2), 2) - for at in [ag[0], ag[2]]: - assert_equal(at in ag2, True) - - # VALID - def test_boolean_indexing_2(self): - # index an array with a sequence of bools - # issue #282 - sel = [True, False, True] - ag = self.universe.atoms[10:13] - ag2 = ag[sel] - assert_equal(len(ag2), 2) - for at in [ag[0], ag[2]]: - assert_equal(at in ag2, True) - - # VALID - def test_bool_IE(self): - # indexing with empty list doesn't run foul of bool check - sel = [] - ag = self.universe.atoms[10:30] - ag2 = ag[sel] - assert_equal(len(ag2), 0) - - # VALID - def test_dihedral_ValueError(self): - """test that AtomGroup.dihedral() raises ValueError if not exactly - 4 atoms given""" - nodih = self.universe.select_atoms("resid 3:10") - assert_raises(ValueError, getattr, nodih, 'dihedral') - nodih = self.universe.select_atoms("resid 3:5") - assert_raises(ValueError, getattr, nodih, 'dihedral') - - # VALID - def test_improper(self): - u = self.universe - u.trajectory.rewind() # just to make sure... - peptbond = u.select_atoms("atom 4AKE 20 C", "atom 4AKE 21 CA", - "atom 4AKE 21 N", "atom 4AKE 21 HN") - assert_almost_equal(peptbond.improper.value(), 168.52952575683594, - self.dih_prec, - "Peptide bond improper dihedral for M21 " - "calculated wrongly.") - - # VALID - def test_dihedral_equals_improper(self): - u = self.universe - u.trajectory.rewind() # just to make sure... - peptbond = u.select_atoms("atom 4AKE 20 C", "atom 4AKE 21 CA", - "atom 4AKE 21 N", "atom 4AKE 21 HN") - assert_equal(peptbond.improper.value(), peptbond.dihedral.value(), - "improper() and proper dihedral() give different results") - - # INVALID: we don't support getting residues by resid as attributes of segments anymore - @skip - def test_bond(self): - self.universe.trajectory.rewind() # just to make sure... - sel2 = self.universe.s4AKE.r98.select_atoms("name OE1", "name OE2") - assert_almost_equal(sel2.bond.value(), 2.1210737228393555, 3, - "distance of Glu98 OE1--OE2 wrong") - - # INVALID: we don't support getting residues by resid as attributes of segments anymore - @skip - def test_bond_pbc(self): - self.universe.trajectory.rewind() - sel2 = self.universe.s4AKE.r98.select_atoms("name OE1", "name OE2") - assert_almost_equal(sel2.bond.value(pbc=True), 2.1210737228393555, 3, - "distance of Glu98 OE1--OE2 wrong") - - # VALID - def test_bond_ValueError(self): - ag = self.universe.atoms[:4] - assert_raises(ValueError, getattr, ag, 'bond') - - # INVALID: we don't support getting residues by resid as attributes of segments anymore - @skip - def test_angle(self): - self.universe.trajectory.rewind() # just to make sure... - sel3 = self.universe.s4AKE.r98.select_atoms("name OE1", "name CD", - "name OE2") - assert_almost_equal(sel3.angle.value(), 117.46187591552734, 3, - "angle of Glu98 OE1-CD-OE2 wrong") - - # VALID - def test_angle_ValueError(self): - ag = self.universe.atoms[:2] - assert_raises(ValueError, getattr, ag, 'angle') - - # INVALID: this topology has no masses, so no `shape_parameter` method - @skip - def test_shape_parameter(self): - s = self.universe.s4AKE.shape_parameter() - assert_almost_equal(s, 0.00240753939086033, 6) - - # INVALID: this topology has no masses, so no `asphericity` method - @skip - def test_asphericity(self): - a = self.universe.s4AKE.asphericity() - assert_almost_equal(a, 0.020227504542775828, 6) - - # TODO: tests for the coordinate manipulation methods - # - transform - # - translate - # - rotate - # - rotateby - - # VALID - def test_positions(self): - ag = self.universe.select_atoms("bynum 12:42") - pos = ag.positions + 3.14 - ag.positions = pos - # should work - assert_almost_equal(ag.positions, pos, - err_msg="failed to update atoms 12:42 position " - "to new position") - - def set_badarr(pos=pos): - # create wrong size array - badarr = np.random.random((pos.shape[0] - 1, pos.shape[1] - 1)) - ag.positions = badarr - assert_raises(ValueError, set_badarr) - - # INVALID: no `set_resids` method for AtomGroup; use - # `AtomGroup.residues.resids` property instead - @skip - def test_set_resids(self): - ag = self.universe.select_atoms("bynum 12:42") - resid = 999 - ag.set_resids(resid) - # check individual atoms - assert_equal([a.resid for a in ag], - resid * np.ones(ag.n_atoms), - err_msg="failed to set_resid atoms 12:42 to same resid") - # check residues - assert_equal(ag.resids, 999 * np.ones(ag.n_residues), - err_msg="failed to set_resid of residues belonging to " - "atoms 12:42 to same resid") - - # INVALID: no `set_names` method for AtomGroup; use `names` property instead - @skip - def test_set_names(self): - ag = self.universe.atoms[:2] - names = ['One', 'Two'] - ag.set_names(names) - for a, b in zip(ag, names): - assert_equal(a.name, b) - - # INVALID: change name to not match name above - # also, no `set_resids` method of AtomGroups; use - # `AtomGroup.residues.resids` property instead - @skip - def test_set_resids(self): - """test_set_resid: set AtomGroup resids on a per-atom basis""" - ag = self.universe.select_atoms("bynum 12:42") - resids = np.array([a.resid for a in ag]) + 1000 - ag.set_resids(resids) - # check individual atoms - assert_equal([a.resid for a in ag], resids, - err_msg="failed to set_resid atoms 12:42 to " - "resids {0}".format(resids)) - # check residues - assert_equal(ag.residues.resids, np.unique(resids), - err_msg="failed to set_resid of residues belonging to " - "atoms 12:42 to same resid") - - # INVALID: setting resids does not set residue membership for atoms. - # must change an atom's resindex, which is guaranteed to be unique for each - # residue - # also no `set_resids` method for AtomGroups - @skip - def test_merge_residues(self): - ag = self.universe.select_atoms("resid 12:14") - nres_old = self.universe.atoms.n_residues - natoms_old = ag.n_atoms - ag.set_resids(12) # merge all into one with resid 12 - nres_new = self.universe.atoms.n_residues - r_merged = self.universe.select_atoms("resid 12:14").residues - natoms_new = self.universe.select_atoms("resid 12").n_atoms - assert_equal(len(r_merged), 1, - err_msg="set_resid failed to merge residues: " - "merged = {0}".format(r_merged)) - assert_equal(nres_new, nres_old - 2, - err_msg="set_resid failed to merge residues: " - "merged = {0}".format(r_merged)) - assert_equal(natoms_new, natoms_old, - err_msg="set_resid lost atoms on merge".format(r_merged)) - - # INVALID: no `set_masses` method for AtomGroup. `AtomGroup.masses` property only exists - # if topology already has it, or if the topologyattr for masses is added - @skip - def test_set_masses(self): - ag = self.universe.select_atoms("bynum 12:42 and name H*") - mass = 2.0 - ag.set_masses(mass) - # check individual atoms - assert_equal([a.mass for a in ag], - mass * np.ones(ag.n_atoms), - err_msg="failed to set_mass H* atoms in resid " - "12:42 to {0}".format(mass)) - - # INVALID: no `set_segids` method of AtomGroup. Residues are members - # of segments, so one must do `AtomGroup.residues.segindices = u.LID.segindex` - @skip - def test_set_segids(self): - u = self.universe - u.select_atoms("(resid 1-29 or resid 60-121 or resid 160-214)").set_segids("CORE") - u.select_atoms("resid 122-159").set_segids("LID") - u.select_atoms("resid 30-59").set_segids("NMP") - assert_equal(u.segments.segids, ["CORE", "NMP", "CORE", "LID", "CORE"], - err_msg="failed to change segids = {0}".format(u.atoms.segids)) - - # INVALID: no `set_masses` method for AtomGroup. `AtomGroup.masses` property only exists - # if topology already has it, or if the topologyattr for masses is added - @skip - def test_wronglen_set(self): - """Give the setter function a list of wrong length""" - assert_raises(ValueError, self.ag.set_masses, [0.1, 0.2]) - - # VALID - # instant selectors - @attr("issue") - def test_nonexistent_instantselector_raises_AttributeError(self): - def access_nonexistent_instantselector(): - self.universe.atoms.NO_SUCH_ATOM - assert_raises(AttributeError, access_nonexistent_instantselector) - - # VALID - def test_atom_order(self): - assert_equal(self.universe.atoms.indices, - sorted(self.universe.atoms.indices)) - - -class TestAtomGroupNoTop(TestCase): - - # VALID - @dec.skipif(parser_not_found('DCD'), - 'DCD parser not available. Are you using python 3?') - def setUp(self): - self.u = MDAnalysis.Universe(PSF_notop, DCD) - self.ag = self.u.atoms[:10] - - # VALID - def tearDown(self): - del self.u - del self.ag - - # INVALID: no bonds attribute if not bonds in topology - @skip - def test_nobonds(self): - assert_equal(self.ag.bonds, []) - - # INVALID: no angles attribute if not bonds in topology - @skip - def test_noangles(self): - assert_equal(self.ag.angles, []) - - # INVALID: no dihedrals attribute if not bonds in topology - @skip - def test_nodihedrals(self): - assert_equal(self.ag.dihedrals, []) - - # INVALID: no impropers attribute if not bonds in topology - @skip - def test_noimps(self): - assert_equal(self.ag.impropers, []) - - # Because I'm messing with atom info, I've put these here separated from - # other tests - # INVALID: AtomGroups keep no caches now - @skip - def test_clear_cache(self): - self.ag._clear_caches() - - assert_equal(self.ag._cache, dict()) - - # INVALID: no caches in AtomGroups anymore - @skip - def test_rebuild_cache_residues(self): - assert_equal(len(self.ag.residues), 1) - - # Mess with stuff, add a different residues and segment for the first - # atom - self.ag[0].residue = self.u.atoms[100].residue - - # There's actually 2 residues now, but because of cache this isn't - # detected - assert_equal(len(self.ag.residues), 1) - - # After cache rebuild second residue is finally seen - self.ag._rebuild_caches() - assert_equal(len(self.ag.residues), 2) - - # INVALID: no caches in AtomGroups anymore - @skip - def test_rebuild_cache_segments(self): - # This test is similar to above, but a second segment has to be taken - # from a new universe - assert_equal(len(self.ag.segments), 1) - - u2 = MDAnalysis.Universe(PSF_notop, DCD) - self.ag[0].segment = u2.atoms[0].segment - - assert_equal(len(self.ag.segments), 1) - self.ag._rebuild_caches() - assert_equal(len(self.ag.segments), 2) - - # INVALID: no caches in AtomGroups anymore - @skip - def test_atom_cachesize_change(self): - # By default 10,000 atoms are required to necessitate cache lookup, we - # can change this though - ag = self.u.atoms[:100] - # run a __contains__ query - self.u.atoms[10] in ag - # Check that cache wasn't used - assert_equal('atoms' in ag._cache, False) - ag._atomcache_size = 50 # now will make cache if size > 50 - # Run another query - self.u.atoms[10] in ag - # Check if cache was built this time - assert_equal('atoms' in ag._cache, True) - - # INVALID: no caches in AtomGroups anymore - @skip - def test_atomcache_use(self): - # Tests that lookup with 'atoms' cache works - ag = self.u.atoms[:100] - ag._atomcache_size = 50 - assert_equal(self.u.atoms[50] in ag, True) - - # INVALID: no caches in AtomGroups anymore - @skip - def test_rebuild_atomcache_no(self): - # Don't always add atoms into cache - ag = self.u.atoms[:100] - ag._rebuild_caches() - assert_equal('atoms' in ag._cache, False) - - # INVALID: no caches in AtomGroups anymore - @skip - def test_rebuild_atomcache(self): - # Tests that 'atoms' is built into cache if size is enough - ag = self.u.atoms[:100] - ag._atomcache_size = 50 - ag._rebuild_caches() - assert_equal('atoms' in ag._cache, True) - - # INVALID: can only set dimensions from the Universe - @skip - def test_set_dimensions(self): - u = MDAnalysis.Universe(PSF, DCD) - box = np.array([10, 11, 12, 90, 90, 90]) - u.atoms.dimensions = np.array([10, 11, 12, 90, 90, 90]) - assert_allclose(u.dimensions, box) - assert_allclose(u.atoms.dimensions, box) - - -class TestUniverseSetTopology(TestCase): - """Tests setting of bonds/angles/dihedrals/impropers from Universe.""" - - # VALID - @dec.skipif(parser_not_found('DCD'), - 'DCD parser not available. Are you using python 3?') - def setUp(self): - self.u = MDAnalysis.Universe(PSF, DCD) - - # VALID - def tearDown(self): - del self.u - - # INVALID: universe doesn't have bonds; AtomGroups do, though - @skip - def test_set_bonds(self): - assert_equal(len(self.u.bonds), 3365) - assert_equal(len(self.u.atoms[0].bonds), 4) - - self.u.bonds = [] - - assert_equal(len(self.u.bonds), 0) - assert_equal(len(self.u.atoms[0].bonds), 0) - - # INVALID: universe doesn't have angles; AtomGroups do, though - @skip - def test_set_angles(self): - assert_equal(len(self.u.angles), 6123) - assert_equal(len(self.u.atoms[0].angles), 9) - - self.u.angles = [] - - assert_equal(len(self.u.angles), 0) - assert_equal(len(self.u.atoms[0].angles), 0) - - # INVALID: universe doesn't have dihedrals; AtomGroups do, though - @skip - def test_set_dihedrals(self): - assert_equal(len(self.u.dihedrals), 8921) - assert_equal(len(self.u.atoms[0].dihedrals), 14) - - self.u.dihedrals = [] - - assert_equal(len(self.u.dihedrals), 0) - assert_equal(len(self.u.atoms[0].dihedrals), 0) - - # INVALID: universe doesn't have impropers; AtomGroups do, though - @skip - def test_set_impropers(self): - assert_equal(len(self.u.impropers), 541) - assert_equal(len(self.u.atoms[4].impropers), 1) - - self.u.impropers = [] - - assert_equal(len(self.u.impropers), 0) - assert_equal(len(self.u.atoms[4].impropers), 0) - - # Test deleting topology information - # In general, access it to make sure it's built - # Assert it's in cache - # Delete - # Assert it's not in cache - - # INVALID: universe has no direct bonds access - @skip - def test_bonds_delete(self): - self.u.bonds - self.u.atoms[0].bonds - - assert_equal('bonds' in self.u._cache, True) - assert_equal('bondDict' in self.u._cache, True) - - del self.u.bonds - - assert_equal('bonds' in self.u._cache, False) - assert_equal('bondDict' in self.u._cache, False) - - # INVALID: universe has no direct angles access - @skip - def test_angles_delete(self): - self.u.angles - self.u.atoms[0].angles - - assert_equal('angles' in self.u._cache, True) - assert_equal('angleDict' in self.u._cache, True) - - del self.u.angles - - assert_equal('angles' in self.u._cache, False) - assert_equal('angleDict' in self.u._cache, False) - - # INVALID: universe has no direct dihedrals access - @skip - def test_dihedrals_delete(self): - self.u.dihedrals - self.u.atoms[0].dihedrals - - assert_equal('dihedrals' in self.u._cache, True) - assert_equal('dihedralDict' in self.u._cache, True) - - del self.u.dihedrals - - assert_equal('dihedrals' in self.u._cache, False) - assert_equal('dihedralDict' in self.u._cache, False) - - # INVALID: universe has no direct impropers access - @skip - def test_impropers_delete(self): - self.u.impropers - self.u.atoms[0].impropers - - assert_equal('impropers' in self.u._cache, True) - assert_equal('improperDict' in self.u._cache, True) - - del self.u.impropers - - assert_equal('impropers' in self.u._cache, False) - assert_equal('improperDict' in self.u._cache, False) - - -class TestResidue(TestCase): - # VALID - @dec.skipif(parser_not_found('DCD'), - 'DCD parser not available. Are you using python 3?') - def setUp(self): - self.universe = MDAnalysis.Universe(PSF, DCD) - self.res = self.universe.residues[100] - - # INVALID: a Universe has its own Residue class, so comparing - # to base class definition isn't meaningful - @skip - def test_type(self): - assert_equal(type(self.res), MDAnalysis.core.groups.Residue) - assert_equal(self.res.name, "ILE") - assert_equal(self.res.id, 101) - - # INVALID: residues do not behave like AtomGroups anymore, - # so cannot index in this way; should use `res.atoms[2]` - @skip - def test_index(self): - atom = self.res[2] - assert_equal(type(atom), MDAnalysis.core.groups.Atom) - assert_equal(atom.name, "CA") - assert_equal(atom.index, 1522) - assert_equal(atom.resid, 101) - - # INVALID: residues do not behave like AtomGroups anymore, - # so cannot slice in this way - @skip - def test_slicing(self): - atoms = self.res[2:10:2] - assert_equal(len(atoms), 4) - assert_equal(type(atoms), MDAnalysis.core.groups.AtomGroup) - - # INVALID: residues do not behave like AtomGroups anymore, - # so cannot slice in this way - @skip - def test_advanced_slicing(self): - atoms = self.res[[0, 2, -2, -1]] - assert_equal(len(atoms), 4) - assert_equal(type(atoms), MDAnalysis.core.groups.AtomGroup) - assert_equal(atoms.names, ["N", "CA", "C", "O"]) - - # VALID - def test_atom_order(self): - assert_equal(self.res.atoms.indices, - sorted(self.res.atoms.indices)) - - -class TestResidueGroup(TestCase): - @dec.skipif(parser_not_found('DCD'), - 'DCD parser not available. Are you using python 3?') - # VALID - def setUp(self): - """Set up the standard AdK system in implicit solvent.""" - self.universe = MDAnalysis.Universe(PSF, DCD) - self.rg = self.universe.atoms.residues - - # VALID - def test_newResidueGroup(self): - """test that slicing a ResidueGroup returns a new ResidueGroup - (Issue 135)""" - rg = self.universe.atoms.residues - newrg = rg[10:20:2] - assert_equal(type(newrg), type(rg), - "Failed to make a new ResidueGroup: type mismatch") - assert_equal(len(newrg), len(rg[10:20:2])) - - # VALID - def test_n_atoms(self): - assert_equal(self.rg.n_atoms, 3341) - - # VALID - def test_n_residues(self): - assert_equal(self.rg.n_residues, 214) - - # VALID - def test_resids_dim(self): - assert_equal(len(self.rg.resids), len(self.rg)) - - # INVALID: this topology has no resnums, so no resnums property - @skip - def test_resnums_dim(self): - assert_equal(len(self.rg.resnums), len(self.rg)) - - # VALID - def test_segids_dim(self): - assert_equal(len(self.rg.segids), len(self.rg)) - - # VALID - def test_len(self): - """testing that len(residuegroup) == residuegroup.n_residues""" - assert_equal(len(self.rg), self.rg.n_residues, - "len and n_residues disagree") - - # INVALID: set resids with `ResidueGroup.resids` property; no `set_resids` method - @skip - def test_set_resids(self): - rg = self.universe.select_atoms("bynum 12:42").residues - resid = 999 - rg.set_resids(resid) - # check individual atoms - assert_equal([a.resid for a in rg.atoms], - resid * np.ones(rg.n_atoms), - err_msg="failed to set_resid atoms 12:42 to same resid") - # check residues - assert_equal(rg.resids, resid * np.ones(rg.n_residues), - err_msg="failed to set_resid of residues belonging to " - "atoms 12:42 to same resid") - - # INVALID: set resids with `ResidueGroup.resids` property; no `set_resids` method - @skip - def test_set_resids(self): - """test_set_resid: set ResidueGroup resids on a per-residue basis""" - rg = self.universe.select_atoms("resid 10:18").residues - resids = np.array(rg.resids) + 1000 - rg.set_resids(resids) - # check individual atoms - for r, resid in zip(rg, resids): - assert_equal([a.resid for a in r.atoms], - resid * np.ones(r.n_atoms), - err_msg="failed to set_resid residues 10:18 to same " - "resid in residue {0}\n" - "(resids = {1}\nresidues = {2})".format(r, resids, rg)) - # check residues - # NOTE: need to create a new selection because underlying Residue - # objects are not changed; only Atoms are changed, and Residues - # are rebuilt from Atoms. - rgnew = self.universe.select_atoms("resid 1010:1018").residues - assert_equal(rgnew.resids, np.unique(resids), - err_msg="failed to set_resid of residues belonging to " - "residues 10:18 to new resids") - - # INVALID: set resids with `ResidueGroup.resids` property; no `set_resids` method - @skip - def test_set_resids_updates_self(self): - rg = self.universe.select_atoms("resid 10:18").residues - resids = np.array(rg.resids) + 1000 - rg.set_resids(resids) - assert_equal(rg.resids, np.unique(resids), - err_msg="old selection was not changed in place " - "after set_resid") - - # INVALID: no resnums in this topology, so no resnums property - @skip - def test_set_resnum_single(self): - rg = self.universe.residues[:3] - new = 22 - rg.set_resnums(new) - - assert_equal(all(rg.resnums == new), True) - for r in rg: - assert_equal(r.resnum, new) - - # INVALID: no resnums in this topology, so no resnums property - @skip - def test_set_resnum_many(self): - rg = self.universe.residues[:3] - new = [22, 23, 24] - rg.set_resnums(new) - - assert_equal(all(rg.resnums == new), True) - for r, v in zip(rg, new): - assert_equal(r.resnum, v) - - # INVALID: no resnums in this topology, so no resnums property - @skip - def test_set_resnum_ValueError(self): - rg = self.universe.residues[:3] - new = [22, 23, 24, 25] - - assert_raises(ValueError, rg.set_resnums, new) - - # INVALID: no `set_resnames` method; use `resnames` property directly - @skip - def test_set_resname_single(self): - rg = self.universe.residues[:3] - new = 'newname' - - rg.set_resnames(new) - assert_equal(all(rg.resnames == new), True) - for r in rg: - assert_equal(r.name, new) - - # INVALID: no `set_resnames` method; use `resnames` property directly - @skip - def test_set_resname_many(self): - rg = self.universe.residues[:3] - new = ['a', 'b', 'c'] - rg.set_resnames(new) - - assert_equal(all(rg.resnames == new), True) - for r, v in zip(rg, new): - assert_equal(r.name, v) - - # INVALID: no `set_resnames` method; use `resnames` property directly - @skip - def test_set_resname_ValueError(self): - rg = self.universe.residues[:3] - new = ['a', 'b', 'c', 'd'] - - assert_raises(ValueError, rg.set_resnames, new) - - # INVALID: no `set_resids` method; also, residues are not mergeable - # by setting resids; resids are not necessarily unique; atoms must - # have their resindex set to change residue membership - @skip - def test_merge_residues(self): - rg = self.universe.select_atoms("resid 12:14").residues - nres_old = self.universe.atoms.n_residues - natoms_old = rg.n_atoms - rg.set_resids(12) # merge all into one with resid 12 - nres_new = self.universe.atoms.n_residues - r_merged = self.universe.select_atoms("resid 12:14").residues - natoms_new = self.universe.select_atoms("resid 12").n_atoms - assert_equal(len(r_merged), 1, err_msg="set_resid failed to merge " - "residues: merged = {0}".format(r_merged)) - assert_equal(nres_new, nres_old - 2, - err_msg="set_resid failed to merge residues: " - "merged = {0}".format(r_merged)) - assert_equal(natoms_new, natoms_old, err_msg="set_resid lost atoms " - "on merge".format(r_merged)) - - assert_equal(self.universe.residues.n_residues, - self.universe.atoms.n_residues, - err_msg="Universe.residues and Universe.atoms.n_residues " - "do not agree after residue " - "merge.") - - # INVALID: no `set_masses` method; use `masses` property directly - @skip - def test_set_masses(self): - rg = self.universe.select_atoms("bynum 12:42 and name H*").residues - mass = 2.0 - rg.set_masses(mass) - # check individual atoms - assert_equal([a.mass for a in rg.atoms], - mass * np.ones(rg.n_atoms), - err_msg="failed to set_mass H* atoms in resid 12:42 to {0}".format(mass)) - - # VALID - def test_atom_order(self): - assert_equal(self.universe.residues.atoms.indices, - sorted(self.universe.residues.atoms.indices)) - - -class TestSegment(TestCase): - @dec.skipif(parser_not_found('DCD'), - 'DCD parser not available. Are you using python 3?') - - # INVALID: no `set_segids` method for ResidueGroup; c - @skip - def setUp(self): - self.universe = MDAnalysis.Universe(PSF, DCD) - self.universe.residues[:100].set_segids("A") # make up some segments - self.universe.residues[100:150].set_segids("B") - self.universe.residues[150:].set_segids("C") - self.sB = self.universe.segments[1] - - # VALID but temporary - @dec.skipif(parser_not_found('DCD'), - 'DCD parser not available. Are you using python 3?') - def setUp(self): - self.universe = MDAnalysis.Universe(PSF, DCD) - - # INVALID: `Segment` from a particular Universe will not be the same class - # in core; each Universe generates its own set of classes based on topology - @skip - def test_type(self): - assert_equal(type(self.sB), MDAnalysis.core.groups.Segment) - assert_equal(self.sB.name, "B") - - # INVALID: `Segment` doesn't behave as a `ResidueGroup`; - # no index behavior - @skip - def test_index(self): - s = self.sB - res = s[5] - assert_equal(type(res), MDAnalysis.core.groups.Residue) - - # INVALID: `Segment` doesn't behave as a `ResidueGroup`; - # no slicing behavior - @skip - def test_slicing(self): - res = self.sB[5:10] - assert_equal(len(res), 5) - assert_equal(type(res), MDAnalysis.core.groups.ResidueGroup) - - # INVALID: `Segment` doesn't behave as a `ResidueGroup`; - # no slicing behavior - @skip - def test_advanced_slicing(self): - res = self.sB[[3, 7, 2, 4]] - assert_equal(len(res), 4) - assert_equal(type(res), MDAnalysis.core.groups.ResidueGroup) - - # INVALID: no `name` or `id` attribute for `Segment`; only `segid` - @skip - def test_id(self): - assert_equal(self.sB.name, self.sB.id) - - # INVALID: no `name` or `id` attribute for `Segment`; only `segid` - @skip - def test_set_id(self): - # Test setting the name via the id attribute - new = 'something' - self.sB.id = new - for val in [self.sB.id, self.sB.name]: - assert_equal(val, new) - - # VALID - def test_atom_order(self): - assert_equal(self.universe.segments[0].atoms.indices, - sorted(self.universe.segments[0].atoms.indices)) - - -class TestSegmentGroup(TestCase): - # VALID - @dec.skipif(parser_not_found('DCD'), - 'DCD parser not available. Are you using python 3?') - def setUp(self): - """Set up the standard AdK system in implicit solvent.""" - self.universe = MDAnalysis.Universe(PSF, DCD) - self.g = self.universe.atoms.segments - - # VALID - def test_newSegmentGroup(self): - """test that slicing a SegmentGroup returns a new SegmentGroup (Issue 135)""" - g = self.universe.atoms.segments - newg = g[:] - assert_equal(type(newg), type(g), "Failed to make a new SegmentGroup: type mismatch") - assert_equal(len(newg), len(g)) - - # VALID - def test_n_atoms(self): - assert_equal(self.g.n_atoms, 3341) - - # VALID - def test_n_residues(self): - assert_equal(self.g.n_residues, 214) - - # INVALID: `SegmentGroup.resids` gives list of arrays, one array for each segment - @skip - def test_resids_dim(self): - assert_equal(len(self.g.resids), len(self.g.residues)) - - # INVALID: topology has no resnums - @skip - def test_resnums_dim(self): - assert_equal(len(self.g.resnums), len(self.g.residues)) - - # VALID - def test_segids_dim(self): - assert_equal(len(self.g.segids), len(self.g)) - - # INVALID: cannot set resids from `SegmentGroup`; no `set_resids` method - @skip - def test_set_resids(self): - g = self.universe.select_atoms("bynum 12:42").segments - resid = 999 - g.set_resids(resid) - # check individual atoms - assert_equal([a.resid for a in g.atoms], - resid * np.ones(g.n_atoms), - err_msg="failed to set_resid for segment to same resid") - # check residues - assert_equal(g.residues.resids, resid * np.ones(g.n_residues), - err_msg="failed to set_resid of segments belonging to atoms 12:42 to same resid") - - # INVALID: cannot set resids from `SegmentGroup`; no `set_resids` method - @skip - def test_set_resids(self): - g = self.universe.select_atoms("resid 10:18").segments - resid = 999 - g.set_resids(resid * np.ones(len(g))) - # note: all is now one residue... not meaningful but it is the correct behaviour - assert_equal(g.resids, [resid], - err_msg="failed to set_resid in Segment {0}".format(g)) - - # INVALID: no `set_segids` method; use `segids` property directly - @skip - def test_set_segids(self): - s = self.universe.select_atoms('all').segments - s.set_segids(['ADK']) - assert_equal(self.universe.segments.segids, ['ADK'], - err_msg="failed to set_segid on segments") - - # INVALID: no `set_segids` method; use `segids` property directly - @skip - def test_set_segid_updates_self(self): - g = self.universe.select_atoms("resid 10:18").segments - g.set_segids('ADK') - assert_equal(g.segids, ['ADK'], - err_msg="old selection was not changed in place after set_segid") - - # INVALID: no `set_segids` method; use `segids` property directly - @skip - def test_set_masses(self): - g = self.universe.select_atoms("bynum 12:42 and name H*").segments - mass = 2.0 - g.set_masses(mass) - # check individual atoms - assert_equal([a.mass for a in g.atoms], - mass * np.ones(g.n_atoms), - err_msg="failed to set_mass in segment of H* atoms in resid 12:42 to {0}".format(mass)) - - # INVALID: no `set_segids` method; use `segids` property directly - @skip - def test_set_segid_ValueError(self): - assert_raises(ValueError, self.g.set_resids, [1, 2, 3, 4]) - - # VALID - def test_atom_order(self): - assert_equal(self.universe.segments.atoms.indices, - sorted(self.universe.segments.atoms.indices)) - - -class TestAtomGroupTimestep(TestCase): - """Tests the AtomGroup.ts attribute (partial timestep)""" - - @dec.skipif(parser_not_found('TRZ'), - 'TRZ parser not available. Are you using python 3?') - def setUp(self): - self.universe = MDAnalysis.Universe(TRZ_psf, TRZ) - self.prec = 6 - - def tearDown(self): - del self.universe - del self.prec - - def test_partial_timestep(self): - ag = self.universe.select_atoms('name Ca') - idx = ag.indices - - assert_equal(len(ag.ts._pos), len(ag)) - - for ts in self.universe.trajectory[0:20:5]: - assert_array_almost_equal(ts.positions[idx], ag.ts.positions, self.prec, - err_msg="Partial timestep coordinates wrong") - assert_array_almost_equal(ts.velocities[idx], ag.ts.velocities, self.prec, - err_msg="Partial timestep coordinates wrong") - - -# INVALID: AtomGroups can't exist without a Universe -@skip -def test_empty_AtomGroup(): - """Test that an empty AtomGroup can be constructed (Issue 12)""" - ag = MDAnalysis.core.groups.AtomGroup([]) - assert_equal(len(ag), 0) - - -class _WriteAtoms(TestCase): - """Set up the standard AdK system in implicit solvent.""" - ext = None # override to test various output writers - precision = 3 - - # VALID - @dec.skipif(parser_not_found('DCD'), - 'DCD parser not available. Are you using python 3?') - def setUp(self): - self.universe = MDAnalysis.Universe(PSF, DCD) - suffix = '.' + self.ext - self.tempdir = tempdir.TempDir() - self.outfile = os.path.join(self.tempdir.name, 'writeatoms' + suffix) - - # VALID - def tearDown(self): - del self.universe - del self.tempdir - - # VALID - def universe_from_tmp(self): - return MDAnalysis.Universe(self.outfile, convert_units=True) - - # VALID - def test_write_atoms(self): - self.universe.atoms.write(self.outfile) - u2 = self.universe_from_tmp() - assert_array_almost_equal(self.universe.atoms.positions, u2.atoms.positions, self.precision, - err_msg="atom coordinate mismatch between original and {0!s} file".format(self.ext)) - - # VALID - def test_write_empty_atomgroup(self): - sel = self.universe.select_atoms('name doesntexist') - assert_raises(IndexError, sel.write, self.outfile) - - # VALID - def test_write_selection(self): - CA = self.universe.select_atoms('name CA') - CA.write(self.outfile) - u2 = self.universe_from_tmp() - CA2 = u2.select_atoms('all') # check EVERYTHING, otherwise we might get false positives! - assert_equal(len(u2.atoms), len(CA.atoms), "written CA selection does not match original selection") - assert_almost_equal(CA2.positions, CA.positions, self.precision, - err_msg="CA coordinates do not agree with original") - - # INVALID: Only `AtomGroup`s have `write` method. Must do `G.atoms.write` - @skip - def test_write_Residue(self): - G = self.universe.s4AKE.ARG[-2] # 2nd but last Arg - G.write(self.outfile) - u2 = self.universe_from_tmp() - G2 = u2.select_atoms('all') # check EVERYTHING, otherwise we might get false positives! - assert_equal(len(u2.atoms), len(G.atoms), "written R206 Residue does not match original ResidueGroup") - assert_almost_equal(G2.positions, G.positions, self.precision, - err_msg="Residue R206 coordinates do not agree with original") - - # INVALID: Only `AtomGroup`s have `write` method. Must do `G.atoms.write` - @skip - def test_write_ResidueGroup(self): - G = self.universe.s4AKE.LEU - G.write(self.outfile) - u2 = self.universe_from_tmp() - G2 = u2.select_atoms('all') # check EVERYTHING, otherwise we might get false positives! - assert_equal(len(u2.atoms), len(G.atoms), "written LEU ResidueGroup does not match original ResidueGroup") - assert_almost_equal(G2.positions, G.positions, self.precision, - err_msg="ResidueGroup LEU coordinates do not agree with original") - - # INVALID: Only `AtomGroup`s have `write` method. Must do `G.atoms.write` - @skip - def test_write_Segment(self): - G = self.universe.s4AKE - G.write(self.outfile) - u2 = self.universe_from_tmp() - G2 = u2.select_atoms('all') # check EVERYTHING, otherwise we might get false positives! - assert_equal(len(u2.atoms), len(G.atoms), "written s4AKE segment does not match original segment") - assert_almost_equal(G2.positions, G.positions, self.precision, - err_msg="segment s4AKE coordinates do not agree with original") - - # VALID - def test_write_Universe(self): - U = self.universe - W = MDAnalysis.Writer(self.outfile) - W.write(U) - W.close() - u2 = self.universe_from_tmp() - assert_equal(len(u2.atoms), len(U.atoms), "written 4AKE universe does not match original universe in size") - assert_almost_equal(u2.atoms.positions, U.atoms.positions, self.precision, - err_msg="written universe 4AKE coordinates do not agree with original") - - -class TestWritePDB(_WriteAtoms): - ext = "pdb" - precision = 3 - - -import MDAnalysis.coordinates - - -class TestWriteGRO(_WriteAtoms): - ext = "gro" - precision = 2 - - # INVALID: flags should be retired(?) - @skip - def test_flag_convert_length(self): - assert_equal(MDAnalysis.core.flags['convert_lengths'], True, - "The flag convert_lengths SHOULD be True by default! " - "(If it is not then this might indicate a race condition in the " - "testing suite.)") - - -# VALID -@attr("issue") -@dec.skipif(parser_not_found('DCD'), - 'DCD parser not available. Are you using python 3?') -def test_generated_residueselection(): - """Test that a generated residue group always returns a ResidueGroup (Issue 47) - unless there is a single residue (Issue 363 change)""" - universe = MDAnalysis.Universe(PSF, DCD) - # only a single Cys in AdK - cys = universe.s4AKE.CYS - assert_(isinstance(cys, MDAnalysis.core.groups.Residue), - "Single Cys77 is NOT returned as a single Residue (Issue 47)") - - # multiple Met - met = universe.s4AKE.MET - assert_(isinstance(met, MDAnalysis.core.groups.ResidueGroup), - "Met selection does not return a ResidueGroup") - - del universe - - -# VALID -@attr('issue') -@dec.skipif(parser_not_found('DCD'), - 'DCD parser not available. Are you using python 3?') -def test_instantselection_termini(): - """Test that instant selections work, even for residues that are also termini (Issue 70)""" - universe = MDAnalysis.Universe(PSF, DCD) - assert_equal(universe.residues[20].CA.name, 'CA', "CA of MET21 is not selected correctly") - del universe - -class TestPBCFlag(object): - @dec.skipif(parser_not_found('TRZ'), - 'TRZ parser not available. Are you using python 3?') - def setUp(self): - self.prec = 3 - self.universe = MDAnalysis.Universe(TRZ_psf, TRZ) - self.ref_noPBC = { - 'COG': np.array([4.23789883, 0.62429816, 2.43123484], dtype=np.float32), - 'COM': np.array([4.1673783, 0.70507009, 2.21175832]), - 'ROG': 119.30368949900134, 'Shape': 0.6690026954813445, - 'Asph': 0.5305456387833748, - 'MOI': np.array([ - [152117.06620921, 55149.54042136, -26630.46034023], - [55149.54042136, 72869.64061494, 21998.1778074], - [-26630.46034023, 21998.1778074, 162388.70002471]]), - 'BBox': np.array([[-75.74159241, -144.86634827, -94.47974396], [95.83090973, 115.11561584, 88.09812927]], - dtype=np.float32), - 'BSph': (173.40482, np.array([4.23789883, 0.62429816, 2.43123484], dtype=np.float32)), - 'PAxes': np.array([ - [0.46294889, -0.85135849, 0.24671249], - [0.40611024, 0.45112859, 0.7947059], - [-0.78787867, -0.26771575, 0.55459488]]) - } - self.ref_PBC = { - 'COG': np.array([26.82960892, 31.5592289, 30.98238945], dtype=np.float32), - 'COM': np.array([26.67781143, 31.2104336, 31.19796289]), - 'ROG': 27.713008969174918, 'Shape': 0.0017390512580463542, - 'Asph': 0.020601215358731016, - 'MOI': np.array([ - [7333.79167791, -211.8997285, -721.50785456], - [-211.8997285, 7059.07470427, -91.32156884], - [-721.50785456, -91.32156884, 6509.31735029]]), - 'BBox': np.array( - [[1.45964116e-01, 1.85623169e-02, 4.31785583e-02], [5.53314018e+01, 5.54227829e+01, 5.54158211e+01]], - dtype=np.float32), - 'BSph': (47.923367, np.array([26.82960892, 31.5592289, 30.98238945], dtype=np.float32)), - 'PAxes': np.array([ - [-0.50622389, -0.18364489, -0.84262206], - [-0.07520116, -0.96394227, 0.25526473], - [-0.85911708, 0.19258726, 0.4741603]]) - } - self.ag = self.universe.residues[0:3] - - def tearDown(self): - MDAnalysis.core.flags['use_pbc'] = False - del self.universe - del self.ref_noPBC - del self.ref_PBC - del self.ag - - def test_flag(self): - # Test default setting of flag - assert_equal(MDAnalysis.core.flags['use_pbc'], False) - - def test_default(self): - # Test regular behaviour - assert_almost_equal(self.ag.center_of_geometry(), self.ref_noPBC['COG'], self.prec) - assert_almost_equal(self.ag.center_of_mass(), self.ref_noPBC['COM'], self.prec) - assert_almost_equal(self.ag.radius_of_gyration(), self.ref_noPBC['ROG'], self.prec) - assert_almost_equal(self.ag.shape_parameter(), self.ref_noPBC['Shape'], self.prec) - assert_almost_equal(self.ag.asphericity(), self.ref_noPBC['Asph'], self.prec) - assert_almost_equal(self.ag.moment_of_inertia(), self.ref_noPBC['MOI'], self.prec) - assert_almost_equal(self.ag.bbox(), self.ref_noPBC['BBox'], self.prec) - assert_almost_equal(self.ag.bsphere()[0], self.ref_noPBC['BSph'][0], self.prec) - assert_almost_equal(self.ag.bsphere()[1], self.ref_noPBC['BSph'][1], self.prec) - assert_almost_equal(self.ag.principal_axes(), self.ref_noPBC['PAxes'], self.prec) - - def test_pbcflag(self): - # Test using ag method flag - assert_almost_equal(self.ag.center_of_geometry(pbc=True), self.ref_PBC['COG'], self.prec) - assert_almost_equal(self.ag.center_of_mass(pbc=True), self.ref_PBC['COM'], self.prec) - assert_almost_equal(self.ag.radius_of_gyration(pbc=True), self.ref_PBC['ROG'], self.prec) - assert_almost_equal(self.ag.shape_parameter(pbc=True), self.ref_PBC['Shape'], self.prec) - assert_almost_equal(self.ag.asphericity(pbc=True), self.ref_PBC['Asph'], self.prec) - assert_almost_equal(self.ag.moment_of_inertia(pbc=True), self.ref_PBC['MOI'], self.prec) - assert_almost_equal(self.ag.bbox(pbc=True), self.ref_PBC['BBox'], self.prec) - assert_almost_equal(self.ag.bsphere(pbc=True)[0], self.ref_PBC['BSph'][0], self.prec) - assert_almost_equal(self.ag.bsphere(pbc=True)[1], self.ref_PBC['BSph'][1], self.prec) - assert_almost_equal(self.ag.principal_axes(pbc=True), self.ref_PBC['PAxes'], self.prec) - - def test_usepbc_flag(self): - # Test using the core.flags flag - MDAnalysis.core.flags['use_pbc'] = True - assert_almost_equal(self.ag.center_of_geometry(), self.ref_PBC['COG'], self.prec) - assert_almost_equal(self.ag.center_of_mass(), self.ref_PBC['COM'], self.prec) - assert_almost_equal(self.ag.radius_of_gyration(), self.ref_PBC['ROG'], self.prec) - assert_almost_equal(self.ag.shape_parameter(), self.ref_PBC['Shape'], self.prec) - assert_almost_equal(self.ag.asphericity(), self.ref_PBC['Asph'], self.prec) - assert_almost_equal(self.ag.moment_of_inertia(), self.ref_PBC['MOI'], self.prec) - assert_almost_equal(self.ag.bbox(), self.ref_PBC['BBox'], self.prec) - assert_almost_equal(self.ag.bsphere()[0], self.ref_PBC['BSph'][0], self.prec) - assert_almost_equal(self.ag.bsphere()[1], self.ref_PBC['BSph'][1], self.prec) - assert_almost_equal(self.ag.principal_axes(), self.ref_PBC['PAxes'], self.prec) - MDAnalysis.core.flags['use_pbc'] = False - - def test_override_flag(self): - # Test using the core.flags flag, then overriding - MDAnalysis.core.flags['use_pbc'] = True - assert_almost_equal(self.ag.center_of_geometry(pbc=False), self.ref_noPBC['COG'], self.prec) - assert_almost_equal(self.ag.center_of_mass(pbc=False), self.ref_noPBC['COM'], self.prec) - assert_almost_equal(self.ag.radius_of_gyration(pbc=False), self.ref_noPBC['ROG'], self.prec) - assert_almost_equal(self.ag.shape_parameter(pbc=False), self.ref_noPBC['Shape'], self.prec) - assert_almost_equal(self.ag.asphericity(pbc=False), self.ref_noPBC['Asph'], self.prec) - assert_almost_equal(self.ag.moment_of_inertia(pbc=False), self.ref_noPBC['MOI'], self.prec) - assert_almost_equal(self.ag.bbox(pbc=False), self.ref_noPBC['BBox'], self.prec) - assert_almost_equal(self.ag.bsphere(pbc=False)[0], self.ref_noPBC['BSph'][0], self.prec) - assert_almost_equal(self.ag.bsphere(pbc=False)[1], self.ref_noPBC['BSph'][1], self.prec) - assert_almost_equal(self.ag.principal_axes(pbc=False), self.ref_noPBC['PAxes'], self.prec) - MDAnalysis.core.flags['use_pbc'] = False - - -# INVALID: not including as_Universe, since not clear what it does that's different from `Universe()` -@skip -class TestAsUniverse(TestCase): - @dec.skipif(parser_not_found('DCD'), - 'DCD parser not available. Are you using python 3?') - def setUp(self): - self.u = MDAnalysis.Universe(PSF_notop, DCD) - - def tearDown(self): - del self.u - - def test_empty_TypeError(self): - assert_raises(TypeError, as_Universe) - - def test_passback(self): - returnval = as_Universe(self.u) - - assert_equal(returnval is self.u, True) - - def test_makeuni(self): - returnval = as_Universe(PSF_notop, DCD) - - ## __eq__ method for Universe doesn't exist, make one up here - assert_equal(set(returnval.atoms), set(self.u.atoms)) - - -class TestUniverseCache(TestCase): - # VALID: we should support building Universes with empty Topologies somehow - def setUp(self): - self.u = MDAnalysis.Universe() # not using atoms so just blank universe - self.fill = [1, 2, 3] - - # VALID - def tearDown(self): - del self.u - del self.fill - - # INVALID: no caches at all in Universe - @skip - def test_add_to_cache(self): - # add an item to cache and see if it sticks - cache = 'aa' - self.u._fill_cache(cache, self.fill) - - assert_equal('aa' in self.u._cache, True) - assert_equal(self.u._cache[cache], self.fill) - - # INVALID: no caches at all in Universe - @skip - def test_remove_single(self): - # remove a single item from cache - cache = 'bb' - - self.u._fill_cache(cache, self.fill) - - assert_equal(cache in self.u._cache, True) - - self.u._clear_caches(cache) - - assert_equal(cache in self.u._cache, False) - - # INVALID: no caches at all in Universe - @skip - def test_remove_list(self): - # remove a few things from cache - caches = ['cc', 'dd'] - for c in caches: - self.u._fill_cache(c, self.fill) - - for c in caches: - assert_equal(c in self.u._cache, True) - - self.u._clear_caches(*caches) - - for c in caches: - assert_equal(c in self.u._cache, False) - - # INVALID: no caches at all in Universe - @skip - def test_clear_all(self): - # remove everything from cache - caches = ['ee', 'ff', 'gg'] - for c in caches: - self.u._fill_cache(c, self.fill) - - self.u._clear_caches() - - assert_equal(self.u._cache, dict()) - - -class TestCustomReaders(TestCase): - """ - Can pass a reader as kwarg on Universe creation - """ - # VALID - @dec.skipif(parser_not_found('TRZ'), - 'TRZ parser not available. Are you using python 3?') - def test_custom_reader(self): - # check that reader passing works - u = MDAnalysis.Universe(TRZ_psf, TRZ, format=MDAnalysis.coordinates.TRZ.TRZReader) - assert_equal(len(u.atoms), 8184) - - # VALID - def test_custom_reader_singleframe(self): - T = MDAnalysis.topology.GROParser.GROParser - R = MDAnalysis.coordinates.GRO.GROReader - u = MDAnalysis.Universe(two_water_gro, two_water_gro, - topology_format=T, format=R) - assert_equal(len(u.atoms), 6) - - # VALID - def test_custom_reader_singleframe_2(self): - # Same as before, but only one argument to Universe - T = MDAnalysis.topology.GROParser.GROParser - R = MDAnalysis.coordinates.GRO.GROReader - u = MDAnalysis.Universe(two_water_gro, - topology_format=T, format=R) - assert_equal(len(u.atoms), 6) - - # VALID - @dec.skipif(parser_not_found('TRZ'), - 'TRZ parser not available. Are you using python 3?') - def test_custom_parser(self): - # topology reader passing works - u = MDAnalysis.Universe(TRZ_psf, TRZ, topology_format=MDAnalysis.topology.PSFParser.PSFParser) - assert_equal(len(u.atoms), 8184) - - # VALID - @dec.skipif(parser_not_found('TRZ'), - 'TRZ parser not available. Are you using python 3?') - def test_custom_both(self): - # use custom for both - u = MDAnalysis.Universe(TRZ_psf, TRZ, format=MDAnalysis.coordinates.TRZ.TRZReader, - topology_format=MDAnalysis.topology.PSFParser.PSFParser) - assert_equal(len(u.atoms), 8184) - - -class TestInMemoryUniverse(TestCase): - - @staticmethod - @dec.skipif(parser_not_found('DCD'), - 'DCD parser not available. Are you using python 3?') - def test_reader_w_timeseries(): - universe = MDAnalysis.Universe(PSF, DCD, in_memory=True) - assert_equal(universe.trajectory.timeseries(universe.atoms).shape, - (3341, 98, 3), - err_msg="Unexpected shape of trajectory timeseries") - - @staticmethod - def test_reader_wo_timeseries(): - universe = MDAnalysis.Universe(GRO, TRR, in_memory=True) - assert_equal(universe.trajectory.timeseries(universe.atoms).shape, - (47681, 10, 3), - err_msg="Unexpected shape of trajectory timeseries") - - @staticmethod - @dec.skipif(parser_not_found('DCD'), - 'DCD parser not available. Are you using python 3?') - def test_reader_w_timeseries_frame_interval(): - universe = MDAnalysis.Universe(PSF, DCD, in_memory=True, - in_memory_frame_interval=10) - assert_equal(universe.trajectory.timeseries(universe.atoms).shape, - (3341, 10, 3), - err_msg="Unexpected shape of trajectory timeseries") - - @staticmethod - def test_reader_wo_timeseries_frame_interval(): - universe = MDAnalysis.Universe(GRO, TRR, in_memory=True, - in_memory_frame_interval=3) - assert_equal(universe.trajectory.timeseries(universe.atoms).shape, - (47681, 4, 3), - err_msg="Unexpected shape of trajectory timeseries") - - @staticmethod - @dec.skipif(parser_not_found('DCD'), - 'DCD parser not available. Are you using python 3?') - def test_existing_universe(): - universe = MDAnalysis.Universe(PDB_small, DCD) - universe.transfer_to_memory() - assert_equal(universe.trajectory.timeseries(universe.atoms).shape, - (3341, 98, 3), - err_msg="Unexpected shape of trajectory timeseries") - - @staticmethod - @dec.skipif(parser_not_found('DCD'), - 'DCD parser not available. Are you using python 3?') - def test_frame_interval_convention(): - universe1 = MDAnalysis.Universe(PSF, DCD) - array1 = universe1.trajectory.timeseries(skip=10) - universe2 = MDAnalysis.Universe(PSF, DCD, in_memory=True, - in_memory_frame_interval=10) - array2 = universe2.trajectory.timeseries() - assert_equal(array1, array2, - err_msg="Unexpected differences between arrays.") - - -class TestWrap(TestCase): - # VALID - @dec.skipif(parser_not_found('TRZ'), - 'TRZ parser not available. Are you using python 3?') - def setUp(self): - self.u = MDAnalysis.Universe(TRZ_psf, TRZ) - self.ag = self.u.atoms[:100] - - # VALID - def tearDown(self): - del self.u - del self.ag - - # VALID - def test_wrap_comp_fail(self): - assert_raises(ValueError, self.ag.wrap, compound='strawberries') - - # VALID - def test_wrap_cent_fail(self): - assert_raises(ValueError, self.ag.wrap, compound='residues', center='avacado') - - # VALID - def test_wrap_box_fail(self): - assert_raises(ValueError, self.ag.wrap, box=np.array([0, 1])) - - def _in_box(self, coords): - """Check that a set of coordinates are 0.0 <= r <= box""" - box = self.u.dimensions[:3] - - return (coords >= 0.0).all() and (coords <= box).all() - - # VALID - def test_wrap_atoms(self): - ag = self.u.atoms[100:200] - ag.wrap(compound='atoms') - - assert_equal(self._in_box(ag.positions), True) - - # VALID - def test_wrap_group(self): - ag = self.u.atoms[:100] - ag.wrap(compound='group') - - cen = ag.center_of_mass() - - assert_equal(self._in_box(cen), True) - - # INVALID: must do `r.atoms.center_of_mass()` - @skip - def test_wrap_residues(self): - ag = self.u.atoms[300:400] - ag.wrap(compound='residues') - - cen = np.vstack([r.center_of_mass() for r in ag.residues]) - - assert_equal(self._in_box(cen), True) - - # INVALID: must do `s.atoms.center_of_mass()` - @skip - def test_wrap_segments(self): - ag = self.u.atoms[1000:1200] - ag.wrap(compound='segments') - - cen = np.vstack([s.center_of_mass() for s in ag.segments]) - - assert_equal(self._in_box(cen), True) - - # VALID - def test_wrap_fragments(self): - ag = self.u.atoms[:250] - ag.wrap(compound='fragments') - - cen = np.vstack([f.center_of_mass() for f in ag.fragments]) - - assert_equal(self._in_box(cen), True) - - -class TestGuessBonds(TestCase): - """Test the AtomGroup methed guess_bonds - - This needs to be done both from Universe creation (via kwarg) and AtomGroup - - It needs to: - - work if all atoms are in vdwradii table - - fail properly if not - - work again if vdwradii are passed. - """ - # VALID - def setUp(self): - self.vdw = {'A':1.05, 'B':0.4} - - # VALID - def tearDown(self): - del self.vdw - - # INVALID: Universe has no bonds; AtomGroup does - def _check_universe(self, u): - """Verify that the Universe is created correctly""" - assert_equal(len(u.bonds), 4) - assert_equal(len(u.angles), 2) - assert_equal(len(u.dihedrals), 0) - assert_equal(len(u.atoms[0].bonds), 2) - assert_equal(len(u.atoms[1].bonds), 1) - assert_equal(len(u.atoms[2].bonds), 1) - assert_equal(len(u.atoms[3].bonds), 2) - assert_equal(len(u.atoms[4].bonds), 1) - assert_equal(len(u.atoms[5].bonds), 1) - assert_('guess_bonds' in u.kwargs) - - def test_universe_guess_bonds(self): - """Test that making a Universe with guess_bonds works""" - u = MDAnalysis.Universe(two_water_gro, guess_bonds=True) - self._check_universe(u) - assert_(u.kwargs['guess_bonds'] is True) - - def test_universe_guess_bonds_no_vdwradii(self): - """Make a Universe that has atoms with unknown vdwradii.""" - assert_raises(ValueError, MDAnalysis.Universe, two_water_gro_nonames, guess_bonds=True) - - def test_universe_guess_bonds_with_vdwradii(self): - """Unknown atom types, but with vdw radii here to save the day""" - u = MDAnalysis.Universe(two_water_gro_nonames, guess_bonds=True, - vdwradii=self.vdw) - self._check_universe(u) - assert_(u.kwargs['guess_bonds'] is True) - assert_equal(self.vdw, u.kwargs['vdwradii']) - - def test_universe_guess_bonds_off(self): - u = MDAnalysis.Universe(two_water_gro_nonames, guess_bonds=False) - - for attr in ('bonds', 'angles', 'dihedrals'): - assert_(not hasattr(u, attr)) - assert_(u.kwargs['guess_bonds'] is False) - - def _check_atomgroup(self, ag, u): - """Verify that the AtomGroup made bonds correctly, - and that the Universe got all this info - """ - assert_equal(len(ag.bonds), 2) - assert_equal(len(ag.angles), 1) - assert_equal(len(ag.dihedrals), 0) - assert_equal(len(u.bonds), 2) - assert_equal(len(u.angles), 1) - assert_equal(len(u.dihedrals), 0) - assert_equal(len(u.atoms[0].bonds), 2) - assert_equal(len(u.atoms[1].bonds), 1) - assert_equal(len(u.atoms[2].bonds), 1) - assert_equal(len(u.atoms[3].bonds), 0) - assert_equal(len(u.atoms[4].bonds), 0) - assert_equal(len(u.atoms[5].bonds), 0) - - def test_atomgroup_guess_bonds(self): - """Test an atomgroup doing guess bonds""" - u = MDAnalysis.Universe(two_water_gro) - - ag = u.atoms[:3] - ag.guess_bonds() - self._check_atomgroup(ag, u) - - def test_atomgroup_guess_bonds_no_vdwradii(self): - u = MDAnalysis.Universe(two_water_gro_nonames) - - ag = u.atoms[:3] - assert_raises(ValueError, ag.guess_bonds) - - def test_atomgroup_guess_bonds_with_vdwradii(self): - u = MDAnalysis.Universe(two_water_gro_nonames) - - ag = u.atoms[:3] - ag.guess_bonds(vdwradii=self.vdw) - self._check_atomgroup(ag, u) - - -class TestAtomGroupProperties(object): - """Test working with the properties of Atoms via AtomGroups - - Check that: - - getting properties from AG matches the Atom values - - setting properties from AG changes the Atom - - setting the property on Atom changes AG - """ - @staticmethod - def get_new(att_type): - """Return enough values to change the small g""" - if att_type == 'string': - return ['A', 'B', 'C', 'D', 'E', 'F'] - elif att_type == 'float': - return np.array([0.001, 0.002, 0.003, 0.005, 0.012, 0.025], dtype=np.float32) - elif att_type == 'int': - return [4, 6, 8, 1, 5, 4] - - def _check_ag_matches_atom(self, att, atts, ag): - """Checking Atomgroup property matches Atoms""" - # Check that accessing via AtomGroup is identical to doing - # a list comprehension over AG - ref = [getattr(atom, att) for atom in ag] - - assert_equal(ref, getattr(ag, atts), - err_msg="AtomGroup doesn't match Atoms for property: {0}".format(att)) - - def _change_atom_check_ag(self, att, atts, vals, ag): - """Changing Atom, checking AtomGroup matches this""" - # Set attributes via Atoms - for atom, val in zip(ag, vals): - setattr(atom, att, val) - # Check that AtomGroup returns new values - other = getattr(ag, atts) - - assert_equal(vals, other, - err_msg="Change to Atoms not reflected in AtomGroup for property: {0}".format(att)) - - @dec.skipif(parser_not_found('DCD'), - 'DCD parser not available. Are you using python 3?') - def test_attributes(self): - u = make_Universe(('names', 'resids', 'segids', 'types', 'altLocs', - 'charges', 'masses', 'radii', 'bfactors', - 'occupancies')) - u.atoms.occupancies = 1.0 - master = u.atoms - idx = [0, 1, 4, 7, 11, 14] - ag = master[idx] - - for att, atts, att_type in ( - ('name', 'names', 'string'), - ('type', 'types', 'string'), - ('altLoc', 'altLocs', 'string'), - ('charge', 'charges', 'float'), - ('mass', 'masses', 'float'), - ('radius', 'radii', 'float'), - ('bfactor', 'bfactors', 'float'), - ('occupancy', 'occupancies', 'float') - ): - vals = self.get_new(att_type) - yield self._check_ag_matches_atom, att, atts, ag - yield self._change_atom_check_ag, att, atts, vals, ag - - -# VALID -class TestOrphans(object): - """Test moving Universes out of scope and having A/AG persist - - Atoms and AtomGroups from other scopes should work, namely: - - should have access to Universe - - should be able to use the Reader (coordinates) - """ - # VALID - def test_atom(self): - u = MDAnalysis.Universe(two_water_gro) - - def getter(): - u2 = MDAnalysis.Universe(two_water_gro) - return u2.atoms[1] - - atom = getter() - - assert_(atom is not u.atoms[1]) - assert_(len(atom.universe.atoms) == len(u.atoms)) - assert_array_almost_equal(atom.position, u.atoms[1].position) - - # VALID - def test_atomgroup(self): - u = MDAnalysis.Universe(two_water_gro) - - def getter(): - u2 = MDAnalysis.Universe(two_water_gro) - return u2.atoms[:4] - - ag = getter() - ag2 = u.atoms[:4] - assert_(ag is not ag2) - assert_(len(ag.universe.atoms) == len(u.atoms)) - assert_array_almost_equal(ag.positions, ag2.positions) - - -class TestCrossUniverse(object): - """Test behaviour when we mix Universes""" - - # VALID - def _check_badadd(self, a, b): - def add(x, y): - return x + y - assert_raises(ValueError, add, a, b) - - # VALID: currently gives TypeError and unhelpful message - def test_add_mixed_universes(self): - # Issue #532 - # Checks that adding objects from different universes - # doesn't proceed quietly. - u1 = MDAnalysis.Universe(two_water_gro) - u2 = MDAnalysis.Universe(two_water_gro) - - A = [u1.atoms[:2], u1.atoms[3]] - B = [u2.atoms[:3], u2.atoms[0]] - - # Checks Atom to Atom, Atom to AG, AG to Atom and AG to AG - for x, y in itertools.product(A, B): - yield self._check_badadd, x, y - - def test_adding_empty_ags(self): - # Check that empty AtomGroups don't trip up on the Universe check - u = MDAnalysis.Universe(two_water_gro) - - assert_(len(u.atoms[[]] + u.atoms[:3]) == 3) - assert_(len(u.atoms[:3] + u.atoms[[]]) == 3) - - -class TestDihedralSelections(object): - @dec.skipif(parser_not_found('DCD'), - 'DCD parser not available. Are you using python 3?') - def setUp(self): - self.universe = MDAnalysis.Universe(PSF, DCD) - self.dih_prec = 2 - - def tearDown(self): - del self.universe - del self.dih_prec - - def test_phi_selection(self): - phisel = self.universe.s4AKE.r10.phi_selection() - assert_equal(phisel.names, ['C', 'N', 'CA', 'C']) - assert_equal(phisel.residues.resids, [9, 10]) - assert_equal(phisel.residues.resnames, ['PRO', 'GLY']) - - def test_psi_selection(self): - psisel = self.universe.s4AKE.r10.psi_selection() - assert_equal(psisel.names, ['N', 'CA', 'C', 'N']) - assert_equal(psisel.residues.resids, [10, 11]) - assert_equal(psisel.residues.resnames, ['GLY', 'ALA']) - - def test_omega_selection(self): - osel = self.universe.s4AKE.r8.omega_selection() - assert_equal(osel.names, ['CA', 'C', 'N', 'CA']) - assert_equal(osel.residues.resids, [8, 9]) - assert_equal(osel.residues.resnames, ['ALA', 'PRO']) - - def test_chi1_selection(self): - sel = self.universe.s4AKE.r13.chi1_selection() # LYS - assert_equal(sel.names, ['N', 'CA', 'CB', 'CG']) - assert_equal(sel.residues.resids, [13]) - assert_equal(sel.residues.resnames, ['LYS']) - - def test_phi_sel_fail(self): - sel = self.universe.residues[0].phi_selection() - assert_equal(sel, None) - - def test_psi_sel_fail(self): - sel = self.universe.residues[-1].psi_selection() - assert_equal(sel, None) - - def test_omega_sel_fail(self): - sel = self.universe.residues[-1].omega_selection() - assert_equal(sel, None) - - def test_ch1_sel_fail(self): - sel = self.universe.s4AKE.r8.chi1_selection() - assert_equal(sel, None) # ALA - - def test_dihedral_phi(self): - u = self.universe - u.trajectory.rewind() # just to make sure... - phisel = u.s4AKE.r10.phi_selection() - assert_almost_equal(phisel.dihedral.value(), -168.57384, self.dih_prec) - - def test_dihedral_psi(self): - u = self.universe - u.trajectory.rewind() # just to make sure... - psisel = u.s4AKE.r10.psi_selection() - assert_almost_equal(psisel.dihedral.value(), -30.064838, self.dih_prec) - - def test_dihedral_omega(self): - u = self.universe - u.trajectory.rewind() # just to make sure... - osel = u.s4AKE.r8.omega_selection() - assert_almost_equal(osel.dihedral.value(), -179.93439, self.dih_prec) - - def test_dihedral_chi1(self): - u = self.universe - u.trajectory.rewind() # just to make sure... - sel = u.s4AKE.r13.chi1_selection() # LYS - assert_almost_equal(sel.dihedral.value(), -58.428127, self.dih_prec) diff --git a/testsuite/MDAnalysisTests/test_persistence.py b/testsuite/MDAnalysisTests/test_persistence.py index f35645a137c..41a81af8cbe 100644 --- a/testsuite/MDAnalysisTests/test_persistence.py +++ b/testsuite/MDAnalysisTests/test_persistence.py @@ -36,7 +36,7 @@ import warnings from MDAnalysisTests.datafiles import PDB_small, GRO, XTC, TRR -from MDAnalysisTests.core.groupbase import make_Universe +from MDAnalysisTests import make_Universe class TestAtomGroupPickle(object): def setUp(self): diff --git a/testsuite/MDAnalysisTests/topology/test_guessers.py b/testsuite/MDAnalysisTests/topology/test_guessers.py index e12643cbafd..1c39fdcb685 100644 --- a/testsuite/MDAnalysisTests/topology/test_guessers.py +++ b/testsuite/MDAnalysisTests/topology/test_guessers.py @@ -31,7 +31,7 @@ from MDAnalysis.topology import guessers from MDAnalysis.core.topologyattrs import Angles -from MDAnalysisTests.core.groupbase import make_Universe +from MDAnalysisTests import make_Universe from MDAnalysisTests.core.test_fragments import make_starshape class TestGuessMasses(object): diff --git a/testsuite/MDAnalysisTests/util.py b/testsuite/MDAnalysisTests/util.py index 63f4690aece..38578503a72 100644 --- a/testsuite/MDAnalysisTests/util.py +++ b/testsuite/MDAnalysisTests/util.py @@ -33,8 +33,11 @@ builtins_name = 'builtins' importer = builtins.__import__ +from contextlib import contextmanager from functools import wraps +import importlib import mock +import os def block_import(package): @@ -69,3 +72,78 @@ def blocker(*args, **kwargs): return blocker_wrapper +def executable_not_found(*args): + """Return ``True`` if none of the executables in args can be found. + + ``False`` otherwise (i.e. at least one was found). + + To be used as the argument of:: + + @dec.skipif(executable_not_found("binary_name"), msg="skip test because binary_name not available") + """ + # This must come here so that MDAnalysis isn't imported prematurely, + # which spoils coverage accounting (see Issue 344). + import MDAnalysis.lib.util + for name in args: + if MDAnalysis.lib.util.which(name) is not None: + return False + return True + + +def module_not_found(module): + try: + importlib.import_module(module) + except ImportError: + return True + else: + return False + + +def parser_not_found(parser_name): + """Return ``True`` if the parser of the given name cannot be found. + + This allows to skip a test when the parser is unavailable (e.g in python 3 + when the parser is not compatible). + + To be used as the argument of:: + + @dec.skipif(parser_not_found('DCD'), + 'DCD parser is not available. Are you on python 3?') + """ + import MDAnalysis.coordinates + try: + getattr(MDAnalysis.coordinates, parser_name) + except AttributeError: + return True + else: + return False + + +@contextmanager +def in_dir(dirname): + """Context manager for safely changing directories. + + Arguments + --------- + dirname : string + directory to change into + + Example + ------- + Change into a temporary directory and always change back to the + current one:: + + with in_dir("/tmp") as tmpdir: + # do stuff + + SeeAlso + ------- + The :mod:`tmpdir` module provides functionality such as :func:`tmpdir.in_tmpdir` + to create temporary directories that are automatically deleted once they are no + longer used. + """ + + old_path = os.getcwd() + os.chdir(dirname) + yield dirname + os.chdir(old_path)