Calculating force

Calculating force in DQC is straightforward as shown by the code below

import torch
import dqc
atomzs = torch.tensor([1, 1])
atomposs = torch.tensor([[1, 0, 0], [-1, 0, 0]], dtype=torch.double).requires_grad_()
mol = dqc.Mol(moldesc=(atomzs, atomposs), basis="3-21G")
qc = dqc.HF(mol).run()
ene = qc.energy()  # calculate the energy
force = -torch.autograd.grad(ene, atomposs)[0]  # calculate the force
print(force)
The 3-21G basis for atomz 1 does not exist, but we will download it
Downloaded to /home/docs/checkouts/readthedocs.org/user_builds/dqc/envs/latest/lib/python3.8/site-packages/dqc/api/.database/3-21g/01.gaussian94
tensor([[-0.1033, -0.0000, -0.0000],
        [ 0.1033, -0.0000, -0.0000]], dtype=torch.float64)

Let’s take a look at each parts. First, we set up the atomic numbers and positions with lines:

atomzs = torch.tensor([1, 1])
atomposs = torch.tensor([[1, 0, 0], [-1, 0, 0]], dtype=torch.double).requires_grad_()

The system has two atoms with atomic number 1 and 1 (i.e. hydrogen molecule) as set by atomzs variable. In the next line, atomposs describes the position of each atom, i.e. the first one is at (1, 0, 0) and the second one is at (-1, 0, 0). Please note that atomposs is marked as differentiable by .requires_grad_() command. This is required as we want to differentiate the energy later with respect to the atomic positions.

Next, we construct the DQC system by:

mol = dqc.Mol((atomzs, atomposs), basis="3-21G")

The first argument of Mol is molecular description which can accept a tuple of (atomzs, atomposs) or a string description (explained later). The basis keyword choose the basis for each atom. In this case, it uses "3-21G" basis set.

Once the DQC system is constructed, then we can run the calculation by

qc = dqc.HF(mol).run()
ene = qc.energy()  # calculate the energy

The first line above runs the simulation until it reaches convergence. Then, the next line calculates the energy. The output energy is differentiable with respect to floating point tensors in the system that are set to be differentiable. Therefore, the force can be simply calculated by

force = -torch.autograd.grad(ene, mol.atompos)[0]  # calculate the force

How if we have molecule description in string, e.g. "H -1 0 0; H 1 0 0"? In this case, we need a help from parse_moldesc(),

import torch
import dqc
atomzs, atomposs = dqc.parse_moldesc("H -1 0 0; H 1 0 0")
atomposs = atomposs.requires_grad_()  # marking atomposs as differentiable
mol = dqc.Mol(moldesc=(atomzs, atomposs), basis="3-21G")
qc = dqc.HF(mol).run()
ene = qc.energy()  # calculate the energy
force = -torch.autograd.grad(ene, atomposs)[0]  # calculate the force
print(force)
tensor([[ 0.1033, -0.0000, -0.0000],
        [-0.1033, -0.0000, -0.0000]], dtype=torch.float64)

The only difference in this case is the lines

atomzs, atomposs = dqc.parse_moldesc("H -1 0 0; H 1 0 0")
atomposs = atomposs.requires_grad_()  # marking atomposs as differentiable

where parse_moldesc() parses the string and returns two tensors describing the atomic numbers and atomic positions. The rest are just the same as the previous case.