Metadata-Version: 2.4
Name: pm6-rs-python
Version: 0.1.2
Requires-Dist: numpy>=1.21
Requires-Dist: ase>=3.22 ; extra == 'ase'
Provides-Extra: ase
License-File: LICENSE
Summary: PM6 semiempirical method (pm6-rs) with a Python-native API and an ASE calculator, including PM6-D3/D3H4/D3H4X corrections
Author: ss0832
License: GPL-3.0-or-later
Requires-Python: >=3.9
Description-Content-Type: text/markdown; charset=UTF-8; variant=GFM

# pm6-rs

Rust-native implementation of the **PM6** semiempirical NDDO method
(J. J. P. Stewart, *J. Mol. Model.* **13**, 1173 (2007)). Non-periodic 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 and ASE APIs.

- 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, dipole | ✅ (≤1e-5 e / ≤1e-4 D) |
| Lanthanide sparkles (Ce…Yb) | ⚠️ functional (0-orbital cores, oracle-derived ΔH_f); energy ~10 kcal/mol from MOPAC |
| D3 dispersion (PM6-D3) | ⚠️ implemented, ~0.25 kcal/mol vs MOPAC |
| H4 / X corrections (PM6-D3H4 / -D3H4X) | ⚠️ implemented per source; full PM6-D3H4 parity pending |
| Python native + ASE APIs (energy/grad/forces/Hessian/opt/freq) | ✅ |
| PBC | ⛔ out of scope (PM6 is molecular) |

✅ = validated to the stated tolerance; ⚠️ = implemented and functional, not yet
bit-exact. See `docs/scope.md` and `THIRD_PARTY_NOTICES.md`.

## 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

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

## CLI

```sh
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
```

## Rust API

```rust
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

```sh
pip install maturin
maturin develop --release --features python   # or: pip install pm6-rs-python
```

```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)
```

### ASE

```python
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
```

## 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.

