Geometry optimisation¶
Relax a molecular structure to its energy minimum. vibe-qc uses ASE’s
BFGS-line-search optimiser under the hood, driven by analytic HF/DFT
forces from the C++ core. run_job(..., optimize=True) is the
one-liner; the standard .out / .molden / .traj files all get
written as usual, plus the trajectory animation.
Water at HF/6-31G*¶
Starting from an approximate geometry:
from pathlib import Path
from vibeqc import Atom, Molecule, run_job
mol = Molecule([
Atom(8, [0.0, 0.0, 0.0 ]),
Atom(1, [0.0, 1.60, -1.10]), # slightly off-equilibrium
Atom(1, [0.0, -1.60, -1.10]),
])
run_job(
mol,
basis="6-31g*",
method="rhf",
output="water_opt",
optimize=True,
fmax=0.05, # eV/Å — converged when |F|_max drops below this
max_opt_steps=50,
)
Files produced:
water_opt.out— initial geometry, optimiser parameters, final optimised geometry, then the full SCF trace at the minimumwater_opt.molden— MOs at the minimum, for Avogadro / Jmolwater_opt.traj— one frame per BFGS step (ASE binary); view withase gui water_opt.traj
Reading the output¶
After the banner and atom table, water_opt.out contains:
Geometry optimisation (ASE/BFGS) -> water_opt.traj
fmax = 0.05 eV/A, max_steps = 50
Optimised geometry
Atoms (bohr)
------------------------------------------------------
1 Z= 8 0.00000000 0.00000000 0.07390216
2 Z= 1 0.00000000 1.44014211 -1.00364326
3 Z= 1 0.00000000 -1.44014211 -1.00364326
charge=0 multiplicity=1 n_electrons=10
iter energy (Ha) ...
(SCF trace at the optimised geometry)
...
converged in 7 iterations; E = -76.0108432849 Ha
The OH bond length and HOH angle at this HF/6-31G* minimum: r(OH) = 0.947 Å, HOH = 105.5° (experimental: 0.957 Å, 104.5°). HF slightly under-shoots the bond and opens the angle.
Parameters¶
Argument |
Default |
Meaning |
|---|---|---|
|
0.05 |
Force-convergence tolerance (eV/Å) |
|
200 |
Hard iteration limit for the optimiser |
|
|
Applied to every intermediate SCF; bump |
rhf_options / rks_options forward through to every frame of the
optimisation, not just the final single point. This matters for
H-bonded systems and floppy molecules where one BFGS step sometimes
lands on a particularly stubborn geometry — bump max_iter = 200 or
add damping = 0.3 to the options struct and the whole trajectory
stays stable.
A harder case: the water dimer¶
H-bonded clusters sit on a very flat potential-energy surface — plain
BFGS Hessian extrapolation can push the waters apart before the
quadratic model is trustworthy. run_job uses BFGSLineSearch
which rejects uphill steps by construction, and pair this with a
realistic fmax:
from vibeqc import Atom, InitialGuess, Molecule, RHFOptions, run_job
mol = Molecule.from_xyz("h2o_dimer.xyz") # see examples/h2o_dimer.xyz
rhf_opts = RHFOptions()
rhf_opts.max_iter = 200 # H-bonded intermediates need it
rhf_opts.initial_guess = InitialGuess.SAD
run_job(
mol, basis="6-31g*", method="rhf",
output="water_dimer_opt",
optimize=True,
fmax=0.2, # force precision floor is ~0.1 eV/Å
max_opt_steps=25,
rhf_options=rhf_opts,
)
You’ll get R(OO) ≈ 2.98 Å (experimental: 2.976 Å) — the well-known
HF/6-31G* result. For a real binding-energy number add a correlation
method and dispersion; for a chemistry demonstration this is enough.
Visualising the trajectory¶
The .traj file holds positions, energies, and forces for every
frame. ASE’s built-in GUI animates it:
ase gui water_opt.traj
Or inspect programmatically:
import numpy as np
from ase.io import read
frames = read("water_opt.traj", index=":")
energies = np.array([f.get_potential_energy() for f in frames])
print(f"Energy dropped {energies[0] - energies[-1]:.4f} eV over "
f"{len(frames) - 1} steps")
DFT geometries¶
Same call, different method:
run_job(
mol, basis="def2-tzvp", method="rks", functional="PBE",
output="water_pbe_opt",
optimize=True,
)
PBE lengthens O-H bonds by ~0.02 Å relative to HF and pulls the HOH angle back toward 104°, generally matching experiment more closely (with the well-known caveat that GGA over-binds H-bonds somewhat).
Next¶
Post-SCF analysis — charges, bond orders, and dipole moment at the optimised geometry.
Vibrational frequencies — once the geometry is at a stationary point, compute the Hessian to classify it.