Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
44 commits
Select commit Hold shift + click to select a range
bb9b69d
feat(units): add expression parser with SI dimensional analysis
HaoZeke Mar 9, 2026
bff8428
test(units): address review feedback on tests
HaoZeke Mar 9, 2026
eb8e28a
docs(units): move unit docs to Doxygen, simplify misc.rst
HaoZeke Mar 9, 2026
1aa7e0e
fix: address remaining review items
HaoZeke Mar 9, 2026
006284d
style: use literal µ instead of \u00b5 escape in Python tests
HaoZeke Mar 9, 2026
7eb3d41
style: use literal µ instead of hex escapes in C++ sources
HaoZeke Mar 9, 2026
4c8ab76
fix: ASCII-only tolower and restore documentation.py stub
HaoZeke Mar 9, 2026
bf743a1
fix: address Luthaf review on unit expression parser
HaoZeke Mar 11, 2026
71331c1
fix: add [[deprecated]] attribute to 3-arg unit_conversion_factor
HaoZeke Mar 11, 2026
cb9756a
fix: make unit_conversion_factor TorchScript-compatible
HaoZeke Mar 11, 2026
7ba0e25
fix: add units.hpp to umbrella header, use unit_conversion_factor in …
HaoZeke Mar 11, 2026
e9217ad
style: fix ruff format in units test
HaoZeke Mar 11, 2026
3762bf1
fix(units): throw error on empty unit strings instead of returning 1.0
HaoZeke Mar 12, 2026
e8ba70f
fix(units): add overflow/underflow checks for arithmetic operations
HaoZeke Mar 12, 2026
1e16a03
docs(units): document dimension comparison tolerance choice
HaoZeke Mar 12, 2026
40a669c
chor(units): add DimIndex enum for dim vec indices
HaoZeke Mar 12, 2026
6c2ee73
perf(units): add thread-local cache for parsed unit expressions
HaoZeke Mar 12, 2026
8fd89fa
improve(units): use human-readable dimension string format
HaoZeke Mar 12, 2026
3a7893d
test(units): add overflow/underflow detection tests
HaoZeke Mar 12, 2026
edb7548
test(units): add accumulated floating-point error tests
HaoZeke Mar 12, 2026
44a8304
docs(units): clarify quantity validation behavior
HaoZeke Mar 12, 2026
f72635b
docs(units): add migration guide
HaoZeke Mar 12, 2026
4e7a87b
fix(units): revert to backwards-compatible empty string handling
HaoZeke Mar 12, 2026
eaf9e34
test(units): add multithreading tests for thread-local cache
HaoZeke Mar 12, 2026
c325846
chore(units): even more docs
HaoZeke Mar 12, 2026
86b6d1a
chore(lint): apply
HaoZeke Mar 12, 2026
b378421
fix(units): remove extra namespace closing brace
HaoZeke Mar 12, 2026
7678e68
test(units): fix overflow tests to use same-dimension conversions
HaoZeke Mar 12, 2026
7454923
test(units): fix overflow tests to use units with extreme SI factors
HaoZeke Mar 12, 2026
fb312d2
test(units): fix division overflow test to use same dimension
HaoZeke Mar 12, 2026
0c479b7
fix: use single unit_conversion_factor op with argument dispatch
HaoZeke Mar 12, 2026
3417510
fix: use double return type in lambda, update test
HaoZeke Mar 12, 2026
090fbf9
docs: add comprehensive unit table with µm/µs support
HaoZeke Mar 12, 2026
dd9b8ee
docs: update unit documentation for expression parser API
HaoZeke Mar 13, 2026
37899dd
fix(docs): add missing label and fix title underline in misc.rst
HaoZeke Mar 13, 2026
f29211f
fix: address review round 3 comments
HaoZeke Mar 15, 2026
88c0c33
fix(docs): update all cross-refs to renamed _known-base-units label
HaoZeke Mar 15, 2026
ef3a1df
test: add coverage for version_compatible, unit types, and kwargs compat
HaoZeke Mar 15, 2026
c7a839b
chore(pragma): swallow deprecations
HaoZeke Mar 16, 2026
10a5e2c
feat(units): more flexible argument management
HaoZeke Mar 17, 2026
3a98e26
style: apply ruff formatting to tests/units.py
HaoZeke Mar 17, 2026
d987f48
refactor(units): remove thread-local cache (YAGNI per review)
HaoZeke Mar 17, 2026
39bb015
Cleanup
Luthaf Mar 17, 2026
b0fcc4a
Use tigher tests
Luthaf Mar 18, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion docs/src/torch/reference/cxx/misc.rst
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
Miscellaneous
=============

.. doxygenfunction:: metatomic_torch::unit_conversion_factor
.. doxygenfunction:: metatomic_torch::unit_conversion_factor(const std::string &from_unit, const std::string &to_unit)

.. doxygenfunction:: metatomic_torch::unit_conversion_factor(const std::string &quantity, const std::string &from_unit, const std::string &to_unit)

.. doxygenfunction:: metatomic_torch::pick_device

Expand Down
92 changes: 59 additions & 33 deletions docs/src/torch/reference/misc.rst
Original file line number Diff line number Diff line change
Expand Up @@ -7,36 +7,62 @@ Miscellaneous

.. autofunction:: metatomic.torch.unit_conversion_factor

.. _known-quantities-units:

Known quantities and units
--------------------------

The following quantities and units can be used with metatomic models. Adding new
units and quantities is very easy, please contact us if you need something else!
In the mean time, you can create :py:class:`metatomic.torch.ModelOutput` with
quantities that are not in this table. A warning will be issued and no unit
conversion will be performed.

When working with one of the quantities in this table, the unit you use must be
one of the registered unit.

+----------------+------------------------------------------------------------------------------------------------------------------------------------------------------+
| quantity | units |
+================+======================================================================================================================================================+
| **length** | ``angstrom`` (``A``), ``Bohr``, ``meter``, ``centimeter`` (``cm``), ``millimeter`` (``mm``), ``micrometer`` (``um``, ``µm``), ``nanometer`` (``nm``) |
+----------------+------------------------------------------------------------------------------------------------------------------------------------------------------+
| **energy** | ``eV``, ``meV``, ``Hartree``, ``kcal/mol``, ``kJ/mol``, ``Joule`` (``J``), ``Rydberg`` (``Ry``) |
+----------------+------------------------------------------------------------------------------------------------------------------------------------------------------+
| **force** | ``eV/Angstrom`` (``eV/A``), ``Hartree/Bohr`` |
+----------------+------------------------------------------------------------------------------------------------------------------------------------------------------+
| **pressure** | ``eV/Angstrom^3`` (``eV/A^3``) |
+----------------+------------------------------------------------------------------------------------------------------------------------------------------------------+
| **momentum** | ``u*A/fs``, ``u*A/ps``, ``(eV*u)^(1/2)``, ``kg*m/s``, ``hbar/Bohr`` |
+----------------+------------------------------------------------------------------------------------------------------------------------------------------------------+
| **mass** | ``u`` (``Dalton``), ``kg`` (``kilogram``), ``g`` (``gram``), ``electron_mass`` (``m_e``) |
+----------------+------------------------------------------------------------------------------------------------------------------------------------------------------+
| **velocity** | ``nm/fs``, ``A/fs``, ``m/s``, ``nm/ps``, ``Bohr*Hartree/hbar`` |
+----------------+------------------------------------------------------------------------------------------------------------------------------------------------------+
| **charge** | ``e``, ``Coulomb`` (``C``) |
+----------------+------------------------------------------------------------------------------------------------------------------------------------------------------+
The set of recognized base units is documented in the
:cpp:func:`C++ API reference <metatomic_torch::unit_conversion_factor>`.

The :py:func:`unit_conversion_factor` function accepts any valid unit expression
built from base units combined with operators. There is no need to specify a
physical quantity --- the parser automatically verifies dimensional compatibility
between the source and target units.

.. _known-base-units:

Supported base units
~~~~~~~~~~~~~~~~~~~~

Unit expressions are built from the following base units. Matching is
case-insensitive, and whitespace is ignored.

**Length**:
``angstrom`` (``A``), ``Bohr``, ``meter`` (``m``), ``centimeter`` (``cm``),
``millimeter`` (``mm``), ``micrometer`` (``um``, ``µm``), ``nanometer`` (``nm``)

**Energy**:
``eV``, ``meV``, ``Hartree``, ``kcal``, ``kJ``, ``Joule`` (``J``), ``Rydberg`` (``Ry``)

**Time**:
``second`` (``s``), ``millisecond`` (``ms``), ``microsecond`` (``us``, ``µs``),
``nanosecond`` (``ns``), ``picosecond`` (``ps``), ``femtosecond`` (``fs``)

**Mass**:
``u`` (``Dalton``), ``kilogram`` (``kg``), ``gram`` (``g``), ``electron_mass`` (``m_e``)

**Charge**:
``e``, ``Coulomb`` (``C``)

**Dimensionless**:
``mol``

**Derived constants**:
``hbar``

Expression syntax
~~~~~~~~~~~~~~~~~~~

Base units can be combined using the following operators:

- Multiplication: ``*`` or whitespace (``kJ mol``, ``kJ*mol``)
- Division: ``/`` (``kJ/mol``)
- Exponentiation: ``^`` (``A^3``, ``m^2``)
- Parentheses: ``()`` for grouping (``(eV*u)^(1/2)``)

Examples of valid compound expressions:

- ``kJ/mol`` --- energy per mole
- ``eV/Angstrom^3`` or ``eV/A^3`` --- pressure
- ``(eV*u)^(1/2)`` --- momentum (fractional powers)
Comment thread
Luthaf marked this conversation as resolved.
- ``Hartree/Bohr`` --- force in atomic units
- ``nm/fs`` --- velocity

The parser automatically checks that both unit expressions have matching
physical dimensions before computing the conversion factor.
12 changes: 12 additions & 0 deletions metatomic-torch/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,18 @@ a changelog](https://keepachangelog.com/en/1.1.0/) format. This project follows

## [Unreleased](https://github.com/metatensor/metatomic/)

### Added

- Unit expression parser supporting compound expressions (`kJ/mol/A^2`,
`(eV*u)^(1/2)`, etc.) with automatic dimensional validation
- 2-argument `unit_conversion_factor(from_unit, to_unit)` that parses
arbitrary unit expressions and checks dimensional compatibility

### Changed

- 3-argument `unit_conversion_factor(quantity, from_unit, to_unit)` is
deprecated; the `quantity` parameter is ignored

## [Version 0.1.11](https://github.com/metatensor/metatomic/releases/tag/metatomic-torch-v0.1.11) - 2026-02-27

### Added
Expand Down
2 changes: 2 additions & 0 deletions metatomic-torch/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,7 @@ find_package(Torch 2.3 REQUIRED)
set(METATOMIC_TORCH_HEADERS
"include/metatomic/torch/system.hpp"
"include/metatomic/torch/model.hpp"
"include/metatomic/torch/units.hpp"
"include/metatomic/torch.hpp"
"include/metatomic/torch/outputs.hpp"
)
Expand All @@ -104,6 +105,7 @@ set(METATOMIC_TORCH_SOURCE
"src/misc.cpp"
"src/system.cpp"
"src/model.cpp"
"src/units.cpp"
"src/outputs.cpp"
"src/register.cpp"
"src/internal/shared_libraries.cpp"
Expand Down
1 change: 1 addition & 0 deletions metatomic-torch/include/metatomic/torch.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,5 @@
#include "metatomic/torch/misc.hpp" // IWYU pragma: export
#include "metatomic/torch/system.hpp" // IWYU pragma: export
#include "metatomic/torch/model.hpp" // IWYU pragma: export
#include "metatomic/torch/units.hpp" // IWYU pragma: export
#include "metatomic/torch/outputs.hpp" // IWYU pragma: export
18 changes: 0 additions & 18 deletions metatomic-torch/include/metatomic/torch/model.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -28,16 +28,6 @@ class ModelMetadataHolder;
/// TorchScript will always manipulate `ModelMetadataHolder` through a `torch::intrusive_ptr`
using ModelMetadata = torch::intrusive_ptr<ModelMetadataHolder>;

/// Check that a given physical quantity is valid and known. This is
/// intentionally not exported with `METATOMIC_TORCH_EXPORT`, and is only
/// intended for internal use.
bool valid_quantity(const std::string& quantity);

/// Check that a given unit is valid and known for some physical quantity. This
/// is intentionally not exported with `METATOMIC_TORCH_EXPORT`, and is only
/// intended for internal use.
void validate_unit(const std::string& quantity, const std::string& unit);


/// Information about one of the quantity a model can compute
class METATOMIC_TORCH_EXPORT ModelOutputHolder: public torch::CustomClassHolder {
Expand Down Expand Up @@ -326,14 +316,6 @@ METATOMIC_TORCH_EXPORT metatensor_torch::Module load_atomistic_model(
c10::optional<std::string> extensions_directory = c10::nullopt
);

/// Get the multiplicative conversion factor to use to convert from unit `from`
/// to unit `to`. Both should be units for the given physical `quantity`.
METATOMIC_TORCH_EXPORT double unit_conversion_factor(
const std::string& quantity,
const std::string& from_unit,
const std::string& to_unit
);

}

#endif
77 changes: 77 additions & 0 deletions metatomic-torch/include/metatomic/torch/units.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
#ifndef METATOMIC_TORCH_UNITS_HPP
#define METATOMIC_TORCH_UNITS_HPP

#include <string>

#include "metatomic/torch/exports.h"

namespace metatomic_torch {

/// Check that a given physical quantity is valid and known. This is
/// intentionally not exported with `METATOMIC_TORCH_EXPORT`, and is only
/// intended for internal use.
///
/// Known quantities are: "length", "energy", "force", "pressure", "momentum",
/// "mass", "velocity", and "charge".
bool valid_quantity(const std::string& quantity);

/// Check that a given unit is valid and known for some physical quantity. This
/// is intentionally not exported with `METATOMIC_TORCH_EXPORT`, and is only
/// intended for internal use.
///
/// This function parses the unit expression and verifies that its physical
/// dimensions match the expected dimensions for the given quantity. For example,
/// `validate_unit("energy", "eV")` succeeds, but `validate_unit("energy", "eV/A")`
/// throws an error because eV/A has dimensions of force, not energy.
void validate_unit(const std::string& quantity, const std::string& unit);

/// Get the multiplicative conversion factor to use to convert from
/// `from_unit` to `to_unit`. Both units are parsed as expressions (e.g.
/// "kJ/mol/A^2", "(eV*u)^(1/2)") and their dimensions must match.
///
/// Unit expressions are built from base units combined with `*`, `/`, `^`,
/// and parentheses. Unit lookup is case-insensitive, and whitespace is
/// ignored. For example:
///
/// - `"kJ/mol"` -- energy per mole
/// - `"eV/Angstrom^3"` -- pressure
/// - `"(eV*u)^(1/2)"` -- momentum (fractional powers)
/// - `"Hartree/Bohr"` -- force in atomic units
///
/// Supported base units:
///
/// - **Length**: angstrom (A), bohr, nanometer (nm), meter (m),
/// centimeter (cm), millimeter (mm), micrometer (um, µm)
/// - **Energy**: eV, meV, Hartree, Ry (rydberg), Joule (J), kcal, kJ
/// (note: kcal and kJ are bare; write kcal/mol for per-mole)
/// - **Time**: second (s), millisecond (ms), microsecond (us, µs),
/// nanosecond (ns), picosecond (ps), femtosecond (fs)
/// - **Mass**: dalton (u), kilogram (kg), gram (g), electron_mass (m_e)
/// - **Charge**: e, coulomb (c)
/// - **Dimensionless**: mol
/// - **Derived**: hbar
///
/// Note on quantity validation:
/// The 2-argument form `unit_conversion_factor(from_unit, to_unit)` does not
/// take a quantity parameter. Dimensional compatibility is checked automatically
/// by comparing the parsed dimensions of both unit expressions. The deprecated
/// 3-argument form accepts a `quantity` parameter, but it is ignored for the
/// conversion calculation - it only emits a deprecation warning.
METATOMIC_TORCH_EXPORT double unit_conversion_factor(
const std::string& from_unit,
const std::string& to_unit
);

/// @deprecated Use the 2-argument overload instead. The `quantity` parameter
/// is ignored; dimensional compatibility is checked by the parser. Emits a
/// one-time runtime deprecation warning.
[[deprecated("use the 2-argument unit_conversion_factor(from_unit, to_unit) instead")]]
METATOMIC_TORCH_EXPORT double unit_conversion_factor(
Comment thread
HaoZeke marked this conversation as resolved.
const std::string& quantity,
const std::string& from_unit,
const std::string& to_unit
);

}

#endif
Loading
Loading