Statistiques
| Révision :

root / ase / embed.py @ 17

Historique | Voir | Annoter | Télécharger (11,71 ko)

1
"""
2
This module is the EMBED module for ASE
3
implemented by T. Kerber
4

5
Torsten Kerber, ENS LYON: 2011, 07, 11
6

7
This work is supported by Award No. UK-C0017, made by King Abdullah
8
University of Science and Technology (KAUST)
9
"""
10

    
11
import math
12
from ase import Atom, Atoms
13
from ase.data import covalent_radii, atomic_numbers
14

    
15
import numpy as np
16

    
17
class Embed(Atoms):
18
    #--- constructor of the Embed class ---
19
    def __init__(self, system, cluster, cell_cluster = "Auto", cluster_pos = True, linkType = 'H'):
20
        super(Embed, self).__init__()
21
        # define the atom map
22
        self.atom_map_sys_cl = []
23
        self.atom_map_cl_sys = []
24
        self.linkatoms = []
25
        # cluster dimensions
26
        self.xyz_cl_min = None
27
        self.xyz_cl_max = None
28
        # set the search radius for link atoms
29
        self.d = 10
30
        # define the systems for calculations
31
        if system is None or cluster is None:
32
            raise RuntimeError("Embed: system or cluster is not definied")
33
        self.set_system(system)
34
        if cluster_pos:
35
            self.set_cluster(cluster)
36
        else:
37
            self.set_cluster_by_numbers(cluster)
38
            
39
        # set the cell of the system
40
        self.set_cell(system.get_cell())
41
        self.cell_cluster = cell_cluster
42
        
43
        self.embed(linkType)
44
        return
45

    
46
    #--- set the cluster ---
47
    def set_cluster(self, atoms):
48
        import copy
49
        # set the min/max cluster dimensions
50
        self.xyz_cl_min = atoms[0].get_position()
51
        self.xyz_cl_max = atoms[0].get_position()
52
        for atom in atoms:
53
            # assign the label "Cluster (10)" in atom.TAG
54
            atom.set_tag(10)
55
            xyz=atom.get_position()
56
            for i in xrange(3):
57
                # set the min/max cluster dimensions
58
                if xyz[i] < self.xyz_cl_min[i]:
59
                    self.xyz_cl_min[i] = xyz[i]
60
                if xyz[i] > self.xyz_cl_max[i]:
61
                    self.xyz_cl_max[i] = xyz[i]
62

    
63
        # add self.d around min/max cluster dimensions
64
        self.xyz_cl_min -= [self.d, self.d, self.d]
65
        self.xyz_cl_max += [self.d, self.d, self.d]
66
        # set the cluster for low and high level calculation
67
        self.atoms_cluster = atoms
68
        return
69
        
70
    #--- set cluster by atom numbers ---
71
    def set_cluster_by_numbers(self, numbers):
72
        cluster = Atoms()
73
        nat = len(self.atoms_system)
74
        for number in numbers:
75
            if nat > numbers:
76
                raise RuntimeError("QMX: The number of the cluster atom ", number, "is bigger than the number of atoms")
77
            cluster.append(self.atoms_system[number-1])
78
        self.set_cluster(cluster)
79

    
80
    #--- set the system ---
81
    def set_system(self, atoms):
82
        self.atoms_system = atoms
83
        # assign the label "Cluster (10)" in atom.TAG
84
        for atom in atoms:
85
            atom.set_tag(0)
86
        # update search radius for link atoms
87
        dx = 0
88
        for atom in atoms:
89
            r = covalent_radii[atom.get_atomic_number()]
90
            if (r > dx):
91
                dx = r
92
        self.d = dx * 2.1
93
        return
94

    
95
    #--- return cluster ---
96
    def get_cluster(self):
97
        return self.atoms_cluster
98

    
99
    def get_system(self):
100
        return self.atoms_system
101

    
102
    #--- Embedding ---
103
    def embed(self, linkType):
104
        # is the cluster and the host system definied ?
105
        if self.atoms_cluster is None or self.atoms_system is None:
106
            return
107
        self.find_cluster()
108
        self.set_linkatoms(linkType)
109
        print "link atoms found: ", len(self.linkatoms)
110
        if self.cell_cluster == "System":
111
            self.atoms_cluster.set_cell(self.atoms_system.get_cell())
112
        elif self.cell_cluster == "Auto":
113
            positions = self.atoms_cluster.get_positions()
114
            # build cell
115
            cell = np.zeros((3, 3),  float)
116
            #for all directions
117
            for idir in xrange(3):
118
                #find the biggest dimensions of the cluster in x,y,z direction
119
                l = positions[:, idir].max() - positions[:, idir].min()
120
                # calculate the box parameters (2*cluster + 60 pm)
121
                l = (math.floor((2*l+6)/2.5)+1)*2.5
122
                # apply cell parameters
123
                cell[idir, idir] = l
124
            # set parameters to cluster
125
            self.atoms_cluster.set_cell(cell)
126
            # print information on the screen
127
            print "dimension of box surrounding the cluster: ", 
128
            for idir in xrange(3):
129
                print cell[idir, idir]*10, 
130
            print " pm"
131
            exit()
132
        else:
133
            self.atoms_cluster.set_cell(self.cell_cluster)
134

    
135
    def find_cluster(self):
136
        # set tolerance
137
        d = 0.001
138
        #atoms
139
        xyzs_cl=[]
140
        for atom_cl in self.atoms_cluster:
141
            xyzs_cl.append(atom_cl.get_position())
142
        xyzs_sys=[]
143
        for atom_sys in self.atoms_system:
144
            xyzs_sys.append(atom_sys.get_position())
145

    
146
        self.atom_map_sys_cl = np.zeros(len(self.atoms_system), int)
147
        self.atom_map_cl_sys = np.zeros(len(self.atoms_cluster), int)
148
        # loop over cluster atoms atom_sys
149
        for iat_sys in xrange(len(self.atoms_system)):
150
            # get the coordinates of the system atom atom_sys
151
            xyz_sys = xyzs_sys[iat_sys]
152
            # bSysOnly: no identical atom has been found
153
            bSysOnly = True
154
            # loop over system atoms atom_cl
155
            for iat_cl in xrange(len(self.atoms_cluster)):
156
                # difference vector between both atoms
157
                xyz_diff = np.abs(xyzs_sys[iat_sys]-xyzs_cl[iat_cl])
158
                # identical atoms
159
                if xyz_diff[0] < d and xyz_diff[1] < d and xyz_diff[2] < d:
160
                    # set tag (CLUSTER+HOST: 10) to atom_sys
161
                    self.atoms_system[iat_sys].set_tag(10)
162
                    # map the atom in the atom list
163
                    self.atom_map_sys_cl[iat_sys] = iat_cl
164
                    self.atom_map_cl_sys[iat_cl] = iat_sys
165
                    # atom has been identified
166
                    bSysOnly = False
167
                    break
168
            if bSysOnly:
169
                self.atom_map_sys_cl[iat_sys] = -1
170

    
171
    def set_linkatoms(self, linkType, tol=15.):
172
        # local copies of xyz coordinates to avoid massive copying of xyz objects
173
        xyzs_cl=[]
174
        for atom_cl in self.atoms_cluster:
175
            xyzs_cl.append(atom_cl.get_position())
176
        xyzs_sys=[]
177
        for atom_sys in self.atoms_system:
178
            xyzs_sys.append(atom_sys.get_position())
179
        # number of atoms in the cluster and the system
180
        nat_cl=len(self.atoms_cluster)
181
        nat_sys=len(self.atoms_system)
182
        # system has pbc?
183
        pbc = self.get_pbc()
184
        # set the bond table
185
        bonds = []
186
        # set the 27 cell_vec, starting with the (0,0,0) vector for the unit cell
187
        cells_L = [(0.0,  0.0,  0.0)]
188
        # get the cell vectors of the host system and build up a 3 by 3 supercell to search for neighbors in the surrounding
189
        cell = self.atoms_system.get_cell()
190
        if self.atoms_system.get_pbc().any():
191
            for ix in xrange(-1, 2):
192
                for iy in xrange(-1, 2):
193
                    for iz in xrange(-1, 2):
194
                        if ix == 0 and iy == 0 and iz == 0:
195
                            continue
196
                        cells_L.append(np.dot([ix, iy, iz], cell))
197
        # save the radius of system atoms
198
        rs_sys = []
199
        for atom in self.atoms_system:
200
            rs_sys.append(covalent_radii[atom.get_atomic_number()])
201
        # sum over cluster atoms (iat_cl)
202
        for iat_cl in xrange(nat_cl):
203
            # get the cluster atom
204
            atom_cl=self.atoms_cluster[iat_cl]
205
            # ignore link atoms
206
            if atom_cl.get_tag() == 50:
207
                continue
208
            # xyz coordinates and covalent radius of the cluster atom iat_cl
209
            xyz_cl = xyzs_cl[iat_cl]
210
            r_cl = covalent_radii[atom_cl.get_atomic_number()]
211

    
212
            # sum over system atoms (iat_sys)
213
            for iat_sys in xrange(nat_sys):
214
                # avoid cluster atoms
215
                if self.atoms_system[iat_sys].get_tag()==10:
216
                    continue
217
                # sum over all cell_L
218
                for cell_L in cells_L:
219
                    # xyz coordinates and covalent radius of the system atom iat_sys
220
                    xyz_sys = xyzs_sys[iat_sys]+cell_L
221
                    # go only in distance self.d around the cluster
222
                    lcont = True
223
                    for i in xrange(3):
224
                        if (xyz_sys[i] < self.xyz_cl_min[i] or
225
                            xyz_sys[i] > self.xyz_cl_max[i]):
226
                            lcont = False
227
                            break
228
                    if not lcont:
229
                        continue
230
                    # xyz coordinates and covalent radius of the system atom iat_sys
231
                    r_sys = rs_sys[iat_sys]
232
                    # diff vector
233
                    xyz_diff = xyz_sys - xyz_cl
234
                    # distance between the atoms
235
                    r = np.sqrt(np.dot(xyz_diff, xyz_diff))
236
                    # ratio of the distance to the sum of covalent radius
237
                    f = r / (r_cl + r_sys)
238
                    if f <= (1+tol/100.) and f >= (1-2*tol/100.):
239
                        s = cell_L, self.atom_map_cl_sys[iat_cl], iat_sys, r_cl
240
                        bonds.append(s)
241
                        break
242
                    if f <= (1-2*tol/100.):
243
                        raise RuntimeError("QMX: The cluster atom", iat_cl, " and the system atom", iat_sys, "came too close")
244

    
245
        r_h = covalent_radii[atomic_numbers[linkType]]
246
        for bond in bonds:
247
            cell_L, iat_cl_sys, iat_sys, r_cl = bond
248
            # assign the tags for the border atoms
249
            atom_sys.set_tag(1)
250
            atom_cl.set_tag(11)
251
            #difference vector for the link atom, scaling
252
            xyz_diff = xyzs_sys[iat_sys] + cell_L - xyzs_sys[iat_cl_sys]
253
            r = (r_cl + r_h)
254
            xyz_diff *=  r / np.sqrt(np.dot(xyz_diff, xyz_diff))
255
            # determine position of the link atom
256
            xyz_diff += xyzs_sys[iat_cl_sys]
257
            # create link atom
258
            atom = Atom(symbol=linkType, position=xyz_diff, tag=50)
259
            # add atom to cluster
260
            self.atoms_cluster.append(atom)
261
            # add atom to the linkatoms
262
            s = cell_L, iat_cl_sys, iat_sys, r, len(self.atoms_cluster)-1
263
            self.linkatoms.append(s)
264
        return
265

    
266
    def set_positions(self, positions_new):
267
        # number of atoms
268
        nat_sys=len(self.atoms_system)
269
        # go over all pairs of atoms
270
        for iat_sys in xrange(nat_sys):
271
            xyz = positions_new[iat_sys]
272
            self.atoms_system[iat_sys].set_position(xyz)
273
            iat_cl = self.atom_map_sys_cl[iat_sys]
274
            if iat_cl > -1:
275
                self.atoms_cluster[iat_cl].set_position(xyz)
276

    
277
        for cell_L, iat_cl_sys, iat_sys, r, iat in self.linkatoms:
278
            # determine position of the link atom
279
            xyz_cl = positions_new[iat_cl_sys]
280
            xyz = positions_new[iat_sys] - xyz_cl + cell_L
281
            xyz *= r / np.sqrt(np.dot(xyz, xyz))
282
            xyz += xyz_cl
283
            # update xyz coordinates of the cluster
284
            self.atoms_cluster[iat].set_position(xyz)
285

    
286
    def __getitem__(self, i):
287
        return self.atoms_system.__getitem__(i)
288

    
289
    def get_positions(self):
290
        return self.atoms_system.get_positions()
291

    
292
    def __add__(self, other):
293
        return self.atoms_system.__add__(other)
294

    
295
    def __delitem__(self, i):
296
        return self.atoms_system.__delitem__(i)
297

    
298
    def __len__(self):
299
        return self.atoms_system.__len__()
300
        
301
    def get_chemical_symbols(self, reduce=False):
302
        return self.atoms_system.get_chemical_symbols(reduce)