Statistiques
| Révision :

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