Examples#

Below, you find the most common usage examples for tad-dftd4.

Single Molecule#

Calculate the DFT-D4 energy of a single molecule.

 1# SPDX-Identifier: CC0-1.0
 2import tad_mctc as mctc
 3import torch
 4
 5import tad_dftd4 as d4
 6
 7numbers = mctc.convert.symbol_to_number(
 8    symbols="C C C C N C S H H H H H".split()
 9)
10
11# coordinates in Bohr
12positions = torch.tensor(
13    [
14        [-2.56745685564671, -0.02509985979910, 0.00000000000000],
15        [-1.39177582455797, +2.27696188880014, 0.00000000000000],
16        [+1.27784995624894, +2.45107479759386, 0.00000000000000],
17        [+2.62801937615793, +0.25927727028120, 0.00000000000000],
18        [+1.41097033661123, -1.99890996077412, 0.00000000000000],
19        [-1.17186102298849, -2.34220576284180, 0.00000000000000],
20        [-2.39505990368378, -5.22635838332362, 0.00000000000000],
21        [+2.41961980455457, -3.62158019253045, 0.00000000000000],
22        [-2.51744374846065, +3.98181713686746, 0.00000000000000],
23        [+2.24269048384775, +4.24389473203647, 0.00000000000000],
24        [+4.66488984573956, +0.17907568006409, 0.00000000000000],
25        [-4.60044244782237, -0.17794734637413, 0.00000000000000],
26    ]
27)
28
29# total charge of the system
30charge = torch.tensor(0.0)
31
32# TPSSh-D4-ATM parameters
33param = d4.damping.Param(
34    s6=positions.new_tensor(1.0),
35    s8=positions.new_tensor(1.85897750),
36    s9=positions.new_tensor(1.0),
37    a1=positions.new_tensor(0.44286966),
38    a2=positions.new_tensor(4.60230534),
39)
40
41# parameters can also be obtained using the functional name:
42# param = d4.get_params(method="d4", functional="tpssh")
43
44energy1 = d4.dftd4(numbers, positions, charge, param)
45
46# class-based interface
47disp = d4.dispersion.DispD4()
48energy2 = disp.calculate(numbers, positions, charge, param)
49
50torch.set_printoptions(precision=10)
51
52ref = torch.tensor(
53    [
54        -0.0020841344,
55        -0.0018971195,
56        -0.0018107513,
57        -0.0018305695,
58        -0.0021737693,
59        -0.0019484236,
60        -0.0022788253,
61        -0.0004080658,
62        -0.0004261866,
63        -0.0004199839,
64        -0.0004280768,
65        -0.0005108935,
66    ]
67)
68assert torch.allclose(energy1, ref, atol=1e-8), "Energy does not match"
69assert torch.allclose(energy2, ref, atol=1e-8), "Energy does not match"
70
71
72print(energy1)
73# tensor([-0.0020841344, -0.0018971195, -0.0018107513, -0.0018305695,
74#         -0.0021737693, -0.0019484236, -0.0022788253, -0.0004080658,
75#         -0.0004261866, -0.0004199839, -0.0004280768, -0.0005108935])

Batched Calculations#

 1# SPDX-Identifier: CC0-1.0
 2import tad_mctc as mctc
 3import torch
 4
 5import tad_dftd4 as d4
 6
 7# S22 system 4: formamide dimer
 8numbers = mctc.batch.pack(
 9    (
10        mctc.convert.symbol_to_number("C C N N H H H H H H O O".split()),
11        mctc.convert.symbol_to_number("C O N H H H".split()),
12    )
13)
14
15# coordinates in Bohr
16positions = mctc.batch.pack(
17    (
18        torch.tensor(
19            [
20                [-3.81469488143921, +0.09993441402912, 0.00000000000000],
21                [+3.81469488143921, -0.09993441402912, 0.00000000000000],
22                [-2.66030049324036, -2.15898251533508, 0.00000000000000],
23                [+2.66030049324036, +2.15898251533508, 0.00000000000000],
24                [-0.73178529739380, -2.28237795829773, 0.00000000000000],
25                [-5.89039325714111, -0.02589114569128, 0.00000000000000],
26                [-3.71254944801331, -3.73605775833130, 0.00000000000000],
27                [+3.71254944801331, +3.73605775833130, 0.00000000000000],
28                [+0.73178529739380, +2.28237795829773, 0.00000000000000],
29                [+5.89039325714111, +0.02589114569128, 0.00000000000000],
30                [-2.74426102638245, +2.16115570068359, 0.00000000000000],
31                [+2.74426102638245, -2.16115570068359, 0.00000000000000],
32            ]
33        ),
34        torch.tensor(
35            [
36                [-0.55569743203406, +1.09030425468557, 0.00000000000000],
37                [+0.51473634678469, +3.15152550263611, 0.00000000000000],
38                [+0.59869690244446, -1.16861263789477, 0.00000000000000],
39                [-0.45355203669134, -2.74568780438064, 0.00000000000000],
40                [+2.52721209544999, -1.29200800956867, 0.00000000000000],
41                [-2.63139587595376, +0.96447869452240, 0.00000000000000],
42            ]
43        ),
44    )
45)
46
47# total charge of both system
48charge = torch.tensor([0.0, 0.0])
49
50# TPSSh-D4-ATM parameters
51param = d4.Param(
52    s6=positions.new_tensor(1.0),
53    s8=positions.new_tensor(1.85897750),
54    s9=positions.new_tensor(1.0),
55    a1=positions.new_tensor(0.44286966),
56    a2=positions.new_tensor(4.60230534),
57)
58
59# calculate dispersion energy in Hartree
60energy = torch.sum(d4.dftd4(numbers, positions, charge, param), -1)
61torch.set_printoptions(precision=10)
62print(energy)
63# tensor([-0.0088341432, -0.0027013607])
64print(energy[0] - 2 * energy[1])
65# tensor(-0.0034314217)

Gradient / Forces#

 1# SPDX-Identifier: CC0-1.0
 2import tad_mctc as mctc
 3import torch
 4
 5import tad_dftd4 as d4
 6
 7dd: mctc.typing.DD = {"device": torch.device("cpu"), "dtype": torch.float64}
 8
 9#####################
10# System Parameters #
11#####################
12
13numbers = mctc.convert.symbol_to_number(
14    symbols="C C C C N C S H H H H H".split()
15)
16
17# coordinates in Bohr
18positions = torch.tensor(
19    [
20        [-2.56745685564671, -0.02509985979910, 0.00000000000000],
21        [-1.39177582455797, +2.27696188880014, 0.00000000000000],
22        [+1.27784995624894, +2.45107479759386, 0.00000000000000],
23        [+2.62801937615793, +0.25927727028120, 0.00000000000000],
24        [+1.41097033661123, -1.99890996077412, 0.00000000000000],
25        [-1.17186102298849, -2.34220576284180, 0.00000000000000],
26        [-2.39505990368378, -5.22635838332362, 0.00000000000000],
27        [+2.41961980455457, -3.62158019253045, 0.00000000000000],
28        [-2.51744374846065, +3.98181713686746, 0.00000000000000],
29        [+2.24269048384775, +4.24389473203647, 0.00000000000000],
30        [+4.66488984573956, +0.17907568006409, 0.00000000000000],
31        [-4.60044244782237, -0.17794734637413, 0.00000000000000],
32    ],
33    **dd
34)
35
36# total charge of the system
37charge = torch.tensor(0.0)
38
39# TPSSh-D4-ATM parameters
40param = d4.get_params(method="d4", functional="tpssh")
41
42
43#######################
44# Analytical Gradient #
45#######################
46
47pos = positions.clone().requires_grad_(True)
48energy = d4.dftd4(numbers, pos, charge, param)
49
50(grad,) = torch.autograd.grad(energy.sum(), pos)
51
52
53######################
54# Numerical Gradient #
55######################
56
57num_grad = torch.zeros(positions.shape, **dd)
58step = 1e-5
59
60for i in range(numbers.shape[-1]):
61    for j in range(3):
62        positions[i, j] += step
63        e1 = d4.dftd4(numbers, positions, charge, param).sum()
64
65        positions[i, j] -= 2 * step
66        e2 = d4.dftd4(numbers, positions, charge, param).sum()
67
68        positions[i, j] += step
69        num_grad[i, j] = (e1 - e2) / (2 * step)
70
71
72# Check if analytical and numerical gradients match
73assert torch.allclose(grad, num_grad, atol=1e-8), "Gradient check failed!"

D4S Model#

This example shows how to use the D4SModel.

 1# SPDX-Identifier: CC0-1.0
 2import tad_mctc as mctc
 3import torch
 4
 5import tad_dftd4 as d4
 6
 7numbers = mctc.convert.symbol_to_number(
 8    symbols="C C C C N C S H H H H H".split()
 9)
10
11# coordinates in Bohr
12positions = torch.tensor(
13    [
14        [-2.56745685564671, -0.02509985979910, 0.00000000000000],
15        [-1.39177582455797, +2.27696188880014, 0.00000000000000],
16        [+1.27784995624894, +2.45107479759386, 0.00000000000000],
17        [+2.62801937615793, +0.25927727028120, 0.00000000000000],
18        [+1.41097033661123, -1.99890996077412, 0.00000000000000],
19        [-1.17186102298849, -2.34220576284180, 0.00000000000000],
20        [-2.39505990368378, -5.22635838332362, 0.00000000000000],
21        [+2.41961980455457, -3.62158019253045, 0.00000000000000],
22        [-2.51744374846065, +3.98181713686746, 0.00000000000000],
23        [+2.24269048384775, +4.24389473203647, 0.00000000000000],
24        [+4.66488984573956, +0.17907568006409, 0.00000000000000],
25        [-4.60044244782237, -0.17794734637413, 0.00000000000000],
26    ]
27)
28
29# total charge of the system
30charge = torch.tensor(0.0)
31
32# Create the D4S model
33model = d4.model.D4SModel(numbers)
34
35param = d4.get_params(method="d4", functional="tpssh")
36energy = d4.dftd4(numbers, positions, charge, param, model=model)
37torch.set_printoptions(precision=10)
38print(energy)
39# tensor([-0.0020843975, -0.0019013016, -0.0018165035, -0.0018363572,
40#         -0.0021877293, -0.0019495023, -0.0022923108, -0.0004326892,
41#         -0.0004439871, -0.0004362087, -0.0004454589, -0.0005344027])