Using built-in ase optimization with vasp

Using built-in ase optimization with vasp#

ASE has some nice optimization tools built into it. We can use them in vasp too. This example is adapted from this test: https://trac.fysik.dtu.dk/projects/ase/browser/trunk/ase/test/vasp/vasp_Al_volrelax.py

First the VASP way.

from vasp import Vasp
from ase.build import bulk

Al = bulk('Al', 'fcc', a=4.5, cubic=True)
calc = Vasp(label='bulk/Al-lda-vasp',
            xc='LDA', isif=7, nsw=5,
            ibrion=1, ediffg=-1e-3,
            lwave=False, lcharg=False,
            atoms=Al)
print(calc.potential_energy)
print(calc)
-10.07430731
============================================================
VASP Calculation: /home/jovyan/dft-book/notebooks/04-bulk-systems/bulk/Al-lda-vasp
============================================================

Formula: Al4
Number of atoms: 4

Unit cell:
  Volume: 73.3685 ų
  a=4.1864 b=4.1864 c=4.1864 Å
  α=90.00° β=90.00° γ=90.00°

Energy: -10.074307 eV (-2.518577 eV/atom)
Max force: 0.000000 eV/Å
Pressure: -1.533 GPa
Stress (GPa): xx=1.533 yy=1.533 zz=1.533
              yz=0.000 xz=0.000 xy=0.000

Parameters:
  xc: LDA
  kpts: (1, 1, 1)
  ismear: 1
  sigma: 0.1
  ibrion: 1
  isif: 7
  nsw: 5
============================================================

Now, the ASE way using ExpCellFilter for cell optimization. This filter uses an exponential parametrization of the cell deformation which is more numerically stable than the older StrainFilter.

from vasp import Vasp
from ase.build import bulk
from ase.optimize import BFGS
from ase.constraints import ExpCellFilter  # More stable than StrainFilter
import os

Al = bulk('Al', 'fcc', a=4.5, cubic=True)

calc = Vasp(label='bulk/Al-lda-ase',
            xc='LDA',
            atoms=Al)

calc.calculate()

# Check if we have a completed calculation to work with
if os.path.exists(os.path.join(calc.directory, 'OUTCAR')):
    # Get initial energy to ensure calculator is properly attached
    Al.get_potential_energy()
    
    # Use ExpCellFilter instead of StrainFilter for better numerical stability
    ecf = ExpCellFilter(Al)
    opt = BFGS(ecf, logfile='relaxation.log')
    opt.run(fmax=0.1, steps=5)
    
    print('Stress:\n', calc.stress)
    print('Al post ASE volume relaxation\n', calc.load_atoms().get_cell())
    print(calc)
else:
    print('VASP calculation not yet complete. Run previous cell first.')
    print('Note: ASE cell optimization requires VASP to compute stress.')
Stress:
 [ 7.34461581  7.34461581  7.34461581 -0.         -0.         -0.        ]
Al post ASE volume relaxation
 Cell([4.5, 4.5, 4.5])
============================================================
VASP Calculation: /home/jovyan/dft-book/notebooks/04-bulk-systems/bulk/Al-lda-ase
============================================================

Formula: Al4
Number of atoms: 4

Unit cell:
  Volume: 91.1250 ų
  a=4.5000 b=4.5000 c=4.5000 Å
  α=90.00° β=90.00° γ=90.00°

Energy: -9.584487 eV (-2.396122 eV/atom)
Max force: 0.000000 eV/Å
Pressure: -1176.737 GPa
Stress (GPa): xx=1176.737 yy=1176.737 zz=1176.737
              yz=-0.000 xz=-0.000 xy=-0.000

Parameters:
  xc: LDA
  kpts: (1, 1, 1)
  ismear: 1
  sigma: 0.1
============================================================

Now for a detailed comparison:

from vasp import Vasp
import os
import numpy as np

calc1 = Vasp(label='bulk/Al-lda-vasp')
calc2 = Vasp(label='bulk/Al-lda-ase')

# Check if both calculations exist
if (os.path.exists(os.path.join(calc1.directory, 'OUTCAR')) and 
    os.path.exists(os.path.join(calc2.directory, 'OUTCAR'))):
    atoms1 = calc1.load_atoms()
    atoms2 = calc2.load_atoms()

    cellA = atoms1.get_cell()
    cellB = atoms2.get_cell()

    print('VASP-relaxed cell:')
    print(cellA)
    print(f'Volume: {atoms1.get_volume():.4f} ų\n')
    
    print('ASE-optimized cell:')
    print(cellB)
    print(f'Volume: {atoms2.get_volume():.4f} ų\n')

    match = (np.abs(cellA - cellB) < 0.01).all()
    print(f'Cells match within 0.01 Å: {match}')
    if not match:
        print('Note: If ASE optimization failed, the cell remains at initial geometry.')
else:
    print('One or both calculations not complete. Run previous cells first.')
VASP-relaxed cell:
Cell([4.18635928601072, 4.18635928601072, 4.18635928601072])
Volume: 73.3685 ų

ASE-optimized cell:
Cell([4.5, 4.5, 4.5])
Volume: 91.1250 ų

Cells match within 0.01 Å: False
Note: If ASE optimization failed, the cell remains at initial geometry.

This could be handy if you want to use any of the optimizers in mod:ase.optimize in conjunction with mod:ase.constraints, which are more advanced than what is in VASP.