4.16. FOD optimizationΒΆ

This example offers a simple implementation for the optimization of FODs without an analytical expression for the forces.


import numpy as np
from scipy.optimize import minimize

from eminus import Atoms, read, SCF
from eminus.energies import get_Esic
from eminus.orbitals import FLO, WO
from eminus.tools import orbital_center

Do a simple calculation for methane

Use a very small cutoff energy for a small grid

The optimization function will be called hundreds of times, so we are interested in speed for this simple example

atom, pos = read("CH4.xyz")
atoms = Atoms(atom, pos, ecut=5, center=True)
scf = SCF(atoms)
scf.run()

Generate an initial guess by calculating the center of mass of Wannier orbitals

wo = WO(scf)
fods = orbital_center(atoms, wo[0])
print(f"\nInitial FODs:\n{fods}")

Example implementation for a FOD optimization

This implementation works for restricted and unrestricted calculations

def optimize_fods(scf, fods):
    def x2fods(x):
        """Transform a 1d list back to FODs."""
        nfods = [len(fod) for fod in fods]
        fod_up = np.reshape(x[: nfods[0] * 3], (nfods[0], 3))
        if len(nfods) > 1 or nfods[1] > 0:
            fod_dn = np.reshape(x[nfods[0] * 3 :], (nfods[1], 3))
            return [fod_up, fod_dn]
        return [fod_up]

    def get_sic_energy(x):
        """Wrapper function to calculate the SIC energy from a 1d list of FODs."""
        fods = x2fods(x)
        flo = FLO(scf, fods=fods)
        return get_Esic(scf, scf.atoms.J(flo, full=False))

    # Convert FODs to a list such that SciPy's minimize function can work with them
    x = np.concatenate([fod.flatten() for fod in fods])
    # Call the optimizer
    print("\nStart FOD optimization...")
    result = minimize(get_sic_energy, x0=x, method="nelder-mead", tol=1e-4, options={"disp": True})
    # Print the SIC energies
    print(f"\nInitial SIC energy:   {get_sic_energy(x):.6f} Eh")
    print(f"Optimized SIC energy: {get_sic_energy(result.x):.6f} Eh")
    # Return the FODs in a usable format
    return x2fods(result.x)

Optimize the FODs

You may have to run this a few times since the optimizer can sometimes run into an unphysical solution

fods = optimize_fods(scf, fods)
print(f"\nOptimized FODs:\n{fods}")

Write the optimized FODs to a file

atoms.write("CH4_fods.xyz", fods=fods)

Download 16_fod_optimization.py CH4.xyz