def get_rxn_e_table_string(self, pure_el_ref, open_el, PE_list, oe_amt_list, mu_trans_list, plot_rxn_e):
        neg_flag = (max(mu_trans_list) > 1e-6)
        rxn_trans_list = [mu for mu in mu_trans_list]
        rxn_e_list = []
        ext = 0.2
        rxn_trans_list = [rxn_trans_list[0] + ext] + rxn_trans_list if neg_flag else [0] + rxn_trans_list
        for data in zip(oe_amt_list, PE_list, rxn_trans_list):
            oe_amt, PE, ext_miu = data
            rxn = ComputedReaction([self, pure_el_ref], PE)
            rxn.normalize_to(self.composition.reduced_composition)
            rxn_e_list.append(rxn.calculated_reaction_energy - oe_amt * ext_miu)
        rxn_trans_list = rxn_trans_list + [rxn_trans_list[-1] - ext]
        rxn_e_list = rxn_e_list + [rxn_e_list[-1] + ext * oe_amt_list[-1]]
        rxn_e_list = [e / self.composition.num_atoms for e in rxn_e_list]
        df = pandas.DataFrame()
        df["miu_{} (eV)".format(open_el)] = rxn_trans_list
        df["Rxn energy (eV/atom)"] = rxn_e_list

        if plot_rxn_e:
            plt.figure(figsize=(8, 6))
            ax = plt.gca()
            ax.invert_xaxis()
            ax.axvline(0, linestyle='--', color='k', linewidth=0.5, zorder=1)
            ax.plot(rxn_trans_list, rxn_e_list, '-', linewidth=1.5, color='cornflowerblue', zorder=3)
            ax.scatter(rxn_trans_list[1:-1], rxn_e_list[1:-1], edgecolors='cornflowerblue', facecolors='w',
                       linewidth=1.5, s=50, zorder=4)
            ax.set_xlabel('Chemical potential ref. to {}'.format(open_el))
            ax.set_ylabel('Reaction energy (eV/atom)')
            ax.set_xlim([float(rxn_trans_list[0]), float(rxn_trans_list[-1])])
            plt.show()
        print_df = df.to_string(index=False, float_format='{:,.2f}'.format, justify='center')

        return print_df
Example #2
0
 def chemical_reaction(reactant_entries,
                       product_entries,
                       possible_yield_entries,
                       mode="strict"):
     try:
         rxn = ComputedReaction(reactant_entries, product_entries)
     except:
         try:
             rxn = ComputedReaction(
                 reactant_entries, product_entries + possible_yield_entries)
         except:
             return None
     if rxn.reactants[0] != rxn.products[0]:  # avoid self reactions
         if mode == "strict":
             reactant_comp = [
                 entry.composition.reduced_composition
                 for entry in reactant_entries
             ]
             product_comp = [
                 entry.composition.reduced_composition
                 for entry in product_entries + possible_yield_entries
             ]
             if set(rxn.reactants).issubset(set(reactant_comp)) and set(
                     rxn.products).issubset(set(product_comp)):
                 return rxn
         else:
             return rxn
    def get_decomposition_in_gppd(self,
                                  chempot,
                                  entries=None,
                                  exclusions=None,
                                  trypreload=False):
        gppd_entries = entries if entries \
            else self.get_gppd_entries(chempot, exclusions=exclusions, trypreload=trypreload)
        pd = PhaseDiagram(gppd_entries)
        gppd_entries = pd.stable_entries
        open_el_entries = [
            _ for _ in gppd_entries if _.is_element
            and _.composition.elements[0].symbol in chempot.keys()
        ]
        el_ref = {
            _.composition.elements[0].symbol: _.energy_per_atom
            for _ in open_el_entries
        }
        chempot_vaspref = {_: chempot[_] + el_ref[_] for _ in chempot}
        for open_entry in open_el_entries:
            open_entry.correction += chempot_vaspref[
                open_entry.composition.elements[0].symbol]

        GPPD = GrandPotentialPhaseDiagram(gppd_entries, chempot_vaspref)
        GPComp = self.GPComp(chempot)
        decomp_GP_entries = GPPD.get_decomposition(GPComp)
        decomp_entries = [gpe.original_entry for gpe in decomp_GP_entries]
        rxn = ComputedReaction([self] + open_el_entries, decomp_entries)
        rxn.normalize_to(self.composition)
        return decomp_entries, rxn
 def get_printable_PE_data_in_pd(self, entries=None):
     decomp, hull_e = self.get_decomp_entries_and_e_above_hull(entries=entries)
     output = ['-' * 60]
     PE = list(decomp.keys())
     output.append("Reduced formula of the given composition: " + self.composition.reduced_formula)
     output.append("Calculated phase equilibria: " + "\t".join(i.name for i in PE))
     rxn = ComputedReaction([self], PE)
     rxn.normalize_to(self.composition.reduced_composition)
     output.append(str(rxn))
     output.append('-' * 60)
     string = '\n'.join(output)
     return string
    def setUp(self):
        d = [
            {
                "correction": 0.0,
                "data": {},
                "energy": -108.56492362,
                "parameters": {},
                "composition": {
                    "Li": 54
                },
            },
            {
                "correction": 0.0,
                "data": {},
                "energy": -577.94689128,
                "parameters": {},
                "composition": {
                    "O": 32,
                    "Li": 64
                },
            },
            {
                "correction": 0.0,
                "data": {},
                "energy": -17.02844794,
                "parameters": {},
                "composition": {
                    "O": 2
                },
            },
            {
                "correction": 0.0,
                "data": {},
                "energy": -959.64693323,
                "parameters": {},
                "composition": {
                    "O": 72,
                    "Li": 72
                },
            },
        ]
        entries = []
        for e in d:
            entries.append(ComputedEntry.from_dict(e))
        rcts = list(
            filter(lambda e: e.composition.reduced_formula in ["Li", "O2"],
                   entries))
        prods = list(
            filter(lambda e: e.composition.reduced_formula == "Li2O2",
                   entries))

        self.rxn = ComputedReaction(rcts, prods)
class ComputedReactionTest(unittest.TestCase):
    def setUp(self):
        d = [{
            "correction": 0.0,
            "data": {},
            "energy": -108.56492362,
            "parameters": {},
            "composition": {
                "Li": 54
            }
        }, {
            "correction": 0.0,
            "data": {},
            "energy": -577.94689128,
            "parameters": {},
            "composition": {
                "O": 32,
                "Li": 64
            }
        }, {
            "correction": 0.0,
            "data": {},
            "energy": -17.02844794,
            "parameters": {},
            "composition": {
                "O": 2
            }
        }, {
            "correction": 0.0,
            "data": {},
            "energy": -959.64693323,
            "parameters": {},
            "composition": {
                "O": 72,
                "Li": 72
            }
        }]
        entries = []
        for e in d:
            entries.append(ComputedEntry.from_dict(e))
        rcts = list(
            filter(lambda e: e.composition.reduced_formula in ["Li", "O2"],
                   entries))
        prods = list(
            filter(lambda e: e.composition.reduced_formula == "Li2O2",
                   entries))

        self.rxn = ComputedReaction(rcts, prods)

    def test_calculated_reaction_energy(self):
        self.assertAlmostEqual(self.rxn.calculated_reaction_energy,
                               -5.60748821935)

    def test_init(self):
        self.assertEqual(str(self.rxn), "1.000 O2 + 2.000 Li -> 1.000 Li2O2")

    def test_to_from_dict(self):
        d = self.rxn.as_dict()
        new_rxn = ComputedReaction.from_dict(d)
        self.assertEqual(str(new_rxn), "1.000 O2 + 2.000 Li -> 1.000 Li2O2")
class ComputedReactionTest(unittest.TestCase):

    def setUp(self):
        d = [{"correction": 0.0, "data": {}, "energy": -108.56492362,
              "parameters": {}, "composition": {"Li": 54}},
             {"correction": 0.0, "data": {}, "energy": -577.94689128,
              "parameters": {}, "composition": {"O": 32, "Li": 64}},
             {"correction": 0.0, "data": {}, "energy": -17.02844794,
              "parameters": {}, "composition": {"O": 2}},
             {"correction": 0.0, "data": {}, "energy": -959.64693323,
              "parameters": {}, "composition": {"O": 72, "Li": 72}}]
        entries = []
        for e in d:
            entries.append(ComputedEntry.from_dict(e))
        rcts = list(filter(lambda e: e.composition.reduced_formula in ["Li", "O2"],
                      entries))
        prods = list(filter(lambda e: e.composition.reduced_formula == "Li2O2",
                       entries))

        self.rxn = ComputedReaction(rcts, prods)

    def test_calculated_reaction_energy(self):
        self.assertAlmostEqual(self.rxn.calculated_reaction_energy,
                               - 5.60748821935)

    def test_init(self):
        self.assertEqual(str(self.rxn), "1.000 O2 + 2.000 Li -> 1.000 Li2O2")

    def test_to_from_dict(self):
        d = self.rxn.as_dict()
        new_rxn = ComputedReaction.from_dict(d)
        self.assertEqual(str(new_rxn), "1.000 O2 + 2.000 Li -> 1.000 Li2O2")
    def setUp(self):
        d = [
            {"correction": 0.0, "data": {}, "energy": -108.56492362, "parameters": {}, "composition": {"Li": 54}},
            {
                "correction": 0.0,
                "data": {},
                "energy": -577.94689128,
                "parameters": {},
                "composition": {"O": 32, "Li": 64},
            },
            {"correction": 0.0, "data": {}, "energy": -17.02844794, "parameters": {}, "composition": {"O": 2}},
            {
                "correction": 0.0,
                "data": {},
                "energy": -959.64693323,
                "parameters": {},
                "composition": {"O": 72, "Li": 72},
            },
        ]
        entries = []
        for e in d:
            entries.append(ComputedEntry.from_dict(e))
        rcts = list(filter(lambda e: e.composition.reduced_formula in ["Li", "O2"], entries))
        prods = list(filter(lambda e: e.composition.reduced_formula == "Li2O2", entries))

        self.rxn = ComputedReaction(rcts, prods)
def get_full_evolution_profile(pd, entry1, entry2, x1, x2):
    """
    This function is used to solve the transition points along a path on convex hull.
    The essence is to use binary search, which is more accurate and faster than brutal force screening
    This is a recursive function.
    :param pd: PhaseDiagram of GrandPotentialPhaseDiagram
    :param entry1 & entry2: mixing entry1/entry2, PDEntry for pd_mixing, GrandPotEntry for gppd_mixing
    :param x1 & x2: The mixing ratio range for binary search.
    :return: An uncleaned but complete profile with all transition points.
    """
    evolution_profile = {}
    entry_left = get_mix_entry({entry1: x1, entry2: 1 - x1})
    entry_right = get_mix_entry({entry1: x2, entry2: 1 - x2})
    (decomp1, h1) = pd.get_decomp_and_e_above_hull(entry_left)
    (decomp2, h2) = pd.get_decomp_and_e_above_hull(entry_right)
    decomp1 = set(decomp1.keys())
    decomp2 = set(decomp2.keys())
    evolution_profile[x1] = (decomp1, h1)
    evolution_profile[x2] = (decomp2, h2)

    if decomp1 == decomp2:
        return evolution_profile

    intersect = decomp1 & decomp2
    if len(intersect) > 0:
        # This is try to catch a single transition point
        try:
            rxn = ComputedReaction([entry_left, entry_right], list(intersect))
            if not {entry_left, entry_right} < set(rxn.all_entries):
                return evolution_profile

            c1 = rxn.coeffs[rxn.all_entries.index(entry_left)]
            c2 = rxn.coeffs[rxn.all_entries.index(
                entry_right
            )]  # I know this is tedious but this is the only way I found that works..
            x = (c1 * x1 + c2 * x2) / (c1 + c2)
            if c1 * c2 == 0:
                return evolution_profile
            entry_mid = VirtualEntry.from_mixing({
                entry_left: c1 / (c1 + c2),
                entry_right: c2 / (c1 + c2)
            })
            h_mid = pd.get_decomp_and_e_above_hull(entry_mid)[1]
            evolution_profile[x] = (intersect, h_mid)
            return evolution_profile
        except ReactionError:
            pass

    x_mid = (x1 + x2) / 2.0
    entry_mid = get_mix_entry({entry1: 0.5, entry2: 0.5})
    (decomp_mid, h_mid) = pd.get_decomp_and_e_above_hull(entry_mid)
    decomp_mid = set(decomp_mid.keys())
    evolution_profile[x_mid] = (decomp_mid, h_mid)
    part1 = get_full_evolution_profile(pd, entry1, entry2, x1, x_mid)
    part2 = get_full_evolution_profile(pd, entry1, entry2, x_mid, x2)
    evolution_profile.update(part1)
    evolution_profile.update(part2)
    return evolution_profile
 def get_evolution_phases_table_string(self, open_el, pure_el_ref, PE_list, oe_amt_list, mu_trans_list, allowpmu):
     if not allowpmu:
         mu_h_list = [0] + mu_trans_list
     mu_l_list = mu_h_list[1:] + ['-inf']
     df = pandas.DataFrame()
     df['mu_high (eV)'] = mu_h_list
     df['mu_low (eV)'] = mu_l_list
     df['d(n_{})'.format(open_el)] = oe_amt_list
     PE_names = []
     rxns = []
     for PE in PE_list:
         rxn = ComputedReaction([self, pure_el_ref], PE)
         rxn.normalize_to(self.composition.reduced_composition)
         PE_names.append(', '.join(sorted([_.name for _ in PE])))
         rxns.append(str(rxn))
     df['Phase equilibria'] = PE_names
     df['Reaction'] = rxns
     print_df = df.to_string(index=False, float_format='{:,.2f}'.format, justify='center')
     return print_df
Example #11
0
    def test_calculated_reaction_energy_uncertainty_for_nan(self):
        # test that reaction_energy_uncertainty property is nan when the uncertainty
        # for any product/reactant is nan
        d = [
            {
                "correction": 0.0,
                "data": {},
                "energy": -108.56492362,
                "parameters": {},
                "composition": {"Li": 54},
            },
            {
                "correction": 0.0,
                "data": {},
                "energy": -17.02844794,
                "parameters": {},
                "composition": {"O": 2},
            },
            {
                "@module": "pymatgen.entries.computed_entries",
                "@class": "ComputedEntry",
                "energy": -38.76889738,
                "composition": defaultdict(float, {"Li": 4.0, "O": 4.0}),
                "energy_adjustments": [
                    {
                        "@module": "pymatgen.entries.computed_entries",
                        "@class": "ConstantEnergyAdjustment",
                        "@version": "2020.6.8",
                        "value": -1.864,
                        "uncertainty": np.nan,
                        "name": "MP2020 Composition Correction",
                        "cls": {
                            "@module": "pymatgen.entries.compatibility",
                            "@class": "MaterialsProject2020Compatibility",
                            "@version": "2020.6.8",
                            "compat_type": "Advanced",
                            "correct_peroxide": True,
                            "check_potcar_hash": False,
                        },
                        "description": "Constant energy adjustment (-1.864 eV)",
                    }
                ],
                "parameters": {
                    "run_type": "GGA",
                    "is_hubbard": False,
                    "pseudo_potential": {
                        "functional": "PBE",
                        "labels": ["Li_sv", "O"],
                        "pot_type": "paw",
                    },
                    "hubbards": {},
                    "potcar_symbols": ["PBE Li_sv", "PBE O"],
                    "oxide_type": "peroxide",
                },
                "data": {"oxide_type": "peroxide"},
                "entry_id": "mp-841",
                "correction": -1.864,
            },
        ]
        entries = []
        for e in d:
            entries.append(ComputedEntry.from_dict(e))
        rcts = list(
            filter(lambda e: e.composition.reduced_formula in ["Li", "O2"], entries)
        )
        prods = list(
            filter(lambda e: e.composition.reduced_formula == "Li2O2", entries)
        )

        rxn_with_uncertainty = ComputedReaction(rcts, prods)
        self.assertTrue(
            isnan(rxn_with_uncertainty.calculated_reaction_energy_uncertainty)
        )
 def test_to_from_dict(self):
     d = self.rxn.as_dict()
     new_rxn = ComputedReaction.from_dict(d)
     self.assertEqual(str(new_rxn), "1.000 O2 + 2.000 Li -> 1.000 Li2O2")
Example #13
0
try:
    stable_result = get_most_stable_entry(result)
except IndexError:
    error["error"] = "No structure with that formula"
    json_dir = output_path + "/" + "output.json"
    with open(json_dir, "w") as outfile:
        json.dump(error, outfile)
    print(error)
    exit()

# print stable_result.name, phase.get_form_energy(stable_result)
(compo,
 factor) = stable_result.composition.get_reduced_composition_and_factor()
form_energy[stable_result.name] = phase.get_form_energy(stable_result) / factor

reaction = ComputedReaction(stable_formula, [stable_result])
energy = FloatWithUnit(reaction.calculated_reaction_energy, "eV atom^-1")

json_result = dict()
json_result["reaction"] = str(reaction)

value1 = dict()
value1["value"] = energy.to("kJ mol^-1")
value1["unit"] = "kJ mol^-1"

value2 = dict()
value2["value"] = energy.to("eV atom^-1")
value2["unit"] = "eV"

json_result["value1"] = value1
json_result["value2"] = value2
    def test_calculated_reaction_energy_uncertainty_for_nan(self):
        # test that reaction_energy_uncertainty property is nan when the uncertainty
        # for any product/reactant is nan
        d = [
            {
                "correction": 0.0,
                "data": {},
                "energy": -108.56492362,
                "parameters": {},
                "composition": {
                    "Li": 54
                },
            },
            {
                "correction": 0.0,
                "data": {},
                "energy": -17.02844794,
                "parameters": {},
                "composition": {
                    "O": 2
                },
            },
            {
                '@module':
                'pymatgen.entries.computed_entries',
                '@class':
                'ComputedEntry',
                'energy':
                -38.76889738,
                'composition':
                defaultdict(float, {
                    'Li': 4.0,
                    'O': 4.0
                }),
                'energy_adjustments': [{
                    '@module':
                    'pymatgen.entries.computed_entries',
                    '@class':
                    'ConstantEnergyAdjustment',
                    '@version':
                    '2020.6.8',
                    'value':
                    -1.864,
                    'uncertainty':
                    np.nan,
                    'name':
                    'MP2020 Composition Correction',
                    'cls': {
                        '@module': 'pymatgen.entries.compatibility',
                        '@class': 'MaterialsProject2020Compatibility',
                        '@version': '2020.6.8',
                        'compat_type': 'Advanced',
                        'correct_peroxide': True,
                        'check_potcar_hash': False
                    },
                    'description':
                    'Constant energy adjustment (-1.864 eV)'
                }],
                'parameters': {
                    'run_type': 'GGA',
                    'is_hubbard': False,
                    'pseudo_potential': {
                        'functional': 'PBE',
                        'labels': ['Li_sv', 'O'],
                        'pot_type': 'paw'
                    },
                    'hubbards': {},
                    'potcar_symbols': ['PBE Li_sv', 'PBE O'],
                    'oxide_type': 'peroxide'
                },
                'data': {
                    'oxide_type': 'peroxide'
                },
                'entry_id':
                'mp-841',
                'correction':
                -1.864
            },
        ]
        entries = []
        for e in d:
            entries.append(ComputedEntry.from_dict(e))
        rcts = list(
            filter(lambda e: e.composition.reduced_formula in ["Li", "O2"],
                   entries))
        prods = list(
            filter(lambda e: e.composition.reduced_formula == "Li2O2",
                   entries))

        rxn_with_uncertainty = ComputedReaction(rcts, prods)
        self.assertTrue(
            isnan(rxn_with_uncertainty.calculated_reaction_energy_uncertainty))
class ComputedReactionTest(unittest.TestCase):
    def setUp(self):
        d = [
            {
                "correction": 0.0,
                "data": {},
                "energy": -108.56492362,
                "parameters": {},
                "composition": {
                    "Li": 54
                },
            },
            {
                "correction": 0.0,
                "data": {},
                "energy": -577.94689128,
                "parameters": {},
                "composition": {
                    "O": 32,
                    "Li": 64
                },
            },
            {
                "correction": 0.0,
                "data": {},
                "energy": -17.02844794,
                "parameters": {},
                "composition": {
                    "O": 2
                },
            },
            {
                "correction": 0.0,
                "data": {},
                "energy": -959.64693323,
                "parameters": {},
                "composition": {
                    "O": 72,
                    "Li": 72
                },
            },
        ]
        entries = []
        for e in d:
            entries.append(ComputedEntry.from_dict(e))
        rcts = list(
            filter(lambda e: e.composition.reduced_formula in ["Li", "O2"],
                   entries))
        prods = list(
            filter(lambda e: e.composition.reduced_formula == "Li2O2",
                   entries))

        self.rxn = ComputedReaction(rcts, prods)

    def test_calculated_reaction_energy(self):
        self.assertAlmostEqual(self.rxn.calculated_reaction_energy,
                               -5.60748821935)

    def test_calculated_reaction_energy_uncertainty(self):
        d = [
            {
                "correction": 0.0,
                "data": {},
                "energy": -108.56492362,
                "parameters": {},
                "composition": {
                    "Li": 54
                },
            },
            {
                "correction": 0.0,
                "data": {},
                "energy": -17.02844794,
                "parameters": {},
                "composition": {
                    "O": 2
                },
            },
            {
                '@module':
                'pymatgen.entries.computed_entries',
                '@class':
                'ComputedEntry',
                'energy':
                -38.76889738,
                'composition':
                defaultdict(float, {
                    'Li': 4.0,
                    'O': 4.0
                }),
                'energy_adjustments': [{
                    '@module':
                    'pymatgen.entries.computed_entries',
                    '@class':
                    'ConstantEnergyAdjustment',
                    '@version':
                    '2020.6.8',
                    'value':
                    -1.864,
                    'uncertainty':
                    0.0744,
                    'name':
                    'MP2020 Composition Correction',
                    'cls': {
                        '@module': 'pymatgen.entries.compatibility',
                        '@class': 'MaterialsProject2020Compatibility',
                        '@version': '2020.6.8',
                        'compat_type': 'Advanced',
                        'correct_peroxide': True,
                        'check_potcar_hash': False
                    },
                    'description':
                    'Constant energy adjustment (-1.864 eV)'
                }],
                'parameters': {
                    'run_type': 'GGA',
                    'is_hubbard': False,
                    'pseudo_potential': {
                        'functional': 'PBE',
                        'labels': ['Li_sv', 'O'],
                        'pot_type': 'paw'
                    },
                    'hubbards': {},
                    'potcar_symbols': ['PBE Li_sv', 'PBE O'],
                    'oxide_type': 'peroxide'
                },
                'data': {
                    'oxide_type': 'peroxide'
                },
                'entry_id':
                'mp-841',
                'correction':
                -1.864
            },
        ]
        entries = []
        for e in d:
            entries.append(ComputedEntry.from_dict(e))
        rcts = list(
            filter(lambda e: e.composition.reduced_formula in ["Li", "O2"],
                   entries))
        prods = list(
            filter(lambda e: e.composition.reduced_formula == "Li2O2",
                   entries))

        rxn_with_uncertainty = ComputedReaction(rcts, prods)
        self.assertAlmostEqual(
            rxn_with_uncertainty.calculated_reaction_energy_uncertainty,
            0.5 * 0.0744)

    def test_calculated_reaction_energy_uncertainty_for_no_uncertainty(self):
        # test that reaction_energy_uncertainty property doesn't cause errors
        # when products/reactants have no uncertainties
        self.assertAlmostEqual(self.rxn.calculated_reaction_energy_uncertainty,
                               0)

    def test_calculated_reaction_energy_uncertainty_for_nan(self):
        # test that reaction_energy_uncertainty property is nan when the uncertainty
        # for any product/reactant is nan
        d = [
            {
                "correction": 0.0,
                "data": {},
                "energy": -108.56492362,
                "parameters": {},
                "composition": {
                    "Li": 54
                },
            },
            {
                "correction": 0.0,
                "data": {},
                "energy": -17.02844794,
                "parameters": {},
                "composition": {
                    "O": 2
                },
            },
            {
                '@module':
                'pymatgen.entries.computed_entries',
                '@class':
                'ComputedEntry',
                'energy':
                -38.76889738,
                'composition':
                defaultdict(float, {
                    'Li': 4.0,
                    'O': 4.0
                }),
                'energy_adjustments': [{
                    '@module':
                    'pymatgen.entries.computed_entries',
                    '@class':
                    'ConstantEnergyAdjustment',
                    '@version':
                    '2020.6.8',
                    'value':
                    -1.864,
                    'uncertainty':
                    np.nan,
                    'name':
                    'MP2020 Composition Correction',
                    'cls': {
                        '@module': 'pymatgen.entries.compatibility',
                        '@class': 'MaterialsProject2020Compatibility',
                        '@version': '2020.6.8',
                        'compat_type': 'Advanced',
                        'correct_peroxide': True,
                        'check_potcar_hash': False
                    },
                    'description':
                    'Constant energy adjustment (-1.864 eV)'
                }],
                'parameters': {
                    'run_type': 'GGA',
                    'is_hubbard': False,
                    'pseudo_potential': {
                        'functional': 'PBE',
                        'labels': ['Li_sv', 'O'],
                        'pot_type': 'paw'
                    },
                    'hubbards': {},
                    'potcar_symbols': ['PBE Li_sv', 'PBE O'],
                    'oxide_type': 'peroxide'
                },
                'data': {
                    'oxide_type': 'peroxide'
                },
                'entry_id':
                'mp-841',
                'correction':
                -1.864
            },
        ]
        entries = []
        for e in d:
            entries.append(ComputedEntry.from_dict(e))
        rcts = list(
            filter(lambda e: e.composition.reduced_formula in ["Li", "O2"],
                   entries))
        prods = list(
            filter(lambda e: e.composition.reduced_formula == "Li2O2",
                   entries))

        rxn_with_uncertainty = ComputedReaction(rcts, prods)
        self.assertTrue(
            isnan(rxn_with_uncertainty.calculated_reaction_energy_uncertainty))

    def test_init(self):
        self.assertEqual(str(self.rxn), "2 Li + O2 -> Li2O2")

    def test_to_from_dict(self):
        d = self.rxn.as_dict()
        new_rxn = ComputedReaction.from_dict(d)
        self.assertEqual(str(new_rxn), "2 Li + O2 -> Li2O2")

    def test_all_entries(self):
        for c, e in zip(self.rxn.coeffs, self.rxn.all_entries):
            if c > 0:
                self.assertEqual(e.composition.reduced_formula, "Li2O2")
                self.assertAlmostEqual(e.energy, -959.64693323)
#This method simply gets the lowest energy entry for all entry with the same composition.
def get_most_stable_entry(formula):
    relevant_entries = [
        entry for entry in all_entries if entry.composition.reduced_formula ==
        Composition(formula).reduced_formula
    ]
    relevant_entries = sorted(relevant_entries,
                              key=lambda e: e.energy_per_atom)
    return relevant_entries[0]


CaO = get_most_stable_entry("CaO")
CO2 = get_most_stable_entry("CO2")
CaCO3 = get_most_stable_entry("CaCO3")

reaction = ComputedReaction([CaO, CO2], [CaCO3])

print("Caculated")
print(reaction)
print("Reaction energy = {:.2f}".format(reaction.calculated_reaction_energy *
                                        EV_PER_ATOM_TO_KJ_PER_MOL)
      )  #Conversion needed since our computed energies are in eV.
print()

# The following portions demonstrate how to get the experimental values as well.
exp_CaO = a.get_exp_entry("CaO")
exp_CaCO3 = a.get_exp_entry("CaCO3")

#Unfortunately, the Materials Project database does not have gas phase experimental entries. This is the value from NIST. We manually create the entry.
#Exp entries should be in kJ/mol.
exp_CO2 = ComputedEntry("CO2", -393.51)
Example #17
0
    def compute_corrections(self, exp_entries: list,
                            calc_entries: dict) -> dict:
        """
        Computes the corrections and fills in correction, corrections_std_error, and corrections_dict.

        Args:
            exp_entries: list of dictionary objects with the following keys/values:
                    {"formula": chemical formula, "exp energy": formation energy in eV/formula unit,
                    "uncertainty": uncertainty in formation energy}
            calc_entries: dictionary of computed entries, of the form {chemical formula: ComputedEntry}

        Raises:
            ValueError: calc_compounds is missing an entry
        """

        self.exp_compounds = exp_entries
        self.calc_compounds = calc_entries

        self.names: List[str] = []
        self.diffs: List[float] = []
        self.coeff_mat: List[List[float]] = []
        self.exp_uncer: List[float] = []

        # remove any corrections in calc_compounds
        for entry in self.calc_compounds.values():
            entry.correction = 0

        for cmpd_info in self.exp_compounds:

            # to get consistent element ordering in formula
            name = Composition(cmpd_info["formula"]).reduced_formula

            allow = True

            compound = self.calc_compounds.get(name, None)
            if not compound:
                warnings.warn(
                    "Compound {} is not found in provided computed entries and is excluded from the fit"
                    .format(name))
                continue

            # filter out compounds with large uncertainties
            relative_uncertainty = abs(cmpd_info["uncertainty"] /
                                       cmpd_info["exp energy"])
            if relative_uncertainty > self.max_error:
                allow = False
                warnings.warn(
                    "Compound {} is excluded from the fit due to high experimental uncertainty ({}%)"
                    .format(name, relative_uncertainty))

            # filter out compounds containing certain polyanions
            for anion in self.exclude_polyanions:
                if anion in name or anion in cmpd_info["formula"]:
                    allow = False
                    warnings.warn(
                        "Compound {} contains the polyanion {} and is excluded from the fit"
                        .format(name, anion))
                    break

            # filter out compounds that are unstable
            if isinstance(self.allow_unstable, float):
                try:
                    eah = compound.data["e_above_hull"]
                except KeyError:
                    raise ValueError("Missing e above hull data")
                if eah > self.allow_unstable:
                    allow = False
                    warnings.warn(
                        "Compound {} is unstable and excluded from the fit (e_above_hull = {})"
                        .format(name, eah))

            if allow:
                comp = Composition(name)
                elems = list(comp.as_dict())

                reactants = []
                for elem in elems:
                    try:
                        elem_name = Composition(elem).reduced_formula
                        reactants.append(self.calc_compounds[elem_name])
                    except KeyError:
                        raise ValueError("Computed entries missing " + elem)

                rxn = ComputedReaction(reactants, [compound])
                rxn.normalize_to(comp)
                energy = rxn.calculated_reaction_energy

                coeff = []
                for specie in self.species:
                    if specie == "oxide":
                        if compound.data["oxide_type"] == "oxide":
                            coeff.append(comp["O"])
                            self.oxides.append(name)
                        else:
                            coeff.append(0)
                    elif specie == "peroxide":
                        if compound.data["oxide_type"] == "peroxide":
                            coeff.append(comp["O"])
                            self.peroxides.append(name)
                        else:
                            coeff.append(0)
                    elif specie == "superoxide":
                        if compound.data["oxide_type"] == "superoxide":
                            coeff.append(comp["O"])
                            self.superoxides.append(name)
                        else:
                            coeff.append(0)
                    elif specie == "S":
                        if Element("S") in comp:
                            sf_type = "sulfide"
                            if compound.data.get("sulfide_type"):
                                sf_type = compound.data["sulfide_type"]
                            elif hasattr(compound, "structure"):
                                sf_type = sulfide_type(compound.structure)
                            if sf_type == "sulfide":
                                coeff.append(comp["S"])
                                self.sulfides.append(name)
                            else:
                                coeff.append(0)
                        else:
                            coeff.append(0)
                    else:
                        try:
                            coeff.append(comp[specie])
                        except ValueError:
                            raise ValueError(
                                "We can't detect this specie: {}".format(
                                    specie))

                self.names.append(name)
                self.diffs.append(
                    (cmpd_info["exp energy"] - energy) / comp.num_atoms)
                self.coeff_mat.append([i / comp.num_atoms for i in coeff])
                self.exp_uncer.append(
                    (cmpd_info["uncertainty"]) / comp.num_atoms)

        # for any exp entries with no uncertainty value, assign average uncertainty value
        sigma = np.array(self.exp_uncer)
        sigma[sigma == 0] = np.nan

        with warnings.catch_warnings():
            warnings.simplefilter(
                "ignore", category=RuntimeWarning
            )  # numpy raises warning if the entire array is nan values
            mean_uncer = np.nanmean(sigma)

        sigma = np.where(np.isnan(sigma), mean_uncer, sigma)

        if np.isnan(mean_uncer):
            # no uncertainty values for any compounds, don't try to weight
            popt, self.pcov = curve_fit(_func,
                                        self.coeff_mat,
                                        self.diffs,
                                        p0=np.ones(len(self.species)))
        else:
            popt, self.pcov = curve_fit(
                _func,
                self.coeff_mat,
                self.diffs,
                p0=np.ones(len(self.species)),
                sigma=sigma,
                absolute_sigma=True,
            )
        self.corrections = popt.tolist()
        self.corrections_std_error = np.sqrt(np.diag(self.pcov)).tolist()
        for i in range(len(self.species)):
            self.corrections_dict[self.species[i]] = (
                round(self.corrections[i], 3),
                round(self.corrections_std_error[i], 4),
            )
        return self.corrections_dict
Example #18
0
class ComputedReactionTest(unittest.TestCase):
    def setUp(self):
        d = [
            {
                "correction": 0.0,
                "data": {},
                "energy": -108.56492362,
                "parameters": {},
                "composition": {"Li": 54},
            },
            {
                "correction": 0.0,
                "data": {},
                "energy": -577.94689128,
                "parameters": {},
                "composition": {"O": 32, "Li": 64},
            },
            {
                "correction": 0.0,
                "data": {},
                "energy": -17.02844794,
                "parameters": {},
                "composition": {"O": 2},
            },
            {
                "correction": 0.0,
                "data": {},
                "energy": -959.64693323,
                "parameters": {},
                "composition": {"O": 72, "Li": 72},
            },
        ]
        entries = []
        for e in d:
            entries.append(ComputedEntry.from_dict(e))
        rcts = list(
            filter(lambda e: e.composition.reduced_formula in ["Li", "O2"], entries)
        )
        prods = list(
            filter(lambda e: e.composition.reduced_formula == "Li2O2", entries)
        )

        self.rxn = ComputedReaction(rcts, prods)

    def test_calculated_reaction_energy(self):
        self.assertAlmostEqual(self.rxn.calculated_reaction_energy, -5.60748821935)

    def test_calculated_reaction_energy_uncertainty(self):
        d = [
            {
                "correction": 0.0,
                "data": {},
                "energy": -108.56492362,
                "parameters": {},
                "composition": {"Li": 54},
            },
            {
                "correction": 0.0,
                "data": {},
                "energy": -17.02844794,
                "parameters": {},
                "composition": {"O": 2},
            },
            {
                "@module": "pymatgen.entries.computed_entries",
                "@class": "ComputedEntry",
                "energy": -38.76889738,
                "composition": defaultdict(float, {"Li": 4.0, "O": 4.0}),
                "energy_adjustments": [
                    {
                        "@module": "pymatgen.entries.computed_entries",
                        "@class": "ConstantEnergyAdjustment",
                        "@version": "2020.6.8",
                        "value": -1.864,
                        "uncertainty": 0.0744,
                        "name": "MP2020 Composition Correction",
                        "cls": {
                            "@module": "pymatgen.entries.compatibility",
                            "@class": "MaterialsProject2020Compatibility",
                            "@version": "2020.6.8",
                            "compat_type": "Advanced",
                            "correct_peroxide": True,
                            "check_potcar_hash": False,
                        },
                        "description": "Constant energy adjustment (-1.864 eV)",
                    }
                ],
                "parameters": {
                    "run_type": "GGA",
                    "is_hubbard": False,
                    "pseudo_potential": {
                        "functional": "PBE",
                        "labels": ["Li_sv", "O"],
                        "pot_type": "paw",
                    },
                    "hubbards": {},
                    "potcar_symbols": ["PBE Li_sv", "PBE O"],
                    "oxide_type": "peroxide",
                },
                "data": {"oxide_type": "peroxide"},
                "entry_id": "mp-841",
                "correction": -1.864,
            },
        ]
        entries = []
        for e in d:
            entries.append(ComputedEntry.from_dict(e))
        rcts = list(
            filter(lambda e: e.composition.reduced_formula in ["Li", "O2"], entries)
        )
        prods = list(
            filter(lambda e: e.composition.reduced_formula == "Li2O2", entries)
        )

        rxn_with_uncertainty = ComputedReaction(rcts, prods)
        self.assertAlmostEqual(
            rxn_with_uncertainty.calculated_reaction_energy_uncertainty, 0.5 * 0.0744
        )

    def test_calculated_reaction_energy_uncertainty_for_no_uncertainty(self):
        # test that reaction_energy_uncertainty property doesn't cause errors
        # when products/reactants have no uncertainties
        self.assertAlmostEqual(self.rxn.calculated_reaction_energy_uncertainty, 0)

    def test_calculated_reaction_energy_uncertainty_for_nan(self):
        # test that reaction_energy_uncertainty property is nan when the uncertainty
        # for any product/reactant is nan
        d = [
            {
                "correction": 0.0,
                "data": {},
                "energy": -108.56492362,
                "parameters": {},
                "composition": {"Li": 54},
            },
            {
                "correction": 0.0,
                "data": {},
                "energy": -17.02844794,
                "parameters": {},
                "composition": {"O": 2},
            },
            {
                "@module": "pymatgen.entries.computed_entries",
                "@class": "ComputedEntry",
                "energy": -38.76889738,
                "composition": defaultdict(float, {"Li": 4.0, "O": 4.0}),
                "energy_adjustments": [
                    {
                        "@module": "pymatgen.entries.computed_entries",
                        "@class": "ConstantEnergyAdjustment",
                        "@version": "2020.6.8",
                        "value": -1.864,
                        "uncertainty": np.nan,
                        "name": "MP2020 Composition Correction",
                        "cls": {
                            "@module": "pymatgen.entries.compatibility",
                            "@class": "MaterialsProject2020Compatibility",
                            "@version": "2020.6.8",
                            "compat_type": "Advanced",
                            "correct_peroxide": True,
                            "check_potcar_hash": False,
                        },
                        "description": "Constant energy adjustment (-1.864 eV)",
                    }
                ],
                "parameters": {
                    "run_type": "GGA",
                    "is_hubbard": False,
                    "pseudo_potential": {
                        "functional": "PBE",
                        "labels": ["Li_sv", "O"],
                        "pot_type": "paw",
                    },
                    "hubbards": {},
                    "potcar_symbols": ["PBE Li_sv", "PBE O"],
                    "oxide_type": "peroxide",
                },
                "data": {"oxide_type": "peroxide"},
                "entry_id": "mp-841",
                "correction": -1.864,
            },
        ]
        entries = []
        for e in d:
            entries.append(ComputedEntry.from_dict(e))
        rcts = list(
            filter(lambda e: e.composition.reduced_formula in ["Li", "O2"], entries)
        )
        prods = list(
            filter(lambda e: e.composition.reduced_formula == "Li2O2", entries)
        )

        rxn_with_uncertainty = ComputedReaction(rcts, prods)
        self.assertTrue(
            isnan(rxn_with_uncertainty.calculated_reaction_energy_uncertainty)
        )

    def test_init(self):
        self.assertEqual(str(self.rxn), "2 Li + O2 -> Li2O2")

    def test_to_from_dict(self):
        d = self.rxn.as_dict()
        new_rxn = ComputedReaction.from_dict(d)
        self.assertEqual(str(new_rxn), "2 Li + O2 -> Li2O2")

    def test_all_entries(self):
        for c, e in zip(self.rxn.coeffs, self.rxn.all_entries):
            if c > 0:
                self.assertEqual(e.composition.reduced_formula, "Li2O2")
                self.assertAlmostEqual(e.energy, -959.64693323)
def find_theo_redenth(compstr):
    """
    Finds theoretical redox enthalpies from the Materials Project from perovskite to brownmillerite
    based partially on https://github.com/materialsproject/pymatgen/blob/b3e972e293885c5b3c69fb3e9aa55287869d4d84/
    examples/Calculating%20Reaction%20Energies%20with%20the%20Materials%20API.ipynb

    :param compstr: composition as a string

    :return:
    red_enth:  redox enthalpy in kJ/mol O
    """
    compstr_perovskite = compstr.split("O")[0] + "O3"

    comp_spl = split_comp(compstr)
    chem_sys = ""
    for i in range(len(comp_spl)):
        if comp_spl[i] is not None:
            chem_sys = chem_sys + comp_spl[i][0] + "-"
    chem_sys = chem_sys + "O"
    chem_sys = chem_sys.split("-")

    all_entries = mpr.get_entries_in_chemsys(chem_sys)

    # This method simply gets the lowest energy entry for all entries with the same composition.
    def get_most_stable_entry(formula):
        relevant_entries = [entry for entry in all_entries if
        entry.composition.reduced_formula == Composition(formula).reduced_formula]
        relevant_entries = sorted(relevant_entries, key=lambda e: e.energy_per_atom)
        return relevant_entries[0]

    formula_spl = [''.join(g) for _, g in groupby(str(compstr), str.isalpha)]
    perov_formula = []
    for k in range(len(formula_spl)):
        try:
            perov_formula += str(int(float(formula_spl[k]) * 8))
        except ValueError:
            perov_formula += str(formula_spl[k])
    perov_formula = "".join(perov_formula)
    perov_formula = str(perov_formula).split("O")[0] + "O24"
    perovskite = get_most_stable_entry(perov_formula)

    brownm_formula = []
    for k in range(len(formula_spl)):
        try:
            brownm_formula += str(int(float(formula_spl[k]) * 32))
        except ValueError:
            brownm_formula += str(formula_spl[k])
    brownm_formula = "".join(brownm_formula)
    brownm_formula = str(brownm_formula).split("O")[0] + "O80"
    brownmillerite = get_most_stable_entry(brownm_formula)

    # for oxygen: do not use the most stable phase O8 but the most stable O2 phase
    def get_oxygen():
        relevant_entries = [entry for entry in all_entries if
        entry.composition == Composition("O2")]
        relevant_entries = sorted(relevant_entries, key=lambda e: e.energy_per_atom)
        return relevant_entries[0]

    oxygen = get_oxygen()

    reaction = ComputedReaction([perovskite], [brownmillerite, oxygen])
    energy = FloatWithUnit(reaction.calculated_reaction_energy, "eV atom^-1")

    # figure out the stoichiometry of O2 in the reaction equation in order to normalize the energies per mol of O
    try:
        o_stoich = float(str(str(reaction.as_dict).split(" O2")[0]).split()[-1])
    except ValueError:
        o_stoich = 1
    # energy in J/mol per mol of O2
    ener = (float(energy.to("kJ mol^-1")) * 1000) / o_stoich
    # per mol of O
    ener = ener / 2

    return ener
Example #20
0
 def get_gibbs_rxn(self, rxn):
     reactant_gibbs_entries = self.get_gibbs_computed_structure_entries(
         rxn._reactant_entries)
     product_gibbs_entries = self.get_gibbs_computed_structure_entries(
         rxn._product_entries)
     return ComputedReaction(reactant_gibbs_entries, product_gibbs_entries)
 def test_to_from_dict(self):
     d = self.rxn.as_dict()
     new_rxn = ComputedReaction.from_dict(d)
     self.assertEqual(str(new_rxn), "O2 + 2 Li -> Li2O2")
 def test_to_from_dict(self):
     d = self.rxn.to_dict
     new_rxn = ComputedReaction.from_dict(d)
     self.assertEqual(str(new_rxn), "1.000 O2 + 2.000 Li -> 1.000 Li2O2")