Statistiques
| Révision :

root / ase / calculators / test.py @ 1

Historique | Voir | Annoter | Télécharger (4,09 ko)

1
from math import pi
2

    
3
import numpy as np
4

    
5
from ase.atoms import Atoms
6

    
7

    
8
def make_test_dft_calculation():
9
    a = b = 2.0
10
    c = 6.0
11
    atoms = Atoms(positions=[(0, 0, c / 2)],
12
                  symbols='H',
13
                  pbc=(1, 1, 0),
14
                  cell=(a, b, c),
15
                  calculator=TestCalculator())
16
    return atoms
17

    
18

    
19
class TestCalculator:
20
    def __init__(self, nk=8):
21
        assert nk % 2 == 0
22
        bzk = []
23
        weights = []
24
        ibzk = []
25
        w = 1.0 / nk**2
26
        for i in range(-nk + 1, nk, 2):
27
            for j in range(-nk + 1, nk, 2):
28
                k = (0.5 * i / nk, 0.5 * j / nk, 0)
29
                bzk.append(k)
30
                if i >= j > 0:
31
                    ibzk.append(k)
32
                    if i == j:
33
                        weights.append(4 * w)
34
                    else:
35
                        weights.append(8 * w)
36
        assert abs(sum(weights) - 1.0) < 1e-12
37
        self.bzk = np.array(bzk)
38
        self.ibzk = np.array(ibzk)
39
        self.weights = np.array(weights)
40

    
41
        # Calculate eigenvalues and wave functions:
42
        self.init()
43

    
44
    def init(self):
45
        nibzk = len(self.weights)
46
        nbands = 1
47

    
48
        V = -1.0
49
        self.eps = 2 * V * (np.cos(2 * pi * self.ibzk[:, 0]) +
50
                            np.cos(2 * pi * self.ibzk[:, 1]))
51
        self.eps.shape = (nibzk, nbands)
52

    
53
        self.psi = np.zeros((nibzk, 20, 20, 60), complex)
54
        phi = np.empty((2, 2, 20, 20, 60))
55
        z = np.linspace(-1.5, 1.5, 60, endpoint=False)
56
        for i in range(2):
57
            x = np.linspace(0, 1, 20, endpoint=False) - i
58
            for j in range(2):
59
                y = np.linspace(0, 1, 20, endpoint=False) - j
60
                r = (((x[:, None]**2 +
61
                       y**2)[:, :, None] +
62
                      z**2)**0.5).clip(0, 1)
63
                phi = 1.0 - r**2 * (3.0 - 2.0 * r)
64
                phase = np.exp(pi * 2j * np.dot(self.ibzk, (i, j, 0)))
65
                self.psi += phase[:, None, None, None] * phi
66

    
67
    def get_pseudo_wave_function(self, band=0, kpt=0, spin=0):
68
        assert spin == 0 and band == 0
69
        return self.psi[kpt]
70

    
71
    def get_eigenvalues(self, kpt=0, spin=0):
72
        assert spin == 0
73
        return self.eps[kpt]
74

    
75
    def get_number_of_bands(self):
76
        return 1
77

    
78
    def get_k_point_weights(self):
79
        return self.weights
80

    
81
    def get_number_of_spins(self):
82
        return 1
83

    
84
    def get_fermi_level(self):
85
        return 0.0
86

    
87

    
88
class TestPotential:
89
    def get_forces(self, atoms):
90
        E = 0.0
91
        R = atoms.positions
92
        F = np.zeros_like(R)
93
        for a, r in enumerate(R):
94
            D = R - r
95
            d = (D**2).sum(1)**0.5
96
            x = d - 1.0
97
            E += np.vdot(x, x)
98
            d[a] = 1
99
            F -= (x / d)[:, None] * D
100
        self.energy = 0.25 * E
101
        return F
102

    
103
    def get_potential_energy(self, atoms):
104
        self.get_forces(atoms)
105
        return self.energy
106

    
107
    def get_stress(self, atoms):
108
        raise NotImplementedError
109

    
110

    
111
def numeric_force(atoms, a, i, d=0.001):
112
    """Evaluate force along i'th axis on a'th atom using finite difference.
113

114
    This will trigger two calls to get_potential_energy(), with atom a moved
115
    plus/minus d in the i'th axial direction, respectively.
116
    """
117
    p0 = atoms.positions[a, i]
118
    atoms.positions[a, i] += d
119
    eplus = atoms.get_potential_energy()
120
    atoms.positions[a, i] -= 2 * d
121
    eminus = atoms.get_potential_energy()
122
    atoms.positions[a, i] = p0
123
    return (eminus - eplus) / (2 * d)
124

    
125

    
126
def numeric_forces(atoms, indices=None, axes=(0, 1, 2), d=0.001):
127
    """Evaluate finite-difference forces on several atoms.
128

129
    Returns an array of forces for each specified atomic index and
130
    each specified axis, calculated using finite difference on each
131
    atom and direction separately.  Array has same shape as if
132
    returned from atoms.get_forces(); uncalculated elements are zero.
133

134
    Calculates all forces by default."""
135

    
136
    if indices is None:
137
        indices = range(len(atoms))
138
    F_ai = np.zeros_like(atoms.positions)
139
    for a in indices:
140
        for i in axes:
141
            F_ai[a, i] = numeric_force(atoms, a, i, d)
142
    return F_ai