Coverage for /builds/kinetik161/ase/ase/build/ribbon.py: 63.75%

80 statements  

« prev     ^ index     » next       coverage.py v7.2.7, created at 2023-12-10 11:04 +0000

1from math import sqrt 

2 

3import numpy as np 

4 

5from ase.atoms import Atoms 

6 

7 

8def graphene_nanoribbon(n, m, type='zigzag', saturated=False, C_H=1.09, 

9 C_C=1.42, vacuum=None, magnetic=False, initial_mag=1.12, 

10 sheet=False, main_element='C', saturate_element='H'): 

11 """Create a graphene nanoribbon. 

12 

13 Creates a graphene nanoribbon in the x-z plane, with the nanoribbon 

14 running along the z axis. 

15 

16 Parameters: 

17 

18 n: int 

19 The width of the nanoribbon. For armchair nanoribbons, this 

20 n may be half-integer to repeat by half a cell. 

21 m: int 

22 The length of the nanoribbon. 

23 type: str 

24 The orientation of the ribbon. Must be either 'zigzag' 

25 or 'armchair'. 

26 saturated: bool 

27 If true, hydrogen atoms are placed along the edge. 

28 C_H: float 

29 Carbon-hydrogen bond length. Default: 1.09 Angstrom. 

30 C_C: float 

31 Carbon-carbon bond length. Default: 1.42 Angstrom. 

32 vacuum: None (default) or float 

33 Amount of vacuum added to non-periodic directions, if present. 

34 magnetic: bool 

35 Make the edges magnetic. 

36 initial_mag: float 

37 Magnitude of magnetic moment if magnetic. 

38 sheet: bool 

39 If true, make an infinite sheet instead of a ribbon (default: False) 

40 """ 

41 

42 if m % 1 != 0: 

43 raise ValueError('m must be integer') 

44 if type == 'zigzag' and n % 1 != 0: 

45 raise ValueError('n must be an integer for zigzag ribbons') 

46 

47 b = sqrt(3) * C_C / 4 

48 arm_unit = Atoms(main_element + '4', 

49 pbc=(1, 0, 1), 

50 cell=[4 * b, 0, 3 * C_C]) 

51 arm_unit.positions = [[0, 0, 0], 

52 [b * 2, 0, C_C / 2.], 

53 [b * 2, 0, 3 * C_C / 2.], 

54 [0, 0, 2 * C_C]] 

55 arm_unit_half = Atoms(main_element + '2', 

56 pbc=(1, 0, 1), 

57 cell=[2 * b, 0, 3 * C_C]) 

58 arm_unit_half.positions = [[b * 2, 0, C_C / 2.], 

59 [b * 2, 0, 3 * C_C / 2.]] 

60 zz_unit = Atoms(main_element + '2', 

61 pbc=(1, 0, 1), 

62 cell=[3 * C_C / 2.0, 0, b * 4]) 

63 zz_unit.positions = [[0, 0, 0], 

64 [C_C / 2.0, 0, b * 2]] 

65 atoms = Atoms() 

66 

67 if type == 'zigzag': 

68 edge_index0 = np.arange(m) * 2 

69 edge_index1 = (n - 1) * m * 2 + np.arange(m) * 2 + 1 

70 

71 if magnetic: 

72 mms = np.zeros(m * n * 2) 

73 for i in edge_index0: 

74 mms[i] = initial_mag 

75 for i in edge_index1: 

76 mms[i] = -initial_mag 

77 

78 for i in range(n): 

79 layer = zz_unit.repeat((1, 1, m)) 

80 layer.positions[:, 0] += 3 * C_C / 2 * i 

81 if i % 2 == 1: 

82 layer.positions[:, 2] += 2 * b 

83 layer[-1].position[2] -= b * 4 * m 

84 atoms += layer 

85 

86 xmin = atoms.positions[0, 0] 

87 

88 if magnetic: 

89 atoms.set_initial_magnetic_moments(mms) 

90 if saturated: 

91 H_atoms0 = Atoms(saturate_element + str(m)) 

92 H_atoms0.positions = atoms[edge_index0].positions 

93 H_atoms0.positions[:, 0] -= C_H 

94 H_atoms1 = Atoms(saturate_element + str(m)) 

95 H_atoms1.positions = atoms[edge_index1].positions 

96 H_atoms1.positions[:, 0] += C_H 

97 atoms += H_atoms0 + H_atoms1 

98 atoms.cell = [n * 3 * C_C / 2, 0, m * 4 * b] 

99 

100 elif type == 'armchair': 

101 n *= 2 

102 n_int = int(round(n)) 

103 if abs(n_int - n) > 1e-10: 

104 raise ValueError( 

105 'The argument n has to be half-integer for armchair ribbons.') 

106 n = n_int 

107 

108 for i in range(n // 2): 

109 layer = arm_unit.repeat((1, 1, m)) 

110 layer.positions[:, 0] -= 4 * b * i 

111 atoms += layer 

112 if n % 2: 

113 layer = arm_unit_half.repeat((1, 1, m)) 

114 layer.positions[:, 0] -= 4 * b * (n // 2) 

115 atoms += layer 

116 

117 xmin = atoms.positions[-1, 0] 

118 

119 if saturated: 

120 if n % 2: 

121 arm_right_saturation = Atoms(saturate_element + '2', 

122 pbc=(1, 0, 1), 

123 cell=[2 * b, 0, 3 * C_C]) 

124 arm_right_saturation.positions = [ 

125 [- sqrt(3) / 2 * C_H, 0, C_C / 2 - C_H * 0.5], 

126 [- sqrt(3) / 2 * C_H, 0, 3 * C_C / 2.0 + C_H * 0.5]] 

127 else: 

128 arm_right_saturation = Atoms(saturate_element + '2', 

129 pbc=(1, 0, 1), 

130 cell=[4 * b, 0, 3 * C_C]) 

131 arm_right_saturation.positions = [ 

132 [- sqrt(3) / 2 * C_H, 0, C_H * 0.5], 

133 [- sqrt(3) / 2 * C_H, 0, 2 * C_C - C_H * 0.5]] 

134 arm_left_saturation = Atoms(saturate_element + '2', pbc=(1, 0, 1), 

135 cell=[4 * b, 0, 3 * C_C]) 

136 arm_left_saturation.positions = [ 

137 [b * 2 + sqrt(3) / 2 * C_H, 0, C_C / 2 - C_H * 0.5], 

138 [b * 2 + sqrt(3) / 2 * C_H, 0, 3 * C_C / 2.0 + C_H * 0.5]] 

139 arm_right_saturation.positions[:, 0] -= 4 * b * (n / 2.0 - 1) 

140 

141 atoms += arm_right_saturation.repeat((1, 1, m)) 

142 atoms += arm_left_saturation.repeat((1, 1, m)) 

143 

144 atoms.cell = [b * 4 * n / 2.0, 0, 3 * C_C * m] 

145 

146 atoms.set_pbc([sheet, False, True]) 

147 

148 # The ribbon was 'built' from x=0 towards negative x. 

149 # Move the ribbon to positive x: 

150 atoms.positions[:, 0] -= xmin 

151 if not sheet: 

152 atoms.cell[0] = 0.0 

153 if vacuum is not None: 

154 atoms.center(vacuum, axis=1) 

155 if not sheet: 

156 atoms.center(vacuum, axis=0) 

157 return atoms