Пример #1
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.compatibility.process_entries(item)

        try:
            pd = PhaseDiagram(entries)

            docs = []

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

                d = {
                    self.thermo.key: e.entry_id,
                    "thermo": {
                        "formation_energy_per_atom":
                        pd.get_form_energy_per_atom(e),
                        "e_above_hull": ehull,
                        "is_stable": e in pd.stable_entries
                    }
                }

                # Logic for if stable or decomposes
                if d["thermo"]["is_stable"]:
                    d["thermo"][
                        "eq_reaction_e"] = pd.get_equilibrium_reaction_energy(
                            e)
                else:
                    d["thermo"]["decomposes_to"] = [{
                        "task_id": de.entry_id,
                        "formula": de.composition.formula,
                        "amount": amt
                    } for de, amt in decomp.items()]

                d["thermo"]["entry"] = e.as_dict()
                d["thermo"][
                    "explanation"] = self.compatibility.get_explanation_dict(e)

                elsyms = sorted(
                    set([el.symbol for el in e.composition.elements]))
                d["chemsys"] = "-".join(elsyms)
                d["nelements"] = len(elsyms)
                d["elements"] = list(elsyms)

                docs.append(d)
        except PhaseDiagramError as p:
            print(e.as_dict())
            self.logger.warning("Phase diagram error: {}".format(p))
            return []

        return docs
Пример #2
0
    def from_entries(cls, entries: List[ComputedEntry], sandboxes=None):

        pd = PhaseDiagram(entries)
        sandboxes = sandboxes or ["core"]

        docs = []

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

            d = {
                "material_id":
                e.entry_id,
                "uncorrected_energy_per_atom":
                e.uncorrected_energy / e.composition.num_atoms,
                "energy_per_atom":
                e.uncorrected_energy / e.composition.num_atoms,
                "formation_energy_per_atom":
                pd.get_form_energy_per_atom(e),
                "energy_above_hull":
                ehull,
                "is_stable":
                e in pd.stable_entries,
                "sandboxes":
                sandboxes,
            }

            if "last_updated" in e.data:
                d["last_updated"] = e.data["last_updated"]

            # Store different info if stable vs decomposes
            if d["is_stable"]:
                d["equillibrium_reaction_energy_per_atom"] = pd.get_equilibrium_reaction_energy(
                    e)
            else:
                d["decomposes_to"] = [{
                    "material_id": de.entry_id,
                    "formula": de.composition.formula,
                    "amount": amt,
                } for de, amt in decomp.items()]

            d["energy_type"] = e.parameters.get("run_type", "Unknown")
            d["entry_types"] = [e.parameters.get("run_type", "Unknown")]
            d["entries"] = {e.parameters.get("run_type", ""): e}

            for k in ["last_updated"]:
                if k in e.parameters:
                    d[k] = e.parameters[k]
                elif k in e.data:
                    d[k] = e.data[k]

            docs.append(
                ThermoDoc.from_composition(composition=e.composition, **d))

        return docs
Пример #3
0
    def process_item(self, item):
        """
        Process the list of entries into thermo docs for each sandbox
        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
        """

        docs = []

        sandboxes, entries = item
        entries = self.compatibility.process_entries(entries)

        # determine chemsys
        chemsys = "-".join(
            sorted(
                set([
                    el.symbol for e in entries for el in e.composition.elements
                ])))

        self.logger.debug(
            f"Procesing {len(entries)} entries for {chemsys} - {sandboxes}")

        try:
            pd = PhaseDiagram(entries)

            docs = []

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

                d = {
                    self.thermo.key: e.entry_id,
                    "thermo": {
                        "energy": e.uncorrected_energy,
                        "energy_per_atom":
                        e.uncorrected_energy / e.composition.num_atoms,
                        "formation_energy_per_atom":
                        pd.get_form_energy_per_atom(e),
                        "e_above_hull": ehull,
                        "is_stable": e in pd.stable_entries,
                    },
                }

                # Store different info if stable vs decomposes
                if d["thermo"]["is_stable"]:
                    d["thermo"][
                        "eq_reaction_e"] = pd.get_equilibrium_reaction_energy(
                            e)
                else:
                    d["thermo"]["decomposes_to"] = [{
                        "task_id": de.entry_id,
                        "formula": de.composition.formula,
                        "amount": amt,
                    } for de, amt in decomp.items()]

                d["thermo"]["entry"] = e.as_dict()
                d["thermo"][
                    "explanation"] = self.compatibility.get_explanation_dict(e)

                elsyms = sorted(
                    set([el.symbol for el in e.composition.elements]))
                d["chemsys"] = "-".join(elsyms)
                d["nelements"] = len(elsyms)
                d["elements"] = list(elsyms)
                d["_sbxn"] = list(sandboxes)

                docs.append(d)
        except PhaseDiagramError as p:
            elsyms = []
            for e in entries:
                elsyms.extend([el.symbol for el in e.composition.elements])

            self.logger.warning("Phase diagram errorin chemsys {}: {}".format(
                "-".join(sorted(set(elsyms))), p))
            return []

        return docs
Пример #4
0
    def present(self,
                df=None,
                new_result_ids=None,
                all_result_ids=None,
                filename=None,
                save_hull_distance=False,
                finalize=False):
        """
        Generate plots of convex hulls for each of the runs

        Args:
            df (DataFrame): dataframe with formation energies, compositions, ids
            new_result_ids ([]): list of new result ids (i. e. indexes
                in the updated dataframe)
            all_result_ids ([]): list of all result ids associated
                with the current run
            filename (str): filename to output, if None, no file output
                is produced

        Returns:
            (pyplot): plotter instance
        """
        df = df if df is not None else self.df
        new_result_ids = new_result_ids if new_result_ids is not None \
            else self.new_result_ids
        all_result_ids = all_result_ids if all_result_ids is not None \
            else self.all_result_ids

        # TODO: consolidate duplicated code here
        # Generate all entries
        comps = df.loc[all_result_ids]['Composition'].dropna()
        system_elements = []
        for comp in comps:
            system_elements += list(Composition(comp).as_dict().keys())
        elems = set(system_elements)
        if len(elems) > 4:
            warnings.warn(
                "Number of elements too high for phase diagram plotting")
            return None
        ind_to_include = []
        for ind in df.index:
            if set(Composition(
                    df.loc[ind]['Composition']).as_dict().keys()).issubset(
                        elems):
                ind_to_include.append(ind)
        _df = df.loc[ind_to_include]

        # Create computed entry column
        _df['entry'] = [
            ComputedEntry(
                Composition(row['Composition']),
                row['delta_e'] * Composition(
                    row['Composition']).num_atoms,  # un-normalize the energy
                entry_id=index) for index, row in _df.iterrows()
        ]
        # Partition ids into sets of prior to CAMD run, from CAMD but prior to
        # current iteration, and new ids
        ids_prior_to_camd = list(set(_df.index) - set(all_result_ids))
        ids_prior_to_run = list(set(all_result_ids) - set(new_result_ids))

        # Create phase diagram based on everything prior to current run
        entries = list(_df.loc[ids_prior_to_run + ids_prior_to_camd]['entry'])
        # Filter for nans by checking if it's a computed entry
        entries = [
            entry for entry in entries if isinstance(entry, ComputedEntry)
        ]
        pg_elements = [Element(el) for el in sorted(elems)]
        pd = PhaseDiagram(entries, elements=pg_elements)
        plotkwargs = {
            "markerfacecolor": "white",
            "markersize": 7,
            "linewidth": 2,
        }
        if finalize:
            plotkwargs.update({'linestyle': '--'})
        else:
            plotkwargs.update({'linestyle': '-'})
        plotter = PDPlotter(pd, **plotkwargs)

        getplotkwargs = {"label_stable": False} if finalize else {}
        plot = plotter.get_plot(**getplotkwargs)
        # Get valid results
        valid_results = [
            new_result_id for new_result_id in new_result_ids
            if new_result_id in _df.index
        ]

        if finalize:
            # If finalize, we'll reset pd to all entries at this point
            # to measure stabilities wrt. the ultimate hull.
            pd = PhaseDiagram(_df['entry'].values, elements=pg_elements)
            plotter = PDPlotter(
                pd, **{
                    "markersize": 0,
                    "linestyle": "-",
                    "linewidth": 2
                })
            plot = plotter.get_plot(plt=plot)

        for entry in _df['entry'][valid_results]:
            decomp, e_hull = pd.get_decomp_and_e_above_hull(
                entry, allow_negative=True)
            if e_hull < self.hull_distance:
                color = 'g'
                marker = 'o'
                markeredgewidth = 1
            else:
                color = 'r'
                marker = 'x'
                markeredgewidth = 1

            # Get coords
            coords = [
                entry.composition.get_atomic_fraction(el) for el in pd.elements
            ][1:]
            if pd.dim == 2:
                coords = coords + [pd.get_form_energy_per_atom(entry)]
            if pd.dim == 3:
                coords = triangular_coord(coords)
            elif pd.dim == 4:
                coords = tet_coord(coords)
            plot.plot(*coords,
                      marker=marker,
                      markeredgecolor=color,
                      markerfacecolor="None",
                      markersize=11,
                      markeredgewidth=markeredgewidth)

        if filename is not None:
            plot.savefig(filename, dpi=70)
        plot.close()

        if filename is not None and save_hull_distance:
            if self.stabilities is None:
                print("ERROR: No stability information in analyzer.")
                return None
            with open(filename.split(".")[0] + '.json', 'w') as f:
                json.dump(self.stabilities, f)