class TestPourbaixAnalyzer(unittest.TestCase): def setUp(self): module_dir = os.path.dirname(os.path.abspath(__file__)) (elements, entries) = PourbaixEntryIO.from_csv(os.path.join(module_dir, "test_entries.csv")) self.num_simplices = {"Zn(s)": 7, "ZnO2(s)": 7, "Zn[2+]": 4, "ZnO2[2-]": 4, "ZnHO2[-]": 4} self.e_above_hull_test = {"ZnHO[+]": 0.0693, "ZnO(aq)": 0.0624} self.decomp_test = {"ZnHO[+]": {"ZnO(s)": 0.5, "Zn[2+]": 0.5}, "ZnO(aq)": {"ZnO(s)": 1.0}} self.pd = PourbaixDiagram(entries) self.analyzer = PourbaixAnalyzer(self.pd) def test_get_facet_chempots(self): range_map = self.analyzer.get_chempot_range_map() range_map_dict = {} for PourEntry in range_map.keys(): range_map_dict[PourEntry.name] = range_map[PourEntry] for entry in self.num_simplices.keys(): self.assertEqual(len(range_map_dict[entry]), self.num_simplices[entry]) def test_get_decomp(self): for entry in [entry for entry in self.pd.all_entries if entry not in self.pd.stable_entries]: decomp_entries = self.analyzer.get_decomposition(entry) for entr in decomp_entries: self.assertEqual(decomp_entries[entr], self.decomp_test[entry.name][entr.name]) e_above_hull = self.analyzer.get_e_above_hull(entry) self.assertAlmostEqual(e_above_hull, self.e_above_hull_test[entry.name], 3)
def pourbaix_plot_data(self, limits=None): """ Get data required to plot Pourbaix diagram. Args: limits: 2D list containing limits of the Pourbaix diagram of the form [[xlo, xhi], [ylo, yhi]] Returns: stable_entries, unstable_entries stable_entries: dict of lines. The keys are Pourbaix Entries, and lines are in the form of a list unstable_entries: list of unstable entries """ analyzer = PourbaixAnalyzer(self._pd) self._analyzer = analyzer if limits: analyzer.chempot_limits = limits chempot_ranges = analyzer.get_chempot_range_map(limits) self.chempot_ranges = chempot_ranges stable_entries_list = collections.defaultdict(list) for entry in chempot_ranges: for line in chempot_ranges[entry]: x = [line.coords[0][0], line.coords[1][0]] y = [line.coords[0][1], line.coords[1][1]] coords = [x, y] stable_entries_list[entry].append(coords) unstable_entries_list = [entry for entry in self._pd.all_entries if entry not in self._pd.stable_entries] return stable_entries_list, unstable_entries_list
def stable_mat_one_elem(pH, V, mat): """ Returns pymatgen pourbaix entry object corresponding to most stable species Args: pH: pH of system V: Potential with reference to the Standard Hydrogen Electrode (SHE) """ # | - - stable_mat_one_elem from pourdiag_single import pourdiag_single as pd_sgle from pymatgen.analysis.pourbaix.maker import PourbaixDiagram # Access entry Gibbs(pH, V) from pymatgen.analysis.pourbaix.analyzer import PourbaixAnalyzer pH = 0 V = 1 mat = 'Pt' all_entries = pd_sgle(mat) pourbaix = PourbaixDiagram(all_entries) PA = PourbaixAnalyzer(pourbaix) templist = [] for i in all_entries: templist.append(PA.g(i, pH, V)) minE = min(templist) for i in all_entries: if PA.g(i, pH, V) == minE: StableSpec = i return StableSpec # Returns the entries object of the stable species
def setUp(self): module_dir = os.path.dirname(os.path.abspath(__file__)) (elements, entries) = PourbaixEntryIO.from_csv( os.path.join(module_dir, "test_entries.csv")) self.num_simplices = { "Zn(s)": 7, "ZnO2(s)": 7, "Zn[2+]": 4, "ZnO2[2-]": 4, "ZnHO2[-]": 4 } self.e_above_hull_test = {"ZnHO[+]": 0.0693, "ZnO(aq)": 0.0624} self.decomp_test = { "ZnHO[+]": { "ZnO(s)": 0.5, "Zn[2+]": 0.5 }, "ZnO(aq)": { "ZnO(s)": 1.0 } } self.pd = PourbaixDiagram(entries) self.analyzer = PourbaixAnalyzer(self.pd) self.multi_data = loadfn(os.path.join(test_dir, 'multicomp_pbx.json')) warnings.simplefilter("ignore")
def test_get_entry_stability(self): stab = self.analyzer.get_entry_stability(self.pd.all_entries[0], pH=0, V=1) self.assertAlmostEqual(stab, 3.88159999) # binary pd_binary = PourbaixDiagram(self.multi_data['binary'], comp_dict = {"Ag": 0.5, "Te": 0.5}) analyzer_binary = PourbaixAnalyzer(pd_binary) ghull = analyzer_binary.get_entry_stability(self.multi_data['binary'][16], 0, -5)
def test_binary(self): # Test get_all_decomp_and_e_above_hull pd_binary = PourbaixDiagram(self.multi_data['binary'], comp_dict = {"Ag": 0.5, "Te": 0.5}) analyzer_binary = PourbaixAnalyzer(pd_binary) te_entry = pd_binary._unprocessed_entries[4] de, hull_e, entries = analyzer_binary.get_all_decomp_and_e_above_hull(te_entry) self.assertEqual(len(de), 10) self.assertEqual(len(hull_e), 10) self.assertAlmostEqual(hull_e[0], 5.4419792326439893)
def test_ternary(self): # Ternary te_entry = self.multi_data['ternary'][20] pd_ternary = PourbaixDiagram(self.multi_data['ternary'], comp_dict = {"Ag": 0.33333, "Te": 0.33333, "N": 0.33333}) analyzer_ternary = PourbaixAnalyzer(pd_ternary) de, hull_e, entries = analyzer_ternary.get_all_decomp_and_e_above_hull(te_entry) self.assertEqual(len(de), 116) self.assertEqual(len(hull_e), 116) self.assertAlmostEqual(hull_e[0], 29.2520325229)
def test_ternary(self): # Ternary te_entry = self.multi_data['ternary'][20] pd_ternary = PourbaixDiagram(self.multi_data['ternary'], comp_dict = {"Ag": 0.33333, "Te": 0.33333, "N": 0.33333}) analyzer_ternary = PourbaixAnalyzer(pd_ternary) de, hull_e, entries = analyzer_ternary.get_all_decomp_and_e_above_hull(te_entry) self.assertEqual(len(de), 116) self.assertEqual(len(hull_e), 116) tuples = zip(de, hull_e, entries) test_tuple = [t for t in tuples if t[2].name=='N2(s) + TeO4[2-] + Ag[2+]'][0] self.assertAlmostEqual(test_tuple[1], 50.337069095866745)
def test_binary(self): # Test get_all_decomp_and_e_above_hull pd_binary = PourbaixDiagram(self.multi_data['binary'], comp_dict = {"Ag": 0.5, "Te": 0.5}) analyzer_binary = PourbaixAnalyzer(pd_binary) te_entry = [e for e in pd_binary._unprocessed_entries if e.composition.formula == "Te3"][0] de, hull_e, entries = analyzer_binary.get_all_decomp_and_e_above_hull(te_entry) self.assertEqual(len(de), 10) self.assertEqual(len(hull_e), 10) # Find a specific multientry to test tuples = zip(de, hull_e, entries) test_tuple = [t for t in tuples if t[2].name=='Te(s) + Ag[2+]'][0] self.assertAlmostEqual(test_tuple[1], 5.1396968548627315)
def test_v_fe(self): v_fe_entries = self.multi_data['v_fe'] pd = PourbaixDiagram(v_fe_entries, comp_dict={"Fe": 0.5, "V": 0.5}) analyzer_vfe = PourbaixAnalyzer(pd) entries = sorted(pd._all_entries, key=lambda x: x.energy) self.assertAlmostEqual(entries[1].weights[1], 0.6666666666) self.assertAlmostEqual(entries[1].energy, -110.77582628499995) self.assertAlmostEqual(entries[100].energy, -20.685496454465955)
def setUp(self): module_dir = os.path.dirname(os.path.abspath(__file__)) (elements, entries) = PourbaixEntryIO.from_csv(os.path.join(module_dir, "test_entries.csv")) self.num_simplices = {"Zn(s)": 7, "ZnO2(s)": 7, "Zn[2+]": 4, "ZnO2[2-]": 4, "ZnHO2[-]": 4} self.e_above_hull_test = {"ZnHO[+]": 0.0693, "ZnO(aq)": 0.0624} self.decomp_test = {"ZnHO[+]": {"ZnO(s)": 0.5, "Zn[2+]": 0.5}, "ZnO(aq)": {"ZnO(s)": 1.0}} self.pd = PourbaixDiagram(entries) self.analyzer = PourbaixAnalyzer(self.pd)
def pourbaix_plot_data(element1,element2,mat_co_1,limits=None): """ Get stable/unstable species required to plot Pourbaix diagram. Args: limits: 2D list containing limits of the Pourbaix diagram of the form [[xlo, xhi], [ylo, yhi]] Returns: stable_entries, unstable_entries stable_entries: dict of lines. The keys are Pourbaix Entries, and lines are in the form of a list unstable_entries: list of unstable entries """ entries= pd_entries(element1,element2) pourbaix = PourbaixDiagram(entries, {element1: mat_co_1, element2: 1- mat_co_1}) pd = pourbaix analyzer = PourbaixAnalyzer(pd) # self._analyzer = analyzer if limits: analyzer.chempot_limits = limits chempot_ranges = analyzer.get_chempot_range_map(limits) # self.chempot_ranges = chempot_ranges stable_entries_list = collections.defaultdict(list) for entry in chempot_ranges: for line in chempot_ranges[entry]: x = [line.coords[0][0], line.coords[1][0]] y = [line.coords[0][1], line.coords[1][1]] coords = [x, y] stable_entries_list[entry].append(coords) unstable_entries_list = [entry for entry in pd.all_entries if entry not in pd.stable_entries] stable_entries_aq = [] stable_entries_solid = [] for entry in entries: entry_kj_per_mol = entry.energy * 96.4853 if entry.phase_type == "Ion": stable_entries_aq.append([entry.name,entry_kj_per_mol]) else: stable_entries_solid.append([entry.name,entry_kj_per_mol]) return stable_entries_list, unstable_entries_list, stable_entries_aq, stable_entries_solid
class TestPourbaixAnalyzer(PymatgenTest): def setUp(self): module_dir = os.path.dirname(os.path.abspath(__file__)) (elements, entries) = PourbaixEntryIO.from_csv( os.path.join(module_dir, "test_entries.csv")) self.num_simplices = { "Zn(s)": 7, "ZnO2(s)": 7, "Zn[2+]": 4, "ZnO2[2-]": 4, "ZnHO2[-]": 4 } self.e_above_hull_test = {"ZnHO[+]": 0.0693, "ZnO(aq)": 0.0624} self.decomp_test = { "ZnHO[+]": { "ZnO(s)": 0.5, "Zn[2+]": 0.5 }, "ZnO(aq)": { "ZnO(s)": 1.0 } } self.pd = PourbaixDiagram(entries) self.analyzer = PourbaixAnalyzer(self.pd) self.multi_data = loadfn(os.path.join(test_dir, 'multicomp_pbx.json')) warnings.simplefilter("ignore") def tearDown(self): warnings.resetwarnings() def test_chempot_range_map(self): range_map = self.analyzer.get_chempot_range_map() range_map_dict = {} for PourEntry in range_map.keys(): range_map_dict[PourEntry.name] = range_map[PourEntry] for entry in self.num_simplices.keys(): self.assertEqual(len(range_map_dict[entry]), self.num_simplices[entry]) ZnO2_entry = [e for e in self.pd.all_entries if e.name == "ZnO2(s)"][0] test_vertices = self.analyzer.pourbaix_domain_vertices[ZnO2_entry] self.assertArrayAlmostEqual(test_vertices[0], [16, 4]) def test_get_decomp(self): for entry in [ entry for entry in self.pd.all_entries if entry not in self.pd.stable_entries ]: decomp_entries = self.analyzer.get_decomposition(entry) for entr in decomp_entries: self.assertEqual(decomp_entries[entr], self.decomp_test[entry.name][entr.name]) e_above_hull = self.analyzer.get_e_above_hull(entry) self.assertAlmostEqual(e_above_hull, self.e_above_hull_test[entry.name], 3) def test_binary(self): # Test get_all_decomp_and_e_above_hull pd_binary = PourbaixDiagram(self.multi_data['binary'], comp_dict={ "Ag": 0.5, "Te": 0.5 }) analyzer_binary = PourbaixAnalyzer(pd_binary) te_entry = [ e for e in pd_binary._unprocessed_entries if e.composition.formula == "Te3" ][0] de, hull_e, entries = analyzer_binary.get_all_decomp_and_e_above_hull( te_entry) self.assertEqual(len(de), 10) self.assertEqual(len(hull_e), 10) # Find a specific multientry to test tuples = zip(de, hull_e, entries) test_tuple = [t for t in tuples if t[2].name == 'Te(s) + Ag[2+]'][0] self.assertAlmostEqual(test_tuple[1], 5.1396968548627315) def test_ternary(self): # Ternary te_entry = self.multi_data['ternary'][20] pd_ternary = PourbaixDiagram(self.multi_data['ternary'], comp_dict={ "Ag": 0.33333, "Te": 0.33333, "N": 0.33333 }) analyzer_ternary = PourbaixAnalyzer(pd_ternary) de, hull_e, entries = analyzer_ternary.get_all_decomp_and_e_above_hull( te_entry) self.assertEqual(len(de), 116) self.assertEqual(len(hull_e), 116) tuples = zip(de, hull_e, entries) test_tuple = [ t for t in tuples if t[2].name == 'N2(s) + TeO4[2-] + Ag[2+]' ][0] self.assertAlmostEqual(test_tuple[1], 50.337069095866745) def test_get_entry_stability(self): stab = self.analyzer.get_entry_stability(self.pd.all_entries[0], pH=0, V=1) self.assertAlmostEqual(stab, 3.88159999) # binary pd_binary = PourbaixDiagram(self.multi_data['binary'], comp_dict={ "Ag": 0.5, "Te": 0.5 }) analyzer_binary = PourbaixAnalyzer(pd_binary) ghull = analyzer_binary.get_entry_stability( self.multi_data['binary'][16], 0, -5)
class TestPourbaixAnalyzer(unittest.TestCase): def setUp(self): module_dir = os.path.dirname(os.path.abspath(__file__)) (elements, entries) = PourbaixEntryIO.from_csv(os.path.join(module_dir, "test_entries.csv")) self.num_simplices = {"Zn(s)": 7, "ZnO2(s)": 7, "Zn[2+]": 4, "ZnO2[2-]": 4, "ZnHO2[-]": 4} self.e_above_hull_test = {"ZnHO[+]": 0.0693, "ZnO(aq)": 0.0624} self.decomp_test = {"ZnHO[+]": {"ZnO(s)": 0.5, "Zn[2+]": 0.5}, "ZnO(aq)": {"ZnO(s)": 1.0}} self.pd = PourbaixDiagram(entries) self.analyzer = PourbaixAnalyzer(self.pd) self.multi_data = loadfn(os.path.join(test_dir, 'multicomp_pbx.json')) def test_get_facet_chempots(self): range_map = self.analyzer.get_chempot_range_map() range_map_dict = {} for PourEntry in range_map.keys(): range_map_dict[PourEntry.name] = range_map[PourEntry] for entry in self.num_simplices.keys(): self.assertEqual(len(range_map_dict[entry]), self.num_simplices[entry]) def test_get_decomp(self): for entry in [entry for entry in self.pd.all_entries if entry not in self.pd.stable_entries]: decomp_entries = self.analyzer.get_decomposition(entry) for entr in decomp_entries: self.assertEqual(decomp_entries[entr], self.decomp_test[entry.name][entr.name]) e_above_hull = self.analyzer.get_e_above_hull(entry) self.assertAlmostEqual(e_above_hull, self.e_above_hull_test[entry.name], 3) def test_binary(self): # Test get_all_decomp_and_e_above_hull pd_binary = PourbaixDiagram(self.multi_data['binary'], comp_dict = {"Ag": 0.5, "Te": 0.5}) analyzer_binary = PourbaixAnalyzer(pd_binary) te_entry = pd_binary._unprocessed_entries[4] de, hull_e, entries = analyzer_binary.get_all_decomp_and_e_above_hull(te_entry) self.assertEqual(len(de), 10) self.assertEqual(len(hull_e), 10) self.assertAlmostEqual(hull_e[0], 5.4419792326439893) def test_ternary(self): # Ternary te_entry = self.multi_data['ternary'][20] pd_ternary = PourbaixDiagram(self.multi_data['ternary'], comp_dict = {"Ag": 0.33333, "Te": 0.33333, "N": 0.33333}) analyzer_ternary = PourbaixAnalyzer(pd_ternary) de, hull_e, entries = analyzer_ternary.get_all_decomp_and_e_above_hull(te_entry) self.assertEqual(len(de), 116) self.assertEqual(len(hull_e), 116) self.assertAlmostEqual(hull_e[0], 29.2520325229) def test_v_fe(self): v_fe_entries = self.multi_data['v_fe'] pd = PourbaixDiagram(v_fe_entries, comp_dict = {"Fe": 0.5, "V": 0.5}) analyzer_vfe = PourbaixAnalyzer(pd) entries = sorted(pd._all_entries, key=lambda x: x.energy) self.assertAlmostEqual(entries[1].weights[1], 0.6666666666) self.assertAlmostEqual(entries[1].energy, -110.77582628499995) self.assertAlmostEqual(entries[100].energy, -20.685496454465955)
class TestPourbaixAnalyzer(unittest.TestCase): def setUp(self): module_dir = os.path.dirname(os.path.abspath(__file__)) (elements, entries) = PourbaixEntryIO.from_csv( os.path.join(module_dir, "test_entries.csv")) self.num_simplices = { "Zn(s)": 7, "ZnO2(s)": 7, "Zn[2+]": 4, "ZnO2[2-]": 4, "ZnHO2[-]": 4 } self.e_above_hull_test = {"ZnHO[+]": 0.0693, "ZnO(aq)": 0.0624} self.decomp_test = { "ZnHO[+]": { "ZnO(s)": 0.5, "Zn[2+]": 0.5 }, "ZnO(aq)": { "ZnO(s)": 1.0 } } self.pd = PourbaixDiagram(entries) self.analyzer = PourbaixAnalyzer(self.pd) self.multi_data = loadfn(os.path.join(test_dir, 'multicomp_pbx.json')) def test_get_facet_chempots(self): range_map = self.analyzer.get_chempot_range_map() range_map_dict = {} for PourEntry in range_map.keys(): range_map_dict[PourEntry.name] = range_map[PourEntry] for entry in self.num_simplices.keys(): self.assertEqual(len(range_map_dict[entry]), self.num_simplices[entry]) def test_get_decomp(self): for entry in [ entry for entry in self.pd.all_entries if entry not in self.pd.stable_entries ]: decomp_entries = self.analyzer.get_decomposition(entry) for entr in decomp_entries: self.assertEqual(decomp_entries[entr], self.decomp_test[entry.name][entr.name]) e_above_hull = self.analyzer.get_e_above_hull(entry) self.assertAlmostEqual(e_above_hull, self.e_above_hull_test[entry.name], 3) def test_binary(self): # Test get_all_decomp_and_e_above_hull pd_binary = PourbaixDiagram(self.multi_data['binary'], comp_dict={ "Ag": 0.5, "Te": 0.5 }) analyzer_binary = PourbaixAnalyzer(pd_binary) te_entry = pd_binary._unprocessed_entries[4] de, hull_e, entries = analyzer_binary.get_all_decomp_and_e_above_hull( te_entry) self.assertEqual(len(de), 10) self.assertEqual(len(hull_e), 10) self.assertAlmostEqual(hull_e[0], 5.4419792326439893) def test_ternary(self): # Ternary te_entry = self.multi_data['ternary'][20] pd_ternary = PourbaixDiagram(self.multi_data['ternary'], comp_dict={ "Ag": 0.33333, "Te": 0.33333, "N": 0.33333 }) analyzer_ternary = PourbaixAnalyzer(pd_ternary) de, hull_e, entries = analyzer_ternary.get_all_decomp_and_e_above_hull( te_entry) self.assertEqual(len(de), 116) self.assertEqual(len(hull_e), 116) self.assertAlmostEqual(hull_e[0], 29.2520325229) def test_v_fe(self): v_fe_entries = self.multi_data['v_fe'] pd = PourbaixDiagram(v_fe_entries, comp_dict={"Fe": 0.5, "V": 0.5}) analyzer_vfe = PourbaixAnalyzer(pd) entries = sorted(pd._all_entries, key=lambda x: x.energy) self.assertAlmostEqual(entries[1].weights[1], 0.6666666666) self.assertAlmostEqual(entries[1].energy, -110.77582628499995) self.assertAlmostEqual(entries[100].energy, -20.685496454465955)
def pourbaix_data(element1, element2, mat_co_1, limits): """ Get data required to plot Pourbaix diagram. Args: limits: 2D list containing limits of the Pourbaix diagram of the form [[xlo, xhi], [ylo, yhi]] Returns: """ (stable_pb, unstable_pb) = pourbaix_plot_data(element1, element2, mat_co_1, limits) if limits: xlim = limits[0] ylim = limits[1] else: analyzer = PourbaixAnalyzer(pd) xlim = analyzer.chempot_limits[0] ylim = analyzer.chempot_limits[1] # h_line[0]: x_axis: [pH_h_lmin, pH_h_max] # h_line[1]: y_axis: [U_h_lmin, U_h_max] 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]]) labels_name = [] labels_loc_x = [] labels_loc_y = [] species_lines = [] for entry, lines in stable_pb.items(): center_x = 0.0 center_y = 0.0 coords = [] count_center = 0.0 for line in lines: (x, y) = line species_lines.append(line) ## print(x,y) # plt.plot(x,y,"k-",linewidth = 3) for coord in np.array(line).T: if not in_coord_list(coords, coord): coords.append(coord.tolist()) cx = coord[0] cy = coord[1] center_x += cx center_y += cy count_center += 1.0 if count_center == 0.0: count_center = 1.0 center_x /= count_center center_y /= count_center if ((center_x <= xlim[0]) | (center_x >= xlim[1]) | (center_y <= ylim[0]) | (center_y >= ylim[1])): continue # xy = (center_x, center_y) labels_name.append(print_name(element1, element2, mat_co_1, entry)) # labels_name.append(entry) labels_loc_x.append(center_x) labels_loc_y.append(center_y) return labels_name, labels_loc_x, labels_loc_y, species_lines, h_line, o_line, neutral_line, V0_line