Units of measure for biomolecular software.
OpenFF Units is based on Pint. Its
Measurement types inherit from Pint’s, and add improved support for serialization and deserialization. OpenFF Units improves support for biomolecular software by providing a system of units that are compatible with OpenMM and providing functions to convert to OpenMM units and back. It also provides atomic masses with units, as well as some other useful maps.
We recommend installing OpenFF Units with the Conda or Mamba package managers. If you don’t yet have a Conda distribution installed, we recommend MambaForge for most users; see the OpenFF install docs. The
openff-units package can be installed from Conda Forge:
conda install -c conda-forge openff-units
OpenFF Units provides the
Quantity class, which represents a numerical value with units. A
Quantity can be created by providing a value and units:
>>> from openff.units import unit, Quantity >>> >>> Quantity(1.007, unit.amu) <Quantity(1.007, 'unified_atomic_mass_unit')>
unit singleton value is a registry of units, but also exposes the
Measurement classes so you don’t have to import them individually. Even easier, multiplying a number by the appropriate unit also provides a
>>> mass_proton = 1.007 * unit.amu >>> mass_proton == unit.Quantity(1.007, unit.amu) True
Quantity can also wrap NumPy arrays. It’s best to wrap an array of floats in a quantity, rather than have an array of quantities:
>>> import numpy as np >>> >>> box_vectors = np.array([ ... [5.0, 0.0, 0.0], ... [0.0, 5.0, 0.0], ... [0.0, 0.0, 5.0], ... ]) * unit.nanometer
When constructed like this,
Quantity is transparent; it will pass any attributes it doesn’t have through to the inner value. This means that an quantity-wrapped array can be used exactly as though it were an array — the units are just checked silently in the background:
>>> from numpy.random import rand >>> >>> trajectory = 10 * rand(10, 10000, 3) * unit.nanometer >>> centroids = trajectory.mean(axis=1)[..., None] >>> last_water = trajectory[:, 97:99, :] >>> last_water_recentered = last_water - centroids
This transparency works with most container types, so it’s usually best to have
Quantity be the outermost wrapper type.
Complex units can be constructed by combining units with the usual arithmetic operations:
>>> boltzmann_constant = 8.314462618e-3 * unit.kilojoule / unit.kelvin / unit.avogadro_number
Some common constants are provided as units as well:
>>> boltzmann_constant = 1.0 * unit.boltzmann_constant
Adding or subtracting different units with the same dimensions just works:
>>> 1.0 * unit.angstrom + 1.0 * unit.nanometer <Quantity(11.0, 'angstrom')>
But quantities with different dimensions raise an exception:
>>> 1.0 * unit.angstrom + 1.0 * unit.nanojoule Traceback (most recent call last): ... pint.errors.DimensionalityError: Cannot convert from 'angstrom' ([length]) to 'nanojoule' ([length] ** 2 * [mass] / [time] ** 2)
Quantities can be converted between units with the
>>> (1.0 * unit.nanometer).to(unit.angstrom) <Quantity(10.0, 'angstrom')>
Or with the
.ito() method for in-place transformations:
>>> quantity = 10.0 * unit.angstrom >>> quantity.ito(unit.nanometer) >>> quantity <Quantity(1.0, 'nanometer')>
>>> quantity = (1.0 * unit.k_B).to_base_units() >>> assert quantity.units == unit.kilogram * unit.meter**2 / unit.kelvin / unit.second**2 >>> quantity.magnitude 1.380649e-23
Alternatively, specify the target units of the output magnitude with
>>> quantity = 1.0 * unit.k_B >>> quantity.m_as(unit.kilogram * unit.meter**2 / unit.kelvin / unit.second**2) 1.380649e-23
>>> from openff.units.openmm import from_openmm, to_openmm >>> >>> quantity = 10.0 * unit.angstrom >>> omm_quant = to_openmm(quantity) >>> omm_quant Quantity(value=10.0, unit=angstrom) >>> type(omm_quant) <class 'openmm.unit.quantity.Quantity'> >>> quant_roundtrip = from_openmm(omm_quant) >>> quant_roundtrip <Quantity(10.0, 'angstrom')> >>> type(quant_roundtrip) <class 'openff.units.units.Quantity'>
For more details, see the API reference.
#62 Drops support for Python 3.8, following NEP 29.