Exemplo n.º 1
0
    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
Exemplo n.º 2
0
    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
Exemplo n.º 3
0
 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
Exemplo n.º 4
0
 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
Exemplo n.º 5
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