4.13. Geometry optimizationΒΆ

This example serves as a minimal example for geometry optimizations by finding the optimal bond distance in the H2 molecule.


import matplotlib.pyplot as plt
import numpy as np
from scipy.optimize import minimize_scalar

from eminus import Atoms, SCF
from eminus.units import bohr2ang, ha2ev

Create lists to save intermediate results of the optimization

distances = []
energies = []

Create a simple cost function for the optimizer

The objective is of course the total energy of the molecule

Displace one hydrogen by a distance d, choose a small ecut for a faster evaluation

def cost(d):
    atoms = Atoms("H2", [[0, 0, 0], [0, 0, d]], ecut=5)
    scf = SCF(atoms, verbose=0)
    etot = scf.run()
    print(f"d={bohr2ang(d):.6f} A    Etot={ha2ev(etot):.6f} eV")
    distances.append(d)
    energies.append(etot)
    return etot

The total energy for an H2 molecule will be saved in the above lists when calling the cost function

cost(0.5)

Since we only optimize one value here, we use the minimize scalar function

We also add bounds since d=0 should be forbidden

When optimizing multiple coordinates one can give a reasonable start configuration instead of giving explicit bounds

res = minimize_scalar(cost, bounds=(0.1, 10))
print(f"Optimized bond length: {bohr2ang(res.x):.6f} A")

With the saved intermediate H-H distances and the respective energies one can generate a potential energy surface plot

Find the plot named dissociation_energy_h2.png

../_images/dissociation_energy_h2.png
plt.style.use("../eminus.mplstyle")
sort = np.argsort(distances)
plt.figure()
plt.axvline(res.x, c="dimgrey", ls="--", marker="")
plt.plot(np.array(distances)[sort], np.array(energies)[sort])
plt.xlabel(r"H-H distance [$a_0$]")
plt.ylabel(r"E$_\mathrm{tot}$ [$E_\mathrm{h}$]")
plt.savefig("dissociation_energy_h2.png")

The procedure could also be done, e.g., for optimizing the lattice constant of a germanium solid as seen in example 12

Download 13_geometry_optimization.py dissociation_energy_h2.png