Source code for MDAnalysis.core.units

# -*- Mode: python; tab-width: 4; indent-tabs-mode:nil; coding: utf-8 -*-
# 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
#

"""
Constants and unit conversion --- :mod:`MDAnalysis.core.units`
===============================================================

The base units of MDAnalysis are *Angstrom* for *length* (1 Angstrom =
0.1 nm = 10^-10 m) and *ps* (pico second) for *time* (1 ps = 10^-12
sec). For *force* we adopted kJ/(mol*Angstrom).

All conversions: the conversion factor f to a unit b' for a quantity X
(whose numeric value relative to the base unit b is stored in the
program) is a quantity with unit `b'/b`. In the dictionaries below only
the numeric value `f(b->b')` is stored::

  X/b' = f(b->b') * X/b

:func:`get_conversion_factor` returns the appropriate factor f(b->b').

Conversion is done via the base units::

    x is in u1: from u1 to b:  x'  = x  / factor[u1]
                from b  to u2: x'' = x' * factor[u2]
    so f[u1,u2] = factor[u2]/factor[u1]


Conversions
-----------

density conversion factor. Base unit is A**-3::

   n/x = n/A**3 * densityUnit_factor[x]

nm::

   f = 1 A^-3/1 nm^-3 = 1/(10A)^-3 = 1/1000

Molar::

   factor = 1 A**-3 / (N_Avogadro * (10**-9 dm)**-3)

relative to a density rho0 in g/cm^3::

    M(H2O) = 18 g/mol   Molar mass of water

    factor = 1/(1e-24 * N_Avogadro / M(H2O))

from `rho/rho0 = n/(N_A * M**-1) / rho0`  where `[n] = 1/Volume`, `[rho] = mass/Volume`


.. SeeAlso:: Maybe we should simply use Quantities_?

.. _Quantities: http://packages.python.org/quantities/

Functions
---------

.. autofunction:: get_conversion_factor
.. autofunction:: convert

Data
----

.. autodata:: lengthUnit_factor
.. autodata:: N_Avogadro
.. autodata:: water
.. autodata:: densityUnit_factor
.. autodata:: timeUnit_factor
.. autodata:: speedUnit_factor
.. autodata:: forceUnit_factor
.. autodata:: chargeUnit_factor
.. autodata:: conversion_factor
.. autodata:: unit_types


References
----------

.. [Jorgensen1998]  W. Jorgensen, C. Jenson, J Comp Chem 19 (1998), 1179-1186

.. _AKMA: http://www.charmm.org/documentation/c37b1/usage.html#%20AKMA
.. _electron charge: http://physics.nist.gov/cgi-bin/cuu/Value?e
.. _`Avogadro's constant`: http://physics.nist.gov/cgi-bin/cuu/Value?na

"""

#: The basic unit of *length* in MDAnalysis is the Angstrom.
#: Conversion factors between the base unit and other lengthUnits *x* are stored.
#: Conversions follow `L/x = L/Angstrom * lengthUnit_factor[x]`.
#: *x* can be *nm*/*nanometer* or *fm*.
lengthUnit_factor = {'Angstrom': 1.0, 'A': 1.0, 'Å': 1.0, 'angstrom': 1.0,
                     'nm': 1.0/10, 'nanometer': 1.0/10,
                     'pm': 1e2, 'picometer': 1e2,
                     'fm': 1e5, 'femtometer': 1e5,
                     }


#: `Avogadro's constant`_ in mol**-1,
N_Avogadro = 6.02214179e+23  # mol**-1

#: water density values ay 1179: T=298K, P=1atm [Jorgensen1998]_
#:  ======== =========
#:  model    g cm**-3
#:  ======== =========
#:    SPC     0.985(1)
#:    TIP3P   1.002(1)
#:    TIP4P   1.001(1)
#:    exp     0.997
#:  ======== =========
#:
#: and molar mass 18.016 g mol**-1.
water = {'exp':0.997, 'SPC':0.985, 'TIP3P':1.002, 'TIP4P':1.001,  # in g cm**-3
         'MolarMass': 18.016,                                     # in g mol**-1
         }

#: The basic unit for *densities* is Angstroem**(-3), i.e.
#: the volume per molecule in A**3. Especially for water
#: it can be convenient to measure the density relative to bulk, and
#: hence a number of values are pre-stored in :data:`water`.
densityUnit_factor = {
    'Angstrom^{-3}': 1/1.0, 'A^{-3}': 1/1.0, 'Å^{-3}': 1/1.0,
    'nm^{-3}': 1/1e-3, 'nanometer^{-3}': 1/1e-3,
    'Molar': 1/(1e-27*N_Avogadro),
    'SPC':   1/(1e-24*N_Avogadro*water['SPC']  / water['MolarMass']),
    'TIP3P': 1/(1e-24*N_Avogadro*water['TIP3P']/ water['MolarMass']),
    'TIP4P': 1/(1e-24*N_Avogadro*water['TIP4P']/ water['MolarMass']),
    'water': 1/(1e-24*N_Avogadro*water['exp']  / water['MolarMass']),
    }


#: For *time*, the basic unit is ps; in particular CHARMM's
#: 1 AKMA_ time unit = 4.888821E-14 sec is supported.
timeUnit_factor = {'ps': 1.0, 'picosecond': 1.0,    # 1/1.0
                   'fs': 1e3, 'femtosecond': 1e3,   # 1/1e-3,
                   'ns': 1e-3, 'nanosecond': 1e-3,  # 1/1e3,
                   'second': 1e-12, 'sec':  1e-12, 's':  1e-12, # 1/1e12,
                   'AKMA': 1/4.888821e-2,
                   }
# getting the factor f:  1200ps * f = 1.2 ns  ==> f = 1/1000 ns/ps

#: For *speed*, the basic unit is Angstrom/ps.
speedUnit_factor = {'Angstrom/ps': 1.0, 'A/ps': 1.0, 'Å/ps': 1.0, 'Angstrom/picosecond': 1.0,  'angstrom/picosecond': 1.0, # 1
                    'Angstrom/AKMA': 4.888821e-2,
                    'nm/ps': 0.1, 'nanometer/ps': 0.1, 'nanometer/picosecond': 0.1,       # 1/10
                    'nm/ns': 0.1/1e-3,
                    'pm/ps': 1e2,
                    'm/s': 1e-10/1e-12,
                    }
# (TODO: build this combinatorically from lengthUnit and timeUnit)

#: For *force* the basic unit is kJ/(mol*Angstrom).
forceUnit_factor = {'kJ/(mol*Angstrom)': 1.0, 'kJ/(mol*A)': 1.0, 'kJ/(mol*Å)': 1.0,
                    'kJ/(mol*nm)': 10.0,
                    }
# (TODO: build this combinatorically from lengthUnit and ... a new energyUnit)

#: *Charge* is measured in multiples of the `electron charge`_
#: *e* =  1.602176487 x 10**(-19) C.
chargeUnit_factor = {'e': 1.0,
                     'Amber': 18.2223,  # http://ambermd.org/formats.html#parm
                     'C': 1.602176487e-19, 'As': 1.602176487e-19,
                     }

#: :data:`conversion_factor` is used by :func:`get_conversion_factor`:
#: Note: any observable with a unit (i.e. one with an entry in
#: the :attr:`unit` attribute) needs an entry in :data:`conversion_factor`
conversion_factor = {'length': lengthUnit_factor,
                     'density': densityUnit_factor,
                     'time': timeUnit_factor,
                     'charge': chargeUnit_factor,
                     'speed': speedUnit_factor,
                     'force': forceUnit_factor,
                     }

#: Generated lookup table (dict): returns the type of unit for a known input unit.
#: Note: Any unit must be *unique* because this dict is used to guess the
#: unit type.
unit_types = {}
for utype,ufactor in conversion_factor.items():
    for unit in ufactor.keys():
        assert not unit in unit_types  # see comment!
        unit_types[unit] = utype


[docs]def get_conversion_factor(unit_type, u1, u2): """generate the conversion factor u1 -> u2 by using the base unit as an intermediate f[u1 -> u2] = factor[u2]/factor[u1] Conversion of X (in u1) to X' (in u2): X' = conversion_factor * X """ # x is in u1: from u1 to b: x' = x / factor[u1] # from b to u2: x'' = x' * factor[u2] # so f[u1,u2] = factor[u2]/factor[u1] return conversion_factor[unit_type][u2] / conversion_factor[unit_type][u1]
[docs]def convert(x, u1, u2): """Convert value in unit *u1* to *u2*.""" try: ut1 = unit_types[u1] ut2 = unit_types[u2] except KeyError: raise ValueError("units must be one of %r, not %r or %r" % (unit_types.keys(), u1, u2)) if ut1 != ut2: raise ValueError("Cannot convert between unit types %(ut1)s --> %(ut2)s" % vars()) return x * get_conversion_factor(ut1, u1, u2)