def get_bond_length(sp1, sp2, bond_order=1): """ Get the bond length between two species. Args: sp1: First specie. sp2: Second specie. bond_order: For species with different possible bond orders, this allows one to obtain the bond length for a particular bond order. For example, to get the C=C bond length instead of the C-C bond length, this should be set to 2. Defaults to 1. Returns: Bond length in Angstrom. None if no data is available. """ syms = tuple( sorted([ smart_element_or_specie(sp1).symbol, smart_element_or_specie(sp2).symbol ])) if syms in bond_lengths: all_lengths = bond_lengths[syms] if bond_order: return all_lengths.get(bond_order) else: return all_lengths.get(1) return None
def get_bond_length(sp1, sp2, bond_order=1): """ Get the bond length between two species. Args: sp1: First specie. sp2: Second specie. bond_order: For species with different possible bond orders, this allows one to obtain the bond length for a particular bond order. For example, to get the C=C bond length instead of the C-C bond length, this should be set to 2. Defaults to 1. Returns: Bond length in Angstrom. None if no data is available. """ syms = tuple(sorted([smart_element_or_specie(sp1).symbol, smart_element_or_specie(sp2).symbol])) if syms in bond_lengths: all_lengths = bond_lengths[syms] if bond_order: return all_lengths.get(bond_order) else: return all_lengths.get(1) return None
def __init__(self, atoms_n_occu, coords, properties=None): """ Create a *non-periodic* site. Args: atoms_n_occu: Species on the site. Can be: i. A sequence of element / specie specified either as string symbols, e.g. ["Li", "Fe2+", "P", ...] or atomic numbers, e.g., (3, 56, ...) or actual Element or Specie objects. ii. List of dict of elements/species and occupancies, e.g., [{"Fe" : 0.5, "Mn":0.5}, ...]. This allows the setup of disordered structures. coords: Cartesian coordinates of site. properties: Properties associated with the site as a dict, e.g. {"magmom": 5}. Defaults to None. """ if issubclass(atoms_n_occu.__class__, collections.Mapping): self._species = Composition({smart_element_or_specie(k): v for k, v in atoms_n_occu.items()}) totaloccu = self._species.num_atoms if totaloccu > 1: raise ValueError("Species occupancies sum to more than 1!") self._is_ordered = (totaloccu == 1 and len(self._species) == 1) else: self._species = Composition( {smart_element_or_specie(atoms_n_occu): 1}) self._is_ordered = True self._coords = coords self._properties = properties if properties else {}
def apply_transformation(self, structure): species_map = {} for k, v in self._species_map.items(): if isinstance(v, dict): value = {smart_element_or_specie(x): y for x, y in v.items()} else: value = smart_element_or_specie(v) species_map[smart_element_or_specie(k)] = value s = Structure.from_sites(structure.sites) s.replace_species(species_map) return s
def apply_transformation(self, structure): species_map = {} for k, v in self._species_map.items(): if isinstance(v, dict): value = {smart_element_or_specie(x): y for x, y in v.items()} else: value = smart_element_or_specie(v) species_map[smart_element_or_specie(k)] = value editor = StructureEditor(structure) editor.replace_species(species_map) return editor.modified_structure
def __init__(self, *args, **kwargs): """ Very flexible Composition construction, similar to the built-in Python dict(). Also extended to allow simple string init. Args: Any form supported by the Python built-in dict() function. 1. A dict of either {Element/Specie: amount}, {string symbol:amount}, or {atomic number:amount} or any mixture of these. E.g., {Element("Li"):2 ,Element("O"):1}, {"Li":2, "O":1}, {3:2, 8:1} all result in a Li2O composition. 2. Keyword arg initialization, similar to a dict, e.g., Compostion(Li = 2, O = 1) In addition, the Composition constructor also allows a single string as an input formula. E.g., Composition("Li2O"). """ if len(args) == 1 and isinstance(args[0], basestring): elmap = self._parse_formula(args[0]) else: elmap = dict(*args, **kwargs) if any([e < 0 for e in elmap.values()]): raise ValueError("Amounts in Composition cannot be negative!") self._elmap = {smart_element_or_specie(k): v for k, v in elmap.items()} self._natoms = sum(self._elmap.values())
def apply_transformation(self, structure, return_ranked_list=False): """ Apply the transformation. Args: structure: input structure return_ranked_list: Boolean stating whether or not multiple structures are returned. If return_ranked_list is an int, that number of structures is returned. Returns: Depending on returned_ranked list, either a transformed structure or a list of dictionaries, where each dictionary is of the form {"structure" = .... , "other_arguments"} the key "transformation" is reserved for the transformation that was actually applied to the structure. This transformation is parsed by the alchemy classes for generating a more specific transformation history. Any other information will be stored in the transformation_parameters dictionary in the transmuted structure class. """ sp = smart_element_or_specie(self._specie) specie_indices = [i for i in xrange(len(structure)) if structure[i].species_and_occu == Composition({sp: 1})] trans = PartialRemoveSitesTransformation([specie_indices], [self._frac], algo=self._algo) return trans.apply_transformation(structure, return_ranked_list)
def get_conversion_factor(structure, species, temperature): """ Conversion factor to convert between cm^2/s diffusivity measurements and mS/cm conductivity measurements based on number of atoms of diffusing species and . Args: structure: Input structure. species: Diffusing species. temperature: Temperature of the diffusion run in Kelvin. Returns: Conversion factor. Conductivity (in mS/cm) = Conversion Factor * Diffusivity (in cm^2/s) """ df_sp = smart_element_or_specie(species) if df_sp.Z in [1, 3, 11, 19, 37, 55]: z = 1 else: z = df_sp.oxi_state n = structure.composition[species] V = structure.volume * 1e-24 # units cm^3 F = ELECTRON_CHARGE * AVOGADROS_CONST # sA/mol return 1000 * n / (V * AVOGADROS_CONST) * z ** 2 * F ** 2\ / (BOLTZMANN_CONST * AVOGADROS_CONST * temperature)
def __init__(self, *args, **kwargs): """ Very flexible Composition construction, similar to the built-in Python dict(). Also extended to allow simple string init. Args: Any form supported by the Python built-in dict() function. 1. A dict of either {Element/Specie: amount}, {string symbol:amount}, or {atomic number:amount} or any mixture of these. E.g., {Element("Li"):2 ,Element("O"):1}, {"Li":2, "O":1}, {3:2, 8:1} all result in a Li2O composition. 2. Keyword arg initialization, similar to a dict, e.g., Compostion(Li = 2, O = 1) In addition, the Composition constructor also allows a single string as an input formula. E.g., Composition("Li2O"). """ if len(args) == 1 and isinstance(args[0], basestring): elmap = self._parse_formula(args[0]) else: elmap = dict(*args, **kwargs) if any([e < 0 for e in elmap.values()]): raise CompositionError("Amounts in Composition cannot be " "negative!") self._elmap = {smart_element_or_specie(k): v for k, v in elmap.items()} self._natoms = sum(self._elmap.values())
def apply_transformation(self, structure): charge = structure.charge specie = smart_element_or_specie(self._charge_balance_sp) num_to_remove = charge / specie.oxi_state num_in_structure = structure.composition[specie] removal_fraction = num_to_remove / num_in_structure if removal_fraction < 0: raise ValueError("addition of specie not yet supported by " "ChargeBalanceTransformation") trans = SubstitutionTransformation({self._charge_balance_sp: {self._charge_balance_sp: 1 - removal_fraction}}) return trans.apply_transformation(structure)
def get_wt_fraction(self, el): """ Args: el: Element or Specie Returns: Weight fraction for element el in Composition """ return smart_element_or_specie(el).atomic_mass * self[el] / self.weight
def reduce_formula(sym_amt): """ Help method to reduce a sym_amt dict to a reduced formula and factor. Args: Dict of the form {symbol: amount}. Returns: (reduced_formula, factor). """ syms = sorted(sym_amt.keys(), key=lambda s: smart_element_or_specie(s).X) syms = filter(lambda s: sym_amt[s] > Composition.amount_tolerance, syms) num_el = len(syms) contains_polyanion = (num_el >= 3 and smart_element_or_specie(syms[num_el - 1]).X - smart_element_or_specie(syms[num_el - 2]).X < 1.65) factor = reduce(gcd, sym_amt.values()) reduced_form = [] n = num_el - 2 if contains_polyanion else num_el for i in range(0, n): s = syms[i] normamt = sym_amt[s] * 1.0 / factor reduced_form.append(s) reduced_form.append(formula_double_format(normamt)) if contains_polyanion: poly_sym_amt = { syms[i]: sym_amt[syms[i]] / factor for i in range(n, num_el) } (poly_form, poly_factor) = reduce_formula(poly_sym_amt) if poly_factor != 1: reduced_form.append("({}){}".format(poly_form, int(poly_factor))) else: reduced_form.append(poly_form) reduced_form = "".join(reduced_form) return reduced_form, factor
def reduce_formula(sym_amt): """ Help method to reduce a sym_amt dict to a reduced formula and factor. Args: Dict of the form {symbol: amount}. Returns: (reduced_formula, factor). """ syms = sorted(sym_amt.keys(), key=lambda s: smart_element_or_specie(s).X) syms = filter(lambda s: sym_amt[s] > Composition.amount_tolerance, syms) num_el = len(syms) contains_polyanion = (num_el >= 3 and smart_element_or_specie(syms[num_el - 1]).X - smart_element_or_specie(syms[num_el - 2]).X < 1.65) factor = reduce(gcd, sym_amt.values()) reduced_form = [] n = num_el - 2 if contains_polyanion else num_el for i in range(0, n): s = syms[i] normamt = sym_amt[s] * 1.0 / factor reduced_form.append(s) reduced_form.append(formula_double_format(normamt)) if contains_polyanion: poly_sym_amt = {syms[i]: sym_amt[syms[i]] / factor for i in range(n, num_el)} (poly_form, poly_factor) = reduce_formula(poly_sym_amt) if poly_factor != 1: reduced_form.append("({}){}".format(poly_form, int(poly_factor))) else: reduced_form.append(poly_form) reduced_form = "".join(reduced_form) return reduced_form, factor
def formula(self): """ Returns a formula string, with elements sorted by electronegativity, e.g., Li4 Fe4 P4 O16. """ sym_amt = self.get_el_amt_dict() syms = sorted(sym_amt.keys(), key=lambda s: smart_element_or_specie(s).X) formula = [] for s in syms: if sym_amt[s] != 0: formula.append(s + formula_double_format(sym_amt[s], False)) return " ".join(formula)
def __add__(self, other): """ Adds two compositions. For example, an Fe2O3 composition + an FeO composition gives a Fe3O4 composition. """ new_el_map = {el: self[el] for el in self} for k in other.keys(): el = smart_element_or_specie(k) if el in self: new_el_map[el] += other[k] else: new_el_map[el] = other[k] return Composition(new_el_map)
def apply_transformation(self, structure, return_ranked_list=False): #Make a mutable structure first mods = Structure.from_sites(structure) for sp, spin in self.mag_species_spin.items(): sp = smart_element_or_specie(sp) oxi_state = getattr(sp, "oxi_state", 0) up = Specie(sp.symbol, oxi_state, {"spin": abs(spin)}) down = Specie(sp.symbol, oxi_state, {"spin": -abs(spin)}) mods.replace_species( {sp: Composition({up: self.order_parameter, down: 1 - self.order_parameter})}) enum_args = self.enum_kwargs enum_args["min_cell_size"] = max(int( MagOrderingTransformation.determine_min_cell( structure, self.mag_species_spin, self.order_parameter)), enum_args.get("min_cell_size")) max_cell = self.enum_kwargs.get('max_cell_size') if max_cell: if enum_args["min_cell_size"] > max_cell: raise ValueError('Specified max cell size is smaller' ' than the minimum enumerable cell size') else: enum_args["max_cell_size"] = enum_args["min_cell_size"] t = EnumerateStructureTransformation(**enum_args) alls = t.apply_transformation(mods, return_ranked_list=return_ranked_list) try: num_to_return = int(return_ranked_list) except ValueError: num_to_return = 1 if num_to_return == 1: return alls[0]["structure"] m = StructureMatcher(comparator=SpinComparator()) grouped = m.group_structures([d["structure"] for d in alls]) alls = [{"structure": g[0], "energy": self.emodel.get_energy(g[0])} for g in grouped] self._all_structures = sorted(alls, key=lambda d: d["energy"]) return self._all_structures[0:num_to_return]
def __init__(self, specie_and_min_dist_dict): """ Args: specie_and_min_dist_dict: A species string to float mapping. For example, {"Na+": 1} means that all Na+ ions must be at least 1 Angstrom away from each other. Multiple species criteria can be applied. Note that the testing is done based on the actual object. If you have a structure with Element, you must use {"Na":1} instead to filter based on Element and not Specie. """ self.specie_and_min_dist = {smart_element_or_specie(k): v for k, v in specie_and_min_dist_dict.items()}
def apply_transformation(self, structure): charge = structure.charge specie = smart_element_or_specie(self._charge_balance_sp) num_to_remove = charge / specie.oxi_state num_in_structure = structure.composition[specie] removal_fraction = num_to_remove / num_in_structure if removal_fraction < 0: raise ValueError("addition of specie not yet supported by " "ChargeBalanceTransformation") trans = SubstitutionTransformation({ self._charge_balance_sp: { self._charge_balance_sp: 1 - removal_fraction } }) return trans.apply_transformation(structure)
def __init__(self, specie_and_min_dist_dict): """ Args: specie_and_min_dist_dict: A species string to float mapping. For example, {"Na+": 1} means that all Na+ ions must be at least 1 Angstrom away from each other. Multiple species criteria can be applied. Note that the testing is done based on the actual object. If you have a structure with Element, you must use {"Na":1} instead to filter based on Element and not Specie. """ self.specie_and_min_dist = { smart_element_or_specie(k): v for k, v in specie_and_min_dist_dict.items() }
def __sub__(self, other): """ Subtracts two compositions. For example, an Fe2O3 composition - an FeO composition gives an FeO2 composition. Raises: CompositionError if the subtracted composition is greater than the original composition in any of its elements. """ new_el_map = {el: self[el] for el in self} for k in other.keys(): el = smart_element_or_specie(k) if el in self and other[k] <= self[el]: new_el_map[el] -= other[k] else: raise CompositionError( "All elements in subtracted composition must exist in " "original composition in equal or lesser amount!") return Composition(new_el_map)
def __sub__(self, other): """ Subtracts two compositions. For example, an Fe2O3 composition - an FeO composition gives an FeO2 composition. Raises: ValueError if the subtracted composition is greater than the original composition in any of its elements. """ new_el_map = {el: self[el] for el in self} for k in other.keys(): el = smart_element_or_specie(k) if el in self and other[k] <= self[el]: new_el_map[el] -= other[k] else: raise ValueError(("All elements in subtracted composition " "must exist in original composition in " "equal or lesser amount!")) return Composition(new_el_map)
def __getitem__(self, el): """ Get the amount for element. """ return self._elmap.get(smart_element_or_specie(el), 0)
def apply_transformation(self, structure): editor = StructureEditor(structure) map(editor.remove_species, [[smart_element_or_specie(sp)] for sp in self._species]) return editor.modified_structure
def apply_transformation(self, structure): s = Structure.from_sites(structure.sites) map(s.remove_species, [[smart_element_or_specie(sp)] for sp in self._species]) return s
def test_smart_element_or_specie(self): self.assertEqual(smart_element_or_specie("Fe2+"), Specie("Fe", 2)) self.assertEqual(smart_element_or_specie("3"), Element("Li")) self.assertEqual(smart_element_or_specie("U"), Element("U")) self.assertEqual(smart_element_or_specie("X2+"), DummySpecie("X", 2)) self.assertEqual(smart_element_or_specie("Mn3+"), Specie("Mn", 3))