# -*- Mode: python; tab-width: 4; indent-tabs-mode:nil; -*-
# vim: tabstop=4 expandtab shiftwidth=4 softtabstop=4
# MDAnalysis --- http://mdanalysis.googlecode.com
# Copyright (c) 2006-2011 Naveen Michaud-Agrawal,
#               Elizabeth J. Denning, Oliver Beckstein,
#               and contributors (see website for details)
# Released under the GNU Public Licence, v2 or any higher version
# Please cite your use of MDAnalysis in published work:
#     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
Core functions of MDAnalysis
The basic class is an :class:`~MDAnalysis.core.AtomGroup.AtomGroup`;
the whole simulation is called the
:class:`~MDAnalysis.core.AtomGroup.Universe`. Selections are computed
on an :class:`~MDAnalysis.core.AtomGroup.AtomGroup` and return another
:mod:`~MDAnalysis.Timeseries` are a convenient way to analyse trajectories.
To get started, load the Universe::
  u = Universe(psffilename,dcdfilename)
A simple selection of all water oxygens within 4 A of the protein::
  water_shell = u.selectAtoms('name OH2 and around 4.0 protein')
  water_shell.numberOfAtoms()   # how many waters were selected
  water_shell.totalMass()       # their total mass
:class:`AtomGroup` instances have various methods that allow
calculation of simple properties. For more complicated analysis,
obtain the coordinates as a numpy array ::
  coords = water_shell.coordinates()
and write your own Python code.
.. _flags-label:
(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
through the pseudo-dictionary :data:`MDAnalysis.core.flags`.
The entries appear as 'name'-'value' pairs. Flags check values and illegal ones
raise a :exc:`ValueError`. Documentation on all flags can be obtained with ::
 print MDAnalysis.core.flags.doc()
List of MDAnalysis flags with default values
.. autoclass:: flagsDocs
.. data:: flags
.. autoclass:: Flags
.. autoclass:: Flag
__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)
class Flags(dict):
    """Global registry of flags. Acts like a dict for item access.
    There are a number flags defined that influence how MDAnalysis behaves. They are
    accessible through the pseudo-dictionary
    The entries appear as 'name'-'value' pairs. Flags check values and illegal ones
    raise a :exc:`ValueError`. Documentation on all flags can be obtained with ::
      print MDAnalysis.core.flags.__doc__
    New flags are added with the :meth:`Flags.register` method which takes a new :class:`Flag`
    instance as an argument.
    def __init__(self,*args):
        """For DEVELOPERS: Initialize Flags registry with a *list* of :class:`Flag` instances."""
        super(Flags,self).__init__([(flag.name,flag) for flag in args])
    def get_flag(self,name):
        return super(Flags,self).__getitem__(name)
    def doc(self):
        """Shows doc strings for all flags."""
        return "\n\n".join([flag.__doc__ for flag in self._itervalues()])
    def register(self,flag):
        """Register a new Flag instance with the Flags registry."""
    def update(self,*flags):
        """Update Flags registry with a list of Flag instances."""
        super(Flags,self).update([(flag.name,flag) for flag in flags])
    def setdefault(self,k,d=None):
        raise NotImplementedError
    def __getitem__(self,name):
        return self.get_flag(name).get()
    def __setitem__(self,name,value):
    def _itervalues(self):
        return super(Flags,self).itervalues()
    def _items(self):
        return super(Flags,self).items()
    def itervalues(self):
        for flag in self._itervalues():
            yield flag.value
    def iteritems(self):
        for flag in self._itervalues():
            yield flag.name,flag.value
    def values(self):
        return [flag.value for flag in self._itervalues()]
    def items(self):
        return [(flag.name,flag.value) for flag in self._itervalues()]
    def __repr__(self):
        return str(self.items())
class FlagsDynamicDocs(Flags):
    # docs are generated on the fly for interactive use; but because
    # this does not work well with the sphinx documentation system
    # ("AttributeError: 'property' object has no attribute
    # 'expandtabs'") we split the class...
    def __doc__(self):
        # generate dynamic docs on all flags
        return self.doc()
class IdentityMapping(dict):
    def __getitem__(self,key):
        return key
class Flag(object):
    """A Flag, essentially a variable that knows its default and legal values."""
    def __init__(self,name,default,mapping=None,doc=None):
        """Create a new flag which will be registered with FLags.
          newflag = Flag(name,default,mapping,doc)
            name of the flag, must be a legal python name
            default value
            dict that maps allowed input values to canonical values;
            if ``None`` then no argument checking will be performed and
            all values are directly set.
            doc string; may contain string interpolation mappings for::
                    %%(name)s        name of the flag
                    %%(default)r     default value
                    %%(value)r       current value
                    %%(mapping)r     mapping
            Doc strings are generated dynamically and reflect the current state.
        self.name = name
        self.value = default
        self.default = default
        # {v1:v1,v2:v1,v3:v3, ...} mapping of allowed values to canonical ones
        self.mapping = mapping or IdentityMapping()
        self._doctemplate = "**%(name)s** = *%(value)r*\n" + (doc or "*undocumented flag*")
    def get(self):
        return self.value
    def set(self,value):
        if value is not None:
                self.value = self.mapping[value]
            except KeyError:
                raise ValueError("flag must be None or one of "+str(self.mapping.keys()))
        return self.get()
    def prop(self):
        """Use this for ``property(**flag.prop())``"""
        return {'fget':self.get, 'fset':self.set, 'doc':self.__doc__}
    def __repr__(self):
        return """Flag('%(name)s',%(value)r)""" % self.__dict__
class _Flag(Flag):
    def __doc__(self):
        # generate dynamic docs with current values
        return  self._doctemplate % self.__dict__
_flags = [
            Determines if distance selections (AROUND, POINT) respect periodicity.
            >>> flags['%(name)s'] = value
            Values of flag:
             * True     - periodicity is taken into account if supported
             * False    - periodicity is ignored
            The MDAnalysis preset of this flag is %(default)r.
            Note that KD-tree based distance selections always ignore this flag. (For
            details see the docs for the 'use_KDTree_routines' flag.)
         {True:'fast','fast':'fast',   # only KDTree if advantageous
          'always':'always',           # always even if slower (for testing)
          False:'never','never':'never'},  # never, only use (slower) alternatives
            Determines which KDTree routines are used for distance selections
            >>> flags['%(name)s'] = value
            Values for flag:
            * True, 'fast'   - only use KDTree routines that are typically faster than others
              -               POINT      uses distance matrix routines (with periodicity)
              -               AROUND     uses KDTree routines (always ignores periodicity)
            * 'always'       - always use KDTree routines where available (eg for benchmarking)
            * False, 'never' - always use alternatives
            The preset value for MDAnalysis is %(default)r.
            :mod:`MDAnalysis.KDTree` routines are significantly faster for some distance
            selections. However, they cannot deal with periodic boxes and thus ignore
            periodicity; if periodicity is crucial, disable KDTree routines with
            >>> MDAnalysis.core.flags['use_KDTree_routines'] = False
         {True:True, False:False},
            Determine if trajectory reader and writer converts length units between native and MDAnalysis default.
            >>> flags['%(name)s'] = value
            Some trajectories are in other length units than the MDAnalysis
            default (see the flag *length_unit*); e.g. Gromacs XTC and TRR
            trajectories are in nm. If ``True`` then coordinates are
            automatically converted, with ``False`` the coordinate values are
            presented as read from the trajectories.
            .. Note:: The conversion of lengths also affects conversion of velocities.
         {'Angstrom': 'Angstrom', 'A':'Angstrom',
          'nm': 'nm', 'nano meter': 'nm', 'nanometer': 'nm'
            Base unit for lengths (in particular coordinates in trajectories)
            >>> flags['%(name)s'] = value
            .. Warning:: Do not change, only Angstrom fully supported.
         {'ps': 'ps', 'pico second': 'ps', 'picosecond': 'ps',
          'ns': 'ns', 'nano second': 'ns', 'nanosecond': 'ns',
          'fs': 'fs', 'femto second': 'fs', 'femtosecond': 'fs',
          'AKMA': 'AKMA', 'Charmm': 'AKMA',
            Base unit for times (in particular time steps in trajectories)
            >>> flags['%(name)s'] = value
         {'Angstrom/ps': 'Angstrom/ps', 'A/ps':'Angstrom/ps',
          'nm/ps': 'nm/ps', 'nanometer/ps': 'nm/ps', 'nanometer/picosecond': 'nm/ps',
          'Angstrom/AKMA': 'Angstrom/AKMA', 'A/AKMA': 'Angstrom/AKMA',
          'pm/ps': 'pm/ps',
          'm/s': 'm/s',
            Base unit for speed (in particular velocities in trajectories)
            >>> flags['%(name)s'] = value
         {'kJ/(mol*Angstrom)': 'kJ/(mol*Angstrom)', 'kJ/(mol*A)': 'kJ/(mol*Angstrom)',
          'kJ/(mol*nm)': 'kJ/(mol*nm)',
            Base unit for forces (in particular forces in trajectories)
            >>> flags['%(name)s'] = value
         {'e': 'e', 'electron charge': 'e',
          'Amber': 'Amber',
          'C': 'C', 'Coulomb': 'C', 'As': 'C',
            Base unit for charge
            >>> flags['%(name)s'] = value
         {'primitive': True, 'permissive': True, True: True,
          'Bio.PDB': False, 'biopython': False, False: False,
            Select the default reader for PDB Brookhaven databank files.
            >>> flags['%(name)s'] = value
            The Bio.PDB reader (value=``False``) can deal with 'proper' PDB
            files from the Protein Databank that contain special PDB features
            such as insertion codes and it can auto-correct some common
            mistakes; see :mod:`Bio.PDB` for details. However, Bio.PDB has been
            known to read some simulation system PDB files **incompletely**; a
            sure sign of problems is a warning that an atom has appeared twice
            in a residue.
            Therefore, the default for the PDB reader is ``True``, which
            selects the "primitive" (or "permissive") reader
            :class:`MDAnalysis.coordinates.PDB.PrimitivePDBReader`, which
            essentially just reads ATOM and HETATM lines and puts atoms in a
            One can manually switch between the two by providing the *permissive*
            keyword to :class:`MDAnalysis.Universe`.
          {True: True, False: False},
          Choose whether to consider periodic boundary conditions when
          performing many :class:`MDAnalysis.core.AtomGroup.AtomGroup` methods.
          This is set to ``False`` by default but can be enabled with:
          >>> MDAnalysis.core.flags['use_pbc'] = True
          Values for flag:
          * ``True`` - Move all atoms within the primary unit cell before calculation
          * ``False`` - Use coordinates as supplied
          .. Warning::
             Changing this to ``True`` changes the default behaviour of
             commonly used :class:`MDAnalysis.core.AtomGroup.AtomGroup` methods
             such as :meth:`MDAnalysis.core.AtomGroup.AtomGroup.centerOfMass`
             and :meth:`MDAnalysis.core.AtomGroup.AtomGroup.centerOfGeometry`!
#: Global flag registry for :mod:`MDAnalysis.core`.
#: Can be accessed like a dictionary and appears to the casual user as such.
flags = FlagsDynamicDocs(*_flags)
del _flags
# only for sphinx docs
class flagsDocs(object):
    __doc__ = flags.doc()
import AtomGroup, Selection, Timeseries