gfn1-rs
A Rust implementation of GFN1-xTB energies, analytical nuclear gradients,
stress, Hessians, cubic (FC3) and quartic (FC4) force constants, and periodic
(Gamma / Monkhorst-Pack k-point) electrostatics, with external electric fields,
IR/Raman spectroscopy, parameter tooling, and an optional Python/ASE interface.
The official parametrization is bundled — nothing to download. Dense linear
algebra uses faer; no BLAS/LAPACK required. Unofficial — use with caution.
Names: Cargo package gfn1-rs; Rust library target gfn1_rs; CLI binary
gfn1_rs_cli; Python package gfn1_rs (PyO3 extension module gfn1_rs._native).
Scope
Headlines for v0.5.0 — everything below is analytic and finite-difference gated unless it says otherwise:
- Energy, gradient, stress — molecular and periodic (Γ and Monkhorst–Pack k-points), with generalised-Ewald Klopman–Ohno electrostatics.
- Hessian — molecular, periodic Γ, and periodic k-point.
- Cubic force constants (FC3) — the strict closed form for molecules, a native finite-temperature analytic path, and — new in v0.5.0 — the closed form under PBC at Γ and on an arbitrary k-mesh, plus seminumerical references for every one of them.
- Quartic force constants (FC4) — fully analytic, no nuclear finite differences; the directional route has no degrees-of-freedom cap as of v0.5.0, and a finite-temperature analytic path exists alongside it.
- Charge-space response solver — one LU-factored dielectric serves every first- and second-order right-hand side; Fermi smearing is native, and the exactly-degenerate second order is closed in Daleckii–Krein resolvent form.
- Periodic thermal expansion — strain-mixed
dH/dlnVand mode/thermodynamic Grüneisen parameters (with a separate second-order volumetric step). - Bulk polarization — Berry phase, King-Smith–Vanderbilt and Resta forms.
- Excited states — TD-GFN1 (TDA) singlet/triplet, non-periodic, Γ and k-point, with analytic excited-state gradients.
- Fields and magnetics — external electric field (molecular and periodic), closed-shell magnetic SCC in London orbitals (GFN1-xTB-M0/M1), magnetizability (finite-field and analytic), NMR shieldings, and magneto-optical tensors.
- Spectroscopy and tooling — IR/Raman, polarizability, geometry optimization, parameter round-trip and finite-difference parameter derivatives, Python/ASE bindings.
Experimental, off by default (stock GFN1 is byte-identical with them off): multipole/CAMM electrostatics, higher-order on-site charge, range-separated Fock exchange, DFT+U/+V, collinear spin polarization, and self-consistent D4.
The full feature matrix — including the FC3/FC4 coverage tables — lives in docs/scope.md; the honest list of gaps is in docs/limitations.md.
Documentation
The docs are organised one page per subsystem — see the index at
docs/README.md:
docs/scope.md— the full feature matrix, including the FC3/FC4 coverage tables (molecular / PBC Γ / PBC k-mesh ×T = 0/ finiteT).docs/parameters.md— bundled official parameters,Gfn1Parameters::resolveprecedence,ParamSourceprovenance reporting, reference data, unit constants.docs/derivatives.md— the nuclear derivative ladder (gradient → Hessian → FC3 → FC4), theterms::require_orderregistry, and the verification-gate philosophy (analytic ordernvs FD of ordern−1,h²ladders).docs/finite-temperature.md— the charge-space dielectric response solver (first and second order, natively Fermi-smeared) and the analytic finite-temperature FC3.docs/pbc.md— periodic derivatives, the analytic periodic third derivative (Γ and k-point) and its seminumerical reference, strain-mixeddH/dlnV, Grüneisen parameters, and Berry-phase bulk polarization.docs/td.md— TD-GFN1 (TDA) excited states: working equations, the analytic excited-state gradients, the MO phase gauge, and the gate numbers.docs/limitations.md— known gaps and silent failure modes, with the exact error messages. Read this one.docs/rust-api.md— Rust library API (run_electronic,analytic_gradient,analytic_hessian,optimize_geometry, the periodicrun_pbc_scc/pbc_analytic_gradient/pbc_stress/pbc_gamma_hessianentry points,vibrational_analysis, the external-field / spectroscopy API (ExternalFieldOptions,dipole_derivatives,static_polarizability,ir_spectrum,raman_spectrum), the third/fourth-derivative APIs, and the parameter tooling (to_param_string,ParameterTarget,parameter_finite_difference)).docs/python-api.md— Python (Gfn1NativeCalculator) and ASE (GFN1RSCalculator) API, including charge/spin and SCC controls.docs/CONTRIBUTING.md— project discipline: docs are updated in the same commit as the feature that lands.
Build
cargo build --release # CLI -> target/release/gfn1_rs_cli
Python extension (Python 3.9–3.14):
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.
Performance
The dense linear algebra (faer) runs multi-threaded by default, using the shared
rayon pool — the O(N³) SCF eigensolve and the analytic-gradient assembly scale across
all cores (≈1.9× on a 300-atom cluster vs single-threaded). The closed-form cubic
force constants additionally compute their independent per-slab response in parallel over
the same pool, and offer Dense / Vector / Block output so you only build what you need;
the quartic force constants evaluate their deduplicated polarization directions in
parallel against one shared reference. Diagnostics:
-
GFN1_PROFILE=1— emit per-region wall-clock timings to stderr. -
GFN1_FAER_THREADS=1— force single-threadedfaer(reproducible benchmarking). -
GFN1_PBC_MULTIPOLE_FIELD_CACHE_MB=256controls the periodic multipole field-cache cap; larger cells automatically use the lower-memory streaming path instead of risking OOM.
Parameters
The official GFN1-xTB parametrization is bundled — everything works out of
the box with no downloads. The bundled files (param_gfn1-xtb.txt and the
GFN1(Si)-xTB silicon reparametrization param_gfn1-si-xtb.txt) are verbatim
copies from grimme-lab/xtb
(LGPL-3.0-or-later; see third_party/xtb/PROVENANCE.md and
THIRD_PARTY_NOTICES.md).
Resolution order (Gfn1Parameters::resolve): --param / param_path >
GFN1_XTB_PARAM environment variable > builtin. Every run reports which
parametrization is active (CLI banner parameters: ..., Python
calc.param_source()). Details in
docs/parameters.md.
--param builtinselects the bundled GFN1-xTB set explicitly;--param builtin:siselects the bundled GFN1(Si)-xTB set.- To use your own file:
--param PATHor
export GFN1_XTB_PARAM=/path/to/param_gfn1-xtb.txt # (PowerShell: $env:GFN1_XTB_PARAM = "...")
GFN2 parameter files are rejected intentionally.
D3 reference data
D3(BJ) uses the s-dftd3 reference table. A minimal LGPL-3.0-or-later
simple-dftd3 data copy is bundled under third_party/simple-dftd3 (see
THIRD_PARTY_NOTICES.md); an explicit
--d3-reference PATH / GFN1_D3_REFERENCE still takes precedence.
Experimental D4 dispersion
--experimental-d4 replaces the default D3 term with an experimental
non-periodic, self-consistent D4 term, including the Axilrod-Teller-Muto
three-body contribution. The D4 charge potential is fed back into SCC as an
atomic scalar shift, and non-PBC analytic gradients include the fixed-charge D4
geometry and coordination-number response. The GFN1 damping constants a1,
a2, and s8 are read from the active param_gfn1-xtb.txt; s9 is an
API/CLI option that defaults to the GFN2-xTB value 5.0 only when D4 is active
(ordinary non-D4 calculations use s9 = 0). The saved upstream DFT-D4 reference
data and provenance live under third_party/dftd4.
Charge and spin multiplicity
Formal charge (--charge) and spin multiplicity (--multiplicity) are passed
through ElectronicOptions. Multiplicity constrains the alpha/beta occupations of
the spin-independent GFN1 Hamiltonian.
Optional spin polarization (spGFN1). Plain GFN1 is restricted-open-shell (the
energy does not depend on the spin density). Setting --spinpol (CLI) /
spin_polarization=True (Python / ASE) / ElectronicOptions::spin_polarization
adds the collinear spin-DFTB spin-polarization energy from the tabulated atomic
spin constants W ("spGFN1"): non-PBC, energy + analytic forces, off by
default. It only affects open-shell configurations — a closed-shell singlet is
byte-identical to plain GFN1. The W constants are transcribed byte-exact from
tblite (LGPL-3.0; provenance under third_party/tblite/ and THIRD_PARTY_NOTICES.md).
v1 is the bare GFN1 model (not combinable with the experimental multipole / CAMM /
exchange / D4 / field paths). See scope.md.
Extended Hubbard correction (DFT+U / +U+V), non-empirical. GFN1 describes the
on-site charge fluctuation only at the monopole (shell-charge) level and carries no
orbital-resolved penalty against the self-interaction / over-delocalisation of a
localised transition-metal d shell — the diagnosed root cause of its poor TM
spin-state energetics. --plus-u (CLI) / plus_u=True (Python / ASE) adds the
rotationally-invariant FLL +U energy (and, with --plus-u-v, the inter-site +V
that restores metal–ligand hybridisation), built from the dual population of the
correlated subspace; energy + analytic forces (overlap-Pulay term, frozen-U),
on the open-shell spin path (requires --spinpol), non-PBC, off by default. With
--linear-response-u the U (and V) are computed non-empirically from the
occupation response (Cococcioni–de Gironcoli, U = χ0⁻¹ − χ⁻¹) and the correlated
d shells are auto-selected — no fitted parameters (the FD step and +V cutoff
are numerical/geometric settings, not fits). Experimental; in the screened
semiempirical SCC the bare/screened response separation is approximate. See
scope.md.
CLI
Coordinates are XYZ/extXYZ in Angstrom (use --bohr for Bohr). A periodic cell is
read from the extXYZ Lattice="..." header and auto-dispatches the periodic path.
See --help for the full option set. No --param is needed — the official
parametrization is compiled in, so the examples below run as written from a fresh
checkout; pass --param builtin:si for the GFN1(Si) set or --param PATH for
your own file. Common workflows:
# single-point energy (+ Mulliken charges)
gfn1_rs_cli examples/water.xyz --charges
# energy + analytical gradient / forces
gfn1_rs_cli --gradient examples/ammonia.xyz
# analytical Hessian (non-periodic)
gfn1_rs_cli --hessian examples/water.xyz
# periodic single point + component stress (Lattice from the extXYZ header)
gfn1_rs_cli --stress examples/diamond.xyz
# L-BFGS optimization (non-periodic; analytical gradient). --opt-output writes the final
# geometry, --opt-traj writes the full multi-frame XYZ trajectory.
gfn1_rs_cli --optimize --opt-output opt.xyz --opt-traj opt_traj.xyz examples/ammonia.xyz
# mDFTB2 multipole-corrected single point / optimization (experimental, non-PBC)
gfn1_rs_cli --multipole --optimize --opt-output opt.xyz examples/ammonia.xyz
# CAMM-on-mDFTB2: GFN2-style anisotropic electrostatics (q-mu/q-Theta/mu-mu) on cumulative
# atomic multipoles, replacing the mDFTB2 off-site term (experimental, non-PBC, v0.4.2).
# camm_damp (kappa) is the primary range-selective lever; camm_aes_scale (s_AES) the secondary amplitude.
gfn1_rs_cli --multipole --multipole-model camm_on_mdftb2 --camm-damp 1.0 examples/water48.xyz
# experimental self-consistent D4 single point / gradient (non-PBC only)
gfn1_rs_cli --experimental-d4 --gradient examples/water.xyz
# range-separated Fock exchange single point (experimental, non-PBC); --onsite-exchange adds
# the exact one-center (OFX) term, --scf-trah forces the TRAH second-order SCF
gfn1_rs_cli --lr-exchange --onsite-exchange --charges examples/water.xyz
# charged / open-shell single point
gfn1_rs_cli --charge 1 --multiplicity 2 examples/h2.xyz
# spin-polarized GFN1 ("spGFN1"): collinear spin term for open-shell systems (non-PBC, energy +
# analytic forces). Off by default; a closed-shell singlet stays byte-identical to GFN1.
gfn1_rs_cli --multiplicity 3 --spinpol --gradient examples/h2.xyz
# TD-GFN1 (TDA) excited states; --tda-grad adds the excited-state gradient. The CLI default is
# --tda-gradient-method semi-numerical; pass `analytic` for the fully analytic direct-CPHF gradient
# (or `fd` for the root-tracked full finite difference).
gfn1_rs_cli --tda examples/water.xyz
# single point in a uniform external electric field (atomic units, Ex Ey Ez)
gfn1_rs_cli --field 0.0 0.01 0.0 --charges examples/water.xyz
# static polarizability, IR, and Raman (each also prints the raw derivative tensors)
gfn1_rs_cli --polarizability examples/water.xyz
gfn1_rs_cli --ir examples/water.xyz
gfn1_rs_cli --raman examples/water.xyz
# NMR nuclear magnetic shielding tensor of atom 0 (ppm; isotropic + 3x3)
gfn1_rs_cli --nmr 0 examples/water.xyz
# finite-difference parameter derivatives (dE/dp, add --param-forces for dF/dp)
gfn1_rs_cli --param-deriv \
--targets glob:ks,elem:1:GAM,pair:1:1 examples/h2.xyz
cargo run --bin gfn1_rs_cli -- ... works too if you have not built the binary.
IR/Raman are taken at the input geometry, so optimize first (--optimize) for a
clean spectrum free of spurious low-frequency modes.
Python / ASE
Periodic single points (energy / forces / stress) dispatch automatically for
ASE Atoms with a cell/pbc (Monkhorst-Pack via the kgrid parameter), and
periodic k-point TD is exposed (get_tda_kpoint); the property/spectroscopy
methods (polarizability, IR/Raman, magnetic) are non-periodic.
from ase import Atoms
from ase.optimize import LBFGS
from gfn1_rs.ase import GFN1RSCalculator # reads GFN1_XTB_PARAM, or pass param_path=...
atoms = Atoms("H2", positions=[[0.0, 0.0, 0.0], [0.74, 0.0, 0.0]])
atoms.calc = GFN1RSCalculator(charge=0.0, multiplicity=None)
print(atoms.get_potential_energy()) # eV
print(atoms.get_forces()) # eV/Angstrom
print(atoms.get_dipole_moment()) # e*Angstrom
print(atoms.calc.results["native_energy_terms_hartree"])
# ASE-driven optimization, or the Rust-native L-BFGS:
LBFGS(atoms).run(fmax=0.05)
atoms.calc.optimize_native(atoms, gradient_tolerance=1e-4)
Units: two layers, two conventions
The package has exactly one unit boundary, and it is gfn1_rs.ase:
| Layer | Convention |
|---|---|
gfn1_rs.ase.GFN1RSCalculator (ASE) |
ASE units — Angstrom, eV, eV/Angstrom, eV/Angstrom³, e·Angstrom, e |
gfn1_rs.Gfn1NativeCalculator, gfn1_rs.torch_interop, the CLI, the Rust API |
atomic units — bohr, Hartree, Hartree/bohr, Hartree/bohr³, e·bohr |
Everything the ASE calculator returns is in ASE units, and every conversion is
taken from ase.units (Bohr, Hartree, invcm) — there are no hand-typed
factors in the ASE layer, and it deliberately does not use the engine's own
CODATA constants (they differ at the 1e-8 level). Concretely:
get_potential_energy()eV ·get_forces()eV/Å ·get_stress()eV/ų in the ASE Voigt order(xx, yy, zz, yz, xz, xy)with the ASE sign conventionsigma = (1/V) dE/d(strain)·get_dipole_moment()e·Å ·get_charges()eget_hessian()eV/Ų,get_third_derivative*()eV/ų,get_polarizability()/get_magnetic_polarizability()e²Å²/eV,get_magnetic_energy()eV- gauge/multipole
originarguments are read in Angstrom - dicts follow a suffix rule: an unsuffixed key (
forces,total_energy) is in ASE units, a suffixed one (*_hartree,*_hartree_per_bohr,*_au,*_ev) is in the unit its name states — soresults["native_energy_terms_hartree"]andget_tda_gradient()["forces_hartree_per_bohr"]still give you the raw atomic-unit numbers - observables with a universal unit of their own keep it, exactly as ASE's own
ase.vibrationsdoes: cm⁻¹ wavenumbers, km/mol IR intensities, ppm NMR shieldings, 1e-30 J/T² magnetizabilities; raw AO-integral and magnetic-response tensors stay in atomic units and say so in their docstrings - the calculator's construction parameters are model knobs passed straight to
the engine, so they stay in native units (
electric_fielda.u.,hubbard_u/hubbard_v/level_shift/energy_toleranceHartree,hubbard_v_cutoffand thed4_*cutoffs bohr,electronic_temperatureK), as do the numericalstep/e_step/b_step/field_steparguments
The low-level gfn1_rs.Gfn1NativeCalculator exposes the same controls without
ASE, in atomic units. Set experimental_d4=True on either interface to use the
non-PBC D4 path. Use d4_s9=0.0 / --d4-s9 0 or --no-d4-atm to disable the D4
three-body term explicitly.
Validation
Tests resolve parameters through Gfn1Parameters::resolve(None), so they run
against the bundled parametrization with no environment variable — and, unlike
before, no longer silently no-op when GFN1_XTB_PARAM is unset:
cargo test --profile reltest # use reltest: these are numerical gates
cargo test --profile reltest -- --ignored # long-running (FD probes, finite-T ladders)
maturin develop --release --features python && python -m pytest -q
The reltest profile is release-grade optimization without fat LTO or
single-codegen-unit linking, so the numerical gates run at production speed
without re-linking every test binary at full LTO cost. A plain debug-profile
cargo test is correct but much slower on this suite.
Set GFN1_XTB_PARAM=/path/to/param_gfn1-xtb.txt only to test against a
different parametrization.
Tests cover finite-difference gradient / Hessian checks
(tests/gradient_fd_probe.rs, tests/hessian.rs), the analytic third and fourth
derivatives against their seminumerical references (tests/third_derivative.rs,
tests/fourth_derivative.rs), the periodic path (tests/pbc_integration.rs) and
its third derivative / Grüneisen module (tests/pbc_third_derivative.rs), the
optimizer (tests/optimizer.rs), and an opt-in external tblite parity suite
(tests/tblite_parity.rs, enabled by setting GFN1_TBLITE_BIN; tblite is not a
Cargo dependency). GFN1_FD_TIGHT=1 tightens the finite-difference probe and
GFN1_PROFILE=1 emits per-scope native timings.
The gate design — analytic order n against a central FD of the validated
analytic order n−1, with the h² truncation ladder as the completeness
diagnostic — is described in
docs/derivatives.md.
The Python suite lives in tests/python/ (collected by the pytest config in
pyproject.toml). tests/python/test_ase_units.py pins the unit contract above:
every ASE-calculator output is asserted to equal the corresponding
Gfn1NativeCalculator (atomic-unit) output times the exact ase.units factor.
Its pure-Python half mocks the native layer, so it runs even without the compiled
extension.
Limitations
The full, honest list — including the failure modes that are silent — lives in
docs/limitations.md. The headline items:
- Fermi smearing reaches everything except the strict
T = 0closed forms. Thethird_derivative::finite_tdrivers carry smearing through the analytic FC3 and FC4 (directional, dense and block); the strict closed forms (third_derivative_analytic_*,fourth_derivative_analytic_*) still reject fractional occupations with an explicit error pointing at them. - The analytic periodic FC3 carries a
~1e-7residual and rejects smearing. Both the Γ path (pbc_gamma_third_analytic_*) and the k-point path (pbc_kpoint_third_analytic_*, any Monkhorst–Pack mesh, vector / dense / block) are complete and agree with the seminumerical reference to≈8e-8; the two share one assembly, so they share that open residual. Fermi-smeared cells and order-1 model options still needpbc_kpoint_third_derivative_seminumerical_*. - The
n⁴-expanded FC4 front ends are capped at 30 DOF (MAX_FOURTH_DERIVATIVE_NDOF, the8·n⁴-byte expansion and the full-spaceJet4working set of the D3/halogen full-tensor routes). The directional quartic has no such cap — 45 DOF is exercised in the test suite — and the block mode applies the cap to|dofs|, not to3N. pbc_kpoint_hessianis wrong on exactly degenerate frontier orbitals (perfect-symmetry cells; up to 100 % error, vanishing under a 0.03 Å distortion). The Γ path and every FC3 route are structurally immune; seedocs/pbc.md§2b.- Experimental model flags cap the derivative ladder at the gradient. D4,
multipole/CAMM, MFX/OFX exchange, +U, spGFN1 and an external electric field all
carry
max_analytic_order: 1; asking for a Hessian/FC3/FC4 with any of them on now fails fast instead of silently returning the derivative of a different energy expression. - Open-shell magnetic properties (spin-Zeeman / NMR shieldings) and the
length-gauge orbital-current optical-rotation
G-tensor / MCD (both vanish in the GFN1 point-charge model sincedq/dB = 0; thelao_dipole_matrixintegrals are the route to the orbital-current versions). The closed-shell magnetics — analytic magnetizability and the FD-verified magneto-optical suite — are already complete; see scope.md.
References
- A. Buccheri, R. Li, J. E. Deustua, S. M. Moosavi, P. J. Bygrave, F. R. Manby, "Periodic GFN1-xTB Tight Binding: A Generalized Ewald Partitioning Scheme for the Klopman–Ohno Function," J. Chem. Theory Comput. 21, 1615–1625 (2025). DOI: 10.1021/acs.jctc.4c01234.
- S. Grimme, C. Bannwarth, P. Shushkov, "A Robust and Accurate Tight-Binding Quantum Chemical Method for Structures, Vibrational Frequencies, and Noncovalent Interactions of Large Molecular Systems Parametrized for All spd-Block Elements (Z = 1–86)," J. Chem. Theory Comput. 13, 1989–2009 (2017). DOI: 10.1021/acs.jctc.7b00118.
- A. Hjorth Larsen et al., "The Atomic Simulation Environment — A Python library for working with atoms," J. Phys.: Condens. Matter 29, 273002 (2017). DOI: 10.1088/1361-648X/aa680e.
- T. A. Niehaus, S. Suhai, F. Della Sala, P. Lugli, M. Elstner, G. Seifert, T. Frauenheim, "Tight-binding approach to time-dependent density-functional response theory," Phys. Rev. B 63, 085108 (2001). DOI: 10.1103/PhysRevB.63.085108. (The TD-DFTB transition-charge response model behind TD-GFN1.)
- C. Y. Cheng, A. M. Wibowo-Teale, "Semiempirical Methods for Molecular Systems
in Strong Magnetic Fields," J. Chem. Theory Comput. 19, 6226–6241 (2023).
DOI: 10.1021/acs.jctc.3c00671.
(The GFN1-xTB London-orbital magnetic-field method behind the
magneticmodule.)
License
GPL-3.0-or-later. See LICENSE and THIRD_PARTY_NOTICES.md.
Release files for gfn1-rs-python 0.5.1
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| gfn1_rs_python-0.5.1.tar.gz | 2.1 MB | Details |
Release files / gfn1_rs_python-0.5.1.tar.gz
| Download URL | gfn1_rs_python-0.5.1.tar.gz |
|---|---|
| Size | 2.1 MB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
31d41629fed5362f0aedefcc7b2187272a3ba6080037fe31c3ab772ce1870ad9
|
|
BLAKE2b-256 checksum How to use checksums |
89462b4740a3d6abd1e9cfee4aa017e7fd763fa5382640c7cae1c19ba7d99513
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.13.9
|