root / ase / structure.py @ 15
Historique | Voir | Annoter | Télécharger (12,22 ko)
1 |
"""Atomic structure.
|
---|---|
2 |
|
3 |
This mudule contains helper functions for setting up nanotubes,
|
4 |
graphene nanoribbons and simple bulk crystals."""
|
5 |
|
6 |
from math import sqrt |
7 |
|
8 |
import numpy as np |
9 |
|
10 |
from ase.atoms import Atoms, string2symbols |
11 |
from ase.data import covalent_radii |
12 |
|
13 |
|
14 |
def gcd(m,n): |
15 |
while n:
|
16 |
m,n=n,m%n |
17 |
return m
|
18 |
|
19 |
def nanotube(n, m, length=1, bond=1.42, symbol='C', verbose=False): |
20 |
if n < m:
|
21 |
m, n = n, m |
22 |
nk = 6000
|
23 |
sq3 = sqrt(3.0)
|
24 |
a = sq3 * bond |
25 |
l2 = n * n + m * m + n * m |
26 |
l = sqrt(l2) |
27 |
dt = a * l / np.pi |
28 |
|
29 |
nd = gcd(n ,m) |
30 |
if (n - m) % (3 * nd ) == 0: |
31 |
ndr = 3 * nd
|
32 |
else:
|
33 |
ndr = nd |
34 |
|
35 |
nr = (2 * m + n) / ndr
|
36 |
ns = -(2 * n + m) / ndr
|
37 |
nt2 = 3 * l2 / ndr / ndr
|
38 |
nt = np.floor(sqrt(nt2)) |
39 |
nn = 2 * l2 / ndr
|
40 |
|
41 |
ichk = 0
|
42 |
if nr == 0: |
43 |
n60 = 1
|
44 |
else:
|
45 |
n60 = nr * 4
|
46 |
|
47 |
absn = abs(n60)
|
48 |
nnp = [] |
49 |
nnq = [] |
50 |
for i in range(-absn, absn + 1): |
51 |
for j in range(-absn, absn + 1): |
52 |
j2 = nr * j - ns * i |
53 |
if j2 == 1: |
54 |
j1 = m * i - n * j |
55 |
if j1 > 0 and j1 < nn: |
56 |
ichk += 1
|
57 |
nnp.append(i) |
58 |
nnq.append(j) |
59 |
|
60 |
if ichk == 0: |
61 |
raise RuntimeError('not found p, q strange!!') |
62 |
if ichk >= 2: |
63 |
raise RuntimeError('more than 1 pair p, q strange!!') |
64 |
|
65 |
nnnp = nnp[0]
|
66 |
nnnq = nnq[0]
|
67 |
|
68 |
if verbose:
|
69 |
print 'the symmetry vector is', nnnp, nnnq |
70 |
|
71 |
lp = nnnp * nnnp + nnnq * nnnq + nnnp * nnnq |
72 |
r = a * sqrt(lp) |
73 |
c = a * l |
74 |
t = sq3 * c / ndr |
75 |
|
76 |
if 2 * nn > nk: |
77 |
raise RuntimeError('parameter nk is too small!') |
78 |
|
79 |
rs = c / (2.0 * np.pi)
|
80 |
|
81 |
if verbose:
|
82 |
print 'radius=', rs, t |
83 |
|
84 |
q1 = np.arctan((sq3 * m) / (2 * n + m))
|
85 |
q2 = np.arctan((sq3 * nnnq) / (2 * nnnp + nnnq))
|
86 |
q3 = q1 - q2 |
87 |
|
88 |
q4 = 2.0 * np.pi / nn
|
89 |
q5 = bond * np.cos((np.pi / 6.0) - q1) / c * 2.0 * np.pi |
90 |
|
91 |
h1 = abs(t) / abs(np.sin(q3)) |
92 |
h2 = bond * np.sin((np.pi / 6.0) - q1)
|
93 |
|
94 |
ii = 0
|
95 |
x, y, z = [], [], [] |
96 |
for i in range(nn): |
97 |
x1, y1, z1 = 0, 0, 0 |
98 |
|
99 |
k = np.floor(i * abs(r) / h1)
|
100 |
x1 = rs * np.cos(i * q4) |
101 |
y1 = rs * np.sin(i * q4) |
102 |
z1 = (i * abs(r) - k * h1) * np.sin(q3)
|
103 |
kk2 = abs(np.floor((z1 + 0.0001) / t)) |
104 |
if z1 >= t - 0.0001: |
105 |
z1 -= t * kk2 |
106 |
elif z1 < 0: |
107 |
z1 += t * kk2 |
108 |
ii += 1
|
109 |
|
110 |
x.append(x1) |
111 |
y.append(y1) |
112 |
z.append(z1) |
113 |
z3 = (i * abs(r) - k * h1) * np.sin(q3) - h2
|
114 |
ii += 1
|
115 |
|
116 |
if z3 >= 0 and z3 < t: |
117 |
x2 = rs * np.cos(i * q4 + q5) |
118 |
y2 = rs * np.sin(i * q4 + q5) |
119 |
z2 = (i * abs(r) - k * h1) * np.sin(q3) - h2
|
120 |
x.append(x2) |
121 |
y.append(y2) |
122 |
z.append(z2) |
123 |
else:
|
124 |
x2 = rs * np.cos(i * q4 + q5) |
125 |
y2 = rs * np.sin(i * q4 + q5) |
126 |
z2 = (i * abs(r) - (k + 1) * h1) * np.sin(q3) - h2 |
127 |
kk = abs(np.floor(z2 / t))
|
128 |
if z2 >= t - 0.0001: |
129 |
z2 -= t * kk |
130 |
elif z2 < 0: |
131 |
z2 += t * kk |
132 |
x.append(x2) |
133 |
y.append(y2) |
134 |
z.append(z2) |
135 |
|
136 |
ntotal = 2 * nn
|
137 |
X = [] |
138 |
for i in range(ntotal): |
139 |
X.append([x[i], y[i], z[i]]) |
140 |
|
141 |
if length > 1: |
142 |
xx = X[:] |
143 |
for mnp in range(2, length + 1): |
144 |
for i in range(len(xx)): |
145 |
X.append(xx[i][:2] + [xx[i][2] + (mnp - 1) * t]) |
146 |
|
147 |
TransVec = t |
148 |
NumAtom = ntotal * length |
149 |
Diameter = rs * 2
|
150 |
ChiralAngle = np.arctan((sq3 * n) / (2 * m + n)) / (np.pi * 180) |
151 |
|
152 |
cell = [Diameter * 2, Diameter * 2, length * t] |
153 |
atoms = Atoms(symbol + str(NumAtom), positions=X, cell=cell,
|
154 |
pbc=[False, False, True]) |
155 |
atoms.center() |
156 |
if verbose:
|
157 |
print 'translation vector =', TransVec |
158 |
print 'diameter = ', Diameter |
159 |
print 'chiral angle = ', ChiralAngle |
160 |
return atoms
|
161 |
|
162 |
def graphene_nanoribbon(n, m, type='zigzag', saturated=False, C_H=1.09, |
163 |
C_C=1.42, vacc=5., magnetic=None, initial_mag=1.12, |
164 |
sheet=False, main_element='C', satur_element='H'): |
165 |
"""Create a graphene nanoribbon.
|
166 |
|
167 |
Creates a graphene nanoribbon in the x-z plane, with the nanoribbon
|
168 |
running along the z axis.
|
169 |
|
170 |
Parameters:
|
171 |
|
172 |
n: The width of the nanoribbon
|
173 |
|
174 |
m: The length of the nanoribbon.
|
175 |
|
176 |
type ('zigzag'): The orientation of the ribbon. Must be either 'zigzag'
|
177 |
or 'armchair'.
|
178 |
|
179 |
saturated (Falsi): If true, hydrogen atoms are placed along the edge.
|
180 |
|
181 |
C_H: Carbon-hydrogen bond length. Default: 1.09 Angstrom
|
182 |
|
183 |
C_C: Carbon-carbon bond length. Default: 1.42 Angstrom.
|
184 |
|
185 |
vacc: Amount of vacuum. Default 5 Angstrom.
|
186 |
|
187 |
magnetic: Make the edges magnetic.
|
188 |
|
189 |
initial_mag: Magnitude of magnetic moment if magnetic=True.
|
190 |
|
191 |
sheet: If true, make an infinite sheet instead of a ribbon.
|
192 |
"""
|
193 |
#This function creates the coordinates for a graphene nanoribbon,
|
194 |
#n is width, m is length
|
195 |
|
196 |
b = sqrt(3) * C_C / 4 |
197 |
arm_unit = Atoms(main_element+'4', pbc=(1,0,1), |
198 |
cell = [4 * b, vacc, 3 * C_C]) |
199 |
arm_unit.positions = [[0, 0, 0], |
200 |
[b * 2, 0, C_C / 2.], |
201 |
[b * 2, 0, 3 * C_C / 2.], |
202 |
[0, 0, 2 * C_C]] |
203 |
zz_unit = Atoms(main_element+'2', pbc=(1,0,1), |
204 |
cell = [3 * C_C /2., vacc, b * 4]) |
205 |
zz_unit.positions = [[0, 0, 0], |
206 |
[C_C / 2., 0, b * 2]] |
207 |
atoms = Atoms() |
208 |
tol = 1e-4
|
209 |
if sheet:
|
210 |
vacc2 = 0.0
|
211 |
else:
|
212 |
vacc2 = vacc |
213 |
if type == 'zigzag': |
214 |
edge_index0 = np.arange(m) * 2 + 1 |
215 |
edge_index1 = (n - 1) * m * 2 + np.arange(m) * 2 |
216 |
if magnetic:
|
217 |
mms = np.zeros(m * n * 2)
|
218 |
for i in edge_index0: |
219 |
mms[i] = initial_mag |
220 |
for i in edge_index1: |
221 |
mms[i] = -initial_mag |
222 |
|
223 |
for i in range(n): |
224 |
layer = zz_unit.repeat((1, 1, m)) |
225 |
layer.positions[:, 0] -= 3 * C_C / 2 * i |
226 |
if i % 2 == 1: |
227 |
layer.positions[:, 2] += 2 * b |
228 |
layer[-1].position[2] -= b * 4 * m |
229 |
atoms += layer |
230 |
if magnetic:
|
231 |
atoms.set_initial_magnetic_moments(mms) |
232 |
if saturated:
|
233 |
H_atoms0 = Atoms(satur_element + str(m))
|
234 |
H_atoms0.positions = atoms[edge_index0].positions |
235 |
H_atoms0.positions[:, 0] += C_H
|
236 |
H_atoms1 = Atoms(satur_element + str(m))
|
237 |
H_atoms1.positions = atoms[edge_index1].positions |
238 |
H_atoms1.positions[:, 0] -= C_H
|
239 |
atoms += H_atoms0 + H_atoms1 |
240 |
atoms.cell = [n * 3 * C_C / 2 + vacc2, vacc, m * 4 * b] |
241 |
|
242 |
elif type == 'armchair': |
243 |
for i in range(n): |
244 |
layer = arm_unit.repeat((1, 1, m)) |
245 |
layer.positions[:, 0] -= 4 * b * i |
246 |
atoms += layer |
247 |
atoms.cell = [b * 4 * n + vacc2, vacc, 3 * C_C * m] |
248 |
atoms.center() |
249 |
atoms.set_pbc([sheet, False, True]) |
250 |
return atoms
|
251 |
|
252 |
|
253 |
def bulk(name, crystalstructure, a=None, c=None, covera=None, |
254 |
orthorhombic=False, cubic=False): |
255 |
"""Helper function for creating bulk systems.
|
256 |
|
257 |
name: str
|
258 |
Chemical symbol or symbols as in 'MgO' or 'NaCl'.
|
259 |
crystalstructure: str
|
260 |
Must be one of sc, fcc, bcc, hcp, diamond, zincblende or
|
261 |
rocksalt.
|
262 |
a: float
|
263 |
Lattice constant.
|
264 |
c: float
|
265 |
Lattice constant.
|
266 |
covera: float
|
267 |
c/a raitio used for hcp. Defaults to ideal ratio.
|
268 |
orthorhombic: bool
|
269 |
Construct orthorhombic unit cell instead of primitive cell
|
270 |
which is the default.
|
271 |
cubic: bool
|
272 |
Construct cubic unit cell.
|
273 |
"""
|
274 |
|
275 |
if covera is not None and c is not None: |
276 |
raise ValueError("Don't specify both c and c/a!") |
277 |
|
278 |
if covera is None and c is None: |
279 |
covera = sqrt(8.0 / 3.0) |
280 |
|
281 |
if a is None: |
282 |
a = estimate_lattice_constant(name, crystalstructure, covera) |
283 |
|
284 |
if covera is None and c is not None: |
285 |
covera = c / a |
286 |
|
287 |
x = crystalstructure.lower() |
288 |
|
289 |
if orthorhombic and x != 'sc': |
290 |
return _orthorhombic_bulk(name, x, a, covera)
|
291 |
|
292 |
if cubic and x == 'bcc': |
293 |
return _orthorhombic_bulk(name, x, a, covera)
|
294 |
|
295 |
if cubic and x != 'sc': |
296 |
return _cubic_bulk(name, x, a)
|
297 |
|
298 |
if x == 'sc': |
299 |
atoms = Atoms(name, cell=(a, a, a), pbc=True)
|
300 |
elif x == 'fcc': |
301 |
b = a / 2
|
302 |
atoms = Atoms(name, cell=[(0, b, b), (b, 0, b), (b, b, 0)], pbc=True) |
303 |
elif x == 'bcc': |
304 |
b = a / 2
|
305 |
atoms = Atoms(name, cell=[(-b, b, b), (b, -b, b), (b, b, -b)], |
306 |
pbc=True)
|
307 |
elif x == 'hcp': |
308 |
atoms = Atoms(2 * name,
|
309 |
scaled_positions=[(0, 0, 0), |
310 |
(1.0 / 3.0, 1.0 / 3.0, 0.5)], |
311 |
cell=[(a, 0, 0), |
312 |
(a / 2, a * sqrt(3) / 2, 0), |
313 |
(0, 0, covera * a)], |
314 |
pbc=True)
|
315 |
elif x == 'diamond': |
316 |
atoms = bulk(2 * name, 'zincblende', a) |
317 |
elif x == 'zincblende': |
318 |
s1, s2 = string2symbols(name) |
319 |
atoms = bulk(s1, 'fcc', a) + bulk(s2, 'fcc', a) |
320 |
atoms.positions[1] += a / 4 |
321 |
elif x == 'rocksalt': |
322 |
s1, s2 = string2symbols(name) |
323 |
atoms = bulk(s1, 'fcc', a) + bulk(s2, 'fcc', a) |
324 |
atoms.positions[1, 0] += a / 2 |
325 |
else:
|
326 |
raise ValueError('Unknown crystal structure: ' + crystalstructure) |
327 |
|
328 |
return atoms
|
329 |
|
330 |
def estimate_lattice_constant(name, crystalstructure, covera): |
331 |
atoms = bulk(name, crystalstructure, 1.0, covera)
|
332 |
v0 = atoms.get_volume() |
333 |
v = 0.0
|
334 |
for Z in atoms.get_atomic_numbers(): |
335 |
r = covalent_radii[Z] |
336 |
v += 4 * np.pi / 3 * r**3 * 1.5 |
337 |
return (v / v0)**(1.0 / 3) |
338 |
|
339 |
def _orthorhombic_bulk(name, x, a, covera=None): |
340 |
if x == 'fcc': |
341 |
b = a / sqrt(2)
|
342 |
atoms = Atoms(2 * name, cell=(b, b, a), pbc=True, |
343 |
scaled_positions=[(0, 0, 0), (0.5, 0.5, 0.5)]) |
344 |
elif x == 'bcc': |
345 |
atoms = Atoms(2 * name, cell=(a, a, a), pbc=True, |
346 |
scaled_positions=[(0, 0, 0), (0.5, 0.5, 0.5)]) |
347 |
elif x == 'hcp': |
348 |
atoms = Atoms(4 * name,
|
349 |
cell=(a, a * sqrt(3), covera * a),
|
350 |
scaled_positions=[(0, 0, 0), |
351 |
(0.5, 0.5, 0), |
352 |
(0.5, 1.0 / 6.0, 0.5), |
353 |
(0, 2.0 / 3.0, 0.5)], |
354 |
pbc=True)
|
355 |
elif x == 'diamond': |
356 |
atoms = _orthorhombic_bulk(2 * name, 'zincblende', a) |
357 |
elif x == 'zincblende': |
358 |
s1, s2 = string2symbols(name) |
359 |
b = a / sqrt(2)
|
360 |
atoms = Atoms(2 * name, cell=(b, b, a), pbc=True, |
361 |
scaled_positions=[(0, 0, 0), (0.5, 0, 0.25), |
362 |
(0.5, 0.5, 0.5), (0, 0.5, 0.75)]) |
363 |
elif x == 'rocksalt': |
364 |
s1, s2 = string2symbols(name) |
365 |
b = a / sqrt(2)
|
366 |
atoms = Atoms(2 * name, cell=(b, b, a), pbc=True, |
367 |
scaled_positions=[(0, 0, 0), (0.5, 0.5, 0), |
368 |
(0.5, 0.5, 0.5), (0, 0, 0.5)]) |
369 |
else:
|
370 |
raise RuntimeError |
371 |
|
372 |
return atoms
|
373 |
|
374 |
def _cubic_bulk(name, x, a): |
375 |
if x == 'fcc': |
376 |
atoms = Atoms(4 * name, cell=(a, a, a), pbc=True, |
377 |
scaled_positions=[(0, 0, 0), (0, 0.5, 0.5), |
378 |
(0.5, 0, 0.5), (0.5, 0.5, 0)]) |
379 |
elif x == 'diamond': |
380 |
atoms = _cubic_bulk(2 * name, 'zincblende', a) |
381 |
elif x == 'zincblende': |
382 |
atoms = Atoms(4 * name, cell=(a, a, a), pbc=True, |
383 |
scaled_positions=[(0, 0, 0), (0.25, 0.25, 0.25), |
384 |
(0, 0.5, 0.5), (0.25, 0.75, 0.75), |
385 |
(0.5, 0, 0.5), (0.75, 0.25, 0.75), |
386 |
(0.5, 0.5, 0), (0.75, 0.75, 0.25)]) |
387 |
elif x == 'rocksalt': |
388 |
atoms = Atoms(4 * name, cell=(a, a, a), pbc=True, |
389 |
scaled_positions=[(0, 0, 0), (0.5, 0, 0), |
390 |
(0, 0.5, 0.5), (0.5, 0.5, 0.5), |
391 |
(0.5, 0, 0.5), (0, 0, 0.5), |
392 |
(0.5, 0.5, 0), (0, 0.5, 0)]) |
393 |
else:
|
394 |
raise RuntimeError |
395 |
|
396 |
return atoms
|