Skip to content

Transition state optimization

Qcore supports transition state (TS) optimization using the eigenvector-following algorithm. This is done by setting the option ts = true within the optimize() command. The minimal input for a TS optimization is:

optimize(
  structure( xyz =
    [[C, -1.7750, -0.5026, 0.0000],
     [C, -0.4916,  0.1859, 0.0000],
     [C,  0.6671, -0.5360, 0.0000],
     [H, -0.4532,  1.2741, 0.0000],
     [H,  1.6489, -0.0536, 0.0000],
     [H, -2.6886,  0.1279, 0.0000],
     [O,  0.7308, -1.8618, 0.0000],
     [O, -1.8897, -1.7305, 0.0000],
     [H, -0.5902, -2.2396, 0.0000]] )
  ts = true
  xyz_output = 'malondialdehyde_ts.xyz'
  xtb()
)

In this example, we seek the proton-transfer TS structure of Malondialdehyde using GFN-xTB. We start with an initial structure that is reasonably close to the true TS structure.

Searching for a TS is usually more difficult than geometry optimization. The model Hessian used in downhill minimization is not adequate for TS optimization. Therefore, the optimize() command will compute the exact Hessian using the energy method provided. This must contain at least one imaginary frequency. To increase the chance of finding the desired TS structure we also recommend first performing a constrained optimization. By fixing bonds/angles/dihedrals that are close to the TS, the Hessian is more likely to possess the desired imaginary frequency. Here is an example input:

! first perform constrained optimization
constrained_opt := optimize(
  structure(xyz =
    [[C, -1.7750, -0.5026, 0.0000],
     [C, -0.4916,  0.1859, 0.0000],
     [C,  0.6671, -0.5360, 0.0000],
     [H, -0.4532,  1.2741, 0.0000],
     [H,  1.6489, -0.0536, 0.0000],
     [H, -2.6886,  0.1279, 0.0000],
     [O,  0.7308, -1.8618, 0.0000],
     [O, -1.8897, -1.7305, 0.0000],
     [H, -0.5902, -2.2396, 0.0000]]
  )
  bond(atoms = [7, 9] value = 1.38 angstrom)
  bond(atoms = [8, 9] value = 1.38 angstrom)
  xtb()
)

! perform TS search by loading constrained structure
ts_opt := optimize(
  structure(load = constrained_opt)
  ts = true
  xtb()
)

! compute Hessian and normal modes for the optimized TS structure
final_hess := hessian(
  structure(load = ts_opt)
  save_normal_modes = 'final.Molden'
  xtb()
)

In this example, we also demonstrate how to perform linked calculations:

  1. First, a constrained geometry optimization is performed, with the two O-H bond lengths set to 1.38 Å and the remaining degrees of freedom relaxed. The results are stored in the user-defined variable constrained_opt using the := operator.

  2. The resulting geometry is used to launch a transition state optimization by loading constrained_opt into the structure() subcommand.
    The results are stored in the user-defined variable ts_opt.

  3. To confirm the transition-state nature of the structure, the TS-optimized geometry is used to perform a vibrational frequency calculation by loading ts_opt in to the structure() subcommand. The resulting frequencies and normal modes are output to the user-defined file 'final.Molden'.

Qcore also allows the Hessian to be computed at a lower-level of theory. This scheme requires care, but it can massively accelerate the process of TS optimization. In the final example below, we demonstrate calculating the Hessian using GFN-xTB to find the Hartree-Fock TS structure. The Hessian is saved to guess_hessian and loaded in to the optimize() command with load = guess_hessian. Here is an example input:

! first perform constrained optimization
constrained_opt := optimize(
  structure( xyz =
    [[C, -1.7750, -0.5026, 0.0000],
     [C, -0.4916,  0.1859, 0.0000],
     [C,  0.6671, -0.5360, 0.0000],
     [H, -0.4532,  1.2741, 0.0000],
     [H,  1.6489, -0.0536, 0.0000],
     [H, -2.6886,  0.1279, 0.0000],
     [O,  0.7308, -1.8618, 0.0000],
     [O, -1.8897, -1.7305, 0.0000],
     [H, -0.5902, -2.2396, 0.0000]]
  )
  bond( atoms = [7, 9] value = 1.38 angstrom )
  bond( atoms = [8, 9] value = 1.38 angstrom )
  xtb()
)

! perform a cheap hessian calculation
guess_hessian := hessian(
  structure( load = constrained_opt )
  xtb()
)

! perform TS search by loading constrained structure and initial guess hessian
ts_opt := optimize(
  structure( load = constrained_opt )
  load = guess_hessian
  ts = true
  dft( xc = pbe ao = '6-31G*' )
)

! compute Hessian and normal modes for the optimized TS structure
final_hess := hessian(
  structure( load = ts_opt )
  save_normal_modes = 'final.Molden'
  dft( xc = pbe ao = '6-31G*' )
)

Next: Ab initio molecular dynamics