Ejemplo n.º 1
0
 def setUp(self):
     entrylist = list()
     weights = list()
     comp = Composition("Mn2O3")
     entry = PDEntry(comp, 49)
     entrylist.append(PourbaixEntry(entry))
     weights.append(1.0)
     comp = Ion.from_formula("MnO4[-]")
     entry = IonEntry(comp, 25)
     entrylist.append(PourbaixEntry(entry))
     weights.append(0.25)
     comp = Composition("Fe2O3")
     entry = PDEntry(comp, 50)
     entrylist.append(PourbaixEntry(entry))
     weights.append(0.5)
     comp = Ion.from_formula("Fe[2+]")
     entry = IonEntry(comp, 15)
     entrylist.append(PourbaixEntry(entry))
     weights.append(2.5)
     comp = Ion.from_formula("Fe[3+]")
     entry = IonEntry(comp, 20)
     entrylist.append(PourbaixEntry(entry))
     weights.append(1.5)
     self.weights = weights
     self.entrylist = entrylist
     self.multientry = MultiEntry(entrylist, weights)
Ejemplo n.º 2
0
 def setUp(self):
     entrylist = list()
     weights = list()
     comp = Composition("Mn2O3")
     entry = PDEntry(comp, 49)
     entrylist.append(PourbaixEntry(entry))
     weights.append(1.0)
     comp = Ion.from_formula("MnO4[-]")
     entry = IonEntry(comp, 25)
     entrylist.append(PourbaixEntry(entry))
     weights.append(0.25)
     comp = Composition("Fe2O3")
     entry = PDEntry(comp, 50)
     entrylist.append(PourbaixEntry(entry))
     weights.append(0.5)
     comp = Ion.from_formula("Fe[2+]")
     entry = IonEntry(comp, 15)
     entrylist.append(PourbaixEntry(entry))
     weights.append(2.5)
     comp = Ion.from_formula("Fe[3+]")
     entry = IonEntry(comp, 20)
     entrylist.append(PourbaixEntry(entry))
     weights.append(1.5)
     self.weights = weights
     self.entrylist = entrylist
     self.multientry = MultiEntry(entrylist, weights)
Ejemplo n.º 3
0
 def test_init_(self):
     c = Composition({'Fe': 4, 'O': 16, 'P': 4})
     charge = 4
     self.assertEqual("Fe4 P4 O16 +4", Ion(c, charge).formula)
     f = {1: 1, 8: 1}
     charge = -1
     self.assertEqual("H1 O1 -1", Ion(Composition(f), charge).formula)
     self.assertEqual("S2 O3 -2", Ion(Composition(S=2, O=3), -2).formula)
Ejemplo n.º 4
0
 def setUp(self):
     self.comp = list()
     self.comp.append(Ion.from_formula("Li+"))
     self.comp.append(Ion.from_formula("MnO4-"))
     self.comp.append(Ion.from_formula("Mn++"))
     self.comp.append(Ion.from_formula("PO3-2"))
     self.comp.append(Ion.from_formula("Fe(CN)6-3"))
     self.comp.append(Ion.from_formula("Fe(CN)6----"))
     self.comp.append(Ion.from_formula("Fe2((PO4)3(CO3)5)2-3"))
     self.comp.append(Ion.from_formula("Ca[2+]"))
     self.comp.append(Ion.from_formula("NaOH(aq)"))
Ejemplo n.º 5
0
 def setUp(self):
     self.comp = list()
     self.comp.append(Ion.from_formula("Li+"))
     self.comp.append(Ion.from_formula("MnO4-"))
     self.comp.append(Ion.from_formula("Mn++"))
     self.comp.append(Ion.from_formula("PO3-2"))
     self.comp.append(Ion.from_formula("Fe(CN)6-3"))
     self.comp.append(Ion.from_formula("Fe(CN)6----"))
     self.comp.append(Ion.from_formula("Fe2((PO4)3(CO3)5)2-3"))
     self.comp.append(Ion.from_formula("Ca[2+]"))
     self.comp.append(Ion.from_formula("NaOH(aq)"))
Ejemplo n.º 6
0
 def len_elts(entry):
     if "(s)" in entry:
         comp = Composition(entry[:-3])
     else:
         comp = Ion.from_formula(entry)
     return len([el for el in comp.elements if el not in
                 [Element("H"), Element("O")]])
Ejemplo n.º 7
0
 def from_dict(cls, d):
     """
     Returns an IonEntry object from a dict.
     """
     return IonEntry(
         Ion.from_dict(d["ion"]), d["energy"], d.get("name"), d.get("attribute")
     )
Ejemplo n.º 8
0
    def test_mpr_pipeline(self):
        from pymatgen.ext.matproj import MPRester

        mpr = MPRester()
        data = mpr.get_pourbaix_entries(["Zn"])
        pbx = PourbaixDiagram(data, filter_solids=True, conc_dict={"Zn": 1e-8})
        pbx.find_stable_entry(10, 0)

        data = mpr.get_pourbaix_entries(["Ag", "Te"])
        pbx = PourbaixDiagram(data, filter_solids=True, conc_dict={"Ag": 1e-8, "Te": 1e-8})
        self.assertEqual(len(pbx.stable_entries), 30)
        test_entry = pbx.find_stable_entry(8, 2)
        self.assertAlmostEqual(test_entry.energy, 2.3894017960000009, 1)

        # Test custom ions
        entries = mpr.get_pourbaix_entries(["Sn", "C", "Na"])
        ion = IonEntry(Ion.from_formula("NaO28H80Sn12C24+"), -161.676)
        custom_ion_entry = PourbaixEntry(ion, entry_id="my_ion")
        pbx = PourbaixDiagram(
            entries + [custom_ion_entry],
            filter_solids=True,
            comp_dict={"Na": 1, "Sn": 12, "C": 24},
        )
        self.assertAlmostEqual(pbx.get_decomposition_energy(custom_ion_entry, 5, 2), 2.1209002582, 1)

        # Test against ion sets with multiple equivalent ions (Bi-V regression)
        entries = mpr.get_pourbaix_entries(["Bi", "V"])
        pbx = PourbaixDiagram(entries, filter_solids=True, conc_dict={"Bi": 1e-8, "V": 1e-8})
        self.assertTrue(all(["Bi" in entry.composition and "V" in entry.composition for entry in pbx.all_entries]))
Ejemplo n.º 9
0
 def test_from_dict(self):
     sym_dict = {"P": 1, "O": 4, "charge": -2}
     self.assertEqual(
         Ion.from_dict(sym_dict).reduced_formula,
         "PO4[-2]",
         "Creation form sym_amount dictionary failed!",
     )
Ejemplo n.º 10
0
    def test_mpr_pipeline(self):
        from pymatgen import MPRester
        mpr = MPRester()
        data = mpr.get_pourbaix_entries(["Zn"])
        pbx = PourbaixDiagram(data, filter_solids=True, conc_dict={"Zn": 1e-8})
        pbx.find_stable_entry(10, 0)

        data = mpr.get_pourbaix_entries(["Ag", "Te"])
        pbx = PourbaixDiagram(data,
                              filter_solids=True,
                              conc_dict={
                                  "Ag": 1e-8,
                                  "Te": 1e-8
                              })
        self.assertEqual(len(pbx.stable_entries), 30)
        test_entry = pbx.find_stable_entry(8, 2)
        self.assertAlmostEqual(test_entry.energy, 2.3936747835000016, 3)

        # Test custom ions
        entries = mpr.get_pourbaix_entries(["Sn", "C", "Na"])
        ion = IonEntry(Ion.from_formula("NaO28H80Sn12C24+"), -161.676)
        custom_ion_entry = PourbaixEntry(ion, entry_id='my_ion')
        pbx = PourbaixDiagram(entries + [custom_ion_entry],
                              filter_solids=True,
                              comp_dict={
                                  "Na": 1,
                                  "Sn": 12,
                                  "C": 24
                              })
        self.assertAlmostEqual(
            pbx.get_decomposition_energy(custom_ion_entry, 5, 2),
            8.31202738629504, 2)
Ejemplo n.º 11
0
    def test_read_write_csv(self):
        Zn_solids = ["Zn", "ZnO", "ZnO2"]
        sol_g = [0.0, -3.338, -1.315]
        Zn_ions = ["Zn[2+]", "ZnOH[+]", "HZnO2[-]", "ZnO2[2-]", "ZnO"]
        liq_g = [-1.527, -3.415, -4.812, -4.036, -2.921]
        liq_conc = [1e-6, 1e-6, 1e-6, 1e-6, 1e-6]
        solid_entry = list()
        for sol in Zn_solids:
            comp = Composition(sol)
            delg = sol_g[Zn_solids.index(sol)]
            solid_entry.append(PourbaixEntry(PDEntry(comp, delg)))
        ion_entry = list()
        for ion in Zn_ions:
            comp_ion = Ion.from_formula(ion)
            delg = liq_g[Zn_ions.index(ion)]
            conc = liq_conc[Zn_ions.index(ion)]
            PoE = PourbaixEntry(IonEntry(comp_ion, delg))
            PoE.set_conc(conc)
            ion_entry.append(PoE)
        entries = solid_entry + ion_entry
        PourbaixEntryIO.to_csv("pourbaix_test_entries.csv", entries)

        (elements, entries) = PourbaixEntryIO.from_csv(
            "pourbaix_test_entries.csv")
        self.assertEqual(elements,
                         [Element('Zn'), Element('H'), Element('O')],
                         "Wrong elements!")
        self.assertEqual(len(entries), 8, "Wrong number of entries!")
        os.remove("pourbaix_test_entries.csv")
Ejemplo n.º 12
0
    def test_read_write_csv(self):
        Zn_solids = ["Zn", "ZnO", "ZnO2"]
        sol_g = [0.0, -3.338, -1.315]
        Zn_ions = ["Zn[2+]", "ZnOH[+]", "HZnO2[-]", "ZnO2[2-]", "ZnO"]
        liq_g = [-1.527, -3.415, -4.812, -4.036, -2.921]
        liq_conc = [1e-6, 1e-6, 1e-6, 1e-6, 1e-6]
        solid_entry = list()
        for sol in Zn_solids:
            comp = Composition(sol)
            delg = sol_g[Zn_solids.index(sol)]
            solid_entry.append(PourbaixEntry(PDEntry(comp, delg)))
        ion_entry = list()
        for ion in Zn_ions:
            comp_ion = Ion.from_formula(ion)
            delg = liq_g[Zn_ions.index(ion)]
            conc = liq_conc[Zn_ions.index(ion)]
            PoE = PourbaixEntry(IonEntry(comp_ion, delg))
            PoE.conc = conc
            ion_entry.append(PoE)
        entries = solid_entry + ion_entry
        PourbaixEntryIO.to_csv("pourbaix_test_entries.csv", entries)

        (elements,
         entries) = PourbaixEntryIO.from_csv("pourbaix_test_entries.csv")
        self.assertEqual(
            elements, [Element('Zn'),
                       Element('H'), Element('O')], "Wrong elements!")
        self.assertEqual(len(entries), 8, "Wrong number of entries!")
        os.remove("pourbaix_test_entries.csv")
Ejemplo n.º 13
0
 def len_elts(entry):
     if "(s)" in entry:
         comp = Composition(entry[:-3])
     else:
         comp = Ion.from_formula(entry)
     return len([el for el in comp.elements if el not in
                 [Element("H"), Element("O")]])
Ejemplo n.º 14
0
 def setUp(self):
     # comp = Composition("Mn2O3")
     self.solentry = ComputedEntry("Mn2O3", 49)
     ion = Ion.from_formula("MnO4-")
     self.ionentry = IonEntry(ion, 25)
     self.PxIon = PourbaixEntry(self.ionentry)
     self.PxSol = PourbaixEntry(self.solentry)
     self.PxIon.concentration = 1e-4
Ejemplo n.º 15
0
 def setUp(self):
     comp = Composition("Mn2O3")
     self.solentry = PDEntry(comp, 49)
     ion = Ion.from_formula("MnO4-")
     self.ionentry = IonEntry(ion, 25)
     self.PxIon = PourbaixEntry(self.ionentry)
     self.PxSol = PourbaixEntry(self.solentry)
     self.PxIon.conc = 1e-4
Ejemplo n.º 16
0
 def setUp(self):
     # comp = Composition("Mn2O3")
     self.solentry = ComputedEntry("Mn2O3", 49)
     ion = Ion.from_formula("MnO4-")
     self.ionentry = IonEntry(ion, 25)
     self.PxIon = PourbaixEntry(self.ionentry)
     self.PxSol = PourbaixEntry(self.solentry)
     self.PxIon.concentration = 1e-4
Ejemplo n.º 17
0
 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 = Ion(Composition({fixed_el: 1, Element.from_Z(other_z): 0}), 1)
     other_z = random.randint(1, 92)
     while other_z == random_z:
         other_z = random.randint(1, 92)
     comp2 = Ion(Composition({fixed_el: 1, Element.from_Z(other_z): 0}), 1)
     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!")
Ejemplo n.º 18
0
 def setUp(self):
     comp = Composition("Mn2O3")
     self.solentry = PDEntry(comp, 49)
     ion = Ion.from_formula("MnO4-")
     self.ionentry = IonEntry(ion, 25)
     self.PxIon = PourbaixEntry(self.ionentry)
     self.PxSol = PourbaixEntry(self.solentry)
     self.PxIon.set_conc(1e-4)
Ejemplo n.º 19
0
 def test_as_dict(self):
     c = Ion.from_dict({"Mn": 1, "O": 4, "charge": -1})
     d = c.as_dict()
     correct_dict = {"Mn": 1.0, "O": 4.0, "charge": -1.0}
     self.assertEqual(d, correct_dict)
     self.assertEqual(d["charge"], correct_dict["charge"])
     correct_dict = {"Mn": 1.0, "O": 4.0, "charge": -1}
     d = c.to_reduced_dict
     self.assertEqual(d, correct_dict)
     self.assertEqual(d["charge"], correct_dict["charge"])
Ejemplo n.º 20
0
 def test_as_dict(self):
     c = Ion.from_dict({'Mn': 1, 'O': 4, 'charge': -1})
     d = c.as_dict()
     correct_dict = {'Mn': 1.0, 'O': 4.0, 'charge': -1.0}
     self.assertEqual(d, correct_dict)
     self.assertEqual(d['charge'], correct_dict['charge'])
     correct_dict = {'Mn': 1.0, 'O': 4.0, 'charge': -1}
     d = c.to_reduced_dict
     self.assertEqual(d, correct_dict)
     self.assertEqual(d['charge'], correct_dict['charge'])
Ejemplo n.º 21
0
 def test_as_dict(self):
     c = Ion.from_dict({'Mn': 1, 'O': 4, 'charge': -1})
     d = c.as_dict()
     correct_dict = {'Mn': 1.0, 'O': 4.0, 'charge': -1.0}
     self.assertEqual(d, correct_dict)
     self.assertEqual(d['charge'], correct_dict['charge'])
     correct_dict = {'Mn': 1.0, 'O': 4.0, 'charge': -1}
     d = c.to_reduced_dict
     self.assertEqual(d, correct_dict)
     self.assertEqual(d['charge'], correct_dict['charge'])
Ejemplo n.º 22
0
    def get_pourbaix_entries(self, chemsys):
        """
        A helper function to get all entries necessary to generate
        a pourbaix diagram from the rest interface.

        Args:
            chemsys ([str]): A list of elements comprising the chemical
                system, e.g. ['Li', 'Fe']
        """
        from pymatgen.analysis.pourbaix.entry import PourbaixEntry, IonEntry
        from pymatgen.analysis.phase_diagram import PhaseDiagram
        from pymatgen.core.ion import Ion
        from pymatgen.entries.compatibility import\
            MaterialsProjectAqueousCompatibility

        chemsys = list(set(chemsys + ['O', 'H']))
        entries = self.get_entries_in_chemsys(chemsys,
                                              property_data=['e_above_hull'],
                                              compatible_only=False)
        compat = MaterialsProjectAqueousCompatibility("Advanced")
        entries = compat.process_entries(entries)
        solid_pd = PhaseDiagram(
            entries)  # Need this to get ion formation energy
        url = '/pourbaix_diagram/reference_data/' + '-'.join(chemsys)
        ion_data = self._make_request(url)

        pbx_entries = []
        for entry in entries:
            if not set(entry.composition.elements)\
                    <= {Element('H'), Element('O')}:
                pbx_entry = PourbaixEntry(entry)
                pbx_entry.g0_replace(solid_pd.get_form_energy(entry))
                pbx_entry.reduced_entry()
                pbx_entries.append(pbx_entry)

        # position the ion energies relative to most stable reference state
        for n, i_d in enumerate(ion_data):
            ion_entry = IonEntry(Ion.from_formula(i_d['Name']), i_d['Energy'])
            refs = [
                e for e in entries
                if e.composition.reduced_formula == i_d['Reference Solid']
            ]
            if not refs:
                raise ValueError("Reference solid not contained in entry list")
            stable_ref = sorted(refs, key=lambda x: x.data['e_above_hull'])[0]
            rf = stable_ref.composition.get_reduced_composition_and_factor()[1]
            solid_diff = solid_pd.get_form_energy(stable_ref)\
                         - i_d['Reference solid energy'] * rf
            elt = i_d['Major_Elements'][0]
            correction_factor = ion_entry.ion.composition[elt]\
                                / stable_ref.composition[elt]
            correction = solid_diff * correction_factor
            pbx_entries.append(
                PourbaixEntry(ion_entry, correction, 'ion-{}'.format(n)))
        return pbx_entries
Ejemplo n.º 23
0
    def get_pourbaix_entries(self, chemsys):
        """
        A helper function to get all entries necessary to generate
        a pourbaix diagram from the rest interface.

        Args:
            chemsys ([str]): A list of elements comprising the chemical
                system, e.g. ['Li', 'Fe']
        """
        from pymatgen.analysis.pourbaix.entry import PourbaixEntry, IonEntry
        from pymatgen.analysis.phase_diagram import PhaseDiagram
        from pymatgen.core.ion import Ion
        from pymatgen.entries.compatibility import\
            MaterialsProjectAqueousCompatibility

        chemsys = list(set(chemsys + ['O', 'H']))
        entries = self.get_entries_in_chemsys(
            chemsys, property_data=['e_above_hull'], compatible_only=False)
        compat = MaterialsProjectAqueousCompatibility("Advanced")
        entries = compat.process_entries(entries)
        solid_pd = PhaseDiagram(entries) # Need this to get ion formation energy
        url = '/pourbaix_diagram/reference_data/' + '-'.join(chemsys)
        ion_data = self._make_request(url)

        pbx_entries = []
        for entry in entries:
            if not set(entry.composition.elements)\
                    <= {Element('H'), Element('O')}:
                pbx_entry = PourbaixEntry(entry)
                pbx_entry.g0_replace(solid_pd.get_form_energy(entry))
                pbx_entry.reduced_entry()
                pbx_entries.append(pbx_entry)

        # position the ion energies relative to most stable reference state
        for n, i_d in enumerate(ion_data):
            ion_entry = IonEntry(Ion.from_formula(i_d['Name']), i_d['Energy'])
            refs = [e for e in entries
                    if e.composition.reduced_formula == i_d['Reference Solid']]
            if not refs:
                raise ValueError("Reference solid not contained in entry list")
            stable_ref = sorted(refs, key=lambda x: x.data['e_above_hull'])[0]
            rf = stable_ref.composition.get_reduced_composition_and_factor()[1]
            solid_diff = solid_pd.get_form_energy(stable_ref)\
                         - i_d['Reference solid energy'] * rf
            elt = i_d['Major_Elements'][0]
            correction_factor = ion_entry.ion.composition[elt]\
                                / stable_ref.composition[elt]
            correction = solid_diff * correction_factor
            pbx_entries.append(PourbaixEntry(ion_entry, correction,
                                             'ion-{}'.format(n)))
        return pbx_entries
Ejemplo n.º 24
0
    def test_special_formulas(self):
        special_formulas = [
            ("Cl-", "Cl[-1]"),
            ("H+", "H[+1]"),
            ("F-", "F[-1]"),
            ("H4O4", "H2O2(aq)"),
            ("OH-", "OH[-1]"),
            ("CH3COO-", "CH3COO[-1]"),
            ("CH3COOH", "CH3COOH(aq)"),
            ("CH3OH", "CH3OH(aq)"),
            ("H4CO", "CH3OH(aq)"),
            ("C2H6O", "C2H5OH(aq)"),
            ("C3H8O", "C3H7OH(aq)"),
            ("C4H10O", "C4H9OH(aq)"),
            ("Fe(OH)4+", "FeO2.2H2O[+1]"),
            ("Zr(OH)4", "ZrO2.2H2O(aq)"),
        ]

        for tup in special_formulas:
            self.assertEqual(Ion.from_formula(tup[0]).reduced_formula, tup[1])

        self.assertEqual(Ion.from_formula("Fe(OH)4+").get_reduced_formula_and_factor(hydrates=False), ("Fe(OH)4", 1))
        self.assertEqual(Ion.from_formula("Zr(OH)4").get_reduced_formula_and_factor(hydrates=False), ("Zr(OH)4", 1))
Ejemplo n.º 25
0
    def test_get_ion_entries(self, mpr):
        entries = mpr.get_entries_in_chemsys("Ti-O-H")
        pd = PhaseDiagram(entries)
        ion_entry_data = mpr.get_ion_reference_data_for_chemsys("Ti-O-H")
        ion_entries = mpr.get_ion_entries(pd, ion_entry_data)
        assert len(ion_entries) == 5
        assert all([isinstance(i, IonEntry) for i in ion_entries])

        # test an incomplete phase diagram
        entries = mpr.get_entries_in_chemsys("Ti-O")
        pd = PhaseDiagram(entries)
        with pytest.raises(ValueError,
                           match="The phase diagram chemical system"):
            mpr.get_ion_entries(pd)

        # test ion energy calculation
        ion_data = mpr.get_ion_reference_data_for_chemsys('S')
        ion_ref_comps = [
            Ion.from_formula(d["data"]["RefSolid"]).composition
            for d in ion_data
        ]
        ion_ref_elts = set(
            itertools.chain.from_iterable(i.elements for i in ion_ref_comps))
        ion_ref_entries = mpr.get_entries_in_chemsys(
            list([str(e) for e in ion_ref_elts] + ["O", "H"]))
        mpc = MaterialsProjectAqueousCompatibility()
        ion_ref_entries = mpc.process_entries(ion_ref_entries)
        ion_ref_pd = PhaseDiagram(ion_ref_entries)
        ion_entries = mpr.get_ion_entries(ion_ref_pd, ion_ref_data=ion_data)

        # In ion ref data, SO4-2 is -744.27 kJ/mol; ref solid is -1,279.0 kJ/mol
        # so the ion entry should have an energy (-744.27 +1279) = 534.73 kJ/mol
        # or 5.542 eV/f.u. above the energy of Na2SO4
        so4_two_minus = [
            e for e in ion_entries if e.ion.reduced_formula == "SO4[-2]"
        ][0]

        # the ref solid is Na2SO4, ground state mp-4770
        # the rf factor correction is necessary to make sure the composition
        # of the reference solid is normalized to a single formula unit
        ref_solid_entry = [
            e for e in ion_ref_entries if e.entry_id == 'mp-4770'
        ][0]
        rf = ref_solid_entry.composition.get_reduced_composition_and_factor(
        )[1]
        solid_energy = ion_ref_pd.get_form_energy(ref_solid_entry) / rf

        assert np.allclose(so4_two_minus.energy,
                           solid_energy + 5.542,
                           atol=1e-3)
Ejemplo n.º 26
0
    def __init__(self, entries, comp_dict=None):
        """
        Args:
            entries:
                Entries list containing both Solids and Ions
            comp_dict:
                Dictionary of compositions
        """
        self._solid_entries = list()
        self._ion_entries = list()
        for entry in entries:
            if entry.phase_type == "Solid":
                self._solid_entries.append(entry)
            elif entry.phase_type == "Ion":
                self._ion_entries.append(entry)
            else:
                raise StandardError("Incorrect Phase type - needs to be \
                Pourbaix entry of phase type Ion/Solid")
        self._unprocessed_entries = self._solid_entries + self._ion_entries
        self._elt_comp = comp_dict
        if comp_dict:
            self._multielement = True
            self.pourbaix_elements = [key for key in comp_dict]
            w = [comp_dict[key] for key in comp_dict]
            A = []
            for comp in comp_dict:
                m = re.search(r"\[([^\[\]]+)\]|\(aq\)", comp)
                if m:
                    comp_obj = Ion.from_formula(comp)
                else:
                    comp_obj = Composition.from_formula(comp)
                Ai = []
                for elt in self.pourbaix_elements:
                    Ai.append(comp_obj[Element(elt)])
                A.append(Ai)
            A = np.array(A).T.astype(float)
            w = np.array(w)
            A /= np.dot([A[i].sum() for i in xrange(len(A))], w)
            x = np.linalg.solve(A, w)
            self._elt_comp = dict(zip(self.pourbaix_elements, x))

        else:
            self._multielement = False
            self.pourbaix_elements = [
                el.symbol for el in entries[0].composition.elements
                if el.symbol not in ["H", "O"]
            ]
        self._make_pourbaixdiagram()
Ejemplo n.º 27
0
    def __init__(self, entries, comp_dict=None):
        """
        Args:
            entries:
                Entries list containing both Solids and Ions
            comp_dict:
                Dictionary of compositions
        """
        self._solid_entries = list()
        self._ion_entries = list()
        for entry in entries:
            if entry.phase_type == "Solid":
                self._solid_entries.append(entry)
            elif entry.phase_type == "Ion":
                self._ion_entries.append(entry)
            else:
                raise StandardError("Incorrect Phase type - needs to be \
                Pourbaix entry of phase type Ion/Solid")
        self._unprocessed_entries = self._solid_entries + self._ion_entries
        self._elt_comp = comp_dict
        if comp_dict:
            self._multielement = True
            self.pourbaix_elements = [key for key in comp_dict]
            w = [comp_dict[key] for key in comp_dict]
            A = []
            for comp in comp_dict:
                m = re.search(r"\[([^\[\]]+)\]|\(aq\)", comp)
                if m:
                    comp_obj = Ion.from_formula(comp)
                else:
                    comp_obj = Composition.from_formula(comp)
                Ai = []
                for elt in self.pourbaix_elements:
                    Ai.append(comp_obj[Element(elt)])
                A.append(Ai)
            A = np.array(A).T.astype(float)
            w = np.array(w)
            A /= np.dot([A[i].sum() for i in xrange(len(A))], w)
            x = np.linalg.solve(A, w)
            self._elt_comp = dict(zip(self.pourbaix_elements, x))

        else:
            self._multielement = False
            self.pourbaix_elements = [el.symbol
                                      for el in entries[0].composition.elements
                                      if el.symbol not in ["H", "O"]]
        self._make_pourbaixdiagram()
Ejemplo n.º 28
0
    def test_get_decomposition(self):
        # Test a stable entry to ensure that it's zero in the stable region
        entry = self.test_data["Zn"][12]  # Should correspond to mp-2133
        self.assertAlmostEqual(
            self.pbx.get_decomposition_energy(entry, 10, 1),
            0.0,
            5,
            "Decomposition energy of ZnO is not 0.",
        )

        # Test an unstable entry to ensure that it's never zero
        entry = self.test_data["Zn"][11]
        ph, v = np.meshgrid(np.linspace(0, 14), np.linspace(-2, 4))
        result = self.pbx_nofilter.get_decomposition_energy(entry, ph, v)
        self.assertTrue((result >= 0).all(),
                        "Unstable energy has hull energy of 0 or less")

        # Test an unstable hydride to ensure HER correction works
        self.assertAlmostEqual(
            self.pbx.get_decomposition_energy(entry, -3, -2), 3.6979147983333)
        # Test a list of pHs
        self.pbx.get_decomposition_energy(entry, np.linspace(0, 2, 5), 2)

        # Test a list of Vs
        self.pbx.get_decomposition_energy(entry, 4, np.linspace(-3, 3, 10))

        # Test a set of matching arrays
        ph, v = np.meshgrid(np.linspace(0, 14), np.linspace(-3, 3))
        self.pbx.get_decomposition_energy(entry, ph, v)

        # Test custom ions
        entries = self.test_data["C-Na-Sn"]
        ion = IonEntry(Ion.from_formula("NaO28H80Sn12C24+"), -161.676)
        custom_ion_entry = PourbaixEntry(ion, entry_id="my_ion")
        pbx = PourbaixDiagram(
            entries + [custom_ion_entry],
            filter_solids=True,
            comp_dict={
                "Na": 1,
                "Sn": 12,
                "C": 24
            },
        )
        self.assertAlmostEqual(
            pbx.get_decomposition_energy(custom_ion_entry, 5, 2), 2.1209002582,
            1)
Ejemplo n.º 29
0
def ion_or_solid_comp_object(formula):
    """
    Returns either an ion object or composition object given
    a formula.

    Args:
        formula: String formula. Eg. of ion: NaOH(aq), Na[+];
            Eg. of solid: Fe2O3(s), Fe(s), Na2O

    Returns:
        Composition/Ion object
    """
    m = re.search(r"\[([^\[\]]+)\]|\(aq\)", formula)
    if m:
        comp_obj = Ion.from_formula(formula)
    elif re.search(r"\(s\)", formula):
        comp_obj = Composition(formula[:-3])
    else:
        comp_obj = Composition(formula)
    return comp_obj
Ejemplo n.º 30
0
def ion_or_solid_comp_object(formula):
    """
    Returns either an ion object or composition object given
    a formula.

    Args:
        formula: String formula. Eg. of ion: NaOH(aq), Na[+];
            Eg. of solid: Fe2O3(s), Fe(s), Na2O

    Returns:
        Composition/Ion object
    """
    m = re.search(r"\[([^\[\]]+)\]|\(aq\)", formula)
    if m:
        comp_obj = Ion.from_formula(formula)
    elif re.search(r"\(s\)", formula):
        comp_obj = Composition(formula[:-3])
    else:
        comp_obj = Composition(formula)
    return comp_obj
Ejemplo n.º 31
0
    def from_csv(filename):
        """
        Imports PourbaixEntries from a csv.

        Args:
            filename - Filename to import from.

        Returns:
            List of Entries
        """
        import csv
        reader = csv.reader(open(filename, "rb"),
                            delimiter=",",
                            quotechar="\"",
                            quoting=csv.QUOTE_MINIMAL)
        entries = list()
        header_read = False
        for row in reader:
            if not header_read:
                elements = row[1:(len(row) - 4)]
                header_read = True
            else:
                name = row[0]
                energy = float(row[-4])
                conc = float(row[-1])
                comp = dict()
                for ind in range(1, len(row) - 4):
                    if float(row[ind]) > 0:
                        comp[Element(elements[ind - 1])] = float(row[ind])
                phase_type = row[-3]
                if phase_type == "Ion":
                    PoE = PourbaixEntry(
                        IonEntry(Ion.from_formula(name), energy))
                    PoE.set_conc(conc)
                    PoE.set_name(name)
                    entries.append(PoE)
                else:
                    entries.append(
                        PourbaixEntry(PDEntry(Composition(comp), energy)))
        elements = [Element(el) for el in elements]
        return elements, entries
Ejemplo n.º 32
0
 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 = Ion(Composition({fixed_el: 1, Element.from_Z(other_z): 0}), 1)
     other_z = random.randint(1, 92)
     while other_z == random_z:
         other_z = random.randint(1, 92)
     comp2 = Ion(Composition({fixed_el: 1, Element.from_Z(other_z): 0}), 1)
     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!")
Ejemplo n.º 33
0
    def from_csv(filename):
        """
        Imports PourbaixEntries from a csv.

        Args:
            filename: Filename to import from.

        Returns:
            List of Entries
        """
        with open(filename, "r") as f:
            reader = csv.reader(f, delimiter=unicode2str(","),
                                quotechar=unicode2str("\""),
                                quoting=csv.QUOTE_MINIMAL)
            entries = list()
            header_read = False
            for row in reader:
                if not header_read:
                    elements = row[1:(len(row) - 4)]
                    header_read = True
                else:
                    name = row[0]
                    energy = float(row[-4])
                    conc = float(row[-1])
                    comp = dict()
                    for ind in range(1, len(row) - 4):
                        if float(row[ind]) > 0:
                            comp[Element(elements[ind - 1])] = float(row[ind])
                    phase_type = row[-3]
                    if phase_type == "Ion":
                        PoE = PourbaixEntry(IonEntry(Ion.from_formula(name),
                                                     energy))
                        PoE.conc = conc
                        PoE.name = name
                        entries.append(PoE)
                    else:
                        entries.append(PourbaixEntry(PDEntry(Composition(comp),
                                                             energy)))
        elements = [Element(el) for el in elements]
        return elements, entries
Ejemplo n.º 34
0
def mke_pour_ion_entr(mtnme, ion_dict, stable_solids_minus_h2o, ref_state, entries_aqcorr, ref_dict):
    """
    """
    #| -  mke_pour_ion_entr
    from pymatgen import Element # Accesses properties of element
    from pymatgen.core.ion import Ion
    from pymatgen.phasediagram.maker import PhaseDiagram
    from pymatgen.analysis.pourbaix.entry import PourbaixEntry, IonEntry

    from pd_make import ref_entry_find, ref_entry_stoich
    pd = PhaseDiagram(entries_aqcorr)

    ref_entry = ref_entry_find(stable_solids_minus_h2o, ref_state)
    ref_stoich_fact = ref_entry_stoich(ref_entry)

    ## Calculate DFT reference E for ions (Persson et al, PRB (2012))
    dft_for_e=pd.get_form_energy(ref_entry)/ref_stoich_fact # DFT formation E, normalized by composition "factor"
    ion_correction_1 = dft_for_e-ref_dict[ref_state] # Difference of DFT form E and exp for E of reference

    el = Element(mtnme)
    pbx_ion_entries_1 = []
    for id in ion_dict:
        comp = Ion.from_formula(id['Name'])     # Ion name-> Ion comp name (ex. Fe[3+] -> Ion: Fe1 +3)
                               # comp.composition[el] : number of Fe atoms in ion
        num_el_ref = (ref_entry.composition[el]) / ref_stoich_fact # number of element atoms in reference
        factor = comp.composition[el] / num_el_ref    # Stoicheometric factor for ionic correction
                                   # (i.e. Fe2O3 ref but calc E for Fe[2+] ion)
        energy = id['Energy'] + ion_correction_1 * factor

        #TEMP_PRINT
        if id['Name']=='Pd[2+]':
            energy = 123
        # print id['Name']

        pbx_entry_ion = PourbaixEntry(IonEntry(comp, energy))
        pbx_entry_ion.name = id['Name']
        pbx_ion_entries_1.append(pbx_entry_ion)

    return pbx_ion_entries_1
Ejemplo n.º 35
0
    def test_mpr_pipeline(self):
        from pymatgen import MPRester
        mpr = MPRester()
        data = mpr.get_pourbaix_entries(["Zn"])
        pbx = PourbaixDiagram(data, filter_solids=True, conc_dict={"Zn": 1e-8})
        pbx.find_stable_entry(10, 0)

        data = mpr.get_pourbaix_entries(["Ag", "Te"])
        pbx = PourbaixDiagram(data, filter_solids=True,
                              conc_dict={"Ag": 1e-8, "Te": 1e-8})
        self.assertEqual(len(pbx.stable_entries), 30)
        test_entry = pbx.find_stable_entry(8, 2)
        self.assertAlmostEqual(test_entry.energy, 2.393900378500001)

        # Test custom ions
        entries = mpr.get_pourbaix_entries(["Sn", "C", "Na"])
        ion = IonEntry(Ion.from_formula("NaO28H80Sn12C24+"), -161.676)
        custom_ion_entry = PourbaixEntry(ion, entry_id='my_ion')
        pbx = PourbaixDiagram(entries + [custom_ion_entry], filter_solids=True,
                              comp_dict={"Na": 1, "Sn": 12, "C": 24})
        self.assertAlmostEqual(pbx.get_decomposition_energy(custom_ion_entry, 5, 2),
                               8.31082110278154)
Ejemplo n.º 36
0
    def get_pourbaix_plot_colorfill_by_element(self, limits=None, title="",
                                                label_domains=True, element=None):
        """
        Color domains by element
        """
        from matplotlib.patches import Polygon

        entry_dict_of_multientries = collections.defaultdict(list)
        plt = pretty_plot(16)
        optim_colors = ['#0000FF', '#FF0000', '#00FF00', '#FFFF00', '#FF00FF',
                         '#FF8080', '#DCDCDC', '#800000', '#FF8000']
        optim_font_color = ['#FFFFA0', '#00FFFF', '#FF00FF', '#0000FF', '#00FF00',
                            '#007F7F', '#232323', '#7FFFFF', '#007FFF']
        hatch = ['/', '\\', '|', '-', '+', 'o', '*']
        (stable, unstable) = self.pourbaix_plot_data(limits)
        num_of_overlaps = {key: 0 for key in stable.keys()}
        for entry in stable:
            if isinstance(entry, MultiEntry):
                for e in entry.entrylist:
                    if element in e.composition.elements:
                        entry_dict_of_multientries[e.name].append(entry)
                        num_of_overlaps[entry] += 1
            else:
                entry_dict_of_multientries[entry.name].append(entry)
        if limits:
            xlim = limits[0]
            ylim = limits[1]
        else:
            xlim = self._analyzer.chempot_limits[0]
            ylim = self._analyzer.chempot_limits[1]

        h_line = np.transpose([[xlim[0], -xlim[0] * PREFAC],
                               [xlim[1], -xlim[1] * PREFAC]])
        o_line = np.transpose([[xlim[0], -xlim[0] * PREFAC + 1.23],
                               [xlim[1], -xlim[1] * PREFAC + 1.23]])
        neutral_line = np.transpose([[7, ylim[0]], [7, ylim[1]]])
        V0_line = np.transpose([[xlim[0], 0], [xlim[1], 0]])

        ax = plt.gca()
        ax.set_xlim(xlim)
        ax.set_ylim(ylim)
        from pymatgen import Composition, Element
        from pymatgen.core.ion import Ion

        def len_elts(entry):
            if "(s)" in entry:
                comp = Composition(entry[:-3])
            else:
                comp = Ion.from_formula(entry)
            return len([el for el in comp.elements if el not in
                        [Element("H"), Element("O")]])

        sorted_entry = entry_dict_of_multientries.keys()
        sorted_entry.sort(key=len_elts)
        i = -1
        label_chr = map(chr, list(range(65, 91)))
        for entry in sorted_entry:
            color_indx = 0
            x_coord = 0.0
            y_coord = 0.0
            npts = 0
            i += 1
            for e in entry_dict_of_multientries[entry]:
                hc = 0
                fc = 0
                bc = 0
                xy = self.domain_vertices(e)
                c = self.get_center(stable[e])
                x_coord += c[0]
                y_coord += c[1]
                npts += 1
                color_indx = i
                if "(s)" in entry:
                    comp = Composition(entry[:-3])
                else:
                    comp = Ion.from_formula(entry)
                if len([el for el in comp.elements if el not in
                         [Element("H"), Element("O")]]) == 1:
                    if color_indx >= len(optim_colors):
                        color_indx = color_indx -\
                         int(color_indx / len(optim_colors)) * len(optim_colors)
                    patch = Polygon(xy, facecolor=optim_colors[color_indx],
                                     closed=True, lw=3.0, fill=True)
                    bc = optim_colors[color_indx]
                else:
                    if color_indx >= len(hatch):
                        color_indx = color_indx - int(color_indx / len(hatch)) * len(hatch)
                    patch = Polygon(xy, hatch=hatch[color_indx], closed=True, lw=3.0, fill=False)
                    hc = hatch[color_indx]
                ax.add_patch(patch)

            xy_center = (x_coord / npts, y_coord / npts)
            if label_domains:
                if color_indx >= len(optim_colors):
                    color_indx = color_indx -\
                        int(color_indx / len(optim_colors)) * len(optim_colors)
                fc = optim_font_color[color_indx]
                if bc and not hc:
                    bbox = dict(boxstyle="round", fc=fc)
                if hc and not bc:
                    bc = 'k'
                    fc = 'w'
                    bbox = dict(boxstyle="round", hatch=hc, fill=False)
                if bc and hc:
                    bbox = dict(boxstyle="round", hatch=hc, fc=fc)
#                 bbox.set_path_effects([PathEffects.withSimplePatchShadow()])
                plt.annotate(latexify_ion(latexify(entry)), xy_center,
                            color=bc, fontsize=30, bbox=bbox)
#                 plt.annotate(label_chr[i], xy_center,
#                               color=bc, fontsize=30, bbox=bbox)

        lw = 3
        plt.plot(h_line[0], h_line[1], "r--", linewidth=lw)
        plt.plot(o_line[0], o_line[1], "r--", linewidth=lw)
        plt.plot(neutral_line[0], neutral_line[1], "k-.", linewidth=lw)
        plt.plot(V0_line[0], V0_line[1], "k-.", linewidth=lw)

        plt.xlabel("pH")
        plt.ylabel("E (V)")
        plt.title(title, fontsize=20, fontweight='bold')
        return plt
Ejemplo n.º 37
0
 def from_dict(cls, d):
     """
     Returns an IonEntry object from a dict.
     """
     return IonEntry(Ion.from_dict(d["composition"]), d["energy"])
Ejemplo n.º 38
0
pbx_solid_entries = []
for entry in stable_solids_minus_h2o:
    pbx_entry = PourbaixEntry(entry)

    # Replace E with newly corrected E
    pbx_entry.g0_replace(pd.get_form_energy(entry))
    pbx_entry.reduced_entry()  # Applies reduction factor?????
    pbx_solid_entries.append(pbx_entry)

# | - Processing My Solid Entries
for key, value in ion_dict_solids_expt.items():
    print("IDJFIJDS")
    split_key = key.split("_")
    formula_i = split_key[0]

    comp = Ion.from_formula(formula_i)
    energy = value
    pbx_entry_ion = PourbaixEntry(
        ComputedEntry(comp, energy, attribute={"full_name": key}))

    # AP pbx_entry_ion.name = key
    pbx_entry_ion.conc = 1
    pbx_solid_entries.append(pbx_entry_ion)

for a in pbx_solid_entries:
    print(a, a.conc)
    print('hkjo')

#__|

# +
Ejemplo n.º 39
0
 def test_get_composition(self):
     comp = self.entry.composition
     expected_comp = Ion.from_formula('MnO4[-]')
     self.assertEqual(comp, expected_comp, "Wrong composition!")
Ejemplo n.º 40
0
 def from_dict(cls, d):
     """
     Returns an IonEntry object from a dict.
     """
     return IonEntry(Ion.from_dict(d["ion"]), d["energy"], d.get("name", None))
Ejemplo n.º 41
0
 def test_mixed_valence(self):
     comp = Ion(Composition({"Fe2+": 2, "Fe3+": 4, "Li+": 8}))
     self.assertEqual(comp.reduced_formula, "Li4Fe3(aq)")
     self.assertEqual(comp.alphabetical_formula, "Fe6 Li8")
     self.assertEqual(comp.formula, "Li8 Fe6")
Ejemplo n.º 42
0
def plot_pourbaix_diagram(metastability=0.0,
                          ion_concentration=1e-6,
                          fmt='pdf'):
    """
    Creates a Pourbaix diagram for the material in the cwd.

    Args:
        metastability (float): desired metastable tolerance energy
            (meV/atom). <~50 is generally a sensible range to use.
        ion_concentration (float): in mol/kg. Sensible values are
            generally between 1e-8 and 1.
        fmt (str): matplotlib format style. Check the matplotlib
            docs for options.
    """

    # Create a ComputedEntry object for the 2D material.
    composition = Structure.from_file('POSCAR').composition
    energy = Vasprun('vasprun.xml').final_energy

    cmpd = ComputedEntry(composition, energy)

    # Define the chemsys that describes the 2D compound.
    chemsys = ['O', 'H'] + [
        elt.symbol
        for elt in composition.elements if elt.symbol not in ['O', 'H']
    ]

    # Pick out the ions pertaining to the 2D compound.
    ion_dict = dict()
    for elt in chemsys:
        if elt not in ['O', 'H'] and ION_FORMATION_ENERGIES[elt]:
            ion_dict.update(ION_FORMATION_ENERGIES[elt])

    elements = [Element(elt) for elt in chemsys if elt not in ['O', 'H']]

    # Add "correction" for metastability
    cmpd.correction -= float(cmpd.composition.num_atoms)\
        * float(metastability) / 1000.0

    # Calculate formation energy of the compound from its end
    # members
    form_energy = cmpd.energy
    for elt in composition.as_dict():
        form_energy -= CHEMICAL_POTENTIALS[elt] * cmpd.composition[elt]

    # Convert the compound entry to a pourbaix entry.
    # Default concentration for solid entries = 1
    pbx_cmpd = PourbaixEntry(cmpd)
    pbx_cmpd.g0_replace(form_energy)
    pbx_cmpd.reduced_entry()

    # Add corrected ionic entries to the pourbaix diagram
    # dft corrections for experimental ionic energies:
    # Persson et.al PHYSICAL REVIEW B 85, 235438 (2012)
    pbx_ion_entries = list()

    # Get PourbaixEntry corresponding to each ion.
    # Default concentration for ionic entries = 1e-6
    # ion_energy = ion_exp_energy + ion_correction * factor
    # where factor = fraction of element el in the ionic entry
    # compared to the reference entry
    for elt in elements:
        for key in ion_dict:
            comp = Ion.from_formula(key)
            if comp.composition[elt] != 0:
                factor = comp.composition[elt]
                energy = ion_dict[key]
                pbx_entry_ion = PourbaixEntry(IonEntry(comp, energy))
                pbx_entry_ion.correction = (ION_CORRECTIONS[elt.symbol] *
                                            factor)
                pbx_entry_ion.conc = ion_concentration
                pbx_entry_ion.name = key
                pbx_ion_entries.append(pbx_entry_ion)

    # Generate and plot Pourbaix diagram
    # Each bulk solid/ion has a free energy g of the form:
    # g = g0_ref + 0.0591 * log10(conc) - nO * mu_H2O +
    # (nH - 2nO) * pH + phi * (-nH + 2nO + q)

    all_entries = [pbx_cmpd] + pbx_ion_entries

    pourbaix = PourbaixDiagram(all_entries)

    # Analysis features
    # panalyzer = PourbaixAnalyzer(pourbaix)
    # instability = panalyzer.get_e_above_hull(pbx_cmpd)

    plotter = PourbaixPlotter(pourbaix)
    plot = plotter.get_pourbaix_plot(limits=[[0, 14], [-2, 2]],
                                     label_domains=True)
    fig = plot.gcf()
    ax1 = fig.gca()

    # Add coloring to highlight the stability region for the 2D
    # material, if one exists.
    stable_entries = plotter.pourbaix_plot_data(limits=[[0, 14], [-2, 2]])[0]

    for entry in stable_entries:
        if entry == pbx_cmpd:
            col = plt.cm.Blues(0)
        else:
            col = plt.cm.rainbow(
                float(ION_COLORS[entry.composition.reduced_formula]))

        vertices = plotter.domain_vertices(entry)
        patch = Polygon(vertices, closed=True, fill=True, color=col)
        ax1.add_patch(patch)

    fig.set_size_inches((11.5, 9))
    plot.tight_layout(pad=1.09)

    # Save plot
    if metastability:
        plot.suptitle('Metastable Tolerance ='
                      ' {} meV/atom'.format(metastability),
                      fontsize=20)
        plot.savefig('{}_{}.{}'.format(composition.reduced_formula,
                                       ion_concentration, fmt),
                     transparent=True)
    else:
        plot.savefig('{}_{}.{}'.format(composition.reduced_formula,
                                       ion_concentration, fmt),
                     transparent=True)

    plot.close()
Ejemplo n.º 43
0
 def len_elts(entry):
     comp = Composition(entry[:-3]) if "(s)" in entry else Ion.from_formula(entry)
     return len(set(comp.elements) - {Element("H"), Element("O")})
Ejemplo n.º 44
0
    def get_pourbaix_entries(self, chemsys):
        """
        A helper function to get all entries necessary to generate
        a pourbaix diagram from the rest interface.

        Args:
            chemsys ([str]): A list of elements comprising the chemical
                system, e.g. ['Li', 'Fe']
        """
        from pymatgen.analysis.pourbaix_diagram import PourbaixEntry, IonEntry
        from pymatgen.analysis.phase_diagram import PhaseDiagram
        from pymatgen.core.ion import Ion
        from pymatgen.entries.compatibility import\
            MaterialsProjectAqueousCompatibility

        pbx_entries = []

        # Get ion entries first, because certain ions have reference
        # solids that aren't necessarily in the chemsys (Na2SO4)
        url = '/pourbaix_diagram/reference_data/' + '-'.join(chemsys)
        ion_data = self._make_request(url)
        ion_ref_comps = [Composition(d['Reference Solid']) for d in ion_data]
        ion_ref_elts = list(itertools.chain.from_iterable(
            i.elements for i in ion_ref_comps))
        ion_ref_entries = self.get_entries_in_chemsys(
            list(set([str(e) for e in ion_ref_elts] + ['O', 'H'])),
            property_data=['e_above_hull'], compatible_only=False)
        compat = MaterialsProjectAqueousCompatibility("Advanced")
        ion_ref_entries = compat.process_entries(ion_ref_entries)
        ion_ref_pd = PhaseDiagram(ion_ref_entries)

        # position the ion energies relative to most stable reference state
        for n, i_d in enumerate(ion_data):
            ion_entry = IonEntry(Ion.from_formula(i_d['Name']), i_d['Energy'])
            refs = [e for e in ion_ref_entries
                    if e.composition.reduced_formula == i_d['Reference Solid']]
            if not refs:
                raise ValueError("Reference solid not contained in entry list")
            stable_ref = sorted(refs, key=lambda x: x.data['e_above_hull'])[0]
            rf = stable_ref.composition.get_reduced_composition_and_factor()[1]
            solid_diff = ion_ref_pd.get_form_energy(stable_ref)\
                         - i_d['Reference solid energy'] * rf
            elt = i_d['Major_Elements'][0]
            correction_factor = ion_entry.ion.composition[elt]\
                                / stable_ref.composition[elt]
            ion_entry.energy += solid_diff * correction_factor
            pbx_entries.append(PourbaixEntry(ion_entry, 'ion-{}'.format(n)))
            # import nose; nose.tools.set_trace()

        # Construct the solid pourbaix entries from filtered ion_ref entries
        extra_elts = set(ion_ref_elts) - {Element(s) for s in chemsys}\
            - {Element('H'), Element('O')}
        for entry in ion_ref_entries:
            entry_elts = set(entry.composition.elements)
            # Ensure no OH chemsys or extraneous elements from ion references
            if not (entry_elts <= {Element('H'), Element('O')} or \
                    extra_elts.intersection(entry_elts)):
                # replace energy with formation energy, use dict to
                # avoid messing with the ion_ref_pd and to keep all old params
                form_e = ion_ref_pd.get_form_energy(entry)
                new_entry = deepcopy(entry)
                new_entry.uncorrected_energy = form_e
                new_entry.correction = 0.0
                pbx_entry = PourbaixEntry(new_entry)
                if entry.entry_id == "mp-697146":
                    pass
                    # import nose; nose.tools.set_trace()
                # pbx_entry.reduced_entry()
                pbx_entries.append(pbx_entry)

        return pbx_entries
Ejemplo n.º 45
0
 def test_from_dict(self):
     sym_dict = {"P": 1, "O": 4, 'charge': -2}
     self.assertEqual(Ion.from_dict(sym_dict).reduced_formula,
                      "PO4[2-]",
                      "Creation form sym_amount dictionary failed!")
Ejemplo n.º 46
0
 def setUp(self):
     ion = Ion.from_formula("MnO4[-]")
     self.entry = IonEntry(ion, 49)
Ejemplo n.º 47
0
    def get_pourbaix_plot_colorfill_by_element(self, limits=None, title="",
                                                label_domains=True, element=None):
        """
        Color domains by element
        """
        from matplotlib.patches import Polygon
        import matplotlib.patheffects as PathEffects

        entry_dict_of_multientries = collections.defaultdict(list)
        plt = get_publication_quality_plot(16)
        optim_colors = ['#0000FF', '#FF0000', '#00FF00', '#FFFF00', '#FF00FF',
                         '#FF8080', '#DCDCDC', '#800000', '#FF8000']
        optim_font_color = ['#FFFFA0', '#00FFFF', '#FF00FF', '#0000FF', '#00FF00',
                            '#007F7F', '#232323', '#7FFFFF', '#007FFF']
        hatch = ['/', '\\', '|', '-', '+', 'o', '*']
        (stable, unstable) = self.pourbaix_plot_data(limits)
        num_of_overlaps = {key: 0 for key in stable.keys()}
        for entry in stable:
            if isinstance(entry, MultiEntry):
                for e in entry.entrylist:
                    if element in e.composition.elements:
                        entry_dict_of_multientries[e.name].append(entry)
                        num_of_overlaps[entry] += 1
            else:
                entry_dict_of_multientries[entry.name].append(entry)
        if limits:
            xlim = limits[0]
            ylim = limits[1]
        else:
            xlim = self._analyzer.chempot_limits[0]
            ylim = self._analyzer.chempot_limits[1]

        h_line = np.transpose([[xlim[0], -xlim[0] * PREFAC],
                               [xlim[1], -xlim[1] * PREFAC]])
        o_line = np.transpose([[xlim[0], -xlim[0] * PREFAC + 1.23],
                               [xlim[1], -xlim[1] * PREFAC + 1.23]])
        neutral_line = np.transpose([[7, ylim[0]], [7, ylim[1]]])
        V0_line = np.transpose([[xlim[0], 0], [xlim[1], 0]])

        ax = plt.gca()
        ax.set_xlim(xlim)
        ax.set_ylim(ylim)
        from pymatgen import Composition, Element
        from pymatgen.core.ion import Ion

        def len_elts(entry):
            if "(s)" in entry:
                comp = Composition(entry[:-3])
            else:
                comp = Ion.from_formula(entry)
            return len([el for el in comp.elements if el not in
                        [Element("H"), Element("O")]])

        sorted_entry = entry_dict_of_multientries.keys()
        sorted_entry.sort(key=len_elts)
        i = -1
        label_chr = map(chr, range(65, 91))
        for entry in sorted_entry:
            color_indx = 0
            x_coord = 0.0
            y_coord = 0.0
            npts = 0
            i += 1
            for e in entry_dict_of_multientries[entry]:
                hc = 0
                fc = 0
                bc = 0
                xy = self.domain_vertices(e)
                c = self.get_center(stable[e])
                x_coord += c[0]
                y_coord += c[1]
                npts += 1
                color_indx = i
                if "(s)" in entry:
                    comp = Composition(entry[:-3])
                else:
                    comp = Ion.from_formula(entry)
                if len([el for el in comp.elements if el not in
                         [Element("H"), Element("O")]]) == 1:
                    if color_indx >= len(optim_colors):
                        color_indx = color_indx -\
                         int(color_indx / len(optim_colors)) * len(optim_colors)
                    patch = Polygon(xy, facecolor=optim_colors[color_indx],
                                     closed=True, lw=3.0, fill=True)
                    bc = optim_colors[color_indx]
                else:
                    if color_indx >= len(hatch):
                        color_indx = color_indx - int(color_indx / len(hatch)) * len(hatch)
                    patch = Polygon(xy, hatch=hatch[color_indx], closed=True, lw=3.0, fill=False)
                    hc = hatch[color_indx]
                ax.add_patch(patch)
                
            xy_center = (x_coord / npts, y_coord / npts)
            if label_domains:
                if color_indx >= len(optim_colors):
                    color_indx = color_indx -\
                        int(color_indx / len(optim_colors)) * len(optim_colors)
                fc = optim_font_color[color_indx]
                if bc and not hc:
                    bbox = dict(boxstyle="round", fc=fc)
                if hc and not bc:
                    bc = 'k'
                    fc = 'w'
                    bbox = dict(boxstyle="round", hatch=hc, fill=False)
                if bc and hc:
                    bbox = dict(boxstyle="round", hatch=hc, fc=fc)
#                 bbox.set_path_effects([PathEffects.withSimplePatchShadow()])
#                 plt.annotate(latexify_ion(latexify(entry)), xy_center,
#                               color=fc, fontsize=30, bbox=bbox)
                plt.annotate(label_chr[i], xy_center,
                              color=bc, fontsize=30, bbox=bbox)

        lw = 3
        plt.plot(h_line[0], h_line[1], "r--", linewidth=lw)
        plt.plot(o_line[0], o_line[1], "r--", linewidth=lw)
        plt.plot(neutral_line[0], neutral_line[1], "k-.", linewidth=lw)
        plt.plot(V0_line[0], V0_line[1], "k-.", linewidth=lw)

        plt.xlabel("pH")
        plt.ylabel("E (V)")
        plt.title(title, fontsize=20, fontweight='bold')
        return plt
Ejemplo n.º 48
0
 def test_get_composition(self):
     comp = self.entry.composition
     expected_comp = Ion.from_formula('MnO4[-]')
     self.assertEquals(comp, expected_comp, "Wrong composition!")
Ejemplo n.º 49
0
 def setUp(self):
     ion = Ion.from_formula("MnO4[-]")
     self.entry = IonEntry(ion, 49)
Ejemplo n.º 50
0
 def len_elts(entry):
     comp = Composition(
         entry[:-3]) if "(s)" in entry else Ion.from_formula(entry)
     return len(set(comp.elements) - {Element("H"), Element("O")})
Ejemplo n.º 51
0
def plot_pourbaix_diagram(metastability=0.0, ion_concentration=1e-6, fmt='pdf'):
    """
    Creates a Pourbaix diagram for the material in the cwd.

    Args:
        metastability (float): desired metastable tolerance energy
            (meV/atom). <~50 is generally a sensible range to use.
        ion_concentration (float): in mol/kg. Sensible values are
            generally between 1e-8 and 1.
        fmt (str): matplotlib format style. Check the matplotlib
            docs for options.
    """

    # Create a ComputedEntry object for the 2D material.
    composition = Structure.from_file('POSCAR').composition
    energy = Vasprun('vasprun.xml').final_energy

    cmpd = ComputedEntry(composition, energy)

    # Define the chemsys that describes the 2D compound.
    chemsys = ['O', 'H'] + [elt.symbol for elt in composition.elements
                            if elt.symbol not in ['O', 'H']]

    # Experimental ionic energies
    # See ions.yaml for ion formation energies and references.
    exp_dict = ION_DATA['ExpFormEnergy']
    ion_correction = ION_DATA['IonCorrection']

    # Pick out the ions pertaining to the 2D compound.
    ion_dict = dict()
    for elt in chemsys:
        if elt not in ['O', 'H'] and exp_dict[elt]:
            ion_dict.update(exp_dict[elt])

    elements = [Element(elt) for elt in chemsys if elt not in ['O', 'H']]

    # Add "correction" for metastability
    cmpd.correction -= float(cmpd.composition.num_atoms)\
        * float(metastability) / 1000.0

    # Calculate formation energy of the compound from its end
    # members
    form_energy = cmpd.energy
    for elt in composition.as_dict():
        form_energy -= END_MEMBERS[elt] * cmpd.composition[elt]

    # Convert the compound entry to a pourbaix entry.
    # Default concentration for solid entries = 1
    pbx_cmpd = PourbaixEntry(cmpd)
    pbx_cmpd.g0_replace(form_energy)
    pbx_cmpd.reduced_entry()

    # Add corrected ionic entries to the pourbaix diagram
    # dft corrections for experimental ionic energies:
    # Persson et.al PHYSICAL REVIEW B 85, 235438 (2012)
    pbx_ion_entries = list()

    # Get PourbaixEntry corresponding to each ion.
    # Default concentration for ionic entries = 1e-6
    # ion_energy = ion_exp_energy + ion_correction * factor
    # where factor = fraction of element el in the ionic entry
    # compared to the reference entry
    for elt in elements:
        for key in ion_dict:
            comp = Ion.from_formula(key)
            if comp.composition[elt] != 0:
                factor = comp.composition[elt]
                energy = ion_dict[key]
                pbx_entry_ion = PourbaixEntry(IonEntry(comp, energy))
                pbx_entry_ion.correction = ion_correction[elt.symbol]\
                    * factor
                pbx_entry_ion.conc = ion_concentration
                pbx_entry_ion.name = key
                pbx_ion_entries.append(pbx_entry_ion)

    # Generate and plot Pourbaix diagram
    # Each bulk solid/ion has a free energy g of the form:
    # g = g0_ref + 0.0591 * log10(conc) - nO * mu_H2O +
    # (nH - 2nO) * pH + phi * (-nH + 2nO + q)

    all_entries = [pbx_cmpd] + pbx_ion_entries

    pourbaix = PourbaixDiagram(all_entries)

    # Analysis features
    panalyzer = PourbaixAnalyzer(pourbaix)
#    instability = panalyzer.get_e_above_hull(pbx_cmpd)

    plotter = PourbaixPlotter(pourbaix)
    plot = plotter.get_pourbaix_plot(limits=[[0, 14], [-2, 2]],
                                     label_domains=True)
    fig = plot.gcf()
    ax1 = fig.gca()

    # Add coloring to highlight the stability region for the 2D
    # material, if one exists.
    stable_entries = plotter.pourbaix_plot_data(
        limits=[[0, 14], [-2, 2]])[0]

    for entry in stable_entries:
        if entry == pbx_cmpd:
            col = plt.cm.Blues(0)
        else:
            col = plt.cm.rainbow(float(
                ION_COLORS[entry.composition.reduced_formula]))

        vertices = plotter.domain_vertices(entry)
        patch = Polygon(vertices, closed=True, fill=True, color=col)
        ax1.add_patch(patch)

    fig.set_size_inches((11.5, 9))
    plot.tight_layout(pad=1.09)

    # Save plot
    if metastability:
        plot.suptitle('Metastable Tolerance ='
                      ' {} meV/atom'.format(metastability),
                      fontsize=20)
        plot.savefig('{}_{}.{}'.format(
            composition.reduced_formula, ion_concentration, fmt),
            transparent=True)
    else:
        plot.savefig('{}_{}.{}'.format(composition.reduced_formula,
                                        ion_concentration, fmt),
                     transparent=True)

    plot.close()
Ejemplo n.º 52
0
def plot_pourbaix_diagram(metastability=0.0, ion_concentration=1e-6, fmt='pdf'):
    """
    Creates a Pourbaix diagram for the material in the cwd.

    Args:
        metastability (float): desired metastable tolerance energy
            (meV/atom). <~50 is generally a sensible range to use.
        ion_concentration (float): in mol/kg. Sensible values are
            generally between 1e-8 and 1.
        fmt (str): matplotlib format style. Check the matplotlib
            docs for options.
    """

    # Create a ComputedEntry object for the 2D material.
    composition = Structure.from_file('POSCAR').composition
    energy = Vasprun('vasprun.xml').final_energy

    cmpd = ComputedEntry(composition, energy)

    # Define the chemsys that describes the 2D compound.
    chemsys = ['O', 'H'] + [elt.symbol for elt in composition.elements
                            if elt.symbol not in ['O', 'H']]

    # Pick out the ions pertaining to the 2D compound.
    ion_dict = dict()
    for elt in chemsys:
        if elt not in ['O', 'H'] and ION_FORMATION_ENERGIES[elt]:
            ion_dict.update(ION_FORMATION_ENERGIES[elt])

    elements = [Element(elt) for elt in chemsys if elt not in ['O', 'H']]

    # Add "correction" for metastability
    cmpd.correction -= float(cmpd.composition.num_atoms)\
        * float(metastability) / 1000.0

    # Calculate formation energy of the compound from its end
    # members
    form_energy = cmpd.energy
    for elt in composition.as_dict():
        form_energy -= CHEMICAL_POTENTIALS[elt] * cmpd.composition[elt]

    # Convert the compound entry to a pourbaix entry.
    # Default concentration for solid entries = 1
    pbx_cmpd = PourbaixEntry(cmpd)
    pbx_cmpd.g0_replace(form_energy)
    pbx_cmpd.reduced_entry()

    # Add corrected ionic entries to the pourbaix diagram
    # dft corrections for experimental ionic energies:
    # Persson et.al PHYSICAL REVIEW B 85, 235438 (2012)
    pbx_ion_entries = list()

    # Get PourbaixEntry corresponding to each ion.
    # Default concentration for ionic entries = 1e-6
    # ion_energy = ion_exp_energy + ion_correction * factor
    # where factor = fraction of element el in the ionic entry
    # compared to the reference entry
    for elt in elements:
        for key in ion_dict:
            comp = Ion.from_formula(key)
            if comp.composition[elt] != 0:
                factor = comp.composition[elt]
                energy = ion_dict[key]
                pbx_entry_ion = PourbaixEntry(IonEntry(comp, energy))
                pbx_entry_ion.correction = (
                    ION_CORRECTIONS[elt.symbol] * factor
                )
                pbx_entry_ion.conc = ion_concentration
                pbx_entry_ion.name = key
                pbx_ion_entries.append(pbx_entry_ion)

    # Generate and plot Pourbaix diagram
    # Each bulk solid/ion has a free energy g of the form:
    # g = g0_ref + 0.0591 * log10(conc) - nO * mu_H2O +
    # (nH - 2nO) * pH + phi * (-nH + 2nO + q)

    all_entries = [pbx_cmpd] + pbx_ion_entries

    total = sum([composition[el] for el in elements])
    comp_dict = {el.symbol: composition[el]/total for el in elements}
    pourbaix_diagram = PourbaixDiagram(all_entries, comp_dict=comp_dict)

    plotter = PourbaixPlotter(pourbaix_diagram)

    # Plotting details...
    font = "serif"
    fig = plt.figure(figsize=(14, 9))
    ax1 = fig.gca()
    ax1.set_xlim([0, 14])
    ax1.set_xticklabels([int(t) for t in ax1.get_xticks()], fontname=font,
                        fontsize=18)
    ax1.set_ylim(-2, 2)
    ax1.set_yticklabels(ax1.get_yticks(), fontname=font, fontsize=18)
    ax1.set_xlabel("pH", fontname=font, fontsize=18)
    ax1.set_ylabel("Potential vs. SHE (V)", fontname=font, fontsize=18)

    # Outline water's stability range.
    ax1.plot([0, 14], [0, -0.829], color="gray", linestyle="--", alpha=0.7,
             linewidth=2)
    ax1.plot([0, 14], [1.229, 0.401], color="gray", linestyle="--", alpha=0.7,
             linewidth=2)


    stable_entries = plotter.pourbaix_plot_data(
        limits=[[0, 14], [-2, 2]])[0]

    # Add coloring.
    colors = sb.color_palette("Set2", len(stable_entries))

    i = 0
    for entry in stable_entries:
        col = colors[i]
        i += 1
        vertices = plotter.domain_vertices(entry)
        center_x = sum([v[0] for v in vertices])/len(vertices)
        center_y = sum([v[1] for v in vertices])/len(vertices)
        patch = Polygon(vertices, closed=True, fill=True, facecolor=col,
                        linewidth=2, edgecolor="w")
        ax1.text(center_x, center_y, plotter.print_name(entry),
                 verticalalignment="center", horizontalalignment="center",
                 fontname=font, fontsize=18)
        ax1.add_patch(patch)

    plt.savefig("pourbaix.{}".format(fmt))

    plt.close()