Coverage for /builds/ase/ase/ase/build/tube.py: 75.63%
119 statements
« prev ^ index » next coverage.py v7.5.3, created at 2025-08-02 00:12 +0000
« prev ^ index » next coverage.py v7.5.3, created at 2025-08-02 00:12 +0000
1# fmt: off
3from math import gcd, sqrt
5import numpy as np
7from ase.atoms import Atoms
10def nanotube(n, m, length=1, bond=1.42, symbol='C', verbose=False,
11 vacuum=None):
12 """Create an atomic structure.
14 Creates a single-walled nanotube whose structure is specified using the
15 standardized (n, m) notation.
17 Parameters
18 ----------
19 n : int
20 n in the (n, m) notation.
21 m : int
22 m in the (n, m) notation.
23 length : int, optional
24 Length (axial repetitions) of the nanotube.
25 bond : float, optional
26 Bond length between neighboring atoms.
27 symbol : str, optional
28 Chemical element to construct the nanotube from.
29 verbose : bool, optional
30 If True, will display key geometric parameters.
32 Returns
33 -------
34 ase.atoms.Atoms
35 An ASE Atoms object corresponding to the specified molecule.
37 Examples
38 --------
39 >>> from ase.build import nanotube
40 >>> atoms1 = nanotube(6, 0, length=4)
41 >>> atoms2 = nanotube(3, 3, length=6, bond=1.4, symbol='Si')
42 """
43 if n < m:
44 m, n = n, m
45 sign = -1
46 else:
47 sign = 1
49 nk = 6000
50 sq3 = sqrt(3.0)
51 a = sq3 * bond
52 l2 = n * n + m * m + n * m
53 l1 = sqrt(l2)
55 nd = gcd(n, m)
56 if (n - m) % (3 * nd) == 0:
57 ndr = 3 * nd
58 else:
59 ndr = nd
61 nr = (2 * m + n) // ndr
62 ns = -(2 * n + m) // ndr
63 nn = 2 * l2 // ndr
65 ichk = 0
66 if nr == 0:
67 n60 = 1
68 else:
69 n60 = nr * 4
71 absn = abs(n60)
72 nnp = []
73 nnq = []
74 for i in range(-absn, absn + 1):
75 for j in range(-absn, absn + 1):
76 j2 = nr * j - ns * i
77 if j2 == 1:
78 j1 = m * i - n * j
79 if j1 > 0 and j1 < nn:
80 ichk += 1
81 nnp.append(i)
82 nnq.append(j)
84 if ichk == 0:
85 raise RuntimeError('not found p, q strange!!')
86 if ichk >= 2:
87 raise RuntimeError('more than 1 pair p, q strange!!')
89 nnnp = nnp[0]
90 nnnq = nnq[0]
92 if verbose:
93 print('the symmetry vector is', nnnp, nnnq)
95 lp = nnnp * nnnp + nnnq * nnnq + nnnp * nnnq
96 r = a * sqrt(lp)
97 c = a * l1
98 t = sq3 * c / ndr
100 if 2 * nn > nk:
101 raise RuntimeError('parameter nk is too small!')
103 rs = c / (2.0 * np.pi)
105 if verbose:
106 print('radius=', rs, t)
108 q1 = np.arctan((sq3 * m) / (2 * n + m))
109 q2 = np.arctan((sq3 * nnnq) / (2 * nnnp + nnnq))
110 q3 = q1 - q2
112 q4 = 2.0 * np.pi / nn
113 q5 = bond * np.cos((np.pi / 6.0) - q1) / c * 2.0 * np.pi
115 h1 = abs(t) / abs(np.sin(q3))
116 h2 = bond * np.sin((np.pi / 6.0) - q1)
118 ii = 0
119 x, y, z = [], [], []
120 for i in range(nn):
121 x1, y1, z1 = 0, 0, 0
123 k = np.floor(i * abs(r) / h1)
124 x1 = rs * np.cos(i * q4)
125 y1 = rs * np.sin(i * q4)
126 z1 = (i * abs(r) - k * h1) * np.sin(q3)
127 kk2 = abs(np.floor((z1 + 0.0001) / t))
128 if z1 >= t - 0.0001:
129 z1 -= t * kk2
130 elif z1 < 0:
131 z1 += t * kk2
132 ii += 1
134 x.append(x1)
135 y.append(y1)
136 z.append(z1)
137 z3 = (i * abs(r) - k * h1) * np.sin(q3) - h2
138 ii += 1
140 if z3 >= 0 and z3 < t:
141 x2 = rs * np.cos(i * q4 + q5)
142 y2 = rs * np.sin(i * q4 + q5)
143 z2 = (i * abs(r) - k * h1) * np.sin(q3) - h2
144 x.append(x2)
145 y.append(y2)
146 z.append(z2)
147 else:
148 x2 = rs * np.cos(i * q4 + q5)
149 y2 = rs * np.sin(i * q4 + q5)
150 z2 = (i * abs(r) - (k + 1) * h1) * np.sin(q3) - h2
151 kk = abs(np.floor(z2 / t))
152 if z2 >= t - 0.0001:
153 z2 -= t * kk
154 elif z2 < 0:
155 z2 += t * kk
156 x.append(x2)
157 y.append(y2)
158 z.append(z2)
160 ntotal = 2 * nn
161 X = []
162 for i in range(ntotal):
163 X.append([x[i], y[i], sign * z[i]])
165 if length > 1:
166 xx = X[:]
167 for mnp in range(2, length + 1):
168 for i in range(len(xx)):
169 X.append(xx[i][:2] + [xx[i][2] + (mnp - 1) * t])
171 transvec = t
172 numatom = ntotal * length
173 diameter = rs * 2
174 chiralangle = np.arctan((sq3 * n) / (2 * m + n)) / np.pi * 180
176 cell = [[0, 0, 0], [0, 0, 0], [0, 0, length * t]]
177 atoms = Atoms(symbol + str(numatom),
178 positions=X,
179 cell=cell,
180 pbc=[False, False, True])
181 if vacuum:
182 atoms.center(vacuum, axis=(0, 1))
183 if verbose:
184 print('translation vector =', transvec)
185 print('diameter = ', diameter)
186 print('chiral angle = ', chiralangle)
187 return atoms