class TestPourbaixPlotter(unittest.TestCase): def setUp(self): warnings.simplefilter("ignore") 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.multi_data = loadfn(os.path.join(test_dir, 'multicomp_pbx.json')) self.plotter = PourbaixPlotter(self.pd) def tearDown(self): warnings.resetwarnings() def test_plot_pourbaix(self): # Default limits plt = self.plotter.get_pourbaix_plot() # Non-standard limits plt = self.plotter.get_pourbaix_plot(limits=[[-5, 4], [-2, 2]]) # Try 3-D plot plot_3d = self.plotter._get_plot() plot_3d_unstable = self.plotter._get_plot(label_unstable=True) plt.close() plot_3d.close() plot_3d_unstable.close() def test_plot_entry_stability(self): entry = self.pd.all_entries[0] plt = self.plotter.plot_entry_stability(entry, limits=[[-2, 14], [-3, 3]]) # binary system pd_binary = PourbaixDiagram(self.multi_data['binary'], comp_dict={ "Ag": 0.5, "Te": 0.5 }) binary_plotter = PourbaixPlotter(pd_binary) test_entry = pd_binary._unprocessed_entries[0] plt = binary_plotter.plot_entry_stability(test_entry) plt.close()
class TestPourbaixPlotter(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.plotter = PourbaixPlotter(self.pd) def test_plot_pourbaix(self): plt = self.plotter.get_pourbaix_plot(limits=[[-2, 14], [-3, 3]]) def test_get_entry_stability(self): entry = self.pd.all_entries[0] plt = self.plotter.plot_entry_stability(entry, limits=[[-2, 14], [-3, 3]])
class TestPourbaixPlotter(unittest.TestCase): def setUp(self): warnings.simplefilter("ignore") 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.multi_data = loadfn(os.path.join(test_dir, 'multicomp_pbx.json')) self.plotter = PourbaixPlotter(self.pd) def tearDown(self): warnings.resetwarnings() def test_plot_pourbaix(self): # Default limits plt = self.plotter.get_pourbaix_plot() # Non-standard limits plt = self.plotter.get_pourbaix_plot(limits=[[-5, 4], [-2, 2]]) # Try 3-D plot plot_3d = self.plotter._get_plot() plot_3d_unstable = self.plotter._get_plot(label_unstable=True) plt.close() plot_3d.close() plot_3d_unstable.close() def test_plot_entry_stability(self): entry = self.pd.all_entries[0] plt = self.plotter.plot_entry_stability(entry, limits=[[-2, 14], [-3, 3]]) # binary system pd_binary = PourbaixDiagram(self.multi_data['binary'], comp_dict = {"Ag": 0.5, "Te": 0.5}) binary_plotter = PourbaixPlotter(pd_binary) test_entry = pd_binary._unprocessed_entries[0] plt = binary_plotter.plot_entry_stability(test_entry) plt.close()
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()
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()