예제 #1
0
def get_hull_distance(competing_phase_directory='../competing_phases'):
    """
    Calculate the material's distance to the thermodynamic hull,
    based on species in the Materials Project database.

    Args:
        competing_phase_directory (str): absolute or relative path
            to the location where your competing phases have been
            relaxed. The default expectation is that they are stored
            in a directory named 'competing_phases' at the same level
            as your material's relaxation directory.
    Returns:
        float. distance (eV/atom) between the material and the
            hull.
    """

    finished_competitors = {}
    original_directory = os.getcwd()
    # Determine which competing phases have been relaxed in the current
    # framework and store them in a dictionary ({formula: entry}).
    if os.path.isdir(competing_phase_directory):
        os.chdir(competing_phase_directory)
        for comp_dir in [
            dir for dir in os.listdir(os.getcwd()) if os.path.isdir(dir) and
            is_converged(dir)
                ]:
            vasprun = Vasprun('{}/vasprun.xml'.format(comp_dir))
            composition = vasprun.final_structure.composition
            energy = vasprun.final_energy
            finished_competitors[comp_dir] = ComputedEntry(composition, energy)
        os.chdir(original_directory)
    else:
        raise ValueError('Competing phase directory does not exist.')

    composition = Structure.from_file('POSCAR').composition
    try:
        energy = Vasprun('vasprun.xml').final_energy
    except:
        raise ValueError('This directory does not have a converged vasprun.xml')
    my_entry = ComputedEntry(composition, energy)  # 2D material
    entries = MPR.get_entries_in_chemsys(
        [elt.symbol for elt in composition]
        )

    # If the energies of competing phases have been calculated in
    # the current framework, put them in the phase diagram instead
    # of the MP energies.
    for i in range(len(entries)):
        formula = entries[i].composition.reduced_formula
        if formula in finished_competitors:
            entries[i] = finished_competitors[formula]
        else:
            entries[i] = ComputedEntry(entries[i].composition, 100)

    entries.append(my_entry)  # 2D material

    pda = PDAnalyzer(PhaseDiagram(entries))
    decomp = pda.get_decomp_and_e_above_hull(my_entry, allow_negative=True)

    return decomp[1]
예제 #2
0
def get_competing_phases_new(structure):
    """
    Collect the species to which the 2D materials might decompose to.
    Since a lot of 2D materials with similar compositions will have the
    same competing phases, duplicates aren't counted.
    """

    total_competing_phases = []
    composition = structure.composition
    energy = 100
    my_entry = ComputedEntry(composition, energy)  # 2D material
    entries = MPR.get_entries_in_chemsys(
        elements=[elt.symbol for elt in composition])

    entries.append(my_entry)  # 2D material

    pda = PDAnalyzer(PhaseDiagram(entries))
    decomp = pda.get_decomp_and_e_above_hull(my_entry, allow_negative=True)
    competing_phases = [(entry.composition.reduced_formula, entry.entry_id)
                        for entry in decomp[0]]

    # Keep a running list of all unique competing phases, since in
    # high throughput 2D searches there is usually some overlap in
    # competing phases for different materials.
    for specie in competing_phases:
        if specie not in total_competing_phases:
            total_competing_phases.append(specie)

    return total_competing_phases
예제 #3
0
def get_competing_phases():
    """
    Collect the species to which the material might decompose to.

    Returns:
        A list of phases as tuples formatted as
        [(formula_1, Materials_Project_ID_1),
        (formula_2, Materials_Project_ID_2), ...]
    """

    composition = Structure.from_file('POSCAR').composition
    try:
        energy = Vasprun('vasprun.xml').final_energy
    except:
        energy = 100  # The function can work without a vasprun.xml
    entries = MPR.get_entries_in_chemsys([elt.symbol for elt in composition])
    my_entry = ComputedEntry(composition, energy)
    entries.append(my_entry)

    pda = PDAnalyzer(PhaseDiagram(entries))
    decomp = pda.get_decomp_and_e_above_hull(my_entry, allow_negative=True)
    competing_phases = [(entry.composition.reduced_formula, entry.entry_id)
                        for entry in decomp[0]]

    return competing_phases
예제 #4
0
 def test_1d_pd(self):
     entry = PDEntry('H', 0)
     pd = PhaseDiagram([entry])
     pda = PDAnalyzer(pd)
     decomp, e = pda.get_decomp_and_e_above_hull(PDEntry('H', 1))
     self.assertAlmostEqual(e, 1)
     self.assertAlmostEqual(decomp[entry], 1.0)
예제 #5
0
 def test_get_stability(self):
     entries = self.rester.get_entries_in_chemsys(["Fe", "O"])
     modified_entries = []
     for entry in entries:
         # Create modified entries with energies that are 0.01eV higher
         # than the corresponding entries.
         if entry.composition.reduced_formula == "Fe2O3":
             modified_entries.append(
                 ComputedEntry(entry.composition,
                               entry.uncorrected_energy + 0.01,
                               parameters=entry.parameters,
                               entry_id="mod_{}".format(entry.entry_id)))
     rest_ehulls = self.rester.get_stability(modified_entries)
     all_entries = entries + modified_entries
     compat = MaterialsProjectCompatibility()
     all_entries = compat.process_entries(all_entries)
     pd = PhaseDiagram(all_entries)
     a = PDAnalyzer(pd)
     for e in all_entries:
         if str(e.entry_id).startswith("mod"):
             for d in rest_ehulls:
                 if d["entry_id"] == e.entry_id:
                     data = d
                     break
             self.assertAlmostEqual(a.get_e_above_hull(e),
                                    data["e_above_hull"])
예제 #6
0
 def test_get_stability(self):
     entries = self.rester.get_entries_in_chemsys(["Fe", "O"])
     modified_entries = []
     for entry in entries:
         # Create modified entries with energies that are 0.01eV higher
         # than the corresponding entries.
         if entry.composition.reduced_formula == "Fe2O3":
             modified_entries.append(
                 ComputedEntry(entry.composition,
                               entry.uncorrected_energy + 0.01,
                               parameters=entry.parameters,
                               entry_id="mod_{}".format(entry.entry_id)))
     rest_ehulls = self.rester.get_stability(modified_entries)
     all_entries = entries + modified_entries
     compat = MaterialsProjectCompatibility()
     all_entries = compat.process_entries(all_entries)
     pd = PhaseDiagram(all_entries)
     a = PDAnalyzer(pd)
     for e in all_entries:
         if str(e.entry_id).startswith("mod"):
             for d in rest_ehulls:
                 if d["entry_id"] == e.entry_id:
                     data = d
                     break
             self.assertAlmostEqual(a.get_e_above_hull(e),
                                    data["e_above_hull"])
예제 #7
0
 def test_1d_pd(self):
     entry = PDEntry('H', 0)
     pd = PhaseDiagram([entry])
     pda = PDAnalyzer(pd)
     decomp, e = pda.get_decomp_and_e_above_hull(PDEntry('H', 1))
     self.assertAlmostEqual(e, 1)
     self.assertAlmostEqual(decomp[entry], 1.0)
예제 #8
0
def get_competing_phases():
    """
    Collect the species to which the material might decompose to.

    Returns:
        A list of phases as tuples formatted as
        [(formula_1, Materials_Project_ID_1),
        (formula_2, Materials_Project_ID_2), ...]
    """

    total_competing_phases = []

    composition = Structure.from_file('POSCAR').composition
    try:
        energy = Vasprun('vasprun.xml').final_energy
    except:
        energy = 100  # The function can work without a vasprun.xml
    entries = MPR.get_entries_in_chemsys(
        [elt.symbol for elt in composition]
        )
    my_entry = ComputedEntry(composition, energy)
    entries.append(my_entry)

    pda = PDAnalyzer(PhaseDiagram(entries))
    decomp = pda.get_decomp_and_e_above_hull(my_entry, allow_negative=True)
    competing_phases = [
        (entry.composition.reduced_formula,
         entry.entry_id) for entry in decomp[0]
        ]

    return competing_phases
예제 #9
0
def get_hull_distance(competing_phase_directory='../competing_phases'):
    """
    Calculate the material's distance to the thermodynamic hull,
    based on species in the Materials Project database.

    Args:
        competing_phase_directory (str): absolute or relative path
            to the location where your competing phases have been
            relaxed. The default expectation is that they are stored
            in a directory named 'competing_phases' at the same level
            as your material's relaxation directory.
    Returns:
        float. distance (eV/atom) between the material and the
            hull.
    """

    finished_competitors = {}
    original_directory = os.getcwd()
    # Determine which competing phases have been relaxed in the current
    # framework and store them in a dictionary ({formula: entry}).
    if os.path.isdir(competing_phase_directory):
        os.chdir(competing_phase_directory)
        for comp_dir in [
                dir for dir in os.listdir(os.getcwd())
                if os.path.isdir(dir) and is_converged(dir)
        ]:
            vasprun = Vasprun('{}/vasprun.xml'.format(comp_dir))
            composition = vasprun.final_structure.composition
            energy = vasprun.final_energy
            finished_competitors[comp_dir] = ComputedEntry(composition, energy)
        os.chdir(original_directory)
    else:
        raise ValueError('Competing phase directory does not exist.')

    composition = Structure.from_file('POSCAR').composition
    try:
        energy = Vasprun('vasprun.xml').final_energy
    except:
        raise ValueError(
            'This directory does not have a converged vasprun.xml')
    my_entry = ComputedEntry(composition, energy)  # 2D material
    entries = MPR.get_entries_in_chemsys([elt.symbol for elt in composition])

    # If the energies of competing phases have been calculated in
    # the current framework, put them in the phase diagram instead
    # of the MP energies.
    for i in range(len(entries)):
        formula = entries[i].composition.reduced_formula
        if formula in finished_competitors:
            entries[i] = finished_competitors[formula]
        else:
            entries[i] = ComputedEntry(entries[i].composition, 100)

    entries.append(my_entry)  # 2D material

    pda = PDAnalyzer(PhaseDiagram(entries))
    decomp = pda.get_decomp_and_e_above_hull(my_entry, allow_negative=True)

    return decomp[1]
예제 #10
0
    def test_get_critical_compositions(self):
        c1 = Composition('Fe2O3')
        c2 = Composition('Li3FeO4')
        c3 = Composition('Li2O')

        comps = self.analyzer.get_critical_compositions(c1, c2)
        expected = [
            Composition('Fe2O3'),
            Composition('Li0.3243244Fe0.1621621O0.51351349') * 7.4,
            Composition('Li3FeO4')
        ]
        for crit, exp in zip(comps, expected):
            self.assertTrue(crit.almost_equals(exp, rtol=0, atol=1e-5))

        comps = self.analyzer.get_critical_compositions(c1, c3)
        expected = [
            Composition('Fe2O3'),
            Composition('LiFeO2'),
            Composition('Li5FeO4') / 3,
            Composition('Li2O')
        ]
        for crit, exp in zip(comps, expected):
            self.assertTrue(crit.almost_equals(exp, rtol=0, atol=1e-5))

        # Don't fail silently if input compositions aren't in phase diagram
        # Can be very confusing if you're working with a GrandPotentialPD
        self.assertRaises(ValueError, self.analyzer.get_critical_compositions,
                          Composition('Xe'), Composition('Mn'))

        # For the moment, should also fail even if compositions are in the gppd
        # because it isn't handled properly
        gppd = GrandPotentialPhaseDiagram(self.pd.all_entries, {'Xe': 1},
                                          self.pd.elements + [Element('Xe')])
        pda = PDAnalyzer(gppd)
        self.assertRaises(ValueError, pda.get_critical_compositions,
                          Composition('Fe2O3'), Composition('Li3FeO4Xe'))

        # check that the function still works though
        comps = pda.get_critical_compositions(c1, c2)
        expected = [
            Composition('Fe2O3'),
            Composition('Li0.3243244Fe0.1621621O0.51351349') * 7.4,
            Composition('Li3FeO4')
        ]
        for crit, exp in zip(comps, expected):
            self.assertTrue(crit.almost_equals(exp, rtol=0, atol=1e-5))

        # case where the endpoints are identical
        self.assertEqual(self.analyzer.get_critical_compositions(c1, c1 * 2),
                         [c1, c1 * 2])
예제 #11
0
    def get_e_above_hull(self):
        """ 
        Get e_above_hull for this compound

        Args: 
            allow_negative: whether to calculate negative energy above hull for stable compound

        Returns:
            decomposition, energy above hull
        """
        pda = PDAnalyzer(self.pd)
        (decomp, hull) = pda.get_decomp_and_e_above_hull(self.entry)
        decomp = [compound.composition.reduced_formula for compound in decomp]
        return (decomp, hull)
예제 #12
0
def get_deco(e, pda):
    """get_deco
    Gets the non-stoichiometric decomposition products of e.
    :param e: Entry we want the decomposition of.
    :param pda: PDAnalzyer containing e.
    """
    e_c = e.composition.reduced_composition
    shifted_entries = [copy.deepcopy(e) for e in pda._pd.all_entries]
    for o_e in shifted_entries:
        if o_e.composition.reduced_composition == e_c:
            o_e.uncorrected_energy += 10.0
    new_pd = PhaseDiagram(shifted_entries)
    new_pda = PDAnalyzer(new_pd)
    decomp = new_pda.get_decomposition(e_c)
    return decomp
예제 #13
0
    def test_dim1(self):
        #Ensure that dim 1 PDs can eb generated.
        for el in ["Li", "Fe", "O2"]:
            entries = [e for e in self.entries
                       if e.composition.reduced_formula == el]
            pd = PhaseDiagram(entries)
            self.assertEqual(len(pd.stable_entries), 1)

            a = PDAnalyzer(pd)
            for e in entries:
                decomp, ehull = a.get_decomp_and_e_above_hull(e)
                self.assertGreaterEqual(ehull, 0)
            plotter = PDPlotter(pd)
            lines, stable_entries, unstable_entries = plotter.pd_plot_data
            self.assertEqual(lines[0][1], [0, 0])
예제 #14
0
    def from_composition_and_pd(comp, pd, working_ion_symbol="Li"):
        """
        Convenience constructor to make a ConversionElectrode from a
        composition and a phase diagram.

        Args:
            comp:
                Starting composition for ConversionElectrode, e.g.,
                Composition("FeF3")
            pd:
                A PhaseDiagram of the relevant system (e.g., Li-Fe-F)
            working_ion_symbol:
                Element symbol of working ion. Defaults to Li.
        """
        working_ion = Element(working_ion_symbol)
        entry = None
        working_ion_entry = None
        for e in pd.stable_entries:
            if e.composition.reduced_formula == comp.reduced_formula:
                entry = e
            elif e.is_element and \
                    e.composition.reduced_formula == working_ion_symbol:
                working_ion_entry = e

        if not entry:
            raise ValueError(
                "Not stable compound found at composition {}.".format(comp))

        analyzer = PDAnalyzer(pd)

        profile = analyzer.get_element_profile(working_ion, comp)
        # Need to reverse because voltage goes form most charged to most
        # discharged.
        profile.reverse()
        if len(profile) < 2:
            return None
        working_ion_entry = working_ion_entry
        working_ion = working_ion_entry.composition.elements[0].symbol
        normalization_els = {}
        for el, amt in comp.items():
            if el != Element(working_ion):
                normalization_els[el] = amt
        vpairs = [
            ConversionVoltagePair.from_steps(profile[i], profile[i + 1],
                                             normalization_els)
            for i in range(len(profile) - 1)
        ]
        return ConversionElectrode(vpairs, working_ion_entry, comp)
예제 #15
0
    def test_dim1(self):
        #Ensure that dim 1 PDs can eb generated.
        for el in ["Li", "Fe", "O2"]:
            entries = [
                e for e in self.entries if e.composition.reduced_formula == el
            ]
            pd = PhaseDiagram(entries)
            self.assertEqual(len(pd.stable_entries), 1)

            a = PDAnalyzer(pd)
            for e in entries:
                decomp, ehull = a.get_decomp_and_e_above_hull(e)
                self.assertGreaterEqual(ehull, 0)
            plotter = PDPlotter(pd)
            lines, stable_entries, unstable_entries = plotter.pd_plot_data
            self.assertEqual(lines[0][1], [0, 0])
예제 #16
0
    def from_composition_and_pd(comp, pd, working_ion_symbol="Li"):
        """
        Convenience constructor to make a ConversionElectrode from a
        composition and a phase diagram.

        Args:
            comp:
                Starting composition for ConversionElectrode, e.g.,
                Composition("FeF3")
            pd:
                A PhaseDiagram of the relevant system (e.g., Li-Fe-F)
            working_ion_symbol:
                Element symbol of working ion. Defaults to Li.
        """
        working_ion = Element(working_ion_symbol)
        entry = None
        working_ion_entry = None
        for e in pd.stable_entries:
            if e.composition.reduced_formula == comp.reduced_formula:
                entry = e
            elif e.is_element and \
                    e.composition.reduced_formula == working_ion_symbol:
                working_ion_entry = e

        if not entry:
            raise ValueError("Not stable compound found at composition {}."
                             .format(comp))

        analyzer = PDAnalyzer(pd)

        profile = analyzer.get_element_profile(working_ion, comp)
        # Need to reverse because voltage goes form most charged to most
        # discharged.
        profile.reverse()
        if len(profile) < 2:
            return None
        working_ion_entry = working_ion_entry
        working_ion = working_ion_entry.composition.elements[0].symbol
        normalization_els = {}
        for el, amt in comp.items():
            if el != Element(working_ion):
                normalization_els[el] = amt
        vpairs = [ConversionVoltagePair.from_steps(profile[i], profile[i + 1],
                                                   normalization_els)
                  for i in range(len(profile) - 1)]
        return ConversionElectrode(vpairs, working_ion_entry, comp)
예제 #17
0
def get_mu_range(mpid, ext_elts=[]):
    if 'hse' in mpid:
        mpid = mpid.split('_')[0]
    try:
        entry = m.get_entry_by_material_id(mpid)
	in_MP = True
    except:
	from high_throughput.defects.database import TasksOperater
	in_MP = False
	TO = TasksOperater()
	id_ = TO.groups['bulk'][mpid][0]
	rec = TO.collection.find_one({'_id':id_},['output'])
	stru_tmp =Structure.from_dict(rec['output']['crystal'])
	energy = rec['output']['final_energy']
	entry = PDEntry(stru_tmp.composition, energy)
    elts = [i.symbol for i in entry.composition.elements]
    for i in ext_elts:
        elts.append(i)
    entries = m.get_entries_in_chemsys(elts)
    if not in_MP:
	entries.append(entry)
    for entry in entries:
        entry.correction = 0.0
    pd=PhaseDiagram(entries)
    pda=PDAnalyzer(pd)
    chempots={}
    decompositions=pda.get_decomposition(entry.composition).keys()
    decomposition_inds=[pd.qhull_entries.index(entry) for entry in decompositions]
    facets_around=[]
    for facet in pd.facets:
        is_facet_around=True
        for ind in decomposition_inds:
            if ind not in facet:
                is_facet_around=False
        if is_facet_around==True:
            facets_around.append(facet)
    for facet in facets_around:
        s=[]
        for ind in facet:
            s.append(str(pd.qhull_entries[ind].name))
        s.sort()
        chempots['-'.join(s)]=pda.get_facet_chempots(facet)
    chempots={i:{j.symbol:chempots[i][j] for j in chempots[i]} for i in chempots}
    return chempots
예제 #18
0
    def test_get_critical_compositions(self):
        c1 = Composition('Fe2O3')
        c2 = Composition('Li3FeO4')
        c3 = Composition('Li2O')

        comps = self.analyzer.get_critical_compositions(c1, c2)
        expected = [Composition('Fe2O3'),
                    Composition('Li0.3243244Fe0.1621621O0.51351349') * 7.4,
                    Composition('Li3FeO4')]
        for crit, exp in zip(comps, expected):
            self.assertTrue(crit.almost_equals(exp, rtol=0, atol=1e-5))

        comps = self.analyzer.get_critical_compositions(c1, c3)
        expected = [Composition('Fe2O3'),
                    Composition('LiFeO2'),
                    Composition('Li5FeO4') / 3,
                    Composition('Li2O')]
        for crit, exp in zip(comps, expected):
            self.assertTrue(crit.almost_equals(exp, rtol=0, atol=1e-5))

        # Don't fail silently if input compositions aren't in phase diagram
        # Can be very confusing if you're working with a GrandPotentialPD
        self.assertRaises(ValueError, self.analyzer.get_critical_compositions,
                          Composition('Xe'), Composition('Mn'))

        # For the moment, should also fail even if compositions are in the gppd
        # because it isn't handled properly
        gppd = GrandPotentialPhaseDiagram(self.pd.all_entries, {'Xe': 1},
                                          self.pd.elements + [Element('Xe')])
        pda = PDAnalyzer(gppd)
        self.assertRaises(ValueError, pda.get_critical_compositions,
                          Composition('Fe2O3'), Composition('Li3FeO4Xe'))

        # check that the function still works though
        comps = pda.get_critical_compositions(c1, c2)
        expected = [Composition('Fe2O3'),
                    Composition('Li0.3243244Fe0.1621621O0.51351349') * 7.4,
                    Composition('Li3FeO4')]
        for crit, exp in zip(comps, expected):
            self.assertTrue(crit.almost_equals(exp, rtol=0, atol=1e-5))

        # case where the endpoints are identical
        self.assertEqual(self.analyzer.get_critical_compositions(c1, c1 * 2),
                         [c1, c1 * 2])
예제 #19
0
def main():
    """
    Main function.
    """
    # Read the calculation results into a ComputedEntry.
    entl = VaspToComputedEntryDrone().assimilate(sys.argv[1])

    # Check if our local calculation is compatible with MP.
    if MaterialsProjectCompatibility().process_entry(entl) is None:
        print("Calculation not compatible with MP.")
        sys.exit(0)

    # Get other entries sharing a chemical system with the results.
    chemsys = [ele for ele in entl.composition.as_dict()]
    with MPRester() as mpr:
        chemsys_entries = mpr.get_entries_in_chemsys(chemsys)

    # Append our local calculation to the list of entries.
    chemsys_entries.append(entl)

    # Process the entries.
    p_e = MaterialsProjectCompatibility().process_entries(chemsys_entries)

    # Build a phase diagram and an analyzer for it.
    p_d = PhaseDiagram(p_e)
    pda = PDAnalyzer(p_d)

    # Scan stable entries for our calculation.
    for ent in p_d.stable_entries:
        if ent.entry_id is None:
            print(str(ent.composition.reduced_formula) + " 0.0")
            sys.exit(0)

    # Scan unstable entries for our calculation and print decomposition.
    for ent in p_d.unstable_entries:
        if ent.entry_id is None:
            dco, ehull = pda.get_decomp_and_e_above_hull(ent)
            pretty_dc = [("{}:{}".format(k.composition.reduced_formula,
                                         k.entry_id), round(v, 2))
                         for k, v in dco.items()]
            print(
                str(ent.composition.reduced_formula) + " %.3f" % ehull + " " +
                str(pretty_dc))
예제 #20
0
    def process_item(self, item):
        """
        Process the list of entries into a phase diagram

        Args:
            item (set(entry)): a list of entries to process into a phase diagram
            
        Returns:
            [dict]: a list of thermo dictionaries to update thermo with
        """
        entries = self.__compat.process_entries(item)
        try:
            pd = PhaseDiagram(entries)
            analyzer = PDAnalyzer(pd)

            docs = []

            for e in entries:
                (decomp, ehull) = \
                    analyzer.get_decomp_and_e_above_hull(e)

                d = {"material_id": e.entry_id}
                d["thermo"] = {}
                d["thermo"][
                    "formation_energy_per_atom"] = pd.get_form_energy_per_atom(
                        e)
                d["thermo"]["e_above_hull"] = ehull
                d["thermo"]["is_stable"] = e in stable_entries
                d["thermo"][
                    "eq_reaction_e"] = analyzer.get_equilibrium_reaction_energy(
                        e)
                d["thermo"]["decomposes_to"] = [{
                    "material_id": de.entry_id,
                    "formula": de.composition.formula,
                    "amount": amt
                } for de, amt in decomp.items()]
                docs.append(d)
        except PhaseDiagramError as p:
            self.__logger.warning("Phase diagram error: {}".format(p))
            return []

        return docs
예제 #21
0
def get_deco_old(e, pda):
    """get_deco
    Gets the non-stoichiometric decomposition products of e.
    :param e: Entry we want the decomposition of.
    :param pda: PDAnalzyer containing e.
    """
    # If the entry is stable, we need to lift it off the hull.
    e_c = e.composition.reduced_composition
    if "e_above_hull" in e.data and e.data["e_above_hull"] < 0.008:
        shifted_entries = [copy.deepcopy(e) for e in pda._pd.all_entries]
        for o_e in shifted_entries:
            if o_e.composition.reduced_composition == e_c:
                o_e.uncorrected_energy += 10.0
        new_pd = PhaseDiagram(shifted_entries)
        new_pda = PDAnalyzer(new_pd)
        decomp = new_pda.get_decomposition(e_c)
    elif pda.get_decomp_and_e_above_hull(e)[1] < 0.008:
        shifted_entries = [copy.deepcopy(e) for e in pda._pd.all_entries]
        for o_e in shifted_entries:
            if o_e.composition.reduced_composition == e_c:
                o_e.uncorrected_energy += 10.0
        new_pd = PhaseDiagram(shifted_entries)
        new_pda = PDAnalyzer(new_pd)
        decomp = new_pda.get_decomposition(e_c)
    else:
        decomp = pda.get_decomposition(e_c)
    return decomp
예제 #22
0
    def get_equilibrium_reaction_energy(self):
        """
        Adapted from pymatgen. Only work if entry is stable(hull = 0)

        Provides the reaction energy of a stable entry from the neighboring
        equilibrium stable entries (also known as the inverse distance to
        hull).

        Returns:
            Equilibrium reaction energy of entry. Stable entries should have
            equilibrium reaction energy <= 0.
        """
        if self.entry not in self.pd.stable_entries:
            raise ValueError("Equilibrium reaction energy is available only "
                             "for stable entries.")
        entries = [e for e in self.pd.stable_entries if e != self.entry
                   ]  #all stable entries without this stable entry
        modpd = PhaseDiagram(entries, self.pd.elements)
        analyzer = PDAnalyzer(modpd)
        (decomp,
         hull) = analyzer.get_decomp_and_e_above_hull(self.entry,
                                                      allow_negative=True)
        decomp = [compound.composition.reduced_formula for compound in decomp]
        return (decomp, hull)
예제 #23
0
    def get_contour_pd_plot(self):
        """
        Plot a contour phase diagram plot, where phase triangles are colored
        according to degree of instability by interpolation. Currently only
        works for 3-component phase diagrams.

        Returns:
            A matplotlib plot object.
        """
        from scipy import interpolate
        from matplotlib import cm

        pd = self._pd
        entries = pd.qhull_entries
        data = np.array(pd.qhull_data)

        plt = self._get_2d_plot()
        analyzer = PDAnalyzer(pd)
        data[:, 0:2] = triangular_coord(data[:, 0:2]).transpose()
        for i, e in enumerate(entries):
            data[i, 2] = analyzer.get_e_above_hull(e)

        gridsize = 0.005
        xnew = np.arange(0, 1., gridsize)
        ynew = np.arange(0, 1, gridsize)

        f = interpolate.LinearNDInterpolator(data[:, 0:2], data[:, 2])
        znew = np.zeros((len(ynew), len(xnew)))
        for (i, xval) in enumerate(xnew):
            for (j, yval) in enumerate(ynew):
                znew[j, i] = f(xval, yval)

        plt.contourf(xnew, ynew, znew, 1000, cmap=cm.autumn_r)

        plt.colorbar()
        return plt
예제 #24
0
파일: cost.py 프로젝트: bhourahine/CyGutz
    def get_lowest_decomposition(self, composition):
        """
        Get the decomposition leading to lowest cost

        Args:
            composition:
                Composition as a pymatgen.core.structure.Composition
        Returns:
            Decomposition as a dict of {Entry: amount}
        """

        entries_list = []
        elements = [e.symbol for e in composition.elements]
        for i in range(len(elements)):
            for combi in itertools.combinations(elements, i + 1):
                chemsys = [Element(e) for e in combi]
                x = self.costdb.get_entries(chemsys)
                entries_list.extend(x)
        try:
            pd = PhaseDiagram(entries_list)
            return PDAnalyzer(pd).get_decomposition(composition)
        except IndexError:
            raise ValueError("Error during PD building; most likely, "
                             "cost data does not exist!")
예제 #25
0
    outfile = open('./results.csv', 'w')
    outfile.write(
        "full_compound,working_ion,transition_metal,anion,v_int_max,"
        "v_conv_max,max_rxn,max_species,v_int_min,v_conv_min,min_rxn,"
        "min_species,v_int_gs,v_conv_gs,gs_rxn,gs_species,v_int_topo,"
        "v_conv_topo,v_conv_rxn,v_conv_species\n")

    calclist = get_subdir(root_dir)
    for ion in working_ions:
        for tm in tms:
            for anion in anions:
                interc_formula = ion + tm + str(n_tm) + anion + str(n_anion)
                print("About to run", interc_formula)
                empty_formula = tm + str(n_tm) + anion + str(n_anion)
                pd_full = build_complete_pd(interc_formula, calclist)
                pda_full = PDAnalyzer(pd_full)
                structs_interc = get_polymorphs(interc_formula, pda_full)
                structs_empty = get_polymorphs(empty_formula, pda_full)
                voltage_dict = get_voltages(structs_interc, structs_empty,
                                            pda_full)
                outfile.write(str(interc_formula) + "," + str(ion) + "," + str(tm)\
                        + "," + str(anion) + "," +
                        str(voltage_dict["max_intercalation"]) + "," +
                        str(voltage_dict["max_conversion"]) + "," +
                        str(voltage_dict["max_rxn"]) + "," +
                        str(voltage_dict["max_dict"]) + "," +
                        str(voltage_dict["min_intercalation"]) + "," +
                        str(voltage_dict["min_conversion"]) + "," +
                        str(voltage_dict["min_rxn"]) + "," +
                        str(voltage_dict["min_dict"]) + "," +
                        str(voltage_dict["ground_state_intercalation"]) + "," +
예제 #26
0
class PDAnalyzerTest(unittest.TestCase):

    def setUp(self):
        module_dir = os.path.dirname(os.path.abspath(__file__))
        (elements, entries) = PDEntryIO.from_csv(os.path.join(module_dir,
                                                              "pdentries_test.csv"))
        self.pd = PhaseDiagram(entries)
        self.analyzer = PDAnalyzer(self.pd)

    def test_get_e_above_hull(self):
        for entry in self.pd.stable_entries:
            self.assertLess(self.analyzer.get_e_above_hull(entry), 1e-11,
                            "Stable entries should have e above hull of zero!")
        for entry in self.pd.all_entries:
            if entry not in self.pd.stable_entries:
                e_ah = self.analyzer.get_e_above_hull(entry)
                self.assertGreaterEqual(e_ah, 0)
                self.assertTrue(isinstance(e_ah, Number))

    def test_get_equilibrium_reaction_energy(self):
        for entry in self.pd.stable_entries:
            self.assertLessEqual(
                self.analyzer.get_equilibrium_reaction_energy(entry), 0,
                "Stable entries should have negative equilibrium reaction energy!")

    def test_get_decomposition(self):
        for entry in self.pd.stable_entries:
            self.assertEqual(len(self.analyzer.get_decomposition(entry.composition)), 1,
                              "Stable composition should have only 1 decomposition!")
        dim = len(self.pd.elements)
        for entry in self.pd.all_entries:
            ndecomp = len(self.analyzer.get_decomposition(entry.composition))
            self.assertTrue(ndecomp > 0 and ndecomp <= dim,
                            "The number of decomposition phases can at most be equal to the number of components.")

        #Just to test decomp for a ficitious composition
        ansdict = {entry.composition.formula: amt
                   for entry, amt in
                   self.analyzer.get_decomposition(Composition("Li3Fe7O11")).items()}
        expected_ans = {"Fe2 O2": 0.0952380952380949,
                        "Li1 Fe1 O2": 0.5714285714285714,
                        "Fe6 O8": 0.33333333333333393}
        for k, v in expected_ans.items():
            self.assertAlmostEqual(ansdict[k], v)

    def test_get_transition_chempots(self):
        for el in self.pd.elements:
            self.assertLessEqual(len(self.analyzer.get_transition_chempots(el)),
                                 len(self.pd.facets))

    def test_get_element_profile(self):
        for el in self.pd.elements:
            for entry in self.pd.stable_entries:
                if not (entry.composition.is_element):
                    self.assertLessEqual(len(self.analyzer.get_element_profile(el, entry.composition)),
                                         len(self.pd.facets))

        expected = [{'evolution': 1.0,
                     'chempot': -4.2582781416666666,
                     'reaction': 'Li2O + 0.5 O2 -> Li2O2'},
                    {'evolution': 0,
                     'chempot': -5.0885906699999968,
                     'reaction': 'Li2O -> Li2O'},
                    {'evolution': -1.0,
                     'chempot': -10.487582010000001,
                     'reaction': 'Li2O -> 2 Li + 0.5 O2'}]
        result = self.analyzer.get_element_profile(Element('O'), Composition('Li2O'))
        for d1, d2 in zip(expected, result):
            self.assertAlmostEqual(d1['evolution'], d2['evolution'])
            self.assertAlmostEqual(d1['chempot'], d2['chempot'])
            self.assertEqual(d1['reaction'], str(d2['reaction']))

    def test_get_get_chempot_range_map(self):
        elements = [el for el in self.pd.elements if el.symbol != "Fe"]
        self.assertEqual(len(self.analyzer.get_chempot_range_map(elements)), 10)

    def test_getmu_vertices_stability_phase(self):
        results = self.analyzer.getmu_vertices_stability_phase(Composition("LiFeO2"), Element("O"))
        self.assertAlmostEqual(len(results), 6)
        test_equality = False
        for c in results:
            if abs(c[Element("O")]+7.115) < 1e-2 and abs(c[Element("Fe")]+6.596) < 1e-2 and \
                    abs(c[Element("Li")]+3.931) < 1e-2:
                test_equality = True
        self.assertTrue(test_equality,"there is an expected vertex missing in the list")


    def test_getmu_range_stability_phase(self):
        results = self.analyzer.get_chempot_range_stability_phase(
            Composition("LiFeO2"), Element("O"))
        self.assertAlmostEqual(results[Element("O")][1], -4.4501812249999997)
        self.assertAlmostEqual(results[Element("Fe")][0], -6.5961470999999996)
        self.assertAlmostEqual(results[Element("Li")][0], -3.6250022625000007)

    def test_get_hull_energy(self):
        for entry in self.pd.stable_entries:
            h_e = self.analyzer.get_hull_energy(entry.composition)
            self.assertAlmostEqual(h_e, entry.energy)
            n_h_e = self.analyzer.get_hull_energy(entry.composition.fractional_composition)
            self.assertAlmostEqual(n_h_e, entry.energy_per_atom)

    def test_1d_pd(self):
        entry = PDEntry('H', 0)
        pd = PhaseDiagram([entry])
        pda = PDAnalyzer(pd)
        decomp, e = pda.get_decomp_and_e_above_hull(PDEntry('H', 1))
        self.assertAlmostEqual(e, 1)
        self.assertAlmostEqual(decomp[entry], 1.0)

    def test_get_critical_compositions_fractional(self):
        c1 = Composition('Fe2O3').fractional_composition
        c2 = Composition('Li3FeO4').fractional_composition
        c3 = Composition('Li2O').fractional_composition

        comps = self.analyzer.get_critical_compositions(c1, c2)
        expected = [Composition('Fe2O3').fractional_composition,
                    Composition('Li0.3243244Fe0.1621621O0.51351349'),
                    Composition('Li3FeO4').fractional_composition]
        for crit, exp in zip(comps, expected):
            self.assertTrue(crit.almost_equals(exp, rtol=0, atol=1e-5))

        comps = self.analyzer.get_critical_compositions(c1, c3)
        expected = [Composition('Fe0.4O0.6'),
                    Composition('LiFeO2').fractional_composition,
                    Composition('Li5FeO4').fractional_composition,
                    Composition('Li2O').fractional_composition]
        for crit, exp in zip(comps, expected):
            self.assertTrue(crit.almost_equals(exp, rtol=0, atol=1e-5))

    def test_get_critical_compositions(self):
        c1 = Composition('Fe2O3')
        c2 = Composition('Li3FeO4')
        c3 = Composition('Li2O')

        comps = self.analyzer.get_critical_compositions(c1, c2)
        expected = [Composition('Fe2O3'),
                    Composition('Li0.3243244Fe0.1621621O0.51351349') * 7.4,
                    Composition('Li3FeO4')]
        for crit, exp in zip(comps, expected):
            self.assertTrue(crit.almost_equals(exp, rtol=0, atol=1e-5))

        comps = self.analyzer.get_critical_compositions(c1, c3)
        expected = [Composition('Fe2O3'),
                    Composition('LiFeO2'),
                    Composition('Li5FeO4') / 3,
                    Composition('Li2O')]
        for crit, exp in zip(comps, expected):
            self.assertTrue(crit.almost_equals(exp, rtol=0, atol=1e-5))

        # Don't fail silently if input compositions aren't in phase diagram
        # Can be very confusing if you're working with a GrandPotentialPD
        self.assertRaises(ValueError, self.analyzer.get_critical_compositions,
                          Composition('Xe'), Composition('Mn'))

        # For the moment, should also fail even if compositions are in the gppd
        # because it isn't handled properly
        gppd = GrandPotentialPhaseDiagram(self.pd.all_entries, {'Xe': 1},
                                          self.pd.elements + [Element('Xe')])
        pda = PDAnalyzer(gppd)
        self.assertRaises(ValueError, pda.get_critical_compositions,
                          Composition('Fe2O3'), Composition('Li3FeO4Xe'))

        # check that the function still works though
        comps = pda.get_critical_compositions(c1, c2)
        expected = [Composition('Fe2O3'),
                    Composition('Li0.3243244Fe0.1621621O0.51351349') * 7.4,
                    Composition('Li3FeO4')]
        for crit, exp in zip(comps, expected):
            self.assertTrue(crit.almost_equals(exp, rtol=0, atol=1e-5))

        # case where the endpoints are identical
        self.assertEqual(self.analyzer.get_critical_compositions(c1, c1 * 2),
                         [c1, c1 * 2])

    def test_get_composition_chempots(self):
        c1 = Composition('Fe3.1O4')
        c2 = Composition('Fe3.2O4.1Li0.01')

        e1 = self.analyzer.get_hull_energy(c1)
        e2 = self.analyzer.get_hull_energy(c2)

        cp = self.analyzer.get_composition_chempots(c1)
        calc_e2 = e1 + sum(cp[k] * v for k, v in (c2 - c1).items())
        self.assertAlmostEqual(e2, calc_e2)
예제 #27
0
class PDAnalyzerTest(unittest.TestCase):
    def setUp(self):
        module_dir = os.path.dirname(os.path.abspath(__file__))
        (elements, entries) = PDEntryIO.from_csv(
            os.path.join(module_dir, "pdentries_test.csv"))
        self.pd = PhaseDiagram(entries)
        self.analyzer = PDAnalyzer(self.pd)

    def test_get_e_above_hull(self):
        for entry in self.pd.stable_entries:
            self.assertLess(
                self.analyzer.get_e_above_hull(entry), 1e-11,
                "Stable entries should have e above hull of zero!")
        for entry in self.pd.all_entries:
            if entry not in self.pd.stable_entries:
                e_ah = self.analyzer.get_e_above_hull(entry)
                self.assertGreaterEqual(e_ah, 0)
                self.assertTrue(isinstance(e_ah, Number))

    def test_get_equilibrium_reaction_energy(self):
        for entry in self.pd.stable_entries:
            self.assertLessEqual(
                self.analyzer.get_equilibrium_reaction_energy(entry), 0,
                "Stable entries should have negative equilibrium reaction energy!"
            )

    def test_get_decomposition(self):
        for entry in self.pd.stable_entries:
            self.assertEqual(
                len(self.analyzer.get_decomposition(entry.composition)), 1,
                "Stable composition should have only 1 decomposition!")
        dim = len(self.pd.elements)
        for entry in self.pd.all_entries:
            ndecomp = len(self.analyzer.get_decomposition(entry.composition))
            self.assertTrue(
                ndecomp > 0 and ndecomp <= dim,
                "The number of decomposition phases can at most be equal to the number of components."
            )

        #Just to test decomp for a ficitious composition
        ansdict = {
            entry.composition.formula: amt
            for entry, amt in self.analyzer.get_decomposition(
                Composition("Li3Fe7O11")).items()
        }
        expected_ans = {
            "Fe2 O2": 0.0952380952380949,
            "Li1 Fe1 O2": 0.5714285714285714,
            "Fe6 O8": 0.33333333333333393
        }
        for k, v in expected_ans.items():
            self.assertAlmostEqual(ansdict[k], v)

    def test_get_transition_chempots(self):
        for el in self.pd.elements:
            self.assertLessEqual(
                len(self.analyzer.get_transition_chempots(el)),
                len(self.pd.facets))

    def test_get_element_profile(self):
        for el in self.pd.elements:
            for entry in self.pd.stable_entries:
                if not (entry.composition.is_element):
                    self.assertLessEqual(
                        len(
                            self.analyzer.get_element_profile(
                                el, entry.composition)), len(self.pd.facets))

    def test_get_get_chempot_range_map(self):
        elements = [el for el in self.pd.elements if el.symbol != "Fe"]
        self.assertEqual(len(self.analyzer.get_chempot_range_map(elements)),
                         10)

    def test_getmu_vertices_stability_phase(self):
        results = self.analyzer.getmu_vertices_stability_phase(
            Composition("LiFeO2"), Element("O"))
        self.assertAlmostEqual(len(results), 6)
        test_equality = False
        for c in results:
            if abs(c[Element("O")]+7.115) < 1e-2 and abs(c[Element("Fe")]+6.596) < 1e-2 and \
                    abs(c[Element("Li")]+3.931) < 1e-2:
                test_equality = True
        self.assertTrue(test_equality,
                        "there is an expected vertex missing in the list")

    def test_getmu_range_stability_phase(self):
        results = self.analyzer.get_chempot_range_stability_phase(
            Composition("LiFeO2"), Element("O"))
        self.assertAlmostEqual(results[Element("O")][1], -4.4501812249999997)
        self.assertAlmostEqual(results[Element("Fe")][0], -6.5961470999999996)
        self.assertAlmostEqual(results[Element("Li")][0], -3.6250022625000007)

    def test_get_hull_energy(self):
        for entry in self.pd.stable_entries:
            h_e = self.analyzer.get_hull_energy(entry.composition)
            self.assertAlmostEqual(h_e, entry.energy)
            n_h_e = self.analyzer.get_hull_energy(
                entry.composition.fractional_composition)
            self.assertAlmostEqual(n_h_e, entry.energy_per_atom)

    def test_1d_pd(self):
        entry = PDEntry('H', 0)
        pd = PhaseDiagram([entry])
        pda = PDAnalyzer(pd)
        decomp, e = pda.get_decomp_and_e_above_hull(PDEntry('H', 1))
        self.assertAlmostEqual(e, 1)
        self.assertAlmostEqual(decomp[entry], 1.0)
    ]
    return relevant_entries


def find_entry_index(formula, all_entries):
    entry_index = [
        all_entries.index(entry) for entry in all_entries
        if entry.composition.reduced_formula == Composition(
            formula).reduced_formula
    ]
    return entry_index


rester = MPRester(
    '5TxDLF4Iwa7rGcAl')  #Generate your own key from materials project..
mp_entries = rester.get_entries_in_chemsys(["Li", "Fe", "O", "S"])

pd = PhaseDiagram(mp_entries)
plotter = PDPlotter(pd)
analyzer = PDAnalyzer(pd)

li_entries = [e for e in mp_entries if e.composition.reduced_formula == "Li"]
uli0 = min(li_entries, key=lambda e: e.energy_per_atom).energy_per_atom

el_profile = analyzer.get_element_profile(Element("Li"),
                                          Composition("Li2FeSO"))
for i, d in enumerate(el_profile):
    voltage = -(d["chempot"] - uli0)
    print("Voltage: %s V" % voltage)
    print(d["reaction"])
    print("")
예제 #29
0
class PDAnalyzerTest(unittest.TestCase):

    def setUp(self):
        module_dir = os.path.dirname(os.path.abspath(__file__))
        (elements, entries) = PDEntryIO.from_csv(os.path.join(module_dir,
                                                              "pdentries_test.csv"))
        self.pd = PhaseDiagram(entries)
        self.analyzer = PDAnalyzer(self.pd)

    def test_get_e_above_hull(self):
        for entry in self.pd.stable_entries:
            self.assertLess(self.analyzer.get_e_above_hull(entry), 1e-11,
                            "Stable entries should have e above hull of zero!")
        for entry in self.pd.all_entries:
            if entry not in self.pd.stable_entries:
                e_ah = self.analyzer.get_e_above_hull(entry)
                self.assertGreaterEqual(e_ah, 0)
                self.assertTrue(isinstance(e_ah, Number))

    def test_get_equilibrium_reaction_energy(self):
        for entry in self.pd.stable_entries:
            self.assertLessEqual(
                self.analyzer.get_equilibrium_reaction_energy(entry), 0,
                "Stable entries should have negative equilibrium reaction energy!")

    def test_get_decomposition(self):
        for entry in self.pd.stable_entries:
            self.assertEqual(len(self.analyzer.get_decomposition(entry.composition)), 1,
                              "Stable composition should have only 1 decomposition!")
        dim = len(self.pd.elements)
        for entry in self.pd.all_entries:
            ndecomp = len(self.analyzer.get_decomposition(entry.composition))
            self.assertTrue(ndecomp > 0 and ndecomp <= dim,
                            "The number of decomposition phases can at most be equal to the number of components.")

        #Just to test decomp for a ficitious composition
        ansdict = {entry.composition.formula: amt
                   for entry, amt in
                   self.analyzer.get_decomposition(Composition("Li3Fe7O11")).items()}
        expected_ans = {"Fe2 O2": 0.0952380952380949,
                        "Li1 Fe1 O2": 0.5714285714285714,
                        "Fe6 O8": 0.33333333333333393}
        for k, v in expected_ans.items():
            self.assertAlmostEqual(ansdict[k], v)

    def test_get_transition_chempots(self):
        for el in self.pd.elements:
            self.assertLessEqual(len(self.analyzer.get_transition_chempots(el)),
                                 len(self.pd.facets))

    def test_get_element_profile(self):
        for el in self.pd.elements:
            for entry in self.pd.stable_entries:
                if not (entry.composition.is_element):
                    self.assertLessEqual(len(self.analyzer.get_element_profile(el, entry.composition)),
                                         len(self.pd.facets))

    def test_get_get_chempot_range_map(self):
        elements = [el for el in self.pd.elements if el.symbol != "Fe"]
        self.assertEqual(len(self.analyzer.get_chempot_range_map(elements)), 10)

    def test_getmu_vertices_stability_phase(self):
        results = self.analyzer.getmu_vertices_stability_phase(Composition("LiFeO2"), Element("O"))
        self.assertAlmostEqual(len(results), 6)
        test_equality = False
        for c in results:
            if abs(c[Element("O")]+7.115) < 1e-2 and abs(c[Element("Fe")]+6.596) < 1e-2 and \
                    abs(c[Element("Li")]+3.931) < 1e-2:
                test_equality = True
        self.assertTrue(test_equality,"there is an expected vertex missing in the list")


    def test_getmu_range_stability_phase(self):
        results = self.analyzer.get_chempot_range_stability_phase(
            Composition("LiFeO2"), Element("O"))
        self.assertAlmostEqual(results[Element("O")][1], -4.4501812249999997)
        self.assertAlmostEqual(results[Element("Fe")][0], -6.5961470999999996)
        self.assertAlmostEqual(results[Element("Li")][0], -3.6250022625000007)

    def test_get_hull_energy(self):
        for entry in self.pd.stable_entries:
            h_e = self.analyzer.get_hull_energy(entry.composition)
            self.assertAlmostEqual(h_e, entry.energy)
            n_h_e = self.analyzer.get_hull_energy(entry.composition.fractional_composition)
            self.assertAlmostEqual(n_h_e, entry.energy_per_atom)

    def test_1d_pd(self):
        entry = PDEntry('H', 0)
        pd = PhaseDiagram([entry])
        pda = PDAnalyzer(pd)
        decomp, e = pda.get_decomp_and_e_above_hull(PDEntry('H', 1))
        self.assertAlmostEqual(e, 1)
        self.assertAlmostEqual(decomp[entry], 1.0)
예제 #30
0
    Composition(names[11]),
    Composition(names[32])
]

# creating entries from for the LDA phase diagram
entries_lda = []
for i in range(len(names)):
    #    entries_lda.append(PDEntry(names[i],enlda[i], names[i], "LDA"))
    entries_lda.append(PDEntry(names[i], enlda[i], " ", "LDA"))

# creating the phase diagram for LDA
pd_lda = PhaseDiagram(entries_lda)
cpd_lda = CompoundPhaseDiagram(entries_lda,
                               term_comp,
                               normalize_terminal_compositions=True)
a_lda = PDAnalyzer(pd_lda)

# creating entries from for the PBE phase diagram
entries_pbe = []
for i in range(len(names)):
    #    entries_pbe.append(PDEntry(names[i],enpbe[i], names[i], "PBE"))
    entries_pbe.append(PDEntry(names[i], enpbe[i], " ", "PBE"))

# creating the phase diagram for PBE
pd_pbe = PhaseDiagram(entries_pbe)
cpd_pbe = CompoundPhaseDiagram(entries_pbe,
                               term_comp,
                               normalize_terminal_compositions=True)
a_pbe = PDAnalyzer(pd_pbe)

# # visualize quaternary phase diagrams
예제 #31
0
 def setUp(self):
     module_dir = os.path.dirname(os.path.abspath(__file__))
     (elements, entries) = PDEntryIO.from_csv(
         os.path.join(module_dir, "pdentries_test.csv"))
     self.pd = PhaseDiagram(entries)
     self.analyzer = PDAnalyzer(self.pd)
예제 #32
0
    def _get_2d_plot(self, label_stable=True, label_unstable=True,
                     ordering=None, energy_colormap=None, vmin_mev=-60.0,
                     vmax_mev=60.0, show_colorbar=True,
                     process_attributes=False):
        """
        Shows the plot using pylab.  Usually I won't do imports in methods,
        but since plotting is a fairly expensive library to load and not all
        machines have matplotlib installed, I have done it this way.
        """

        plt = pretty_plot(8, 6)
        from matplotlib.font_manager import FontProperties
        if ordering is None:
            (lines, labels, unstable) = self.pd_plot_data
        else:
            (_lines, _labels, _unstable) = self.pd_plot_data
            (lines, labels, unstable) = order_phase_diagram(
                _lines, _labels, _unstable, ordering)
        if energy_colormap is None:
            if process_attributes:
                for x, y in lines:
                    plt.plot(x, y, "k-", linewidth=3, markeredgecolor="k")
                # One should think about a clever way to have "complex"
                # attributes with complex processing options but with a clear
                #  logic. At this moment, I just use the attributes to know
                # whether an entry is a new compound or an existing (from the
                #  ICSD or from the MP) one.
                for x, y in labels.keys():
                    if labels[(x, y)].attribute is None or \
                            labels[(x, y)].attribute == "existing":
                        plt.plot(x, y, "ko", linewidth=3, markeredgecolor="k",
                                 markerfacecolor="b", markersize=12)
                    else:
                        plt.plot(x, y, "k*", linewidth=3, markeredgecolor="k",
                                 markerfacecolor="g", markersize=18)
            else:
                for x, y in lines:
                    plt.plot(x, y, "ko-", linewidth=3, markeredgecolor="k",
                             markerfacecolor="b", markersize=15)
        else:
            from matplotlib.colors import Normalize, LinearSegmentedColormap
            from matplotlib.cm import ScalarMappable
            pda = PDAnalyzer(self._pd)
            for x, y in lines:
                plt.plot(x, y, "k-", linewidth=3, markeredgecolor="k")
            vmin = vmin_mev / 1000.0
            vmax = vmax_mev / 1000.0
            if energy_colormap == 'default':
                mid = - vmin / (vmax - vmin)
                cmap = LinearSegmentedColormap.from_list(
                    'my_colormap', [(0.0, '#005500'), (mid, '#55FF55'),
                                    (mid, '#FFAAAA'), (1.0, '#FF0000')])
            else:
                cmap = energy_colormap
            norm = Normalize(vmin=vmin, vmax=vmax)
            _map = ScalarMappable(norm=norm, cmap=cmap)
            _energies = [pda.get_equilibrium_reaction_energy(entry)
                         for coord, entry in labels.items()]
            energies = [en if en < 0.0 else -0.00000001 for en in _energies]
            vals_stable = _map.to_rgba(energies)
            ii = 0
            if process_attributes:
                for x, y in labels.keys():
                    if labels[(x, y)].attribute is None or \
                            labels[(x, y)].attribute == "existing":
                        plt.plot(x, y, "o", markerfacecolor=vals_stable[ii],
                                 markersize=12)
                    else:
                        plt.plot(x, y, "*", markerfacecolor=vals_stable[ii],
                                 markersize=18)
                    ii += 1
            else:
                for x, y in labels.keys():
                    plt.plot(x, y, "o", markerfacecolor=vals_stable[ii],
                             markersize=15)
                    ii += 1

        font = FontProperties()
        font.set_weight("bold")
        font.set_size(24)

        # Sets a nice layout depending on the type of PD. Also defines a
        # "center" for the PD, which then allows the annotations to be spread
        # out in a nice manner.
        if len(self._pd.elements) == 3:
            plt.axis("equal")
            plt.xlim((-0.1, 1.2))
            plt.ylim((-0.1, 1.0))
            plt.axis("off")
            center = (0.5, math.sqrt(3) / 6)
        else:
            all_coords = labels.keys()
            miny = min([c[1] for c in all_coords])
            ybuffer = max(abs(miny) * 0.1, 0.1)
            plt.xlim((-0.1, 1.1))
            plt.ylim((miny - ybuffer, ybuffer))
            center = (0.5, miny / 2)
            plt.xlabel("Fraction", fontsize=28, fontweight='bold')
            plt.ylabel("Formation energy (eV/fu)", fontsize=28,
                       fontweight='bold')

        for coords in sorted(labels.keys(), key=lambda x: -x[1]):
            entry = labels[coords]
            label = entry.name

            # The follow defines an offset for the annotation text emanating
            # from the center of the PD. Results in fairly nice layouts for the
            # most part.
            vec = (np.array(coords) - center)
            vec = vec / np.linalg.norm(vec) * 10 if np.linalg.norm(vec) != 0 \
                else vec
            valign = "bottom" if vec[1] > 0 else "top"
            if vec[0] < -0.01:
                halign = "right"
            elif vec[0] > 0.01:
                halign = "left"
            else:
                halign = "center"
            if label_stable:
                if process_attributes and entry.attribute == 'new':
                    plt.annotate(latexify(label), coords, xytext=vec,
                                 textcoords="offset points",
                                 horizontalalignment=halign,
                                 verticalalignment=valign,
                                 fontproperties=font,
                                 color='g')
                else:
                    plt.annotate(latexify(label), coords, xytext=vec,
                                 textcoords="offset points",
                                 horizontalalignment=halign,
                                 verticalalignment=valign,
                                 fontproperties=font)

        if self.show_unstable:
            font = FontProperties()
            font.set_size(16)
            pda = PDAnalyzer(self._pd)
            energies_unstable = [pda.get_e_above_hull(entry)
                                 for entry, coord in unstable.items()]
            if energy_colormap is not None:
                energies.extend(energies_unstable)
                vals_unstable = _map.to_rgba(energies_unstable)
            ii = 0
            for entry, coords in unstable.items():
                ehull = pda.get_e_above_hull(entry)
                if ehull < self.show_unstable:
                    vec = (np.array(coords) - center)
                    vec = vec / np.linalg.norm(vec) * 10 \
                        if np.linalg.norm(vec) != 0 else vec
                    label = entry.name
                    if energy_colormap is None:
                        plt.plot(coords[0], coords[1], "ks", linewidth=3,
                                 markeredgecolor="k", markerfacecolor="r",
                                 markersize=8)
                    else:
                        plt.plot(coords[0], coords[1], "s", linewidth=3,
                                 markeredgecolor="k",
                                 markerfacecolor=vals_unstable[ii],
                                 markersize=8)
                    if label_unstable:
                        plt.annotate(latexify(label), coords, xytext=vec,
                                     textcoords="offset points",
                                     horizontalalignment=halign, color="b",
                                     verticalalignment=valign,
                                     fontproperties=font)
                    ii += 1
        if energy_colormap is not None and show_colorbar:
            _map.set_array(energies)
            cbar = plt.colorbar(_map)
            cbar.set_label(
                'Energy [meV/at] above hull (in red)\nInverse energy ['
                'meV/at] above hull (in green)',
                rotation=-90, ha='left', va='center')
            ticks = cbar.ax.get_yticklabels()
            # cbar.ax.set_yticklabels(['${v}$'.format(
            #     v=float(t.get_text().strip('$'))*1000.0) for t in ticks])
        f = plt.gcf()
        f.set_size_inches((8, 6))
        plt.subplots_adjust(left=0.09, right=0.98, top=0.98, bottom=0.07)
        return plt
예제 #33
0
class PDAnalyzerTest(unittest.TestCase):
    def setUp(self):
        module_dir = os.path.dirname(os.path.abspath(__file__))
        (elements, entries) = PDEntryIO.from_csv(
            os.path.join(module_dir, "pdentries_test.csv"))
        self.pd = PhaseDiagram(entries)
        self.analyzer = PDAnalyzer(self.pd)

    def test_get_e_above_hull(self):
        for entry in self.pd.stable_entries:
            self.assertLess(
                self.analyzer.get_e_above_hull(entry), 1e-11,
                "Stable entries should have e above hull of zero!")
        for entry in self.pd.all_entries:
            if entry not in self.pd.stable_entries:
                e_ah = self.analyzer.get_e_above_hull(entry)
                self.assertGreaterEqual(e_ah, 0)
                self.assertTrue(isinstance(e_ah, Number))

    def test_get_equilibrium_reaction_energy(self):
        for entry in self.pd.stable_entries:
            self.assertLessEqual(
                self.analyzer.get_equilibrium_reaction_energy(entry), 0,
                "Stable entries should have negative equilibrium reaction energy!"
            )

    def test_get_decomposition(self):
        for entry in self.pd.stable_entries:
            self.assertEqual(
                len(self.analyzer.get_decomposition(entry.composition)), 1,
                "Stable composition should have only 1 decomposition!")
        dim = len(self.pd.elements)
        for entry in self.pd.all_entries:
            ndecomp = len(self.analyzer.get_decomposition(entry.composition))
            self.assertTrue(
                ndecomp > 0 and ndecomp <= dim,
                "The number of decomposition phases can at most be equal to the number of components."
            )

        #Just to test decomp for a ficitious composition
        ansdict = {
            entry.composition.formula: amt
            for entry, amt in self.analyzer.get_decomposition(
                Composition("Li3Fe7O11")).items()
        }
        expected_ans = {
            "Fe2 O2": 0.0952380952380949,
            "Li1 Fe1 O2": 0.5714285714285714,
            "Fe6 O8": 0.33333333333333393
        }
        for k, v in expected_ans.items():
            self.assertAlmostEqual(ansdict[k], v)

    def test_get_transition_chempots(self):
        for el in self.pd.elements:
            self.assertLessEqual(
                len(self.analyzer.get_transition_chempots(el)),
                len(self.pd.facets))

    def test_get_element_profile(self):
        for el in self.pd.elements:
            for entry in self.pd.stable_entries:
                if not (entry.composition.is_element):
                    self.assertLessEqual(
                        len(
                            self.analyzer.get_element_profile(
                                el, entry.composition)), len(self.pd.facets))

        expected = [{
            'evolution': 1.0,
            'chempot': -4.2582781416666666,
            'reaction': 'Li2O + 0.5 O2 -> Li2O2'
        }, {
            'evolution': 0,
            'chempot': -5.0885906699999968,
            'reaction': 'Li2O -> Li2O'
        }, {
            'evolution': -1.0,
            'chempot': -10.487582010000001,
            'reaction': 'Li2O -> 2 Li + 0.5 O2'
        }]
        result = self.analyzer.get_element_profile(Element('O'),
                                                   Composition('Li2O'))
        for d1, d2 in zip(expected, result):
            self.assertAlmostEqual(d1['evolution'], d2['evolution'])
            self.assertAlmostEqual(d1['chempot'], d2['chempot'])
            self.assertEqual(d1['reaction'], str(d2['reaction']))

    def test_get_get_chempot_range_map(self):
        elements = [el for el in self.pd.elements if el.symbol != "Fe"]
        self.assertEqual(len(self.analyzer.get_chempot_range_map(elements)),
                         10)

    def test_getmu_vertices_stability_phase(self):
        results = self.analyzer.getmu_vertices_stability_phase(
            Composition("LiFeO2"), Element("O"))
        self.assertAlmostEqual(len(results), 6)
        test_equality = False
        for c in results:
            if abs(c[Element("O")]+7.115) < 1e-2 and abs(c[Element("Fe")]+6.596) < 1e-2 and \
                    abs(c[Element("Li")]+3.931) < 1e-2:
                test_equality = True
        self.assertTrue(test_equality,
                        "there is an expected vertex missing in the list")

    def test_getmu_range_stability_phase(self):
        results = self.analyzer.get_chempot_range_stability_phase(
            Composition("LiFeO2"), Element("O"))
        self.assertAlmostEqual(results[Element("O")][1], -4.4501812249999997)
        self.assertAlmostEqual(results[Element("Fe")][0], -6.5961470999999996)
        self.assertAlmostEqual(results[Element("Li")][0], -3.6250022625000007)

    def test_get_hull_energy(self):
        for entry in self.pd.stable_entries:
            h_e = self.analyzer.get_hull_energy(entry.composition)
            self.assertAlmostEqual(h_e, entry.energy)
            n_h_e = self.analyzer.get_hull_energy(
                entry.composition.fractional_composition)
            self.assertAlmostEqual(n_h_e, entry.energy_per_atom)

    def test_1d_pd(self):
        entry = PDEntry('H', 0)
        pd = PhaseDiagram([entry])
        pda = PDAnalyzer(pd)
        decomp, e = pda.get_decomp_and_e_above_hull(PDEntry('H', 1))
        self.assertAlmostEqual(e, 1)
        self.assertAlmostEqual(decomp[entry], 1.0)

    def test_get_critical_compositions_fractional(self):
        c1 = Composition('Fe2O3').fractional_composition
        c2 = Composition('Li3FeO4').fractional_composition
        c3 = Composition('Li2O').fractional_composition

        comps = self.analyzer.get_critical_compositions(c1, c2)
        expected = [
            Composition('Fe2O3').fractional_composition,
            Composition('Li0.3243244Fe0.1621621O0.51351349'),
            Composition('Li3FeO4').fractional_composition
        ]
        for crit, exp in zip(comps, expected):
            self.assertTrue(crit.almost_equals(exp, rtol=0, atol=1e-5))

        comps = self.analyzer.get_critical_compositions(c1, c3)
        expected = [
            Composition('Fe0.4O0.6'),
            Composition('LiFeO2').fractional_composition,
            Composition('Li5FeO4').fractional_composition,
            Composition('Li2O').fractional_composition
        ]
        for crit, exp in zip(comps, expected):
            self.assertTrue(crit.almost_equals(exp, rtol=0, atol=1e-5))

    def test_get_critical_compositions(self):
        c1 = Composition('Fe2O3')
        c2 = Composition('Li3FeO4')
        c3 = Composition('Li2O')

        comps = self.analyzer.get_critical_compositions(c1, c2)
        expected = [
            Composition('Fe2O3'),
            Composition('Li0.3243244Fe0.1621621O0.51351349') * 7.4,
            Composition('Li3FeO4')
        ]
        for crit, exp in zip(comps, expected):
            self.assertTrue(crit.almost_equals(exp, rtol=0, atol=1e-5))

        comps = self.analyzer.get_critical_compositions(c1, c3)
        expected = [
            Composition('Fe2O3'),
            Composition('LiFeO2'),
            Composition('Li5FeO4') / 3,
            Composition('Li2O')
        ]
        for crit, exp in zip(comps, expected):
            self.assertTrue(crit.almost_equals(exp, rtol=0, atol=1e-5))

        # Don't fail silently if input compositions aren't in phase diagram
        # Can be very confusing if you're working with a GrandPotentialPD
        self.assertRaises(ValueError, self.analyzer.get_critical_compositions,
                          Composition('Xe'), Composition('Mn'))

        # For the moment, should also fail even if compositions are in the gppd
        # because it isn't handled properly
        gppd = GrandPotentialPhaseDiagram(self.pd.all_entries, {'Xe': 1},
                                          self.pd.elements + [Element('Xe')])
        pda = PDAnalyzer(gppd)
        self.assertRaises(ValueError, pda.get_critical_compositions,
                          Composition('Fe2O3'), Composition('Li3FeO4Xe'))

        # check that the function still works though
        comps = pda.get_critical_compositions(c1, c2)
        expected = [
            Composition('Fe2O3'),
            Composition('Li0.3243244Fe0.1621621O0.51351349') * 7.4,
            Composition('Li3FeO4')
        ]
        for crit, exp in zip(comps, expected):
            self.assertTrue(crit.almost_equals(exp, rtol=0, atol=1e-5))

        # case where the endpoints are identical
        self.assertEqual(self.analyzer.get_critical_compositions(c1, c1 * 2),
                         [c1, c1 * 2])

    def test_get_composition_chempots(self):
        c1 = Composition('Fe3.1O4')
        c2 = Composition('Fe3.2O4.1Li0.01')

        e1 = self.analyzer.get_hull_energy(c1)
        e2 = self.analyzer.get_hull_energy(c2)

        cp = self.analyzer.get_composition_chempots(c1)
        calc_e2 = e1 + sum(cp[k] * v for k, v in (c2 - c1).items())
        self.assertAlmostEqual(e2, calc_e2)
예제 #34
0
 def setUp(self):
     module_dir = os.path.dirname(os.path.abspath(__file__))
     (elements, entries) = PDEntryIO.from_csv(os.path.join(module_dir,
                                                           "pdentries_test.csv"))
     self.pd = PhaseDiagram(entries)
     self.analyzer = PDAnalyzer(self.pd)
예제 #35
0
    def get_chempot_range_map_plot(self, elements,referenced=True):
        """
        Returns a plot of the chemical potential range _map. Currently works
        only for 3-component PDs.

        Args:
            elements: Sequence of elements to be considered as independent
                variables. E.g., if you want to show the stability ranges of
                all Li-Co-O phases wrt to uLi and uO, you will supply
                [Element("Li"), Element("O")]
            referenced: if True, gives the results with a reference being the
                        energy of the elemental phase. If False, gives absolute values.

        Returns:
            A matplotlib plot object.
        """

        plt = pretty_plot(12, 8)
        analyzer = PDAnalyzer(self._pd)
        chempot_ranges = analyzer.get_chempot_range_map(
            elements, referenced=referenced)
        missing_lines = {}
        excluded_region = []
        for entry, lines in chempot_ranges.items():
            comp = entry.composition
            center_x = 0
            center_y = 0
            coords = []
            contain_zero = any([comp.get_atomic_fraction(el) == 0
                                for el in elements])
            is_boundary = (not contain_zero) and \
                sum([comp.get_atomic_fraction(el) for el in elements]) == 1
            for line in lines:
                (x, y) = line.coords.transpose()
                plt.plot(x, y, "k-")

                for coord in line.coords:
                    if not in_coord_list(coords, coord):
                        coords.append(coord.tolist())
                        center_x += coord[0]
                        center_y += coord[1]
                if is_boundary:
                    excluded_region.extend(line.coords)

            if coords and contain_zero:
                missing_lines[entry] = coords
            else:
                xy = (center_x / len(coords), center_y / len(coords))
                plt.annotate(latexify(entry.name), xy, fontsize=22)

        ax = plt.gca()
        xlim = ax.get_xlim()
        ylim = ax.get_ylim()

        #Shade the forbidden chemical potential regions.
        excluded_region.append([xlim[1], ylim[1]])
        excluded_region = sorted(excluded_region, key=lambda c: c[0])
        (x, y) = np.transpose(excluded_region)
        plt.fill(x, y, "0.80")

        #The hull does not generate the missing horizontal and vertical lines.
        #The following code fixes this.
        el0 = elements[0]
        el1 = elements[1]
        for entry, coords in missing_lines.items():
            center_x = sum([c[0] for c in coords])
            center_y = sum([c[1] for c in coords])
            comp = entry.composition
            is_x = comp.get_atomic_fraction(el0) < 0.01
            is_y = comp.get_atomic_fraction(el1) < 0.01
            n = len(coords)
            if not (is_x and is_y):
                if is_x:
                    coords = sorted(coords, key=lambda c: c[1])
                    for i in [0, -1]:
                        x = [min(xlim), coords[i][0]]
                        y = [coords[i][1], coords[i][1]]
                        plt.plot(x, y, "k")
                        center_x += min(xlim)
                        center_y += coords[i][1]
                elif is_y:
                    coords = sorted(coords, key=lambda c: c[0])
                    for i in [0, -1]:
                        x = [coords[i][0], coords[i][0]]
                        y = [coords[i][1], min(ylim)]
                        plt.plot(x, y, "k")
                        center_x += coords[i][0]
                        center_y += min(ylim)
                xy = (center_x / (n + 2), center_y / (n + 2))
            else:
                center_x = sum(coord[0] for coord in coords) + xlim[0]
                center_y = sum(coord[1] for coord in coords) + ylim[0]
                xy = (center_x / (n + 1), center_y / (n + 1))

            plt.annotate(latexify(entry.name), xy,
                         horizontalalignment="center",
                         verticalalignment="center", fontsize=22)

        plt.xlabel("$\\mu_{{{0}}} - \\mu_{{{0}}}^0$ (eV)"
                   .format(el0.symbol))
        plt.ylabel("$\\mu_{{{0}}} - \\mu_{{{0}}}^0$ (eV)"
                   .format(el1.symbol))
        plt.tight_layout()
        return plt
예제 #36
0
# Plot!
plotter = PDPlotter(phase, show_unstable=True)
#plotter.show()

lines, stable, unstable = plotter.pd_plot_data

### NEED_TO_CHANGE ###
plt = plotter.get_plot()
f = plt.gcf()
f.set_size_inches((15, 12))
fig_dir = output_Path + "/" + "phase.png"
plt.savefig(fig_dir, image_format="png")

# Analyze phase diagram!
pda = PDAnalyzer(phase)

# elements : enum Element to array
elements_array = []
for ele in phase.elements:
    elements_array.append(ele.value)
result["elements"] = sorted(elements_array, key=SortByAtomicNumber)

# dimension
result["dimension"] = len(phase.elements)

# lines : numpy.ndarray to array
lines_array = []
for line in lines:
    if isinstance(line, numpy.ndarray):
        ll = line.tolist()