Metadata-Version: 2.4
Name: gfn2-rs-python
Version: 0.4.5
Classifier: Programming Language :: Rust
Classifier: Programming Language :: Python :: 3
Requires-Dist: ase>=3.22 ; extra == 'ase'
Requires-Dist: numpy>=1.22 ; extra == 'ase'
Requires-Dist: pytest>=8 ; extra == 'test'
Requires-Dist: ase>=3.22 ; extra == 'test'
Requires-Dist: numpy>=1.22 ; extra == 'test'
Provides-Extra: ase
Provides-Extra: test
License-File: LICENSE
Summary: Python and ASE interface for the native Rust GFN2-RS implementation
Author: ss0832
License-Expression: GPL-3.0-or-later
Requires-Python: >=3.9, <3.15
Description-Content-Type: text/markdown; charset=UTF-8; variant=GFM

# gfn2-rs

A Rust implementation of GFN2-xTB-style energies, analytical nuclear gradients,
stress, Hessians, and periodic (Gamma / Monkhorst-Pack k-point) electrostatics,
with an optional Python/ASE interface. Dense linear algebra uses `faer`; no
BLAS/LAPACK required. **Unofficial — use with caution.**

Names: Cargo package `gfn2-rs`; CLI + Python package `gfn2_rs`; Rust library
target `gfn2_rs_core` (alias to `gfn2_rs` downstream if preferred).

## Scope 

| Area | Included |
| --- | --- |
| SCC energy | Gamma (real path) and explicit Monkhorst-Pack k-points (Bloch path). |
| Gradients / stress | Analytical, Gamma and Bloch; stress for periodic cells (opt-in). |
| Hessian | **Semi-numerical** (default): the classical / non-electronic-structure block (nuclear repulsion + D4 dispersion) is the **exact analytic** Hessian; the SCC/electronic block is the **central finite difference of the validated analytic gradient** (non-PBC / Gamma / k-point). The closed-form analytic CPHF response is opt-in via `GFN2_ANALYTIC_HESSIAN=1` (carries a small ~9e-5 Eh/bohr² structural residual). |
| Periodic AES/Ewald | 3D + 2D-slab multipolar electrostatics (shell charge, dipole, quadrupole). Charged cells need `--charged-pbc background`. |
| D4 dispersion | Pair + ATM from in-tree DFT-D4 reference data. |
| TD-GFN2 / spin | spGFN2 spin projection, TD-GFN2/TDA excitations + analytical excited-state gradients (closed-shell + spin-conserving open-shell, Gamma + Bloch). |
| Python / ASE | PyO3 bindings + one ASE calculator `GFN2RSCalculator` (energy/forces/stress/charges + TD/Hessian methods). |

## Documentation

Full API references live in `docs/`:

- [`docs/rust-api.md`](docs/rust-api.md) — Rust library API (`run_electronic_core`,
  `native_analytic_gradient`, `compute_hessian`, CPHF/TD-GFN2/spin entry points,
  parameter refitting).
- [`docs/python-api.md`](docs/python-api.md) — Python (`Gfn2NativeCalculator`) and
  ASE (`GFN2RSCalculator`) API, including k-points, stress, TD-GFN2, Hessian, and
  spin controls.

## Build

```bash
cargo build --release --bins          # CLI (target/release/gfn2_rs, gradient_check, stress_check, hessian_check)
```

Python extension (Python 3.9–3.14):

```bash
python -m pip install maturin "ase>=3.22" "numpy>=1.22"
maturin develop --release --features python
```

For a Python newer than PyO3's support window, set
`PYO3_USE_ABI3_FORWARD_COMPATIBILITY=1` before `cargo`/`maturin`.

## Parameters

The GFN2-xTB parameter file is **not** distributed. Obtain `param_gfn2-xtb.txt`
from the `xtb` repository and point to it via `--param PATH` or:

```bash
export GFN2_XTB_PARAM=/path/to/param_gfn2-xtb.txt   # (PowerShell: $env:GFN2_XTB_PARAM = "...")
```

### Spin-polarization constants

Open-shell collinear spin-polarization (`--spinpol`) needs per-element spin
constants `W_ll'` (Hartree). These are **not bundled** (they are not part of
`param_gfn2-xtb.txt`). Obtain them from the **`tblite`** library — the universal
atomic spin constants in `tblite` (`src/tblite/data/spin.f90`) are not fitted to a
specific GFNn-xTB method and are usable with GFN1/GFN2-xTB — and write them into a
text file. Format (one element per line, comments with `#`/`!`, `-` for shell pairs
absent from the AO basis); see `examples/spin_params_template.txt`:

- `H  ss <W_ss>` — a single named shell-pair value, or
- `<El> <ss> <sp> <pp> <sd> <pd> <dd>` — packed symmetric s/p/d matrix, or
- `<El> <ss> <sp> <sd> <ps> <pp> <pd> <ds> <dp> <dd>` — full row-major 3x3 matrix.

Use it from the CLI or either API:

```bash
gfn2_rs --xyz radical.xyz --electronic --charge 0 -u 1 \
  --spinpol --spin-param spin_params.txt          # add --kgrid a b c [--kshift] for k-points
```

```rust
// Rust: load the file, attach to the electronic options
let params = gfn2_rs::SpinPolarizationParameters::from_file("spin_params.txt")?;
let spin = gfn2_rs::SpinPolarizationOptions { enabled: true, parameters: params };
// electronic_options.spin_polarization = spin;
```

```python
# Python / ASE (low-level Gfn2NativeCalculator takes the same two kwargs)
calc = GFN2RSCalculator(charge=0.0, uhf=1,
                        spin_polarized=True, spin_param_path="spin_params.txt")
```

## CLI

Coordinates are XYZ/extXYZ in Angstrom (use `--bohr` for Bohr). See `--help` for
the full option set; common workflows:

```bash
# single-point energy
gfn2_rs --xyz examples/water_dimer_distorted.xyz --electronic --charge 0 --uhf 0

# energy + analytical gradient (add --stress for periodic stress)
gfn2_rs --xyz examples/water_dimer_pbc_16a.xyz --electronic --gradients --stress

# L-BFGS optimization (analytical gradient; add --kgrid a b c for Bloch k-points)
gfn2_rs --xyz examples/water_dimer_distorted.xyz --electronic --optimize \
  --opt-output opt.xyz --opt-trace opt.tsv --opt-grad-tol 5e-4
```

TD-GFN2/spin (`--td-gfn2`, `--td-spin multiplicity:N`, `--sp-gfn2`) and parameter
finite-difference derivatives (`param_deriv --active-targets ...`) are also
available; see `--help`.

## Python / ASE

```python
from ase import Atoms
from ase.optimize import LBFGS
from gfn2_rs.ase import GFN2RSCalculator      # reads GFN2_XTB_PARAM, or pass param_path=...

atoms = Atoms("H2", positions=[[0, 0, 0], [0.74, 0, 0]])
atoms.calc = GFN2RSCalculator(charge=0.0, uhf=0)
print(atoms.get_potential_energy())            # eV
print(atoms.get_forces())                      # eV/Angstrom

# periodic + k-points
atoms.set_cell([16, 16, 16]); atoms.set_pbc(True)
atoms.calc = GFN2RSCalculator(kgrid=[2, 1, 1], kshift=True)
print(atoms.get_potential_energy(), atoms.get_stress())

# explicit GFN2-RS workflows
tda = atoms.calc.get_tda(n_states=3, spin_adaptation="multiplicity:1")
hess = atoms.calc.get_hessian(relaxed=True)    # eV/Angstrom^2
```

ASE units throughout: Angstrom, eV, eV/A, eV/A^3. The low-level
`gfn2_rs.Gfn2NativeCalculator` exposes the same controls without ASE.

## Validation

```bash
cargo test --quiet --offline
GFN2_XTB_PARAM=/path/to/param_gfn2-xtb.txt maturin develop --release && python -m pytest -q
```

Finite-difference checkers (`gradient_check`, `stress_check`, `hessian_check`)
compare analytical vs FD derivatives; `GFN2_PROFILE=1` enables timing scopes.
Example:

```bash
gfn2_rs/target/release/hessian_check --xyz examples/water_dimer_distorted.xyz --d4 \
  --step 1e-4 --out hessian_check.tsv
```

## References

1. V. Alizadeh, J. Pototschnig, S. Ehlert, T. D. Kuehne, "Periodic GFN2-xTB in
   CP2K," ChemRxiv (2026). DOI:
   [10.26434/chemrxiv.15003939/v1](https://chemrxiv.org/doi/full/10.26434/chemrxiv.15003939/v1).
2. C. Bannwarth, S. Ehlert, S. Grimme, "GFN2-xTB," J. Chem. Theory Comput. 15,
   1652 (2019). DOI:
   [10.1021/acs.jctc.8b01176](https://doi.org/10.1021/acs.jctc.8b01176).
3. A. Hjorth Larsen et al., "The Atomic Simulation Environment," J. Phys.:
   Condens. Matter 29, 273002 (2017). DOI:
   [10.1088/1361-648X/aa680e](https://doi.org/10.1088/1361-648X/aa680e).
4. M. Neugebauer, S. Ehlert, et al., spin-polarized extended tight-binding
   (spGFNn-xTB); spin constants `W_ll'` are distributed with the `tblite` library
   (<https://github.com/tblite/tblite>), J. Comput. Chem. (2023). DOI:
   [10.1002/jcc.27185](https://doi.org/10.1002/jcc.27185).

## License

GPL-3.0-or-later.

