Skip to main content

A common units module for the OpenFF software stack

Project description

openff-units

CI Status codecov pre-commit pre-commit.ci status

A common units module for the OpenFF software stack

Please note that there may still be some changes to the API prior to a stable 1.0.0 release.

This package provides a common unit registry for all OpenFF packages to use in order to ensure consistent unit definitions across the software ecosystem.

The unit definitions are currently sourced from the NIST 2018 CODATA, but may be updated in future versions as new CODATA updates are made.

While this repository is based on Pint, the main classes (Unit, Quantity, and Measurement) have been slightly modified in order to provide non-dynamic, more readily serialisable representations.

Installation

Install from PyPI using uv (recommended):

uv pip install neoralab-openff-units

Developer installation

Clone the repo, create a Python 3.12 environment with uv, and install editable dependencies:

git clone https://github.com/openforcefield/openff-units.git
cd openff-units
uv venv --python 3.12
source .venv/bin/activate
uv pip install -e ."[test,typing]" -e downstream_dummy/

Getting Started

Below shows how to tag a number with a unit (generating a Quantity object), get its magnitude with and without units, convert to another unit, and also get its magnitude after converting to another unit.

>>> from openff.units import Quantity
>>> bond_length = Quantity(1.4, "angstrom")
>>> bond_length
<Quantity(1.4, 'angstrom')>
>>> bond_length.magnitude
1.4
>>> bond_length.to("nanometer")
<Quantity(0.14, 'nanometer')>
>>> bond_length.m_as("nanometer")
0.14

One could also do the Pint tutorial using the unit object above as a drop-in replacement for ureg in the tutorial.

Serialization

Scalar quantities can be serialized to strings using the built-in str() function and deserialized using the unit.Quantity constructor.

>>> k = Quantity(10, "kilocalorie / mol / nanometer**2")
>>> k
<Quantity(10.0, 'kilocalorie / mole / nanometer ** 2')>
>>> str(k)
'10.0 kcal / mol / nm ** 2'
>>> Quantity(str(k))
<Quantity(10.0, 'kilocalorie / mole / nanometer ** 2')>

OpenMM Interoperability

For compatibility with OpenMM units, a submodule (openff.units.openmm) with conversion functions (to_openmm, from_openmm) is also provided.

>>> from openff.units import Quantity
>>> from openff.units.openmm import to_openmm, from_openmm
>>> distance = Quantity(24.0, "meter")
>>> converted = to_openmm(distance)
>>> converted
24.0 m
>>> type(converted)
<class 'openmm.unit.quantity.Quantity'>
>>> roundtripped = from_openmm(converted)
>>> roundtripped
<Quantity(24.0, 'meter')>
>>> type(roundtripped)
pint.Quantity

An effort is made to convert from OpenMM constructs, such as when OpenMM provides array-like data as a list of Vec3 objects:into Pint's wrapped NumPy arrays:

>>> from openmm import app
>>> positions = app.PDBFile("top.pdb").getPositions()
>>> positions
Quantity(value=[Vec3(x=-0.07890000000000001, y=-0.0198, z=-0.0), Vec3(x=-0.0006000000000000001, y=0.039200000000000006, z=-0.0), Vec3(x=0.07950000000000002, y=-0.0194, z=0.0), Vec3(x=0.9211, y=0.9802, z=1.0), Vec3(x=0.9994000000000001, y=1.0392, z=1.0), Vec3(x=1.0795000000000001, y=0.9805999999999999, z=1.0)], unit=nanometer)
>>> type(positions)
<class 'openmm.unit.quantity.Quantity'>
>>> type(positions._value)
<class 'list'>
>>> type(positions._value[0])
<class 'openmm.vec3.Vec3'>
>>> converted = from_openmm(positions)
>>> converted
<Quantity([[-7.8900e-02 -1.9800e-02 -0.0000e+00]
 [-6.0000e-04  3.9200e-02 -0.0000e+00]
 [ 7.9500e-02 -1.9400e-02  0.0000e+00]
 [ 9.2110e-01  9.8020e-01  1.0000e+00]
 [ 9.9940e-01  1.0392e+00  1.0000e+00]
 [ 1.0795e+00  9.8060e-01  1.0000e+00]], 'nanometer')>
>>> converted.m
array([[-7.8900e-02, -1.9800e-02, -0.0000e+00],
       [-6.0000e-04,  3.9200e-02, -0.0000e+00],
       [ 7.9500e-02, -1.9400e-02,  0.0000e+00],
       [ 9.2110e-01,  9.8020e-01,  1.0000e+00],
       [ 9.9940e-01,  1.0392e+00,  1.0000e+00],
       [ 1.0795e+00,  9.8060e-01,  1.0000e+00]])
>>> type(converted)
<class 'openff.units.units.Quantity'>
>>> type(converted.m)
<class 'numpy.ndarray'>

Dealing with multiple unit packages

You may find yourself needing to normalize a quantity to a particular unit package, while accepting inputs from either openff.units or openmm.unit. The ensure_quantity function simplifies this. It takes as arguments a quantity object from either unit solution and a string ("openff" or "openmm") indicating the desired unit type, and returns a quantity from that package. If the quantity argument is already the requested type, the function short-circuits, so it should not introduce substantial overhead compared to simply requiring the target quantity type.

>>> from openff.units import Quantity, ensure_quantity
>>> ensure_quantity(Quantity(4.0, "angstrom"), "openff")
<Quantity(4.0, 'angstrom')>  # OpenFF
>>> ensure_quantity(Quantity(4.0, "angstrom"), "openmm")
4.0 A
>>>
>>> import openmm.unit
>>> ensure_quantity(openmm.unit.Quantity(4.0, openmm.unit.angstrom), "openmm")
4.0 A
>>> ensure_quantity(openmm.unit.Quantity(4.0, openmm.unit.angstrom), "openff")
<Quantity(4.0, 'angstrom')>  # OpenFF

Known issues

There is a quirk with cached unit registry definitions that could cause issues when running tests in parallel (i.e. with pytest-xdist). See Issue #111 for more details. This was fixed in version 0.3.1.

Copyright

Copyright (c) 2021, Open Force Field Initiative

Acknowledgements

Project based on the Computational Molecular Science Python Cookiecutter version 1.5.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

neoralab_openff_units-0.1.0.tar.gz (18.2 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

neoralab_openff_units-0.1.0-py3-none-any.whl (21.7 kB view details)

Uploaded Python 3

File details

Details for the file neoralab_openff_units-0.1.0.tar.gz.

File metadata

  • Download URL: neoralab_openff_units-0.1.0.tar.gz
  • Upload date:
  • Size: 18.2 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.12.13

File hashes

Hashes for neoralab_openff_units-0.1.0.tar.gz
Algorithm Hash digest
SHA256 6291009d7cf1585a4874d04babff742a67cd56e70166411b6779a73098649e6a
MD5 5f214ee80316752a239fa1465434d4f0
BLAKE2b-256 5e682f5ec86a7cb7c623a3c5c00019f30d21390bb24f150ee4351e899dc4e863

See more details on using hashes here.

File details

Details for the file neoralab_openff_units-0.1.0-py3-none-any.whl.

File metadata

File hashes

Hashes for neoralab_openff_units-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 f61ae66d42ce4d2c21fa3fbd5d4a22ec916f583a19932e07ff965f9cc47f9977
MD5 abd0d849e381a2a15ad9b5ec67c717e1
BLAKE2b-256 d207c7b16cd2754a7008cc1aa56c9810061d167269c01568b8b0d06cc2420b7d

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page