Statistiques
| Révision :

root / ase / embed.py @ 5

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

1
from ase import Atom, Atoms
2
from ase.data import covalent_radii, atomic_numbers
3

    
4
import numpy as np
5

    
6
class Embed(Atoms):
7
    #--- constructor of the Embed class ---
8
    def __init__(self, system=None, cluster=None):
9
        super(Embed, self).__init__()
10
        # define the atom map
11
        self.atom_map_sys_cl = []
12
        self.atom_map_cl_sys = []
13
        self.linkatoms = []
14
        # cluster dimensions
15
        self.xyz_cl_min = None
16
        self.xyz_cl_max = None
17
        # set the search radius for link atoms
18
        self.d = 10
19
        # define the systems for calculations
20
        self.set_system(system)
21
        self.set_cluster(cluster)
22
        #
23
        self.set_cell([10, 10, 10])
24
        return
25

    
26
    #--- set the cluster ---
27
    def set_cluster(self, atoms):
28
        import copy
29
        # set the min/max cluster dimensions
30
        self.xyz_cl_min = atoms[0].get_position()
31
        self.xyz_cl_max = atoms[0].get_position()
32
        for atom in atoms:
33
            # assign the label "Cluster (10)" in atom.TAG
34
            atom.set_tag(10)
35
            xyz=atom.get_position()
36
            for i in xrange(3):
37
                # set the min/max cluster dimensions
38
                if xyz[i] < self.xyz_cl_min[i]:
39
                    self.xyz_cl_min[i] = xyz[i]
40
                if xyz[i] > self.xyz_cl_max[i]:
41
                    self.xyz_cl_max[i] = xyz[i]
42

    
43
        # add self.d around min/max cluster dimensions
44
        self.xyz_cl_min -= [self.d, self.d, self.d]
45
        self.xyz_cl_max += [self.d, self.d, self.d]
46
        # set the cluster for low and high level calculation
47
        self.atoms_cluster = atoms
48
        return
49

    
50
    #--- set the system ---
51
    def set_system(self, atoms):
52
        self.atoms_system = atoms
53
        # assign the label "Cluster (10)" in atom.TAG
54
        for atom in atoms:
55
            atom.set_tag(0)
56
        # update search radius for link atoms
57
        dx = 0
58
        for atom in atoms:
59
            r = covalent_radii[atom.get_atomic_number()]
60
            if (r > dx):
61
                dx = r
62
        self.d = dx * 2.1
63
        return
64

    
65
    #--- return cluster ---
66
    def get_cluster(self):
67
        return self.atoms_cluster
68

    
69
    def get_system(self):
70
        return self.atoms_system
71

    
72
    #--- Embedding ---
73
    def embed(self):
74
        # is the cluster and the host system definied ?
75
        if self.atoms_cluster is None or self.atoms_system is None:
76
            return
77
        self.find_cluster()
78
        self.set_linkatoms()
79
        print "link atoms found: ", len(self.linkatoms)
80

    
81
    def find_cluster(self):
82
        # set tolerance
83
        d = 0.001
84
        #atoms
85
        xyzs_cl=[]
86
        for atom_cl in self.atoms_cluster:
87
            xyzs_cl.append(atom_cl.get_position())
88
        xyzs_sys=[]
89
        for atom_sys in self.atoms_system:
90
            xyzs_sys.append(atom_sys.get_position())
91

    
92
        self.atom_map_sys_cl = np.zeros(len(self.atoms_system), int)
93
        self.atom_map_cl_sys = np.zeros(len(self.atoms_cluster), int)
94
        # loop over cluster atoms atom_sys
95
        for iat_sys in xrange(len(self.atoms_system)):
96
            # get the coordinates of the system atom atom_sys
97
            xyz_sys = xyzs_sys[iat_sys]
98
            # bSysOnly: no identical atom has been found
99
            bSysOnly = True
100
            # loop over system atoms atom_cl
101
            for iat_cl in xrange(len(self.atoms_cluster)):
102
                # difference vector between both atoms
103
                xyz_diff = np.abs(xyzs_sys[iat_sys]-xyzs_cl[iat_cl])
104
                # identical atoms
105
                if xyz_diff[0] < d and xyz_diff[1] < d and xyz_diff[2] < d:
106
                    # set tag (CLUSTER+HOST: 10) to atom_sys
107
                    self.atoms_system[iat_sys].set_tag(10)
108
                    # map the atom in the atom list
109
                    self.atom_map_sys_cl[iat_sys] = iat_cl
110
                    self.atom_map_cl_sys[iat_cl] = iat_sys
111
                    # atom has been identified
112
                    bSysOnly = False
113
                    break
114
            if bSysOnly:
115
                self.atom_map_sys_cl[iat_sys] = -1
116

    
117
    def set_linkatoms(self, tol=15., linkAtom=None, debug=False):
118
        # local copies of xyz coordinates to avoid massive copying of xyz objects
119
        xyzs_cl=[]
120
        for atom_cl in self.atoms_cluster:
121
            xyzs_cl.append(atom_cl.get_position())
122
        xyzs_sys=[]
123
        for atom_sys in self.atoms_system:
124
            xyzs_sys.append(atom_sys.get_position())
125
        # FIXME: mapping does not work when cluster atoms are outside of the box
126
        # set the standard link atom
127
        if linkAtom is None:
128
            linkAtom ='H'
129
        # number of atoms in the cluster and the system
130
        nat_cl=len(self.atoms_cluster)
131
        nat_sys=len(self.atoms_system)
132
        # system has pbc?
133
        pbc = self.get_pbc()
134
        # set the bond table
135
        bonds = []
136
        # set the 27 cell_vec, starting with the (0,0,0) vector for the unit cell
137
        cells_L = [(0.0,  0.0,  0.0)]
138
        # get the cell vectors
139
        cell = self.get_cell()
140
        # TODO: check 0D, 1D, 2D, 3D
141
        if False:
142
         for ix in xrange(-1, 2):
143
            for iy in xrange(-1, 2):
144
                for iz in xrange(-1, 2):
145
                    if ix == 0 and iy == 0 and iz == 0:
146
                        continue
147

    
148
                    cells_L.append(np.dot([ix, iy, iz], cell))
149
        # save the radius of system atoms
150
        rs_sys = []
151
        for atom in self.atoms_system:
152
            rs_sys.append(covalent_radii[atom.get_atomic_number()])
153
        # sum over cluster atoms (iat_cl)
154
        for iat_cl in xrange(nat_cl):
155
            # get the cluster atom
156
            atom_cl=self.atoms_cluster[iat_cl]
157
            # ignore link atoms
158
            if atom_cl.get_tag() == 50:
159
                continue
160
            # xyz coordinates and covalent radius of the cluster atom iat_cl
161
            xyz_cl = xyzs_cl[iat_cl]
162
            r_cl = covalent_radii[atom_cl.get_atomic_number()]
163

    
164
            # sum over system atoms (iat_sys)
165
            for iat_sys in xrange(nat_sys):
166
                # avoid cluster atoms
167
                if self.atoms_system[iat_sys].get_tag()==10:
168
                    continue
169
                # sum over all cell_L
170
                for cell_L in cells_L:
171
                    # xyz coordinates and covalent radius of the system atom iat_sys
172
                    xyz_sys = xyzs_sys[iat_sys]+cell_L
173
                    # go only in distance self.d around the cluster
174
                    lcont = True
175
                    for i in xrange(3):
176
                        if (xyz_sys[i] < self.xyz_cl_min[i] or
177
                            xyz_sys[i] > self.xyz_cl_max[i]):
178
                            lcont = False
179
                            break
180
                    if not lcont:
181
                        continue
182
                    # xyz coordinates and covalent radius of the system atom iat_sys
183
                    r_sys = rs_sys[iat_sys]
184
                    # diff vector
185
                    xyz_diff = xyz_sys - xyz_cl
186
                    # distance between the atoms
187
                    r = np.sqrt(np.dot(xyz_diff, xyz_diff))
188
                    # ratio of the distance to the sum of covalent radius
189
                    f = r / (r_cl + r_sys)
190
                    if debug:
191
                        print "Covalent radii = ",r_cl, r_sys
192
                        print "Distance ", f
193
                        print "tol = ",(1+tol/100.),(1-tol/100.),(1-2*tol/100.)
194
                    if f <= (1+tol/100.) and f >= (1-2*tol/100.):
195
                        s = cell_L, self.atom_map_cl_sys[iat_cl], iat_sys, r_cl
196
                        bonds.append(s)
197
                        break
198
                    if f <= (1-2*tol/100.):
199
                        raise RuntimeError("QMX: The cluster atom", iat_cl, " and the system atom", iat_sys, "came too close")
200

    
201
        r_h = covalent_radii[atomic_numbers[linkAtom]]
202
        for bond in bonds:
203
            cell_L, iat_cl_sys, iat_sys, r_cl = bond
204
            # assign the tags for the border atoms
205
            atom_sys.set_tag(1)
206
            atom_cl.set_tag(11)
207
            #difference vector for the link atom, scaling
208
            xyz_diff = xyzs_sys[iat_sys] + cell_L - xyzs_sys[iat_cl_sys]
209
            r = (r_cl + r_h)
210
            xyz_diff *=  r / np.sqrt(np.dot(xyz_diff, xyz_diff))
211
            # determine position of the link atom
212
            xyz_diff += xyzs_sys[iat_cl_sys]
213
            # create link atom
214
            atom = Atom(symbol=linkAtom, position=xyz_diff, tag=50)
215
            # add atom to cluster
216
            self.atoms_cluster.append(atom)
217
            # add atom to the linkatoms
218
            s = cell_L, iat_cl_sys, iat_sys, r, len(self.atoms_cluster)-1
219
            self.linkatoms.append(s)
220
        return
221

    
222
    def set_positions(self, positions_new):
223
        # number of atoms
224
        nat_sys=len(self.atoms_system)
225
        # go over all pairs of atoms
226
        for iat_sys in xrange(nat_sys):
227
            xyz = positions_new[iat_sys]
228
            self.atoms_system[iat_sys].set_position(xyz)
229
            iat_cl = self.atom_map_sys_cl[iat_sys]
230
            if iat_cl > -1:
231
                self.atoms_cluster[iat_cl].set_position(xyz)
232

    
233
        for cell_L, iat_cl_sys, iat_sys, r, iat in self.linkatoms:
234
            # determine position of the link atom
235
            xyz_cl = positions_new[iat_cl_sys]
236
            xyz = positions_new[iat_sys] - xyz_cl + cell_L
237
            xyz *= r / np.sqrt(np.dot(xyz, xyz))
238
            xyz += xyz_cl
239
            # update xyz coordinates of the cluster
240
            self.atoms_cluster[iat].set_position(xyz)
241

    
242
    def __getitem__(self, i):
243
        return self.atoms_system.__getitem__(i)
244

    
245
    def get_positions(self):
246
        return self.atoms_system.get_positions()
247

    
248
    def __add__(self, other):
249
        return self.atoms_system.__add__(other)
250

    
251
    def __delitem__(self, i):
252
        return self.atoms_system.__delitem__(i)
253

    
254
    def __len__(self):
255
        return self.atoms_system.__len__()
256
        
257
    def get_chemical_symbols(self, reduce=False):
258
        return self.atoms_system.get_chemical_symbols(reduce)