Statistiques
| Révision :

root / ase / embed.py @ 13

Historique | Voir | Annoter | Télécharger (11,24 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=None, cluster=None, cell_cluster = "Auto"):
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
        self.set_system(system)
32
        self.set_cluster(cluster)
33
        # set the cell of the system
34
        self.set_cell(system.get_cell())
35
        self.cell_cluster = cell_cluster
36
        return
37

    
38
    #--- set the cluster ---
39
    def set_cluster(self, atoms):
40
        import copy
41
        # set the min/max cluster dimensions
42
        self.xyz_cl_min = atoms[0].get_position()
43
        self.xyz_cl_max = atoms[0].get_position()
44
        for atom in atoms:
45
            # assign the label "Cluster (10)" in atom.TAG
46
            atom.set_tag(10)
47
            xyz=atom.get_position()
48
            for i in xrange(3):
49
                # set the min/max cluster dimensions
50
                if xyz[i] < self.xyz_cl_min[i]:
51
                    self.xyz_cl_min[i] = xyz[i]
52
                if xyz[i] > self.xyz_cl_max[i]:
53
                    self.xyz_cl_max[i] = xyz[i]
54

    
55
        # add self.d around min/max cluster dimensions
56
        self.xyz_cl_min -= [self.d, self.d, self.d]
57
        self.xyz_cl_max += [self.d, self.d, self.d]
58
        # set the cluster for low and high level calculation
59
        self.atoms_cluster = atoms
60
        return
61

    
62
    #--- set the system ---
63
    def set_system(self, atoms):
64
        self.atoms_system = atoms
65
        # assign the label "Cluster (10)" in atom.TAG
66
        for atom in atoms:
67
            atom.set_tag(0)
68
        # update search radius for link atoms
69
        dx = 0
70
        for atom in atoms:
71
            r = covalent_radii[atom.get_atomic_number()]
72
            if (r > dx):
73
                dx = r
74
        self.d = dx * 2.1
75
        return
76

    
77
    #--- return cluster ---
78
    def get_cluster(self):
79
        return self.atoms_cluster
80

    
81
    def get_system(self):
82
        return self.atoms_system
83

    
84
    #--- Embedding ---
85
    def embed(self):
86
        # is the cluster and the host system definied ?
87
        if self.atoms_cluster is None or self.atoms_system is None:
88
            return
89
        self.find_cluster()
90
        self.set_linkatoms()
91
        print "link atoms found: ", len(self.linkatoms)
92
        if self.cell_cluster == "System":
93
            self.atoms_cluster.set_cell(self.atoms_system.get_cell())
94
        elif self.cell_cluster == "Auto":
95
            positions = self.atoms_cluster.get_positions()
96
            #find the biggest dimensions of the cluster in x,y,z direction
97
            l = 0
98
            for idir in xrange(3):
99
                l = max(l, positions[:, idir].max() - positions[:, idir].min())
100
            # calculate the box parameters (cluster + min 5 Ang)
101
            l = (math.floor(l/2.5)+1)*2.5 + 5.0
102
            # build cell
103
            cell = np.zeros((3, 3),  float)
104
            # apply cell parameters
105
            for idir in xrange(3):
106
                cell[idir, idir] = l
107
            # set parameters to cluster
108
            self.atoms_cluster.set_cell(cell)
109
            # print information on the screen
110
            print "length of box surrounding the cluster: ", 
111
            print l*10, 
112
            print "pm"
113
        else:
114
            self.atoms_cluster.set_cell(self.cell_cluster)
115

    
116
    def find_cluster(self):
117
        # set tolerance
118
        d = 0.001
119
        #atoms
120
        xyzs_cl=[]
121
        for atom_cl in self.atoms_cluster:
122
            xyzs_cl.append(atom_cl.get_position())
123
        xyzs_sys=[]
124
        for atom_sys in self.atoms_system:
125
            xyzs_sys.append(atom_sys.get_position())
126

    
127
        self.atom_map_sys_cl = np.zeros(len(self.atoms_system), int)
128
        self.atom_map_cl_sys = np.zeros(len(self.atoms_cluster), int)
129
        # loop over cluster atoms atom_sys
130
        for iat_sys in xrange(len(self.atoms_system)):
131
            # get the coordinates of the system atom atom_sys
132
            xyz_sys = xyzs_sys[iat_sys]
133
            # bSysOnly: no identical atom has been found
134
            bSysOnly = True
135
            # loop over system atoms atom_cl
136
            for iat_cl in xrange(len(self.atoms_cluster)):
137
                # difference vector between both atoms
138
                xyz_diff = np.abs(xyzs_sys[iat_sys]-xyzs_cl[iat_cl])
139
                # identical atoms
140
                if xyz_diff[0] < d and xyz_diff[1] < d and xyz_diff[2] < d:
141
                    # set tag (CLUSTER+HOST: 10) to atom_sys
142
                    self.atoms_system[iat_sys].set_tag(10)
143
                    # map the atom in the atom list
144
                    self.atom_map_sys_cl[iat_sys] = iat_cl
145
                    self.atom_map_cl_sys[iat_cl] = iat_sys
146
                    # atom has been identified
147
                    bSysOnly = False
148
                    break
149
            if bSysOnly:
150
                self.atom_map_sys_cl[iat_sys] = -1
151

    
152
    def set_linkatoms(self, tol=15., linkAtom=None, debug=False):
153
        # local copies of xyz coordinates to avoid massive copying of xyz objects
154
        xyzs_cl=[]
155
        for atom_cl in self.atoms_cluster:
156
            xyzs_cl.append(atom_cl.get_position())
157
        xyzs_sys=[]
158
        for atom_sys in self.atoms_system:
159
            xyzs_sys.append(atom_sys.get_position())
160
        # set the standard link atom
161
        if linkAtom is None:
162
            linkAtom ='H'
163
        # number of atoms in the cluster and the system
164
        nat_cl=len(self.atoms_cluster)
165
        nat_sys=len(self.atoms_system)
166
        # system has pbc?
167
        pbc = self.get_pbc()
168
        # set the bond table
169
        bonds = []
170
        # set the 27 cell_vec, starting with the (0,0,0) vector for the unit cell
171
        cells_L = [(0.0,  0.0,  0.0)]
172
        # get the cell vectors of the host system and build up a 3 by 3 supercell to search for neighbors in the surrounding
173
        cell = self.atoms_system.get_cell()
174
        if self.atoms_system.get_pbc().any():
175
            for ix in xrange(-1, 2):
176
                for iy in xrange(-1, 2):
177
                    for iz in xrange(-1, 2):
178
                        if ix == 0 and iy == 0 and iz == 0:
179
                            continue
180
                        cells_L.append(np.dot([ix, iy, iz], cell))
181
        # save the radius of system atoms
182
        rs_sys = []
183
        for atom in self.atoms_system:
184
            rs_sys.append(covalent_radii[atom.get_atomic_number()])
185
        # sum over cluster atoms (iat_cl)
186
        for iat_cl in xrange(nat_cl):
187
            # get the cluster atom
188
            atom_cl=self.atoms_cluster[iat_cl]
189
            # ignore link atoms
190
            if atom_cl.get_tag() == 50:
191
                continue
192
            # xyz coordinates and covalent radius of the cluster atom iat_cl
193
            xyz_cl = xyzs_cl[iat_cl]
194
            r_cl = covalent_radii[atom_cl.get_atomic_number()]
195

    
196
            # sum over system atoms (iat_sys)
197
            for iat_sys in xrange(nat_sys):
198
                # avoid cluster atoms
199
                if self.atoms_system[iat_sys].get_tag()==10:
200
                    continue
201
                # sum over all cell_L
202
                for cell_L in cells_L:
203
                    # xyz coordinates and covalent radius of the system atom iat_sys
204
                    xyz_sys = xyzs_sys[iat_sys]+cell_L
205
                    # go only in distance self.d around the cluster
206
                    lcont = True
207
                    for i in xrange(3):
208
                        if (xyz_sys[i] < self.xyz_cl_min[i] or
209
                            xyz_sys[i] > self.xyz_cl_max[i]):
210
                            lcont = False
211
                            break
212
                    if not lcont:
213
                        continue
214
                    # xyz coordinates and covalent radius of the system atom iat_sys
215
                    r_sys = rs_sys[iat_sys]
216
                    # diff vector
217
                    xyz_diff = xyz_sys - xyz_cl
218
                    # distance between the atoms
219
                    r = np.sqrt(np.dot(xyz_diff, xyz_diff))
220
                    # ratio of the distance to the sum of covalent radius
221
                    f = r / (r_cl + r_sys)
222
                    if debug:
223
                        print "Covalent radii = ",r_cl, r_sys
224
                        print "Distance ", f
225
                        print "tol = ",(1+tol/100.),(1-tol/100.),(1-2*tol/100.)
226
                    if f <= (1+tol/100.) and f >= (1-2*tol/100.):
227
                        s = cell_L, self.atom_map_cl_sys[iat_cl], iat_sys, r_cl
228
                        bonds.append(s)
229
                        break
230
                    if f <= (1-2*tol/100.):
231
                        raise RuntimeError("QMX: The cluster atom", iat_cl, " and the system atom", iat_sys, "came too close")
232

    
233
        r_h = covalent_radii[atomic_numbers[linkAtom]]
234
        for bond in bonds:
235
            cell_L, iat_cl_sys, iat_sys, r_cl = bond
236
            # assign the tags for the border atoms
237
            atom_sys.set_tag(1)
238
            atom_cl.set_tag(11)
239
            #difference vector for the link atom, scaling
240
            xyz_diff = xyzs_sys[iat_sys] + cell_L - xyzs_sys[iat_cl_sys]
241
            r = (r_cl + r_h)
242
            xyz_diff *=  r / np.sqrt(np.dot(xyz_diff, xyz_diff))
243
            # determine position of the link atom
244
            xyz_diff += xyzs_sys[iat_cl_sys]
245
            # create link atom
246
            atom = Atom(symbol=linkAtom, position=xyz_diff, tag=50)
247
            # add atom to cluster
248
            self.atoms_cluster.append(atom)
249
            # add atom to the linkatoms
250
            s = cell_L, iat_cl_sys, iat_sys, r, len(self.atoms_cluster)-1
251
            self.linkatoms.append(s)
252
        return
253

    
254
    def set_positions(self, positions_new):
255
        # number of atoms
256
        nat_sys=len(self.atoms_system)
257
        # go over all pairs of atoms
258
        for iat_sys in xrange(nat_sys):
259
            xyz = positions_new[iat_sys]
260
            self.atoms_system[iat_sys].set_position(xyz)
261
            iat_cl = self.atom_map_sys_cl[iat_sys]
262
            if iat_cl > -1:
263
                self.atoms_cluster[iat_cl].set_position(xyz)
264

    
265
        for cell_L, iat_cl_sys, iat_sys, r, iat in self.linkatoms:
266
            # determine position of the link atom
267
            xyz_cl = positions_new[iat_cl_sys]
268
            xyz = positions_new[iat_sys] - xyz_cl + cell_L
269
            xyz *= r / np.sqrt(np.dot(xyz, xyz))
270
            xyz += xyz_cl
271
            # update xyz coordinates of the cluster
272
            self.atoms_cluster[iat].set_position(xyz)
273

    
274
    def __getitem__(self, i):
275
        return self.atoms_system.__getitem__(i)
276

    
277
    def get_positions(self):
278
        return self.atoms_system.get_positions()
279

    
280
    def __add__(self, other):
281
        return self.atoms_system.__add__(other)
282

    
283
    def __delitem__(self, i):
284
        return self.atoms_system.__delitem__(i)
285

    
286
    def __len__(self):
287
        return self.atoms_system.__len__()
288
        
289
    def get_chemical_symbols(self, reduce=False):
290
        return self.atoms_system.get_chemical_symbols(reduce)