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
« 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
6import numpy as np
9class CLICommand:
10 """Build an atom, molecule or bulk structure.
12 Atom:
14 ase build <chemical symbol> ...
16 Molecule:
18 ase build <formula> ...
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).
23 Bulk:
25 ase build -x <crystal structure> <formula> ...
27 Examples:
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 """
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')
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
83 if args.vacuum0:
84 parser.error('Please use -V or --vacuum instead!')
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)
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)))
100 if args.modify:
101 exec(args.modify, {'atoms': atoms})
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])
109 if args.gui:
110 view(atoms)
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)
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
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)
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()
179 atoms.pbc = args.periodic
181 return atoms
184def build_bulk(args):
185 from ase.build import bulk
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)
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))
199 return atoms