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) |