Skip to content

DFTB+

DFTB+: density-functional tight binding (DFTB, SCC-DFTB, DFTB3) and xTB.

The codec writes dftb_in.hsd and geometry.xyz and runs dftb+ in the calculation directory; results come from DFTB+'s machine-readable results.tag. Install DFTB+ with conda install -c conda-forge dftbplus; qccompute finds dftb+ on PATH (or set QCCOMPUTE_DFTBPLUS_EXECUTABLE). Tested with DFTB+ 25.1, and the input pins HSD parser version 14.

Inputs map to DFTB+ as follows:

  • model.method is the Hamiltonian: DFTB3 (third order, with Hubbard derivatives and H-bond damping), SCC-DFTB (self-consistent charges), DFTB (non-self-consistent), or GFN2-xTB, GFN1-xTB, IPEA1-xTB (DFTB+'s xTB implementation, which needs no parameter files).
  • model.basis names the Slater-Koster parameter set for the DFTB methods (3ob-3-1, mio-1-1) and is empty for xTB. The codec takes each element's maximum angular momentum and, for DFTB3, its Hubbard derivative and the H-bond damping exponent from qcref.dftb_parameter_sets, as the set's documentation prescribes. Cite the references in the set's citation when you publish.
  • The Slater-Koster files are not shipped with DFTB+. Download sets from https://github.com/dftbparams into one directory, each in its own subdirectory named like model.basis (as the archives unpack: <dir>/3ob-3-1/O-H.skf), and name that directory with the slater_koster_dir keyword or the QCCODEC_DFTB_PARAMETERS environment variable, read when the input is encoded. (ASE's DFTB_PREFIX names a single set's directory instead, so it is not used.)
  • structure.charge becomes Charge. A multiplicity above 1 adds SpinPolarisation = Colinear { UnpairedElectrons = ... }; DFTB methods then also need the set's SpinConstants in keywords.
  • keywords are DFTB+ input in HSD form, as a nested dictionary merged into the generated input: {"Hamiltonian": {"SCCTolerance": 1e-8, "MaxSCCIterations": 500}}. A dictionary is an HSD block, True/False become Yes/No, numbers are written as numbers, a list becomes space-separated values, and a string is written verbatim, so it carries HSD's own syntax: '"water"' for a quoted string, 'Lbfgs {}' or a multi-line 'DftD3 { ... }' for a typed block (HSD takes one assignment per line, so write such blocks across lines). Geometry and Hamiltonian/Charge come from structure and cannot be set. Atom indices in verbatim HSD (MovedAtoms, Atoms) are 1-based, as DFTB+ counts them.
  • cmdline_args are appended to the dftb+ command.

Calculation types: energy, gradient, hessian (Driver = SecondDerivatives: DFTB+ differentiates gradients at displaced geometries only, so the result has the Hessian but no energy or gradient at the input geometry; run a gradient for those. The codec tightens SCCTolerance to 1e-8 for these runs, since DFTB+'s default 1e-5 leaves noise of about 1e-4 in the Hessian), and optimization (Driver = GeometryOptimisation with LBFGS on all atoms; override any part through keywords["Driver"]). Energies and gradients also fill atomic_charges (Mulliken) and scf_dipole_moment. The optimization trajectory holds each step's structure and energy; the last step also carries the gradient.

DFTB+ never moves or reorients the molecule, so results are in the input's frame. DFTB+ stops on any keyword it does not recognize, and the codec reports its error message.

Examples

Each script runs as written. Copy it, delete the options you don't need, and run it with python script.py. They live in the repository's examples/ folder.

Single point: energy, gradient, Hessian

examples/dftbplus/single_point.py

"""DFTB+ single point: DFTB3, SCC-DFTB, or xTB energy, gradient, or Hessian.

Requires: ``pip install qccompute`` (it installs qccodec) and DFTB+ on PATH, e.g.
``conda install -c conda-forge dftbplus`` (the program is ``dftb+``). The DFTB
methods also need Slater-Koster parameter files, which DFTB+ does not ship: download
sets from https://github.com/dftbparams (for example 3ob-3-1 and mio-1-1) into one
directory, each in its own subdirectory, and set ``QCCODEC_DFTB_PARAMETERS`` to it
(or pass ``slater_koster_dir`` below). The xTB methods need no files.

How the codec talks to DFTB+ (https://dftbplus.org/documentation):

- ``model.method`` is the Hamiltonian: "DFTB3", "SCC-DFTB", "DFTB" (non-self-
  consistent), or "GFN2-xTB", "GFN1-xTB", "IPEA1-xTB".
- ``model.basis`` names the parameter set for the DFTB methods ("3ob-3-1" for DFTB3,
  "mio-1-1" for SCC-DFTB). The codec fills in the per-element settings the set
  prescribes (angular momenta, DFTB3 Hubbard derivatives, H-bond damping) from
  ``qcref.dftb_parameter_sets``. Cite the set's references: see its ``citation``.
- ``keywords`` are DFTB+ input (HSD) as a nested dictionary merged into what the
  codec writes. Strings are written verbatim in HSD syntax, so quote HSD strings
  ('"water"') and write typed blocks as text, one assignment per line (DFTB+ reads
  ``a = 1  b = 2`` on one line as a single value).
- DFTB+ stops on any keyword it does not recognize, and the error says which.
- Hessians come from finite differences of gradients at displaced geometries only,
  so a Hessian result has no energy or gradient at your structure; run a gradient
  for those.
"""

from qccompute import compute
from qcdata import CalcType, Model, ProgramInput, Structure
from qcref import dftb_parameter_sets

calctype = CalcType.gradient  # or CalcType.energy, CalcType.hessian

# Coordinates are in bohr (Structure.from_xyz / Structure.open read angstrom files).
structure = Structure(
    symbols=["O", "H", "H"],
    geometry=[[0.0, 0.0, 0.0], [0.524, 1.687, 0.48], [1.146, -0.45, -1.354]],
    charge=0,
    multiplicity=1,  # open shells also need the set's SpinConstants in keywords
)

prog_input = ProgramInput(
    program="dftbplus",
    calctype=calctype,
    structure=structure,
    model=Model(method="DFTB3", basis="3ob-3-1"),  # or "SCC-DFTB"/"mio-1-1"
    # model=Model(method="GFN2-xTB"),  # xTB: no basis, no parameter files
    keywords={
        # "slater_koster_dir": "/path/to/dftb-params",  # or QCCODEC_DFTB_PARAMETERS
        #
        # DFTB+ settings, exactly as in its manual (HSD as nested dictionaries):
        "Hamiltonian": {
            "SCCTolerance": 1e-7,  # SCC convergence (charge)
            "MaxSCCIterations": 200,
            # D3(BJ) dispersion with the 3ob README's DFTB3-D3(BJ) parameters. Verbatim
            # HSD must keep one assignment per line, so use a multi-line string:
            # "Dispersion": """DftD3 {
            #     Damping = BeckeJohnson {
            #       a1 = 0.746
            #       a2 = 4.191
            #     }
            #     s6 = 1.0
            #     s8 = 3.209
            #   }""",
            # Implicit solvent: "Solvation" with a GeneralizedBorn or Cosmo block as in
            # the DFTB+ manual (it needs its solvent parameters spelled out).
        },
        # "Parallel": {"UseOmpThreads": True},  # threads come from OMP_NUM_THREADS
    },
)

output = compute(
    prog_input,
    # timeout=600,  # seconds before DFTB+ is killed
    # collect_files=True,  # keep detailed.out, results.tag, ... in output.results.files
    # executables={"dftbplus": "/path/to/dftb+"},  # or QCCOMPUTE_DFTBPLUS_EXECUTABLE
)

results = output.results
print("Energy (Hartree):", results.energy)
if results.gradient is not None:
    print("Gradient (Hartree/bohr):\n", results.gradient)
if results.hessian is not None:
    print("Hessian (Hartree/bohr^2), shape:", results.hessian.shape)
print("Mulliken charges:", results.atomic_charges)
print("Dipole (e*bohr):", results.scf_dipole_moment)
print("DFTB+ version:", results.provenance.program_version)
print("Cite:", dftb_parameter_sets.by_name("3ob-3-1").citation)

Optimization

examples/dftbplus/optimization.py

"""DFTB+ geometry optimization with its own optimizer (LBFGS by default).

Requires: DFTB+ on PATH (``conda install -c conda-forge dftbplus``) and, for the DFTB
methods, the Slater-Koster files from https://github.com/dftbparams in a directory
named by ``QCCODEC_DFTB_PARAMETERS`` (one subdirectory per set). See
single_point.py for how methods, parameter sets, and keywords map onto DFTB+.

Optimization specifics:

- The codec writes ``Driver = GeometryOptimisation`` with LBFGS, all atoms moving,
  200 steps at most, and a largest-gradient criterion of 1e-4 Hartree/bohr. Change
  any of it through ``keywords["Driver"]``, which merges into that block.
- ``MovedAtoms`` takes DFTB+'s own syntax with 1-based atom numbers, e.g. "2:-1" to
  freeze the first atom.
- Every step in ``results.frames`` carries its structure and energy; the last one
  also carries the gradient. The first step is your input structure: DFTB+ does not
  move or reorient it.
- A run that hits MaxSteps raises with "did not converge", trajectory attached.
- For transition states, use geomeTRIC with DFTB+ gradients (``program="geometric"``).
"""

from qccompute import compute
from qcdata import CalcType, Model, ProgramInput, Structure

# Coordinates are in bohr.
structure = Structure(
    symbols=["O", "H", "H"],
    geometry=[[0.0, 0.0, 0.0], [0.524, 1.687, 0.48], [1.146, -0.45, -1.354]],
    charge=0,
    multiplicity=1,
)

prog_input = ProgramInput(
    program="dftbplus",
    calctype=CalcType.optimization,
    structure=structure,
    model=Model(method="DFTB3", basis="3ob-3-1"),  # or Model(method="GFN2-xTB")
    keywords={
        "Driver": {
            "MaxSteps": 100,
            "Convergence": {"GradElem": 1e-4},  # Hartree/bohr
            # "Optimiser": "Rational {}",  # or "Fire {}", "SteepestDescent {}"
            # "MovedAtoms": "2:-1",  # 1-based: keep atom 1 fixed
        },
    },
)

output = compute(prog_input)

results = output.results
print("Steps:", len(results.frames))
print("Energies (Hartree):", results.energies)
print("Final structure (bohr):\n", results.final_structure.geometry)