Hide keyboard shortcuts

Hot-keys on this page

r m x p   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

1# Note: 

2# Try to avoid module level import statements here to reduce 

3# import time during CLI execution 

4import sys 

5import numpy as np 

6 

7 

8class CLICommand: 

9 """Build an atom, molecule or bulk structure. 

10 

11 Atom: 

12 

13 ase build <chemical symbol> ... 

14 

15 Molecule: 

16 

17 ase build <formula> ... 

18 

19 where <formula> must be one of the formulas known to ASE 

20 (see here: https://wiki.fysik.dtu.dk/ase/ase/build/build.html#molecules). 

21 

22 Bulk: 

23 

24 ase build -x <crystal structure> <formula> ... 

25 

26 Examples: 

27 

28 ase build Li # lithium atom 

29 ase build Li -M 1 # ... with a magnetic moment of 1 

30 ase build Li -M 1 -V 3.5 # ... in a 7x7x7 Ang cell 

31 ase build H2O # water molecule 

32 ase build -x fcc Cu -a 3.6 # FCC copper 

33 """ 

34 

35 @staticmethod 

36 def add_arguments(parser): 

37 add = parser.add_argument 

38 add('name', metavar='formula/input-file', 

39 help='Chemical formula or input filename.') 

40 add('output', nargs='?', help='Output file.') 

41 add('-M', '--magnetic-moment', 

42 metavar='M1,M2,...', 

43 help='Magnetic moments. ' 

44 'Use "-M 1" or "-M 2.3,-2.3"') 

45 add('--modify', metavar='...', 

46 help='Modify atoms with Python statement. ' 

47 'Example: --modify="atoms.positions[-1,2]+=0.1"') 

48 add('-V', '--vacuum', type=float, 

49 help='Amount of vacuum to add around isolated atoms ' 

50 '(in Angstrom)') 

51 add('-v', '--vacuum0', type=float, 

52 help='Deprecated. Use -V or --vacuum instead') 

53 add('--unit-cell', metavar='CELL', 

54 help='Unit cell in Angstrom. Examples: "10.0" or "9,10,11"') 

55 add('--bond-length', type=float, metavar='LENGTH', 

56 help='Bond length of dimer in Angstrom') 

57 add('-x', '--crystal-structure', 

58 help='Crystal structure', 

59 choices=['sc', 'fcc', 'bcc', 'hcp', 'diamond', 

60 'zincblende', 'rocksalt', 'cesiumchloride', 

61 'fluorite', 'wurtzite']) 

62 add('-a', '--lattice-constant', default='', metavar='LENGTH', 

63 help='Lattice constant or comma-separated lattice constantes in ' 

64 'Angstrom') 

65 add('--orthorhombic', action='store_true', 

66 help='Use orthorhombic unit cell') 

67 add('--cubic', action='store_true', 

68 help='Use cubic unit cell') 

69 add('-r', '--repeat', 

70 help='Repeat unit cell. Use "-r 2" or "-r 2,3,1"') 

71 add('-g', '--gui', action='store_true', 

72 help='open ase gui') 

73 add('--periodic', action='store_true', 

74 help='make structure fully periodic') 

75 

76 @staticmethod 

77 def run(args, parser): 

78 from ase.db import connect 

79 from ase.io import read, write 

80 from ase.visualize import view 

81 

82 if args.vacuum0: 

83 parser.error('Please use -V or --vacuum instead!') 

84 

85 if '.' in args.name: 

86 # Read from file: 

87 atoms = read(args.name) 

88 elif args.crystal_structure: 

89 atoms = build_bulk(args) 

90 else: 

91 atoms = build_molecule(args) 

92 

93 if args.magnetic_moment: 

94 magmoms = np.array( 

95 [float(m) for m in args.magnetic_moment.split(',')]) 

96 atoms.set_initial_magnetic_moments( 

97 np.tile(magmoms, len(atoms) // len(magmoms))) 

98 

99 if args.modify: 

100 exec(args.modify, {'atoms': atoms}) 

101 

102 if args.repeat is not None: 

103 r = args.repeat.split(',') 

104 if len(r) == 1: 

105 r = 3 * r 

106 atoms = atoms.repeat([int(c) for c in r]) 

107 

108 if args.gui: 

109 view(atoms) 

110 

111 if args.output: 

112 write(args.output, atoms) 

113 elif sys.stdout.isatty(): 

114 write(args.name + '.json', atoms) 

115 else: 

116 con = connect(sys.stdout, type='json') 

117 con.write(atoms, name=args.name) 

118 

119 

120def build_molecule(args): 

121 from ase.atoms import Atoms 

122 from ase.build import molecule 

123 from ase.data import ground_state_magnetic_moments 

124 from ase.data import atomic_numbers, covalent_radii 

125 from ase.symbols import string2symbols 

126 

127 try: 

128 # Known molecule or atom? 

129 atoms = molecule(args.name) 

130 except (NotImplementedError, KeyError): 

131 symbols = string2symbols(args.name) 

132 if len(symbols) == 1: 

133 Z = atomic_numbers[symbols[0]] 

134 magmom = ground_state_magnetic_moments[Z] 

135 atoms = Atoms(args.name, magmoms=[magmom]) 

136 elif len(symbols) == 2: 

137 # Dimer 

138 if args.bond_length is None: 

139 b = (covalent_radii[atomic_numbers[symbols[0]]] + 

140 covalent_radii[atomic_numbers[symbols[1]]]) 

141 else: 

142 b = args.bond_length 

143 atoms = Atoms(args.name, positions=[(0, 0, 0), 

144 (b, 0, 0)]) 

145 else: 

146 raise ValueError('Unknown molecule: ' + args.name) 

147 else: 

148 if len(atoms) == 2 and args.bond_length is not None: 

149 atoms.set_distance(0, 1, args.bond_length) 

150 

151 if args.unit_cell is None: 

152 if args.vacuum: 

153 atoms.center(vacuum=args.vacuum) 

154 else: 

155 atoms.center(about=[0, 0, 0]) 

156 else: 

157 a = [float(x) for x in args.unit_cell.split(',')] 

158 if len(a) == 1: 

159 cell = [a[0], a[0], a[0]] 

160 elif len(a) == 3: 

161 cell = a 

162 else: 

163 a, b, c, alpha, beta, gamma = a 

164 degree = np.pi / 180.0 

165 cosa = np.cos(alpha * degree) 

166 cosb = np.cos(beta * degree) 

167 sinb = np.sin(beta * degree) 

168 cosg = np.cos(gamma * degree) 

169 sing = np.sin(gamma * degree) 

170 cell = [[a, 0, 0], 

171 [b * cosg, b * sing, 0], 

172 [c * cosb, c * (cosa - cosb * cosg) / sing, 

173 c * np.sqrt( 

174 sinb**2 - ((cosa - cosb * cosg) / sing)**2)]] 

175 atoms.cell = cell 

176 atoms.center() 

177 

178 atoms.pbc = args.periodic 

179 

180 return atoms 

181 

182 

183def build_bulk(args): 

184 from ase.build import bulk 

185 

186 L = args.lattice_constant.replace(',', ' ').split() 

187 d = dict([(key, float(x)) for key, x in zip('ac', L)]) 

188 atoms = bulk(args.name, crystalstructure=args.crystal_structure, 

189 a=d.get('a'), c=d.get('c'), 

190 orthorhombic=args.orthorhombic, cubic=args.cubic) 

191 

192 M, X = {'Fe': (2.3, 'bcc'), 

193 'Co': (1.2, 'hcp'), 

194 'Ni': (0.6, 'fcc')}.get(args.name, (None, None)) 

195 if M is not None and args.crystal_structure == X: 

196 atoms.set_initial_magnetic_moments([M] * len(atoms)) 

197 

198 return atoms