Coverage for gpaw/test/solvation/test_forces.py: 68%
77 statements
« prev ^ index » next coverage.py v7.7.1, created at 2025-07-09 00:21 +0000
« prev ^ index » next coverage.py v7.7.1, created at 2025-07-09 00:21 +0000
1# flake8: noqa
2import pytest
3from ase import Atoms
4from ase.units import Pascal, m
5from ase.data.vdw import vdw_radii
6from gpaw.mpi import rank
7from gpaw import Mixer
8from gpaw.solvation import (
9 SolvationGPAW,
10 EffectivePotentialCavity,
11 Power12Potential,
12 LinearDielectric,
13 KB51Volume,
14 GradientSurface,
15 VolumeInteraction,
16 SurfaceInteraction,
17 LeakedDensityInteraction)
19import numpy as np
21SKIP_ENERGY_CALCULATION = True
22F_max_err = 0.005
24h = 0.2
25u0 = 0.180
26epsinf = 80.
27T = 298.15
30@pytest.mark.slow
31def test_solvation_forces():
32 atoms = Atoms('NaCl', positions=((5.6, 5.6, 6.8), (5.6, 5.6, 8.8)))
33 atoms.set_cell((11.2, 11.2, 14.4))
35 atoms.calc = SolvationGPAW(
36 mode='fd',
37 mixer=Mixer(0.5, 7, 50.0),
38 eigensolver='dav',
39 xc='oldPBE', h=h, setups={'Na': '1'},
40 cavity=EffectivePotentialCavity(
41 effective_potential=Power12Potential(u0=u0),
42 temperature=T,
43 volume_calculator=KB51Volume(),
44 surface_calculator=GradientSurface()),
45 dielectric=LinearDielectric(epsinf=epsinf),
46 # parameters chosen to give ~ 1eV for each interaction
47 interactions=[
48 VolumeInteraction(pressure=-1e9 * Pascal),
49 SurfaceInteraction(surface_tension=100. * 1e-3 * Pascal * m),
50 LeakedDensityInteraction(voltage=10.)])
52 def vac(atoms):
53 return min(
54 atoms.positions[0][2],
55 14.4 - atoms.positions[1][2])
57 step = 0.05
58 if not SKIP_ENERGY_CALCULATION:
59 d = []
60 E = []
61 F = []
62 while vac(atoms) >= 5.6:
63 d.append(abs(atoms.positions[0][2] - atoms.positions[1][2]))
64 E.append(atoms.calc.get_potential_energy(atoms, force_consistent=True))
65 F.append(atoms.calc.get_forces(atoms))
66 atoms.positions[0][2] -= step
68 d = np.array(d)
69 E = np.array(E)
70 F = np.array(F)
72 if rank == 0:
73 np.save('d.npy', d)
74 np.save('E.npy', E)
75 np.save('F.npy', F)
76 from pprint import pprint
77 print('d')
78 pprint(list(d))
79 print()
80 print('E')
81 pprint(list(E))
82 print()
83 print('F')
84 pprint([list([list(l2) for l2 in l1]) for l1 in F])
85 else:
86 # h=0.2, setups: 0.9.11271, analytic gradient for dielectric
87 d = [
88 2.0000000000000009,
89 2.0500000000000007,
90 2.1000000000000005,
91 2.1500000000000004,
92 2.2000000000000002,
93 2.25,
94 2.2999999999999998,
95 2.3499999999999996,
96 2.3999999999999995,
97 2.4499999999999993,
98 2.4999999999999991,
99 2.5499999999999989,
100 2.5999999999999988,
101 2.6499999999999986,
102 2.6999999999999984,
103 2.7499999999999982,
104 2.799999999999998,
105 2.8499999999999979,
106 2.8999999999999977,
107 2.9499999999999975,
108 2.9999999999999973,
109 3.0499999999999972,
110 3.099999999999997,
111 3.1499999999999968,
112 3.1999999999999966
113 ]
115 E = [
116 -3.563293139400153,
117 -3.8483728941723685,
118 -4.0711353674406672,
119 -4.2422702451403147,
120 -4.370843143863639,
121 -4.4644180141621437,
122 -4.5291689227132208,
123 -4.5703343350581118,
124 -4.5923649740023134,
125 -4.5988279473461287,
126 -4.5927583650683008,
127 -4.5766206693403326,
128 -4.552512679729916,
129 -4.5222435353897978,
130 -4.4871564101028012,
131 -4.4484418158342498,
132 -4.4070815207527305,
133 -4.3639583908891826,
134 -4.3196547998536206,
135 -4.2746896647162842,
136 -4.2295530542966002,
137 -4.1846071284157169,
138 -4.1401332574837273,
139 -4.0962878505444289,
140 -4.0533187524468151
141 ]
143 F = [
144 [[2.6695821634315027e-14, -1.9815222433664118e-14, -6.4048490688641513],
145 [-3.4082241002543622e-12, -1.6240575950714694e-12, 6.4041543958163452]],
146 [[5.0237752987734333e-14, -1.8243293058344225e-14, -5.0393351704101388],
147 [-1.5274896963914418e-12, -2.6398128880388972e-12, 5.0384209277642569]],
148 [[-3.3648148693779676e-14, -5.9189391352451007e-15, -3.9060170441670885],
149 [-8.4063321767924475e-13, -1.8821508970704685e-12, 3.905543132953595]],
150 [[8.033887557888106e-14, 2.2292480376612319e-14, -2.9689893614346223],
151 [-1.223736631764757e-12, -3.0969447122690189e-12, 2.9698074624482889]],
152 [[-7.1961182128217342e-14, -9.2369061233814621e-15, -2.1978946263288037],
153 [-8.8389572960386298e-13, -1.7994648800211978e-12, 2.1973890330684451]],
154 [[6.839819262929247e-14, -5.8813682508237254e-14, -1.5641714869085164],
155 [-1.7622743497617748e-12, -3.0046379594525875e-12, 1.5635650982049627]],
156 [[-9.2964056597828368e-15, -7.5214119452955559e-15, -1.0432778856648208],
157 [-1.5739852469080523e-12, -2.4988123414408888e-12, 1.0427858515240367]],
158 [[4.9365637419280049e-14, -1.2806637110762107e-14, -0.61777588826245566],
159 [-4.4633495248199538e-12, -8.4857800635478483e-13, 0.6191354280714596]],
160 [[-2.7465001491431585e-15, 2.2194992830262815e-15, -0.27335940186763846],
161 [2.2159953452415821e-12, -1.6412130688619773e-12, 0.27444763375180131]],
162 [[-1.1307851878181378e-15, 1.1152866935660596e-14, 0.0049642333143336626],
163 [-4.8799705828538529e-13, -2.6066991522648012e-12, -0.0056198136590138066]],
164 [[-3.347924840460358e-14, -3.2847274823028079e-14, 0.22997686507849791],
165 [-1.3848503589740797e-12, -3.1093262854030672e-12, -0.23005715760038931]],
166 [[-1.8200707826858258e-14, -4.3512573943996744e-14, 0.40941568333028211],
167 [1.1291695481871819e-12, -1.7474358989696015e-12, -0.40852810117309907]],
168 [[-1.8441970643900321e-14, 5.61993561755351e-15, 0.54932435973885352],
169 [-2.9861492319230874e-12, -3.0227468023234651e-12, -0.54910391544690951]],
170 [[5.7679940133610544e-14, -2.8084539456163582e-14, 0.65784317706542283],
171 [-1.0234224221710884e-12, -2.5375102240498517e-12, -0.65870527795968759]],
172 [[-4.036664664589713e-15, -4.8205738644069875e-15, 0.7418625322893313],
173 [-1.2904684108015449e-12, -2.1348233844072512e-12, -0.74210985960596609]],
174 [[1.5318647653685363e-14, -3.421407576047577e-14, 0.80443218412113771],
175 [8.3926234818054389e-13, -2.6560910148144288e-12, -0.80377843042267316]],
176 [[6.9157565324685627e-15, -2.0265394116808891e-14, 0.84757769602691035],
177 [2.2564924910397676e-12, -2.2656092749597723e-12, -0.84788230013436461]],
178 [[3.6816279559139879e-14, -5.6217408874846806e-14, 0.87630143837759911],
179 [-2.8919485514440065e-12, -1.5785323374781975e-12, -0.8773089495263976]],
180 [[-2.7265578221877411e-15, 1.8112097101058187e-14, 0.89457926961794421],
181 [-4.3811750564077208e-12, -2.461714836405928e-12, -0.89468520749309732]],
182 [[3.7482219205057949e-14, -4.1131307258530919e-14, 0.90318154899617831],
183 [-1.0179375774172992e-12, -1.3810014419535958e-12, -0.90288363369205138]],
184 [[6.8901452284609408e-14, -1.8368129697299518e-14, 0.90212377386324472],
185 [-2.5725988597478254e-12, -2.4508096630572398e-12, -0.90278569263072728]],
186 [[-2.9987853255786194e-14, -8.7933798499792121e-15, 0.89490384223117858],
187 [-1.9656418164895607e-12, -2.497775366538778e-12, -0.89593617789571922]],
188 [[3.5493492557854517e-14, -9.8132290416331934e-15, 0.88412548228462806],
189 [-2.1672889098350696e-13, -1.9186968970762135e-12, -0.88434959210747588]],
190 [[-4.2258218665175417e-14, -7.2197458334904237e-15, 0.86940669722572905],
191 [-6.3029668473160416e-13, -2.8462694786350656e-12, -0.86883783222557032]],
192 [[-1.3554138996860724e-14, 7.8037843761066284e-16, 0.84970515380081257],
193 [-4.5784249812786949e-12, -2.0773781762455308e-12, -0.8501771195912331]]
194 ]
195 d = np.array(d)
196 E = np.array(E)
197 F = np.array(F)
200 # test for orthogonal forces equal zero:
201 assert F[..., :2] == pytest.approx(.0, abs=1e-7)
203 stencil = 2 # 1 is too rough, 3 does not change compared to 2
204 FNa, FCl = F[..., 2].T
205 FNa *= -1.
206 # test symmetry
207 assert FNa == pytest.approx(FCl, abs=F_max_err)
208 dd = np.diff(d)[0]
209 kernel = {
210 1: np.array((0.5, 0, -0.5)),
211 2: np.array((-1. / 12., 2. / 3., 0, -2. / 3., 1. / 12.)),
212 3: np.array((1. / 60., -0.15, 0.75, 0, -0.75, 0.15, -1. / 60.))}
214 dEdz = np.convolve(E, kernel[stencil] / step, 'valid')
216 err = np.maximum(
217 np.abs(-dEdz - FNa[stencil:-stencil]),
218 np.abs(-dEdz - FCl[stencil:-stencil]))
220 # test forces against -dE / dd finite difference
221 print(err)
222 assert err == pytest.approx(.0, abs=F_max_err)
224 if SKIP_ENERGY_CALCULATION:
225 # check only selected points:
227 def check(index):
228 atoms.positions[0][2] = 6.8 - index * step
229 F_check = atoms.get_forces()
230 assert F_check[..., :2] == pytest.approx(.0, abs=1e-7)
231 FNa_check, FCl_check = F_check[..., 2].T
232 FNa_check *= -1.
233 assert FNa_check == pytest.approx(FCl_check, abs=F_max_err)
234 err = np.maximum(
235 np.abs(-dEdz[index - stencil] - FNa_check),
236 np.abs(-dEdz[index - stencil] - FCl_check))
237 print(err)
238 assert err == pytest.approx(.0, abs=F_max_err)
240 l = len(FNa)
241 # check(stencil)
242 check(l // 2)
243 # check(l - 1 - stencil)