Skip to content

Add a GFN-xTB engine (tblite) alongside DFTB+ - #68

Merged
galjos merged 1 commit into
mainfrom
xtb-engine
Jul 5, 2026
Merged

Add a GFN-xTB engine (tblite) alongside DFTB+#68
galjos merged 1 commit into
mainfrom
xtb-engine

Conversation

@galjos

@galjos galjos commented Jul 5, 2026

Copy link
Copy Markdown
Member

Adds xTB (GFN-xTB via tblite) as a second engine for the screening pipeline. It runs in-process — no external binary and no Slater-Koster files, since GFN-xTB parameters are built in for H–Rn.

Usage

thermo screen mols/ --engine xtb --method GFN2-xTB

(parameter-set / solvent apply only to --engine dftb+.)

What changed

  • calculator/xtb.py (new) — optimise_and_frequencies (ASE optimiser + finite-difference vibrations → energy in Hartree + sorted real frequencies in cm⁻¹) and xtb_calculator (a tblite GFN-xTB ASE calculator). tblite is imported lazily, so it's only needed when the xTB engine is actually used — the package imports fine without it.
  • thermo/api.pyxtb_thermo, mirroring dftbplus_thermo. Charge and the minimum-spin electron-count guess become xTB's charge / unpaired electrons (atoms.info), so radicals run open-shell automatically. quasi-RRHO composes.
  • thermo/thermo.py — accept engine="xtb" (same Hartree/cm⁻¹ units as dftb+).
  • thermo/screening.py, cli/thermo.pyscreen(engine=…, method=…) dispatch + --engine/--method.

Frequency handling

ASE's Vibrations returns 3N complex frequencies; imaginary modes are mapped to negative reals and the array is sorted ascending, so System keeps the top dof real vibrations (trans/rot and any imaginary modes drop out) — the same contract as the DFTB+ modes path.

Scope note

Solvation isn't wired up: the tblite ASE calculator doesn't expose GBSA/ALPB (the alpb/gbsa kwargs are silently ignored — energy unchanged), so xTB runs gas-phase. Adding it would need the lower-level tblite API; a clean follow-up.

Validation

  • Real GFN2-xTB end-to-end: gas-phase water S = 45.05 cal/mol/K vs. experiment ~45.1; OH radical auto-runs open-shell; quasi-RRHO toggle composes.
  • optimise_and_frequencies is covered offline with ASE's built-in EMT (no tblite); xtb_thermo/dispatch are covered by mocked tests; a real GFN2 test runs when tblite is present and skips otherwise.
  • Offline suite green (214 passed); real DFTB+ integration green (regression, 59 passed); patch coverage verified with and without tblite (0 uncovered either way, so codecov/patch passes in CI where tblite is absent).

Dependency

The xTB engine needs tblite-python (optional): conda install -c conda-forge tblite-python. Without it, DFTB+ works unchanged and the xTB tests skip.

Add xTB as a second engine for the screening pipeline, running in-process
through tblite (no external binary, no Slater-Koster files -- GFN-xTB
parameters are built in for H-Rn). The geometry is optimised with an ASE
optimiser and frequencies come from ASE's finite-difference vibrations, so the
result feeds the existing thermochemistry exactly like the DFTB+ path.

- calculator/xtb.py: `optimise_and_frequencies` (ASE optimiser + vibrations,
  returning energy in Hartree and sorted real frequencies in cm^-1) and
  `xtb_calculator` (a tblite GFN-xTB ASE calculator). tblite is imported
  lazily, so it is only required when the xTB engine is actually used.
- thermo/api.py: `xtb_thermo`, mirroring `dftbplus_thermo`. Charge and the
  minimum-spin electron-count guess are passed through as xTB charge/unpaired
  electrons, so radicals run open-shell automatically. quasi-RRHO composes.
- thermo/thermo.py: accept `engine="xtb"` (same Hartree/cm^-1 units as dftb+).
- thermo/screening.py, cli/thermo.py: `screen(engine=..., method=...)` and
  `screen --engine xtb --method GFN2-xTB`; parameter-set/solvent stay DFTB+-only.

Implicit solvation is not wired up: the tblite ASE calculator does not expose
GBSA/ALPB, so xTB runs gas-phase for now. Validated against real GFN2-xTB: water
gives S = 45.05 cal/mol/K (experiment ~45.1) and an OH radical runs open-shell.
@codecov

codecov Bot commented Jul 5, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 96.57%. Comparing base (c403e97) to head (037cca7).

Additional details and impacted files
@@            Coverage Diff             @@
##             main      #68      +/-   ##
==========================================
+ Coverage   96.47%   96.57%   +0.09%     
==========================================
  Files          22       23       +1     
  Lines        1419     1459      +40     
==========================================
+ Hits         1369     1409      +40     
  Misses         50       50              
Flag Coverage Δ
unittests 96.57% <100.00%> (+0.09%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@galjos
galjos merged commit fd6cac2 into main Jul 5, 2026
3 checks passed
@galjos
galjos deleted the xtb-engine branch July 5, 2026 11:17
galjos added a commit that referenced this pull request Jul 6, 2026
#70)

Closes the gap the last two discussions surfaced: **neither prior xTB
path can do open-shell + solvation together.**
- The tblite in-process engine (#68) can't do solvation — tblite's ASE
calculator exposes no ALPB/GBSA (its C-API has no solvation entry
point).
- Running xTB *through* DFTB+ can't do open-shell — spin-polarized xTB
is a [known unresolved DFTB+
limitation](dftbplus/dftbplus#972).

The **native `xtb` binary does all of it**, so this adds it as the
`xtb-cli` engine — covering **charged radicals in solution** with
method-consistent parameters.

## Usage
```
thermo screen mols/ --engine xtb-cli --solvent water          # radical anions in solvent
```

## What changed
- **`calculator/xtb_cli.py`** (new) — runs `xtb --ohess --gfn <n> --chrg
<c> --uhf <2S> [--alpb <solvent>]`, then parses the energy
(`xtbopt.xyz`), optimised geometry, and frequencies (`vibspectrum`,
wavenumber = 3rd-from-last token). The executable is found via
`$XTB_COMMAND` or PATH; **nothing is imported from xtb**, so the package
still imports without it.
- **`thermo/api.py`** — `xtb_cli_thermo`, resolving the electron-count
spin guess to `--uhf` (radicals open-shell automatically) and `solvent`
→ `--alpb`.
- **`thermo/screening.py`, `cli/thermo.py`** —
`screen(engine="xtb-cli")` / `--engine xtb-cli`; `solvent` now applies
to both dftb+ and xtb-cli.

## Validation
Real xtb 6.7.1: gas-phase water **S = 45.05 cal/mol/K** (experiment
~45.1); an OH radical runs **open-shell** and is stabilised by **~6.5
kcal/mol in water** (`--alpb`) — `ΔG_solv` on an open-shell radical, the
exact case that motivated this. Energy/frequencies match the tblite
path.

Offline suite **223 passed / 6 skipped**; real DFTB+ + xtb integration
**45 passed**. CI-safe tests cover parsing, command construction (mocked
subprocess), dispatch, and the missing-binary error; a real end-to-end
test runs when `xtb` is present and skips otherwise. Patch coverage
verified **without** the xtb binary (0 uncovered).

## Dependency
`xtb-cli` needs the native `xtb` binary (optional): `conda install -c
conda-forge xtb`. Without it, the other engines work unchanged and the
xtb-cli tests skip.
galjos added a commit that referenced this pull request Jul 6, 2026
Follow-up to the xtb engines (#68, #70): `thermo doctor` only checked
the DFTB+ toolchain. Now it also reports the xTB backends, so `doctor`
reflects all three engines.

## What it reports
- **`xtb`** — the native binary for `--engine xtb-cli` (resolved via
`$XTB_COMMAND`, then PATH).
- **`tblite`** — the Python package for `--engine xtb`.

```
dftb+        found    /.../bin/dftb+
modes        found    /.../bin/modes
DFTB_PREFIX  found    /.../3ob/skfiles
C-C.skf      found    /.../3ob/skfiles/C-C.skf
xtb          found    /.../xtbcli/bin/xtb
tblite       found    importable
```
When missing, the detail line gives the fix:
```
xtb     missing  not found (set XTB_COMMAND or `conda install -c conda-forge xtb`; needed for --engine xtb-cli)  (optional)
tblite  missing  not importable (`conda install -c conda-forge tblite-python`; needed for --engine xtb)  (optional)
```

## Design: optional, so DFTB+-only users are unaffected
- The xTB checks are marked `(optional)` and **do not change the doctor
exit code** — it still fails only on a missing *required* DFTB+ backend
(`dftb+`/`modes`/`DFTB_PREFIX`/`C-C.skf`).
- `Diagnostic` gains an `optional` flag; `run_doctor` filters optional
items from the pass/fail decision.

This is a small step toward the "helpful doctor" idea — the detail now
includes the exact `conda install` command for whatever's missing.

## Note
This was originally committed onto the #70 branch, but #70 merged before
its CI picked up the extra commit — so it's re-based here as its own PR
off the current main. Offline suite: 252 passed / 11 skipped.
galjos added a commit that referenced this pull request Jul 6, 2026
`environment.yml` already installed DFTB+ but predates the xTB engines
(#68, #70). Add the xTB backends so `conda env create -f
environment.yml` provisions **every** calculation engine in one command.

```yaml
dependencies:
  - python>=3.12
  - dftbplus        # DFTB+ engine: `dftb+` and `modes`
  - xtb             # native xtb binary, for --engine xtb-cli
  - tblite-python   # GFN-xTB via tblite, for --engine xtb
  - pip
  - pip:
      - "-e .[test,lint]"
```

After `conda env create -f environment.yml && conda activate
thermoscreening`, `thermo doctor` reports every backend as found (pairs
with #71). README updated to say the env covers all backends.

Verified the three backends resolve on conda-forge (dftbplus-25.1 nompi,
xtb-6.6.1, tblite-python-0.3.0); YAML validated.

This is the "installable through the project" follow-up: the compiled
backends (DFTB+/modes/xtb/tblite) can't be pip-installed, so the conda
env is how the project bundles them, while `thermo setup-dftb` handles
the Slater-Koster and GBSA parameter files.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant