Skip to content
17 changes: 12 additions & 5 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,17 @@ jobs:
python-version: ${{ matrix.python-version }}
- name: Install
run: |
sudo apt-get install -y libopenblas-dev
pip install .
pip install pytest
sudo add-apt-repository ppa:ubuntu-toolchain-r/test
sudo apt-get update
sudo apt install gcc-10 g++-10 gfortran-10
sudo update-alternatives \
--install /usr/bin/gcc gcc /usr/bin/gcc-10 100 \
--slave /usr/bin/gfortran gfortran /usr/bin/gfortran-10 \
--slave /usr/bin/gcov gcov /usr/bin/gcov-10
sudo apt-get install -y libopenblas-dev
pip install .
pip install pytest
- name: Test
run: |
export PYSCF_EXT_PATH=$(pwd):$PYSCF_EXT_PATH
pytest -s pyscf/dispersion
export PYSCF_EXT_PATH=$(pwd):$PYSCF_EXT_PATH
pytest -s pyscf/dispersion
6 changes: 5 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -62,8 +62,12 @@ Makefile
cmake_install.cmake
pyscf/lib/deps
pyscf/lib/config.h

pyscf/lib/cmake/
pyscf/lib/pkgconfig/
pyscf/lib/libdftd4.*
pyscf/lib/libs-dftd3.*
settings.py
*.cmake

# Memoization and caching
tmp/
Expand Down
53 changes: 34 additions & 19 deletions pyscf/dispersion/dftd4.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,9 @@ class _d4_restype(ctypes.Structure):
libdftd4.dftd4_new_error.restype = _d4_p
libdftd4.dftd4_new_structure.restype = _d4_p
libdftd4.dftd4_new_d4_model.restype = _d4_p
libdftd4.dftd4_new_d4s_model.restype = _d4_p
libdftd4.dftd4_custom_d4_model.restype = _d4_p
libdftd4.dftd4_custom_d4s_model.restype = _d4_p
libdftd4.dftd4_load_rational_damping.restype = _d4_p
libdftd4.dftd4_new_rational_damping.restype = _d4_p

Expand All @@ -40,18 +42,22 @@ def error_check(err):
raise RuntimeError(message.value.decode())

class DFTD4Dispersion(lib.StreamObject):
def __init__(self, mol, xc, ga=None, gc=None, wf=None, atm=False):
def __init__(self, mol, xc, version='d4', ga=None, gc=None, wf=None, atm=False):
xc_lc = xc.lower().encode()
self._disp = None
self._mol = None
self._param = None

log = lib.logger.new_logger(mol)
# https://github.com/dftd4/dftd4/pull/276
if xc_lc == 'wb97x':
log.warn('The previous wb97x is renamed as wb97x-2008. Now D4 dispersion for wb97x is the replacement of vv10 in wb97x-v. See https://github.com/dftd4/dftd4/blob/main/README.md')
coords = np.asarray(mol.atom_coords(), dtype=np.double, order='C')
charge = np.array([mol.charge], dtype=np.double)
nuc_types = [gto.charge(mol.atom_symbol(ia))
for ia in range(mol.natm)]
nuc_types = np.asarray(nuc_types, dtype=np.int32)

self.natm = mol.natm
if isinstance(mol, gto.Mole):
lattice = lib.c_null_ptr()
Expand All @@ -71,20 +77,29 @@ def __init__(self, mol, xc, ga=None, gc=None, wf=None, atm=False):
lattice, periodic,
)
error_check(err)
if ga is None and gc is None and wf is None:
self._disp = libdftd4.dftd4_new_d4_model(err, self._mol)
if version.lower() == 'd4':
if ga is None and gc is None and wf is None:
self._disp = libdftd4.dftd4_new_d4_model(err, self._mol)
else:
# Default from DFTD4 repo, https://github.com/dftd4/dftd4/blob/main/python/dftd4/interface.py#L290
if ga is None: ga = 3.0
if gc is None: gc = 2.0
if wf is None: wf = 6.0
self._disp = libdftd4.dftd4_custom_d4_model(err, self._mol,
ctypes.c_double(ga),
ctypes.c_double(gc),
ctypes.c_double(wf))
elif version.lower() == 'd4s':
if ga is None and gc is None:
self._disp = libdftd4.dftd4_new_d4s_model(err, self._mol)
else:
if ga is None: ga = 3.0
if gc is None: gc = 2.0
self._disp = libdftd4.dftd4_custom_d4s_model(err, self._mol,
ctypes.c_double(ga),
ctypes.c_double(gc))
else:
# Default from DFTD4 repo, https://github.com/dftd4/dftd4/blob/main/python/dftd4/interface.py#L290
if ga is None:
ga = 3.0
if gc is None:
gc = 2.0
if wf is None:
wf = 6.0
self._disp = libdftd4.dftd4_custom_d4_model(err, self._mol,
ctypes.c_double(ga),
ctypes.c_double(gc),
ctypes.c_double(wf))
raise ValueError('version must be d4 or d4s')
error_check(err)
self._param = libdftd4.dftd4_load_rational_damping(
err,
Expand All @@ -107,10 +122,10 @@ def __del__(self):
def set_param(self, s8, a1, a2, s6=1.0, s9=1.0, alp=16.0):
self._param = libdftd4.dftd4_new_rational_damping(
ctypes.c_double(s6),
ctypes.c_double(s8),
ctypes.c_double(s9),
ctypes.c_double(a1),
ctypes.c_double(a2),
ctypes.c_double(s8),
ctypes.c_double(s9),
ctypes.c_double(a1),
ctypes.c_double(a2),
ctypes.c_double(alp))
return

Expand Down
86 changes: 84 additions & 2 deletions pyscf/dispersion/tests/test_d4.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,10 +25,16 @@ def test_d4_unknown_xc():

def test_d4_energy():
mol = pyscf.M(atom='H 0 0 0; H 0 0 1')
model = DFTD4Dispersion(mol, xc='WB97X')
model = DFTD4Dispersion(mol, xc='WB97X-2008')
out = model.get_dispersion()
assert abs(out['energy'] - -2.21334459527e-05) < 1e-10

def test_wb97x_d4_energy():
mol = pyscf.M(atom='H 0 0 0; H 0 0 1')
model = DFTD4Dispersion(mol, xc='WB97X')
out = model.get_dispersion()
assert abs(out['energy'] - -0.00027002) < 1e-8

def test_d4_gradients():
mol = pyscf.M(atom='H 0 0 0; H 0 0 1')
model = DFTD4Dispersion(mol, xc='HF')
Expand All @@ -39,6 +45,82 @@ def test_d4_gradients():

def test_d4_with_pbc():
mol = pyscf.M(atom='H 0 0 0; H 0 0 1', a=np.eye(3)*2)
model = DFTD4Dispersion(mol, xc='WB97X')
model = DFTD4Dispersion(mol, xc='WB97X-2008')
out = model.get_dispersion()
assert abs(out['energy'] - -0.002715970438476524) < 1e-10

def test_d4s_energy():
''' Test copied from DFTD4
'''
mol = pyscf.M(
atom="""
Na -1.855282634 3.586705153 -2.417637293
H 4.401780235 0.023388444 -4.954577493
O -2.987060334 4.762520654 1.270433015
H 0.799808860 1.411034556 -5.046553216
F -4.206474694 1.842757675 4.550380848
H -3.543561218 -3.188356651 1.462400217
H 2.700321601 1.068184525 -1.732346503
O 3.731140888 -2.070015433 2.231609376
N -1.753068192 0.359514171 1.053234061
H 5.417557885 -1.578818300 1.753940027
H -2.234628682 -2.138565050 4.109222857
Cl 1.015658662 -3.219521545 -3.360509630
B 2.421192557 0.266264350 -3.918624743
B -3.025260988 2.536678890 2.316649847
N -2.004389486 -2.292351369 2.197828073
Al 1.122265541 -1.369420070 0.484550554
"""
)
model = DFTD4Dispersion(mol, xc="TPSS", version="d4s")
out = model.get_dispersion()
assert abs(out['energy'] - -0.016049411775539424) < 1.0e-7

def test_d4s_gradient():
''' Test copied from DFTD4
'''
mol = pyscf.M(
atom="""
H -1.795376258 -3.778664229 -1.078835583
S -2.682788333 0.388926662 1.662148652
B 0.114846497 1.488579332 3.656603965
O -1.079988795 -0.162591216 -4.557030658
Mg 0.603028329 4.088161496 -0.025893731
H -1.225340893 -1.799813824 -3.707731733
H -1.334609820 -4.248190824 2.727919027
H -0.162780825 2.412679941 5.690306951
Si 2.878024440 -0.331205250 1.883113735
H 0.684893279 0.327902040 -4.205476937
B -1.209197735 -2.872537625 0.940642042
Li -3.255726045 2.212410929 -2.867155493
F -1.831474682 5.205272937 -2.269762706
H 4.908858657 -1.925765619 2.990699194
H 1.268062422 -2.604093417 0.551628052
S 4.119569763 1.598928667 -1.391174777
""",
spin=1
)
ref = np.array(
[
[-1.04361222e-04, -1.65054791e-04, -1.36662175e-04],
[-1.41500522e-03, +1.89282651e-04, +2.16639105e-04],
[-1.18067839e-04, +4.50543787e-04, +1.50087553e-03],
[+3.37690080e-04, -4.10348598e-04, -3.02311767e-04],
[+4.39892308e-04, +1.54862493e-03, +1.33655085e-04],
[+1.31259180e-06, -7.51721105e-05, -1.39848135e-04],
[-4.61111364e-05, -1.65382677e-04, +1.81820530e-04],
[-1.94292825e-05, +7.21791149e-05, +1.79879351e-04],
[+1.14226323e-03, -6.08455689e-04, +6.24007890e-04],
[+6.95738570e-05, -1.86718359e-05, -1.25837081e-04],
[-1.66091884e-04, -1.03519307e-03, -1.71797180e-04],
[-1.29925668e-03, +6.18658801e-05, -6.30138324e-04],
[-1.58991399e-04, +5.73306273e-04, -2.35799582e-04],
[+2.90056077e-04, -2.14985916e-04, +1.62430848e-04],
[+6.43808246e-05, -3.35585457e-04, -2.45131168e-04],
[+9.82145702e-04, +1.33047503e-04, -1.01178292e-03],
]
)

model = DFTD4Dispersion(mol, xc="BLYP", version="d4s")
out = model.get_dispersion(grad=True)
assert np.linalg.norm(out['gradient'] - ref) < 1.0e-7
8 changes: 6 additions & 2 deletions pyscf/lib/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@
cmake_minimum_required (VERSION 3.5)
project (pyscf-dispersion)

set(CMAKE_Fortran_FLAGS "${CMAKE_Fortran_FLAGS} -cpp -ffree-line-length-none -Wall")

include(ExternalProject)
# statically compile deps mctc mstore
ExternalProject_Add(dftd3_static
Expand Down Expand Up @@ -46,23 +48,25 @@ add_dependencies(dftd3 dftd3_static)
# statically compile deps mctc mstore
ExternalProject_Add(dftd4_static
GIT_REPOSITORY "https://github.com/dftd4/dftd4"
GIT_TAG v3.7.0
GIT_TAG cf1af36a0f89fdaa2faa15def912148db9f7cb07
GIT_SHALLOW FALSE
GIT_PROGRESS TRUE
PREFIX ${PROJECT_BINARY_DIR}/deps
INSTALL_DIR ${PROJECT_SOURCE_DIR}/deps
CMAKE_ARGS -DWITH_OpenMP=OFF
-DCMAKE_Fortran_FLAGS=-cpp\ -ffree-line-length-none\ -Wall
-DCMAKE_INSTALL_PREFIX:PATH=<INSTALL_DIR>
-DCMAKE_INSTALL_LIBDIR:PATH=lib
)
ExternalProject_Add(dftd4
GIT_REPOSITORY "https://github.com/dftd4/dftd4"
GIT_TAG v3.7.0
GIT_TAG cf1af36a0f89fdaa2faa15def912148db9f7cb07
GIT_SHALLOW FALSE
GIT_PROGRESS TRUE
PREFIX ${PROJECT_BINARY_DIR}/deps
INSTALL_DIR ${PROJECT_SOURCE_DIR}/deps
CMAKE_ARGS -DWITH_OpenMP=OFF -DBUILD_SHARED_LIBS=ON
-DCMAKE_Fortran_FLAGS=-cpp\ -ffree-line-length-none\ -Wall
-DCMAKE_INSTALL_PREFIX:PATH=<INSTALL_DIR>
-DCMAKE_INSTALL_LIBDIR:PATH=${PROJECT_SOURCE_DIR}
)
Expand Down