Source code for ase.build.ribbon

from math import sqrt

import numpy as np

from ase.atoms import Atoms


[docs] def graphene_nanoribbon(n, m, type='zigzag', saturated=False, C_H=1.09, C_C=1.42, vacuum=None, magnetic=False, initial_mag=1.12, sheet=False, main_element='C', saturate_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: int The width of the nanoribbon. For armchair nanoribbons, this n may be half-integer to repeat by half a cell. m: int The length of the nanoribbon. type: str The orientation of the ribbon. Must be either 'zigzag' or 'armchair'. saturated: bool If true, hydrogen atoms are placed along the edge. C_H: float Carbon-hydrogen bond length. Default: 1.09 Angstrom. C_C: float Carbon-carbon bond length. Default: 1.42 Angstrom. vacuum: None (default) or float Amount of vacuum added to non-periodic directions, if present. magnetic: bool Make the edges magnetic. initial_mag: float Magnitude of magnetic moment if magnetic. sheet: bool If true, make an infinite sheet instead of a ribbon (default: False) """ if m % 1 != 0: raise ValueError('m must be integer') if type == 'zigzag' and n % 1 != 0: raise ValueError('n must be an integer for zigzag ribbons') b = sqrt(3) * C_C / 4 arm_unit = Atoms(main_element + '4', pbc=(1, 0, 1), cell=[4 * b, 0, 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]] arm_unit_half = Atoms(main_element + '2', pbc=(1, 0, 1), cell=[2 * b, 0, 3 * C_C]) arm_unit_half.positions = [[b * 2, 0, C_C / 2.], [b * 2, 0, 3 * C_C / 2.]] zz_unit = Atoms(main_element + '2', pbc=(1, 0, 1), cell=[3 * C_C / 2.0, 0, b * 4]) zz_unit.positions = [[0, 0, 0], [C_C / 2.0, 0, b * 2]] atoms = Atoms() if type == 'zigzag': edge_index0 = np.arange(m) * 2 edge_index1 = (n - 1) * m * 2 + np.arange(m) * 2 + 1 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 xmin = atoms.positions[0, 0] if magnetic: atoms.set_initial_magnetic_moments(mms) if saturated: H_atoms0 = Atoms(saturate_element + str(m)) H_atoms0.positions = atoms[edge_index0].positions H_atoms0.positions[:, 0] -= C_H H_atoms1 = Atoms(saturate_element + str(m)) H_atoms1.positions = atoms[edge_index1].positions H_atoms1.positions[:, 0] += C_H atoms += H_atoms0 + H_atoms1 atoms.cell = [n * 3 * C_C / 2, 0, m * 4 * b] elif type == 'armchair': n *= 2 n_int = int(round(n)) if abs(n_int - n) > 1e-10: raise ValueError( 'The argument n has to be half-integer for armchair ribbons.') n = n_int for i in range(n // 2): layer = arm_unit.repeat((1, 1, m)) layer.positions[:, 0] -= 4 * b * i atoms += layer if n % 2: layer = arm_unit_half.repeat((1, 1, m)) layer.positions[:, 0] -= 4 * b * (n // 2) atoms += layer xmin = atoms.positions[-1, 0] if saturated: if n % 2: arm_right_saturation = Atoms(saturate_element + '2', pbc=(1, 0, 1), cell=[2 * b, 0, 3 * C_C]) arm_right_saturation.positions = [ [- sqrt(3) / 2 * C_H, 0, C_C / 2 - C_H * 0.5], [- sqrt(3) / 2 * C_H, 0, 3 * C_C / 2.0 + C_H * 0.5]] else: arm_right_saturation = Atoms(saturate_element + '2', pbc=(1, 0, 1), cell=[4 * b, 0, 3 * C_C]) arm_right_saturation.positions = [ [- sqrt(3) / 2 * C_H, 0, C_H * 0.5], [- sqrt(3) / 2 * C_H, 0, 2 * C_C - C_H * 0.5]] arm_left_saturation = Atoms(saturate_element + '2', pbc=(1, 0, 1), cell=[4 * b, 0, 3 * C_C]) arm_left_saturation.positions = [ [b * 2 + sqrt(3) / 2 * C_H, 0, C_C / 2 - C_H * 0.5], [b * 2 + sqrt(3) / 2 * C_H, 0, 3 * C_C / 2.0 + C_H * 0.5]] arm_right_saturation.positions[:, 0] -= 4 * b * (n / 2.0 - 1) atoms += arm_right_saturation.repeat((1, 1, m)) atoms += arm_left_saturation.repeat((1, 1, m)) atoms.cell = [b * 4 * n / 2.0, 0, 3 * C_C * m] atoms.set_pbc([sheet, False, True]) # The ribbon was 'built' from x=0 towards negative x. # Move the ribbon to positive x: atoms.positions[:, 0] -= xmin if not sheet: atoms.cell[0] = 0.0 if vacuum is not None: atoms.center(vacuum, axis=1) if not sheet: atoms.center(vacuum, axis=0) return atoms