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)
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")
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")
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
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
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)
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
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
class TestPourbaixEntry(unittest.TestCase): """ Test all functions using a fictitious entry """ 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 def test_pourbaix_entry(self): self.assertEqual(self.PxIon.entry.energy, 25, "Wrong Energy!") self.assertEqual(self.PxIon.entry.name,\ "MnO4[-]", "Wrong Entry!") self.assertEqual(self.PxSol.entry.energy, 49, "Wrong Energy!") self.assertEqual(self.PxSol.entry.name,\ "Mn2O3", "Wrong Entry!") self.assertEqual(self.PxIon.g0, 25, "Wrong Energy!") self.assertEqual(self.PxSol.g0, 49, "Wrong Energy!") self.assertEqual(self.PxIon.conc, 1e-4, "Wrong concentration!") def test_calc_coeff_terms(self): self.assertEqual(self.PxIon.npH, -8, "Wrong npH!") self.assertEqual(self.PxIon.nPhi, -7, "Wrong nPhi!") self.assertEqual(self.PxIon.nH2O, 4, "Wrong nH2O!") self.assertEqual(self.PxSol.npH, -6, "Wrong npH!") self.assertEqual(self.PxSol.nPhi, -6, "Wrong nPhi!") self.assertEqual(self.PxSol.nH2O, 3, "Wrong nH2O!") def test_to_from_dict(self): d = self.PxIon.as_dict() ion_entry = self.PxIon.from_dict(d) self.assertEqual(ion_entry.entry.name, "MnO4[-]", "Wrong Entry!")
class TestPourbaixEntry(unittest.TestCase): """ Test all functions using a fictitious entry """ 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) def test_pourbaix_entry(self): self.assertEquals(self.PxIon.entry.energy, 25, "Wrong Energy!") self.assertEquals(self.PxIon.entry.name,\ "MnO4[-]", "Wrong Entry!") self.assertEquals(self.PxSol.entry.energy, 49, "Wrong Energy!") self.assertEquals(self.PxSol.entry.name,\ "Mn2O3", "Wrong Entry!") self.assertEquals(self.PxIon.g0, 25, "Wrong Energy!") self.assertEquals(self.PxSol.g0, 49, "Wrong Energy!") self.assertEquals(self.PxIon.conc, 1e-4, "Wrong concentration!") def test_calc_coeff_terms(self): self.assertEquals(self.PxIon.npH, -8, "Wrong npH!") self.assertEquals(self.PxIon.nPhi, -7, "Wrong nPhi!") self.assertEquals(self.PxIon.nH2O, 4, "Wrong nH2O!") self.assertEquals(self.PxSol.npH, -6, "Wrong npH!") self.assertEquals(self.PxSol.nPhi, -6, "Wrong nPhi!") self.assertEquals(self.PxSol.nH2O, 3, "Wrong nH2O!") def test_to_from_dict(self): d = self.PxIon.to_dict ion_entry = self.PxIon.from_dict(d) self.assertEquals(ion_entry.entry.name, "MnO4[-]", "Wrong Entry!")
def form_e(stable_solids_minus_h2o, entries_aqcorr, gas_gibbs=True): """ Calculate the formation energy for the entries in stable_solids_minus_h2o Reduce by stoicheometric factor if applicable (ex. Fe4O4) Args: stable_solids_minus_h2o: list of stable solids without O2, H2O, H2, and H2O2 (from stable_entr) entries_aqcorr: entries list before being modified by stable_entr """ #| - form_e #| - Imported Modules from pymatgen.analysis.pourbaix.entry import PourbaixEntry from pymatgen.phasediagram.maker import PhaseDiagram from entry_methods import base_atom from energy_scheme import ref_atoms_dict #__| ref_atom_energies = ref_atoms_dict() e_o = ref_atom_energies['e_o'] e_h = ref_atom_energies['e_h'] pd = PhaseDiagram(entries_aqcorr) base_at = base_atom(stable_solids_minus_h2o) d = {} for i in stable_solids_minus_h2o: if i.name in base_at: d[i.name] = i.energy_per_atom def form_energy(entry, solid_ref_energy_dict, gas_gibbs=True): """ Calculates the formation energy of an entry relative to the VASP energies of the reference atoms. Gibbs/entropy corrections for the gas phase are optionable Args: entry: Entry whose formation energy will be calculated solid_ref_energy_dict: Dictionary of reference atom energies gas_gibbs: Whether the gas phase reference atoms will have an entropic correction """ if gas_gibbs==True: ref_dict = {} ref_dict['O'] = e_o-0.3167 ref_dict['H'] = e_h-0.36218 elif gas_gibbs==False: ref_dict = {} ref_dict['O'] = e_o ref_dict['H'] = e_h z = ref_dict.copy() z.update(solid_ref_energy_dict) ref_dict = z elem_dict = entry.composition.get_el_amt_dict() entry_e = entry.energy for elem in entry.composition.elements: elem_num = elem_dict[elem.symbol] entry_e = entry_e - elem_num*ref_dict[elem.symbol] return entry_e pbx_solid_entries = [] for entry in stable_solids_minus_h2o: pbx_entry = PourbaixEntry(entry) pbx_entry.g0_replace(form_energy(entry, d, gas_gibbs=gas_gibbs)) #Replace E with form E relative to ref elements # pbx_entry.g0_replace(pd.get_form_energy(entry)) #Replace E with form E relative to ref elements pbx_entry.reduced_entry() #Reduces parameters by stoich factor (ex. Li2O2 -> LiO) pbx_solid_entries.append(pbx_entry) return pbx_solid_entries
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()
Created on Wed Feb 28 15:40:02 2018 @author: jherfson """ from pymatgen.analysis.pourbaix.entry import PourbaixEntry, IonEntry #, MultiEntry from pymatgen.analysis.pourbaix.entry import PourbaixEntryIO from pymatgen.analysis.phase_diagram import PDEntry from pymatgen.core.ion import Ion from pymatgen.core.structure import Composition 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)
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()
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()