van der Waals correction

A correction on top of the PBE functional has been proposed by Tkatchenko and Scheffler [1]. While nearly all parameters are obtained from ab-initio calculations, the method requires nearly no additional computational cost and performs very well:

.

PBE

TPSS

vdW-DF

vdW-DF2

TS09

Grimme D4

Mean absolute deviation

115

154

76

48

16

14

RMS deviation

108

128

60

42

21

14

Error in energies compared to CCSD results of the S26 test set. All values in meV. GPAW calculations were done with h=0.18 and at least 4 A vacuum. The TS09 results are in good agreement to the results obtained with the FHI-aims code [2]. Grimme D4 is available at github.

Calculating the S26 test set

As an example of the usage, here the S26 (S22 plus 4 other pairs) test set is calculated:

import sys
from ase import Atoms
from ase.parallel import paropen
from ase.data.s22 import data
from ase.calculators.vdwcorrection import vdWTkatchenko09prl
from gpaw import GPAW, FermiDirac
from gpaw.utilities.adjust_cell import adjust_cell
from gpaw.analyse.hirshfeld import HirshfeldPartitioning
from gpaw.analyse.vdwradii import vdWradii
try:
    from dftd4 import D4_model
except ModuleNotFoundError:
    pass

h = 0.18
box = 4.

xc = 'TS09'
if len(sys.argv) > 1:
    xc = sys.argv[1]

f = paropen('energies_' + xc + '.dat', 'w')
print('# h=', h, file=f)
print('# box=', box, file=f)
print('# molecule E[1]  E[2]  E[1+2]  E[1]+E[2]-E[1+2]', file=f)
for molecule in data:
    print(molecule, end=' ', file=f)
    ss = Atoms(data[molecule]['symbols'],
               data[molecule]['positions'])
    # split the structures
    s1 = ss.find_connected(0)
    s2 = ss.find_connected(-1)
    assert len(ss) == len(s1) + len(s2)
    if xc == 'TS09' or xc == 'TPSS' or xc == 'M06-L' or xc == 'dftd4':
        c = GPAW(mode='fd', xc='PBE', h=h, nbands=-6,
                 occupations=FermiDirac(width=0.1))
    else:
        c = GPAW(mode='fd', xc=xc, h=h, nbands=-6,
                 occupations=FermiDirac(width=0.1))
    E = []
    for s in [s1, s2, ss]:
        s.calc = c
        adjust_cell(s, box, h=h)
        if xc == 'TS09':
            s.get_potential_energy()
            cc = vdWTkatchenko09prl(HirshfeldPartitioning(c),
                                    vdWradii(s.get_chemical_symbols(), 'PBE'))
            s.calc = cc
        elif xc == 'dftd4':
            s.get_potential_energy()
            cc = D4_model(xc='PBE', calc=c)
            s.calc = cc
        if xc == 'TPSS' or xc == 'M06-L':
            ene = s.get_potential_energy()
            ene += c.get_xc_difference(xc)
            E.append(ene)
        else:
            E.append(s.get_potential_energy())
    print(E[0], E[1], E[2], end=' ', file=f)
    print(E[0] + E[1] - E[2], file=f)
    f.flush()
f.close()