-
Notifications
You must be signed in to change notification settings - Fork 25
Expand file tree
/
Copy pathcleaned.py
More file actions
executable file
·129 lines (101 loc) · 4.15 KB
/
Copy pathcleaned.py
File metadata and controls
executable file
·129 lines (101 loc) · 4.15 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
#!/usr/bin/env python3
# This file is part of dxtb.
#
# SPDX-Identifier: Apache-2.0
# Copyright (C) 2024 Grimme Group
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""
https://github.com/grimme-lab/dxtb/issues/183
"""
import torch
import dxtb
from dxtb.typing import DD
dd: DD = {"device": torch.device("cpu"), "dtype": torch.double}
numbers = torch.tensor([8, 1, 1], device=dd["device"])
positions = torch.tensor(
[
[-2.95915993, 1.40005084, 0.24966306],
[-2.1362031, 1.4795743, -1.38758999],
[-2.40235213, 2.84218589, 1.24419946],
],
requires_grad=True,
**dd,
)
opts = {
"scf_mode": dxtb.labels.SCF_MODE_FULL,
"cache_enabled": True,
}
def main() -> int:
calc = dxtb.Calculator(numbers, dxtb.GFN1_XTB, opts=opts, **dd)
assert calc.integrals.hcore is not None
def get_energy_force(calc: dxtb.Calculator):
# Using get_force() instead messes with the autograd graph
# forces = calc.get_forces(positions, create_graph=True)
energy = calc.get_energy(positions)
forces = -torch.autograd.grad(energy, positions, create_graph=True)[0]
return energy, forces
es2 = calc.interactions.get_interaction("ES2")
es2.gexp = es2.gexp.clone().detach().requires_grad_(True)
hcore = calc.integrals.hcore
hcore.selfenergy = hcore.selfenergy.clone().detach().requires_grad_(True)
# AD gradient w.r.t. params
energy, force = get_energy_force(calc)
de_dparam = torch.autograd.grad(
energy, (es2.gexp, hcore.selfenergy), retain_graph=True
)
calc = dxtb.Calculator(numbers, dxtb.GFN1_XTB, opts=opts, **dd)
es2 = calc.interactions.get_interaction("ES2")
es2.gexp = es2.gexp.clone().detach().requires_grad_(True)
hcore = calc.integrals.hcore
assert hcore is not None
hcore.selfenergy = hcore.selfenergy.clone().detach().requires_grad_(True)
pos = positions.clone().detach().requires_grad_(True)
energy = calc.get_energy(pos)
force = -torch.autograd.grad(energy, pos, create_graph=True)[0]
dfnorm_dparam = torch.autograd.grad(
torch.norm(force), (es2.gexp, hcore.selfenergy)
)
# Numerical gradient w.r.t. params
dparam = 2e-6
calc = dxtb.Calculator(numbers, dxtb.GFN1_XTB, **dd)
es2 = calc.interactions.get_interaction("ES2")
es2.gexp += dparam / 2
energy1, force1 = get_energy_force(calc)
calc = dxtb.Calculator(numbers, dxtb.GFN1_XTB, **dd)
es2 = calc.interactions.get_interaction("ES2")
es2.gexp -= dparam / 2
energy2, force2 = get_energy_force(calc)
de_dgexp = (energy1 - energy2) / dparam
print(f"dE / dgexp (AD) = {de_dparam[0]: .8f}")
print(f"dE / dgexp (Num) = {de_dgexp: .8f}")
dF_dgexp = (torch.norm(force1) - torch.norm(force2)) / dparam
print(f"d|F| / dgexp (AD) = {dfnorm_dparam[0]: .8f}")
print(f"d|F| / dgexp (Num) = {dF_dgexp: .8f}")
calc = dxtb.Calculator(numbers, dxtb.GFN1_XTB, opts=opts, **dd)
assert calc.integrals.hcore is not None
calc.integrals.hcore.selfenergy[0] += dparam / 2
energy1, force1 = get_energy_force(calc)
calc = dxtb.Calculator(numbers, dxtb.GFN1_XTB, opts=opts, **dd)
assert calc.integrals.hcore is not None
calc.integrals.hcore.selfenergy[0] -= dparam / 2
energy2, force2 = get_energy_force(calc)
de_dp = (energy1 - energy2) / dparam
print(f"dE / dselfenergy[0] (AD) = {de_dparam[1][0]: .8f}")
print(f"dE / dselfenergy[0] (Num) = {de_dp: .8f}")
df_dp = (torch.norm(force1) - torch.norm(force2)) / dparam
print(f"d|F| / dselfenergy[0] (AD) = {dfnorm_dparam[1][0]: .8f}")
print(f"d|F| / dselfenergy[0] (Num) = {df_dp: .8f}")
return 0
if __name__ == "__main__":
raise SystemExit(main())