Coverage for /builds/kinetik161/ase/ase/cli/build.py: 58.82%

102 statements  

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

1# Note: 

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

3# import time during CLI execution 

4import sys 

5 

6import numpy as np 

7 

8 

9class CLICommand: 

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

11 

12 Atom: 

13 

14 ase build <chemical symbol> ... 

15 

16 Molecule: 

17 

18 ase build <formula> ... 

19 

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

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

22 

23 Bulk: 

24 

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

26 

27 Examples: 

28 

29 ase build Li # lithium atom 

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

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

32 ase build H2O # water molecule 

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

34 """ 

35 

36 @staticmethod 

37 def add_arguments(parser): 

38 add = parser.add_argument 

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

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

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

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

43 metavar='M1,M2,...', 

44 help='Magnetic moments. ' 

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

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

47 help='Modify atoms with Python statement. ' 

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

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

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

51 '(in Angstrom)') 

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

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

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

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

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

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

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

59 help='Crystal structure', 

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

61 'zincblende', 'rocksalt', 'cesiumchloride', 

62 'fluorite', 'wurtzite']) 

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

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

65 'Angstrom') 

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

67 help='Use orthorhombic unit cell') 

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

69 help='Use cubic unit cell') 

70 add('-r', '--repeat', 

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

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

73 help='open ase gui') 

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

75 help='make structure fully periodic') 

76 

77 @staticmethod 

78 def run(args, parser): 

79 from ase.db import connect 

80 from ase.io import read, write 

81 from ase.visualize import view 

82 

83 if args.vacuum0: 

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

85 

86 if '.' in args.name: 

87 # Read from file: 

88 atoms = read(args.name) 

89 elif args.crystal_structure: 

90 atoms = build_bulk(args) 

91 else: 

92 atoms = build_molecule(args) 

93 

94 if args.magnetic_moment: 

95 magmoms = np.array( 

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

97 atoms.set_initial_magnetic_moments( 

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

99 

100 if args.modify: 

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

102 

103 if args.repeat is not None: 

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

105 if len(r) == 1: 

106 r = 3 * r 

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

108 

109 if args.gui: 

110 view(atoms) 

111 

112 if args.output: 

113 write(args.output, atoms) 

114 elif sys.stdout.isatty(): 

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

116 else: 

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

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

119 

120 

121def build_molecule(args): 

122 from ase.atoms import Atoms 

123 from ase.build import molecule 

124 from ase.data import (atomic_numbers, covalent_radii, 

125 ground_state_magnetic_moments) 

126 from ase.symbols import string2symbols 

127 

128 try: 

129 # Known molecule or atom? 

130 atoms = molecule(args.name) 

131 except (NotImplementedError, KeyError): 

132 symbols = string2symbols(args.name) 

133 if len(symbols) == 1: 

134 Z = atomic_numbers[symbols[0]] 

135 magmom = ground_state_magnetic_moments[Z] 

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

137 elif len(symbols) == 2: 

138 # Dimer 

139 if args.bond_length is None: 

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

141 covalent_radii[atomic_numbers[symbols[1]]]) 

142 else: 

143 b = args.bond_length 

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

145 (b, 0, 0)]) 

146 else: 

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

148 else: 

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

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

151 

152 if args.unit_cell is None: 

153 if args.vacuum: 

154 atoms.center(vacuum=args.vacuum) 

155 else: 

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

157 else: 

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

159 if len(a) == 1: 

160 cell = [a[0], a[0], a[0]] 

161 elif len(a) == 3: 

162 cell = a 

163 else: 

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

165 degree = np.pi / 180.0 

166 cosa = np.cos(alpha * degree) 

167 cosb = np.cos(beta * degree) 

168 sinb = np.sin(beta * degree) 

169 cosg = np.cos(gamma * degree) 

170 sing = np.sin(gamma * degree) 

171 cell = [[a, 0, 0], 

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

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

174 c * np.sqrt( 

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

176 atoms.cell = cell 

177 atoms.center() 

178 

179 atoms.pbc = args.periodic 

180 

181 return atoms 

182 

183 

184def build_bulk(args): 

185 from ase.build import bulk 

186 

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

188 d = {key: float(x) for key, x in zip('ac', L)} 

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

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

191 orthorhombic=args.orthorhombic, cubic=args.cubic) 

192 

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

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

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

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

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

198 

199 return atoms