Torch Autodiff Multicharge#
PyTorch implementation of the electronegativity equilibration (EEQ) model for atomic partial charges. This module allows to process a single structure or a batch of structures for the calculation of atom-resolved dispersion energies.
For details on the EEQ model, see
S. A. Ghasemi, A. Hofstetter, S. Saha, and S. Goedecker, Phys. Rev. B, 2015, 92, 045131. DOI: 10.1103/PhysRevB.92.045131
E. Caldeweyher, S. Ehlert, A. Hansen, H. Neugebauer, S. Spicher, C. Bannwarth and S. Grimme, J. Chem. Phys., 2019, 150, 154122. DOI: 10.1063/1.5090222
For alternative implementations, also check out
- multicharge:
Implementation of the EEQ model in Fortran.
Examples#
The following example shows how to calculate the EEQ partial charges and the corresponding electrostatic energy for a single structure.
import torch
from tad_multicharge import eeq
numbers = torch.tensor([7, 7, 1, 1, 1, 1, 1, 1])
# coordinates in Bohr
positions = torch.tensor(
[
[-2.98334550857544, -0.08808205276728, +0.00000000000000],
[+2.98334550857544, +0.08808205276728, +0.00000000000000],
[-4.07920360565186, +0.25775116682053, +1.52985656261444],
[-1.60526800155640, +1.24380481243134, +0.00000000000000],
[-4.07920360565186, +0.25775116682053, -1.52985656261444],
[+4.07920360565186, -0.25775116682053, -1.52985656261444],
[+1.60526800155640, -1.24380481243134, +0.00000000000000],
[+4.07920360565186, -0.25775116682053, +1.52985656261444],
]
)
total_charge = torch.tensor(0.0)
cn = torch.tensor([3.0, 3.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0])
eeq_model = eeq.EEQModel.param2019()
energy, qat = eeq_model.solve(numbers, positions, total_charge, cn)
print(torch.sum(energy, -1))
# tensor(-0.1750)
print(qat)
# tensor([-0.8347, -0.8347, 0.2731, 0.2886, 0.2731, 0.2731, 0.2886, 0.2731])
The next example shows the calculation of the electrostatic energy with a simpler API for a batch of structures.
import torch
from tad_multicharge import eeq
from tad_mctc.batch import pack
from tad_mctc.convert import symbol_to_number
# S22 system 4: formamide dimer
numbers = pack(
(
symbol_to_number("C C N N H H H H H H O O".split()),
symbol_to_number("C O N H H H".split()),
)
)
# coordinates in Bohr
positions = pack(
(
torch.tensor(
[
[-3.81469488143921, +0.09993441402912, 0.00000000000000],
[+3.81469488143921, -0.09993441402912, 0.00000000000000],
[-2.66030049324036, -2.15898251533508, 0.00000000000000],
[+2.66030049324036, +2.15898251533508, 0.00000000000000],
[-0.73178529739380, -2.28237795829773, 0.00000000000000],
[-5.89039325714111, -0.02589114569128, 0.00000000000000],
[-3.71254944801331, -3.73605775833130, 0.00000000000000],
[+3.71254944801331, +3.73605775833130, 0.00000000000000],
[+0.73178529739380, +2.28237795829773, 0.00000000000000],
[+5.89039325714111, +0.02589114569128, 0.00000000000000],
[-2.74426102638245, +2.16115570068359, 0.00000000000000],
[+2.74426102638245, -2.16115570068359, 0.00000000000000],
]
),
torch.tensor(
[
[-0.55569743203406, +1.09030425468557, 0.00000000000000],
[+0.51473634678469, +3.15152550263611, 0.00000000000000],
[+0.59869690244446, -1.16861263789477, 0.00000000000000],
[-0.45355203669134, -2.74568780438064, 0.00000000000000],
[+2.52721209544999, -1.29200800956867, 0.00000000000000],
[-2.63139587595376, +0.96447869452240, 0.00000000000000],
]
),
)
)
# total charge of both system
charge = torch.tensor([0.0, 0.0])
# calculate electrostatic energy in Hartree
energy = torch.sum(eeq.get_energy(numbers, positions, charge), -1)
torch.set_printoptions(precision=10)
print(energy)
# tensor([-0.2086755037, -0.0972094536])
print(energy[0] - 2 * energy[1])
# tensor(-0.0142565966)