def graphene_nanoribbon(n, m, side='zigzag', saturated="no", C_H=1.09, C_C=1.42, vacc=5., magnetic=None, initial_mag=1.12, sheet=False, main_element='C', satur_element='H'): """Create a graphene nanoribbon. Creates a graphene nanoribbon in the x-z plane, with the nanoribbon running along the z axis. Parameters: n: The width of the nanoribbon m: The length of the nanoribbon. side ('zigzag'): The orientation of the ribbon. Must be either 'zigzag' or 'armchair'. saturated (no/yes/side): If yes, hydrogen atoms are placed along the edges. If side, then only 2 sides are saturated. C_H: Carbon-hydrogen bond length. Default: 1.09 Angstrom C_C: Carbon-carbon bond length. Default: 1.42 Angstrom. vacc: Amount of vacuum. Default 5 Angstrom. magnetic: Make the edges magnetic. initial_mag: Magnitude of magnetic moment if magnetic=True. sheet: If true, make an infinite sheet instead of a ribbon. """ #This function creates the coordinates for a graphene nanoribbon, #n is width, m is length b = sqrt(3) * C_C / 4 arm_unit = Atoms(main_element + '4', pbc=(1, 0, 1), cell=[4 * b, vacc, 3 * C_C]) arm_unit.positions = [[0, 0, 0], [b * 2, 0, C_C / 2.], [b * 2, 0, 3 * C_C / 2.], [0, 0, 2 * C_C]] zz_unit = Atoms(main_element + '2', pbc=(1, 0, 1), cell=[3 * C_C / 2., vacc, b * 4]) zz_unit.positions = [[0, 0, 0], [C_C / 2., 0, b * 2]] atoms = Atoms() tol = 1e-4 if sheet: vacc2 = 0.0 else: vacc2 = vacc if side == 'zigzag': edge_index0 = np.arange(m) * 2 + 1 edge_index1 = (n - 1) * m * 2 + np.arange(m) * 2 edge_index2 = [] edge_index3 = [] for i in range(0, n, 2): edge_index2.append(i * m * 2) edge_index3.append((i + 1) * m * 2 - 1) if i > 1: edge_index2.append(i * m * 2 - 1) edge_index3.append(i * m * 2 - 2) edge_index2 = np.array(edge_index2) edge_index3 = np.array(edge_index3) if magnetic: mms = np.zeros(m * n * 2) for i in edge_index0: mms[i] = initial_mag for i in edge_index1: mms[i] = -initial_mag for i in range(n): layer = zz_unit.repeat((1, 1, m)) layer.positions[:, 0] -= 3 * C_C / 2 * i if i % 2 == 1: layer.positions[:, 2] += 2 * b layer[-1].position[2] -= b * 4 * m atoms += layer if magnetic: atoms.set_initial_magnetic_moments(mms) if saturated != "no": H_atoms0 = Atoms(satur_element + str(m)) H_atoms0.positions = atoms[edge_index0].positions H_atoms0.positions[:, 0] += C_H H_atoms1 = Atoms(satur_element + str(m)) H_atoms1.positions = atoms[edge_index1].positions H_atoms1.positions[:, 0] -= C_H H_atoms2 = Atoms(satur_element + str(n)) H_atoms2.positions = atoms[edge_index2].positions H_atoms2.positions[:, 2] -= C_H H_atoms3 = Atoms(satur_element + str(n)) H_atoms3.positions = atoms[edge_index3].positions H_atoms3.positions[:, 2] += C_H if saturated == "yes": atoms += H_atoms0 + H_atoms1 + H_atoms2 + H_atoms3 if saturated == "side": atoms += H_atoms0 + H_atoms1 atoms.cell = [n * 3 * C_C / 2 + vacc2, vacc + 5.0, m * 4 * b] elif side == 'armchair': for i in range(n): layer = arm_unit.repeat((1, 1, m)) layer.positions[:, 0] -= 4 * b * i atoms += layer atoms.cell = [b * 4 * n + vacc2, vacc, 3 * C_C * m] atoms.center() atoms.set_pbc([sheet, False, True]) return atoms
def nanotube(n, m, length=1, bond=1.42, symbol='C', verbose=False): if n < m: m, n = n, m nk = 6000 sq3 = sqrt(3.0) a = sq3 * bond l2 = n * n + m * m + n * m l = sqrt(l2) dt = a * l / np.pi nd = gcd(n, m) if (n - m) % (3 * nd) == 0: ndr = 3 * nd else: ndr = nd nr = (2 * m + n) / ndr ns = -(2 * n + m) / ndr nt2 = 3 * l2 / ndr / ndr nt = np.floor(sqrt(nt2)) nn = 2 * l2 / ndr ichk = 0 if nr == 0: n60 = 1 else: n60 = nr * 4 absn = abs(n60) nnp = [] nnq = [] for i in range(-absn, absn + 1): for j in range(-absn, absn + 1): j2 = nr * j - ns * i if j2 == 1: j1 = m * i - n * j if j1 > 0 and j1 < nn: ichk += 1 nnp.append(i) nnq.append(j) if ichk == 0: raise RuntimeError('not found p, q strange!!') if ichk >= 2: raise RuntimeError('more than 1 pair p, q strange!!') nnnp = nnp[0] nnnq = nnq[0] if verbose: print('the symmetry vector is', nnnp, nnnq) lp = nnnp * nnnp + nnnq * nnnq + nnnp * nnnq r = a * sqrt(lp) c = a * l t = sq3 * c / ndr if 2 * nn > nk: raise RuntimeError('parameter nk is too small!') rs = c / (2.0 * np.pi) if verbose: print('radius=', rs, t) q1 = np.arctan((sq3 * m) / (2 * n + m)) q2 = np.arctan((sq3 * nnnq) / (2 * nnnp + nnnq)) q3 = q1 - q2 q4 = 2.0 * np.pi / nn q5 = bond * np.cos((np.pi / 6.0) - q1) / c * 2.0 * np.pi h1 = abs(t) / abs(np.sin(q3)) h2 = bond * np.sin((np.pi / 6.0) - q1) ii = 0 x, y, z = [], [], [] for i in range(nn): x1, y1, z1 = 0, 0, 0 k = np.floor(i * abs(r) / h1) x1 = rs * np.cos(i * q4) y1 = rs * np.sin(i * q4) z1 = (i * abs(r) - k * h1) * np.sin(q3) kk2 = abs(np.floor((z1 + 0.0001) / t)) if z1 >= t - 0.0001: z1 -= t * kk2 elif z1 < 0: z1 += t * kk2 ii += 1 x.append(x1) y.append(y1) z.append(z1) z3 = (i * abs(r) - k * h1) * np.sin(q3) - h2 ii += 1 if z3 >= 0 and z3 < t: x2 = rs * np.cos(i * q4 + q5) y2 = rs * np.sin(i * q4 + q5) z2 = (i * abs(r) - k * h1) * np.sin(q3) - h2 x.append(x2) y.append(y2) z.append(z2) else: x2 = rs * np.cos(i * q4 + q5) y2 = rs * np.sin(i * q4 + q5) z2 = (i * abs(r) - (k + 1) * h1) * np.sin(q3) - h2 kk = abs(np.floor(z2 / t)) if z2 >= t - 0.0001: z2 -= t * kk elif z2 < 0: z2 += t * kk x.append(x2) y.append(y2) z.append(z2) ntotal = 2 * nn X = [] for i in range(ntotal): X.append([x[i], y[i], z[i]]) if length > 1: xx = X[:] for mnp in range(2, length + 1): for i in range(len(xx)): X.append(xx[i][:2] + [xx[i][2] + (mnp - 1) * t]) TransVec = t NumAtom = ntotal * length Diameter = rs * 2 ChiralAngle = np.arctan((sq3 * n) / (2 * m + n)) / (np.pi * 180) cell = [Diameter * 2, Diameter * 2, length * t] atoms = Atoms(symbol + str(NumAtom), positions=X, cell=cell, pbc=[False, False, True]) atoms.center() if verbose: print('translation vector =', TransVec) print('diameter = ', Diameter) print('chiral angle = ', ChiralAngle) return atoms
def surface(symbol, structure, face, size, a, c, vacuum, orthogonal=True): """Function to build often used surfaces. Don't call this function directly - use fcc100, fcc110, bcc111, ...""" Z = atomic_numbers[symbol] if a is None: sym = reference_states[Z]['symmetry'].lower() if sym != structure: raise ValueError("Can't guess lattice constant for %s-%s!" % (structure, symbol)) a = reference_states[Z]['a'] if structure == 'hcp' and c is None: if reference_states[Z]['symmetry'].lower() == 'hcp': c = reference_states[Z]['c/a'] * a else: c = sqrt(8 / 3.0) * a positions = np.empty((size[2], size[1], size[0], 3)) positions[..., 0] = np.arange(size[0]).reshape((1, 1, -1)) positions[..., 1] = np.arange(size[1]).reshape((1, -1, 1)) positions[..., 2] = np.arange(size[2]).reshape((-1, 1, 1)) numbers = np.ones(size[0] * size[1] * size[2], int) * Z tags = np.empty((size[2], size[1], size[0]), int) tags[:] = np.arange(size[2], 0, -1).reshape((-1, 1, 1)) slab = Atoms(numbers, tags=tags.ravel(), pbc=(True, True, False), cell=size) surface_cell = None sites = {'ontop': (0, 0)} surf = structure + face if surf == 'fcc100': cell = (sqrt(0.5), sqrt(0.5), 0.5) positions[-2::-2, ..., :2] += 0.5 sites.update({'hollow': (0.5, 0.5), 'bridge': (0.5, 0)}) elif surf == 'fcc110': cell = (1.0, sqrt(0.5), sqrt(0.125)) positions[-2::-2, ..., :2] += 0.5 sites.update({'hollow': (0.5, 0.5), 'longbridge': (0.5, 0), 'shortbridge': (0, 0.5)}) elif surf == 'bcc100': cell = (1.0, 1.0, 0.5) positions[-2::-2, ..., :2] += 0.5 sites.update({'hollow': (0.5, 0.5), 'bridge': (0.5, 0)}) else: if orthogonal and size[1] % 2 == 1: raise ValueError(("Can't make orthorhombic cell with size=%r. " % (tuple(size),)) + 'Second number in size must be even.') if surf == 'fcc111': cell = (sqrt(0.5), sqrt(0.375), 1 / sqrt(3)) if orthogonal: positions[-1::-3, 1::2, :, 0] += 0.5 positions[-2::-3, 1::2, :, 0] += 0.5 positions[-3::-3, 1::2, :, 0] -= 0.5 positions[-2::-3, ..., :2] += (0.0, 2.0 / 3) positions[-3::-3, ..., :2] += (0.5, 1.0 / 3) else: positions[-2::-3, ..., :2] += (-1.0 / 3, 2.0 / 3) positions[-3::-3, ..., :2] += (1.0 / 3, 1.0 / 3) sites.update({'bridge': (0.5, 0), 'fcc': (1.0 / 3, 1.0 / 3), 'hcp': (2.0 / 3, 2.0 / 3)}) elif surf == 'hcp0001': cell = (1.0, sqrt(0.75), 0.5 * c / a) if orthogonal: positions[:, 1::2, :, 0] += 0.5 positions[-2::-2, ..., :2] += (0.0, 2.0 / 3) else: positions[-2::-2, ..., :2] += (-1.0 / 3, 2.0 / 3) sites.update({'bridge': (0.5, 0), 'fcc': (1.0 / 3, 1.0 / 3), 'hcp': (2.0 / 3, 2.0 / 3)}) elif surf == 'bcc110': cell = (1.0, sqrt(0.5), sqrt(0.5)) if orthogonal: positions[:, 1::2, :, 0] += 0.5 positions[-2::-2, ..., :2] += (0.0, 1.0) else: positions[-2::-2, ..., :2] += (-0.5, 1.0) sites.update({'shortbridge': (0, 0.5), 'longbridge': (0.5, 0), 'hollow': (0.375, 0.25)}) elif surf == 'bcc111': cell = (sqrt(2), sqrt(1.5), sqrt(3) / 6) if orthogonal: positions[-1::-3, 1::2, :, 0] += 0.5 positions[-2::-3, 1::2, :, 0] += 0.5 positions[-3::-3, 1::2, :, 0] -= 0.5 positions[-2::-3, ..., :2] += (0.0, 2.0 / 3) positions[-3::-3, ..., :2] += (0.5, 1.0 / 3) else: positions[-2::-3, ..., :2] += (-1.0 / 3, 2.0 / 3) positions[-3::-3, ..., :2] += (1.0 / 3, 1.0 / 3) sites.update({'hollow': (1.0 / 3, 1.0 / 3)}) surface_cell = a * np.array([(cell[0], 0), (cell[0] / 2, cell[1])]) if not orthogonal: cell = np.array([(cell[0], 0, 0), (cell[0] / 2, cell[1], 0), (0, 0, cell[2])]) if surface_cell is None: surface_cell = a * np.diag(cell[:2]) if isinstance(cell, tuple): cell = np.diag(cell) slab.set_positions(positions.reshape((-1, 3))) slab.set_cell([a * v * n for v, n in zip(cell, size)], scale_atoms=True) if vacuum is not None: slab.center(vacuum=vacuum, axis=2) slab.adsorbate_info['cell'] = surface_cell slab.adsorbate_info['sites'] = sites return slab