Ejemplo n.º 1
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.º 2
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.º 3
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.º 4
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.º 5
0
# -

pbx_entry_ion.energy

assert False

# + jupyter={}
# | - Ion Entries
# Calculate DFT reference energy for ions (See Persson et al, PRB (2012))
pbx_ion_entries = []
for id in ion_dict:
    # Ion name-> Ion comp name (ex. Fe[3+] -> Ion: Fe1 +3)
    comp = Ion.from_formula(id['Name'])
    energy = id['Energy']  # + ion_correction * factor
    print(id['Name'], comp, energy)
    pbx_entry_ion = PourbaixEntry(IonEntry(comp, energy))
    # AP pbx_entry_ion.name = id['Name']
    pbx_entry_ion.conc = 1.0e-6
    if pbx_entry_ion.name not in ['jfdksl']:  # ['H2RuO2[2+]']: #["RuO4(aq)"]:
        pbx_ion_entries.append(pbx_entry_ion)
#__|

all_entries = pbx_solid_entries + pbx_ion_entries
#__|

# | - Save all_entries

# Pickling data ######################################################
# import os; import pickle
# directory = "out_data"
# if not os.path.exists(directory): os.makedirs(directory)
Ejemplo n.º 6
0
    def get_ion_entries(self,
                        pd: PhaseDiagram,
                        ion_ref_data: List[dict] = None) -> List[IonEntry]:
        """
        Retrieve IonEntry objects that can be used in the construction of
        Pourbaix Diagrams. The energies of the IonEntry are calculaterd from
        the solid energies in the provided Phase Diagram to be
        consistent with experimental free energies.

        NOTE! This is an advanced method that assumes detailed understanding
        of how to construct computational Pourbaix Diagrams. If you just want
        to build a Pourbaix Diagram using default settings, use get_pourbaix_entries.

        Args:
            pd: Solid phase diagram on which to construct IonEntry. Note that this
                Phase Diagram MUST include O and H in its chemical system. For example,
                to retrieve IonEntry for Ti, the phase diagram passed here should contain
                materials in the H-O-Ti chemical system. It is also assumed that solid
                energies have already been corrected with MaterialsProjectAqueousCompatibility,
                which is necessary for proper construction of Pourbaix diagrams.
            ion_ref_data: Aqueous ion reference data. If None (default), the data
                are downloaded from the Aqueous Ion Reference Data project hosted
                on MPContribs. To add a custom ionic species, first download
                data using get_ion_reference_data, then add or customize it with
                your additional data, and pass the customized list here.

        Returns:
            [IonEntry]: IonEntry are similar to PDEntry objects. Their energies
                are free energies in eV.
        """
        # determine the chemsys from the phase diagram
        chemsys = "-".join([el.symbol for el in pd.elements])

        # raise ValueError if O and H not in chemsys
        if "O" not in chemsys or "H" not in chemsys:
            raise ValueError(
                "The phase diagram chemical system must contain O and H! Your"
                f" diagram chemical system is {chemsys}.")

        if not ion_ref_data:
            ion_data = self.get_ion_reference_data_for_chemsys(chemsys)
        else:
            ion_data = ion_ref_data

        # position the ion energies relative to most stable reference state
        ion_entries = []
        for n, i_d in enumerate(ion_data):
            ion = Ion.from_formula(i_d["formula"])
            refs = [
                e for e in pd.all_entries
                if e.composition.reduced_formula == i_d["data"]["RefSolid"]
            ]
            if not refs:
                raise ValueError("Reference solid not contained in entry list")
            stable_ref = sorted(refs, key=lambda x: x.energy_per_atom)[0]
            rf = stable_ref.composition.get_reduced_composition_and_factor()[1]

            # TODO - need a more robust way to convert units
            # use pint here?
            if i_d["data"]["ΔGᶠRefSolid"]["unit"] == "kJ/mol":
                # convert to eV/formula unit
                ref_solid_energy = i_d["data"]["ΔGᶠRefSolid"]["value"] / 96.485
            elif i_d["data"]["ΔGᶠRefSolid"]["unit"] == "MJ/mol":
                # convert to eV/formula unit
                ref_solid_energy = i_d["data"]["ΔGᶠRefSolid"]["value"] / 96485
            else:
                raise ValueError(
                    f"Ion reference solid energy has incorrect unit {i_d['data']['ΔGᶠRefSolid']['unit']}"
                )
            solid_diff = pd.get_form_energy(stable_ref) - ref_solid_energy * rf
            elt = i_d["data"]["MajElements"]
            correction_factor = ion.composition[elt] / stable_ref.composition[
                elt]
            # TODO - need a more robust way to convert units
            # use pint here?
            if i_d["data"]["ΔGᶠ"]["unit"] == "kJ/mol":
                # convert to eV/formula unit
                ion_free_energy = i_d["data"]["ΔGᶠ"]["value"] / 96.485
            elif i_d["data"]["ΔGᶠ"]["unit"] == "MJ/mol":
                # convert to eV/formula unit
                ion_free_energy = i_d["data"]["ΔGᶠ"]["value"] / 96485
            else:
                raise ValueError(
                    f"Ion free energy has incorrect unit {i_d['data']['ΔGᶠ']['unit']}"
                )
            energy = ion_free_energy + solid_diff * correction_factor
            ion_entries.append(IonEntry(ion, energy))

        return ion_entries
Ejemplo n.º 7
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