def __init__(self, atoms, keV, DW, comment=None, occupancy=1.0, fit_cell_to_atoms=False): self.atoms = atoms.copy() from collections import OrderedDict self.atom_types = list( OrderedDict((element, None) for element in self.atoms.get_chemical_symbols())) self.keV = keV self.DW = DW self._check_key_dictionary(self.DW, 'DW') self.comment = comment if np.isscalar(occupancy): self.occupancy = dict( zip(self.atom_types, [occupancy] * len(self.atom_types))) else: self.occupancy = occupancy self._check_key_dictionary(self.occupancy, 'occupancy') self.numbers = symbols2numbers(self.atom_types) if fit_cell_to_atoms: self.atoms.translate(-self.atoms.positions.min(axis=0)) self.atoms.set_cell(self.atoms.positions.max(axis=0))
def _parse_array_from_atoms(self, name, element, check_same_value): """ Return the array "name" for the given element. Parameters ---------- name : str The name of the arrays. Can be any key of `atoms.arrays` element : str, int The element to be considered. check_same_value : bool Check if all values are the same in the array. Necessary for 'occupancies' and 'debye_waller_factors' arrays. Returns ------- array containing the values corresponding defined by "name" for the given element. If check_same_value, return a single element. """ if isinstance(element, str): element = symbols2numbers(element)[0] sliced_array = self.atoms.arrays[name][self.atoms.numbers == element] if check_same_value: # to write the occupancies and the Debye Waller factor of xtl file # all the value must be equal if np.unique(sliced_array).size > 1: raise ValueError( "All the '{}' values for element '{}' must be " "equal.".format(name, chemical_symbols[element])) sliced_array = sliced_array[0] return sliced_array
def AlloyAtomsRandom(symbols, atoms): numbers_new = symbols2numbers(symbols) numbers_old = atoms.get_atomic_numbers() natoms = len(numbers_old) if len(numbers_old) != len(numbers_new): raise ValueError("The number of atoms in symbols (%i) " %(len(numbers_new),) + "must be equal to that in atoms (%i)!" % (len(atoms),)) numbers = [] indexes = [] for n in np.unique(numbers_new + numbers_old.tolist()): d = (numbers_new == n).sum() - (numbers_old == n).sum() if d > 0: # Add while d > 0: i = np.random.random_integers(0, len(numbers)) numbers.insert(i, n) d -= 1 elif d < 0: # Remove ind = np.arange(natoms)[numbers_old == n].tolist() while d < 0: i = np.random.random_integers(0, len(ind) - 1) indexes.append(ind.pop(i)) d += 1 numbers_old[indexes] = numbers atoms.set_atomic_numbers(numbers_old) return atoms
def _get_debye_waller_factors(self, DW): if np.isscalar(DW): if len(self.atom_types) > 1: raise ValueError('This cell contains more then one type of ' 'atoms and the Debye-Waller factor needs to ' 'be provided for each atom using a ' 'dictionary.') DW = np.ones_like(self.atoms.numbers) * DW elif isinstance(DW, dict): verify_dictionary(self.atoms, DW, 'DW') # Get the arrays of DW from mapping the DW defined by symbol DW = {symbols2numbers(k)[0]: v for k, v in DW.items()} DW = np.vectorize(DW.get)(self.atoms.numbers) else: for name in ['DW', 'debye_waller_factors']: if name in self.atoms.arrays: DW = self.atoms.get_array(name) if DW is None: raise ValueError('Missing Debye-Waller factors. It can be ' 'provided as a dictionary with symbols as key or ' 'can be set for each atom by using the ' '`set_array("debye_waller_factors", values)` of ' 'the `Atoms` object.') return DW
def RandomSphereAtoms(symbols, covalent=None, radius=None, vacuum=0.0): numbers = symbols2numbers(symbols) if covalent is None: covalent = 0.8 * np.mean(covalent_radii[numbers]) if radius is None: radius = covalent * float(len(numbers))**(1.0/3.0) cell = 3.0 * radius * np.ones(3, float) + vacuum positions = radius * np.ones((len(numbers), 3)) for i in range(len(numbers)): pos = radius * RandSphere(1) + 0.5 * cell while np.any(np.sum((positions - pos)**2, axis=1) < covalent**2): pos = radius * RandSphere(1) + 0.5 * cell positions[i] = pos.copy() #positions = radius * RandSphere(len(numbers)) + 0.5 * cell return Atoms(numbers, positions=positions, cell=cell)
def __init__(self, atoms, keV, debye_waller_factors=None, comment=None, occupancies=None, fit_cell_to_atoms=False): verify_cell_for_export(atoms.get_cell()) self.atoms = atoms.copy() self.atom_types = sorted(set(atoms.symbols)) self.keV = keV self.comment = comment self.occupancies = self._get_occupancies(occupancies) self.RMS = self._get_RMS(debye_waller_factors) self.numbers = symbols2numbers(self.atom_types) if fit_cell_to_atoms: self.atoms.translate(-self.atoms.positions.min(axis=0)) self.atoms.set_cell(self.atoms.positions.max(axis=0))
def __init__(self, atoms, keV, DW, comment=None, occupancy=1.0, fit_cell_to_atoms=False): self.atoms = atoms.copy() self.atom_types = [] for atom in self.atoms: if atom.symbol not in self.atom_types: self.atom_types.append(atom.symbol) self.keV = keV self.DW = DW self._check_key_dictionary(self.DW, 'DW') self.comment = comment if np.isscalar(occupancy): self.occupancy = dict(zip(self.atom_types, [occupancy] * len(self.atom_types))) else: self.occupancy = occupancy self._check_key_dictionary(self.occupancy, 'occupancy') self.numbers = symbols2numbers(self.atom_types) if fit_cell_to_atoms: self.atoms.translate(-self.atoms.positions.min(axis=0)) self.atoms.set_cell(self.atoms.positions.max(axis=0))
def parse_selection(self, selection, **kwargs): if selection is None or selection == '': expressions = [] elif isinstance(selection, int): expressions = [('id', '=', selection)] elif isinstance(selection, list): expressions = selection else: expressions = [w.strip() for w in selection.split(',')] keys = [] comparisons = [] for expression in expressions: if isinstance(expression, (list, tuple)): comparisons.append(expression) continue if expression.count('<') == 2: value, expression = expression.split('<', 1) if expression[0] == '=': op = '>=' expression = expression[1:] else: op = '>' key = expression.split('<', 1)[0] comparisons.append((key, op, value)) for op in ['!=', '<=', '>=', '<', '>', '=']: if op in expression: break else: if expression in atomic_numbers: comparisons.append((expression, '>', 0)) else: keys.append(expression) continue key, value = expression.split(op) comparisons.append((key, op, value)) cmps = [] for key, value in kwargs.items(): comparisons.append((key, '=', value)) for key, op, value in comparisons: if key == 'age': key = 'ctime' op = invop[op] value = now() - time_string_to_float(value) elif key == 'formula': assert op == '=' numbers = symbols2numbers(value) count = collections.defaultdict(int) for Z in numbers: count[Z] += 1 cmps.extend((Z, '=', count[Z]) for Z in count) key = 'natoms' value = len(numbers) elif key in atomic_numbers: key = atomic_numbers[key] value = int(value) elif isinstance(value, basestring): value = convert_str_to_float_or_str(value) if key in numeric_keys and not isinstance(value, (int, float)): msg = 'Wrong type for "{0}{1}{2}" - must be a number' raise ValueError(msg.format(key, op, value)) cmps.append((key, op, value)) return keys, cmps
def parse_selection(selection, **kwargs): if selection is None or selection == '': expressions = [] elif isinstance(selection, int): expressions = [('id', '=', selection)] elif isinstance(selection, list): expressions = selection else: expressions = [w.strip() for w in selection.split(',')] keys = [] comparisons = [] for expression in expressions: if isinstance(expression, (list, tuple)): comparisons.append(expression) continue if expression.count('<') == 2: value, expression = expression.split('<', 1) if expression[0] == '=': op = '>=' expression = expression[1:] else: op = '>' key = expression.split('<', 1)[0] comparisons.append((key, op, value)) for op in ['!=', '<=', '>=', '<', '>', '=']: if op in expression: break else: if expression in atomic_numbers: comparisons.append((expression, '>', 0)) else: try: symbols = string2symbols(expression) except ValueError: keys.append(expression) else: count = collections.Counter(symbols) comparisons.extend( (symbol, '>', n - 1) for symbol, n in count.items()) continue key, value = expression.split(op) comparisons.append((key, op, value)) cmps = [] for key, value in kwargs.items(): comparisons.append((key, '=', value)) for key, op, value in comparisons: if key == 'age': key = 'ctime' op = invop[op] value = now() - time_string_to_float(value) elif key == 'formula': if op != '=': raise ValueError('Use fomula=...') numbers = symbols2numbers(value) count = collections.defaultdict(int) for Z in numbers: count[Z] += 1 cmps.extend((Z, '=', count[Z]) for Z in count) key = 'natoms' value = len(numbers) elif key in atomic_numbers: key = atomic_numbers[key] value = int(value) elif isinstance(value, basestring): value = convert_str_to_int_float_or_str(value) if key in numeric_keys and not isinstance(value, (int, float)): msg = 'Wrong type for "{}{}{}" - must be a number' raise ValueError(msg.format(key, op, value)) cmps.append((key, op, value)) return keys, cmps
def select(self, selection=None, fancy=True, filter=None, explain=False, verbosity=1, limit=None, **kwargs): """Select rows. Return iterator with results as dictionaries. Selection is done using key-value pairs, keywords and the special keys: formula, age, user, calculator, natoms, energy, magmom and/or charge. selection: int, str or list Can be: * an integer id * a string like 'key=value', where '=' can also be one of '<=', '<', '>', '>=' or '!='. * a string like 'keyword' * comma separated strings like 'key1<value1,key2=value2,keyword' * list of strings or tuples: [('charge', '=', 1)]. fancy: bool return fancy dictionary with keys as attributes (this is the default). filter: function A function that takes as input a dictionary and returns True or False. explain: bool Explain query plan. verbosity: int Possible values: 0, 1 or 2. limit: int or None Limit selection. """ if selection is None or selection == '': expressions = [] elif isinstance(selection, int): expressions = [('id', '=', selection)] elif isinstance(selection, list): expressions = selection else: expressions = selection.split(',') keywords = [] comparisons = [] for expression in expressions: if isinstance(expression, (list, tuple)): comparisons.append(expression) continue if expression.count('<') == 2: value, expression = expression.split('<', 1) if expression[0] == '=': op = '>=' expression = expression[1:] else: op = '>' key = expression.split('<', 1)[0] comparisons.append((key, op, value)) for op in ['!=', '<=', '>=', '<', '>', '=']: if op in expression: break else: keywords.append(expression) continue key, value = expression.split(op) comparisons.append((key, op, value)) cmps = [] for key, value in kwargs.items(): comparisons.append((key, '=', value)) for key, op, value in comparisons: if key == 'age': key = 'ctime' op = invop[op] value = now() - time_string_to_float(value) elif key == 'formula': assert op == '=' numbers = symbols2numbers(value) count = collections.defaultdict(int) for Z in numbers: count[Z] += 1 cmps.extend((Z, '=', count[Z]) for Z in count) key = 'natoms' value = len(numbers) elif key in atomic_numbers: key = atomic_numbers[key] value = int(value) elif isinstance(value, str): try: value = float(value) except ValueError: assert op == '=' or op == '!=' if key in numeric_keys and not isinstance(value, (int, float)): msg = 'Wrong type for "{0}{1}{2}" - must be a number' raise ValueError(msg.format(key, op, value)) cmps.append((key, op, value)) for dct in self._select(keywords, cmps, explain=explain, verbosity=verbosity, limit=limit): if filter is None or filter(dct): if fancy: dct = FancyDict(dct) if 'key_value_pairs' in dct: dct.update(dct['key_value_pairs']) yield dct
def parse_selection(self, selection, **kwargs): if selection is None or selection == "": expressions = [] elif isinstance(selection, int): expressions = [("id", "=", selection)] elif isinstance(selection, list): expressions = selection else: expressions = selection.split(",") keys = [] comparisons = [] for expression in expressions: if isinstance(expression, (list, tuple)): comparisons.append(expression) continue if expression.count("<") == 2: value, expression = expression.split("<", 1) if expression[0] == "=": op = ">=" expression = expression[1:] else: op = ">" key = expression.split("<", 1)[0] comparisons.append((key, op, value)) for op in ["!=", "<=", ">=", "<", ">", "="]: if op in expression: break else: if expression in atomic_numbers: comparisons.append((expression, ">", 0)) else: keys.append(expression) continue key, value = expression.split(op) comparisons.append((key, op, value)) cmps = [] for key, value in kwargs.items(): comparisons.append((key, "=", value)) for key, op, value in comparisons: if key == "age": key = "ctime" op = invop[op] value = now() - time_string_to_float(value) elif key == "formula": assert op == "=" numbers = symbols2numbers(value) count = collections.defaultdict(int) for Z in numbers: count[Z] += 1 cmps.extend((Z, "=", count[Z]) for Z in count) key = "natoms" value = len(numbers) elif key in atomic_numbers: key = atomic_numbers[key] value = int(value) elif isinstance(value, (str, unicode)): try: value = float(value) except ValueError: assert op == "=" or op == "!=" if key in numeric_keys and not isinstance(value, (int, float)): msg = 'Wrong type for "{0}{1}{2}" - must be a number' raise ValueError(msg.format(key, op, value)) cmps.append((key, op, value)) return keys, cmps
def initialise_lammps(self, atoms): # Initialising commands if self.parameters.boundary: # if the boundary command is in the supplied commands use that # otherwise use atoms pbc pbc = atoms.get_pbc() for cmd in self.parameters.lmpcmds: if 'boundary' in cmd: break else: self.lmp.command('boundary ' + ' '.join([self.lammpsbc(bc) for bc in pbc])) # Initialize cell self.set_cell(atoms, change=not self.parameters.create_box) if self.parameters.atom_types is None: raise NameError("atom_types are mandatory.") if isinstance(self.parameters.atom_types, dict): # atom_types is a dictionary with symbols (or numbers) as keys self.parameters.atom_types_equal_atomic_numbers = False symbol_atom_types = self.parameters.atom_types.copy() self.parameters.atom_types = {} for sym in symbol_atom_types: try: num = int(sym) except: num = symbols2numbers(sym)[0] self.parameters.atom_types[num] = symbol_atom_types[sym] else: # not a dict, must be the string TYPE_EQUALS_Z if self.parameters.atom_types == "TYPE_EQUALS_Z": self.parameters.atom_types_equal_atomic_numbers = True self.parameters.atom_types = {} for Z in atoms.get_atomic_numbers(): self.parameters.atom_types[Z] = Z else: raise ValueError( 'atom_types parameter "%s" is string, but not TYPE_EQUALS_Z' % self.parameters.atom_types) # Collect chemical symbols symbols = np.asarray(atoms.get_chemical_symbols()) numbers = np.asarray(atoms.get_atomic_numbers()) # Initialize box if self.parameters.create_box: # count number of known types n_types = len(self.parameters.atom_types) create_box_command = 'create_box {} cell'.format(n_types) # count numbers of bonds and angles defined by potential n_dihedral_types = 0 n_improper_types = 0 n_angle_types = 0 n_bond_types = 0 for cmd in self.parameters.lmpcmds: m = re.match('\s*angle_coeff\s+(\d+)', cmd) if m is not None: n_angle_types = max(int(m.group(1)), n_angle_types) m = re.match('\s*bond_coeff\s+(\d+)', cmd) if m is not None: n_bond_types = max(int(m.group(1)), n_bond_types) m = re.match('\s*dihedral_coeff\s+(\d+)', cmd) if m is not None: n_dihedral_types = max(int(m.group(1)), n_dihedral_types) m = re.match('\s*improper_coeff\s+(\d+)', cmd) if m is not None: n_improper_types = max(int(m.group(1)), n_improper_types) if self.parameters.read_molecular_info: if 'bonds' in atoms.arrays: self.parse_bonds(atoms) create_box_command += ' bond/types {} extra/bond/per/atom {}'.format( n_bond_types, atoms.max_n_bonds) if 'angles' in atoms.arrays: self.parse_angles(atoms) create_box_command += ' angle/types {} extra/angle/per/atom {}'.format( n_angle_types, atoms.max_n_angles) if 'dihedrals' in atoms.arrays: self.parse_dihedrals(atoms) create_box_command += ' dihedral/types {} extra/dihedral/per/atom {}'.format( n_dihedral_types, atoms.max_n_dihedrals) if 'impropers' in atoms.arrays: self.parse_impropers(atoms) create_box_command += ' improper/types {} extra/improper/per/atom {}'.format( n_improper_types, atoms.max_n_impropers) self.lmp.command(create_box_command) # Initialize the atoms with their types # positions do not matter here if self.parameters.create_atoms: self.lmp.command('echo none') # don't echo the atom positions self.rebuild(atoms) self.lmp.command('echo log') # turn back on # execute the user commands for cmd in self.parameters.lmpcmds: self.lmp.command(cmd) # Set masses after user commands, to override EAM provided masses, e.g. masses = atoms.get_masses() for Z in self.parameters.atom_types: in_cur_sys = False for i in range(len(atoms)): if numbers[i] == Z: # convert from amu (ASE) to lammps mass unit) self.lmp.command( 'mass %d %.30f' % (self.parameters.atom_types[Z], masses[i] / unit_convert("mass", self.units))) in_cur_sys = True break if not in_cur_sys: self.lmp.command('mass %d %.30f' % (self.parameters.atom_types[Z], 1.0)) # Define force & energy variables for extraction self.lmp.command('variable pxx equal pxx') self.lmp.command('variable pyy equal pyy') self.lmp.command('variable pzz equal pzz') self.lmp.command('variable pxy equal pxy') self.lmp.command('variable pxz equal pxz') self.lmp.command('variable pyz equal pyz') # I am not sure why we need this next line but LAMMPS will # raise an error if it is not there. Perhaps it is needed to # ensure the cell stresses are calculated self.lmp.command('thermo_style custom pe pxx emol ecoul') self.lmp.command('variable fx atom fx') self.lmp.command('variable fy atom fy') self.lmp.command('variable fz atom fz') # do we need this if we extract from a global ? self.lmp.command('variable pe equal pe') self.lmp.command("neigh_modify delay 0 every 1 check yes") if self.parameters.read_molecular_info: # read in bonds if there are bonds from the ase-atoms object if the molecular flag is set if 'bonds' in atoms.arrays: self.set_bonds(atoms) # read in angles if there are angles from the ase-atoms object if the molecular flag is set if 'angles' in atoms.arrays: self.set_angles(atoms) # read in dihedrals if there are dihedrals from the ase-atoms object if the molecular flag is set if 'dihedrals' in atoms.arrays: self.set_dihedrals(atoms) # read in impropers if there are impropers from the ase-atoms object if the molecular flag is set if 'impropers' in atoms.arrays: self.set_impropers(atoms) if self.parameters.read_molecular_info and 'mmcharge' in atoms.arrays: self.set_charges(atoms) self.initialized = True
def test_mustem_several_elements(): """Check writing and reading a xtl mustem file.""" # Reproduce the sto xtl file distributed with muSTEM atoms = make_STO_atoms() filename = 'sto_mustem.xtl' STO_DW_dict = {'Sr': 0.62, 'O': 0.73, 'Ti': 0.43} STO_DW_dict_Ti_missing = {key: STO_DW_dict[key] for key in ['Sr', 'O']} with pytest.raises(TypeError): atoms.write(filename) with pytest.raises(ValueError): atoms.write(filename, keV=300) with pytest.raises(TypeError): atoms.write(filename, debye_waller_factors=STO_DW_dict) atoms.write(filename, keV=300, debye_waller_factors=STO_DW_dict) atoms2 = read(filename, format='mustem') atoms3 = read(filename) for _atoms in [atoms2, atoms3]: assert atoms.positions == pytest.approx(_atoms.positions) np.testing.assert_allclose(atoms.cell, _atoms.cell) with pytest.raises(ValueError): # Raise an error if there is a missing key. atoms.write(filename, keV=300, debye_waller_factors=STO_DW_dict_Ti_missing) atoms.write(filename, keV=300, debye_waller_factors=STO_DW_dict, occupancies={ 'Sr': 1.0, 'O': 0.5, 'Ti': 0.9 }) with pytest.raises(ValueError): # Raise an error if there is a missing key. atoms.write(filename, keV=300, debye_waller_factors=STO_DW_dict, occupancies={ 'O': 0.5, 'Ti': 0.9 }) with pytest.raises(ValueError): # Raise an error if the unit cell is not defined. atoms4 = Atoms(['Sr', 'Ti', 'O', 'O', 'O'], positions=[[0, 0, 0], [0.5, 0.5, 0.5], [0.5, 0.5, 0], [0.5, 0, 0.5], [0, 0.5, 0.5]]) atoms4.write(filename, keV=300, debye_waller_factors=STO_DW_dict) atoms5 = make_STO_atoms() atoms5.set_array('occupancies', np.ones(5)) atoms5.arrays['occupancies'][atoms5.numbers == symbols2numbers('Sr')] = 0.9 # element 0 is Sr and there is onlye one Sr in the cell: this is a valid # cell to export to xtl file atoms5.write(filename, keV=300, debye_waller_factors=STO_DW_dict) atoms6 = read(filename) condition = atoms6.numbers == symbols2numbers('Sr') np.testing.assert_allclose(atoms6.arrays['occupancies'][condition], 0.9) atoms5.arrays['occupancies'][0] = 0.8 with pytest.raises(ValueError): atoms5.write(filename, keV=300, debye_waller_factors=STO_DW_dict) atoms7 = make_STO_atoms() debye_waller_factors = np.array([0.73, 0.73, 0.73, 0.62, 0.43]) atoms7.set_array('debye_waller_factors', debye_waller_factors) # element 0 is Sr and there is onlye one Sr in the cell: this is a valid # cell to export to xtl file atoms7.write(filename, keV=300) atoms8 = read(filename) for element in ['Sr', 'Ti', 'O']: number = symbols2numbers(element) np.testing.assert_allclose( atoms7.arrays['debye_waller_factors'][atoms7.numbers == number], atoms8.arrays['debye_waller_factors'][atoms8.numbers == number], rtol=1e-2)
def parse_selection(self, selection, **kwargs): if selection is None or selection == '': expressions = [] elif isinstance(selection, int): expressions = [('id', '=', selection)] elif isinstance(selection, list): expressions = selection else: expressions = selection.split(',') keys = [] comparisons = [] for expression in expressions: if isinstance(expression, (list, tuple)): comparisons.append(expression) continue if expression.count('<') == 2: value, expression = expression.split('<', 1) if expression[0] == '=': op = '>=' expression = expression[1:] else: op = '>' key = expression.split('<', 1)[0] comparisons.append((key, op, value)) for op in ['!=', '<=', '>=', '<', '>', '=']: if op in expression: break else: if expression in atomic_numbers: comparisons.append((expression, '>', 0)) else: keys.append(expression) continue key, value = expression.split(op) comparisons.append((key, op, value)) cmps = [] for key, value in kwargs.items(): comparisons.append((key, '=', value)) for key, op, value in comparisons: if key == 'age': key = 'ctime' op = invop[op] value = now() - time_string_to_float(value) elif key == 'formula': assert op == '=' numbers = symbols2numbers(value) count = collections.defaultdict(int) for Z in numbers: count[Z] += 1 cmps.extend((Z, '=', count[Z]) for Z in count) key = 'natoms' value = len(numbers) elif key in atomic_numbers: key = atomic_numbers[key] value = int(value) elif isinstance(value, (str, unicode)): try: value = float(value) except ValueError: assert op == '=' or op == '!=' if key in numeric_keys and not isinstance(value, (int, float)): msg = 'Wrong type for "{0}{1}{2}" - must be a number' raise ValueError(msg.format(key, op, value)) cmps.append((key, op, value)) return keys, cmps
def select(self, selection=None, fancy=True, filter=None, explain=False, verbosity=1, limit=None, **kwargs): """Select rows. Return iterator with results as dictionaries. Selection is done using key-value pairs, keywords and the special keys: formula, age, user, calculator, natoms, energy, magmom and/or charge. selection: int, str or list Can be: * an integer id * a string like 'key=value', where '=' can also be one of '<=', '<', '>', '>=' or '!='. * a string like 'keyword' * comma separated strings like 'key1<value1,key2=value2,keyword' * list of strings or tuples: [('charge', '=', 1)]. fancy: bool return fancy dictionary with keys as attributes (this is the default). filter: function A function that takes as input a dictionary and returns True or False. explain: bool Explain query plan. verbosity: int Possible values: 0, 1 or 2. limit: int or None Limit selection. """ if selection is None or selection == '': expressions = [] elif isinstance(selection, int): expressions = [('id', '=', selection)] elif isinstance(selection, list): expressions = selection else: expressions = selection.split(',') keywords = [] comparisons = [] for expression in expressions: if isinstance(expression, (list, tuple)): comparisons.append(expression) continue if expression.count('<') == 2: value, expression = expression.split('<', 1) if expression[0] == '=': op = '>=' expression = expression[1:] else: op = '>' key = expression.split('<', 1)[0] comparisons.append((key, op, value)) for op in ['!=', '<=', '>=', '<', '>', '=']: if op in expression: break else: keywords.append(expression) continue key, value = expression.split(op) comparisons.append((key, op, value)) cmps = [] for key, value in kwargs.items(): comparisons.append((key, '=', value)) for key, op, value in comparisons: if key == 'age': key = 'ctime' op = invop[op] value = now() - time_string_to_float(value) elif key == 'formula': assert op == '=' numbers = symbols2numbers(value) count = collections.defaultdict(int) for Z in numbers: count[Z] += 1 cmps.extend((Z, '=', count[Z]) for Z in count) key = 'natoms' value = len(numbers) elif key in atomic_numbers: key = atomic_numbers[key] value = int(value) elif isinstance(value, str): try: value = float(value) except ValueError: assert op == '=' or op == '!=' if key in numeric_keys and not isinstance(value, (int, float)): msg = 'Wrong type for "{0}{1}{2}" - must be a number' raise ValueError(msg.format(key, op, value)) cmps.append((key, op, value)) for dct in self._select(keywords, cmps, explain=explain, verbosity=verbosity, limit=limit): if filter is None or filter(dct): if fancy: dct = FancyDict(dct) if 'key_value_pairs' in dct: dct.update(dct['key_value_pairs']) yield dct
def RandomBoxAtoms(symbols, cell=[5.0, 5.0, 5.0]): numbers = symbols2numbers(symbols) scaled_positions = np.random.uniform(0.0, 1.0, (len(numbers), 3)) return Atoms(numbers, scaled_positions=scaled_positions, cell=cell)