def setUp(self): module_dir = os.path.dirname(os.path.abspath(__file__)) (self.elements, self.entries) = PDEntryIO.from_csv( os.path.join(module_dir, "pdentries_test.csv")) self.pd = GrandPotentialPhaseDiagram(self.entries, {Element("O"): -5}, self.elements) self.pd6 = GrandPotentialPhaseDiagram(self.entries, {Element("O"): -6})
def test_get_critical_compositions(self): c1 = Composition('Fe2O3') c2 = Composition('Li3FeO4') c3 = Composition('Li2O') comps = self.analyzer.get_critical_compositions(c1, c2) expected = [ Composition('Fe2O3'), Composition('Li0.3243244Fe0.1621621O0.51351349') * 7.4, Composition('Li3FeO4') ] for crit, exp in zip(comps, expected): self.assertTrue(crit.almost_equals(exp, rtol=0, atol=1e-5)) comps = self.analyzer.get_critical_compositions(c1, c3) expected = [ Composition('Fe2O3'), Composition('LiFeO2'), Composition('Li5FeO4') / 3, Composition('Li2O') ] for crit, exp in zip(comps, expected): self.assertTrue(crit.almost_equals(exp, rtol=0, atol=1e-5)) # Don't fail silently if input compositions aren't in phase diagram # Can be very confusing if you're working with a GrandPotentialPD self.assertRaises(ValueError, self.analyzer.get_critical_compositions, Composition('Xe'), Composition('Mn')) # For the moment, should also fail even if compositions are in the gppd # because it isn't handled properly gppd = GrandPotentialPhaseDiagram(self.pd.all_entries, {'Xe': 1}, self.pd.elements + [Element('Xe')]) pda = PDAnalyzer(gppd) self.assertRaises(ValueError, pda.get_critical_compositions, Composition('Fe2O3'), Composition('Li3FeO4Xe')) # check that the function still works though comps = pda.get_critical_compositions(c1, c2) expected = [ Composition('Fe2O3'), Composition('Li0.3243244Fe0.1621621O0.51351349') * 7.4, Composition('Li3FeO4') ] for crit, exp in zip(comps, expected): self.assertTrue(crit.almost_equals(exp, rtol=0, atol=1e-5)) # case where the endpoints are identical self.assertEqual(self.analyzer.get_critical_compositions(c1, c1 * 2), [c1, c1 * 2])
class GrandPotentialPhaseDiagramTest(unittest.TestCase): def setUp(self): module_dir = os.path.dirname(os.path.abspath(__file__)) (self.elements, self.entries) = PDEntryIO.from_csv( os.path.join(module_dir, "pdentries_test.csv")) self.pd = GrandPotentialPhaseDiagram(self.entries, {Element("O"): -5}, self.elements) self.pd6 = GrandPotentialPhaseDiagram(self.entries, {Element("O"): -6}) def test_stable_entries(self): stable_formulas = [ ent.original_entry.composition.reduced_formula for ent in self.pd.stable_entries ] expected_stable = ['Li5FeO4', 'Li2FeO3', 'LiFeO2', 'Fe2O3', 'Li2O2'] for formula in expected_stable: self.assertTrue(formula in stable_formulas, formula + " not in stable entries!") self.assertEqual(len(self.pd6.stable_entries), 4) def test_get_formation_energy(self): stable_formation_energies = { ent.original_entry.composition.reduced_formula: self.pd.get_form_energy(ent) for ent in self.pd.stable_entries } expected_formation_energies = { 'Fe2O3': 0.0, 'Li5FeO4': -5.305515040000046, 'Li2FeO3': -2.3424741500000152, 'LiFeO2': -0.43026396250000154, 'Li2O2': 0.0 } for formula, energy in expected_formation_energies.items(): self.assertAlmostEqual( energy, stable_formation_energies[formula], 7, "Calculated formation for " + formula + " is not correct!") def test_str(self): self.assertIsNotNone(str(self.pd))
class GrandPotentialPhaseDiagramTest(unittest.TestCase): def setUp(self): module_dir = os.path.dirname(os.path.abspath(__file__)) (self.elements, self.entries) = PDEntryIO.from_csv( os.path.join(module_dir, "pdentries_test.csv")) self.pd = GrandPotentialPhaseDiagram(self.entries, {Element("O"): -5}, self.elements) self.pd6 = GrandPotentialPhaseDiagram(self.entries, {Element("O"): -6}) def test_stable_entries(self): stable_formulas = [ent.original_entry.composition.reduced_formula for ent in self.pd.stable_entries] expected_stable = ['Li5FeO4', 'Li2FeO3', 'LiFeO2', 'Fe2O3', 'Li2O2'] for formula in expected_stable: self.assertTrue(formula in stable_formulas, formula + " not in stable entries!") self.assertEqual(len(self.pd6.stable_entries), 4) def test_get_formation_energy(self): stable_formation_energies = { ent.original_entry.composition.reduced_formula: self.pd.get_form_energy(ent) for ent in self.pd.stable_entries} expected_formation_energies = {'Fe2O3': 0.0, 'Li5FeO4': -5.305515040000046, 'Li2FeO3': -2.3424741500000152, 'LiFeO2': -0.43026396250000154, 'Li2O2': 0.0} for formula, energy in expected_formation_energies.items(): self.assertAlmostEqual(energy, stable_formation_energies[formula], 7, "Calculated formation for " + formula + " is not correct!") def test_str(self): self.assertIsNotNone(str(self.pd))
def get_element_profile(self, element, comp, comp_tol=1e-5): """ Provides the element evolution data for a composition. For example, can be used to analyze Li conversion voltages by varying uLi and looking at the phases formed. Also can be used to analyze O2 evolution by varying uO2. Args: element: An element. Must be in the phase diagram. comp: A Composition comp_tol: The tolerance to use when calculating decompositions. Phases with amounts less than this tolerance are excluded. Defaults to 1e-5. Returns: Evolution data as a list of dictionaries of the following format: [ {'chempot': -10.487582010000001, 'evolution': -2.0, 'reaction': Reaction Object], ...] """ if element not in self._pd.elements: raise ValueError("get_transition_chempots can only be called with" " elements in the phase diagram.") chempots = self.get_transition_chempots(element) stable_entries = self._pd.stable_entries gccomp = Composition( {el: amt for el, amt in comp.items() if el != element}) elref = self._pd.el_refs[element] elcomp = Composition(element.symbol) prev_decomp = [] evolution = [] def are_same_decomp(decomp1, decomp2): for comp in decomp2: if comp not in decomp1: return False return True for c in chempots: gcpd = GrandPotentialPhaseDiagram(stable_entries, {element: c - 1e-5}, self._pd.elements) analyzer = PDAnalyzer(gcpd) gcdecomp = analyzer.get_decomposition(gccomp) decomp = [ gcentry.original_entry.composition for gcentry, amt in gcdecomp.items() if amt > comp_tol ] decomp_entries = [ gcentry.original_entry for gcentry, amt in gcdecomp.items() if amt > comp_tol ] if not are_same_decomp(prev_decomp, decomp): if elcomp not in decomp: decomp.insert(0, elcomp) rxn = Reaction([comp], decomp) rxn.normalize_to(comp) prev_decomp = decomp amt = -rxn.coeffs[rxn.all_comp.index(elcomp)] evolution.append({ 'chempot': c, 'evolution': amt, 'element_reference': elref, 'reaction': rxn, 'entries': decomp_entries }) return evolution