Semiempirical Methods¶
vibe-qc ships a self-contained semiempirical platform covering four method families, DFTB, GFN-xTB, NDDO, and INDO, for molecules and periodic solids. The platform is vibe-qc’s own implementation, not a wrapper around external programs (External QC codes (ORCA / Psi4 / others)). The MACE machine-learning interatomic potential is documented separately because it is an external pre-trained model, not a semiempirical electronic-structure method (Machine-learning interatomic potentials (MACE)).
Warning
Production readiness varies by method. DFTB is intended for screening/preoptimization; GFN2-xTB is gated experimental; PM6, OM2 and OM3 are production for molecular work (PES shape pinned by regression, published-benchmark validation for OM2, see the status table); OM1 is experimental until its analytic core-valence ECP lands; MSINDO is reference-parity validated for its current molecular scope. See the status table and the comparative production brief in Semiempirical and MACE method comparison.
Method families¶
Family |
Methods |
Best for |
Cost |
|---|---|---|---|
DFTB |
DFTB0, SCC-DFTB, UDFTB0, USCC-DFTB |
Screening, preoptimization |
Fastest |
GFN-xTB |
GFN2-xTB |
Organic / main-group |
Fast |
NDDO |
PM6, OM1, OM2, OM3 |
Development benchmarking, pre-screening |
Fast |
INDO |
MSINDO |
Reference-parity molecular semiempirical runs inside the supported element/spin scope |
Fast |
Quick start¶
DFTB0, non-self-consistent tight-binding¶
from vibeqc.semiempirical import DFTB0Model
from vibeqc import Molecule, Atom
mol = Molecule([
Atom(8, [0.00, 0.00, 0.00]),
Atom(1, [1.55, 0.90, 0.00]),
Atom(1, [-1.55, 0.90, 0.00]),
])
model = DFTB0Model(mol)
print(f"Energy: {model.energy():.6f} Ha")
print(f"Gradient shape: {model.gradient().shape}") # (3, 3)
SCC-DFTB, self-consistent charges¶
from vibeqc.semiempirical import SCCDFTBModel
model = SCCDFTBModel(mol, charge_mixing=0.2)
print(f"Energy: {model.energy():.6f} Ha")
charge_mixing is the maximum fraction used by the molecular SCC charge
update. At zero electronic temperature, bounded vector Aitken relaxation may
reduce and subsequently recover that fraction when consecutive Mulliken
residuals reveal charge sloshing; it never raises the fraction above the value
requested by the caller. Molecular geometry optimization also seeds each new
geometry from the preceding converged Mulliken charges, keeping the optimizer
on a continuous SCC branch.
GFN2-xTB, published parameters (experimental)¶
from vibeqc.semiempirical import GFN2Model
from vibeqc.semiempirical.methods.gfn2_params import load_gfn2_params
params = load_gfn2_params() # auto-fetches 86-element Grimme parameter set
model = GFN2Model(mol, params=params, warn=False) # warn=False silences experimental gate
print(f"Energy: {model.energy():.6f} Ha")
Warning
GFN2-xTB is gated experimental. The H0/overlap deep-state
bug was fixed 2026-06-01, and molecular AES (dipole + quadrupole), the
GAM3 third-order term, and post-SCF native D4-style dispersion are now
implemented.
The remaining production gates are periodic AES image-cell multipole Ewald
terms, stricter periodic molecular-limit parity, difficult periodic/polar
fixtures, and a full external-parity matrix against xtb. See
Method status.
The bounded h-BN expansion sweep at lattice scales 1.02 through 1.06 converges
on a smooth branch when given an explicit 1000-iteration total budget; its
scale-1.06 point correctly fails under a 500-iteration cap. The seven-point
bulk-Si sweep converges within 200 iterations. Periodic GFN2 honors
max_iter as a hard total across its ordinary and automatic-stabilization
attempts, and the public runner forwards both that budget and conv_tol to
the native SCC loop. These bounded results do not relax the experimental GFN2
gate. Separately, the graphene periodic-PM6 sweep now has finite image
exchange and converges. The one-carbon-chain periodic OMx sweep now cancels
neutral image monopoles and crosses its 15-bohr image-shell boundary smoothly;
diamond-Si periodic PM6 now uses one shared gamma kernel for electronic and
core image monopoles and crosses its 15-bohr shell boundary smoothly. The
h-BN PM6 and graphene OMx physics gaps remain open.
The post-SCF native D4 term is included for H, He, B, C, N, O, F, and Ne;
outside that reference-data set GFN2 returns zero D4 and emits
GFN2D4UnsupportedWarning.
PM6, NDDO with published parameters¶
from vibeqc.semiempirical import PM6Model
model = PM6Model(mol)
print(f"Energy: {model.energy():.6f} Ha")
The high-level closed-shell run_job(method="pm6") route first attempts the
ordinary zero-temperature SCF. If hard occupations cycle across
near-degenerate fragment orbitals, it uses a bounded finite-temperature
occupation homotopy and then cools back to an idempotent zero-temperature
density. Only the cooled PM6 energy is accepted; the finite-temperature
intermediate is never reported as a successful result.
Gamma-periodic PM6 instead uses Pulay extrapolation of the physical
commutator residual. Convergence requires both the density change and
[F, D] residual to meet conv_tol; this prevents a repeated extrapolated
Fock matrix from being accepted when its density is not stationary under the
physical Fock operator. It uses the shared guarded Pulay history, which
shortens a linearly dependent history before extrapolating and falls back to
the current physical Fock matrix when no stable history remains. This avoids
the cancellation-dominated extrapolation that previously made the archived
Pa-3 dry-ice scale-0.96 point fail after 1200 iterations and made its
scale-0.98 neighbor unusually slow. The archived proton-ordered Ice-Ih and
bounded dry-ice expansion slices now converge on smooth solver branches.
Periodic PM6 remains experimental until its energies receive independent external-reference validation. In particular, dry-ice energies remain sensitive to direct-space image-shell cutoff; solver convergence does not establish a physical molecular-crystal equation of state. The periodic diatomic core branch also cancels the neutral-pair monopole retained by the electronic decomposition. A closed-shell fcc-Ar regression pins that cancellation and the repulsive compressed branch. Bare PM6 has no validated rare-gas dispersion model here, however, so its dissociative Ar curve is not a physical rare-gas equation of state.
For elements such as silicon, whose electronic block uses the multi-term NDDO
gamma expansion, the neutral core baseline uses that identical kernel rather
than the separate PM6 short-range core kernel. Damping and Gaussian pair terms
remain corrections on top. This keeps a neutral image shell neutral before
truncation: the diamond-Si 15-bohr sphere changes from 55 to 43 cells between
lattice scales 1.02 and 1.04, but the total-energy step is about 0.00195 Ha
rather than the former 1.01917 Ha. This is a bounded implementation
regression, not independent MOPAC validation.
OMx, orthogonalization-corrected NDDO¶
from vibeqc.semiempirical import OMxModel
model = OMxModel(mol, variant="om2") # "om1", "om2", or "om3"
print(f"Energy: {model.energy():.6f} Ha")
Through run_job¶
All seven methods are available via vibeqc.runner.run_job():
from vibeqc import run_job
run_job(mol, method="dftb0", optimize=True, output="h2o_dftb0")
run_job(mol, method="pm6", output="h2o_pm6")
run_job(mol, method="gfn2_xtb", output="h2o_gfn2") # emits experimental warning
run_job treats Molden and population sidecars independently. DFTB0,
SCC-DFTB, and GFN2-xTB emit .population.txt and .population.json by
default from the engine’s native net atomic Mulliken charges. DFTB0 forms
these charges from its final one-shot density and overlap; UDFTB0 uses the
sum of its alpha and beta densities. Analyses that require the Gaussian-AO
property stack are present as explicit unsupported: entries. Molden remains
unavailable for every semiempirical route because the current writer
serializes Gaussian GTO shells, not the methods’ minimal Slater-orbital bases.
PM6/OMx and MSINDO population sidecars remain unavailable until their native
result adapters expose a validated atomic-population contract. An explicit
unsupported write_molden_file=True or
write_population_file=True fails before calculation; None (the default)
selects only the sidecars the chosen route can produce truthfully.
DFTB0-SECCM (experimental)¶
The first non-MSINDO SECCM adapter is available through the direct
run_dftb0_seccm API. It consumes a separately built and finite-group-bound
SECCM topology; it does not reinterpret the ordinary Gamma-periodic DFTB0
cutoff domain. The current T3a gate accepts neutral, closed-shell, insulating
one-dimensional H/C cyclic clusters with the built-in in-house screening
parameter set:
import numpy as np
from vibeqc import Atom, Molecule
from vibeqc.semiempirical import run_dftb0_seccm
from vibeqc.semiempirical.seccm import (
bind_finite_group,
build_seccm_topology,
)
a = np.array([8.0, 0.0, 0.0])
coords = np.array([[0.0, 0.0, 0.0], [1.4, 0.0, 0.0]])
topology = build_seccm_topology(
coords, [a], length_unit="bohr", geometry_quantum=1.0e-10
)
topology = bind_finite_group(
topology,
primitive_vectors=[a],
replicas=(1, 1, 1),
geometry_tolerance=1.0e-9,
length_unit="bohr",
)
mol = Molecule([Atom(1, xyz.tolist()) for xyz in coords])
result = run_dftb0_seccm(mol, topology, compute_gradient=True)
The reported energy is per primitive cell. Its complete T3a closure is
energy = electronic_energy + repulsive_energy
+ long_range_energy + dispersion_energy + specific_energy
where the last three terms are explicitly zero. The adapter evaluates the
full finite cyclic cluster, so a valid group-only defect topology is allowed;
translation-orbit reduction is not used. With compute_gradient=True,
result.gradient is the analytic derivative of the same per-cell electronic
and repulsive closure over the frozen record set. It does not differentiate a
topology switch or the cyclic translations.
Periodic reaction paths can select this adapter explicitly with
run_neb(..., method="dftb0", seccm_topology=topology). The endpoint cell must
match the topology translations. A topology containing exact ownership ties
also requires a positive seccm_max_tie_score_excursion trust bound. SCC
charges, open shells, charged cells, stress, smearing, broader parameter sets,
and two- or three-dimensional public calls fail closed.
Periodic systems¶
Periodic support is route-specific. DFTB0, SCC-DFTB, GFN2-xTB, PM6, and OMx have Gamma-point periodic energy routes; their periodic gradients and stress remain experimental and route-labeled as analytic or finite-difference stopgaps in the status table below. Full k-point public semiempirical routes are currently limited to closed-shell DFTB0 and SCC-DFTB energy/band jobs plus the experimental lower-level DFTB derivative and NEB facades. MSINDO periodic work uses the SECCM cyclic-cluster route, not the Gamma/k-point runner.
DFTB periodic¶
from vibeqc._vibeqc_core import PeriodicSystem, Atom
from vibeqc._vibeqc_core import semiempirical as _se
import numpy as np
# 1D carbon chain
atoms = [Atom(6, [0.0, 0.0, 0.0])]
cell = np.diag([2.5, 30.0, 30.0])
system = PeriodicSystem(1, cell, atoms)
params = _se.SemiempiricalParameters.dftb0_default()
result = _se.run_dftb0_gamma(system, params)
print(f"Energy: {result.energy:.6f} Ha")
GFN2 periodic¶
from vibeqc._vibeqc_core.semiempirical import xtb as _xtb
from vibeqc.semiempirical.methods.gfn2_params import load_gfn2_params
params = load_gfn2_params()
result = _xtb.run_gfn2_xtb_gamma(system, params)
The native Gamma driver also accepts explicit finite-temperature occupations for metallic or near-degenerate cells:
opts = _xtb.XTBSccOptions()
opts.electronic_temperature = 0.001 # electronic k_B T in Hartree
result = _xtb.run_gfn2_xtb_gamma(system, params, opts)
This applies particle-number-conserving Fermi-Dirac occupations. The default
remains exact zero-temperature Aufbau occupation; vibe-qc does not silently
select a smearing temperature. The reported energy is the GFN2 internal
energy at the fractional-occupation density, not a Helmholtz free energy.
PM6 / OMx periodic¶
from vibeqc.semiempirical import PeriodicPM6Model, PeriodicOMxModel
pm6 = PeriodicPM6Model(system)
print(f"PM6: {pm6.energy():.6f} Ha")
omx = PeriodicOMxModel(system, variant="om2")
print(f"OM2: {omx.energy():.6f} Ha")
Periodic OMx constructs charge-conserving nonzero-image potentials from
Mulliken populations, using Tr(P S) rather than Tr(P) as the electron
count in its non-orthogonal basis. Directed image overlap, resonance, and
exchange blocks are counted once, while image core pairs carry their required
one-half energy weight. The one-carbon-chain cutoff-shell regression is stable
within 0.7e-6 Ha when its direct-space cutoff doubles from 15 to 30 bohr.
This closes the neutral-monopole discontinuity, not the method’s production
gate: independent MOPAC parity and broader polar/two-dimensional validation
remain open.
Preoptimization workflows¶
Use semiempirical methods for fast structure preoptimization before an expensive DFT calculation:
from vibeqc.semiempirical import preoptimize_periodic
# Preoptimize a periodic system with DFTB0, then run DFT
preoptimize_periodic(
system,
method="dftb0",
fmax=0.01,
)
For molecular systems, use optimize=True with run_job:
from vibeqc import run_job
# Preoptimize with DFTB0, then refine with DFT
run_job(mol, method="dftb0", optimize=True)
run_job(mol, method="rks", functional="PBE", basis="def2-svp", optimize=True)
Method status¶
Method |
Status |
Energy accuracy |
Gradient |
Periodic |
Open-shell |
Elements |
|---|---|---|---|---|---|---|
DFTB0 / UDFTB0 |
Screening/preopt |
In-house parameters, not DFTB+ parity |
Analytic |
Gamma; full-k DFTB0 closed-shell |
molecular/Gamma yes; full-k closed-shell only |
91 in-house |
DFTB0-SECCM |
Gated experimental |
In-house H/C screening parameters; finite-cluster T3a closure |
Analytic, fixed topology |
1-D direct finite torus |
no |
H, C |
SCC-DFTB / USCC |
Screening/preopt |
In-house parameters, not DFTB+ parity |
Analytic |
Gamma; full-k SCC-DFTB closed-shell |
molecular/Gamma yes; full-k closed-shell only |
91 in-house |
GFN2-xTB |
Gated experimental |
External xTB parity matrix still open |
Analytic (FD-consistent to <1e-5 Ha/bohr on H2O/CH4/NH3: full H0 shape + shell-ES + 3rd-order + AES derivatives; periodic Gamma is fixed-charge) |
Gamma experimental; full-k gated |
no |
86 fetched (LGPL) |
PM6 / UPM6 |
Production (molecular) |
Physical PES pinned by regression; spherical Klopman-Ohno fallback where MOPAC diatomic data is absent (wells ~0.1-0.3 bohr long) |
FD |
Gamma experimental, closed-shell |
molecular yes; periodic no |
82 bundled MOPAC |
OM2 / OM3 |
Production (molecular) |
Published OMx Hamiltonian (Dral 2016); relative energetics match published benchmarks (H3- bend 0.1 kcal/mol, ethane barrier ±0.6); bond minima ~0.1-0.2 Å long (documented integral stand-ins) |
FD |
Gamma, legacy-model parity only, closed-shell |
molecular yes; periodic no |
5 published (H,C,N,O,F) |
OM1 |
Experimental (warns) |
Analytic core-valence ECP (Kolb-Thiel 1993) not implemented; X-H bonds ~0.3 A short, close contacts can collapse |
FD |
Gamma, legacy-model parity only, closed-shell |
molecular yes; periodic no |
5 published (H,C,N,O,F) |
MSINDO |
Production within scope |
Reference MSINDO parity <=1 uHa (INDO + NDDO) |
FD and analytic molecular/CCM paths |
SECCM 1-D/2-D/3-D + Ewald |
molecular UHF s/p/d within validated fixtures; SECCM closed-shell |
H-Xe (Z=1-54); NDDO H,Li-F,Na-Cl |
The same implementation labels are available from Python through
vibeqc.semiempirical.semiempirical_route_status(route). Routes such as
msindo-cis, msindo-cis-gradient, msindo-ovgf, msindo-md, and
msindo-metadynamics are explicitly marked python-reference until their hot
loops move to native kernels or are declared intentionally orchestration-only.
periodic-pm6 and periodic-omx are marked mixed-native: Gamma energy uses
native NDDO kernels, while the current gradient, stress, and cell-optimization
helpers still rely on finite-difference Python orchestration. The lookup also
labels molecular pm6-gradient-fd and omx-gradient-fd as native-fd: their
displacement loops are C++-backed, but they remain finite-difference stopgaps
until analytic molecular NDDO gradients land. The lookup accepts public method
aliases such as dftb0, scc-dftb, gfn2xtb, om2, om2-gradient-fd,
gfn2, msindo, and ccm.
Direct run_semiempirical(...) results compute gradients lazily when
result.gradient() is called. DFTB0/SCC-DFTB, GFN2-xTB, PM6/UPM6, and OMx
expose their existing gradient surfaces through that adapter, while
closed-shell MSINDO INDO uses the native analytic-gradient route there.
MSINDO NDDO and open-shell MSINDO keep gradient() unset at the unified runner
layer until those gradient surfaces are promoted. Geometry-optimizer
SemiempiricalProvider calls the same unified runner, preserving the documented
MSINDO finite-difference force fallback when a MSINDO result is energy-only.
See also
Semiempirical and MACE method comparison for production guidance and
../semiempirical_acceptance_matrix.py for the living validation-gate
matrix.
Element coverage¶
DFTB, 91 elements (H-U except Po/Z=84), including 3d/4d/5d transition metals, lanthanides (La-Lu), and early actinides (Ac-U). All parameters are in-house estimates; DFT-fitted production repulsive potentials are deferred.
GFN2-xTB, 86 elements from the published Grimme-group parameter set. Parameters are fetched on demand at first use (LGPL-3.0 licensed, not bundled, see ADR-002).
PM6, 82 elements from the bundled MOPAC PM6 parameter cache (Apache-2.0 provenance in the TOML header), with the Stewart 2007 H/C/N/O/F subset still available. The public wrapper auto-selects the MOPAC-derived cache for elements outside H/C/N/O/F.
OMx, 5 elements (H, C, N, O, F) from Dral 2016 Tables 1-3.
# Check element coverage
from vibeqc.semiempirical import SemiempiricalParameters
params = SemiempiricalParameters.dftb0_default()
elements = [Z for Z in range(1, 93) if params.has_element(Z)]
print(f"DFTB covers {len(elements)} elements")
Parameter customisation¶
DFTB custom parameters¶
from vibeqc.semiempirical import SemiempiricalParameters
custom = SemiempiricalParameters()
custom.add_element(
Z=1, on_site=[-0.21], zeta=[1.24],
hubbard_u=0.42, valence_electrons=1,
)
custom.add_element(
Z=8, on_site=[-0.89, -0.33], zeta=[2.25, 2.25],
hubbard_u=0.45, valence_electrons=6,
)
# Set repulsive pair (R⁻¹² form)
custom.set_repulsive_pair_analytic(1, 1, A=5.0)
custom.set_repulsive_pair_analytic(1, 8, A=15.0)
custom.set_repulsive_pair_analytic(8, 8, A=40.0)
model = DFTB0Model(mol, params=custom)
GFN2 parameters¶
GFN2-xTB parameters are fetched automatically from the Grimme group’s GitHub repository. To force a refresh:
from vibeqc.semiempirical.methods.gfn2_params import load_gfn2_params
params = load_gfn2_params(force_refetch=True)
For offline batch planning, probe the local cache without opening the network:
from vibeqc.semiempirical import semiempirical_route_runtime_available
if not semiempirical_route_runtime_available("gfn2_xtb"):
# Mark GFN2-xTB rows unavailable before submitting the batch.
...
PM6 parameters¶
from vibeqc.semiempirical.methods.pm6_params import load_pm6_params
params = load_pm6_params()
model = PM6Model(mol, params=params)
Comparing against external programs¶
Reference energies from external programs can be obtained via out-of-process subprocess runners (External QC codes (ORCA / Psi4 / others)):
from examples.regression.core.runner_xtb import energy as xtb_energy
from examples.regression.core.runner_mopac import energy as mopac_energy
from examples.regression.core.runner_dftbp import energy as dftbp_energy
print(f"xTB GFN2 H2O: {xtb_energy('H2O'):.6f} Eh")
print(f"MOPAC PM6 H2O: {mopac_energy('H2O'):.6f} Ha")
print(f"DFTB+ H2O: {dftbp_energy('H2O'):.6f} Ha")
These runners require the external program to be installed on $PATH
(see each runner’s docstring for install instructions).
Performance tips¶
DFTB0 is 3-5× faster than SCC‑DFTB (no SCF loop). Use it for preoptimization where charge self-consistency is less important.
DFTB gradients (DFTB0 and SCC-DFTB) are analytic and match finite differences tightly; the SCC energy is variational in the density, so its fixed-charge gradient is exact at SCC convergence.
Periodic systems support Gamma-point energy routes for DFTB0/SCC-DFTB, GFN2-xTB, PM6, and OMx. Public full-k semiempirical support is DFTB0 and SCC-DFTB only, closed-shell only. Increase the lattice cutoff (
cutoff_bohr) for tight cells.Memory is negligible, the basis is minimal (one function per valence shell).
Known limitations¶
DFTB repulsive potentials are in-house R−12 estimates; DFT-fitted production repulsives are deferred.
GFN2-xTB still lacks periodic AES image-cell multipole Ewald terms and a closed external
xtbparity matrix (Method status).PM6 reports a PM6-like total, not a MOPAC heat of formation; use MOPAC out-of-process when exact MOPAC convention parity is required.
Periodic PM6 cancels neutral-pair lattice monopoles, but bare PM6 has no validated rare-gas dispersion model; do not use its dissociative fcc-Ar curve as a physical equation of state.
OM2/OM3 are production molecular paths within their documented H/C/N/O/F scope; OM1 remains experimental until the analytic core-valence ECP lands.
Periodic GFN2/NDDO gradients are finite-difference only; analytic periodic NDDO gradients are deferred.
MSINDO molecular closed-shell analytic gradients and closed-shell CCM analytic / finite-difference gradients are available through the native route inside their documented scopes. NDDO, odd-electron analytic gradients, and excited-state/root-tracking gradients remain on their documented fallback or reference paths. See MSINDO (semiempirical INDO).