Skip to main content

pm6-rs

Rust-native implementation of the PM6 semiempirical NDDO method (J. J. P. Stewart, J. Mol. Model. 13, 1173 (2007)). Heats of formation, fully analytic nuclear gradients and Hessians, L-BFGS geometry optimization, P-RFO transition-state search, and the PM6-D3 / PM6-D3H4 / PM6-D3H4X post-SCF corrections, with Rust, Python-native, ASE and command-line interfaces.

  • Author: ss0832
  • License: GPL-3.0-or-later
  • Pure Rust linear algebra (faer + rayon); no LAPACK/BLAS.

Scope and validation status

Validated against the MOPAC v23.2.5 oracle (openmopac/mopac, Apache-2.0); parameters are extracted from the same release.

Capability Status
SCF energy (RHF + UHF), heats of formation ✅ frozen molecular set; s/p ≤1e-6, hypervalent/TM ≤1e-4 kcal/mol vs MOPAC; see limitations below
d-orbital two-center integrals (S, P, Cl, transition metals, …) ✅ e.g. TiCl₄ ≤2e-5, HCl ≤1e-8 kcal/mol
Analytic gradient (dual-number, Hellmann–Feynman) ✅ s/p and d, matches MOPAC & FD
Analytic Hessian + frequencies ✅ closed-shell s/p/d CPHF; open-shell d uses FD-of-analytic-gradient
L-BFGS geometry optimization ✅ all elements, matches MOPAC minima
Mulliken charges ✅ (≤1e-4 e across s/p and d)
Dipole (full vector, s/p and d) ✅ vs MOPAC ≤3e-4 D; the p–d channel was missing before 0.2.1 and cost H₂S 1.39 D
External electric field: energy, analytic gradient, analytic Hessian ✅ RHF + UHF + DC, vs MOPAC FIELD= both signs; periodic refused
Dipole derivatives (atomic polar tensor), IR intensities ✅ three independent routes agree to 4e-8 e
Molecular orbitals, molden export, first-order orbital response ✅ round-tripped; the expensive products are lazy
Lanthanide sparkles (Ce…Yb) ⚠️ functional (0-orbital cores, oracle-derived ΔH_f); energy ~10 kcal/mol from MOPAC
D3 dispersion (PM6-D3) ✅ matches MOPAC ≤1e-5 kcal/mol (incl. 102 atoms)
H4 / X corrections (PM6-D3H4 / -D3H4X) ✅ matches MOPAC ≤1e-5 kcal/mol; see the H4 gate note below
PM6-D3/H4/X under PBC (1D/2D/3D lattice sums) ✅ supercell-exact; energy, gradient, stress, Hessian
Python native + ASE APIs (energy/grad/forces/Hessian/opt/freq)
PBC — periodic SCF at Γ and k, RHF + UHF, 1D/2D/3D, s/p and d ✅ supercell Γ ⇔ k mesh ≤1e-8 eV/atom
PBC — analytic gradient and analytic stress (6 Voigt components) ✅ vs E(ε) finite difference ≤1e-6
PBC — analytic Hessian (periodic CPHF at Γ; complex CPHF at k, q = 0) ✅ vs FD of analytic forces ≤1e-6
PBC — phonons at any q of the k-mesh, band structure, ZPE, free energy ✅ supercell force constants; ASR imposed
PBC — perturbation theory (DFPT) at any q of the k-mesh, primitive cell ✅ vs 2× supercell spectrum 0.0018 cm⁻¹; LO–TO dipole term pinned
PBC — charged cells (background / Makov–Payne), 1D/2D/3D ✅ NaCl Madelung matches; E(L) follows q²α_M/2L
Divide-and-conquer (molecular, PBC Γ, PBC k): energy, gradient, stress ✅ exponent 1.13 vs dense 1.81; crossover ~98 atoms

✅ = validated to the stated tolerance; ⚠️ = implemented and functional, not yet bit-exact. Because MOPAC has no PM6 periodic path, the periodic code is validated against internal identities — supercell Γ ⇔ k mesh, analytic ⇔ finite difference, D(q) ⇔ supercell Γ — each a statement that two independently-coded routes must agree. See docs/scope.md for the full table of capabilities and limitations, and THIRD_PARTY_NOTICES.md.

The H4 covalent gate is discontinuous

PM6-D3H4 selects a donor–H–acceptor triple only while the donor–H bond is covalent (rDH < 1.15 Å), and drops the whole triple the instant that cutoff is crossed. That is a genuine jump discontinuity in the potential energy surface — 2.3–3.7 kcal/mol on an ammonium/water pair, a water dimer and a formic acid dimer — and MOPAC behaves identically, so it belongs to the H4 model rather than to this implementation. Where the energy jumps the gradient contains a delta function and the Hessian does not exist, but automatic differentiation cannot see a branch it did not take and returns a plausible number regardless.

  • Default (H4Smoothing::Mopac): bit-exact MOPAC energies, discontinuous surface.
  • H4Smoothing::C2 / CLI --h4-smoothing c2: tapers the gate over the last 0.05 Å with the C² switch the model already uses elsewhere, giving a differentiable surface for optimization, MD, Hessians and phonons. Outside that window the energy is bit-identical to MOPAC.

pm6_rs::h4_discontinuity_warning reports proximity to the gate; the CLI warns automatically for gradient, optimize, saddle, frequencies and hessian.

Units

Internal computation uses eV and Bohr with MOPAC's 2018-CODATA model constants (src/constants.rs). At the boundaries:

  • Rust and Python-native APIs return atomic units (Hartree, Bohr), plus convenience eV fields and ΔH_f in kcal/mol.
  • The ASE calculator uses eV / Å (PM6 is natively eV, so no round-trip).

Build

cargo build --release        # library + pm6_rs_cli
cargo test                   # unit + MOPAC-oracle regression tests

CLI

pm6_rs_cli energy      water.xyz
pm6_rs_cli gradient    water.xyz
pm6_rs_cli optimize    water.xyz            # writes water.pm6opt.xyz
pm6_rs_cli frequencies water.pm6opt.xyz
pm6_rs_cli charges     water.xyz --charge 0 --multiplicity 1
pm6_rs_cli energy      dimer.xyz --method PM6-D3H4X   # PM6 | PM6-D3 | PM6-D3H4 | PM6-D3H4X

pip install pm6-rs-python installs the same interface as pm6-rs. Periodic runs read the cell from an extended-XYZ Lattice="..." header, or from --cell:

pm6-rs energy   chain.xyz --pbc FFT --kpts 1,1,8
pm6-rs stress   chain.xyz --pbc FFT --kpts 1,1,8
pm6-rs bands    chain.xyz --pbc FFT --kpts 1,1,8 --path "0,0,0;0,0,0.5"
pm6-rs phonons  chain.xyz --pbc FFT --kpts 1,1,8 --supercell 1,1,4
pm6-rs phonons-dfpt chain.xyz --pbc FFT --kpts 1,1,8 --qpoints "0,0,0;0.5,0,0"
pm6-rs energy   big.xyz   --dc --dc-buffer 12.0     # divide-and-conquer

Rust API

use pm6_rs::{Molecule, Pm6Parameters, Pm6Options, run_pm6};

let mol = Molecule::from_xyz_file("water.xyz", 0.0)?;
let params = Pm6Parameters::standard()?;
let r = run_pm6(&mol, &params, &Pm6Options::default())?;
println!("ΔHf = {} kcal/mol", r.heat_of_formation_kcal);

Python

pip install maturin
maturin develop --release --features python   # or: pip install pm6-rs-python
import pm6_rs
import numpy as np

numbers = [8, 1, 1]
positions = np.array([[0.0, 0.0, 0.0], [0.9584, 0.0, 0.0], [-0.24, 0.9278, 0.0]])

# charge, multiplicity and reference ("auto"/"rhf"/"uhf") are per-call arguments.
out = pm6_rs.single_point(numbers, positions, charge=0.0, multiplicity=1, reference="auto")
print(out["heat_of_formation_kcal"], out["charges"])

g = pm6_rs.gradient(numbers, positions)          # dE/dR (Hartree/Bohr and eV/Å)
f = pm6_rs.forces(numbers, positions)            # −dE/dR
h = pm6_rs.hessian(numbers, positions)           # Hartree/Bohr², 3N×3N
w = pm6_rs.frequencies(numbers, positions)       # cm⁻¹ (evaluate at a minimum)

Periodic

cell = [[20.0, 0.0, 0.0], [0.0, 20.0, 0.0], [0.0, 0.0, 2.55]]   # Å
pbc = (False, False, True)                                       # a 1D chain

r = pm6_rs.calculate(numbers, positions, cell=cell, pbc=pbc, kpts=(1, 1, 8))
print(r["energy_ev"], r["stress_ev_per_angstrom3"])   # Voigt: xx yy zz yz xz xy

ph = pm6_rs.phonons(numbers, positions, cell, pbc=pbc,
                    supercell=(1, 1, 4), kpts=(1, 1, 8))
print(ph["frequencies_cm"][0], ph["decay_ratio"])

charge is the net charge per cell, and heat_of_formation_kcal is None — ΔH_f is not defined per cell. kpts=None picks a mesh from the cell rather than defaulting to Γ, which is wrong (not merely coarse) on a small cell.

ASE

from ase.build import molecule
from pm6_rs.ase import PM6

atoms = molecule("H2O")
atoms.calc = PM6(charge=0, multiplicity=1, reference="auto")
print(atoms.get_potential_energy())   # eV
print(atoms.get_forces())             # eV/Å
print(atoms.calc.get_gradient())      # eV/Å  (= −forces)
print(atoms.calc.get_hessian())       # eV/Ų, 3N×3N

The calculator reads atoms.cell and atoms.pbc, so a periodic system needs no extra arguments, and get_stress() returns ASE's six-component Voigt vector in eV/ų — enough for StrainFilter, FrechetCellFilter and ase.md.npt.NPT:

from ase import Atoms
from ase.filters import FrechetCellFilter
from ase.optimize import BFGS

# Polyethylene: one C2H4 unit per cell, periodic along z only.
atoms = Atoms("C2H4",
              positions=[[0.000, 0.635, 0.000], [0.000, -0.635, 1.275],
                         [0.876, 1.235, 0.000], [-0.876, 1.235, 0.000],
                         [0.876, -1.235, 1.275], [-0.876, -1.235, 1.275]],
              cell=[20.0, 20.0, 2.55], pbc=(False, False, True))
atoms.calc = PM6(kpts=(1, 1, 8))

# mask relaxes the c axis only, which is the one that is periodic.
BFGS(FrechetCellFilter(atoms, mask=[0, 0, 1, 0, 0, 0])).run(fmax=0.05)
print(atoms.cell[2, 2])          # 2.529 A  (experiment: 2.53 A)
print(atoms.get_stress()[2])     # -2.6e-4 eV/A^3, i.e. relaxed

Ten BFGS steps, 7 s. The relaxed repeat lands on the experimental polyethylene c-axis, which nothing in the calculation was fitted to.

References

  • J. J. P. Stewart, J. Mol. Model. 13, 1173 (2007) — PM6.
  • W. Thiel, A. A. Voityuk, J. Phys. Chem. 100, 616 (1996) — MNDO-d.
  • S. Grimme et al., J. Chem. Phys. 132, 154104 (2010) — D3.
  • J. Řezáč, P. Hobza, J. Chem. Theory Comput. 8, 141 (2012) — H4.
  • J. J. P. Moussa, J. J. P. Stewart, J. Open Source Softw. 11(119), 8025 (2026) — MOPAC.

Download files

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

Source Distribution

pm6_rs_python-0.2.5.tar.gz (1.0 MB view details)

Uploaded Source

File details

Details for the file pm6_rs_python-0.2.5.tar.gz.

File metadata

  • Download URL: pm6_rs_python-0.2.5.tar.gz
  • Upload date:
  • Size: 1.0 MB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.13.9

File hashes

Hashes for pm6_rs_python-0.2.5.tar.gz
Algorithm Hash digest
SHA256 e4c1bfd47044a6742d6368206d6f2dec5289fca15cbefc1907cd72a902a16e36
MD5 1ca0e62dbb7140462a9375fa09dffcdc
BLAKE2b-256 9970898b742c7731c4cfe1040ec479332b2ef5c25f20a076b9e04e68b6133395

See more details on using hashes here.

Release history Release notifications | RSS feed

0.2.7

1 file

This release

0.2.5 This release

1 file

0.1.2

1 file

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page