Statistiques
| Révision :

root / ase / gui / images.py @ 20

Historique | Voir | Annoter | Télécharger (12,65 ko)

1
from math import sqrt
2

    
3
import numpy as np
4

    
5
from ase.data import covalent_radii
6
from ase.atoms import Atoms
7
from ase.calculators.singlepoint import SinglePointCalculator
8
from ase.io import read, write, string2index
9
from ase.constraints import FixAtoms
10

    
11

    
12
class Images:
13
    def __init__(self, images=None):
14

    
15
        if images is not None:
16
            self.initialize(images)
17
    
18
    def initialize(self, images, filenames=None, init_magmom=False):
19
        
20
        self.natoms = len(images[0])
21

    
22
        self.nimages = len(images)
23
        if filenames is None:
24
            filenames = [None] * self.nimages
25
        self.filenames = filenames
26
        self.P = np.empty((self.nimages, self.natoms, 3))
27
        self.E = np.empty(self.nimages)
28
        self.K = np.empty(self.nimages)
29
        self.F = np.empty((self.nimages, self.natoms, 3))
30
        self.M = np.empty((self.nimages, self.natoms))
31
        self.T = np.empty((self.natoms))
32
        self.A = np.empty((self.nimages, 3, 3))
33
        self.Z = images[0].get_atomic_numbers()
34
        self.pbc = images[0].get_pbc()
35
        warning = False
36
        for i, atoms in enumerate(images):
37
            natomsi = len(atoms)
38
            if (natomsi != self.natoms or
39
                (atoms.get_atomic_numbers() != self.Z).any()):
40
                raise RuntimeError('Can not handle different images with ' +
41
                                   'different numbers of atoms or different ' +
42
                                   'kinds of atoms!')
43
            self.P[i] = atoms.get_positions()
44
            self.A[i] = atoms.get_cell()
45
            if (atoms.get_pbc() != self.pbc).any():
46
                warning = True
47
            try:
48
                self.E[i] = atoms.get_potential_energy()
49
            except RuntimeError:
50
                self.E[i] = np.nan
51
            self.K[i] = atoms.get_kinetic_energy()
52
            try:
53
                self.F[i] = atoms.get_forces(apply_constraint=False)
54
            except RuntimeError:
55
                self.F[i] = np.nan
56
            try:
57
                if init_magmom:
58
                    self.M[i] = atoms.get_initial_magnetic_moments()
59
                else:
60
                  self.M[i] = atoms.get_magnetic_moments()
61
            except (RuntimeError, AttributeError):
62
                self.M[i] = 0.0
63
                
64
            # added support for tags
65
            try:
66
                self.T = atoms.get_tags()
67
            except RuntimeError:
68
                self.T = np.nan
69
                
70

    
71
        if warning:
72
            print('WARNING: Not all images have the same bondary conditions!')
73
            
74
        self.selected = np.zeros(self.natoms, bool)
75
        self.selected_ordered  = []
76
        self.atoms_to_rotate_0 = np.zeros(self.natoms, bool)
77
        self.visible = np.ones(self.natoms, bool)
78
        self.nselected = 0
79
        self.set_dynamic(constraints = images[0].constraints)
80
        self.repeat = np.ones(3, int)
81
        self.set_radii(0.89)
82
        
83
    def prepare_new_atoms(self):
84
        "Marks that the next call to append_atoms should clear the images."
85
        self.next_append_clears = True
86
        
87
    def append_atoms(self, atoms, filename=None):
88
        "Append an atoms object to the images already stored."
89
        assert len(atoms) == self.natoms
90
        if self.next_append_clears:
91
            i = 0
92
        else:
93
            i = self.nimages
94
        for name in ('P', 'E', 'K', 'F', 'M', 'A'):
95
            a = getattr(self, name)
96
            newa = np.empty( (i+1,) + a.shape[1:] )
97
            if not self.next_append_clears:
98
                newa[:-1] = a
99
            setattr(self, name, newa)
100
        self.next_append_clears = False
101
        self.P[i] = atoms.get_positions()
102
        self.A[i] = atoms.get_cell()
103
        try:
104
            self.E[i] = atoms.get_potential_energy()
105
        except RuntimeError:
106
            self.E[i] = np.nan
107
        self.K[i] = atoms.get_kinetic_energy()
108
        try:
109
            self.F[i] = atoms.get_forces(apply_constraint=False)
110
        except RuntimeError:
111
            self.F[i] = np.nan
112
        try:
113
            self.M[i] = atoms.get_magnetic_moments()
114
        except (RuntimeError, AttributeError):
115
            self.M[i] = np.nan
116
        self.nimages = i + 1
117
        self.filenames.append(filename)
118
        self.set_dynamic()
119
        return self.nimages
120
        
121
    def set_radii(self, scale):
122
        self.r = covalent_radii[self.Z] * scale
123
                
124
    def read(self, filenames, index=-1):
125
        images = []
126
        names = []
127
        for filename in filenames:
128
            i = read(filename, index)
129
            
130
            if not isinstance(i, list):
131
                i = [i]
132
            images.extend(i)
133
            names.extend([filename] * len(i))
134
            
135
        self.initialize(images, names)
136
    
137
    def import_atoms(self, filename, cur_frame):
138
        if filename:
139
            filename = filename[0]
140
            old_a = self.get_atoms(cur_frame)
141
            imp_a = read(filename, -1)
142
            new_a = old_a + imp_a
143
            self.initialize([new_a], [filename])
144
    
145
    def repeat_images(self, repeat):
146
        n = self.repeat.prod()
147
        repeat = np.array(repeat)
148
        self.repeat = repeat
149
        N = repeat.prod()
150
        natoms = self.natoms // n
151
        P = np.empty((self.nimages, natoms * N, 3))
152
        M = np.empty((self.nimages, natoms * N))
153
        T = np.empty(natoms * N, int)
154
        F = np.empty((self.nimages, natoms * N, 3))
155
        Z = np.empty(natoms * N, int)
156
        r = np.empty(natoms * N)
157
        dynamic = np.empty(natoms * N, bool)
158
        a0 = 0
159
        for i0 in range(repeat[0]):
160
            for i1 in range(repeat[1]):
161
                for i2 in range(repeat[2]):
162
                    a1 = a0 + natoms
163
                    for i in range(self.nimages):
164
                        P[i, a0:a1] = (self.P[i, :natoms] +
165
                                       np.dot((i0, i1, i2), self.A[i]))
166
                    F[:, a0:a1] = self.F[:, :natoms]
167
                    M[:, a0:a1] = self.M[:, :natoms]
168
                    T[a0:a1] = self.T[:natoms]
169
                    Z[a0:a1] = self.Z[:natoms]
170
                    r[a0:a1] = self.r[:natoms]
171
                    dynamic[a0:a1] = self.dynamic[:natoms]
172
                    a0 = a1
173
        self.P = P
174
        self.F = F
175
        self.Z = Z
176
        self.T = T
177
        self.M = M
178
        self.r = r
179
        self.dynamic = dynamic
180
        self.natoms = natoms * N
181
        self.selected = np.zeros(natoms * N, bool)
182
        self.atoms_to_rotate_0 = np.zeros(self.natoms, bool)
183
        self.visible = np.ones(natoms * N, bool)
184
        self.nselected = 0
185
        
186
    def graph(self, expr):
187
        import ase.units as units
188
        code = compile(expr + ',', 'atoms.py', 'eval')
189

    
190
        n = self.nimages
191
        def d(n1, n2):
192
            return sqrt(((R[n1] - R[n2])**2).sum())
193
        def a(n1, n2, n3):
194
            v1 = R[n1]-R[n2]
195
            v2 = R[n3]-R[n2]
196
            arg = np.vdot(v1,v2)/(sqrt((v1**2).sum()*(v2**2).sum()))
197
            if arg > 1.0: arg = 1.0
198
            if arg < -1.0: arg = -1.0
199
            return 180.0*np.arccos(arg)/np.pi
200
        def dih(n1, n2, n3, n4):
201
            # vector 0->1, 1->2, 2->3 and their normalized cross products:
202
            a    = R[n2]-R[n1]
203
            b    = R[n3]-R[n2]
204
            c    = R[n4]-R[n3]
205
            bxa  = np.cross(b,a)
206
            bxa /= np.sqrt(np.vdot(bxa,bxa))
207
            cxb  = np.cross(c,b)
208
            cxb /= np.sqrt(np.vdot(cxb,cxb))
209
            angle = np.vdot(bxa,cxb)
210
            # check for numerical trouble due to finite precision:
211
            if angle < -1: angle = -1
212
            if angle >  1: angle =  1
213
            angle = np.arccos(angle)
214
            if (np.vdot(bxa,c)) > 0: angle = 2*np.pi-angle
215
            return angle*180.0/np.pi
216
        # get number of mobile atoms for temperature calculation
217
        ndynamic = 0
218
        for dyn in self.dynamic: 
219
            if dyn: ndynamic += 1
220
        S = self.selected
221
        D = self.dynamic[:, np.newaxis]
222
        E = self.E
223
        s = 0.0
224
        data = []
225
        for i in range(n):
226
            R = self.P[i]
227
            F = self.F[i]
228
            A = self.A[i]
229
            f = ((F * D)**2).sum(1)**.5
230
            fmax = max(f)
231
            fave = f.mean()
232
            epot = E[i]
233
            ekin = self.K[i]
234
            e = epot + ekin
235
            T = 2.0 * ekin / (3.0 * ndynamic * units.kB)
236
            data = eval(code)
237
            if i == 0:
238
                m = len(data)
239
                xy = np.empty((m, n))
240
            xy[:, i] = data
241
            if i + 1 < n:
242
                s += sqrt(((self.P[i + 1] - R)**2).sum())
243
        return xy
244

    
245
    def set_dynamic(self, constraints = None):
246
        if self.nimages == 1:
247
            self.dynamic = np.ones(self.natoms, bool)
248
        else:
249
            self.dynamic = np.zeros(self.natoms, bool)
250
            R0 = self.P[0]
251
            for R in self.P[1:]:
252
                self.dynamic |= (np.abs(R - R0) > 1.0e-10).any(1)
253
        if constraints is not None:
254
            for con in constraints: 
255
                if isinstance(con,FixAtoms):
256
                    self.dynamic[con.index] = False
257

    
258
    def write(self, filename, rotations='', show_unit_cell=False, bbox=None):
259
        indices = range(self.nimages)
260
        p = filename.rfind('@')
261
        if p != -1:
262
            try:
263
                slice = string2index(filename[p + 1:])
264
            except ValueError:
265
                pass
266
            else:
267
                indices = indices[slice]
268
                filename = filename[:p]
269
                if isinstance(indices, int):
270
                    indices = [indices]
271

    
272
        images = [self.get_atoms(i) for i in indices]
273
        if len(filename) > 4 and filename[-4:] in ['.eps', '.png', '.pov']:
274
            write(filename, images, 
275
                  rotation=rotations, show_unit_cell=show_unit_cell,
276
                  bbox=bbox)
277
        else:
278
            write(filename, images)
279

    
280
    def get_atoms(self, frame):
281
        atoms = Atoms(positions=self.P[frame],
282
                      numbers=self.Z,
283
                      magmoms=self.M[0],
284
                      tags=self.T,
285
                      cell=self.A[frame],
286
                      pbc=self.pbc)
287
        
288
        # check for constrained atoms and add them accordingly:
289
        if not self.dynamic.all():
290
            atoms.set_constraint(FixAtoms(mask=1-self.dynamic))
291
        
292
        atoms.set_calculator(SinglePointCalculator(self.E[frame],
293
                                                   self.F[frame],
294
                                                   None, None, atoms))
295
        return atoms
296
                           
297
    def delete(self, i):
298
        self.nimages -= 1
299
        P = np.empty((self.nimages, self.natoms, 3))
300
        F = np.empty((self.nimages, self.natoms, 3))
301
        A = np.empty((self.nimages, 3, 3))
302
        E = np.empty(self.nimages)
303
        P[:i] = self.P[:i]
304
        P[i:] = self.P[i + 1:]
305
        self.P = P
306
        F[:i] = self.F[:i]
307
        F[i:] = self.F[i + 1:]
308
        self.F = F
309
        A[:i] = self.A[:i]
310
        A[i:] = self.A[i + 1:]
311
        self.A = A
312
        E[:i] = self.E[:i]
313
        E[i:] = self.E[i + 1:]
314
        self.E = E
315
        del self.filenames[i]
316

    
317
    def aneb(self):
318
        n = self.nimages
319
        assert n % 5 == 0
320
        levels = n // 5
321
        n = self.nimages = 2 * levels + 3
322
        P = np.empty((self.nimages, self.natoms, 3))
323
        F = np.empty((self.nimages, self.natoms, 3))
324
        E = np.empty(self.nimages)
325
        for L in range(levels):
326
            P[L] = self.P[L * 5]
327
            P[n - L - 1] = self.P[L * 5 + 4]
328
            F[L] = self.F[L * 5]
329
            F[n - L - 1] = self.F[L * 5 + 4]
330
            E[L] = self.E[L * 5]
331
            E[n - L - 1] = self.E[L * 5 + 4]
332
        for i in range(3):
333
            P[levels + i] = self.P[levels * 5 - 4 + i]
334
            F[levels + i] = self.F[levels * 5 - 4 + i]
335
            E[levels + i] = self.E[levels * 5 - 4 + i]
336
        self.P = P
337
        self.F = F
338
        self.E = E
339

    
340
    def interpolate(self, m):
341
        assert self.nimages == 2
342
        self.nimages = 2 + m
343
        P = np.empty((self.nimages, self.natoms, 3))
344
        F = np.empty((self.nimages, self.natoms, 3))
345
        A = np.empty((self.nimages, 3, 3))
346
        E = np.empty(self.nimages)
347
        P[0] = self.P[0]
348
        F[0] = self.F[0]
349
        A[0] = self.A[0]
350
        E[0] = self.E[0]
351
        for i in range(1, m + 1):
352
            x = i / (m + 1.0)
353
            y = 1 - x
354
            P[i] = y * self.P[0] + x * self.P[1]
355
            F[i] = y * self.F[0] + x * self.F[1]
356
            A[i] = y * self.A[0] + x * self.A[1]
357
            E[i] = y * self.E[0] + x * self.E[1]
358
        P[-1] = self.P[1]
359
        F[-1] = self.F[1]
360
        A[-1] = self.A[1]
361
        E[-1] = self.E[1]
362
        self.P = P
363
        self.F = F
364
        self.A = A
365
        self.E = E
366
        self.filenames[1:1] = [None] * m
367

    
368
if __name__ == '__main__':
369
    import os
370
    os.system('python gui.py')