Exemplo n.º 1
0
def is_solid_phase(mat1, mat2, mat1_co=0.5):
    """Returns TRUE is there exists a all solid phase in the binary Pourbaix
    Diagram this means that the phase doesn't have any aqueous species.
    """
    mat2_co = 1 - mat1_co

    pd_b = pd_entries(mat1, mat2)

    pd = PourbaixDiagram(pd_b, {mat1: mat1_co, mat2: mat2_co})
    pl = PourbaixPlotter(pd)
    ppd = pl.pourbaix_plot_data([[-2, 16], [-3, 3]])

    pd_lst = []
    cnt = 0
    for stable_entry in ppd[0]:
        pd_lst.append([])
        pd_lst[cnt].append(ppd[0][stable_entry])
        pd_lst[cnt].append(stable_entry.entrylist)
        cnt = cnt + 1

    solidphase = False
    for i in pd_lst:
        if len(i[1]) == 1:
            if i[1][0].phase_type == 'Solid':
                solidphase = True
        if len(i[1]) == 2:
            if i[1][0].phase_type and i[1][1].phase_type == 'Solid':
                solidphase = True

    return solidphase
Exemplo n.º 2
0
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()
Exemplo n.º 3
0
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]])
Exemplo n.º 4
0
    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()
Exemplo n.º 5
0
    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]
        binary_plotter.plot_entry_stability(test_entry)
Exemplo n.º 6
0
def phase_coord(entries, atom_comp, prim_elem=False):
    """
	Produces a list of line segments corresponding to each phase area in a PD
	along with the PD entries corresponding to each area.
	The produced list is of the following form:
		list = [[[coordinate data], [pourbaix entry]], [], [] .... ]

	Args:
		entries: List of entries in a PD
		atom_comp: Composition of atom if system is binary, given as a fraction
			between 0 and 1. Corresponds to the element with lowest atomic number if
			prim_elem is left to its default
		prim_elem: Primary element to which the atom_comp is assigned
	"""
    # | -  - phase_coord
    from pymatgen.analysis.pourbaix.maker import PourbaixDiagram
    from pymatgen.analysis.pourbaix.plotter import PourbaixPlotter
    from entry_methods import base_atom

    base_atoms = base_atom(entries)
    mat0 = base_atoms[0]
    if len(base_atoms) == 2: mat1 = base_atoms[1]
    else: mat1 = mat0

    # If the primary element is declared then set the given composition to it
    if not prim_elem == False:
        for atom in base_atoms:
            if atom == prim_elem:
                mat0 = atom
            else:
                mat1 = atom

    pd = PourbaixDiagram(entries, {mat0: atom_comp, mat1: 1 - atom_comp})
    pl = PourbaixPlotter(pd)
    ppd = pl.pourbaix_plot_data([[-2, 16], [-3,
                                            3]])  #ppd == Pourbaix_Plot Data
    pd_lst = []
    cnt = 0

    for stable_entry in ppd[0]:
        pd_lst.append([])
        pd_lst[cnt].append(ppd[0][stable_entry])
        pd_lst[cnt].append(stable_entry.entrylist)
        cnt = cnt + 1

    return pd_lst
Exemplo n.º 7
0
def phase_coord(entries, atom_comp, prim_elem=None):
    """Produces a list of line segments corresponding to each phase area in a PD
    along with the PD entries corresponding to each area.
    The produced list is of the following form:
        list = [[[coordinate data], [pourbaix entry]], [], [] .... ]

    Parameters:
    -----------
    entries: list
        entries in a PD
    atom_comp: float
        Composition of atom if system is binary, given as a fraction
        between 0 and 1. Corresponds to the element with lowest atomic
        number if prim_elem is left to its default
    prim_elem: str
        Primary element to which the atom_comp is assigned
    """
    base_atoms = base_atom(entries)
    mat0 = base_atoms[0]
    if len(base_atoms) == 2:
        mat1 = base_atoms[1]
    else:
        mat1 = mat0

    if prim_elem:
        for atom in base_atoms:
            if atom == prim_elem:
                mat0 = atom
            else:
                mat1 = atom

    pd = PourbaixDiagram(entries, {mat0: atom_comp, mat1: 1 - atom_comp})
    pl = PourbaixPlotter(pd)
    ppd = pl.pourbaix_plot_data([[-2, 16], [-3, 3]])

    pd_lst = []
    for i, stable_entry in enumerate(ppd[0]):
        pd_lst += [[]]
        pd_lst[i] += [ppd[0][stable_entry]]
        if isinstance(stable_entry, PourbaixEntry):
            pd_lst[i] += [stable_entry]
        else:
            pd_lst[i] += [stable_entry.entrylist]
    return pd_lst
Exemplo n.º 8
0
def is_solid_phase(mat1, mat2, mat1_co=0.5):
    """
	Returns TRUE is there exists a all solid phase in the binary Pourbaix Diagram
	This means that the phase doesn't have any aqueous species

	Args:
		mat1:
		mat2:
		mat1_co:
	"""

    # | -  - is_solid_phase
    from pourdiag import pourdiag  # Returns Pourbaix entries for binary system
    from pymatgen.analysis.pourbaix.maker import PourbaixDiagram
    from pymatgen.analysis.pourbaix.plotter import PourbaixPlotter

    mat2_co = 1 - mat1_co

    pd_b = pourdiag(mat1, mat2)
    return pd_b

    pd = PourbaixDiagram(pd_b, {mat1: mat1_co, mat2: mat2_co})
    pl = PourbaixPlotter(pd)
    ppd = pl.pourbaix_plot_data([[-2, 16], [-3,
                                            3]])  #ppd == Pourbaix_Plot Data

    pd_lst = []
    cnt = 0
    for stable_entry in ppd[0]:
        pd_lst.append([])
        pd_lst[cnt].append(ppd[0][stable_entry])
        pd_lst[cnt].append(stable_entry.entrylist)
        cnt = cnt + 1

    solidphase = False
    for i in pd_lst:
        if len(i[1]) == 1:
            if i[1][0].phase_type == 'Solid':
                solidphase = True
        if len(i[1]) == 2:
            if i[1][0].phase_type and i[1][1].phase_type == 'Solid':
                solidphase = True
    return solidphase
Exemplo n.º 9
0
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()
Exemplo n.º 10
0
    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)
Exemplo n.º 11
0
 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)
Exemplo n.º 12
0
def oxidation_dissolution_product_0(i, j, scale):
	"""
	Creates Pourbaix Diagrams for single or binary systems
	"""
	#| -  - oxidation_dissolution_product_0
	# from pourdiag import pd_entries

	from pymatgen.analysis.pourbaix.maker import PourbaixDiagram
	from pymatgen.analysis.pourbaix.plotter import PourbaixPlotter
	from pd_screen_tools import phase_coord, phase_filter
	from stability_crit import most_stable_phase, oxidation_dissolution_product

	elem0 = i.symbol; elem1 = j.symbol

	mat_co_0 = 0.50		# Composition of 1st entry in elem_sys

	entr = pd_entries(elem0,elem1)
	pourbaix = PourbaixDiagram(entr,{elem0: mat_co_0,elem1: 1-mat_co_0})
	plotter = PourbaixPlotter(pourbaix)

	coord = phase_coord(entr,mat_co_0)
	filt1 = phase_filter(coord,'metallic')
	filt2 = phase_filter(coord,'metallic_metallic')
	filt = filt1 + filt2
	msp = most_stable_phase(filt,scale='RHE')

	tmp = oxidation_dissolution_product(coord,msp)

	if 'Ion' in tmp:
		entry_lst = 'dis'
	else:
		entry_lst = 'oxi'

	"""
	entry_lst = ''
	i_cnt = 0
	for i in tmp:
		entry_lst = str(entry_lst)+'\n'+str(i)
		if i_cnt==0:
			entry_lst = entry_lst[1:]
		i_cnt=i_cnt+1

	"""
	return entry_lst
Exemplo n.º 13
0
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()
def find_closest_point_pd(pourbaix_diagram_object):
    """ """
    lw = 1
    limits = [[-2, 16], [-3, 3]]

    plotter = PourbaixPlotter(pourbaix_diagram_object)
    (stable, unstable) = plotter.pourbaix_plot_data(limits)

    # Returns the Desirable Regions of PD in "vertices"
    vertices = []

    for entry, lines in list(stable.items()):
        print(entry.name)
        is_desired = screening_check_desirable(entry, criteria='only-solid')
        if is_desired:
            desired_entry = entry
        for line in lines:
            (x, y) = line
            plt.plot(x, y, "k-", linewidth=lw)
            point1 = [x[0], y[0]]
            point2 = [x[1], y[1]]
            if point1 not in vertices and is_desired:
                vertices.append(point1)
            if point2 not in vertices and is_desired:
                vertices.append(point2)

    # Placing the desired phase's name in the diagram
    center_x = 0
    center_y = 0
    count = 0
    for point in vertices:
        x, y = point
        count = count + 1
        center_x = center_x + x
        center_y = center_y + y
        plt.plot(x, y, 'ro')
    center_x = center_x / count
    center_y = center_y / count
    plt.annotate(str(desired_entry.name), xy=(center_x, center_y))

    # Plotting Water and Hydrogen Equilibrium Lines. Get water line
    h_line, o_line = get_water_stability_lines(limits)
    plt.plot(h_line[0], h_line[1], "r--", linewidth=lw)
    plt.plot(o_line[0], o_line[1], "r--", linewidth=lw)

    # Getting distances
    print("Getting distances of vertices")
    reference_line = o_line
    p1 = np.array([reference_line[0][0], reference_line[1][0]])
    p2 = np.array([reference_line[0][1], reference_line[1][1]])

    min_d = 1000.0
    d_and_vert_lst = []
    for p3 in vertices:
        np.array(p3)
        d = np.linalg.norm(np.cross(p2 - p1, p1 - p3)) / \
            np.linalg.norm(p2 - p1)
        d_and_vert = [d, p3]
        d_and_vert_lst.append(d_and_vert)

        # https://stackoverflow.com/questions/39840030/distance-between-point-and-a-line-from-two-points
        # http://www.fundza.com/vectors/point2line/index.html
        print("Vertex: ", p3, "Distance: ", d)

        if d <= min_d:
            min_d = d

    fin_lst = []
    for i in d_and_vert_lst:
        if round(i[0], 4) == round(min_d, 4):
            fin_lst.append(i)

    # Plotting the star on highest stability vertices
    for i in fin_lst:
        plt.plot(i[1][0], i[1][1], '*b', ms=16)

    ###########################################
    V_RHE = 1.23 - min_d

    pH_0 = fin_lst[0][1][0]
    V_SHE = V_RHE - PREFAC * pH_0
    ###########################################

    # plt.annotate('d = '+ str(round(min_d,2)),xy=(center_x, center_y-0.3))
    plt.annotate('V_crit = ' + str(round(V_RHE, 2)) +
                 ' VvsRHE', xy=(center_x, center_y - 0.3))
    plt.annotate('V_crit = ' + str(round(V_SHE, 2)) +
                 ' VvsSHE', xy=(center_x, center_y - 0.6))
    plt.xlabel("pH")
    plt.ylabel("E (V)")
    plt.show()
Exemplo n.º 15
0
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 find_closest_point_pd(pourbaix_diagram_object):

    from pymatgen.analysis.pourbaix.plotter import PourbaixPlotter

    import matplotlib.pyplot as plt

    import numpy as np

    from pymatgen.util.coord_utils import in_coord_list

    from pymatgen.analysis.pourbaix.maker import PREFAC

    lw = 1

    limits = [[-2, 16], [-3, 3]]

    fig1 = plt.figure()

    ax = fig1.gca()

    plotter = PourbaixPlotter(pourbaix_diagram_object)

    (stable, unstable) = plotter.pourbaix_plot_data(limits)

    #| -  - Ambar's Region Filter Check Function

    def screening_check_desirable(entry, criteria='only-solid'):

        is_desired = False

        if criteria not in ['only-solid']:

            print "Not implemented"

            sys.exit()

        if criteria == 'only-solid':

            if entry.nH2O == 0.0 and entry.npH == 0.0 and entry.nPhi == 0.0:

                is_desired = True

                print "Desired entry", entry.name

        if not criteria:

            print "Not desired entry", entry.name

        return is_desired

#__|

#| -  - Ambar's Function for Water and Hydrogen Lines

    def get_water_stability_lines(limits):

        from pymatgen.analysis.pourbaix.maker import PREFAC

        xlim = limits[0]

        ylim = limits[1]

        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]])

        return (h_line, o_line)

#__|

#| -  - Returns the Desirable Regions of PD in "vertices"

    vertices = []

    import time

    for entry, lines in stable.items():

        print entry.name

        is_desired = screening_check_desirable(entry, criteria='only-solid')

        if is_desired:

            desired_entry = entry

        for line in lines:

            (x, y) = line

            #print "points", x, y

            plt.plot(x, y, "k-", linewidth=lw)

            point1 = [x[0], y[0]]

            point2 = [x[1], y[1]]

            if point1 not in vertices and is_desired:

                vertices.append(point1)

            if point2 not in vertices and is_desired:

                vertices.append(point2)

#__|

#| -  - Placing the desired phase's name in the diagram

    center_x = 0

    center_y = 0

    count = 0

    for point in vertices:

        x, y = point

        count = count + 1

        center_x = center_x + x

        center_y = center_y + y

        plt.plot(x, y, 'ro')

    center_x = center_x / count

    center_y = center_y / count

    plt.annotate(str(desired_entry.name), xy=(center_x, center_y))

    #__|

    #| -  - Plotting Water and Hydrogen Equilibrium Lines

    # Get water line

    h_line, o_line = get_water_stability_lines(limits)

    plt.plot(h_line[0], h_line[1], "r--", linewidth=lw)

    plt.plot(o_line[0], o_line[1], "r--", linewidth=lw)

    #__|

    # Getting distances

    print "Getting distances of vertices"

    reference_line = o_line

    p1 = np.array([reference_line[0][0], reference_line[1][0]])

    p2 = np.array([reference_line[0][1], reference_line[1][1]])

    min_d = 1000.0

    min_vertex = []

    d_and_vert_lst = []

    for p3 in vertices:

        np.array(p3)

        d = np.linalg.norm(np.cross(p2 - p1,
                                    p1 - p3)) / np.linalg.norm(p2 - p1)

        d_and_vert = [d, p3]

        d_and_vert_lst.append(d_and_vert)

        #https://stackoverflow.com/questions/39840030/distance-between-point-and-a-line-from-two-points

        #http://www.fundza.com/vectors/point2line/index.html

        print "Vertex: ", p3, "Distance: ", d

        if d <= min_d:

            min_d = d

            min_vertex = p3

    fin_lst = []

    for i in d_and_vert_lst:

        if round(i[0], 4) == round(min_d, 4):

            fin_lst.append(i)

    # Plotting the star on highest stability vertices

    for i in fin_lst:

        plt.plot(i[1][0], i[1][1], '*b', ms=16)

    ###########################################

    V_RHE = 1.23 - min_d

    pH_0 = fin_lst[0][1][0]

    V_SHE = V_RHE - PREFAC * pH_0

    ###########################################

    # plt.annotate('d = '+ str(round(min_d,2)),xy=(center_x, center_y-0.3))

    plt.annotate('V_crit = ' + str(round(V_RHE, 2)) + ' VvsRHE',
                 xy=(center_x, center_y - 0.3))

    plt.annotate('V_crit = ' + str(round(V_SHE, 2)) + ' VvsSHE',
                 xy=(center_x, center_y - 0.6))

    plt.xlabel("pH")

    plt.ylabel("E (V)")

    plt.show()
Exemplo n.º 17
0
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()
Exemplo n.º 18
-1
    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)