Пример #1
0
 def setUp(self):
     entrylist = list()
     weights = list()
     comp = Composition("Mn2O3")
     entry = PDEntry(comp, 49)
     entrylist.append(PourbaixEntry(entry))
     weights.append(1.0)
     comp = Ion.from_formula("MnO4[-]")
     entry = IonEntry(comp, 25)
     entrylist.append(PourbaixEntry(entry))
     weights.append(0.25)
     comp = Composition("Fe2O3")
     entry = PDEntry(comp, 50)
     entrylist.append(PourbaixEntry(entry))
     weights.append(0.5)
     comp = Ion.from_formula("Fe[2+]")
     entry = IonEntry(comp, 15)
     entrylist.append(PourbaixEntry(entry))
     weights.append(2.5)
     comp = Ion.from_formula("Fe[3+]")
     entry = IonEntry(comp, 20)
     entrylist.append(PourbaixEntry(entry))
     weights.append(1.5)
     self.weights = weights
     self.entrylist = entrylist
     self.multientry = MultiEntry(entrylist, weights)
Пример #2
0
def get_e_above_hull(formula, energy):
    atoms = Atoms(formula)
    full_symbols = atoms.get_chemical_symbols()
    symbols, counts = np.unique(full_symbols, return_counts=True)

    # list(set(atoms.get_chemical_symbols()))

    with MPRester() as m:
        data = m.get_entries_in_chemsys(symbols,
                                        compatible_only=True,
                                        property_data=[
                                            'energy_per_atom',
                                            'unit_cell_formula',
                                            'pretty_formula'
                                        ])
    PDentries = []

    for d in data:
        d = d.as_dict()
        PDentries += [PDEntry(d['data']['unit_cell_formula'], d['energy'])]
        print(d['data']['pretty_formula'], d['energy'])

    PD = PhaseDiagram(PDentries)

    # Need to apply MP corrections to +U calculations
    # MP advanced correction + anion correction

    #print(energy * 2, energy * 2 +  corr_energy * 2)

    PDE0 = PDEntry(formula, energy)
    e_hull = PD.get_e_above_hull(PDE0)

    return e_hull
Пример #3
0
    def test_get_quasi_e_to_hull(self):
        for entry in self.pd.unstable_entries:
            # catch duplicated stable entries
            if entry.normalize(
                    inplace=False) in self.pd.get_stable_entries_normed():
                self.assertLessEqual(
                    self.pd.get_quasi_e_to_hull(entry), 0,
                    "Duplicated stable entries should have negative decomposition energy!"
                )
            else:
                self.assertGreaterEqual(
                    self.pd.get_quasi_e_to_hull(entry), 0,
                    "Unstable entries should have positive decomposition energy!"
                )

        for entry in self.pd.stable_entries:
            if entry.composition.is_element:
                self.assertEqual(
                    self.pd.get_quasi_e_to_hull(entry), 0,
                    "Stable elemental entries should have decomposition energy of zero!"
                )
            else:
                self.assertLessEqual(
                    self.pd.get_quasi_e_to_hull(entry), 0,
                    "Stable entries should have negative decomposition energy!"
                )

        novel_stable_entry = PDEntry("Li5FeO4", -999)
        self.assertLess(
            self.pd.get_quasi_e_to_hull(novel_stable_entry), 0,
            "Novel stable entries should have negative decomposition energy!")

        novel_unstable_entry = PDEntry("Li5FeO4", 999)
        self.assertGreater(
            self.pd.get_quasi_e_to_hull(novel_unstable_entry), 0,
            "Novel unstable entries should have positive decomposition energy!"
        )

        duplicate_entry = PDEntry("Li2O", -14.31361175)
        scaled_dup_entry = PDEntry("Li4O2", -14.31361175 * 2)
        stable_entry = [e for e in self.pd.stable_entries
                        if e.name == "Li2O"][0]

        self.assertEqual(
            self.pd.get_quasi_e_to_hull(duplicate_entry),
            self.pd.get_quasi_e_to_hull(stable_entry),
            "Novel duplicates of stable entries should have same decomposition energy!"
        )

        self.assertEqual(
            self.pd.get_quasi_e_to_hull(scaled_dup_entry),
            self.pd.get_quasi_e_to_hull(stable_entry),
            "Novel scaled duplicates of stable entries should have same decomposition energy!"
        )
Пример #4
0
def _pd(structures, energies, ce):
    """
    Generate a phase diagram with the structures and energies
    """
    entries = []

    for s, e in zip(structures, energies):
        entries.append(PDEntry(s.composition.element_composition, e))

    max_e = max(entries, key=lambda e: e.energy_per_atom).energy_per_atom + 1000
    for el in ce.structure.composition.keys():
        entries.append(PDEntry(Composition({el: 1}).element_composition, max_e))

    return PhaseDiagram(entries)
Пример #5
0
    def get_convex_hull_area(self, composition_space):
        """
        Prints out the area or volume of the convex hull defined by the
        organisms in the promotion set.
        """

        # make a phase diagram of just the organisms in the promotion set
        # (on the lower convex hull)
        pdentries = []
        for organism in self.promotion_set:
            pdentries.append(
                PDEntry(organism.composition, organism.total_energy))
        compound_pd = CompoundPhaseDiagram(pdentries,
                                           composition_space.endpoints)

        # get the data for the convex hull
        qhull_data = compound_pd.qhull_data
        # for some reason, the last point is positive, so remove it
        hull_data = np.delete(qhull_data, -1, 0)

        # make a ConvexHull object from the hull data
        try:
            convex_hull = ConvexHull(hull_data)
        except:
            return None
        if len(composition_space.endpoints) == 2:
            return convex_hull.area
        else:
            return convex_hull.volume
Пример #6
0
    def from_csv(cls, filename: str):
        """
        Imports PDEntries from a csv.

        Args:
            filename: Filename to import from.

        Returns:
            List of Elements, List of PDEntries
        """
        with open(filename, "r", encoding="utf-8") as f:
            reader = csv.reader(f,
                                delimiter=unicode2str(","),
                                quotechar=unicode2str("\""),
                                quoting=csv.QUOTE_MINIMAL)
            entries = list()
            header_read = False
            elements = None
            for row in reader:
                if not header_read:
                    elements = row[1:(len(row) - 1)]
                    header_read = True
                else:
                    name = row[0]
                    energy = float(row[-1])
                    comp = dict()
                    for ind in range(1, len(row) - 1):
                        if float(row[ind]) > 0:
                            comp[Element(elements[ind - 1])] = float(row[ind])
                    entries.append(PDEntry(Composition(comp), energy, name))
        return cls(entries)
Пример #7
0
 def test_read_write_csv(self):
     Zn_solids = ["Zn", "ZnO", "ZnO2"]
     sol_g = [0.0, -3.338, -1.315]
     Zn_ions = ["Zn[2+]", "ZnOH[+]", "HZnO2[-]", "ZnO2[2-]", "ZnO"]
     liq_g = [-1.527, -3.415, -4.812, -4.036, -2.921]
     liq_conc = [1e-6, 1e-6, 1e-6, 1e-6, 1e-6]
     solid_entry = list()
     for sol in Zn_solids:
         comp = Composition(sol)
         delg = sol_g[Zn_solids.index(sol)]
         solid_entry.append(PourbaixEntry(PDEntry(comp, delg)))
     ion_entry = list()
     for ion in Zn_ions:
         comp_ion = Ion.from_formula(ion)
         delg = liq_g[Zn_ions.index(ion)]
         conc = liq_conc[Zn_ions.index(ion)]
         PoE = PourbaixEntry(IonEntry(comp_ion, delg))
         PoE.conc = conc
         ion_entry.append(PoE)
     entries = solid_entry + ion_entry
     PourbaixEntryIO.to_csv("pourbaix_test_entries.csv", entries)
     (elements,
      entries) = PourbaixEntryIO.from_csv("pourbaix_test_entries.csv")
     self.assertEqual(
         elements, [Element('Zn'),
                    Element('H'), Element('O')], "Wrong elements!")
     self.assertEqual(len(entries), 8, "Wrong number of entries!")
     os.remove("pourbaix_test_entries.csv")
Пример #8
0
    def Create_Compositional_PhaseDiagram(self):

        phasediagram_entries = []

        for compound in self.compounds_info.keys():

            compound_composition = {}

            # Disregard elements not included in main compound
            if (compound in self.all_elements) and (compound
                                                    not in self.elements_list):
                continue

            # Elements
            if compound in self.elements_list:
                compound_composition[compound] = self.compounds_info[compound][
                    "dft_" + compound]

            # Compounds
            else:
                for element in self.compounds_info[compound]["elements_list"]:
                    compound_composition[element] = self.compounds_info[
                        compound]["dft_" + element]

            compound_total_energy = self.compounds_info[compound][
                "total_energy"]

            phasediagram_entries.append(
                PDEntry(compound_composition, compound_total_energy))

        self.phasediagram = PhaseDiagram(phasediagram_entries)
def get_decomp_product_ids(structures, competing_phases):
    # Create phase diagrams for each of the new structure chemical systems
    phase_diagrams = []
    for competing in competing_phases:
        # Add competing phases
        entries = [
            PDEntry(Composition(i['full_formula']),
                    i['final_energy'],
                    name=i['task_id']) for i in competing
        ]
        pd = PhaseDiagram(entries)
        phase_diagrams.append(pd)

    # Put new structures on phase diagram to get the set of decomp products
    all_decomp_prods = []
    for new_struc, pd in zip(structures, phase_diagrams):
        comp = new_struc['structure'].composition.element_composition
        decomp_prods = pd.get_decomposition(comp=comp)
        all_decomp_prods.extend([i.name for i in decomp_prods])

    # Reduce decomposition products to unique set
    all_decomp_prods = list(set(all_decomp_prods))
    print('{} unique competing phases to calculate'.format(
        len(all_decomp_prods)))
    return (all_decomp_prods)
Пример #10
0
 def setUp(self):
     comp = Composition("Mn2O3")
     self.solentry = PDEntry(comp, 49)
     ion = Ion.from_formula("MnO4-")
     self.ionentry = IonEntry(ion, 25)
     self.PxIon = PourbaixEntry(self.ionentry)
     self.PxSol = PourbaixEntry(self.solentry)
     self.PxIon.conc = 1e-4
Пример #11
0
 def setUp(self):
     comp = Composition("LiFeO2")
     entry = PDEntry(comp, 53)
     self.transformed_entry = TransformedPDEntry(
         {
             DummySpecies("Xa"): 1,
             DummySpecies("Xb"): 1
         }, entry)
Пример #12
0
    def from_entries(cls, entries, working_ion_entry):
        """
        Create a new InsertionElectrode.

        Args:
            entries: A list of ComputedStructureEntries (or subclasses)
                representing the different topotactic states of the battery,
                e.g. TiO2 and LiTiO2.
            working_ion_entry: A single ComputedEntry or PDEntry
                representing the element that carries charge across the
                battery, e.g. Li.
        """
        _working_ion = working_ion_entry.composition.elements[0]
        _working_ion_entry = working_ion_entry

        # Prepare to make phase diagram: determine elements and set their energy
        # to be very high
        elements = set()
        for entry in entries:
            elements.update(entry.composition.elements)

        # Set an artificial energy for each element for convex hull generation
        element_energy = max([entry.energy_per_atom for entry in entries]) + 10

        pdentries = []
        pdentries.extend(entries)
        pdentries.extend(
            [PDEntry(Composition({el: 1}), element_energy) for el in elements])

        # Make phase diagram to determine which entries are stable vs. unstable
        pd = PhaseDiagram(pdentries)

        def lifrac(e):
            return e.composition.get_atomic_fraction(_working_ion)

        # stable entries ordered by amount of Li asc
        _stable_entries = tuple(
            sorted([e for e in pd.stable_entries if e in entries], key=lifrac))

        # unstable entries ordered by amount of Li asc
        _unstable_entries = tuple(
            sorted([e for e in pd.unstable_entries if e in entries],
                   key=lifrac))

        # create voltage pairs
        _vpairs = tuple([
            InsertionVoltagePair.from_entries(
                _stable_entries[i],
                _stable_entries[i + 1],
                working_ion_entry,
            ) for i in range(len(_stable_entries) - 1)
        ])
        return cls(
            voltage_pairs=_vpairs,
            working_ion_entry=_working_ion_entry,
            _stable_entries=_stable_entries,
            _unstable_entries=_unstable_entries,
        )
Пример #13
0
    def is_in_composition_space_pd(self, organism, composition_space,
                                   constraints, pool):
        """
        Returns a boolean indicating whether the organism is in the composition
        space. Whether composition space endpoints are allowed is determined by
        the value of constraints.allow_endpoints.

        Args:
            organism: the Organism to check

            composition_space: the CompositionSpace of the search

            constraints: the Constraints of the search

            pool: the Pool
        """

        # cast the endpoints to PDEntries (just make up some energies)
        pdentries = []
        for endpoint in composition_space.endpoints:
            pdentries.append(PDEntry(endpoint, -10))
        pdentries.append(PDEntry(organism.composition, -10))

        # make a CompoundPhaseDiagram and use it to check if the organism
        # is in the composition space from how many entries it returns
        composition_checker = CompoundPhaseDiagram(pdentries,
                                                   composition_space.endpoints)
        if len(
                composition_checker.transform_entries(
                    pdentries, composition_space.endpoints)[0]) == len(
                        composition_space.endpoints):
            print('Organism {} lies outside the composition space '.format(
                organism.id))
            return False

        # check the composition space endpoints if specified
        if not constraints.allow_endpoints and len(pool.to_list()) > 0:
            for endpoint in composition_space.endpoints:
                if endpoint.almost_equals(
                        organism.composition.reduced_composition):
                    print('Organism {} is at a composition space '
                          'endpoint '.format(organism.id))
                    return False
        return True
Пример #14
0
    def setUp(self):
        comp = Composition("LiFeO2")
        entry = PDEntry(comp, 53)

        terminal_compositions = ["Li2O", "FeO", "LiO8"]
        terminal_compositions = [Composition(c) for c in terminal_compositions]

        sp_mapping = OrderedDict()
        for i, comp in enumerate(terminal_compositions):
            sp_mapping[comp] = DummySpecies("X" + chr(102 + i))

        self.transformed_entry = TransformedPDEntry(entry, sp_mapping)
Пример #15
0
    def setUp(self):
        entries = list(EntrySet.from_csv(os.path.join(module_dir, "pdentries_test.csv")))

        self.pd_ternary = PhaseDiagram(entries)
        self.plotter_ternary_mpl = PDPlotter(self.pd_ternary, backend="matplotlib")
        self.plotter_ternary_plotly = PDPlotter(self.pd_ternary, backend="plotly")

        entrieslio = [e for e in entries if "Fe" not in e.composition]
        self.pd_binary = PhaseDiagram(entrieslio)
        self.plotter_binary_mpl = PDPlotter(self.pd_binary, backend="matplotlib")
        self.plotter_binary_plotly = PDPlotter(self.pd_binary, backend="plotly")

        entries.append(PDEntry("C", 0))
        self.pd_quaternary = PhaseDiagram(entries)
        self.plotter_quaternary_mpl = PDPlotter(self.pd_quaternary, backend="matplotlib")
        self.plotter_quaternary_plotly = PDPlotter(self.pd_quaternary, backend="plotly")
Пример #16
0
    def compute_pd_values(self, organisms_list, composition_space):
        """
        Constructs a convex hull from the provided organisms and sets the
        organisms' values to their distances from the convex hull.

        Returns the CompoundPhaseDiagram object computed from the organisms
        in organisms_list.

        Args:
            organisms_list: a list of Organisms whose values we need to compute

            composition_space: the CompositionSpace of the search
        """

        # create a PDEntry object for each organism in the list of organisms
        pdentries = {}
        for organism in organisms_list:
            pdentries[organism.id] = PDEntry(organism.composition,
                                             organism.total_energy)

        # put the pdentries in a list
        pdentries_list = []
        for organism_id in pdentries:
            pdentries_list.append(pdentries[organism_id])

        # create a compound phase diagram object from the list of pdentries
        compound_pd = CompoundPhaseDiagram(pdentries_list,
                                           composition_space.endpoints)

        # transform the pdentries and put them in a dictionary, with the
        # organism id's as the keys
        transformed_pdentries = {}
        for org_id in pdentries:
            transformed_pdentries[org_id] = compound_pd.transform_entries(
                [pdentries[org_id]], composition_space.endpoints)[0][0]

        # put the values in a dictionary, with the organism id's as the keys
        values = {}
        for org_id in pdentries:
            values[org_id] = compound_pd.get_e_above_hull(
                transformed_pdentries[org_id])

        # assign values to the organisms
        for organism in organisms_list:
            organism.value = values[organism.id]

        return compound_pd
Пример #17
0
    def test_planar_inputs(self):
        e1 = PDEntry("H", 0)
        e2 = PDEntry("He", 0)
        e3 = PDEntry("Li", 0)
        e4 = PDEntry("Be", 0)
        e5 = PDEntry("B", 0)
        e6 = PDEntry("Rb", 0)

        pd = PhaseDiagram([e1, e2, e3, e4, e5, e6], map(Element, ["Rb", "He", "B", "Be", "Li", "H"]))

        self.assertEqual(len(pd.facets), 1)
Пример #18
0
def getPourbaixEntryfromMongo(elements):
    entries = []
    unique_entries = {}
    elements_with_H_O = elements
    elements_with_H_O.append('H')
    elements_with_H_O.append('O')
    elements_with_H_O.sort()
    print(elements)
    allcombinations = getAllCombinations(elements_with_H_O)
    myclient = pymongo.MongoClient("mongodb://localhost:27017/")
    mydb = myclient["mp"]
    mycol = mydb["aml_all5"]
    for c in allcombinations:
        c.sort()
        p = '^'
        for i in c:
            p = p + i + '[0-9]+\s*'
        p = p + '$'
        mq = {"elements": c}
        md = mycol.find(mq)
        for x in md:
            print(x['pretty_formula'])
            fe = x['formation_energy_per_atom']
            if fe is not None:
                try:
                    fe = float(x['formation_energy_per_atom'])
                    if unique_entries.get(x['pretty_formula']) is not None:
                        if unique_entries[x['pretty_formula']] > x[
                                'formation_energy_per_atom'] * x['natoms']:
                            unique_entries[x['pretty_formula']] = x[
                                'formation_energy_per_atom'] * x['natoms']
                    else:
                        unique_entries[x['pretty_formula']] = x[
                            'formation_energy_per_atom'] * x['natoms']
                except ValueError:
                    fe = 100.0
                    print(x['task_id'], x['pretty_formula'], fe)
    for u in unique_entries:
        print(u, unique_entries[u])
        ent = PDEntry(Composition(u),
                      unique_entries[u],
                      attribute={'task_id': x['task_id']})
        ent = PourbaixEntry(ent)
        #ent.phase_type='Solid'
        entries.append(ent)
    return entries
    def Create_Compositional_PhaseDiagram(self):

        # Record all entries for the phase diagram
        phasediagram_entries = []

        for compound in self.compounds_info.keys():

            # Disregard elements not included in main compound
            if (compound in self.all_elements) and (compound
                                                    not in self.elements_list):
                continue

            # Get the compound's composition
            compound_composition = {}
            if compound in self.elements_list:  # Elemental material
                compound_composition[compound] = self.compounds_info[compound][
                    "dft_" + compound]
            else:  # Compound material
                for element in self.compounds_info[compound]["elements_list"]:
                    compound_composition[element] = self.compounds_info[
                        compound]["dft_" + element]

            # Get the compound's total energy
            compound_total_energy = self.compounds_info[compound][
                "total_energy"]

            # Record to list of entries
            phasediagram_entries.append(
                PDEntry(compound_composition, compound_total_energy))

        # Calculate compositional phase diagram (using pymatgen)
        #	The output data structure is as follows:
        #		lines --> List of arrays, each array is 2x2 for ternary (3x3 for quaternary, etc.), column vector represents point on phase diagram.
        #					ex: array([ [0.3, 0.5], [1.0, 0.0] ]) is a line that goes from point [x=0.3, y=1.0] to point [x=0.5, y=0.0]
        #		labels --> Dictionary with point-PDEntry pairs.
        self.pmg_phasediagram = PhaseDiagram(phasediagram_entries)
        self.pmg_phasediagram_plot_object = PDPlotter(self.pmg_phasediagram)
        (lines, labels,
         unstable) = self.pmg_phasediagram_plot_object.pd_plot_data

        # Record all lines and points of the compositional phase diagram
        self.lines = lines
        self.labels = labels
Пример #20
0
    def get_phase_diagram_plot(self):
        """
        Returns a phase diagram plot, as a matplotlib plot object.
        """

        # set the font to Times, rendered with Latex
        plt.rc('font', **{'family': 'serif', 'serif': ['Times']})
        plt.rc('text', usetex=True)

        # parse the composition space endpoints
        endpoints_line = self.lines[0].split()
        endpoints = []
        for word in endpoints_line[::-1]:
            if word == 'endpoints:':
                break
            else:
                endpoints.append(Composition(word))

        if len(endpoints) < 2:
            print('There must be at least 2 endpoint compositions to make a '
                  'phase diagram.')
            quit()

        # parse the compositions and total energies of all the structures
        compositions = []
        total_energies = []
        for i in range(4, len(self.lines)):
            line = self.lines[i].split()
            compositions.append(Composition(line[1]))
            total_energies.append(float(line[2]))

        # make a list of PDEntries
        pdentries = []
        for i in range(len(compositions)):
            pdentries.append(PDEntry(compositions[i], total_energies[i]))

        # make a CompoundPhaseDiagram
        compound_pd = CompoundPhaseDiagram(pdentries, endpoints)

        # make a PhaseDiagramPlotter
        pd_plotter = PDPlotter(compound_pd, show_unstable=50)
        return pd_plotter.get_plot(label_unstable=False)
Пример #21
0
    def from_csv(filename):
        """
        Imports PourbaixEntries from a csv.

        Args:
            filename: Filename to import from.

        Returns:
            List of Entries
        """
        with open(filename, "rt") as f:
            reader = csv.reader(f,
                                delimiter=unicode2str(","),
                                quotechar=unicode2str("\""),
                                quoting=csv.QUOTE_MINIMAL)
            entries = list()
            header_read = False
            for row in reader:
                if not header_read:
                    elements = row[1:(len(row) - 4)]
                    header_read = True
                elif row:
                    name = row[0]
                    energy = float(row[-4])
                    conc = float(row[-1])
                    comp = dict()
                    for ind in range(1, len(row) - 4):
                        if float(row[ind]) > 0:
                            comp[Element(elements[ind - 1])] = float(row[ind])
                    phase_type = row[-3]
                    if phase_type == "Ion":
                        PoE = PourbaixEntry(
                            IonEntry(Ion.from_formula(name), energy))
                        PoE.conc = conc
                        PoE.name = name
                        entries.append(PoE)
                    else:
                        entries.append(
                            PourbaixEntry(PDEntry(Composition(comp), energy)))
        elements = [Element(el) for el in elements]
        return elements, entries
Пример #22
0
    def get_convex_hull_area(self, composition_space):
        """
        Returns the area/volume of the current convex hull.

        Args:
            composition_space: the CompositionSpace of the search
        """

        # check if the initial population contains organisms at all the
        # endpoints of the composition space
        if self.has_endpoints(composition_space) and \
                self.has_non_endpoint(composition_space):

            # compute and print the area or volume of the convex hull
            pdentries = []
            for organism in self.initial_population:
                pdentries.append(
                    PDEntry(organism.composition, organism.total_energy))
            compound_pd = CompoundPhaseDiagram(pdentries,
                                               composition_space.endpoints)

            # get the data for the convex hull
            qhull_data = compound_pd.qhull_data
            # for some reason, the last point is positive, so remove it
            hull_data = np.delete(qhull_data, -1, 0)

            # make a ConvexHull object from the hull data
            # Sometime this fails, saying that only two points were given to
            # construct the convex hull, even though the if statement above
            # checks that there are enough points.
            try:
                convex_hull = ConvexHull(hull_data)
            except:
                return None
            if len(composition_space.endpoints) == 2:
                return convex_hull.area
            else:
                return convex_hull.volume
Пример #23
0
        def update_entries_store(rows):
            if rows is None:
                raise PreventUpdate
            entries = []
            for row in rows:
                try:
                    comp = Composition(row["Formula"])
                    energy = row["Formation Energy (eV/atom)"]
                    if row["Material ID"] is None:
                        attribute = "Custom Entry"
                    else:
                        attribute = row["Material ID"]
                    entry = PDEntry(comp,
                                    float(energy) * comp.num_atoms,
                                    attribute=attribute)
                    entries.append(entry)
                except:
                    pass

            if not entries:
                raise PreventUpdate

            return self.to_data(entries)
Пример #24
0
        def update_entries_store(rows):
            if rows is None:
                raise PreventUpdate
            entries = []
            for row in rows:
                try:
                    comp = Composition(row["Formula"])
                    energy = row["Formation Energy (eV/atom)"]
                    if row["Material ID"] is None:
                        attribute = "Custom Entry"
                    else:
                        attribute = row["Material ID"]
                    # create new entry object containing mpid as attribute (to combine with custom entries)
                    entry = PDEntry(comp,
                                    float(energy) * comp.num_atoms,
                                    attribute=attribute)
                    entries.append(entry)
                except:
                    continue

            if not entries:
                raise PreventUpdate

            return entries
Пример #25
0
 def test_1d_pd(self):
     entry = PDEntry("H", 0)
     pd = PhaseDiagram([entry])
     decomp, e = pd.get_decomp_and_e_above_hull(PDEntry("H", 1))
     self.assertAlmostEqual(e, 1)
     self.assertAlmostEqual(decomp[entry], 1.0)
Пример #26
0
 def setUp(self):
     comp = Composition("LiFeO2")
     self.entry = PDEntry(comp, 53)
     self.gpentry = GrandPotPDEntry(self.entry, {Element("O"): 1.5})
Пример #27
0
    def from_entries(cls, entries, working_ion_entry, strip_structures=False):
        """
        Create a new InsertionElectrode.

        Args:
            entries: A list of ComputedStructureEntries (or subclasses)
                representing the different topotactic states of the battery,
                e.g. TiO2 and LiTiO2.
            working_ion_entry: A single ComputedEntry or PDEntry
                representing the element that carries charge across the
                battery, e.g. Li.
            strip_structures: Since the electrode document only uses volume we can make the
                electrode object significantly leaner by dropping the structure data.
                If this parameter is set to True, the ComputedStructureEntry will be replaced
                with ComputedEntry and the volume will be stored in ComputedEntry.data['volume']
        """

        if strip_structures:
            ents = []
            for ient in entries:
                dd = ient.as_dict()
                ent = ComputedEntry.from_dict(dd)
                ent.data["volume"] = ient.structure.volume
                ents.append(ent)
            entries = ents

        _working_ion = working_ion_entry.composition.elements[0]
        _working_ion_entry = working_ion_entry

        # Prepare to make phase diagram: determine elements and set their energy
        # to be very high
        elements = set()
        for entry in entries:
            elements.update(entry.composition.elements)

        # Set an artificial energy for each element for convex hull generation
        element_energy = max([entry.energy_per_atom for entry in entries]) + 10

        pdentries = []
        pdentries.extend(entries)
        pdentries.extend(
            [PDEntry(Composition({el: 1}), element_energy) for el in elements])

        # Make phase diagram to determine which entries are stable vs. unstable
        pd = PhaseDiagram(pdentries)

        def lifrac(e):
            return e.composition.get_atomic_fraction(_working_ion)

        # stable entries ordered by amount of Li asc
        _stable_entries = tuple(
            sorted([e for e in pd.stable_entries if e in entries], key=lifrac))

        # unstable entries ordered by amount of Li asc
        _unstable_entries = tuple(
            sorted([e for e in pd.unstable_entries if e in entries],
                   key=lifrac))

        # create voltage pairs
        _vpairs = tuple([
            InsertionVoltagePair.from_entries(
                _stable_entries[i],
                _stable_entries[i + 1],
                working_ion_entry,
            ) for i in range(len(_stable_entries) - 1)
        ])
        framework = _vpairs[0].framework
        return cls(
            voltage_pairs=_vpairs,
            working_ion_entry=_working_ion_entry,
            _stable_entries=_stable_entries,
            _unstable_entries=_unstable_entries,
            _framework_formula=framework.reduced_formula,
        )
Пример #28
0
    def test_get_phase_separation_energy(self):
        for entry in self.pd.unstable_entries:
            if entry.composition.fractional_composition not in [
                    e.composition.fractional_composition
                    for e in self.pd.stable_entries
            ]:
                self.assertGreaterEqual(
                    self.pd.get_phase_separation_energy(entry),
                    0,
                    "Unstable entries should have positive decomposition energy!",
                )
            else:
                if entry.is_element:
                    el_ref = self.pd.el_refs[entry.composition.elements[0]]
                    e_d = entry.energy_per_atom - el_ref.energy_per_atom
                    self.assertAlmostEqual(
                        self.pd.get_phase_separation_energy(entry), e_d, 7)
                # NOTE the remaining materials would require explicit tests as they
                # could be either positive or negative
                pass

        for entry in self.pd.stable_entries:
            if entry.composition.is_element:
                self.assertEqual(
                    self.pd.get_phase_separation_energy(entry),
                    0,
                    "Stable elemental entries should have decomposition energy of zero!",
                )
            else:
                self.assertLessEqual(
                    self.pd.get_phase_separation_energy(entry),
                    0,
                    "Stable entries should have negative decomposition energy!",
                )
                self.assertAlmostEqual(
                    self.pd.get_phase_separation_energy(entry,
                                                        stable_only=True),
                    self.pd.get_equilibrium_reaction_energy(entry),
                    7,
                    ("Using `stable_only=True` should give decomposition energy equal to "
                     "equilibrium reaction energy!"),
                )

        # Test that we get correct behaviour with a polymorph
        toy_entries = {
            "Li": 0.0,
            "Li2O": -5,
            "LiO2": -4,
            "O2": 0.0,
        }

        toy_pd = PhaseDiagram([PDEntry(c, e) for c, e in toy_entries.items()])

        # stable entry
        self.assertAlmostEqual(
            toy_pd.get_phase_separation_energy(PDEntry("Li2O", -5)),
            -1.0,
            7,
        )
        # polymorph
        self.assertAlmostEqual(
            toy_pd.get_phase_separation_energy(PDEntry("Li2O", -4)),
            -2.0 / 3.0,
            7,
        )

        # Test that the method works for novel entries
        novel_stable_entry = PDEntry("Li5FeO4", -999)
        self.assertLess(
            self.pd.get_phase_separation_energy(novel_stable_entry),
            0,
            "Novel stable entries should have negative decomposition energy!",
        )

        novel_unstable_entry = PDEntry("Li5FeO4", 999)
        self.assertGreater(
            self.pd.get_phase_separation_energy(novel_unstable_entry),
            0,
            "Novel unstable entries should have positive decomposition energy!",
        )

        duplicate_entry = PDEntry("Li2O", -14.31361175)
        scaled_dup_entry = PDEntry("Li4O2", -14.31361175 * 2)
        stable_entry = [e for e in self.pd.stable_entries
                        if e.name == "Li2O"][0]

        self.assertEqual(
            self.pd.get_phase_separation_energy(duplicate_entry),
            self.pd.get_phase_separation_energy(stable_entry),
            "Novel duplicates of stable entries should have same decomposition energy!",
        )

        self.assertEqual(
            self.pd.get_phase_separation_energy(scaled_dup_entry),
            self.pd.get_phase_separation_energy(stable_entry),
            "Novel scaled duplicates of stable entries should have same decomposition energy!",
        )
Пример #29
0
    def from_entries(
        cls,
        entries: Iterable[Union[ComputedEntry, ComputedStructureEntry]],
        working_ion_entry: Union[ComputedEntry, ComputedStructureEntry,
                                 PDEntry],
        strip_structures: bool = False,
    ):
        """
        Create a new InsertionElectrode.

        Args:
            entries: A list of ComputedEntries, ComputedStructureEntries, or
                subclasses representing the different topotactic states
                of the battery, e.g. TiO2 and LiTiO2.
            working_ion_entry: A single ComputedEntry or PDEntry
                representing the element that carries charge across the
                battery, e.g. Li.
            strip_structures: Since the electrode document only uses volume we can make the
                electrode object significantly leaner by dropping the structure data.
                If this parameter is set to True, the ComputedStructureEntry will be
                replaced with a ComputedEntry and the volume will be stored in
                ComputedEntry.data['volume']. If entries provided are ComputedEntries,
                must set strip_structures=False.
        """

        if strip_structures:
            ents = []
            for ient in entries:
                dd = ient.as_dict()
                ent = ComputedEntry.from_dict(dd)
                ent.data["volume"] = ient.structure.volume
                ents.append(ent)
            entries = ents

        _working_ion = working_ion_entry.composition.elements[0]
        _working_ion_entry = working_ion_entry

        # Prepare to make phase diagram: determine elements and set their energy
        # to be very high
        elements = set()
        for entry in entries:
            elements.update(entry.composition.elements)

        # Set an artificial high energy for each element for convex hull generation
        element_energy = max(entry.energy_per_atom for entry in entries) + 10

        pdentries: List[Union[ComputedEntry, ComputedStructureEntry,
                              PDEntry]] = []
        pdentries.extend(entries)
        pdentries.extend(
            [PDEntry(Composition({el: 1}), element_energy) for el in elements])

        # Make phase diagram to determine which entries are stable vs. unstable.
        # For each working ion concentration, we want one stable entry
        # to use in forming voltage pairs. PhaseDiagram allows for easy comparison
        # of entry energies.
        pd = PhaseDiagram(pdentries)

        def lifrac(e):
            return e.composition.get_atomic_fraction(_working_ion)

        # stable entries ordered by amount of Li asc
        _stable_entries = tuple(
            sorted((e for e in pd.stable_entries if e in entries), key=lifrac))

        # unstable entries ordered by amount of Li asc
        _unstable_entries = tuple(
            sorted((e for e in pd.unstable_entries if e in entries),
                   key=lifrac))

        # create voltage pairs
        _vpairs: Tuple[AbstractVoltagePair] = tuple(  # type: ignore
            InsertionVoltagePair.from_entries(
                _stable_entries[i],
                _stable_entries[i + 1],
                working_ion_entry,
            ) for i in range(len(_stable_entries) - 1))
        framework = _vpairs[0].framework
        return cls(  # pylint: disable=E1123
            voltage_pairs=_vpairs,
            working_ion_entry=_working_ion_entry,
            stable_entries=_stable_entries,
            unstable_entries=_unstable_entries,
            framework_formula=framework.reduced_formula,
        )
Пример #30
0
    def compute_composition_vector(self, composition_space):
        """
        Returns the composition vector of the organism, as a numpy array.

        Args:
            composition_space: the CompositionSpace of the search.
        """

        if composition_space.objective_function == 'epa':
            return None
        elif composition_space.objective_function == 'pd':
            # make CompoundPhaseDiagram and PDAnalyzer objects
            pdentries = []
            for endpoint in composition_space.endpoints:
                pdentries.append(PDEntry(endpoint, -10))
            compound_pd = CompoundPhaseDiagram(pdentries,
                                               composition_space.endpoints)

            # transform the organism's composition
            transformed_entry = compound_pd.transform_entries(
                [PDEntry(self.composition, 10)], composition_space.endpoints)

            # get the transformed species and amounts
            if len(transformed_entry[0]) == 0:
                return None
            transformed_list = str(transformed_entry[0][0]).split()
            del transformed_list[0]
            popped = ''
            while popped != 'with':
                popped = transformed_list.pop()

            # separate the dummy species symbols from the amounts
            symbols = []
            amounts = []
            for entry in transformed_list:
                split_entry = entry.split('0+')
                symbols.append(split_entry[0])
                amounts.append(float(split_entry[1]))

            # make a dictionary mapping dummy species to amounts
            dummy_species_amounts = {}
            for i in range(len(symbols)):
                dummy_species_amounts[DummySpecie(symbol=symbols[i])] = \
                    amounts[i]

            # make Composition object with dummy species, get decomposition
            dummy_comp = Composition(dummy_species_amounts)
            decomp = compound_pd.get_decomposition(dummy_comp)

            # get amounts of the decomposition in terms of the (untransformed)
            # composition space endpoints
            formatted_decomp = {}
            for key in decomp:
                key_dict = key.as_dict()
                comp = Composition(key_dict['entry']['composition'])
                formatted_decomp[comp] = decomp[key]

            # make the composition vector
            composition_vector = []
            # because the random organism creator shuffles the endpoints
            composition_space.endpoints.sort()
            for endpoint in composition_space.endpoints:
                if endpoint in formatted_decomp:
                    composition_vector.append(formatted_decomp[endpoint])
                else:
                    composition_vector.append(0.0)
            return np.array(composition_vector)