Beispiel #1
0
def parse_composition(structure_type, s, ctype):
    toks = s.strip().split()
    if len(toks) == 1:
        c = Composition({toks[0].split(":")[0]: 1})
    else:
        c = Composition(
            {t.split(":")[0]: float(t.split(":")[1])
             for t in toks})
        c = Composition({k2: v2 / sum(c.values()) for k2, v2 in c.items()})
        if len(c) != 2:
            raise ValueError("Bad composition on %s." % ctype)
        frac = [c.get_atomic_fraction(k) for k in c.keys()]

        if structure_type == 'garnet':
            if ctype == "A":
                if abs(frac[0] - 0.5) > 0.01:
                    raise ValueError("Bad composition on %s. "
                                     "Only 1:1 mixing allowed!" % ctype)
            elif ctype in ["C", "D"]:
                if not (abs(frac[0] - 1.0 / 3) < 0.01
                        or abs(frac[1] - 1.0 / 3) < 0.01):
                    raise ValueError("Bad composition on %s. "
                                     "Only 2:1 mixing allowed!" % ctype)
        elif structure_type == 'perovskite':
            if abs(frac[0] - 0.5) > 0.01:
                raise ValueError("Bad composition on %s. "
                                 "Only 1:1 mixing allowed!" % ctype)
    try:
        for k in c.keys():
            k.oxi_state
            if k not in ELS[structure_type][ctype]:
                raise ValueError("%s is not a valid species for %s site." %
                                 (k, ctype))
    except AttributeError:
        raise ValueError("Oxidation states must be specified for all species!")

    return c
Beispiel #2
0
    def plot_hull(self, df, new_result_ids, filename=None, finalize=False):
        """
        Generate plots of convex hulls for each of the runs

        Args:
            df (DataFrame): dataframe with formation energies and formulas
            new_result_ids ([]): list of new result ids (i. e. indexes
                in the updated dataframe)
            filename (str): filename to output, if None, no file output
                is produced
            finalize (bool): flag indicating whether to include all new results

        Returns:
            (pyplot): plotter instance
        """
        # Generate all entries
        total_comp = Composition(df['Composition'].sum())
        if len(total_comp) > 4:
            warnings.warn(
                "Number of elements too high for phase diagram plotting")
            return None
        filtered = filter_dataframe_by_composition(df, total_comp)
        filtered = filtered[['delta_e', 'Composition']]
        filtered = filtered.dropna()

        # Create computed entry column with un-normalized energies
        filtered["entry"] = [
            ComputedEntry(
                Composition(row["Composition"]),
                row["delta_e"] * Composition(row["Composition"]).num_atoms,
                entry_id=index,
            ) for index, row in filtered.iterrows()
        ]

        ids_prior_to_run = list(set(filtered.index) - set(new_result_ids))
        if not ids_prior_to_run:
            warnings.warn(
                "No prior data, prior phase diagram cannot be constructed")
            return None

        # Create phase diagram based on everything prior to current run
        entries = filtered.loc[ids_prior_to_run]["entry"].dropna()

        # Filter for nans by checking if it's a computed entry
        pg_elements = sorted(total_comp.keys())
        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, backend='matplotlib', **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 filtered.index
        ]

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

        for entry in filtered["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()
Beispiel #3
0
    def __getitem__(self, idx):
        """

        Returns
        -------
        atom_weights: torch.Tensor shape (M, 1)
            weights of atoms in the material
        atom_fea: torch.Tensor shape (M, n_fea)
            features of atoms in the material
        self_fea_idx: torch.Tensor shape (M*M, 1)
            list of self indices
        nbr_fea_idx: torch.Tensor shape (M*M, 1)
            list of neighbor indices
        target: torch.Tensor shape (1,)
            target value for material
        cry_id: torch.Tensor shape (1,)
            input id for the material
        """
        cry_id, composition, target = self.df.iloc[idx]
        comp_dict = Composition(composition).get_el_amt_dict()
        elements = list(comp_dict.keys())
        weights = list(comp_dict.values())
        weights = np.atleast_2d(weights).T / np.sum(weights)
        assert len(elements) != 1, f"cry-id {cry_id} [{composition}] is a pure system"
        try:
            atom_fea = np.vstack(
                [self.elem_features.get_fea(element) for element in elements]
            )
        except AssertionError:
            raise AssertionError(
                f"cry-id {cry_id} [{composition}] contains element types not in embedding"
            )
        except ValueError:
            raise ValueError(
                f"cry-id {cry_id} [{composition}] composition cannot be parsed into elements"
            )

        env_idx = list(range(len(elements)))
        self_fea_idx = []
        nbr_fea_idx = []
        nbrs = len(elements) - 1
        for i, _ in enumerate(elements):
            self_fea_idx += [i] * nbrs
            nbr_fea_idx += env_idx[:i] + env_idx[i + 1 :]

        # convert all data to tensors
        atom_weights = torch.Tensor(weights)
        atom_fea = torch.Tensor(atom_fea)
        self_fea_idx = torch.LongTensor(self_fea_idx)
        nbr_fea_idx = torch.LongTensor(nbr_fea_idx)
        if self.task == "regression":
            targets = torch.Tensor([float(target)])
        elif self.task == "classification":
            targets = torch.LongTensor([target])

        return (
            (atom_weights, atom_fea, self_fea_idx, nbr_fea_idx),
            targets,
            composition,
            cry_id,
        )