Coverage for /builds/ase/ase/ase/io/gpumd.py : 90.68%

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
1import numpy as np
2from ase.neighborlist import NeighborList
3from ase.data import atomic_masses, chemical_symbols
4from ase import Atoms
7def find_nearest_index(array, value):
8 array = np.asarray(array)
9 idx = (np.abs(array - value)).argmin()
10 return idx
13def find_nearest_value(array, value):
14 array = np.asarray(array)
15 idx = (np.abs(array - value)).argmin()
16 return array[idx]
19def write_gpumd(fd, atoms, maximum_neighbors=None, cutoff=None,
20 groupings=None, use_triclinic=False, species=None):
21 """
22 Writes atoms into GPUMD input format.
24 Parameters
25 ----------
26 fd : file
27 File like object to which the atoms object should be written
28 atoms : Atoms
29 Input structure
30 maximum_neighbors: int
31 Maximum number of neighbors any atom can ever have (not relevant when
32 using force constant potentials)
33 cutoff: float
34 Initial cutoff distance used for building the neighbor list (not
35 relevant when using force constant potentials)
36 groupings : list[list[list[int]]]
37 Groups into which the individual atoms should be divided in the form of
38 a list of list of lists. Specifically, the outer list corresponds to
39 the grouping methods, of which there can be three at the most, which
40 contains a list of groups in the form of lists of site indices. The
41 sum of the lengths of the latter must be the same as the total number
42 of atoms.
43 use_triclinic: bool
44 Use format for triclinic cells
45 species : List[str]
46 GPUMD uses integers to define atom types. This list allows customized
47 such definitions (e.g, ['Pd', 'H'] means Pd is type 0 and H type 1).
48 If None, this list is built by assigning each distinct species to
49 an integer in the order of appearance in `atoms`.
51 Raises
52 ------
53 ValueError
54 Raised if parameters are incompatible
55 """
57 # Check velocties parameter
58 if atoms.get_velocities() is None:
59 has_velocity = 0
60 else:
61 has_velocity = 1
62 velocities = atoms.get_velocities()
64 # Check groupings parameter
65 if groupings is None:
66 number_of_grouping_methods = 0
67 else:
68 number_of_grouping_methods = len(groupings)
69 if number_of_grouping_methods > 3:
70 raise ValueError('There can be no more than 3 grouping methods!')
71 for g, grouping in enumerate(groupings):
72 all_indices = [i for group in grouping for i in group]
73 if len(all_indices) != len(atoms) or\
74 set(all_indices) != set(range(len(atoms))):
75 raise ValueError('The indices listed in grouping method {} are'
76 ' not compatible with the input'
77 ' structure!'.format(g))
79 # If not specified, estimate the maximum_neighbors
80 if maximum_neighbors is None:
81 if cutoff is None:
82 cutoff = 0.1
83 maximum_neighbors = 1
84 else:
85 nl = NeighborList([cutoff / 2] * len(atoms), skin=2, bothways=True)
86 nl.update(atoms)
87 maximum_neighbors = 0
88 for atom in atoms:
89 maximum_neighbors = max(maximum_neighbors,
90 len(nl.get_neighbors(atom.index)[0]))
91 maximum_neighbors *= 2
92 maximum_neighbors = min(maximum_neighbors, 1024)
94 # Add header and cell parameters
95 lines = []
96 if atoms.cell.orthorhombic and not use_triclinic:
97 triclinic = 0
98 else:
99 triclinic = 1
100 lines.append('{} {} {} {} {} {}'.format(len(atoms), maximum_neighbors,
101 cutoff, triclinic, has_velocity,
102 number_of_grouping_methods))
103 if triclinic:
104 lines.append((' {}' * 12)[1:].format(*atoms.pbc.astype(int),
105 *atoms.cell[:].flatten()))
106 else:
107 lines.append((' {}' * 6)[1:].format(*atoms.pbc.astype(int),
108 *atoms.cell.lengths()))
110 # Create symbols-to-type map, i.e. integers starting at 0
111 if not species:
112 symbol_type_map = {}
113 for symbol in atoms.get_chemical_symbols():
114 if symbol not in symbol_type_map:
115 symbol_type_map[symbol] = len(symbol_type_map)
116 else:
117 if any([sym not in species
118 for sym in set(atoms.get_chemical_symbols())]):
119 raise ValueError('The species list does not contain all chemical '
120 'species that are present in the atoms object.')
121 else:
122 symbol_type_map = {symbol: i for i, symbol in enumerate(species)}
124 # Add lines for all atoms
125 for a, atm in enumerate(atoms):
126 t = symbol_type_map[atm.symbol]
127 line = (' {}' * 5)[1:].format(t, *atm.position, atm.mass)
128 if has_velocity:
129 line += (' {}' * 3).format(*velocities[a])
130 if groupings is not None:
131 for grouping in groupings:
132 for i, group in enumerate(grouping):
133 if a in group:
134 line += ' {}'.format(i)
135 break
136 lines.append(line)
138 # Write file
139 fd.write('\n'.join(lines))
142def load_xyz_input_gpumd(fd, species=None, isotope_masses=None):
143 """
144 Read the structure input file for GPUMD and return an ase Atoms object
145 togehter with a dictionary with parameters and a types-to-symbols map
147 Parameters
148 ----------
149 fd : file | str
150 File object or name of file from which to read the Atoms object
151 species : List[str]
152 List with the chemical symbols that correspond to each type, will take
153 precedence over isotope_masses
154 isotope_masses: Dict[str, List[float]]
155 Dictionary with chemical symbols and lists of the associated atomic
156 masses, which is used to identify the chemical symbols that correspond
157 to the types not found in species_types. The default is to find the
158 closest match :data:`ase.data.atomic_masses`.
160 Returns
161 -------
162 atoms : Atoms
163 Atoms object
164 input_parameters : Dict[str, int]
165 Dictionary with parameters from the first row of the input file, namely
166 'N', 'M', 'cutoff', 'triclinic', 'has_velocity' and 'num_of_groups'
167 species : List[str]
168 List with the chemical symbols that correspond to each type
170 Raises
171 ------
172 ValueError
173 Raised if the list of species is incompatible with the input file
174 """
175 # Parse first line
176 first_line = next(fd)
177 input_parameters = {}
178 keys = ['N', 'M', 'cutoff', 'triclinic', 'has_velocity',
179 'num_of_groups']
180 types = [float if key == 'cutoff' else int for key in keys]
181 for k, (key, typ) in enumerate(zip(keys, types)):
182 input_parameters[key] = typ(first_line.split()[k])
184 # Parse second line
185 second_line = next(fd)
186 second_arr = np.array(second_line.split())
187 pbc = second_arr[:3].astype(bool)
188 if input_parameters['triclinic']:
189 cell = second_arr[3:].astype(float).reshape((3, 3))
190 else:
191 cell = np.diag(second_arr[3:].astype(float))
193 # Parse all remaining rows
194 n_rows = input_parameters['N']
195 n_columns = 5 + input_parameters['has_velocity'] * 3 +\
196 input_parameters['num_of_groups']
197 rest_lines = [next(fd) for _ in range(n_rows)]
198 rest_arr = np.array([line.split() for line in rest_lines])
199 assert rest_arr.shape == (n_rows, n_columns)
201 # Extract atom types, positions and masses
202 atom_types = rest_arr[:, 0].astype(int)
203 positions = rest_arr[:, 1:4].astype(float)
204 masses = rest_arr[:, 4].astype(float)
206 # Determine the atomic species
207 if species is None:
208 type_symbol_map = {}
209 if isotope_masses is not None:
210 mass_symbols = {mass: symbol for symbol, masses in
211 isotope_masses.items() for mass in masses}
212 symbols = []
213 for atom_type, mass in zip(atom_types, masses):
214 if species is None:
215 if atom_type not in type_symbol_map:
216 if isotope_masses is not None:
217 nearest_value = find_nearest_value(
218 list(mass_symbols.keys()), mass)
219 symbol = mass_symbols[nearest_value]
220 else:
221 symbol = chemical_symbols[
222 find_nearest_index(atomic_masses, mass)]
223 type_symbol_map[atom_type] = symbol
224 else:
225 symbol = type_symbol_map[atom_type]
226 else:
227 if atom_type > len(species):
228 raise Exception('There is no entry for atom type {} in the '
229 'species list!'.format(atom_type))
230 symbol = species[atom_type]
231 symbols.append(symbol)
233 if species is None:
234 species = [type_symbol_map[i] for i in sorted(type_symbol_map.keys())]
236 # Create the Atoms object
237 atoms = Atoms(symbols=symbols, positions=positions, masses=masses, pbc=pbc,
238 cell=cell)
239 if input_parameters['has_velocity']:
240 velocities = rest_arr[:, 5:8].astype(float)
241 atoms.set_velocities(velocities)
242 if input_parameters['num_of_groups']:
243 start_col = 5 + 3 * input_parameters['has_velocity']
244 groups = rest_arr[:, start_col:].astype(int)
245 atoms.info = {i: {'groups': groups[i, :]} for i in range(n_rows)}
247 return atoms, input_parameters, species
250def read_gpumd(fd, species=None, isotope_masses=None):
251 """
252 Read Atoms object from a GPUMD structure input file
254 Parameters
255 ----------
256 fd : file | str
257 File object or name of file from which to read the Atoms object
258 species : List[str]
259 List with the chemical symbols that correspond to each type, will take
260 precedence over isotope_masses
261 isotope_masses: Dict[str, List[float]]
262 Dictionary with chemical symbols and lists of the associated atomic
263 masses, which is used to identify the chemical symbols that correspond
264 to the types not found in species_types. The default is to find the
265 closest match :data:`ase.data.atomic_masses`.
267 Returns
268 -------
269 atoms : Atoms
270 Atoms object
272 Raises
273 ------
274 ValueError
275 Raised if the list of species is incompatible with the input file
276 """
278 return load_xyz_input_gpumd(fd, species, isotope_masses)[0]