Skip to content

Commit b48fdf9

Browse files
committed
Add support for PySCF
1 parent 7eae7fb commit b48fdf9

File tree

3 files changed

+195
-0
lines changed

3 files changed

+195
-0
lines changed

python/dftd4/meson.build

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,11 +63,13 @@ if install
6363
'interface.py',
6464
'library.py',
6565
'parameters.py',
66+
'pyscf.py',
6667
'qcschema.py',
6768
'test_ase.py',
6869
'test_interface.py',
6970
'test_library.py',
7071
'test_parameters.py',
72+
'test_pyscf.py',
7173
'test_qcschema.py',
7274
'..'/'..'/'assets'/'parameters.toml',
7375
),

python/dftd4/pyscf.py

Lines changed: 178 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,178 @@
1+
# This file is part of dftd4.
2+
# SPDX-Identifier: LGPL-3.0-or-later
3+
#
4+
# dftd4 is free software: you can redistribute it and/or modify it under
5+
# the terms of the Lesser GNU General Public License as published by
6+
# the Free Software Foundation, either version 3 of the License, or
7+
# (at your option) any later version.
8+
#
9+
# dftd4 is distributed in the hope that it will be useful,
10+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
11+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12+
# Lesser GNU General Public License for more details.
13+
#
14+
# You should have received a copy of the Lesser GNU General Public License
15+
# along with dftd4. If not, see <https://www.gnu.org/licenses/>.
16+
"""
17+
Compatibility layer for supporting DFT-D3 in `pyscf <https://pyscf.org/>`_.
18+
19+
Example
20+
-------
21+
>>> from pyscf import gto, scf
22+
>>> from pyscf import scf
23+
>>> import dftd4.pyscf as disp
24+
>>> mol = gto.Mole()
25+
>>> mol.atom = ''' O 0.00000000 0.00000000 -0.11081188
26+
... H -0.00000000 -0.84695236 0.59109389
27+
... H -0.00000000 0.89830571 0.52404783 '''
28+
>>> mol.basis = 'cc-pvdz'
29+
>>> _ = mol.build()
30+
>>> mf = disp.dftd4(scf.RHF(mol))
31+
>>> print(mf.kernel())
32+
-75.99396273778923
33+
"""
34+
35+
import numpy as np
36+
37+
try:
38+
from pyscf import lib, gto
39+
except ModuleNotFoundError:
40+
raise ModuleNotFoundError("This submodule requires pyscf installed")
41+
42+
from .interface import DispersionModel, DampingParam
43+
44+
45+
def dftd4(mf):
46+
"""Apply DFT-D3 corrections to SCF or MCSCF methods"""
47+
48+
from pyscf.scf import hf
49+
from pyscf.mcscf import casci
50+
51+
if not isinstance(mf, (hf.SCF, casci.CASCI)):
52+
raise TypeError("mf must be an instance of SCF or CASCI")
53+
54+
with_dftd4 = DFTD4Dispersion(
55+
mf.mol,
56+
xc="hf"
57+
if isinstance(mf, casci.CASCI)
58+
else getattr(mf, "xc", "HF").upper().replace(" ", ""),
59+
)
60+
61+
if isinstance(mf, _DFTD4):
62+
mf.with_dftd4 = with_dftd4
63+
return mf
64+
65+
class DFTD4(_DFTD4, mf.__class__):
66+
def __init__(self, method, with_dftd4):
67+
self.__dict__.update(method.__dict__)
68+
self.with_dftd4 = with_dftd4
69+
self._keys.update(["with_dftd4"])
70+
71+
def dump_flags(self, verbose=None):
72+
mf.__class__.dump_flags(self, verbose)
73+
if self.with_dftd4:
74+
self.with_dftd4.dump_flags(verbose)
75+
return self
76+
77+
def energy_nuc(self):
78+
enuc = mf.__class__.energy_nuc(self)
79+
if self.with_dftd4:
80+
enuc += self.with_dftd4.kernel()[0]
81+
return enuc
82+
83+
def reset(self, mol=None):
84+
self.with_dftd4.reset(mol)
85+
return mf.__class__.reset(self, mol)
86+
87+
def nuc_grad_method(self):
88+
scf_grad = mf.__class__.nuc_grad_method(self)
89+
return grad(scf_grad)
90+
91+
Gradients = lib.alias(nuc_grad_method, alias_name="Gradients")
92+
93+
return DFTD4(mf, with_dftd4)
94+
95+
96+
def grad(scf_grad):
97+
"""
98+
Apply DFT-D3 corrections to SCF or MCSCF nuclear gradients methods
99+
"""
100+
from pyscf.grad import rhf as rhf_grad
101+
102+
if not isinstance(scf_grad, rhf_grad.Gradients):
103+
raise TypeError("scf_grad must be an instance of Gradients")
104+
105+
# Ensure that the zeroth order results include DFTD4 corrections
106+
if not getattr(scf_grad.base, "with_dftd4", None):
107+
scf_grad.base = dftd4(scf_grad.base)
108+
109+
class DFTD4Grad(_DFTD4Grad, scf_grad.__class__):
110+
def grad_nuc(self, mol=None, atmlst=None):
111+
nuc_g = scf_grad.__class__.grad_nuc(self, mol, atmlst)
112+
with_dftd4 = getattr(self.base, "with_dftd4", None)
113+
if with_dftd4:
114+
disp_g = with_dftd4.kernel()[1]
115+
if atmlst is not None:
116+
disp_g = disp_g[atmlst]
117+
nuc_g += disp_g
118+
return nuc_g
119+
120+
mfgrad = DFTD4Grad.__new__(DFTD4Grad)
121+
mfgrad.__dict__.update(scf_grad.__dict__)
122+
return mfgrad
123+
124+
125+
class DFTD4Dispersion(lib.StreamObject):
126+
def __init__(self, mol, xc="hf", atm=True):
127+
self.mol = mol
128+
self.verbose = mol.verbose
129+
self.xc = xc
130+
self.atm = atm
131+
self.edisp = None
132+
self.grads = None
133+
134+
def dump_flags(self, verbose=None):
135+
lib.logger.info(self, "** DFTD4 parameter **")
136+
lib.logger.info(self, "func %s", self.xc)
137+
return self
138+
139+
def kernel(self):
140+
mol = self.mol
141+
142+
disp = DispersionModel(
143+
mol.atom_charges(),
144+
mol.atom_coords(),
145+
mol.charge,
146+
)
147+
148+
param = DampingParam(
149+
method=self.xc,
150+
atm=self.atm,
151+
)
152+
153+
res = disp.get_dispersion(param=param, grad=True)
154+
155+
self.edisp = res.get("energy")
156+
self.grads = res.get("gradient")
157+
return self.edisp, self.grads
158+
159+
def reset(self, mol):
160+
"""Reset mol and clean up relevant attributes for scanner mode"""
161+
self.mol = mol
162+
return self
163+
164+
165+
class _DFTD4:
166+
"""
167+
Stub class used to identify instances of the `DFTD4` class
168+
"""
169+
170+
pass
171+
172+
173+
class _DFTD4Grad:
174+
"""
175+
Stub class used to identify instances of the `DFTD4Grad` class
176+
"""
177+
178+
pass

python/dftd4/test_pyscf.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
# This file is part of dftd4.
2+
# SPDX-Identifier: LGPL-3.0-or-later
3+
#
4+
# dftd4 is free software: you can redistribute it and/or modify it under
5+
# the terms of the Lesser GNU General Public License as published by
6+
# the Free Software Foundation, either version 3 of the License, or
7+
# (at your option) any later version.
8+
#
9+
# dftd4 is distributed in the hope that it will be useful,
10+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
11+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12+
# Lesser GNU General Public License for more details.
13+
#
14+
# You should have received a copy of the Lesser GNU General Public License
15+
# along with dftd4. If not, see <https://www.gnu.org/licenses/>.

0 commit comments

Comments
 (0)