Skip to content

OpenFF compatibility: openff-units form, OpenMM consistency and serialization interop #86

Description

@dprada

What. Make PyUnitWizard interoperate with OpenFF's units stack, so that quantities move between OpenFF and the rest of the ecosystem (MolSysMT, OpenMM, astropy, unyt, and the serialization codec of #82) in one call, without losing or misreading a unit.

OpenFF is the open force-field ecosystem closest to MolSysMT and MolSysSuite, which makes it a natural bridge for our users. Split out of #85 at Diego's request (2026-09-24).

How OpenFF handles units (checked 2026-09-24):

  • openff-units is built on pint with its own registry: "a common unit registry for all OpenFF packages ... to ensure consistent unit definitions" (README). Its unit definitions come from NIST CODATA.
  • OpenMM conversion: openff.units.openmm.to_openmm / from_openmm. from_openmm turns OpenMM Vec3 lists into pint-wrapped numpy arrays.
  • Serialization:
    • scalars with str() → '10.0 kcal / mol / nm ** 2', and back with the Quantity constructor;
    • pydantic models (openff-models, openff-interchange) as {"val": ..., "unit": "..."} (openff-models).
  • Distribution: conda-forge openff-units 0.4.0 (noarch; also on PyPI). It depends on numpy, openff-utilities, pint >=0.24.0,<0.26, and python >=3.12,<3.15.

Scope.

  1. Prerequisite: Quantities from another pint UnitRegistry are not recognized and cannot be converted #84. Today puw.is_quantity returns False for a quantity from another pint registry, and convert raises. Nothing below works until that is fixed.

  2. A form openff.units.

    • Detect OpenFF quantities.
    • convert(q, to_form="openff.units") and back, preserving arrays and dtype.
    • Check that unit definitions agree between the two registries, within a stated tolerance. Fail loudly if they do not; never convert silently across different definitions.
  3. OpenMM consistency. A round trip OpenFF → PyUnitWizard → OpenMM must match openff-units' own to_openmm, and the reverse must match from_openmm, including Vec3 lists.

  4. Serialization interop with QuantityRecord: PyUnitWizard's native inert interchange form (verified quantity records) #82. Read and write OpenFF's {"val", "unit"} records and scalar strings, and convert them to and from the verified codec records.

    A {"val", "unit"} record carries no integrity guarantee. A verified record exported to OpenFF's shape must therefore be documented as leaving the verified domain, and importing one is a boundary that runs the handshake.

  5. Optional dependency. It must be optional, loaded through DepDigest, as pyunitwizard[openff] or a conda extra. openff-units needs Python ≥ 3.12, and PyUnitWizard supports 3.11, so on 3.11 the form is unavailable with a clear diagnostic. It also pins pint <0.26, a constraint to watch against PyUnitWizard's own pint range.

  6. Tests.

    • Round trips for scalars, n-D arrays and compound units (e.g. kcal/mol/nm**2).
    • Cross-registry arithmetic after conversion.
    • A deliberately altered unit definition, which must be refused.
    • Conformance under a non-default session policy.

Why here. PyUnitWizard is the interoperability layer (devguide/interop_future_directions.md). A bridge written separately in each consumer diverges, and divergence is where units get lost. The review in #83 found OpenFF's per-value records to be the nearest precedent to #82 in our domain.

Related: #84 (prerequisite), #85 (other forms and dialects), #82 (codec), #83 (design record and review).

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    needs-triageAwaiting maintainer triageproposalDesign or governance proposal

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions