def test_multicomponent(self): # Assure no ions get filtered at high concentration ag_n = [e for e in self.test_data['Ag-Te-N'] if not "Te" in e.composition] highconc = PourbaixDiagram(ag_n, filter_solids=True, conc_dict={"Ag": 1e-5, "N": 1}) entry_sets = [set(e.entry_id) for e in highconc.stable_entries] self.assertIn({"mp-124", "ion-17"}, entry_sets) # Binary system pd_binary = PourbaixDiagram(self.test_data['Ag-Te'], filter_solids=True, comp_dict={"Ag": 0.5, "Te": 0.5}, conc_dict={"Ag": 1e-8, "Te": 1e-8}) self.assertEqual(len(pd_binary.stable_entries), 30) test_entry = pd_binary.find_stable_entry(8, 2) self.assertTrue("mp-499" in test_entry.entry_id) # Find a specific multientry to test self.assertEqual(pd_binary.get_decomposition_energy(test_entry, 8, 2), 0) self.assertEqual(pd_binary.get_decomposition_energy( test_entry.entry_list[0], 8, 2), 0) pd_ternary = PourbaixDiagram(self.test_data['Ag-Te-N'], filter_solids=True) self.assertEqual(len(pd_ternary.stable_entries), 49) ag = self.test_data['Ag-Te-N'][30] self.assertAlmostEqual(pd_ternary.get_decomposition_energy(ag, 2, -1), 0) self.assertAlmostEqual(pd_ternary.get_decomposition_energy(ag, 10, -2), 0) # Test invocation of pourbaix diagram from ternary data new_ternary = PourbaixDiagram(pd_ternary.all_entries) self.assertEqual(len(new_ternary.stable_entries), 49) self.assertAlmostEqual(new_ternary.get_decomposition_energy(ag, 2, -1), 0) self.assertAlmostEqual(new_ternary.get_decomposition_energy(ag, 10, -2), 0)
def setUpClass(cls): cls.test_data = loadfn( os.path.join(PymatgenTest.TEST_FILES_DIR, "pourbaix_test_data.json")) cls.pbx = PourbaixDiagram(cls.test_data["Zn"], filter_solids=True) cls.pbx_nofilter = PourbaixDiagram(cls.test_data["Zn"], filter_solids=False)
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)
def get_electrochemical_stability(mpid, pH, potential): ''' A wrapper for pymatgen to construct Pourbaix amd calculate electrochemical stability under reaction condition (i.e. at a given pH and applied potential). Arg: mpid Materials project ID of a bulk composition. e.g. Pt: 'mp-126'. pH: pH at reaction condition. Commonly ones are: acidic: pH=0, neutral: pH=7, and basic pH=14. potential: Applied potential at reaction condition. Returns: stability Electrochemical stability of a composition under reaction condition, unit is eV/atom. ''' mpr = MPRester(read_rc('matproj_api_key')) try: entry = mpr.get_entries(mpid)[0] composition = entry.composition comp_dict = {str(key): value for key, value in composition.items() if key not in ELEMENTS_HO} entries = mpr.get_pourbaix_entries(list(comp_dict.keys())) entry = [entry for entry in entries if entry.entry_id == mpid][0] pbx = PourbaixDiagram(entries, comp_dict=comp_dict, filter_solids=False) stability = pbx.get_decomposition_energy(entry, pH=pH, V=potential) stability = round(stability, 3) # Some mpid's stability are not available except IndexError: stability = np.nan return stability
def test_pourbaix_heavy(self): entries = self.rester.get_pourbaix_entries(["Li", "Mg", "Sn", "Pd"]) pbx = PourbaixDiagram(entries, nproc=4, filter_solids=False) entries = self.rester.get_pourbaix_entries(["Ba", "Ca", "V", "Cu", "F"]) pbx = PourbaixDiagram(entries, nproc=4, filter_solids=False) entries = self.rester.get_pourbaix_entries(["Ba", "Ca", "V", "Cu", "F", "Fe"]) pbx = PourbaixDiagram(entries, nproc=4, filter_solids=False) entries = self.rester.get_pourbaix_entries(["Na", "Ca", "Nd", "Y", "Ho", "F"]) pbx = PourbaixDiagram(entries, nproc=4, filter_solids=False)
def test_get_pourbaix_entries(self): pbx_entries = self.rester.get_pourbaix_entries(["Fe"]) for pbx_entry in pbx_entries: self.assertTrue(isinstance(pbx_entry, PourbaixEntry)) # Ensure entries are pourbaix compatible pbx = PourbaixDiagram(pbx_entries) # Try binary system pbx_entries = self.rester.get_pourbaix_entries(["Fe", "Cr"]) pbx = PourbaixDiagram(pbx_entries)
def test_pourbaix_mpr_pipeline(self): data = self.rester.get_pourbaix_entries(["Zn"]) pbx = PourbaixDiagram(data, filter_solids=True, conc_dict={"Zn": 1e-8}) pbx.find_stable_entry(10, 0) data = self.rester.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), 29) test_entry = pbx.find_stable_entry(8, 2) self.assertEqual(sorted(test_entry.entry_id), ["ion-10", "mp-499"]) # Test against ion sets with multiple equivalent ions (Bi-V regression) entries = self.rester.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 ]))
def test_heavy(self): from pymatgen import MPRester mpr = MPRester() entries = mpr.get_pourbaix_entries(["Li", "Mg", "Sn", "Pd"]) pbx = PourbaixDiagram(entries, nproc=4, filter_solids=False) entries = mpr.get_pourbaix_entries(["Ba", "Ca", "V", "Cu", "F"]) pbx = PourbaixDiagram(entries, nproc=4, filter_solids=False) entries = mpr.get_pourbaix_entries(["Ba", "Ca", "V", "Cu", "F", "Fe"]) pbx = PourbaixDiagram(entries, nproc=4, filter_solids=False) entries = mpr.get_pourbaix_entries(["Na", "Ca", "Nd", "Y", "Ho", "F"]) pbx = PourbaixDiagram(entries, nproc=4, filter_solids=False)
def test_get_pourbaix_entries(self): pbx_entries = self.rester.get_pourbaix_entries(["Fe"]) for pbx_entry in pbx_entries: self.assertTrue(isinstance(pbx_entry, PourbaixEntry)) # Ensure entries are pourbaix compatible pbx = PourbaixDiagram(pbx_entries) # Try binary system pbx_entries = self.rester.get_pourbaix_entries(["Fe", "Cr"]) pbx = PourbaixDiagram(pbx_entries) # Test Zn-S, which has Na in reference solids pbx_entries = self.rester.get_pourbaix_entries(["Zn", "S"])
def test_get_pourbaix_entries(self, mpr): # test input chemsys as a list of elements pbx_entries = mpr.get_pourbaix_entries(["Fe", "Cr"]) for pbx_entry in pbx_entries: assert isinstance(pbx_entry, PourbaixEntry) # test input chemsys as a string pbx_entries = mpr.get_pourbaix_entries("Fe-Cr") for pbx_entry in pbx_entries: assert isinstance(pbx_entry, PourbaixEntry) # test use_gibbs kwarg pbx_entries = mpr.get_pourbaix_entries("Li-O", use_gibbs=300) for pbx_entry in pbx_entries: assert isinstance(pbx_entry, PourbaixEntry) # test solid_compat kwarg with pytest.raises(ValueError, match="Solid compatibility can only be"): mpr.get_pourbaix_entries("Ti-O", solid_compat=None) # test removal of extra elements from reference solids # Li-Zn-S has Na in reference solids pbx_entries = mpr.get_pourbaix_entries("Li-Zn-S") assert not any([e for e in pbx_entries if 'Na' in e.composition]) # Ensure entries are pourbaix compatible PourbaixDiagram(pbx_entries)
def setUp(self): warnings.simplefilter("ignore") self.test_data = loadfn( os.path.join(PymatgenTest.TEST_FILES_DIR, "pourbaix_test_data.json")) self.pd = PourbaixDiagram(self.test_data["Zn"]) self.plotter = PourbaixPlotter(self.pd)
def test_pourbaix_diagram(self): self.assertEqual( {e.name for e in self.pbx.stable_entries}, {"ZnO(s)", "Zn[2+]", "ZnHO2[-]", "ZnO2[2-]", "Zn(s)"}, "List of stable entries does not match", ) self.assertEqual( {e.name for e in self.pbx_nofilter.stable_entries}, { "ZnO(s)", "Zn[2+]", "ZnHO2[-]", "ZnO2[2-]", "Zn(s)", "ZnO2(s)", "ZnH(s)" }, "List of stable entries for unfiltered pbx does not match", ) pbx_lowconc = PourbaixDiagram(self.test_data["Zn"], conc_dict={"Zn": 1e-8}, filter_solids=True) self.assertEqual( {e.name for e in pbx_lowconc.stable_entries}, {"Zn(HO)2(aq)", "Zn[2+]", "ZnHO2[-]", "ZnO2[2-]", "Zn(s)"}, )
def test_get_pourbaix_entries(self): # test input chemsys as a list of elements pbx_entries = self.rester.get_pourbaix_entries(["Fe", "Cr"]) for pbx_entry in pbx_entries: self.assertTrue(isinstance(pbx_entry, PourbaixEntry)) # test input chemsys as a string pbx_entries = self.rester.get_pourbaix_entries("Fe-Cr") for pbx_entry in pbx_entries: self.assertTrue(isinstance(pbx_entry, PourbaixEntry)) fe_two_plus = [e for e in pbx_entries if e.entry_id == "ion-0"][0] self.assertAlmostEqual(fe_two_plus.energy, -1.6228450214319294, places=2) feo2 = [e for e in pbx_entries if e.entry_id == "mp-25332"][0] self.assertAlmostEqual(feo2.energy, 2.5523680849999995, places=2) # Test S, which has Na in reference solids pbx_entries = self.rester.get_pourbaix_entries(["S"]) so4_two_minus = pbx_entries[9] self.assertAlmostEqual(so4_two_minus.energy, 0.0644980568750011, places=2) # Ensure entries are pourbaix compatible PourbaixDiagram(pbx_entries)
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)
def test_multicomponent(self): # Assure no ions get filtered at high concentration ag_n = [ e for e in self.test_data['Ag-Te-N'] if not "Te" in e.composition ] highconc = PourbaixDiagram(ag_n, filter_solids=True, conc_dict={ "Ag": 1e-5, "N": 1 }) entry_sets = [set(e.entry_id) for e in highconc.stable_entries] self.assertIn({"mp-124", "ion-17"}, entry_sets) # Binary system pd_binary = PourbaixDiagram(self.test_data['Ag-Te'], filter_solids=True, comp_dict={ "Ag": 0.5, "Te": 0.5 }, conc_dict={ "Ag": 1e-8, "Te": 1e-8 }) self.assertEqual(len(pd_binary.stable_entries), 30) test_entry = pd_binary.find_stable_entry(8, 2) self.assertTrue("mp-499" in test_entry.entry_id) # Find a specific multientry to test self.assertEqual(pd_binary.get_decomposition_energy(test_entry, 8, 2), 0) self.assertEqual( pd_binary.get_decomposition_energy(test_entry.entry_list[0], 8, 2), 0) pd_ternary = PourbaixDiagram(self.test_data['Ag-Te-N'], filter_solids=True) self.assertEqual(len(pd_ternary.stable_entries), 49) ag = self.test_data['Ag-Te-N'][30] self.assertAlmostEqual(pd_ternary.get_decomposition_energy(ag, 2, -1), 0) self.assertAlmostEqual(pd_ternary.get_decomposition_energy(ag, 10, -2), 0)
def test_plot_entry_stability(self): entry = self.pd.all_entries[0] self.plotter.plot_entry_stability(entry, limits=[[-2, 14], [-3, 3]]) # binary system pd_binary = PourbaixDiagram(self.test_data["Ag-Te"], comp_dict={"Ag": 0.5, "Te": 0.5}) binary_plotter = PourbaixPlotter(pd_binary) plt = binary_plotter.plot_entry_stability(self.test_data["Ag-Te"][53]) plt.close()
def test_serialization(self): d = self.pbx.as_dict() new = PourbaixDiagram.from_dict(d) self.assertEqual(set([e.name for e in new.stable_entries]), {"ZnO(s)", "Zn[2+]", "ZnHO2[-]", "ZnO2[2-]", "Zn(s)"}, "List of stable entries does not match") # Test with unprocessed entries included, this should result in the # previously filtered entries being included d = self.pbx.as_dict(include_unprocessed_entries=True) new = PourbaixDiagram.from_dict(d) self.assertEqual( set([e.name for e in new.stable_entries]), { "ZnO(s)", "Zn[2+]", "ZnHO2[-]", "ZnO2[2-]", "Zn(s)", "ZnO2(s)", "ZnH(s)" }, "List of stable entries for unfiltered pbx does not match") pd_binary = PourbaixDiagram(self.test_data['Ag-Te'], filter_solids=True, comp_dict={ "Ag": 0.5, "Te": 0.5 }, conc_dict={ "Ag": 1e-8, "Te": 1e-8 }) new_binary = PourbaixDiagram.from_dict(pd_binary.as_dict()) self.assertEqual(len(pd_binary.stable_entries), len(new_binary.stable_entries))
def test_serialization(self): d = self.pbx.as_dict() new = PourbaixDiagram.from_dict(d) self.assertEqual(set([e.name for e in new.stable_entries]), {"ZnO(s)", "Zn[2+]", "ZnHO2[-]", "ZnO2[2-]", "Zn(s)"}, "List of stable entries does not match") # Test with unprocessed entries included, this should result in the # previously filtered entries being included d = self.pbx.as_dict(include_unprocessed_entries=True) new = PourbaixDiagram.from_dict(d) self.assertEqual( set([e.name for e in new.stable_entries]), {"ZnO(s)", "Zn[2+]", "ZnHO2[-]", "ZnO2[2-]", "Zn(s)", "ZnO2(s)", "ZnH(s)"}, "List of stable entries for unfiltered pbx does not match") pd_binary = PourbaixDiagram(self.test_data['Ag-Te'], filter_solids=True, comp_dict={"Ag": 0.5, "Te": 0.5}, conc_dict={"Ag": 1e-8, "Te": 1e-8}) new_binary = PourbaixDiagram.from_dict(pd_binary.as_dict()) self.assertEqual(len(pd_binary.stable_entries), len(new_binary.stable_entries))
def test_multicomponent(self): # Binary system pd_binary = PourbaixDiagram(self.test_data['Ag-Te'], filter_solids=True, comp_dict={"Ag": 0.5, "Te": 0.5}, conc_dict={"Ag": 1e-8, "Te": 1e-8}) self.assertEqual(len(pd_binary.stable_entries), 30) test_entry = pd_binary.find_stable_entry(8, 2) self.assertTrue("mp-499" in test_entry.entry_id) # Find a specific multientry to test self.assertEqual(pd_binary.get_decomposition_energy(test_entry, 8, 2), 0) self.assertEqual(pd_binary.get_decomposition_energy( test_entry.entry_list[0], 8, 2), 0) pd_ternary = PourbaixDiagram(self.test_data['Ag-Te-N'], filter_solids=True) self.assertEqual(len(pd_ternary.stable_entries), 49) ag = self.test_data['Ag-Te-N'][30] self.assertAlmostEqual(pd_ternary.get_decomposition_energy(ag, 2, -1), 0) self.assertAlmostEqual(pd_ternary.get_decomposition_energy(ag, 10, -2), 0)
def test_solid_filter(self): entries = self.test_data["Zn"] pbx = PourbaixDiagram(entries, filter_solids=False) oxidized_phase = pbx.find_stable_entry(10, 2) self.assertEqual(oxidized_phase.name, "ZnO2(s)") entries = self.test_data["Zn"] pbx = PourbaixDiagram(entries, filter_solids=True) oxidized_phase = pbx.find_stable_entry(10, 2) self.assertEqual(oxidized_phase.name, "ZnO(s)")
def test_get_pourbaix_entries(self): pbx_entries = self.rester.get_pourbaix_entries(["Fe", "Cr"]) for pbx_entry in pbx_entries: self.assertTrue(isinstance(pbx_entry, PourbaixEntry)) fe_two_plus = [e for e in pbx_entries if e.entry_id == "ion-0"][0] self.assertAlmostEqual(fe_two_plus.energy, -1.6228450214319294) feo2 = [e for e in pbx_entries if e.entry_id == "mp-25332"][0] self.assertAlmostEqual(feo2.energy, 4.424365035000003) # Test S, which has Na in reference solids pbx_entries = self.rester.get_pourbaix_entries(["S"]) so4_two_minus = pbx_entries[9] self.assertAlmostEqual(so4_two_minus.energy, 0.0644980568750011) # Ensure entries are pourbaix compatible pbx = PourbaixDiagram(pbx_entries)
def test_get_pourbaix_entries(self): pbx_entries = self.rester.get_pourbaix_entries(["Fe", "Cr"]) for pbx_entry in pbx_entries: self.assertTrue(isinstance(pbx_entry, PourbaixEntry)) fe_two_plus = [e for e in pbx_entries if e.entry_id == "ion-0"][0] self.assertAlmostEqual(fe_two_plus.energy, -1.580096075) feo2 = [e for e in pbx_entries if e.entry_id == "mp-25332"][0] self.assertAlmostEqual(feo2.energy, 2.51083231) # Test S, which has Na in reference solids pbx_entries = self.rester.get_pourbaix_entries(["S"]) so4_two_minus = pbx_entries[9] self.assertAlmostEqual(so4_two_minus.energy, 0.047817821) # Ensure entries are pourbaix compatible pbx = PourbaixDiagram(pbx_entries)
def create_pbx_object( pourbaix_entries, pourbaix_diagram_options, conc_dict, struct ): # Pass along element restriction if pourbaix_entries == "too_many_elements": return "too_many_elements" self.logger.debug("Updating entries") if pourbaix_entries is None or not pourbaix_entries: self.logger.debug("Preventing updating entries") raise PreventUpdate pourbaix_entries = self.from_data(pourbaix_entries) # filter_solids = True if pourbaix_diagram_options is not None: filter_solids = "filter_solids" in pourbaix_diagram_options else: filter_solids = True # Get composition from structure struct = self.from_data(struct) comp_dict = { str(elt): coeff for elt, coeff in struct.composition.items() if elt not in ELEMENTS_HO } if conc_dict is not None: conc_dict = self.from_data(conc_dict) pourbaix_diagram = PourbaixDiagram( pourbaix_entries, comp_dict=comp_dict, conc_dict=conc_dict, filter_solids=filter_solids, ) self.logger.debug("Generated pourbaix diagram") return self.to_data(pourbaix_diagram)
def getentrystability(entry, stabilitydata, ehullmax, newcomp, plotterobj=None, compdict=None, all_entries=None): if newcomp == True: if compdict == None or all_entries == None: raise Exception( 'Need to provide all_entries and compdict when using new composition!' ) pbx = PourbaixDiagram(all_entries, comp_dict=compdict) plotterobj = PourbaixPlotter(pbx) elif plotterobj == None: raise Exception( 'Need to provide plotter object if using previous composition!') stability, (pH, V) = plotterobj.get_entry_stability(entry, e_hull_max=ehullmax) stability_metrics = stabilitymetrics(stability, pH, V) return (stability_metrics, plotterobj)
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)
def test_get_pourbaix_entries(self): # test input chemsys as a list of elements pbx_entries = self.rester.get_pourbaix_entries(["Fe", "Cr"]) for pbx_entry in pbx_entries: self.assertTrue(isinstance(pbx_entry, PourbaixEntry)) # test input chemsys as a string pbx_entries = self.rester.get_pourbaix_entries("Fe-Cr") for pbx_entry in pbx_entries: self.assertTrue(isinstance(pbx_entry, PourbaixEntry)) # fe_two_plus = [e for e in pbx_entries if e.entry_id == "ion-0"][0] # self.assertAlmostEqual(fe_two_plus.energy, -1.12369, places=3) # # feo2 = [e for e in pbx_entries if e.entry_id == "mp-25332"][0] # self.assertAlmostEqual(feo2.energy, 3.56356, places=3) # # # Test S, which has Na in reference solids # pbx_entries = self.rester.get_pourbaix_entries(["S"]) # so4_two_minus = pbx_entries[9] # self.assertAlmostEqual(so4_two_minus.energy, 0.301511, places=3) # Ensure entries are Pourbaix compatible PourbaixDiagram(pbx_entries)
def test_serialization(self): d = self.pbx.as_dict() new = PourbaixDiagram.from_dict(d) self.assertEqual( {e.name for e in new.stable_entries}, {"ZnO(s)", "Zn[2+]", "ZnHO2[-]", "ZnO2[2-]", "Zn(s)"}, "List of stable entries does not match", ) # Test with unstable solid entries included (filter_solids=False), this should result in the # previously filtered entries being included with pytest.warns( DeprecationWarning, match="The include_unprocessed_entries kwarg is deprecated!"): d = self.pbx_nofilter.as_dict(include_unprocessed_entries=True) new = PourbaixDiagram.from_dict(d) self.assertEqual( {e.name for e in new.stable_entries}, { "ZnO(s)", "Zn[2+]", "ZnHO2[-]", "ZnO2[2-]", "Zn(s)", "ZnO2(s)", "ZnH(s)" }, "List of stable entries for unfiltered pbx does not match", ) pd_binary = PourbaixDiagram( self.test_data["Ag-Te"], filter_solids=True, comp_dict={ "Ag": 0.5, "Te": 0.5 }, conc_dict={ "Ag": 1e-8, "Te": 1e-8 }, ) new_binary = PourbaixDiagram.from_dict(pd_binary.as_dict()) self.assertEqual(len(pd_binary.stable_entries), len(new_binary.stable_entries))
def test_solid_filter(self): entries = self.test_data['Ag-Te-N'] pbx = PourbaixDiagram(entries, filter_solids=True) pbx.get_decomposition_energy(entries[0], 0, 0)
def test_nofilter(self): entries = self.test_data['Ag-Te'] pbx = PourbaixDiagram(entries) pbx.get_decomposition_energy(entries[0], 0, 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), 29) 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 ]))
def test_get_pourbaix_domains(self): domains = PourbaixDiagram.get_pourbaix_domains(self.test_data['Zn']) self.assertEqual(len(domains[0]), 7)
def test_multielement_parallel(self): # Simple test to ensure that multiprocessing is working test_entries = self.test_data["Ag-Te-N"] nproc = multiprocessing.cpu_count() pbx = PourbaixDiagram(test_entries, filter_solids=True, nproc=nproc) self.assertEqual(len(pbx.stable_entries), 49)
def test_get_pourbaix_domains(self): domains = PourbaixDiagram.get_pourbaix_domains(self.test_data["Zn"]) self.assertEqual(len(domains[0]), 7)
def test_multicomponent(self): # Assure no ions get filtered at high concentration ag_n = [ e for e in self.test_data["Ag-Te-N"] if "Te" not in e.composition ] highconc = PourbaixDiagram(ag_n, filter_solids=True, conc_dict={ "Ag": 1e-5, "N": 1 }) entry_sets = [set(e.entry_id) for e in highconc.stable_entries] self.assertIn({"mp-124", "ion-17"}, entry_sets) # Binary system pd_binary = PourbaixDiagram( self.test_data["Ag-Te"], filter_solids=True, comp_dict={ "Ag": 0.5, "Te": 0.5 }, conc_dict={ "Ag": 1e-8, "Te": 1e-8 }, ) self.assertEqual(len(pd_binary.stable_entries), 30) test_entry = pd_binary.find_stable_entry(8, 2) self.assertTrue("mp-499" in test_entry.entry_id) # Find a specific multientry to test self.assertEqual(pd_binary.get_decomposition_energy(test_entry, 8, 2), 0) pd_ternary = PourbaixDiagram(self.test_data["Ag-Te-N"], filter_solids=True) self.assertEqual(len(pd_ternary.stable_entries), 49) # Fetch a solid entry and a ground state entry mixture ag_te_n = self.test_data["Ag-Te-N"][-1] ground_state_ag_with_ions = MultiEntry( [self.test_data["Ag-Te-N"][i] for i in [4, 18, 30]], weights=[1 / 3, 1 / 3, 1 / 3], ) self.assertAlmostEqual( pd_ternary.get_decomposition_energy(ag_te_n, 2, -1), 2.767822855765) self.assertAlmostEqual( pd_ternary.get_decomposition_energy(ag_te_n, 10, -2), 3.756840056890625) self.assertAlmostEqual( pd_ternary.get_decomposition_energy(ground_state_ag_with_ions, 2, -1), 0) # Test invocation of pourbaix diagram from ternary data new_ternary = PourbaixDiagram(pd_ternary.all_entries) self.assertEqual(len(new_ternary.stable_entries), 49) self.assertAlmostEqual( new_ternary.get_decomposition_energy(ag_te_n, 2, -1), 2.767822855765) self.assertAlmostEqual( new_ternary.get_decomposition_energy(ag_te_n, 10, -2), 3.756840056890625) self.assertAlmostEqual( new_ternary.get_decomposition_energy(ground_state_ag_with_ions, 2, -1), 0)
def setUpClass(cls): cls.test_data = loadfn(os.path.join(test_dir, 'pourbaix_test_data.json')) cls.pbx = PourbaixDiagram(cls.test_data['Zn'], filter_solids=True) cls.pbx_nofilter = PourbaixDiagram(cls.test_data['Zn'], filter_solids=False)
def setUp(self): warnings.simplefilter("ignore") self.test_data = loadfn(os.path.join(test_dir, "pourbaix_test_data.json")) self.pd = PourbaixDiagram(self.test_data["Zn"]) self.plotter = PourbaixPlotter(self.pd)