Skip to content

Quick start

Relax a water molecule with GFN2-xTB and compare its geometry with experiment.

Start with O–H bonds of 1.10 Å and an angle of 100°.

import math
from atomli import Atoms
bond, half = 1.10, math.radians(100.0) / 2
atoms = Atoms("OH2", positions=[
[0.0, 0.0, 0.0],
[0.0, bond * math.sin(half), bond * math.cos(half)],
[0.0, -bond * math.sin(half), bond * math.cos(half)],
])

Distances are angstrom and energies are electronvolt everywhere at the Python boundary.

The calculator lives on the Atoms object. Everything downstream, optimizers and dynamics included, asks that object for energies and forces.

from atomli.calculators.xtb import XTB
import numpy as np
atoms.calc = XTB(method="gfn2")
print(f"{atoms.get_potential_energy():.3f} eV")
print(f"{np.linalg.norm(atoms.get_forces(), axis=1).max():.3f} eV/A")
-137.320 eV
5.208 eV/A

BFGS follows those forces downhill until the largest one falls below fmax. logfile="-" prints each step to the terminal.

from atomli.optimize import BFGS
frames = [atoms.copy()] # the guess itself
opt = BFGS(atoms, logfile="-")
opt.attach(lambda: frames.append(atoms.copy()), interval=1) # and every step
opt.run(fmax=0.02)
Step Time Energy fmax
BFGS: 0 02:35:01 -137.319890 5.208098
BFGS: 1 02:35:01 -137.910757 1.785617
BFGS: 2 02:35:01 -137.949921 1.460929
BFGS: 3 02:35:01 -137.974519 0.224973
BFGS: 4 02:35:01 -137.975473 0.115909
BFGS: 5 02:35:01 -137.976199 0.110091
BFGS: 6 02:35:01 -137.976494 0.059372
BFGS: 7 02:35:01 -137.976542 0.010002

7 steps. The energy drops 0.657 eV and the force falls by a factor of 521.

Compare the relaxed bond length and angle with experiment.

oh, oh2 = atoms.get_positions()[1:] - atoms.get_positions()[0]
angle = math.degrees(math.acos(oh @ oh2 / (np.linalg.norm(oh) * np.linalg.norm(oh2))))
print(f"O-H {atoms.get_distance(0, 1):.3f} A")
print(f"H-O-H {angle:.1f} deg")

Atoms has get_distance but no get_angle, so the angle is measured by hand.

Your guessGFN2-xTBExperiment
O-H1.100 Å0.959 Å0.958 Å
H-O-H100.0°107.2°104.5°

The bond length lands 0.001 Å from experiment. The angle is 2.7° too wide, which is GFN2-xTB’s own error, not a failed relaxation. A tighter fmax will not move it. Changing method will.

from atomli.visualize import view
view(atoms) # the relaxed molecule
view(frames) # or the whole relaxation, with a scrubber
Water relaxation · 8 frames · GFN2-xTB / BFGS

In a notebook, view() displays the structure in the cell. An atoms object on the last line also displays the structure. Drag to rotate. Use the playback bar to select a frame. See Visualization for image export and live updates.

  • Optimization for the other optimizers and for relaxing cells rather than just positions.
  • Choosing a calculator to swap GFN2-xTB for DFT or a machine-learned potential. Only the atoms.calc line changes.
  • Molecular dynamics to move the atoms instead of minimizing them.
  • ASE compatibility if you are porting a script, including the import atomli as ase drop-in style.

Import from the public atomli modules. The compiled atomli._core is an implementation detail and its surface is not stable.