def test_equals(self): # generate randomized compositions for robustness (tests might pass for specific elements # but fail for others) random_z = random.randint(1, 92) fixed_el = Element.from_Z(random_z) other_z = random.randint(1, 92) while other_z == random_z: other_z = random.randint(1, 92) comp1 = Composition({fixed_el: 1, Element.from_Z(other_z): 0}) other_z = random.randint(1, 92) while other_z == random_z: other_z = random.randint(1, 92) comp2 = Composition({fixed_el: 1, Element.from_Z(other_z): 0}) self.assertEqual( comp1, comp2, "Composition equality test failed. " + f"{comp1.formula} should be equal to {comp2.formula}", ) self.assertEqual(comp1.__hash__(), comp2.__hash__(), "Hashcode equality test failed!") c1, c2 = self.comp[:2] self.assertTrue(c1 == c1) self.assertFalse(c1 == c2)
def test_equals(self): random_z = random.randint(1, 92) fixed_el = Element.from_Z(random_z) other_z = random.randint(1, 92) while other_z == random_z: other_z = random.randint(1, 92) comp1 = Composition({fixed_el: 1, Element.from_Z(other_z): 0}) other_z = random.randint(1, 92) while other_z == random_z: other_z = random.randint(1, 92) comp2 = Composition({fixed_el: 1, Element.from_Z(other_z): 0}) self.assertEqual( comp1, comp2, "Composition equality test failed. " + "%s should be equal to %s" % (comp1.formula, comp2.formula)) self.assertEqual(comp1.__hash__(), comp2.__hash__(), "Hashcode equality test failed!")
def test_equals(self): random_z = random.randint(1, 92) fixed_el = Element.from_Z(random_z) other_z = random.randint(1, 92) while other_z == random_z: other_z = random.randint(1, 92) comp1 = Composition({fixed_el: 1, Element.from_Z(other_z): 0}) other_z = random.randint(1, 92) while other_z == random_z: other_z = random.randint(1, 92) comp2 = Composition({fixed_el: 1, Element.from_Z(other_z): 0}) self.assertEqual( comp1, comp2, "Composition equality test failed. " + "%s should be equal to %s" % (comp1.formula, comp2.formula), ) self.assertEqual(comp1.__hash__(), comp2.__hash__(), "Hashcode equality test failed!")
class Ion(MSONable): """ Basic ion object. It is just a Composition object with an additional variable to store charge. The net charge can either be represented as Mn++, or Mn+2, or Mn[2+]. Note the order of the sign and magnitude in each representation. """ def __init__(self, composition, charge=0.0, properties=None): """ Flexible Ion construction, similar to Composition. For more information, please see pymatgen.core.Composition """ self._composition = Composition(composition) self._charge = charge self._properties = properties if properties else {} def __getattr__(self, a): if a in self._properties: return self._properties[a] try: return getattr(self._composition, a) except: raise AttributeError(a) @staticmethod def from_formula(formula): charge = 0.0 f = formula m = re.search(r"\[([^\[\]]+)\]", f) if m: m_chg = re.search("([\.\d]*)([+-])", m.group(1)) if m_chg: if m_chg.group(1) != "": charge += float(m_chg.group(1)) * \ (float(m_chg.group(2) + "1")) else: charge += float(m_chg.group(2) + "1") f = f.replace(m.group(), "", 1) m = re.search(r"\(aq\)", f) if m: f = f.replace(m.group(), "", 1) for m_chg in re.finditer("([+-])([\.\d]*)", f): sign = m_chg.group(1) sgn = float(str(sign + "1")) if m_chg.group(2).strip() != "": charge += float(m_chg.group(2)) * sgn else: charge += sgn f = f.replace(m_chg.group(), "", 1) composition = Composition(f) return Ion(composition, charge) @property def formula(self): """ Returns a formula string, with elements sorted by electronegativity, e.g., Li4 Fe4 P4 O16. """ formula = self._composition.formula chg_str = "" if self._charge > 0: chg_str = " +" + formula_double_format(self._charge, False) elif self._charge < 0: chg_str = " " + formula_double_format(self._charge, False) return formula + chg_str @property def anonymized_formula(self): """ An anonymized formula. Appends charge to the end of anonymized composition """ anon_formula = self._composition.anonymized_formula chg = self._charge chg_str = "" if chg > 0: chg_str += ("{}{}".format('+', str(int(chg)))) elif chg < 0: chg_str += ("{}{}".format('-', str(int(np.abs(chg))))) return anon_formula + chg_str @property def reduced_formula(self): """ Returns a reduced formula string with appended charge. """ reduced_formula = self._composition.reduced_formula charge = self._charge / float( self._composition.get_reduced_composition_and_factor()[1]) if charge > 0: if abs(charge) == 1: chg_str = "[+]" else: chg_str = "[" + formula_double_format(charge, False) + "+]" elif charge < 0: if abs(charge) == 1: chg_str = "[-]" else: chg_str = "[{}-]".format( formula_double_format(abs(charge), False)) else: chg_str = "(aq)" return reduced_formula + chg_str @property def alphabetical_formula(self): """ Returns a reduced formula string with appended charge """ alph_formula = self._composition.alphabetical_formula chg_str = "" if self._charge > 0: chg_str = " +" + formula_double_format(self._charge, False) elif self._charge < 0: chg_str = " " + formula_double_format(self._charge, False) return alph_formula + chg_str @property def charge(self): """ Charge of the ion """ return self._charge @property def composition(self): """ Return composition object """ return self._composition @property def to_dict(self): """ Returns: dict with composition, as well as charge """ d = self._composition.to_dict d['charge'] = self._charge return d @classmethod def from_dict(cls, d): """ Generates an ion object from a dict created by to_dict. Args: d: {symbol: amount} dict. """ # composition = Composition.from_dict(d['composition']) charge = d['charge'] composition = Composition({i: d[i] for i in d if i != 'charge'}) return Ion(composition, charge) @property def to_reduced_dict(self): """ Returns: dict with element symbol and reduced amount e.g., {"Fe": 2.0, "O":3.0}. """ reduced_formula = self._composition.reduced_formula c = Composition(reduced_formula) d = c.to_dict d['charge'] = self._charge return d def __eq__(self, other): if self.composition != other.composition: return False if self.charge != other.charge: return False return True def __ne__(self, other): return not self.__eq__(other) def __add__(self, other): """ Addition of two ions. """ new_composition = self.composition + other.composition new_charge = self.charge + other.charge return Ion(new_composition, new_charge) def __sub__(self, other): """ Subtraction of two ions """ new_composition = self.composition - other.composition new_charge = self.charge - other.charge return Ion(new_composition, new_charge) def __mul__(self, other): """ Multiplication of an Ion with a factor """ new_composition = self.composition * other new_charge = self.charge * other return Ion(new_composition, new_charge) def __hash__(self): #for now, just use the composition hash code. return self._composition.__hash__() def __len__(self): return len(self._composition) def __str__(self): return self.formula def __repr__(self): return "Ion: " + self.formula def __getitem__(self, el): return self._composition.get(el, 0)
class Ion(MSONable): """ Basic ion object. It is just a Composition object with an additional variable to store charge. The net charge can either be represented as Mn++, or Mn+2, or Mn[2+]. Note the order of the sign and magnitude in each representation. """ def __init__(self, composition, charge=0.0, properties=None): """ Flexible Ion construction, similar to Composition. For more information, please see pymatgen.core.Composition """ self._composition = Composition(composition) self._charge = charge self._properties = properties if properties else {} def __getattr__(self, a): if a in self._properties: return self._properties[a] try: return getattr(self._composition, a) except: raise AttributeError(a) @staticmethod def from_formula(formula): charge = 0.0 f = formula m = re.search(r"\[([^\[\]]+)\]", f) if m: m_chg = re.search(r"([\.\d]*)([+-])", m.group(1)) if m_chg: if m_chg.group(1) != "": charge += float(m_chg.group(1)) * \ (float(m_chg.group(2) + "1")) else: charge += float(m_chg.group(2) + "1") f = f.replace(m.group(), "", 1) m = re.search(r"\(aq\)", f) if m: f = f.replace(m.group(), "", 1) for m_chg in re.finditer(r"([+-])([\.\d]*)", f): sign = m_chg.group(1) sgn = float(str(sign + "1")) if m_chg.group(2).strip() != "": charge += float(m_chg.group(2)) * sgn else: charge += sgn f = f.replace(m_chg.group(), "", 1) composition = Composition(f) return Ion(composition, charge) @property def formula(self): """ Returns a formula string, with elements sorted by electronegativity, e.g., Li4 Fe4 P4 O16. """ formula = self._composition.formula chg_str = "" if self._charge > 0: chg_str = " +" + formula_double_format(self._charge, False) elif self._charge < 0: chg_str = " " + formula_double_format(self._charge, False) return formula + chg_str @property def anonymized_formula(self): """ An anonymized formula. Appends charge to the end of anonymized composition """ anon_formula = self._composition.anonymized_formula chg = self._charge chg_str = "" if chg > 0: chg_str += ("{}{}".format('+', str(int(chg)))) elif chg < 0: chg_str += ("{}{}".format('-', str(int(np.abs(chg))))) return anon_formula + chg_str @property def reduced_formula(self): """ Returns a reduced formula string with appended charge. """ reduced_formula = self._composition.reduced_formula charge = self._charge / float(self._composition. get_reduced_composition_and_factor()[1]) if charge > 0: if abs(charge) == 1: chg_str = "[+]" else: chg_str = "[" + formula_double_format(charge, False) + "+]" elif charge < 0: if abs(charge) == 1: chg_str = "[-]" else: chg_str = "[{}-]".format(formula_double_format(abs(charge), False)) else: chg_str = "(aq)" return reduced_formula + chg_str @property def alphabetical_formula(self): """ Returns a reduced formula string with appended charge """ alph_formula = self._composition.alphabetical_formula chg_str = "" if self._charge > 0: chg_str = " +" + formula_double_format(self._charge, False) elif self._charge < 0: chg_str = " " + formula_double_format(self._charge, False) return alph_formula + chg_str @property def charge(self): """ Charge of the ion """ return self._charge @property def composition(self): """ Return composition object """ return self._composition def as_dict(self): """ Returns: dict with composition, as well as charge """ d = self._composition.as_dict() d['charge'] = self._charge return d @classmethod def from_dict(cls, d): """ Generates an ion object from a dict created by as_dict(). Args: d: {symbol: amount} dict. """ # composition = Composition.from_dict(d['composition']) charge = d['charge'] composition = Composition({i: d[i] for i in d if i != 'charge'}) return Ion(composition, charge) @property def to_reduced_dict(self): """ Returns: dict with element symbol and reduced amount e.g., {"Fe": 2.0, "O":3.0}. """ reduced_formula = self._composition.reduced_formula c = Composition(reduced_formula) d = c.as_dict() d['charge'] = self._charge return d def __eq__(self, other): if self.composition != other.composition: return False if self.charge != other.charge: return False return True def __ne__(self, other): return not self.__eq__(other) def __add__(self, other): """ Addition of two ions. """ new_composition = self.composition + other.composition new_charge = self.charge + other.charge return Ion(new_composition, new_charge) def __sub__(self, other): """ Subtraction of two ions """ new_composition = self.composition - other.composition new_charge = self.charge - other.charge return Ion(new_composition, new_charge) def __mul__(self, other): """ Multiplication of an Ion with a factor """ new_composition = self.composition * other new_charge = self.charge * other return Ion(new_composition, new_charge) def __hash__(self): #for now, just use the composition hash code. return self._composition.__hash__() def __len__(self): return len(self._composition) def __str__(self): return self.formula def __repr__(self): return "Ion: " + self.formula def __getitem__(self, el): return self._composition.get(el, 0)