root / ase / calculators / test.py @ 4
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
|