Statistiques
| Révision :

root / ase / neb.py @ 12

Historique | Voir | Annoter | Télécharger (9,43 ko)

1 1 tkerber
from math import sqrt
2 1 tkerber
3 1 tkerber
import numpy as np
4 1 tkerber
5 1 tkerber
from ase.parallel import world, rank, size
6 1 tkerber
from ase.calculators.singlepoint import SinglePointCalculator
7 1 tkerber
from ase.io import read
8 1 tkerber
9 1 tkerber
class NEB:
10 1 tkerber
    def __init__(self, images, k=0.1, climb=False, parallel=False):
11 1 tkerber
        self.images = images
12 1 tkerber
        self.k = k
13 1 tkerber
        self.climb = climb
14 1 tkerber
        self.parallel = parallel
15 1 tkerber
        self.natoms = len(images[0])
16 1 tkerber
        self.nimages = len(images)
17 1 tkerber
        self.emax = np.nan
18 1 tkerber
19 1 tkerber
    def interpolate(self):
20 1 tkerber
        pos1 = self.images[0].get_positions()
21 1 tkerber
        pos2 = self.images[-1].get_positions()
22 1 tkerber
        d = (pos2 - pos1) / (self.nimages - 1.0)
23 1 tkerber
        for i in range(1, self.nimages - 1):
24 1 tkerber
            self.images[i].set_positions(pos1 + i * d)
25 1 tkerber
26 1 tkerber
    def get_positions(self):
27 1 tkerber
        positions = np.empty(((self.nimages - 2) * self.natoms, 3))
28 1 tkerber
        n1 = 0
29 1 tkerber
        for image in self.images[1:-1]:
30 1 tkerber
            n2 = n1 + self.natoms
31 1 tkerber
            positions[n1:n2] = image.get_positions()
32 1 tkerber
            n1 = n2
33 1 tkerber
        return positions
34 1 tkerber
35 1 tkerber
    def set_positions(self, positions):
36 1 tkerber
        n1 = 0
37 1 tkerber
        for image in self.images[1:-1]:
38 1 tkerber
            n2 = n1 + self.natoms
39 1 tkerber
            image.set_positions(positions[n1:n2])
40 1 tkerber
            n1 = n2
41 1 tkerber
42 1 tkerber
    def get_forces(self):
43 1 tkerber
        """Evaluate and return the forces."""
44 1 tkerber
        images = self.images
45 1 tkerber
        forces = np.empty(((self.nimages - 2), self.natoms, 3))
46 1 tkerber
        energies = np.empty(self.nimages - 2)
47 1 tkerber
48 1 tkerber
        if not self.parallel:
49 1 tkerber
            # Do all images - one at a time:
50 1 tkerber
            for i in range(1, self.nimages - 1):
51 1 tkerber
                energies[i - 1] = images[i].get_potential_energy()
52 1 tkerber
                forces[i - 1] = images[i].get_forces()
53 1 tkerber
        else:
54 1 tkerber
            # Parallelize over images:
55 1 tkerber
            i = rank * (self.nimages - 2) // size + 1
56 1 tkerber
            try:
57 1 tkerber
                energies[i - 1] = images[i].get_potential_energy()
58 1 tkerber
                forces[i - 1] = images[i].get_forces()
59 1 tkerber
            except:
60 1 tkerber
                # Make sure other images also fail:
61 1 tkerber
                error = world.sum(1.0)
62 1 tkerber
                raise
63 1 tkerber
            else:
64 1 tkerber
                error = world.sum(0.0)
65 1 tkerber
                if error:
66 1 tkerber
                    raise RuntimeError('Parallel NEB failed!')
67 1 tkerber
68 1 tkerber
            for i in range(1, self.nimages - 1):
69 1 tkerber
                root = (i - 1) * size // (self.nimages - 2)
70 1 tkerber
                world.broadcast(energies[i - 1:i], root)
71 1 tkerber
                world.broadcast(forces[i - 1], root)
72 1 tkerber
73 1 tkerber
        imax = 1 + np.argsort(energies)[-1]
74 1 tkerber
        self.emax = energies[imax - 1]
75 1 tkerber
76 1 tkerber
        tangent1 = images[1].get_positions() - images[0].get_positions()
77 1 tkerber
        for i in range(1, self.nimages - 1):
78 1 tkerber
            tangent2 = (images[i + 1].get_positions() -
79 1 tkerber
                        images[i].get_positions())
80 1 tkerber
            if i < imax:
81 1 tkerber
                tangent = tangent2
82 1 tkerber
            elif i > imax:
83 1 tkerber
                tangent = tangent1
84 1 tkerber
            else:
85 1 tkerber
                tangent = tangent1 + tangent2
86 1 tkerber
87 1 tkerber
            tt = np.vdot(tangent, tangent)
88 1 tkerber
            f = forces[i - 1]
89 1 tkerber
            ft = np.vdot(f, tangent)
90 1 tkerber
            if i == imax and self.climb:
91 1 tkerber
                f -= 2 * ft / tt * tangent
92 1 tkerber
            else:
93 1 tkerber
                f -= ft / tt * tangent
94 1 tkerber
                f -= (np.vdot(tangent1 - tangent2, tangent) *
95 1 tkerber
                      self.k / tt * tangent)
96 1 tkerber
97 1 tkerber
            tangent1 = tangent2
98 1 tkerber
99 1 tkerber
        return forces.reshape((-1, 3))
100 1 tkerber
101 1 tkerber
    def get_potential_energy(self):
102 1 tkerber
        return self.emax
103 1 tkerber
104 1 tkerber
    def __len__(self):
105 1 tkerber
        return (self.nimages - 2) * self.natoms
106 1 tkerber
107 1 tkerber
class SingleCalculatorNEB(NEB):
108 1 tkerber
    def __init__(self, images, k=0.1, climb=False):
109 1 tkerber
        if isinstance(images, str):
110 1 tkerber
            # this is a filename
111 1 tkerber
            traj = read(images, '0:')
112 1 tkerber
            images = []
113 1 tkerber
            for atoms in traj:
114 1 tkerber
                images.append(atoms)
115 1 tkerber
116 1 tkerber
        NEB.__init__(self, images, k, climb, False)
117 1 tkerber
        self.calculators = [None] * self.nimages
118 1 tkerber
        self.energies_ok = False
119 1 tkerber
120 1 tkerber
    def interpolate(self, initial=0, final=-1):
121 1 tkerber
        """Interpolate linearly between initial and final images."""
122 1 tkerber
        if final < 0:
123 1 tkerber
            final = self.nimages + final
124 1 tkerber
        n = final - initial
125 1 tkerber
        pos1 = self.images[initial].get_positions()
126 1 tkerber
        pos2 = self.images[final].get_positions()
127 1 tkerber
        d = (pos2 - pos1) / n
128 1 tkerber
        for i in range(1, n):
129 1 tkerber
            self.images[initial + i].set_positions(pos1 + i * d)
130 1 tkerber
131 1 tkerber
    def refine(self, steps=1, begin=0, end=-1):
132 1 tkerber
        """Refine the NEB trajectory."""
133 1 tkerber
        if end < 0:
134 1 tkerber
            end = self.nimages + end
135 1 tkerber
        j = begin
136 1 tkerber
        n = end - begin
137 1 tkerber
        for i in range(n):
138 1 tkerber
            for k in range(steps):
139 1 tkerber
                self.images.insert(j + 1, self.images[j].copy())
140 1 tkerber
                self.calculators.insert(j + 1, None)
141 1 tkerber
            self.nimages = len(self.images)
142 1 tkerber
            self.interpolate(j, j + steps + 1)
143 1 tkerber
            j += steps + 1
144 1 tkerber
145 1 tkerber
    def set_positions(self, positions):
146 1 tkerber
        # new positions -> new forces
147 1 tkerber
        if self.energies_ok:
148 1 tkerber
            # restore calculators
149 1 tkerber
            self.set_calculators(self.calculators[1:-1])
150 1 tkerber
        NEB.set_positions(self, positions)
151 1 tkerber
152 1 tkerber
    def get_calculators(self):
153 1 tkerber
        """Return the original calculators."""
154 1 tkerber
        calculators = []
155 1 tkerber
        for i, image in enumerate(self.images):
156 1 tkerber
            if self.calculators[i] is None:
157 1 tkerber
                calculators.append(image.get_calculator())
158 1 tkerber
            else:
159 1 tkerber
                calculators.append(self.calculators[i])
160 1 tkerber
        return calculators
161 1 tkerber
162 1 tkerber
    def set_calculators(self, calculators):
163 1 tkerber
        """Set new calculators to the images."""
164 1 tkerber
        self.energies_ok = False
165 1 tkerber
166 1 tkerber
        if not isinstance(calculators, list):
167 1 tkerber
            calculators = [calculators] * self.nimages
168 1 tkerber
169 1 tkerber
        n = len(calculators)
170 1 tkerber
        if n == self.nimages:
171 1 tkerber
            for i in range(self.nimages):
172 1 tkerber
                self.images[i].set_calculator(calculators[i])
173 1 tkerber
        elif n == self.nimages - 2:
174 1 tkerber
            for i in range(1, self.nimages -1):
175 1 tkerber
                self.images[i].set_calculator(calculators[i-1])
176 1 tkerber
        else:
177 1 tkerber
            raise RuntimeError(
178 1 tkerber
                'len(calculators)=%d does not fit to len(images)=%d'
179 1 tkerber
                % (n, self.nimages))
180 1 tkerber
181 1 tkerber
    def get_energies_and_forces(self, all=False):
182 1 tkerber
        """Evaluate energies and forces and hide the calculators"""
183 1 tkerber
        if self.energies_ok:
184 1 tkerber
            return
185 1 tkerber
186 1 tkerber
        images = self.images
187 1 tkerber
        forces = np.zeros(((self.nimages - 2), self.natoms, 3))
188 1 tkerber
        energies = np.zeros(self.nimages - 2)
189 1 tkerber
        self.emax = -1.e32
190 1 tkerber
191 1 tkerber
        def calculate_and_hide(i):
192 1 tkerber
            image = self.images[i]
193 1 tkerber
            calc = image.get_calculator()
194 1 tkerber
            if self.calculators[i] is None:
195 1 tkerber
                self.calculators[i] = calc
196 1 tkerber
            if calc is not None:
197 1 tkerber
                if not isinstance(calc, SinglePointCalculator):
198 1 tkerber
                    self.images[i].set_calculator(
199 1 tkerber
                        SinglePointCalculator(image.get_potential_energy(),
200 1 tkerber
                                              image.get_forces(),
201 1 tkerber
                                              None,
202 1 tkerber
                                              None,
203 1 tkerber
                                              image))
204 1 tkerber
                self.emax = min(self.emax, image.get_potential_energy())
205 1 tkerber
206 1 tkerber
        if all and self.calculators[0] is None:
207 1 tkerber
            calculate_and_hide(0)
208 1 tkerber
209 1 tkerber
        # Do all images - one at a time:
210 1 tkerber
        for i in range(1, self.nimages - 1):
211 1 tkerber
            calculate_and_hide(i)
212 1 tkerber
213 1 tkerber
        if all and self.calculators[-1] is None:
214 1 tkerber
            calculate_and_hide(-1)
215 1 tkerber
216 1 tkerber
        self.energies_ok = True
217 1 tkerber
218 1 tkerber
    def get_forces(self):
219 1 tkerber
        self.get_energies_and_forces()
220 1 tkerber
        return NEB.get_forces(self)
221 1 tkerber
222 1 tkerber
    def n(self):
223 1 tkerber
        return self.nimages
224 1 tkerber
225 1 tkerber
    def write(self, filename):
226 1 tkerber
        from ase.io.trajectory import PickleTrajectory
227 1 tkerber
        traj = PickleTrajectory(filename, 'w', self)
228 1 tkerber
        traj.write()
229 1 tkerber
        traj.close()
230 1 tkerber
231 1 tkerber
    def __add__(self, other):
232 1 tkerber
        for image in other:
233 1 tkerber
            self.images.append(image)
234 1 tkerber
        return self
235 1 tkerber
236 1 tkerber
def fit(images):
237 1 tkerber
    E = [i.get_potential_energy() for i in images]
238 1 tkerber
    F = [i.get_forces() for i in images]
239 1 tkerber
    R = [i.get_positions() for i in images]
240 1 tkerber
    return fit0(E, F, R)
241 1 tkerber
242 1 tkerber
def fit0(E, F, R):
243 1 tkerber
    E = np.array(E) - E[0]
244 1 tkerber
    n = len(E)
245 1 tkerber
    Efit = np.empty((n - 1) * 20 + 1)
246 1 tkerber
    Sfit = np.empty((n - 1) * 20 + 1)
247 1 tkerber
248 1 tkerber
    s = [0]
249 1 tkerber
    for i in range(n - 1):
250 1 tkerber
        s.append(s[-1] + sqrt(((R[i + 1] - R[i])**2).sum()))
251 1 tkerber
252 1 tkerber
    lines = []
253 1 tkerber
    for i in range(n):
254 1 tkerber
        if i == 0:
255 1 tkerber
            d = R[1] - R[0]
256 1 tkerber
            ds = 0.5 * s[1]
257 1 tkerber
        elif i == n - 1:
258 1 tkerber
            d = R[-1] - R[-2]
259 1 tkerber
            ds = 0.5 * (s[-1] - s[-2])
260 1 tkerber
        else:
261 1 tkerber
            d = R[i + 1] - R[i - 1]
262 1 tkerber
            ds = 0.25 * (s[i + 1] - s[i - 1])
263 1 tkerber
264 1 tkerber
        d = d / sqrt((d**2).sum())
265 1 tkerber
        dEds = -(F[i] * d).sum()
266 1 tkerber
        x = np.linspace(s[i] - ds, s[i] + ds, 3)
267 1 tkerber
        y = E[i] + dEds * (x - s[i])
268 1 tkerber
        lines.append((x, y))
269 1 tkerber
270 1 tkerber
        if i > 0:
271 1 tkerber
            s0 = s[i - 1]
272 1 tkerber
            s1 = s[i]
273 1 tkerber
            x = np.linspace(s0, s1, 20, endpoint=False)
274 1 tkerber
            c = np.linalg.solve(np.array([(1, s0,   s0**2,     s0**3),
275 1 tkerber
                                          (1, s1,   s1**2,     s1**3),
276 1 tkerber
                                          (0,  1,  2 * s0, 3 * s0**2),
277 1 tkerber
                                          (0,  1,  2 * s1, 3 * s1**2)]),
278 1 tkerber
                                np.array([E[i - 1], E[i], dEds0, dEds]))
279 1 tkerber
            y = c[0] + x * (c[1] + x * (c[2] + x * c[3]))
280 1 tkerber
            Sfit[(i - 1) * 20:i * 20] = x
281 1 tkerber
            Efit[(i - 1) * 20:i * 20] = y
282 1 tkerber
283 1 tkerber
        dEds0 = dEds
284 1 tkerber
285 1 tkerber
    Sfit[-1] = s[-1]
286 1 tkerber
    Efit[-1] = E[-1]
287 1 tkerber
    return s, E, Sfit, Efit, lines