From 088d309130ae00c308778c464fd49502a72e901a Mon Sep 17 00:00:00 2001 From: Oliver Beckstein Date: Thu, 15 Jun 2017 16:26:13 -0700 Subject: [PATCH] add deprecation warnings for API changes - closes #1383 - included deprecations: - #1373 Timeseries (targeted for 0.17) Note that the deprecation for core.Timeseries will always show up; this is deliberate so that users WILL see it as it will be gone in the next release! - #1377 Quick selectors (target 1.0) - #782 flags (target 1.0) - updated CHANGELOG --- package/CHANGELOG | 5 +- package/MDAnalysis/coordinates/memory.py | 2 + package/MDAnalysis/core/Timeseries.py | 163 ++++++++++++++---- package/MDAnalysis/core/__init__.py | 30 +++- package/MDAnalysis/core/groups.py | 76 +++++--- package/MDAnalysis/core/universe.py | 11 +- .../source/documentation_pages/selections.rst | 11 ++ 7 files changed, 231 insertions(+), 67 deletions(-) diff --git a/package/CHANGELOG b/package/CHANGELOG index 66827ab51e0..54b3e8b1ca6 100644 --- a/package/CHANGELOG +++ b/package/CHANGELOG @@ -15,7 +15,7 @@ The rules for this file: ------------------------------------------------------------------------------ -mm/dd/17 richardjgowers, rathann, jbarnoud +mm/dd/17 richardjgowers, rathann, jbarnoud, orbeckst * 0.16.2 @@ -27,6 +27,9 @@ Fixes * Groups are hashable on python 3 (Issue #1397) Changes + * added deprecation warnings for API changes (removal of Timeseries and + DCD correl functionality, removal of instant selectors, and removal of + the core.flag registry) (Issue #1383) 06/03/17 utkbansal, kain88-de, xiki-tempula, kaplajon, wouterboomsma, diff --git a/package/MDAnalysis/coordinates/memory.py b/package/MDAnalysis/coordinates/memory.py index 279009fbfd3..859a2d51982 100644 --- a/package/MDAnalysis/coordinates/memory.py +++ b/package/MDAnalysis/coordinates/memory.py @@ -106,6 +106,8 @@ universe2 = mda.Universe(PSF, coordinates, format=MemoryReader, order='afc') +.. _create-in-memory-trajectory-with-AnalysisFromFunction: + .. rubric:: Creating an in-memory trajectory with :func:`~MDAnalysis.analysis.base.AnalysisFromFunction` diff --git a/package/MDAnalysis/core/Timeseries.py b/package/MDAnalysis/core/Timeseries.py index 6dc2952874b..30b0c991886 100644 --- a/package/MDAnalysis/core/Timeseries.py +++ b/package/MDAnalysis/core/Timeseries.py @@ -21,10 +21,19 @@ # -""" -Compute observable timeseries from trajectories --- :mod:`MDAnalysis.core.Timeseries` +"""Compute observable timeseries from trajectories --- :mod:`MDAnalysis.core.Timeseries` ======================================================================================= +.. deprecated:: 0.16.2 + The Timeseries functionality (in particular correl) will be removed in the + 0.17 release. See issue `#1372 + `_ for more details. + To extract coordinates efficiently one can use + :class:`MDAnalysis.analysis.base.AnalysisFromFunction` as shown in + :ref:`Creating an in-memory trajectory with AnalysisFromFunction + `. + + The collection of timeseries (such as :class:`Atom`, :class:`Bond`, :class:`Dihedral`...) can be computed from a trajectory in one go, foregoing the need to iterate through the trajectory frame by frame in python. Inspired @@ -64,6 +73,14 @@ from . import groups +with warnings.catch_warnings(): + warnings.simplefilter('always', DeprecationWarning) + warnings.warn(('The Timeseries module and TimeseriesCollection will be ' + 'removed in the 0.17 release. See issue #1372 ' + 'https://github.com/MDAnalysis/mdanalysis/issues/1373'), + DeprecationWarning) + + class TimeseriesCollection(object): '''A collection of timeseries objects. @@ -81,6 +98,14 @@ class TimeseriesCollection(object): collection.clear() - clear the collection collection[i] - access the i'th timeseries len(collection) - return the number of Timeseries added to the collection + + + .. deprecated:: 0.16.2 + The Timeseries functionality (in particular correl) will be removed in + the 0.17 release. See issue `#1372 + `_ for more + details. Use :class:`MDAnalysis.analysis.base.AnalysisFromFunction` instead. + ''' def __init__(self): @@ -186,7 +211,14 @@ def _getAuxData(self): class Timeseries(object): - '''Base timeseries class - define subclasses for specific timeseries computations + '''Base timeseries class - define subclasses for specific timeseries computations. + + .. deprecated:: 0.16.2 + The Timeseries functionality (in particular correl) will be removed in + the 0.17 release. See issue `#1372 + `_ for more + details. Use :class:`MDAnalysis.analysis.base.AnalysisFromFunction` instead. + ''' def __init__(self, code, atoms, dsize): @@ -253,6 +285,13 @@ class Atom(Timeseries): can be a single :class:`~MDAnalysis.core.groups.Atom` object, a list of :class:`~MDAnalysis.core.groups.Atom` objects, or an :class:`~MDAnalysis.core.groups.AtomGroup` + + .. deprecated:: 0.16.2 + The Timeseries functionality (in particular correl) will be removed in + the 0.17 release. See issue `#1372 + `_ for more + details. Use :class:`MDAnalysis.analysis.base.AnalysisFromFunction` instead. + ''' def __init__(self, code, atoms): @@ -281,8 +320,15 @@ class Bond(Timeseries): t = Bond(atoms) - *atoms* must contain 2 :class:`~MDAnalysis.core.groups.Atom` instances, either as a list or an - :class:`~MDAnalysis.core.groups.AtomGroup` + *atoms* must contain 2 :class:`~MDAnalysis.core.groups.Atom` instances, either as a list or an + :class:`~MDAnalysis.core.groups.AtomGroup` + + .. deprecated:: 0.16.2 + The Timeseries functionality (in particular correl) will be removed in + the 0.17 release. See issue `#1372 + `_ for more + details. Use :class:`MDAnalysis.analysis.base.AnalysisFromFunction` instead. + ''' def __init__(self, atoms): @@ -296,8 +342,15 @@ class Angle(Timeseries): t = Angle(atoms) - atoms must contain 3 :class:`~MDAnalysis.core.groups.Atom` instances, either as a list or an - :class:`~MDAnalysis.core.groups.AtomGroup` + atoms must contain 3 :class:`~MDAnalysis.core.groups.Atom` instances, + either as a list or an :class:`~MDAnalysis.core.groups.AtomGroup` + + .. deprecated:: 0.16.2 + The Timeseries functionality (in particular correl) will be removed in + the 0.17 release. See issue `#1372 + `_ for more + details. Use :class:`MDAnalysis.analysis.base.AnalysisFromFunction` instead. + ''' def __init__(self, atoms): @@ -311,8 +364,15 @@ class Dihedral(Timeseries): t = Dihedral(atoms) - atoms must contain 4 :class:`~MDAnalysis.core.groups.Atom` objects, either as a list or an - :class:`~MDAnalysis.core.groups.AtomGroup` + atoms must contain 4 :class:`~MDAnalysis.core.groups.Atom` objects, either + as a list or an :class:`~MDAnalysis.core.groups.AtomGroup` + + .. deprecated:: 0.16.2 + The Timeseries functionality (in particular correl) will be removed in + the 0.17 release. See issue `#1372 + `_ for more + details. Use :class:`MDAnalysis.analysis.base.AnalysisFromFunction` instead. + ''' def __init__(self, atoms): @@ -326,9 +386,16 @@ class Distance(Timeseries): t = Distance(code, atoms) - code is one of 'd' (distance vector), or 'r' (scalar distance) - atoms must contain 2 :class:`~MDAnalysis.core.groups.Atom` objects, either as a list or an - :class:`~MDAnalysis.core.groups.AtomGroup` + code is one of 'd' (distance vector), or 'r' (scalar distance) atoms must + contain 2 :class:`~MDAnalysis.core.groups.Atom` objects, either as a list + or an :class:`~MDAnalysis.core.groups.AtomGroup` + + .. deprecated:: 0.16.2 + The Timeseries functionality (in particular correl) will be removed in + the 0.17 release. See issue `#1372 + `_ for more + details. Use :class:`MDAnalysis.analysis.base.AnalysisFromFunction` instead. + ''' def __init__(self, code, atoms): @@ -348,8 +415,16 @@ class CenterOfGeometry(Timeseries): t = CenterOfGeometry(atoms) - *atoms* can be a list of :class:`~MDAnalysis.core.groups.Atom` - objects, or a :class:`~MDAnalysis.core.groups.AtomGroup` + *atoms* can be a list of :class:`~MDAnalysis.core.groups.Atom` objects, or + a :class:`~MDAnalysis.core.groups.AtomGroup` + + + .. deprecated:: 0.16.2 + The Timeseries functionality (in particular correl) will be removed in + the 0.17 release. See issue `#1372 + `_ for more + details. Use :class:`MDAnalysis.analysis.base.AnalysisFromFunction` instead. + ''' def __init__(self, atoms): @@ -364,8 +439,15 @@ class CenterOfMass(Timeseries): t = CenterOfMass(atoms) - *atoms* can be a list of :class:`~MDAnalysis.core.groups.Atom` - objects or a :class:`~MDAnalysis.core.groups.AtomGroup` + *atoms* can be a list of :class:`~MDAnalysis.core.groups.Atom` objects or a + :class:`~MDAnalysis.core.groups.AtomGroup` + + .. deprecated:: 0.16.2 + The Timeseries functionality (in particular correl) will be removed in + the 0.17 release. See issue `#1372 + `_ for more + details. Use :class:`MDAnalysis.analysis.base.AnalysisFromFunction` instead. + ''' def __init__(self, atoms): @@ -380,38 +462,43 @@ class WaterDipole(Timeseries): d = WaterDipole(atoms) - *atoms* must contain 3 :class:`~MDAnalysis.core.groups.Atom` - objects, either as a list or an - :class:`~MDAnalysis.core.groups.AtomGroup`; the first one *must* be - the oxygen, the other two are the hydrogens. + *atoms* must contain 3 :class:`~MDAnalysis.core.groups.Atom` objects, + either as a list or an :class:`~MDAnalysis.core.groups.AtomGroup`; the + first one *must* be the oxygen, the other two are the hydrogens. + + The vector ``d``, multiplied by the partial charge on the oxygen atom + (e.g. *q* = -0.0.834 for TIP3P water), gives the actual dipole moment. - The vector ``d``, multiplied by the partial charge on the oxygen atom - (e.g. *q* = -0.0.834 for TIP3P water), gives the actual dipole moment. + The vector is calculated from the positions of the oxygen atom + (:math:`\mathbf{x}_{\text{O}}`) and the two hydrogen atoms + (:math:`\mathbf{x}_{\text{H}_1}`, :math:`\mathbf{x}_{\text{H}_2}`) as - The vector is calculated from the positions of the oxygen atom - (:math:`\mathbf{x}_{\text{O}}`) and the two hydrogen atoms - (:math:`\mathbf{x}_{\text{H}_1}`, :math:`\mathbf{x}_{\text{H}_2}`) as + .. math:: - .. math:: + \mathbf{d} = \mathbf{x}_{\text{O}} - \frac{1}{2}(\mathbf{x}_{\text{H}_1} + \mathbf{x}_{\text{H}_2}) - \mathbf{d} = \mathbf{x}_{\text{O}} - \frac{1}{2}(\mathbf{x}_{\text{H}_1} + \mathbf{x}_{\text{H}_2}) + and the dipole moment vector is - and the dipole moment vector is + .. math:: - .. math:: + \boldsymbol{\mu} = q_{\text{O}} \mathbf{d} - \boldsymbol{\mu} = q_{\text{O}} \mathbf{d} + .. Note:: - .. Note:: + This will only work for water models that have half of the oxygen charge + on each hydrogen. The vector :math:`\mathbf{d}` has the opposite + direction of the dipole moment; multiplying with the oxygen charge + (:math:`q_{\text{O}}<0`) will flip the direction and produce the correct + orientation. - This will only work for water models that have half of the oxygen - charge on each hydrogen. The vector :math:`\mathbf{d}` has the - opposite direction of the dipole moment; multiplying with the oxygen - charge (:math:`q_{\text{O}}<0`) will flip the direction and produce - the correct orientation. + There are no sanity checks; *if the first atom in a water + molecule is not oxygen then results will be wrong.* - There are no sanity checks; *if the first atom in a water - molecule is not oxygen then results will be wrong.* + .. deprecated:: 0.16.2 + The Timeseries functionality (in particular correl) will be removed in + the 0.17 release. See issue `#1372 + `_ for more + details. Use :class:`MDAnalysis.analysis.base.AnalysisFromFunction` instead. ''' diff --git a/package/MDAnalysis/core/__init__.py b/package/MDAnalysis/core/__init__.py index 2101c1d878e..167eb7d893f 100644 --- a/package/MDAnalysis/core/__init__.py +++ b/package/MDAnalysis/core/__init__.py @@ -30,7 +30,8 @@ :class:`~MDAnalysis.core.groups.AtomGroup` and return another :class:`~MDAnalysis.core.groups.AtomGroup`. -:mod:`~MDAnalysis.Timeseries` are a convenient way to analyse trajectories. +:mod:`~MDAnalysis.core.Timeseries` are a convenient way to analyse +trajectories. To get started, load the Universe:: @@ -56,6 +57,12 @@ Flags ----- +.. deprecated:: 0.16.2 + The flags registry will be removed in release 1.0. + Use keyword arguments for functions to obtain the desired behavior. + See issue `#782 `_ + for more details. + (This is an advanced topic and can probably be skipped by most people.) There are a number flags that influence how MDAnalysis behaves. They are accessible @@ -92,8 +99,9 @@ __all__ = ['AtomGroup', 'Selection', 'Timeseries'] -# set up flags for core routines (more convoluted than strictly necessary but should -# be clean to add more flags if needed) +# set up flags for core routines (more convoluted than strictly necessary but +# should be clean to add more flags if needed) + class Flags(dict): """Global registry of flags. Acts like a dict for item access. @@ -109,6 +117,13 @@ class Flags(dict): New flags are added with the :meth:`Flags.register` method which takes a new :class:`Flag` instance as an argument. + + .. deprecated:: 0.16.2 + The flags registry will be removed in release 1.0. + Use keyword arguments for functions to obtain the desired behavior. + See issue `#782 `_ + for more details. + """ def __init__(self, *args): @@ -180,7 +195,14 @@ def __getitem__(self, key): class Flag(object): - """A Flag, essentially a variable that knows its default and legal values.""" + """A Flag, essentially a variable that knows its default and legal values. + + .. deprecated:: 0.16.2 + The flags registry will be removed in release 1.0. + Use keyword arguments for functions to obtain the desired behavior. + See issue `#782 `_ + for more details. + """ def __init__(self, name, default, mapping=None, doc=None): """Create a new flag which will be registered with Flags. diff --git a/package/MDAnalysis/core/groups.py b/package/MDAnalysis/core/groups.py index 5c317740ffb..b3ef855dc18 100644 --- a/package/MDAnalysis/core/groups.py +++ b/package/MDAnalysis/core/groups.py @@ -342,7 +342,7 @@ def wrapped(self, other): class GroupBase(_MutableBase): """Base class from which a Universe's Group class is built. - Instances of :class:`GroupBase` provide the following operations that + Instances of :class:`GroupBase` provide the following operations that conserve element repetitions and order: +-------------------------------+------------+----------------------------+ @@ -989,7 +989,7 @@ def wrap(self, compound="atoms", center="com", box=None): "Please use one of 'group' 'residues' 'segments'" "or 'fragments'".format(compound)) -# TODO: ADD TRY-EXCEPT FOR MASSES PRESENCE + # TODO: ADD TRY-EXCEPT FOR MASSES PRESENCE if center.lower() in ('com', 'centerofmass'): centers = np.vstack([o.atoms.center_of_mass() for o in objects]) elif center.lower() in ('cog', 'centroid', 'centerofgeometry'): @@ -1352,9 +1352,10 @@ def is_strict_superset(self, other): class AtomGroup(GroupBase): """A group of atoms. - An AtomGroup is an ordered collection of atoms. Typically, an AtomGroup is - generated from a selection, or by indexing/slcing the AtomGroup of all - atoms in the Universe at :attr:`MDAnalysis.core.universe.Universe.atoms`. + An :class:`AtomGroup` is an ordered collection of atoms. Typically, an + :class:`AtomGroup` is generated from a selection, or by indexing/slicing + the :class:`AtomGroup` of all atoms in the :class:`Universe` at + :attr:`MDAnalysis.core.universe.Universe.atoms`. An AtomGroup can be indexed and sliced like a list:: @@ -1385,23 +1386,6 @@ class AtomGroup(GroupBase): atoms is crucial (for instance, in order to define angles or dihedrals). - Atoms can also be accessed in a Pythonic fashion by using the atom name as - an attribute. For instance, :: - - ag.CA - - will provide a :class:`AtomGroup` of all CA atoms in the - group. These *instant selector* attributes are auto-generated for - each atom name encountered in the group. - - .. note:: - - The name-attribute instant selector access to atoms is mainly - meant for quick interactive work. Thus it either returns a - single :class:`Atom` if there is only one matching atom, *or* a - new :class:`AtomGroup` for multiple matches. This makes it - difficult to use the feature consistently in scripts. - AtomGroups can be compared and combined using group operators. For instance, AtomGroups can be concatenated using `+` or :meth:`concatenate`:: @@ -1487,12 +1471,46 @@ class AtomGroup(GroupBase): AtomGroup instances are always bound to a :class:`MDAnalysis.core.universe.Universe`. They cannot exist in isolation. + + .. rubric:: Deprecated functionality + + *Instant selectors* will be removed in the 1.0 release. See issue `#1377 + `_ for more details. + + Atoms can also be accessed in a Pythonic fashion by using the atom name as + an attribute. For instance, :: + + ag.CA + + will provide a :class:`AtomGroup` of all CA atoms in the + group. These *instant selector* attributes are auto-generated for + each atom name encountered in the group. + + .. note:: + + The name-attribute instant selector access to atoms is mainly + meant for quick interactive work. Thus it either returns a + single :class:`Atom` if there is only one matching atom, *or* a + new :class:`AtomGroup` for multiple matches. This makes it + difficult to use the feature consistently in scripts. + + See Also -------- :class:`MDAnalysis.core.universe.Universe` + + .. deprecated:: 0.16.2 + *Instant selectors* of AtomGroup will be removed in the 1.0 release. + See issue `#1377 + `_ for + more details. + """ def __getitem__(self, item): + # DEPRECATED in 0.16.2 + # REMOVE in 1.0 + # # u.atoms['HT1'] access, otherwise default if isinstance(item, string_types): try: @@ -1502,6 +1520,9 @@ def __getitem__(self, item): return super(AtomGroup, self).__getitem__(item) def __getattr__(self, attr): + # DEPRECATED in 0.16.2 + # REMOVE in 1.0 + # # is this a known attribute failure? if attr in ('fragments',): # TODO: Generalise this to cover many attributes # eg: @@ -2460,7 +2481,7 @@ def ix(self): @property def ix_array(self): """Unique index of this component as an array. - + This method gives a consistent API between components and groups. See Also @@ -2637,6 +2658,12 @@ class Segment(ComponentBase): ComponentBase, so this class only includes ad-hoc methods specific to Segments. + .. deprecated:: 0.16.2 + *Instant selectors* of Segments will be removed in the 1.0 release. + See issue `#1377 + `_ for + more details. + """ def __repr__(self): me = '`_ for more details. + + If the loaded topology provided segids, then these are made accessible as attributes of the Universe. If the segid starts with a number such as '4AKE', the letter 's' will be prepended to the segid. @@ -313,6 +318,10 @@ def _generate_from_topology(self): # Update Universe namespace with segids # Many segments can have same segid, so group together first + # + # DEPRECATED in 0.16.2 + # REMOVE in 1.0 + # See https://github.com/MDAnalysis/mdanalysis/issues/1377 try: # returns dict of segid:segment segids = self.segments.groupby('segids') @@ -492,7 +501,7 @@ def transfer_to_memory(self, start=None, stop=None, step=None, # object, to provide fast access and allow coordinates # to be manipulated if step is None: - step = 1 + step = 1 self.trajectory = MemoryReader( coordinates, dimensions=self.trajectory.ts.dimensions, diff --git a/package/doc/sphinx/source/documentation_pages/selections.rst b/package/doc/sphinx/source/documentation_pages/selections.rst index 77851225050..c4773df5eb1 100644 --- a/package/doc/sphinx/source/documentation_pages/selections.rst +++ b/package/doc/sphinx/source/documentation_pages/selections.rst @@ -285,6 +285,17 @@ across frames:: Instant selectors ================= +.. deprecated:: 0.16.2 + + *Instant selectors* will be removed in the 1.0 release in order to + streamline the MDAnalysis user interface. They do not seem to be + widely used anymore, can produce cryptic error messages, and are + not considered "Pythonic" (and therefore not very intuitive for new + users). See issue `#1377 + `_ for more + details. + + For interactive work it becomes rather tedious to type common selection strings repeatedly. MDAnalysis automatically generates a number of *instant selectors* as attributes of the :class:`~MDAnalysis.core.universe.Universe` and number of