Rust backend#
The computationally heavy parts of phonopy (dynamical-matrix builders,
reciprocal dipole-dipole, force-constant symmetrization, BZ-grid helpers,
smallest-vector search, tetrahedron-method weights, …) are implemented
as a Rust extension module called from Python through PyO3. The Rust
path is distributed as a separate Python extension module, phonors,
maintained in its own top-level repository at
github.com/phonopy/phonors and
built with maturin and
PyO3.
Since v4, phonors is the default backend and a required runtime
dependency of phonopy. The legacy C extension is still built by default
and can be selected per call via lang="C" or the --legacy-backend
CLI flag; both paths are kept in the source tree for cross-checking.
Numerical parity with the C path is verified by the regression tests
under test/.
Installation#
phonors is listed in pyproject.toml as a required dependency, so
pip install phonopy (and the conda-forge package once updated)
pulls it in automatically. No extra step is needed for normal use.
Building phonors from source#
When you want to track the development version of phonors, or to
build with custom Rust flags, install it from a source clone instead
of the PyPI wheel.
Requirements:
A Rust toolchain (stable, edition 2021,
rustc >= 1.75). The easiest way to install it is via rustup.maturin 1.7 or newer (available on PyPI and conda-forge).
Python 3.10 or newer. The extension is built against the stable ABI (
abi3-py310), so one build works for all Python 3.10+ interpreters.
Clone the repository and build the extension in editable mode:
% git clone https://github.com/phonopy/phonors.git
% cd phonors
% maturin develop --release
After a successful build, the module should import from any Python process
import phonors
Optional: native CPU tuning#
By default, maturin develop --release builds with the Rust baseline
target (x86-64 v1 on x86_64, Armv8.0 on aarch64), so the resulting
module runs on any CPU of that architecture. For a local build that
will only run on the current machine, enabling the host CPU’s full
instruction set can recover a few percent of wall-clock:
% RUSTFLAGS='-C target-cpu=native' maturin develop --release
Usage#
The Rust backend is active by default; no flag or keyword is required.
The legacy C backend is selected per call through the lang keyword on
the Python API or the --legacy-backend flag on the CLI.
Python API#
The constructor and loader take a lang keyword:
import phonopy
ph = phonopy.load("phonopy_disp.yaml") # lang="Rust" (default)
ph_c = phonopy.load("phonopy_disp.yaml", lang="C") # legacy C backend
The current value is exposed as the read-only Phonopy.lang property.
Valid values are "Rust" (default) and "C".
lang is threaded internally to every lang-aware consumer, including
Primitive, Symmetry, ShortestPairs, BZGrid, GridMatrix,
DynamicalMatrix*, DynmatToForceConstants, Mesh, IterMesh,
QpointsPhonon, DerivativeOfDynamicalMatrix, TetrahedronMethod,
TetrahedronDOSAccumulator, TotalDos, ProjectedDos, and
ThermalProperties. No per-call plumbing from the user side is required.
Command line#
The default phonopy invocation runs on Rust and prints
Rust backend (phonors) using rayon (N threads).
in the run header, where N follows rayon’s defaults. To opt back into
the C extension, pass --legacy-backend (conf-file equivalent:
LEGACY_BACKEND = .true.):
% phonopy --legacy-backend ...
The old --rust flag still parses but is a deprecated no-op (the Rust
backend is already active) and emits a DeprecationWarning. It will be
removed in a future release.
Thread pool#
The Rust kernels parallelize with rayon,
which uses its own thread pool. The thread count is controlled by
RAYON_NUM_THREADS (not OMP_NUM_THREADS, which only affects the C
path):
% RAYON_NUM_THREADS=8 phonopy ...
NumPy/SciPy BLAS multithreading used by phonon diagonalization is
independent and is controlled by the BLAS library’s own variables
(e.g. OPENBLAS_NUM_THREADS, MKL_NUM_THREADS).
Dispatch tracing#
To verify that a given code path is actually running on the Rust
backend, set PHONOPY_TRACE_LANG=1 before launching:
% PHONOPY_TRACE_LANG=1 phonopy ...
[phonopy.lang] dispatch name=Phonopy.__init__ lang=Rust
[phonopy.lang] dispatch name=Primitive.__init__ lang=Rust
[phonopy.lang] dispatch name=BZGrid.__init__ lang=Rust
...
Each lang-aware call site emits one line to stderr at construction time
(and the dynamical-matrix builders emit one per call). The trace is
silent by default. It is attached to a dedicated logger
(phonopy.lang) and is independent of the rest of the run log, so
enabling it does not affect normal output.
Building phonopy without the C extension#
For Rust-only deployments (or to validate that every dispatch site has a Rust path), phonopy can be installed with the C extension skipped:
% PHONOPY_NO_C_EXT=1 pip install -e . -vvv
When the env var is set, CMakeLists.txt returns early and neither
phonopy._phonopy nor phonopy._recgrid is built. At runtime,
Phonopy() and phonopy.load() detect the missing C extension, emit
a one-time [phonopy] C extension ... is not available; falling back to lang='Rust' ... message, and silently flip lang="C" requests to
lang="Rust".
To restore the C extension, simply rebuild without the env var:
% pip install -e . -vvv
Note
This option is intended for testing the Rust path and for packagers who want a Rust-only wheel. For day-to-day use the regular install (with the C extension) remains the recommended path.
Scope#
Rust kernels are wired through the lang dispatch for the following groups:
Grid construction (
BZGrid,GridMatrix: SNF, grid addresses, reciprocal / transform rotations, ir-grid mapping).Dipole-dipole and charge-sum kernels for the non-analytical term correction (Wang and Gonze variants).
Tetrahedron-method weights (
tetrahedra_relative_grid_address,TetrahedronDOSAccumulator).Force-constant utilities (
compute_permutation,perm_trans_symmetrize_fc/_compact_fc,transpose_compact_fc,distribute_fc2, drift FC computation).Smallest-vector search (
set_smallest_vectors_{sparse,dense}).Dynamical-matrix construction at arbitrary q-points (
dynamical_matrices_at_qpoints[_gonze]),transform_dynmat_to_fc, and the derivative of the dynamical matrix (derivative_dynmat).
Known limitations#
Dynamical-matrix diagonalization. LAPACK calls (phonon eigenproblem) stay in Python/NumPy by design. They are not part of the Rust port.
Pure-numpy paths. Routines that are already light-weight in NumPy (
ThermalProperties, per-omega tetrahedron lookups) acceptlangfor API consistency but do not call intophonors.
If any of these code paths is reached with lang="Rust", phonopy
transparently uses the C (or Python) implementation for that step.