Coverage for gpaw/test/test_reuse_wfs.py: 98%
48 statements
« prev ^ index » next coverage.py v7.7.1, created at 2025-07-20 00:19 +0000
« prev ^ index » next coverage.py v7.7.1, created at 2025-07-20 00:19 +0000
1import pytest
2from ase.build import bulk
4from gpaw import GPAW, Mixer
5from gpaw.convergence_criteria import Eigenstates
8class MyConvergenceCriterion(Eigenstates):
9 def __init__(self, tol):
10 super().__init__(tol)
11 self.history = []
13 def get_error(self, context):
14 value = super().get_error(context)
15 self.history.append(value)
16 return value
19def run(atoms, method, kwargs):
20 conv_tol = 1e-9
21 conv = MyConvergenceCriterion(conv_tol)
23 kwargs = {
24 'convergence': {'custom': [conv]},
25 'experimental': {'reuse_wfs_method': method},
26 **kwargs}
28 calc = GPAW(**kwargs)
30 atoms.calc = calc
31 with pytest.warns(UserWarning,
32 match='Custom convergence criterion'):
33 E1 = atoms.get_potential_energy()
34 assert conv.history[-1] < conv_tol
35 niter1 = len(conv.history)
36 del conv.history[:]
38 atoms.rattle(stdev=0.0001)
40 if method is None and not calc.old:
41 calc.dft.ibzwfs.move_wave_functions = lambda *args: None
43 E2 = atoms.get_potential_energy()
44 niter2 = len(conv.history)
45 reuse_error = conv.history[0]
47 # If the change in energy is exactly or suspiciously close to zero, it's
48 # because nothing was done at all (something was cached but shouldn't
49 # have been)
50 delta_e = abs(E2 - E1)
51 assert delta_e > 1e-6, delta_e
52 return niter1, niter2, reuse_error
55@pytest.mark.parametrize('mode, reuse_type, max_reuse_error', [
56 ('pw', 'paw', 1e-5),
57 ('pw', None, 1e-4),
58 ('fd', 'paw', 1e-4),
59 ('fd', None, 1e-3)])
60def test_reuse_wfs(mode, reuse_type, max_reuse_error):
61 """Check that wavefunctions are meaningfully reused.
63 For a different modes and parameters, this test asserts that the
64 initial wavefunction error in the second scf step is below a
65 certain threshold, indicating that we are doing better than if
66 we started from scratch."""
68 atoms = bulk('Si')
69 atoms.rattle(stdev=0.01, seed=17) # Break symmetry
71 kwargs = dict(
72 mode=mode,
73 kpts=(2, 2, 2),
74 xc='PBE',
75 mixer=Mixer(0.4, 5, 20.0))
77 niter1, niter2, reuse_error = run(
78 atoms, reuse_type, kwargs)
80 # It should at the very least be faster to do the second step:
81 assert niter2 < niter1
82 assert reuse_error < max_reuse_error
85# @pytest.mark.old_gpaw_only
86def test_reuse_sg15(sg15_hydrogen):
87 """Test wfs reuse with sg15.
89 As of writing this test, the sg15 pseudopotentials have no pseudo
90 partial waves, and therefore, reusing them should have no effect."""
91 from ase.build import molecule
92 atoms = molecule('H2', vacuum=2.0)
94 # If we do not rattle, we will get broken symmetry error:
95 atoms.rattle(stdev=.001)
97 kwargs = dict(
98 mode='pw',
99 setups={'H': sg15_hydrogen},
100 xc='PBE')
102 niter1, niter2, reuse_error = run(atoms, 'paw', kwargs)
103 assert niter2 < niter1
104 assert reuse_error < 1e-5