def __add__(self, other) -> "MullikenContribution": l = "".join(set([self.l, other.l])) d = self.contribution + other.contribution s1 = string2symbols(self.symbol) s2 = string2symbols(other.symbol) s = Formula.from_list(s1 + s2).format("reduce") return MullikenContribution(s, d, l)
def check(key_value_pairs): for key, value in key_value_pairs.items(): if not word.match(key) or key in reserved_keys: raise ValueError('Bad key: {}'.format(key)) try: string2symbols(key) except ValueError: pass else: warnings.warn( 'It is best not to use keys ({0}) that are also a ' 'chemical formula. If you do a "db.select({0!r})",' 'you will not find rows with your key. Instead, you wil get ' 'rows containing the atoms in the formula!'.format(key)) if not isinstance(value, (numbers.Real, basestring, np.bool_)): raise ValueError('Bad value for {!r}: {}'.format(key, value)) if isinstance(value, basestring): for t in [int, float]: if str_represents(value, t): raise ValueError('Value ' + value + ' is put in as string ' + 'but can be interpreted as ' + '{}! Please convert '.format(t.__name__) + 'to {} using '.format(t.__name__) + '{}(value) before '.format(t.__name__) + 'writing to the database OR change ' + 'to a different string.')
def __add__(self, other) -> "DOSContribution": assert (self.values.shape == other.values.shape ), "DOS contributions shape does not match for addition." d = self.values + other.values l = "".join(set([self.l, other.l])) s1 = string2symbols(self.symbol) s2 = string2symbols(other.symbol) s = Formula.from_list(s1 + s2).format("reduce").format("metal") return DOSContribution(s, d, l)
def _orthorhombic_bulk(name, crystalstructure, a, covera=None, u=None): if crystalstructure == 'fcc': b = a / sqrt(2) atoms = Atoms(2 * name, cell=(b, b, a), pbc=True, scaled_positions=[(0, 0, 0), (0.5, 0.5, 0.5)]) elif crystalstructure == 'bcc': atoms = Atoms(2 * name, cell=(a, a, a), pbc=True, scaled_positions=[(0, 0, 0), (0.5, 0.5, 0.5)]) elif crystalstructure == 'hcp': atoms = Atoms(4 * name, cell=(a, a * sqrt(3), covera * a), scaled_positions=[(0, 0, 0), (0.5, 0.5, 0), (0.5, 1 / 6, 0.5), (0, 2 / 3, 0.5)], pbc=True) elif crystalstructure == 'diamond': atoms = _orthorhombic_bulk(2 * name, 'zincblende', a) elif crystalstructure == 'zincblende': s1, s2 = string2symbols(name) b = a / sqrt(2) atoms = Atoms(2 * name, cell=(b, b, a), pbc=True, scaled_positions=[(0, 0, 0), (0.5, 0, 0.25), (0.5, 0.5, 0.5), (0, 0.5, 0.75)]) elif crystalstructure == 'rocksalt': s1, s2 = string2symbols(name) b = a / sqrt(2) atoms = Atoms(2 * name, cell=(b, b, a), pbc=True, scaled_positions=[(0, 0, 0), (0.5, 0.5, 0), (0.5, 0.5, 0.5), (0, 0, 0.5)]) elif crystalstructure == 'cesiumchloride': atoms = Atoms(name, cell=(a, a, a), pbc=True, scaled_positions=[(0, 0, 0), (0.5, 0.5, 0.5)]) elif crystalstructure == 'wurtzite': u = u or 0.25 + 1 / 3 / covera**2 atoms = Atoms(4 * name, cell=(a, a * 3**0.5, covera * a), scaled_positions=[(0, 0, 0), (0, 1 / 3, 0.5 - u), (0, 1 / 3, 0.5), (0, 0, 1 - u), (0.5, 0.5, 0), (0.5, 5 / 6, 0.5 - u), (0.5, 5 / 6, 0.5), (0.5, 0.5, 1 - u)], pbc=True) else: raise incompatible_cell(want='orthorhombic', have=crystalstructure) return atoms
def strain(self, atoms=None): """Return a fingerprint with the espected strain of the site atoms and the termination atoms. Parameters ---------- atoms : object ASE Atoms object. Returns ---------- features : list If None was passed, the elements are strings, naming the feature. """ if atoms is None: return ['strain_site', 'strain_term'] else: if ('key_value_pairs' in atoms.info and 'term' in atoms.info['key_value_pairs']): term = atoms.info['key_value_pairs']['term'] term_numbers = [atomic_numbers[s] for s in string2symbols(term)] elif 'termination_atoms' in atoms.subsets: term = atoms.subsets['termination_atoms'] term_numbers = atoms.numbers[term] else: raise NotImplementedError("strain fingerprint.") if ('key_value_pairs' in atoms.info and 'bulk' in atoms.info['key_value_pairs']): bulk = atoms.info['key_value_pairs']['bulk'] bulk_numbers = [atomic_numbers[s] for s in string2symbols(bulk)] elif 'bulk_atoms' in atoms.subsets: bulk = atoms.subsets['bulk_atoms'] bulk_numbers = atoms.numbers[bulk] else: raise NotImplementedError("strain fingerprint.") site = atoms.subsets['site_atoms'] site_numbers = atoms.numbers[site] rbulk = [] rterm = [] rsite = [] for b in bulk_numbers: rbulk.append(get_radius(b)) for t in term_numbers: rterm.append(get_radius(t)) for z in site_numbers: rsite.append(get_radius(z)) av_term = np.average(rterm) av_bulk = np.average(rbulk) av_site = np.average(rsite) strain_site = (av_site - av_bulk) / av_bulk strain_term = (av_term - av_bulk) / av_bulk return [strain_site, strain_term]
def bulk(self, atoms=None): """Return a fingerprint vector with propeties averaged over the bulk atoms. Parameters ---------- atoms : object ASE Atoms object. Returns ---------- features : list If None was passed, the elements are strings, naming the feature. """ labels = make_labels(self.slab_params, '', '_bulk') labels.append('ground_state_magmom_bulk') if atoms is None: return labels else: if ('key_value_pairs' in atoms.info and 'bulk' in atoms.info['key_value_pairs']): bulk = atoms.info['key_value_pairs']['bulk'] numbers = [atomic_numbers[s] for s in string2symbols(bulk)] elif 'bulk_atoms' in atoms.subsets: bulk = atoms.subsets['bulk_atoms'] numbers = atoms.numbers[bulk] else: raise NotImplementedError("bulk fingerprint.") dat = list_mendeleev_params(numbers, params=self.slab_params) result = list(np.nanmean(dat, axis=0)) result += [np.nanmean([gs_magmom[z] for z in numbers])] check_labels(labels, result, atoms) return result
def term(self, atoms=None): """Return a fingerprint vector with propeties averaged over the termination atoms. Parameters ---------- atoms : object """ labels = make_labels(self.slab_params, '', '_term') labels.append('ground_state_magmom_term') if atoms is None: return labels else: if ('key_value_pairs' in atoms.info and 'term' in atoms.info['key_value_pairs']): term = atoms.info['key_value_pairs']['term'] numbers = [atomic_numbers[s] for s in string2symbols(term)] elif 'termination_atoms' in atoms.subsets: term = atoms.subsets['termination_atoms'] numbers = atoms.numbers[term] else: raise NotImplementedError("termination fingerprint.") dat = list_mendeleev_params(numbers, params=self.slab_params) result = list(np.nanmean(dat, axis=0)) result += [np.nanmean([gs_magmom[z] for z in numbers])] check_labels(labels, result, atoms) return result
def dia_100_2x1(sym, a0): sym = string2symbols(sym) if len(sym) == 1: a = Diamond(sym[0], size = [2*nx, nx, nz], latticeconstant = a0, directions=[ [1,-1,0], [1,1,0], [0,0,1] ] ) else: a = B3(sym, size = [2*nx, nx, nz], latticeconstant = a0, directions=[ [1,-1,0], [1,1,0], [0,0,1] ] ) sx, sy, sz = a.get_cell().diagonal() a.translate([sx/(8*nx), sy/(8*nx), sz/(8*nz)]) bulk = a.copy() for i in a: if i.z < sz/(4*nz) or i.z > sz-sz/(4*nz): if i.x < sx/2: i.x = i.x+0.5 else: i.x = i.x-0.5 return bulk, a
def formula2ads_index(atoms, species): """Return the indexes of atoms, which have symbols matching the chemical formula of the adsorbate. This function will not work for adsorbates containing the same elements as the slab. Parameters ---------- atoms : ase atoms object. atoms.info must be a dictionary containing the key 'key_value_pairs', which is expected to contain CatMAP standard adsorbate structure key value pairs. See the ase db to catmap module in catmap. the key value pair 'species' must be the chemical formula of the adsorbate. species : str chemical formula of the adsorbate. """ try: composition = string2symbols(species) except ValueError: print(species) raise ads_atoms = [a.index for a in atoms if a.symbol in composition] if sorted(symbols2numbers(species)) != sorted(atoms[ads_atoms].numbers): raise AssertionError("ads atoms identification by formula failed.") return ads_atoms
def dia_100_2x1(sym, a0): sym = string2symbols(sym) if len(sym) == 1: a = Diamond(sym[0], size=[2 * nx, nx, nz], latticeconstant=a0, directions=[[1, -1, 0], [1, 1, 0], [0, 0, 1]]) else: a = B3(sym, size=[2 * nx, nx, nz], latticeconstant=a0, directions=[[1, -1, 0], [1, 1, 0], [0, 0, 1]]) sx, sy, sz = a.get_cell().diagonal() a.translate([sx / (8 * nx), sy / (8 * nx), sz / (8 * nz)]) bulk = a.copy() for i in a: if i.z < sz / (4 * nz) or i.z > sz - sz / (4 * nz): if i.x < sx / 2: i.x = i.x + 0.5 else: i.x = i.x - 0.5 return bulk, a
def plot_majority_contribution( self, contributions=[], axes=None, colors=[], labels=[], show_colorbar=True, **kwargs, ): """Utility function to show majority contributions of given list of contributions. A majority-projection only shows the largest contribution to each k-point and band. """ kwargs = self._process_kwargs(kwargs) kwargs["mode"] = "majority" if contributions == []: species = list( dict.fromkeys( string2symbols(self.structure.get_chemical_formula()))) contributions = [(j, "tot") for j in species] labels = species self.plot_contributions( axes=axes, contributions=contributions, colors=colors, labels=labels, show_colorbar=show_colorbar, **kwargs, ) return axes
def plot_all_species(self, axes=None, l="tot", colors=[], show_legend=True, **kwargs): """Utility function to all show species contributions. Args: l (str, optional): Angular momentum. Defaults to "tot". """ kwargs = self._process_kwargs(kwargs) species = list( dict.fromkeys(string2symbols( self.structure.get_chemical_formula()))) contributions = [(j, "tot") for j in species] labels = species if len(colors) == 0: colors = [jmol_colors[symbols2numbers(n)][0] for n in labels] assert len(labels) == len( colors), "Number of symbols does not match number of colors." self.plot_contributions( axes=axes, contributions=contributions, colors=colors, labels=labels, show_legend=show_legend, **kwargs, ) return axes
def formula2stoich(formula): symbols = string2symbols(formula) unique_symbols = list(set(symbols)) count = [symbols.count(us) for us in unique_symbols] idx = np.argsort(count) count = [count[i] for i in idx] unique_symbols = [unique_symbols[i] for i in idx] stoich = '_'.join([str(c) for c in count]) return stoich, unique_symbols
def build_molecule(args): try: # Known molecule or atom? atoms = molecule(args.name) except NotImplementedError: symbols = string2symbols(args.name) if len(symbols) == 1: Z = atomic_numbers[symbols[0]] magmom = ground_state_magnetic_moments[Z] atoms = Atoms(args.name, magmoms=[magmom]) elif len(symbols) == 2: # Dimer if args.bond_length is None: b = (covalent_radii[atomic_numbers[symbols[0]]] + covalent_radii[atomic_numbers[symbols[1]]]) else: b = args.bond_length atoms = Atoms(args.name, positions=[(0, 0, 0), (b, 0, 0)]) else: raise ValueError('Unknown molecule: ' + args.name) else: if len(atoms) == 2 and args.bond_length is not None: atoms.set_distance(0, 1, args.bond_length) if args.unit_cell is None: if args.vacuum: atoms.center(vacuum=args.vacuum) else: atoms.center(about=[0, 0, 0]) else: a = [float(x) for x in args.unit_cell.split(',')] if len(a) == 1: cell = [a[0], a[0], a[0]] elif len(a) == 3: cell = a else: a, b, c, alpha, beta, gamma = a degree = np.pi / 180.0 cosa = np.cos(alpha * degree) cosb = np.cos(beta * degree) sinb = np.sin(beta * degree) cosg = np.cos(gamma * degree) sing = np.sin(gamma * degree) cell = [[a, 0, 0], [b * cosg, b * sing, 0], [c * cosb, c * (cosa - cosb * cosg) / sing, c * np.sqrt( sinb**2 - ((cosa - cosb * cosg) / sing)**2)]] atoms.cell = cell atoms.center() atoms.pbc = args.periodic return atoms
def slab_positions2ads_index(atoms, slab, species): """Return the indexes of adsorbate atoms identified by comparing positions to a reference slab structure. Parameters ---------- atoms : object """ composition = string2symbols(species) ads_atoms = [] for symbol in composition: if (composition.count(symbol) == atoms.get_chemical_symbols().count( symbol)): ads_atoms += [ atom.index for atom in atoms if atom.symbol == symbol ] ua_ads, uc_ads = np.unique(ads_atoms, return_counts=True) ua_comp, uc_comp = np.unique(composition, return_counts=True) if ua_ads == ua_comp and uc_ads == uc_comp: return ads_atoms p_a = atoms.get_positions() p_r = slab.get_positions() for s in composition: if s in np.array(atoms.get_chemical_symbols())[ads_atoms]: continue symbol_count = composition.count(s) index_a = np.where(np.array(atoms.get_chemical_symbols()) == s)[0] index_r = np.where(np.array(slab.get_chemical_symbols()) == s)[0] _, dist = get_distances(p_a[index_a, :], p2=p_r[index_r, :], cell=atoms.cell, pbc=True) # Assume all slab atoms are closest to their reference counterpart. deviations = np.min(dist, axis=1) # Sort deviations. ascending = np.argsort(deviations) # The highest deviations are assumed to be new atoms. ads_atoms += list(index_a[ascending[-symbol_count:]]) # Final check. ua_ads, uc_ads = np.unique(np.array( atoms.get_chemical_symbols())[ads_atoms].sort(), return_counts=True) ua_comp, uc_comp = np.unique(composition.sort(), return_counts=True) if ua_ads != ua_comp: msg = str(ua_ads) + " != " + str(ua_comp) raise AssertionError(msg) elif uc_ads != uc_comp: msg = str(uc_ads) + " != " + str(uc_comp) raise AssertionError(msg) return ads_atoms
def set_symbol(self, symbol): assert type(symbol) == str, "Symbol must be a string." try: s = string2symbols(symbol) except Exception as expt: raise Exception( "String could not be interpreted as atomic symbols.") assert all(k in chemical_symbols for k in s), "Symbol is not an element from the PSE." s = Formula.from_list(s).format("reduce").format("metal") self._symbol = s
def get_atomtypes_from_formula(formula): """Return atom types from chemical formula (optionally prepended with and underscore). """ from ase.symbols import string2symbols symbols = string2symbols(formula.split('_')[0]) atomtypes = [symbols[0]] for s in symbols[1:]: if s != atomtypes[-1]: atomtypes.append(s) return atomtypes
def parse_formula(formula): aq = formula.endswith('(aq)') if aq: formula = formula[:-4] charge = formula.count('+') - formula.count('-') if charge: formula = formula.rstrip('+-') count = {} for symbol in string2symbols(formula): count[symbol] = count.get(symbol, 0) + 1 return count, charge, aq
def connectivity2ads_index(atoms, species): """Return the indexes of atoms from the global list of adsorbate symbols. Parameters ---------- atoms : object ASE atoms object with connectivity attached. This represents an adsorbate*slab structure. species : str chemical formula of the adsorbate. """ composition = string2symbols(species) ads_atoms = [] for symbol in composition: if (composition.count(symbol) == atoms.get_chemical_symbols().count( symbol)): ads_atoms += [ atom.index for atom in atoms if atom.symbol == symbol ] if len(ads_atoms) == len(composition): return ads_atoms elif len(ads_atoms) == 0: raise AssertionError("Formula adsorbate identification failed.") # If an atom in species also occurs in the slab, infer by connectivity. connected_atoms = [] for atom in ads_atoms: edges = atoms.connectivity[atom, :] connected_atoms += [ i for i, bonds in enumerate(edges) if bonds > 0 and atoms[i].symbol in composition and atoms[i].symbol != atoms[atom].symbol ] ads_atoms += connected_atoms # Final check. ua_ads, uc_ads = np.unique(np.array( atoms.get_chemical_symbols())[ads_atoms].sort(), return_counts=True) ua_comp, uc_comp = np.unique(composition.sort(), return_counts=True) if ua_ads != ua_comp: msg = str(ua_ads) + " != " + str(ua_comp) raise AssertionError(msg) elif uc_ads != uc_comp: msg = str(uc_ads) + " != " + str(uc_comp) raise AssertionError(msg) return list(np.unique(ads_atoms))
def atomization_energies(E): """Write given atomization energies to file atomization_energies.csv.""" Ea = {} fd = open('atomization_energies.csv', 'w') for formula in sorted(molecules): ea = -E[formula] for a in string2symbols(data[formula]['symbols']): ea += E[a] eavasp = atomization_vasp[formula][1] * kcal / mol Ea[formula] = (ea, eavasp) name = latex(data[formula]['name']) fd.write('`%s`, %.3f, %.3f, %+.3f\n' % (name[1:-1], ea, eavasp, ea - eavasp)) return Ea
def dia_111(sym, a0): sym = string2symbols(sym) if len(sym) == 1: a = Diamond(sym[0], size=[nx, nx, nz], latticeconstant=a0, directions=[[1, -1, 0], [1, 1, -2], [1, 1, 1]]) else: a = B3(sym, size=[nx, nx, nz], latticeconstant=a0, directions=[[1, -1, 0], [1, 1, -2], [1, 1, 1]]) sx, sy, sz = a.get_cell().diagonal() a.translate([sx / (12 * nx), sy / (4 * nx), sz / (12 * nz)]) return a
def dia_111_pandey(sym, a0, nx=nx, ny=nx, nz=nz): """2x1 Pandey reconstructed (111) surface.""" sym = string2symbols(sym) if len(sym) == 1: a = Diamond(sym[0], size = [nx, ny, nz], latticeconstant = a0, directions=[ [1,-1,0], [1,1,-2], [1,1,1] ] ) else: a = B3(sym, size = [nx, ny, nz], latticeconstant = a0, directions=[ [1,-1,0], [1,1,-2], [1,1,1] ] ) sx, sy, sz = a.get_cell().diagonal() a.translate([sx/(12*nx), sy/(4*ny), sz/(6*nz)]) a.set_scaled_positions(a.get_scaled_positions()%1.0) bulk = a.copy() bondlen = a0*sqrt(3)/4 x, y, z = a.positions.T mask = np.abs(z-z.max()) < 0.1*a0 top1, top2 = np.arange(len(a))[mask].reshape(-1, 2).T mask = np.logical_and(np.abs(z-z.max()) < bondlen, np.logical_not(mask)) topA, topB = np.arange(len(a))[mask].reshape(-1, 2).T y[topA] += bondlen/3 y[topB] -= bondlen/3 y[top1] += bondlen x[top1] += a.cell[0,0]/(2*nx) x[top2] += a.cell[0,0]/(2*nx) mask = np.abs(z-z.min()) < 0.1*a0 bot1, bot2 = np.arange(len(a))[mask].reshape(-1, 2).T mask = np.logical_and(np.abs(z-z.min()) < bondlen, np.logical_not(mask)) botA, botB = np.arange(len(a))[mask].reshape(-1, 2).T y[botA] += bondlen/3 y[botB] -= bondlen/3 y[bot2] -= bondlen x[bot2] += a.cell[0,0]/(2*nx) x[bot1] += a.cell[0,0]/(2*nx) a.set_scaled_positions(a.get_scaled_positions()%1.0) return bulk, a
def dia_111(sym, a0): sym = string2symbols(sym) if len(sym) == 1: a = Diamond(sym[0], size = [nx, nx, nz], latticeconstant = a0, directions=[ [1,-1,0], [1,1,-2], [1,1,1] ] ) else: a = B3(sym, size = [nx, nx, nz], latticeconstant = a0, directions=[ [1,-1,0], [1,1,-2], [1,1,1] ] ) sx, sy, sz = a.get_cell().diagonal() a.translate([sx/(12*nx), sy/(4*nx), sz/(12*nz)]) return a
def dia_111_pandey(sym, a0, nx=nx, ny=nx, nz=nz): """2x1 Pandey reconstructed (111) surface.""" sym = string2symbols(sym) if len(sym) == 1: a = Diamond(sym[0], size=[nx, ny, nz], latticeconstant=a0, directions=[[1, -1, 0], [1, 1, -2], [1, 1, 1]]) else: a = B3(sym, size=[nx, ny, nz], latticeconstant=a0, directions=[[1, -1, 0], [1, 1, -2], [1, 1, 1]]) sx, sy, sz = a.get_cell().diagonal() a.translate([sx / (12 * nx), sy / (4 * ny), sz / (6 * nz)]) a.set_scaled_positions(a.get_scaled_positions() % 1.0) bulk = a.copy() bondlen = a0 * sqrt(3) / 4 x, y, z = a.positions.T mask = np.abs(z - z.max()) < 0.1 * a0 top1, top2 = np.arange(len(a))[mask].reshape(-1, 2).T mask = np.logical_and(np.abs(z - z.max()) < bondlen, np.logical_not(mask)) topA, topB = np.arange(len(a))[mask].reshape(-1, 2).T y[topA] += bondlen / 3 y[topB] -= bondlen / 3 y[top1] += bondlen x[top1] += a.cell[0, 0] / (2 * nx) x[top2] += a.cell[0, 0] / (2 * nx) mask = np.abs(z - z.min()) < 0.1 * a0 bot1, bot2 = np.arange(len(a))[mask].reshape(-1, 2).T mask = np.logical_and(np.abs(z - z.min()) < bondlen, np.logical_not(mask)) botA, botB = np.arange(len(a))[mask].reshape(-1, 2).T y[botA] += bondlen / 3 y[botB] -= bondlen / 3 y[bot2] -= bondlen x[bot2] += a.cell[0, 0] / (2 * nx) x[bot1] += a.cell[0, 0] / (2 * nx) a.set_scaled_positions(a.get_scaled_positions() % 1.0) return bulk, a
def get_formation_energies(energy_dict,ref_dict): formation_energies = {} for key in energy_dict.keys(): #iterate through keys E0 = energy_dict[key] #raw energy name,site = key.split('_') #split key into name/site if 'slab' not in name: #do not include empty site energy (0) if site == '111': E0 -= ref_dict[site] #subtract slab energy if adsorbed #remove - from transition-states formula = name.replace('-','') #get the composition as a list of atomic species composition = string2symbols(formula) #for each atomic species, subtract off the reference energy for atom in composition: E0 -= ref_dict[atom] #round to 3 decimals since this is the accuracy of DFT E0 = round(E0,3) formation_energies[key] = E0 return formation_energies
def get_stoich_formula(formula, return_elements=True): """Get element independent stoichiometry formula i.e. TiO2 -> AB2""" alphabet = list(string.ascii_uppercase) elem_list = string2symbols(formula) unique_symbols, counts = np.unique(elem_list, return_counts=True) sorted_idx = np.argsort(counts) formula = '' for i, j in enumerate(sorted_idx): if counts[j] > 1: formula += alphabet[i] + str(counts[j]) else: formula += alphabet[i] if return_elements: return formula, unique_symbols else: return formula
def solvated(symbols): """Extract solvation energies from database. symbols: str Extract only those molecules that contain the chemical elements given by the symbols string (plus water and H+). Data from: Johnson JW, Oelkers EH, Helgeson HC (1992) Comput Geosci 18(7):899. doi:10.1016/0098-3004(92)90029-Q and: Pourbaix M (1966) Atlas of electrochemical equilibria in aqueous solutions. No. v. 1 in Atlas of Electrochemical Equilibria in Aqueous Solutions. Pergamon Press, New York. Returns list of (name, energy) tuples. """ if isinstance(symbols, basestring): symbols = set(string2symbols(symbols)) if len(_solvated) == 0: for line in _aqueous.splitlines(): energy, formula = line.split(',') name = formula + '(aq)' count, charge, aq = parse_formula(name) energy = float(energy) * 0.001 * units.kcal / units.mol _solvated.append((name, count, charge, aq, energy)) references = [] for name, count, charge, aq, energy in _solvated: for symbol in count: if symbol not in 'HO' and symbol not in symbols: break else: references.append((name, energy)) return references
def get_composition(species_string): """ Convert string of species into a dictionary of species and the number of each species. :param species_string: A string of the reaction species. Should be a chemical formula string that may also contain '-','&',or,'pe'. 'pe' is a special case corresponding to a proton-electron pair and has the compositon of H, while ele corresponds to an electron and has no associated atoms. :type species: str """ composition = {} # clean up transition states and electrochem species_string = species_string.replace('-','') species_string = species_string.replace('pe','H') species_string = species_string.replace('&','') species_string = species_string.replace('ele','') try: symbs = string2symbols(species_string) for a in set(symbs): composition[a] = symbs.count(a) except ValueError: composition = None return composition
def get_composition(species_string): """ Convert string of species into a dictionary of species and the number of each species. :param species_string: A string of the reaction species. Should be a chemical formula string that may also contain '-','&',or,'pe'. 'pe' is a special case corresponding to a proton-electron pair and has the compositon of H, while ele corresponds to an electron and has no associated atoms. :type species: str """ composition = {} # clean up transition states and electrochem species_string = species_string.replace('-', '') species_string = species_string.replace('pe', 'H') species_string = species_string.replace('&', '') species_string = species_string.replace('ele', '') try: symbs = string2symbols(species_string) for a in set(symbs): composition[a] = symbs.count(a) except ValueError: composition = None return composition
def get_atomization_energy(name): """Determine extrapolated experimental atomization energy from the database. The atomization energy is extrapolated from experimental heats of formation at room temperature, using calculated zero-point energies and thermal corrections. The atomization energy is returned in kcal/mol = 43.36 meV: >>> from ase.units import *; print kcal / mol 0.0433641146392 """ d = data[name] e = d['enthalpy'] z = d['ZPE'] dh = d['thermal correction'] ae = -e + z + dh for a in string2symbols(d['symbols']): h = data[a]['enthalpy'] dh = data[a]['thermal correction'] ae += h - dh return ae
def organize(**kwargs): """Read reactions from non-organized folder""" # do argument wrangling before turning it into an obect # since namedtuples are immutable if len(kwargs['adsorbates']) == 0: print("""Warning: no adsorbates specified, can't pick up reaction reaction energies.""") print(" Enter adsorbates like --adsorbates CO,O,CO2") print(" [Comma-separated list without spaces.]") kwargs['adsorbates'] = list( map( lambda x: (''.join(sorted(string2symbols(x)))), kwargs['adsorbates'].split(','), )) if kwargs['energy_corrections']: e_c_dict = {} for e_c in kwargs['energy_corrections'].split(','): key, value = e_c.split('=') e_c_dict.update({key: float(value)}) kwargs['energy_corrections'] = e_c_dict options = collections.namedtuple('options', kwargs.keys())(**kwargs) _organize.main(options=options)
def z2ads_index(atoms, species): """ Returns the indexes of the n atoms with the highest position in the z direction, where n is the number of atoms in the chemical formula from the species key value pair. Parameters ---------- atoms : ase atoms object. atoms.info must be a dictionary containing the key 'key_value_pairs', which is expected to contain CatMAP standard adsorbate structure key value pairs. See the ase db to catmap module in catmap. the key value pair 'species'. species : str chemical formula of the adsorbate. """ composition = string2symbols(species) n_ads = len(composition) z = atoms.get_positions()[:, 2] ads_atoms = np.argsort(z)[-n_ads:] if sorted(symbols2numbers(species)) != sorted(atoms[ads_atoms].numbers): raise AssertionError("adsorbate identification by z coord failed. " + str(species)) return ads_atoms
def last2ads_index(atoms, species): """Return the indexes of the last n atoms in the atoms object, where n is the length of the composition of the adsorbate species. This function will work on atoms objects, where the slab was set up first, and the adsorbate was added after. Parameters ---------- atoms : ase atoms object. atoms.info must be a dictionary containing the key 'key_value_pairs', which is expected to contain CATMAP standard adsorbate structure key value pairs. See the ase db to catmap module in catmap. the key value pair 'species' must be the chemical formula of the adsorbate. species : str chemical formula of the adsorbate. """ n_ads = len(string2symbols(species)) natoms = len(atoms) ads_atoms = list(range(natoms - n_ads, natoms)) if sorted(symbols2numbers(species)) != sorted(atoms[ads_atoms].numbers): raise AssertionError("last index adsorbate identification failed.") warnings.warn("Adsorbate identified by last index.") return ads_atoms
def bulk(name, crystalstructure=None, a=None, c=None, covera=None, u=None, orthorhombic=False, cubic=False): """Creating bulk systems. Crystal structure and lattice constant(s) will be guessed if not provided. name: str Chemical symbol or symbols as in 'MgO' or 'NaCl'. crystalstructure: str Must be one of sc, fcc, bcc, hcp, diamond, zincblende, rocksalt, cesiumchloride, fluorite or wurtzite. a: float Lattice constant. c: float Lattice constant. covera: float c/a ratio used for hcp. Default is ideal ratio: sqrt(8/3). u: float Internal coordinate for Wurtzite structure. orthorhombic: bool Construct orthorhombic unit cell instead of primitive cell which is the default. cubic: bool Construct cubic unit cell if possible. """ if covera is not None and c is not None: raise ValueError("Don't specify both c and c/a!") xref = None ref = {} if name in chemical_symbols: Z = atomic_numbers[name] ref = reference_states[Z] if ref is not None: xref = ref['symmetry'] structures = {'sc': 1, 'fcc': 1, 'bcc': 1, 'hcp': 1, 'diamond': 1, 'zincblende': 2, 'rocksalt':2, 'cesiumchloride':2, 'fluorite': 3, 'wurtzite': 2} if crystalstructure is None: crystalstructure = xref if crystalstructure not in structures: raise ValueError('No suitable reference data for bulk {}.' ' Reference data: {}' .format(name, ref)) if crystalstructure not in structures: raise ValueError('Unknown structure: {}.' .format(crystalstructure)) # Check name: n = len(string2symbols(name)) n0 = structures[crystalstructure] if n != n0: raise ValueError('Please specify {} for {} and not {}' .format(plural(n0, 'atom'), crystalstructure, n)) if a is None: if xref != crystalstructure: raise ValueError('You need to specify the lattice constant.') try: a = ref['a'] except KeyError: raise KeyError('No reference lattice parameter "a" for "{}"' .format(name)) if crystalstructure in ['hcp', 'wurtzite']: cubic = False if c is not None: covera = c / a elif covera is None: if xref == crystalstructure: covera = ref['c/a'] else: covera = sqrt(8 / 3) if orthorhombic and crystalstructure != 'sc': return _orthorhombic_bulk(name, crystalstructure, a, covera, u) if cubic and crystalstructure in ['bcc', 'cesiumchloride']: return _orthorhombic_bulk(name, crystalstructure, a, covera) if cubic and crystalstructure != 'sc': return _cubic_bulk(name, crystalstructure, a) if crystalstructure == 'sc': atoms = Atoms(name, cell=(a, a, a), pbc=True) elif crystalstructure == 'fcc': b = a / 2 atoms = Atoms(name, cell=[(0, b, b), (b, 0, b), (b, b, 0)], pbc=True) elif crystalstructure == 'bcc': b = a / 2 atoms = Atoms(name, cell=[(-b, b, b), (b, -b, b), (b, b, -b)], pbc=True) elif crystalstructure == 'hcp': atoms = Atoms(2 * name, scaled_positions=[(0, 0, 0), (1 / 3, 2 / 3, 0.5)], cell=[(a, 0, 0), (-a / 2, a * sqrt(3) / 2, 0), (0, 0, covera * a)], pbc=True) elif crystalstructure == 'diamond': atoms = bulk(2 * name, 'zincblende', a) elif crystalstructure == 'zincblende': s1, s2 = string2symbols(name) atoms = bulk(s1, 'fcc', a) + bulk(s2, 'fcc', a) atoms.positions[1] += a / 4 elif crystalstructure == 'rocksalt': s1, s2 = string2symbols(name) atoms = bulk(s1, 'fcc', a) + bulk(s2, 'fcc', a) atoms.positions[1, 0] += a / 2 elif crystalstructure == 'cesiumchloride': s1, s2 = string2symbols(name) atoms = bulk(s1, 'sc', a) + bulk(s2, 'sc', a) atoms.positions[1, :] += a / 2 elif crystalstructure == 'fluorite': s1, s2, s3 = string2symbols(name) atoms = bulk(s1, 'fcc', a) + bulk(s2, 'fcc', a) + bulk(s3, 'fcc', a) atoms.positions[1, :] += a / 4 atoms.positions[2, :] += a * 3 / 4 elif crystalstructure == 'wurtzite': u = u or 0.25 + 1 / 3 / covera**2 atoms = Atoms(2 * name, scaled_positions=[(0, 0, 0), (1 / 3, 2 / 3, 0.5 - u), (1 / 3, 2 / 3, 0.5), (0, 0, 1 - u)], cell=[(a, 0, 0), (-a / 2, a * sqrt(3) / 2, 0), (0, 0, a * covera)], pbc=True) else: raise ValueError('Unknown crystal structure: ' + crystalstructure) return atoms
def set_output_attrs(self,rxn_parameters): """ :param rxn_parameters: Reaction parameters. :type rxn_parameters: list """ if True in [v in self.mapper._solver_output for v in self.output_variables]: cvgs = self._coverage self._coverage = list(self.solver.get_coverage( rxn_parameters,c0=cvgs)) #verify coverage self._rate = list(self.solver.get_rate(rxn_parameters, coverages=self._coverage)) if ( 'turnover_frequency' in self.output_variables or 'production_rate' in self.output_variables or 'consumption_rate' in self.output_variables or 'rxn_direction' in self.output_variables): self._turnover_frequency = self.get_turnover_frequency( rxn_parameters) if 'selectivity' in self.output_variables: self._selectivity = self.get_selectivity(rxn_parameters) self.output_labels['selectivity'] = self.gas_names if 'carbon_selectivity' in self.output_variables: weights = [] for g in self.gas_names: name,site = g.split('_') weight = string2symbols(name).count('C') weights.append(weight) self._carbon_selectivity = self.get_selectivity(rxn_parameters,weights=weights) self.output_labels['carbon_selectivity'] = self.gas_names if 'rate_control' in self.output_variables: self._rate_control = self.get_rate_control(rxn_parameters) self.output_labels['rate_control'] = [self.gas_names,self.parameter_names] if 'selectivity_control' in self.output_variables: self._selectivity_control = self.get_selectivity_control(rxn_parameters) self.output_labels['selectivity_control'] = [self.gas_names,self.parameter_names] if 'rxn_order' in self.output_variables: self._rxn_order = self.get_rxn_order(rxn_parameters) self.output_labels['rxn_order'] = [self.gas_names,self.gas_names] if 'apparent_activation_energy' in self.output_variables: self._apparent_activation_energy = self.get_apparent_activation_energy(rxn_parameters) self.output_labels['apparent_activation_energy'] = self.gas_names if 'interacting_energy' in self.output_variables: if self.adsorbate_interaction_model in [None,'ideal']: self._interacting_energy = rxn_parameters else: self._interacting_energy = self.get_interacting_energies(rxn_parameters) self.output_labels['interacting_energy'] = self.adsorbate_names+self.transition_state_names if 'directional_rates' in self.output_variables: self._directional_rates = self.get_directional_rates(rxn_parameters) self.output_labels['directional_rates'] = [str(rxn) + ' forward' for rxn in self.elementary_rxns] + \ [str(rxn) + ' reverse' for rxn in self.elementary_rxns] if 'turnover_frequency' in self.output_variables: self.output_labels['turnover_frequency'] = self.gas_names for out in self.output_variables: if out == 'production_rate': self._production_rate = [max(0,r) for r in self._turnover_frequency] self.output_labels['production_rate'] = self.gas_names if out == 'consumption_rate': self._consumption_rate = [max(0,-r) for r in self._turnover_frequency] self.output_labels['consumption_rate'] = self.gas_names if out == 'forward_rate': self._forward_rate = [max(0,r) for r in self._rate] self.output_labels['forward_rate'] = self.elementary_rxns if out == 'reverse_rate': self._reverse_rate = [max(0,-r) for r in self._rate] self.output_labels['reverse_rate'] = self.elementary_rxns if out == 'rxn_direction': self._rxn_direction = [np.sign(r) for r in self._turnover_frequency] self.output_labels['rxn_direction'] = self.elementary_rxns if out == 'rate_constant': self._rate_constant = list(self._kf)+list(self._kr) self.output_labels['rate_constant'] = self.elementary_rxns + self.elementary_rxns if out == 'forward_rate_constant': self._forward_rate_constant = list(self._kf) self.output_labels['forward_rate_constant'] = self.elementary_rxns if out == 'reverse_rate_constant': self._reverse_rate_constant = list(self._kr) self.output_labels['reverse_rate_constant'] = self.elementary_rxns if out == 'equilibrium_constant': self._equilibrium_constant = [kf/kr for kf,kr in zip(self._kf,self._kr)] self.output_labels['equilibrium_constant'] = self.elementary_rxns