Statistiques
| Révision :

root / ase / embed.py @ 20

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

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

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

7 13 tkerber
This work is supported by Award No. UK-C0017, made by King Abdullah
8 13 tkerber
University of Science and Technology (KAUST)
9 13 tkerber
"""
10 13 tkerber
11 10 tkerber
import math
12 2 tkerber
from ase import Atom, Atoms
13 2 tkerber
from ase.data import covalent_radii, atomic_numbers
14 2 tkerber
15 2 tkerber
import numpy as np
16 2 tkerber
17 2 tkerber
class Embed(Atoms):
18 2 tkerber
    #--- constructor of the Embed class ---
19 16 tkerber
    def __init__(self, system, cluster, cell_cluster = "Auto", cluster_pos = True, linkType = 'H'):
20 2 tkerber
        super(Embed, self).__init__()
21 2 tkerber
        # define the atom map
22 2 tkerber
        self.atom_map_sys_cl = []
23 3 tkerber
        self.atom_map_cl_sys = []
24 2 tkerber
        self.linkatoms = []
25 2 tkerber
        # cluster dimensions
26 2 tkerber
        self.xyz_cl_min = None
27 2 tkerber
        self.xyz_cl_max = None
28 2 tkerber
        # set the search radius for link atoms
29 3 tkerber
        self.d = 10
30 2 tkerber
        # define the systems for calculations
31 15 tkerber
        if system is None or cluster is None:
32 15 tkerber
            raise RuntimeError("Embed: system or cluster is not definied")
33 2 tkerber
        self.set_system(system)
34 15 tkerber
        if cluster_pos:
35 15 tkerber
            self.set_cluster(cluster)
36 15 tkerber
        else:
37 15 tkerber
            self.set_cluster_by_numbers(cluster)
38 15 tkerber
39 10 tkerber
        # set the cell of the system
40 10 tkerber
        self.set_cell(system.get_cell())
41 10 tkerber
        self.cell_cluster = cell_cluster
42 16 tkerber
43 16 tkerber
        self.embed(linkType)
44 2 tkerber
        return
45 3 tkerber
46 3 tkerber
    #--- set the cluster ---
47 2 tkerber
    def set_cluster(self, atoms):
48 2 tkerber
        import copy
49 2 tkerber
        # set the min/max cluster dimensions
50 2 tkerber
        self.xyz_cl_min = atoms[0].get_position()
51 2 tkerber
        self.xyz_cl_max = atoms[0].get_position()
52 2 tkerber
        for atom in atoms:
53 2 tkerber
            # assign the label "Cluster (10)" in atom.TAG
54 2 tkerber
            atom.set_tag(10)
55 2 tkerber
            xyz=atom.get_position()
56 2 tkerber
            for i in xrange(3):
57 2 tkerber
                # set the min/max cluster dimensions
58 2 tkerber
                if xyz[i] < self.xyz_cl_min[i]:
59 2 tkerber
                    self.xyz_cl_min[i] = xyz[i]
60 2 tkerber
                if xyz[i] > self.xyz_cl_max[i]:
61 2 tkerber
                    self.xyz_cl_max[i] = xyz[i]
62 3 tkerber
63 2 tkerber
        # add self.d around min/max cluster dimensions
64 2 tkerber
        self.xyz_cl_min -= [self.d, self.d, self.d]
65 3 tkerber
        self.xyz_cl_max += [self.d, self.d, self.d]
66 2 tkerber
        # set the cluster for low and high level calculation
67 2 tkerber
        self.atoms_cluster = atoms
68 2 tkerber
        return
69 15 tkerber
70 15 tkerber
    #--- set cluster by atom numbers ---
71 15 tkerber
    def set_cluster_by_numbers(self, numbers):
72 15 tkerber
        cluster = Atoms()
73 15 tkerber
        nat = len(self.atoms_system)
74 15 tkerber
        for number in numbers:
75 15 tkerber
            if nat > numbers:
76 15 tkerber
                raise RuntimeError("QMX: The number of the cluster atom ", number, "is bigger than the number of atoms")
77 15 tkerber
            cluster.append(self.atoms_system[number-1])
78 15 tkerber
        self.set_cluster(cluster)
79 2 tkerber
80 2 tkerber
    #--- set the system ---
81 2 tkerber
    def set_system(self, atoms):
82 3 tkerber
        self.atoms_system = atoms
83 2 tkerber
        # assign the label "Cluster (10)" in atom.TAG
84 2 tkerber
        for atom in atoms:
85 2 tkerber
            atom.set_tag(0)
86 2 tkerber
        # update search radius for link atoms
87 2 tkerber
        dx = 0
88 2 tkerber
        for atom in atoms:
89 2 tkerber
            r = covalent_radii[atom.get_atomic_number()]
90 2 tkerber
            if (r > dx):
91 2 tkerber
                dx = r
92 2 tkerber
        self.d = dx * 2.1
93 2 tkerber
        return
94 3 tkerber
95 2 tkerber
    #--- return cluster ---
96 2 tkerber
    def get_cluster(self):
97 2 tkerber
        return self.atoms_cluster
98 3 tkerber
99 2 tkerber
    def get_system(self):
100 2 tkerber
        return self.atoms_system
101 3 tkerber
102 2 tkerber
    #--- Embedding ---
103 16 tkerber
    def embed(self, linkType):
104 2 tkerber
        # is the cluster and the host system definied ?
105 2 tkerber
        if self.atoms_cluster is None or self.atoms_system is None:
106 2 tkerber
            return
107 2 tkerber
        self.find_cluster()
108 16 tkerber
        self.set_linkatoms(linkType)
109 2 tkerber
        print "link atoms found: ", len(self.linkatoms)
110 10 tkerber
        if self.cell_cluster == "System":
111 10 tkerber
            self.atoms_cluster.set_cell(self.atoms_system.get_cell())
112 10 tkerber
        elif self.cell_cluster == "Auto":
113 10 tkerber
            positions = self.atoms_cluster.get_positions()
114 10 tkerber
            # build cell
115 10 tkerber
            cell = np.zeros((3, 3),  float)
116 17 tkerber
            #for all directions
117 10 tkerber
            for idir in xrange(3):
118 17 tkerber
                #find the biggest dimensions of the cluster in x,y,z direction
119 17 tkerber
                l = positions[:, idir].max() - positions[:, idir].min()
120 17 tkerber
                # calculate the box parameters (2*cluster + 60 pm)
121 17 tkerber
                l = (math.floor((2*l+6)/2.5)+1)*2.5
122 17 tkerber
                # apply cell parameters
123 10 tkerber
                cell[idir, idir] = l
124 10 tkerber
            # set parameters to cluster
125 10 tkerber
            self.atoms_cluster.set_cell(cell)
126 10 tkerber
            # print information on the screen
127 17 tkerber
            print "dimension of box surrounding the cluster: ",
128 17 tkerber
            for idir in xrange(3):
129 17 tkerber
                print cell[idir, idir]*10,
130 17 tkerber
            print " pm"
131 10 tkerber
        else:
132 10 tkerber
            self.atoms_cluster.set_cell(self.cell_cluster)
133 2 tkerber
134 2 tkerber
    def find_cluster(self):
135 2 tkerber
        # set tolerance
136 2 tkerber
        d = 0.001
137 2 tkerber
        #atoms
138 2 tkerber
        xyzs_cl=[]
139 2 tkerber
        for atom_cl in self.atoms_cluster:
140 2 tkerber
            xyzs_cl.append(atom_cl.get_position())
141 2 tkerber
        xyzs_sys=[]
142 2 tkerber
        for atom_sys in self.atoms_system:
143 2 tkerber
            xyzs_sys.append(atom_sys.get_position())
144 3 tkerber
145 3 tkerber
        self.atom_map_sys_cl = np.zeros(len(self.atoms_system), int)
146 3 tkerber
        self.atom_map_cl_sys = np.zeros(len(self.atoms_cluster), int)
147 3 tkerber
        # loop over cluster atoms atom_sys
148 2 tkerber
        for iat_sys in xrange(len(self.atoms_system)):
149 2 tkerber
            # get the coordinates of the system atom atom_sys
150 2 tkerber
            xyz_sys = xyzs_sys[iat_sys]
151 2 tkerber
            # bSysOnly: no identical atom has been found
152 2 tkerber
            bSysOnly = True
153 2 tkerber
            # loop over system atoms atom_cl
154 2 tkerber
            for iat_cl in xrange(len(self.atoms_cluster)):
155 2 tkerber
                # difference vector between both atoms
156 2 tkerber
                xyz_diff = np.abs(xyzs_sys[iat_sys]-xyzs_cl[iat_cl])
157 2 tkerber
                # identical atoms
158 2 tkerber
                if xyz_diff[0] < d and xyz_diff[1] < d and xyz_diff[2] < d:
159 2 tkerber
                    # set tag (CLUSTER+HOST: 10) to atom_sys
160 2 tkerber
                    self.atoms_system[iat_sys].set_tag(10)
161 2 tkerber
                    # map the atom in the atom list
162 3 tkerber
                    self.atom_map_sys_cl[iat_sys] = iat_cl
163 3 tkerber
                    self.atom_map_cl_sys[iat_cl] = iat_sys
164 2 tkerber
                    # atom has been identified
165 2 tkerber
                    bSysOnly = False
166 2 tkerber
                    break
167 2 tkerber
            if bSysOnly:
168 2 tkerber
                self.atom_map_sys_cl[iat_sys] = -1
169 2 tkerber
170 16 tkerber
    def set_linkatoms(self, linkType, tol=15.):
171 2 tkerber
        # local copies of xyz coordinates to avoid massive copying of xyz objects
172 2 tkerber
        xyzs_cl=[]
173 2 tkerber
        for atom_cl in self.atoms_cluster:
174 2 tkerber
            xyzs_cl.append(atom_cl.get_position())
175 2 tkerber
        xyzs_sys=[]
176 2 tkerber
        for atom_sys in self.atoms_system:
177 3 tkerber
            xyzs_sys.append(atom_sys.get_position())
178 2 tkerber
        # number of atoms in the cluster and the system
179 2 tkerber
        nat_cl=len(self.atoms_cluster)
180 2 tkerber
        nat_sys=len(self.atoms_system)
181 2 tkerber
        # system has pbc?
182 3 tkerber
        pbc = self.get_pbc()
183 2 tkerber
        # set the bond table
184 2 tkerber
        bonds = []
185 2 tkerber
        # set the 27 cell_vec, starting with the (0,0,0) vector for the unit cell
186 2 tkerber
        cells_L = [(0.0,  0.0,  0.0)]
187 10 tkerber
        # get the cell vectors of the host system and build up a 3 by 3 supercell to search for neighbors in the surrounding
188 10 tkerber
        cell = self.atoms_system.get_cell()
189 10 tkerber
        if self.atoms_system.get_pbc().any():
190 10 tkerber
            for ix in xrange(-1, 2):
191 10 tkerber
                for iy in xrange(-1, 2):
192 10 tkerber
                    for iz in xrange(-1, 2):
193 10 tkerber
                        if ix == 0 and iy == 0 and iz == 0:
194 10 tkerber
                            continue
195 10 tkerber
                        cells_L.append(np.dot([ix, iy, iz], cell))
196 2 tkerber
        # save the radius of system atoms
197 2 tkerber
        rs_sys = []
198 2 tkerber
        for atom in self.atoms_system:
199 2 tkerber
            rs_sys.append(covalent_radii[atom.get_atomic_number()])
200 2 tkerber
        # sum over cluster atoms (iat_cl)
201 2 tkerber
        for iat_cl in xrange(nat_cl):
202 2 tkerber
            # get the cluster atom
203 2 tkerber
            atom_cl=self.atoms_cluster[iat_cl]
204 2 tkerber
            # ignore link atoms
205 2 tkerber
            if atom_cl.get_tag() == 50:
206 2 tkerber
                continue
207 2 tkerber
            # xyz coordinates and covalent radius of the cluster atom iat_cl
208 2 tkerber
            xyz_cl = xyzs_cl[iat_cl]
209 2 tkerber
            r_cl = covalent_radii[atom_cl.get_atomic_number()]
210 3 tkerber
211 2 tkerber
            # sum over system atoms (iat_sys)
212 2 tkerber
            for iat_sys in xrange(nat_sys):
213 2 tkerber
                # avoid cluster atoms
214 2 tkerber
                if self.atoms_system[iat_sys].get_tag()==10:
215 2 tkerber
                    continue
216 2 tkerber
                # sum over all cell_L
217 2 tkerber
                for cell_L in cells_L:
218 2 tkerber
                    # xyz coordinates and covalent radius of the system atom iat_sys
219 2 tkerber
                    xyz_sys = xyzs_sys[iat_sys]+cell_L
220 2 tkerber
                    # go only in distance self.d around the cluster
221 2 tkerber
                    lcont = True
222 2 tkerber
                    for i in xrange(3):
223 3 tkerber
                        if (xyz_sys[i] < self.xyz_cl_min[i] or
224 2 tkerber
                            xyz_sys[i] > self.xyz_cl_max[i]):
225 2 tkerber
                            lcont = False
226 2 tkerber
                            break
227 2 tkerber
                    if not lcont:
228 2 tkerber
                        continue
229 2 tkerber
                    # xyz coordinates and covalent radius of the system atom iat_sys
230 2 tkerber
                    r_sys = rs_sys[iat_sys]
231 2 tkerber
                    # diff vector
232 2 tkerber
                    xyz_diff = xyz_sys - xyz_cl
233 2 tkerber
                    # distance between the atoms
234 2 tkerber
                    r = np.sqrt(np.dot(xyz_diff, xyz_diff))
235 2 tkerber
                    # ratio of the distance to the sum of covalent radius
236 2 tkerber
                    f = r / (r_cl + r_sys)
237 2 tkerber
                    if f <= (1+tol/100.) and f >= (1-2*tol/100.):
238 3 tkerber
                        s = cell_L, self.atom_map_cl_sys[iat_cl], iat_sys, r_cl
239 2 tkerber
                        bonds.append(s)
240 2 tkerber
                        break
241 2 tkerber
                    if f <= (1-2*tol/100.):
242 2 tkerber
                        raise RuntimeError("QMX: The cluster atom", iat_cl, " and the system atom", iat_sys, "came too close")
243 3 tkerber
244 16 tkerber
        r_h = covalent_radii[atomic_numbers[linkType]]
245 2 tkerber
        for bond in bonds:
246 3 tkerber
            cell_L, iat_cl_sys, iat_sys, r_cl = bond
247 2 tkerber
            # assign the tags for the border atoms
248 2 tkerber
            atom_sys.set_tag(1)
249 2 tkerber
            atom_cl.set_tag(11)
250 2 tkerber
            #difference vector for the link atom, scaling
251 3 tkerber
            xyz_diff = xyzs_sys[iat_sys] + cell_L - xyzs_sys[iat_cl_sys]
252 2 tkerber
            r = (r_cl + r_h)
253 2 tkerber
            xyz_diff *=  r / np.sqrt(np.dot(xyz_diff, xyz_diff))
254 2 tkerber
            # determine position of the link atom
255 3 tkerber
            xyz_diff += xyzs_sys[iat_cl_sys]
256 2 tkerber
            # create link atom
257 16 tkerber
            atom = Atom(symbol=linkType, position=xyz_diff, tag=50)
258 2 tkerber
            # add atom to cluster
259 2 tkerber
            self.atoms_cluster.append(atom)
260 2 tkerber
            # add atom to the linkatoms
261 3 tkerber
            s = cell_L, iat_cl_sys, iat_sys, r, len(self.atoms_cluster)-1
262 2 tkerber
            self.linkatoms.append(s)
263 2 tkerber
        return
264 3 tkerber
265 2 tkerber
    def set_positions(self, positions_new):
266 2 tkerber
        # number of atoms
267 2 tkerber
        nat_sys=len(self.atoms_system)
268 2 tkerber
        # go over all pairs of atoms
269 2 tkerber
        for iat_sys in xrange(nat_sys):
270 2 tkerber
            xyz = positions_new[iat_sys]
271 5 tkerber
            self.atoms_system[iat_sys].set_position(xyz)
272 2 tkerber
            iat_cl = self.atom_map_sys_cl[iat_sys]
273 2 tkerber
            if iat_cl > -1:
274 2 tkerber
                self.atoms_cluster[iat_cl].set_position(xyz)
275 3 tkerber
276 3 tkerber
        for cell_L, iat_cl_sys, iat_sys, r, iat in self.linkatoms:
277 2 tkerber
            # determine position of the link atom
278 5 tkerber
            xyz_cl = positions_new[iat_cl_sys]
279 5 tkerber
            xyz = positions_new[iat_sys] - xyz_cl + cell_L
280 2 tkerber
            xyz *= r / np.sqrt(np.dot(xyz, xyz))
281 2 tkerber
            xyz += xyz_cl
282 2 tkerber
            # update xyz coordinates of the cluster
283 2 tkerber
            self.atoms_cluster[iat].set_position(xyz)
284 3 tkerber
285 2 tkerber
    def __getitem__(self, i):
286 2 tkerber
        return self.atoms_system.__getitem__(i)
287 2 tkerber
288 2 tkerber
    def get_positions(self):
289 2 tkerber
        return self.atoms_system.get_positions()
290 3 tkerber
291 2 tkerber
    def __add__(self, other):
292 2 tkerber
        return self.atoms_system.__add__(other)
293 3 tkerber
294 2 tkerber
    def __delitem__(self, i):
295 2 tkerber
        return self.atoms_system.__delitem__(i)
296 3 tkerber
297 2 tkerber
    def __len__(self):
298 2 tkerber
        return self.atoms_system.__len__()
299 3 tkerber
300 3 tkerber
    def get_chemical_symbols(self, reduce=False):
301 3 tkerber
        return self.atoms_system.get_chemical_symbols(reduce)