def test_calculate_energy(self): reactants = [Composition.from_formula("MgO"), Composition.from_formula("Al2O3")] products = [Composition.from_formula("MgAl2O4")] energies = {Composition.from_formula("MgO"):-0.1, Composition.from_formula("Al2O3"):-0.2, Composition.from_formula("MgAl2O4"):-0.5} rxn = Reaction(reactants, products) self.assertEquals(str(rxn), "1.000 MgO + 1.000 Al2O3 -> 1.000 MgAl2O4", "Wrong reaction obtained!") self.assertEquals(rxn.normalized_repr, "MgO + Al2O3 -> MgAl2O4", "Wrong normalized reaction obtained!") self.assertAlmostEquals(rxn.calculate_energy(energies), -0.2, 5)
def test_equals(self): random_z = random.randint(1, 92) fixed_el = Element.from_Z(random_z) other_z = random.randint(1, 92) while other_z == random_z: other_z = random.randint(1, 92) comp1 = Composition({fixed_el:1, Element.from_Z(other_z) :0}) other_z = random.randint(1, 92) while other_z == random_z: other_z = random.randint(1, 92) comp2 = Composition({fixed_el:1, Element.from_Z(other_z) :0}) self.assertEqual(comp1, comp2, "Composition equality test failed. %s should be equal to %s" % (comp1.formula, comp2.formula)) self.assertEqual(comp1.__hash__(), comp2.__hash__(), "Hashcode equality test failed!")
def __init__(self, composition, energy, correction=0.0, parameters=None, data=None): """ Args: composition: Composition of the entry. For flexibility, this can take the form of a {symbol: amt} dictionary as well the standard pymatgen Composition object. energy: Energy of the entry. Usually the final calculated energy from VASP or other electronic structure codes. correction: A correction to be applied to the energy. This is used to modify the energy for certain analyses. Defaults to 0.0. parameters: An optional dict of parameters associated with the entry. Defaults to None. data: An optional dict of any additional data associated with the entry. Defaults to None """ if not isinstance(composition, Composition): comp = Composition.from_dict(composition) else: comp = composition super(ComputedEntry, self).__init__(comp, energy) self.correction = correction self.parameters = parameters if parameters else {} self.data = data if data else {}
def test_get_atoms(self): if not aio.ase_loaded: raise SkipTest("ASE not present. Skipping...") p = Poscar.from_file(os.path.join(test_dir, 'POSCAR')) structure = p.struct atoms = aio.AseAtomsAdaptor.get_atoms(structure) ase_composition = Composition.from_formula(atoms.get_name()) self.assertEqual(ase_composition, structure.composition)
def test_init(self): rct = {Composition.from_formula('K2SO4'):3, Composition.from_formula('Na2S'):1, Composition.from_formula('Li'):24} prod = {Composition.from_formula('KNaS'): 2, Composition.from_formula('K2S'):2, Composition.from_formula('Li2O'):12} rxn = BalancedReaction(rct, prod) self.assertEquals(str(rxn), "24.000 Li + 1.000 Na2S + 3.000 K2SO4 -> 12.000 Li2O + 2.000 K2S + 2.000 KNaS") #Test unbalanced exception rct = {Composition.from_formula('K2SO4'):1, Composition.from_formula('Na2S'):1, Composition.from_formula('Li'):24} prod = {Composition.from_formula('KNaS'): 2, Composition.from_formula('K2S'):2, Composition.from_formula('Li2O'):12} self.assertRaises(ReactionError, BalancedReaction, rct, prod)
def test_init(self): filepath = os.path.join(test_dir, 'POSCAR') poscar = Poscar.from_file(filepath) comp = poscar.struct.composition self.assertEqual(comp, Composition.from_formula("Fe4P4O16")) #Vasp 4 type with symbols at the end. poscar_string = """Test1 1.0 3.840198 0.000000 0.000000 1.920099 3.325710 0.000000 0.000000 -2.217138 3.135509 1 1 direct 0.000000 0.000000 0.000000 Si 0.750000 0.500000 0.750000 F""" poscar = Poscar.from_string(poscar_string) self.assertEqual(poscar.struct.composition, Composition.from_formula("SiF")) #Vasp 4 tyle file with default names, i.e. no element symbol found. poscar_string = """Test2 1.0 3.840198 0.000000 0.000000 1.920099 3.325710 0.000000 0.000000 -2.217138 3.135509 1 1 direct 0.000000 0.000000 0.000000 0.750000 0.500000 0.750000""" poscar = Poscar.from_string(poscar_string) self.assertEqual(poscar.struct.composition, Composition.from_formula("HHe")) #Vasp 4 tyle file with default names, i.e. no element symbol found. poscar_string = """Test3 1.0 3.840198 0.000000 0.000000 1.920099 3.325710 0.000000 0.000000 -2.217138 3.135509 1 1 Selective dynamics direct 0.000000 0.000000 0.000000 T T T Si 0.750000 0.500000 0.750000 F F F O""" poscar = Poscar.from_string(poscar_string) self.assertEqual(poscar.selective_dynamics, [[True, True, True], [False, False, False]])
def test_to_dict(self): c = Composition.from_dict({'Fe': 4, 'O': 6}) d = c.to_dict correct_dict = {'Fe': 4.0, 'O': 6.0} self.assertEqual(d['Fe'], correct_dict['Fe']) self.assertEqual(d['O'], correct_dict['O']) correct_dict = {'Fe': 2.0, 'O': 3.0} d = c.to_reduced_dict self.assertEqual(d['Fe'], correct_dict['Fe']) self.assertEqual(d['O'], correct_dict['O'])
def _get_int_removals_helper(self, spec_amts_oxi, oxid_el, oxid_els, numa): """ This is a helper method for get_removals_int_oxid! Args: spec_amts_oxi - a dict of species to their amounts in the structure oxid_el - the element to oxidize oxid_els - the full list of elements that might be oxidized numa - a running set of numbers of A cation at integer oxidation steps Returns: a set of numbers A; steps for for oxidizing oxid_el first, then the other oxid_els in this list """ # If Mn is the oxid_el, we have a mixture of Mn2+, Mn3+, determine the minimum oxidation state for Mn #this is the state we want to oxidize! oxid_old = min([spec.oxi_state for spec in spec_amts_oxi if spec.symbol == oxid_el.symbol]) oxid_new = math.floor(oxid_old + 1) #if this is not a valid solution, break out of here and don't add anything to the list if oxid_new > oxid_el.max_oxidation_state: return numa #update the spec_amts_oxi map to reflect that the oxidation took place spec_old = Specie(oxid_el.symbol, oxid_old) spec_new = Specie(oxid_el.symbol, oxid_new) specamt = spec_amts_oxi[spec_old] spec_amts_oxi = {sp: amt for sp, amt in spec_amts_oxi.items() if sp != spec_old} spec_amts_oxi[spec_new] = specamt spec_amts_oxi = Composition(spec_amts_oxi) #determine the amount of cation A in the structure needed for charge balance and add it to the list oxi_noA = sum([spec.oxi_state * spec_amts_oxi[spec] for spec in spec_amts_oxi if spec.symbol not in self.cation.symbol]) a = max(0, -oxi_noA / self.cation_charge) numa = numa.union({a}) #recursively try the other oxidation states if a == 0: return numa else: for oxid_el in oxid_els: numa = numa.union( self._get_int_removals_helper(spec_amts_oxi.copy(), oxid_el, oxid_els, numa)) return numa
def test_get_data(self): props = ["energy", "energy_per_atom", "formation_energy_per_atom", "nsites", "unit_cell_formula", "pretty_formula", "is_hubbard", "elements", "nelements", "e_above_hull", "hubbards", "is_compatible", "task_ids", "density", "icsd_ids", "total_magnetization"] # unicode literals have been reintroduced in py>3.2 expected_vals = [-191.33812137, -6.833504334642858, -2.551358929370749, 28, {k: v for k, v in {'P': 4, 'Fe': 4, 'O': 16, 'Li': 4}.items()}, "LiFePO4", True, ['Li', 'O', 'P', 'Fe'], 4, 0.0, {k: v for k, v in {'Fe': 5.3, 'Li': 0.0, 'O': 0.0, 'P': 0.0}.items()}, True, [u'mp-601412', u'mp-19017', u'mp-796535', u'mp-797820', u'mp-540081', u'mp-797269'], 3.4662026991351147, [159107, 154117, 160776, 99860, 181272, 166815, 260571, 92198, 165000, 155580, 38209, 161479, 153699, 260569, 260570, 200155, 260572, 181341, 181342, 72545, 56291, 97764, 162282, 155635], 16.0002716] for (i, prop) in enumerate(props): if prop not in ['hubbards', 'unit_cell_formula', 'elements', 'icsd_ids', 'task_ids']: val = self.rester.get_data("mp-19017", prop=prop)[0][prop] self.assertAlmostEqual(expected_vals[i], val) elif prop in ["elements", "icsd_ids", "task_ids"]: self.assertEqual(set(expected_vals[i]), set(self.rester.get_data("mp-19017", prop=prop)[0][prop])) else: self.assertEqual(expected_vals[i], self.rester.get_data("mp-19017", prop=prop)[0][prop]) props = ['structure', 'initial_structure', 'final_structure', 'entry'] for prop in props: obj = self.rester.get_data("mp-19017", prop=prop)[0][prop] if prop.endswith("structure"): self.assertIsInstance(obj, Structure) elif prop == "entry": obj = self.rester.get_data("mp-19017", prop=prop)[0][prop] self.assertIsInstance(obj, ComputedEntry) #Test chemsys search data = self.rester.get_data('Fe-Li-O', prop='unit_cell_formula') self.assertTrue(len(data) > 1) elements = {Element("Li"), Element("Fe"), Element("O")} for d in data: self.assertTrue( set(Composition(d['unit_cell_formula']).elements).issubset( elements)) self.assertRaises(MPRestError, self.rester.get_data, "Fe2O3", "badmethod")
def ion_or_solid_comp_object(formula): """ Returns either an ion object or composition object given a formula. Args: formula: String formula. Eg. of ion: NaOH(aq), Na[+]; Eg. of solid: Fe2O3(s), Fe(s), Na2O Returns: Composition/Ion object """ m = re.search(r"\[([^\[\]]+)\]|\(aq\)", formula) if m: comp_obj = Ion.from_formula(formula) elif re.search(r"\(s\)", formula): comp_obj = Composition(formula[:-3]) else: comp_obj = Composition(formula) return comp_obj
def ion_or_solid_comp_object(formula): """ Returns either an ion object or composition object given a formula. Args: formula: String formula. Eg. of ion: NaOH(aq), Na[+]; Eg. of solid: Fe2O3(s), Fe(s), Na2O Returns: Composition/Ion object """ m = re.search(r"\[([^\[\]]+)\]|\(aq\)", formula) if m: comp_obj = Ion.from_formula(formula) elif re.search(r"\(s\)", formula): comp_obj = Composition.from_formula(formula[:-3]) else: comp_obj = Composition.from_formula(formula) return comp_obj
def test_indeterminate_formula(self): correct_formulas = [] correct_formulas.append(["Co1"]) correct_formulas.append(["Co1", "C1 O1"]) correct_formulas.append(["Co2 O3", "C1 O5"]) correct_formulas.append(["N1 Ca1 Lu1", "U1 Al1 C1 N1"]) correct_formulas.append(["N1 Ca1 Lu1", "U1 Al1 C1 N1"]) correct_formulas.append(["Li1 Co1 P2 N1 O10", "Li1 P2 C1 N1 O11", "Li1 Co1 Po8 N1 O2", "Li1 Po8 C1 N1 O3"]) correct_formulas.append(["Co2 P4 O4", "P4 C2 O6", "Co2 Po4", "Po4 C2 O2"]) correct_formulas.append([]) for i, c in enumerate(correct_formulas): self.assertEqual([Composition.from_formula(comp) for comp in c], self.indeterminate_comp[i])
def test_products_reactants(self): reactants = [Composition.from_formula("Li3Fe2(PO4)3"), Composition.from_formula("Fe2O3"), Composition.from_formula("O2")] products = [Composition.from_formula("LiFePO4")] energies = {Composition.from_formula("Li3Fe2(PO4)3"):-0.1, Composition.from_formula("Fe2O3"):-0.2, Composition.from_formula("O2"):-0.2, Composition.from_formula("LiFePO4"):-0.5} rxn = Reaction(reactants, products) self.assertIn(Composition.from_formula("O2"), rxn.products, "O not in products!") self.assertIn(Composition.from_formula("Li3Fe2(PO4)3"), rxn.reactants, "Li3Fe2(PO4)4 not in reactants!") self.assertEquals(str(rxn), "0.333 Li3Fe2(PO4)3 + 0.167 Fe2O3 -> 0.250 O2 + 1.000 LiFePO4", "Wrong reaction obtained!") self.assertEquals(rxn.normalized_repr, "4 Li3Fe2(PO4)3 + 2 Fe2O3 -> 3 O2 + 12 LiFePO4", "Wrong normalized reaction obtained!") self.assertAlmostEquals(rxn.calculate_energy(energies), -0.48333333, 5)
def __str__(self): reactant_str = [] product_str = [] for i in range(self._num_comp): comp = self._all_comp[i] coeff = self._coeffs[i] red_comp = Composition.from_formula(comp.reduced_formula) scale_factor = comp.num_atoms / red_comp.num_atoms scaled_coeff = coeff * scale_factor if scaled_coeff < 0: reactant_str.append("%.3f %s" % (-scaled_coeff, comp.reduced_formula)) elif scaled_coeff > 0: product_str.append("%.3f %s" % (scaled_coeff, comp.reduced_formula)) return " + ".join(reactant_str) + " -> " + " + ".join(product_str)
def get_only_sites(self): """ Get a copy of the structure with only the sites Args: Returns: Structure: Structure with all possible migrating ion sites """ migrating_ion_sites = list( filter( lambda site: site.species == Composition( {self.migrating_specie: 1}), self.structure.sites)) return Structure.from_sites(migrating_ion_sites)
def get_element_profile(self, element, comp): """ 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 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.from_formula(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 - 0.01}, self._pd.elements) analyzer = PDAnalyzer(gcpd) decomp = [gcentry.original_entry.composition for gcentry, amt in analyzer.get_decomposition(gccomp).items() if amt > 1e-5] decomp_entries = [gcentry.original_entry for gcentry, amt in analyzer.get_decomposition(gccomp).items() if amt > 1e-5] 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 evolution.append({'chempot':c, 'evolution' :-rxn.coeffs[rxn.all_comp.index(elcomp)], 'element_reference': elref, 'reaction':rxn, 'entries':decomp_entries}) return evolution
def test_get_data(self): props = ["energy", "energy_per_atom", "formation_energy_per_atom", "nsites", "unit_cell_formula", "pretty_formula", "is_hubbard", "elements", "nelements", "e_above_hull", "hubbards", "is_compatible", "task_ids", "density", "icsd_id", "total_magnetization"] expected_vals = [-191.33812137, -6.833504334642858, -2.5532843405078913, 28, {u'P': 4, u'Fe': 4, u'O': 16, u'Li': 4}, "LiFePO4", True, [u'Li', u'O', u'P', u'Fe'], 4, 0.0, {u'Fe': 5.3, u'Li': 0.0, u'O': 0.0, u'P': 0.0}, True, [540081, 19017], 3.4662026991351147, 56291, 16.0001687] for (i, prop) in enumerate(props): if prop not in ['hubbards', 'unit_cell_formula', 'elements']: val = self.rester.get_data(540081, prop=prop)[0][prop] self.assertAlmostEqual(expected_vals[i], val) elif prop == "elements": self.assertEqual(set(expected_vals[i]), set(self.rester.get_data(540081, prop=prop)[0][prop])) else: self.assertEqual(expected_vals[i], self.rester.get_data(540081, prop=prop)[0][prop]) props = ['structure', 'initial_structure', 'final_structure', 'entry'] for prop in props: obj = self.rester.get_data(540081, prop=prop)[0][prop] if prop.endswith("structure"): self.assertIsInstance(obj, Structure) elif prop == "entry": obj = self.rester.get_data(540081, prop=prop)[0][prop] self.assertIsInstance(obj, ComputedEntry) #Test chemsys search data = self.rester.get_data('Fe-Li-O', prop='unit_cell_formula') self.assertTrue(len(data) > 1) elements = {Element("Li"), Element("Fe"), Element("O")} for d in data: self.assertTrue( set(Composition(d['unit_cell_formula']).elements).issubset( elements)) self.assertRaises(MPRestError, self.rester.get_data, "Fe2O3", "badmethod")
def from_csv(filename): """ Imports PourbaixEntries from a csv. Args: filename - Filename to import from. Returns: List of Entries """ import csv reader = csv.reader(open(filename, "rb"), delimiter=",", quotechar="\"", 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 else: 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.set_conc(conc) PoE.set_name(name) entries.append(PoE) else: entries.append( PourbaixEntry(PDEntry(Composition(comp), energy))) elements = [Element(el) for el in elements] return elements, entries
def test_get_data(self): props = [ "energy", "energy_per_atom", "formation_energy_per_atom", "nsites", "unit_cell_formula", "pretty_formula", "is_hubbard", "elements", "nelements", "e_above_hull", "hubbards", "is_compatible", "task_ids", "density", "icsd_ids", "total_magnetization" ] expected_vals = [ -191.3359011, -6.833425039285714, -2.5515769497278913, 28, { 'P': 4, 'Fe': 4, 'O': 16, 'Li': 4 }, "LiFePO4", True, ['Li', 'O', 'P', 'Fe'], 4, 0.0, { 'Fe': 5.3, 'Li': 0.0, 'O': 0.0, 'P': 0.0 }, True, {'mp-19017', 'mp-540081', 'mp-601412'}, 3.464840709092822, [ 159107, 154117, 160776, 99860, 181272, 166815, 260571, 92198, 165000, 155580, 38209, 161479, 153699, 260569, 260570, 200155, 260572, 181341, 181342, 72545, 56291, 97764, 162282, 155635 ], 3.999999999 ] for (i, prop) in enumerate(props): if prop not in [ 'hubbards', 'unit_cell_formula', 'elements', 'icsd_ids', 'task_ids' ]: val = self.rester.get_data("mp-19017", prop=prop)[0][prop] self.assertAlmostEqual(expected_vals[i], val, 2, "Failed with property %s" % prop) elif prop in ["elements", "icsd_ids", "task_ids"]: upstream_vals = set( self.rester.get_data("mp-19017", prop=prop)[0][prop]) self.assertLessEqual(set(expected_vals[i]), upstream_vals) else: self.assertEqual( expected_vals[i], self.rester.get_data("mp-19017", prop=prop)[0][prop]) props = ['structure', 'initial_structure', 'final_structure', 'entry'] for prop in props: obj = self.rester.get_data("mp-19017", prop=prop)[0][prop] if prop.endswith("structure"): self.assertIsInstance(obj, Structure) elif prop == "entry": obj = self.rester.get_data("mp-19017", prop=prop)[0][prop] self.assertIsInstance(obj, ComputedEntry) # Test chemsys search data = self.rester.get_data('Fe-Li-O', prop='unit_cell_formula') self.assertTrue(len(data) > 1) elements = {Element("Li"), Element("Fe"), Element("O")} for d in data: self.assertTrue( set(Composition( d['unit_cell_formula']).elements).issubset(elements)) self.assertRaises(MPRestError, self.rester.get_data, "Fe2O3", "badmethod") # Test getting supported properties self.assertNotEqual(self.rester.get_task_data("mp-30"), []) # Test aliasing data = self.rester.get_task_data("mp-30", "energy") self.assertAlmostEqual(data[0]["energy"], -4.09929227, places=2)
def test_get_data(self): props = { "energy", "energy_per_atom", "formation_energy_per_atom", "nsites", "unit_cell_formula", "pretty_formula", "is_hubbard", "elements", "nelements", "e_above_hull", "hubbards", "is_compatible", "task_ids", "density", "icsd_ids", "total_magnetization", } mpid = "mp-1143" vals = requests.get( f"http://legacy.materialsproject.org/materials/{mpid}/json/") expected_vals = vals.json() for prop in props: if prop not in [ "hubbards", "unit_cell_formula", "elements", "icsd_ids", "task_ids", ]: val = self.rester.get_data(mpid, prop=prop)[0][prop] if prop in ["energy", "energy_per_atom"]: prop = "final_" + prop self.assertAlmostEqual(expected_vals[prop], val, 2, f"Failed with property {prop}") elif prop in ["elements", "icsd_ids", "task_ids"]: upstream_vals = set( self.rester.get_data(mpid, prop=prop)[0][prop]) self.assertLessEqual(set(expected_vals[prop]), upstream_vals) else: self.assertEqual( expected_vals[prop], self.rester.get_data(mpid, prop=prop)[0][prop], ) props = ["structure", "initial_structure", "final_structure", "entry"] for prop in props: obj = self.rester.get_data(mpid, prop=prop)[0][prop] if prop.endswith("structure"): self.assertIsInstance(obj, Structure) elif prop == "entry": obj = self.rester.get_data(mpid, prop=prop)[0][prop] self.assertIsInstance(obj, ComputedEntry) # Test chemsys search data = self.rester.get_data("Fe-Li-O", prop="unit_cell_formula") self.assertTrue(len(data) > 1) elements = {Element("Li"), Element("Fe"), Element("O")} for d in data: self.assertTrue( set(Composition( d["unit_cell_formula"]).elements).issubset(elements)) self.assertRaises(MPRestError, self.rester.get_data, "Fe2O3", "badmethod")
def test_get_data(self): props = [ "energy", "energy_per_atom", "formation_energy_per_atom", "nsites", "unit_cell_formula", "pretty_formula", "is_hubbard", "elements", "nelements", "e_above_hull", "hubbards", "is_compatible", "task_ids", "density", "icsd_ids", "total_magnetization", ] expected_vals = [ -191.7661349, -6.848790532142857, -2.5571951564625857, 28, { "P": 4, "Fe": 4, "O": 16, "Li": 4 }, "LiFePO4", True, ["Li", "O", "P", "Fe"], 4, 0.0, { "Fe": 5.3, "Li": 0.0, "O": 0.0, "P": 0.0 }, True, {"mp-19017", "mp-540081", "mp-601412"}, 3.4708958823634912, [ 159107, 154117, 160776, 99860, 181272, 166815, 260571, 92198, 165000, 155580, 38209, 161479, 153699, 260569, 260570, 200155, 260572, 181341, 181342, 72545, 56291, 97764, 162282, 155635, ], 0, ] for (i, prop) in enumerate(props): if prop not in [ "hubbards", "unit_cell_formula", "elements", "icsd_ids", "task_ids", ]: val = self.rester.get_data("mp-19017", prop=prop)[0][prop] self.assertAlmostEqual(expected_vals[i], val, 2, "Failed with property %s" % prop) elif prop in ["elements", "icsd_ids", "task_ids"]: upstream_vals = set( self.rester.get_data("mp-19017", prop=prop)[0][prop]) self.assertLessEqual(set(expected_vals[i]), upstream_vals) else: self.assertEqual( expected_vals[i], self.rester.get_data("mp-19017", prop=prop)[0][prop], ) props = ["structure", "initial_structure", "final_structure", "entry"] for prop in props: obj = self.rester.get_data("mp-19017", prop=prop)[0][prop] if prop.endswith("structure"): self.assertIsInstance(obj, Structure) elif prop == "entry": obj = self.rester.get_data("mp-19017", prop=prop)[0][prop] self.assertIsInstance(obj, ComputedEntry) # Test chemsys search data = self.rester.get_data("Fe-Li-O", prop="unit_cell_formula") self.assertTrue(len(data) > 1) elements = {Element("Li"), Element("Fe"), Element("O")} for d in data: self.assertTrue( set(Composition( d["unit_cell_formula"]).elements).issubset(elements)) self.assertRaises(MPRestError, self.rester.get_data, "Fe2O3", "badmethod") # Test getting supported properties self.assertNotEqual(self.rester.get_task_data("mp-30"), []) # Test aliasing data = self.rester.get_task_data("mp-30", "energy") self.assertAlmostEqual(data[0]["energy"], -4.09929227, places=2)
def curate_cifstring(cifstring): """ :return integrity_class 3 can run all calculations 2 can only run geometric analyses 1 do not run any calculations (exception) outputs files written by this function structural_schemas structural_schemas['configuration']: the major config structural_schemas['molgraphs']: a list of unique molgraphs from the major config, rsorted by # of atoms structural_schemas['molsmiles']: a list of unique molecular smiles from the major config, rsorted by # of atoms identifier hashconfig(major_config) """ integrity_class = 3 outputs = [] structural_schemas = OrderedDict() dp = DisParser(cifstring) try: print('--- begin DisParser.to_configs ---') dis_pstructure, dis_unwrap_str, dis_mols, config_infos = dp.to_configs( write_files=True, vanilla=True ) # if True writes conf_x.cif, configs is a list of pmg Structure except: emsg = 'ERROR: dp.to_configs failed!' print(emsg) raise CuratorError(emsg) if len(config_infos) not in [1, 2]: emsg = 'ERROR: vanilla to_configs found too many configs' print(emsg) raise CuratorError(emsg) disorder_class = dp.classification print('disorder class: {}'.format(disorder_class)) print('--- end DisParser.to_configs ---\n') print('--- begin composition check ---') try: cif_comp = Composition(dp.cifdata['_chemical_formula_sum']) print('_chemical_formula_sum: {}'.format(cif_comp)) except (KeyError, CompositionError) as e: cif_comp = None print( WEAK_WMSG.format( '_chemical_formula_sum does not exist or cannot be parsed')) try: comp_str = dp.cifdata['_chemical_formula_moiety'] moiety_comps = [Composition(s) for s in comp_str.split(',')] moiety_comps = sorted(moiety_comps, key=lambda x: len(x), reverse=True) print('_chemical_formula_moiety:') for moiety_comp in moiety_comps: print('-- {}'.format(moiety_comp)) except (KeyError, CompositionError) as e: print( WEAK_WMSG.format( '_chemical_formula_moiety does not exist or cannot be parsed')) major_config_structure, major_occu = config_infos[0] print('major config comps: {}'.format(major_config_structure.composition)) print('major config occu: {}'.format(major_occu)) if isinstance(cif_comp, Composition): if not major_config_structure.composition == cif_comp: print( WEAK_WMSG.format( 'major comps does not match _chemical_formula_sum')) try: minor_config_structure, minor_occu = config_infos[1] print('minor config comps: {}'.format( minor_config_structure.composition)) print('minor config occu: {}'.format(minor_occu)) major_minor_comps_match = minor_config_structure.composition == major_config_structure if major_minor_comps_match: print(WEAK_WMSG.format('minor and major comps do not match')) except IndexError: pass print('--- end composition check ---\n') print('--- begin major config check ---') try: major_config = Config.from_labeled_clean_pstructure( major_config_structure, major_occu) except: emsg = 'ERROR: cannot parse major config structure into a config!' print(emsg) raise CuratorError(emsg) structural_schemas['configuration'] = major_config.as_dict() major_config_cif = 'curated_major_config.cif' major_config.pstructure.to('cif', major_config_cif) outputs.append('{}/{}'.format(os.getcwd(), major_config_cif)) if not major_config.molconformers_all_legit(): emsg = 'ERROR: cannot convert all molconformers to rdkit mol' print(emsg) raise CuratorError(emsg) print('major config moiety comps:') mc: MolConformer max_nradicals = 0 imc = 0 for mc in major_config.molconformers: print(' -- imc {}: {}'.format(imc, mc.composition)) try: rdmol, smiles, _, _ = mc.to_rdmol(charged_fragments=False) Chem.SanitizeMol(rdmol) except: print( WMSG.format( 'rdmol sanitize failed, integrity_class is set to 2')) if integrity_class > 2: integrity_class = 2 nradicals = mc.is_missing_hydrogen() print(' missing hydrogen: {}'.format(nradicals)) mc_xyzfils = 'curated_molconformer_{}_{}.xyz'.format(imc, nradicals) mc.to('xyz', mc_xyzfils) outputs.append(mc_xyzfils) if nradicals > max_nradicals: max_nradicals = nradicals imc += 1 molsmiles = [] for mc in sorted(major_config.molconformers, key=lambda x: len(x), reverse=True): # imol == imc, imol is not assigned based on len(mc), so use another for loop to do this molsmiles.append(mc.smiles) structural_schemas['molsmiles'] = list(set(molsmiles)) if max_nradicals: print( WMSG.format( 'major config missing hydrogen, integrity_class is set to 2')) if integrity_class > 2: integrity_class = 2 unique_molgraphs = major_config.molgraph_set() structural_schemas['molgraphs'] = [ umg.as_dict() for umg in sorted(list(unique_molgraphs), key=lambda x: len(x)) ] if len(unique_molgraphs) > 1: emsg = 'ERROR: more than one unique molecule in the major config!' print(emsg) raise CuratorError(emsg) print('--- end major config check ---\n') print('integrity_class: {}'.format(integrity_class)) return integrity_class, outputs, structural_schemas, major_config.hashconfig( )
def read(self): dis_pstructure, dis_unwrap_str, dis_mols, config_infos = self.dp.to_configs(write_files=False, vanilla=True) # if True writes conf_x.cif, configs is a list of pmg Structure self.disorder_class = self.dp.classification self.results['disordered_pstructure'] = dis_unwrap_str self.results['disordered_pmgmols'] = dis_mols config_structures = [] config_occupancies = [] for item in config_infos: config_structures.append(item[0]) config_occupancies.append(item[1]) self.results['config_sturcutures'] = config_structures self.results['config_occupancies'] = config_occupancies configs = [] missh = [] for i in range(len(config_structures)): structure = config_structures[i] conf = Config.from_labeled_clean_pstructure(structure, occu=config_occupancies[i]) config_missingh = False for conformer in conf.molconformers: if conformer.is_missing_hydrogen(): config_missingh = True break if config_missingh: conf.pstructure.to('cif', '{}_mhconf_{}.cif'.format(self.identifier, i)) warnings.warn('missing hydrogens in {}_mhconf_{}.cif'.format(self.identifier, i)) configs.append(conf) missh.append(config_missingh) self.results['configurations'] = configs self.results['missingh'] = missh # these are checked against to configs[0] check_config = configs[0] try: self.results['n_unique_molecule'] = len(check_config.molgraph_set()) self.results['n_molconformers'] = len(check_config.molconformers) self.results['all_molconformers_legit'] = check_config.molconformers_all_legit() self.results['disorder_location'] = self.where_is_disorder(check_config) except: warnings.warn('there are problems in readcif.results, some fileds will be missing!') try: comp = Composition(self.dp.cifdata['_chemical_formula_sum']) self.results['cif_sum_composition'] = comp if not all(self.results['cif_sum_composition'] == mc.composition for mc in check_config.molconformers): self.results['sum_composition_match'] = False print('cif_sum_composition: {}'.format(self.results['cif_sum_composition'])) for mc in check_config.molconformers: print('mc composition: {}'.format(mc.composition)) warnings.warn('moiety sum composition does not match that specified in cif file!') else: self.results['sum_composition_match'] = True except (KeyError, CompositionError) as e: self.results['cif_sum_composition'] = None self.results['sum_composition_match'] = None try: comp_str = self.dp.cifdata['_chemical_formula_moiety'] comps = [Composition(s) for s in comp_str.split(',')] comps = sorted(comps, key=lambda x:len(x), reverse=True) if len(comps) > 1: warnings.warn('more than 1 moiety from cif file! only the largest one is checked!') self.results['cif_moiety_composition'] = comps[0] if not all(self.results['cif_moiety_composition'] == mc.composition for mc in check_config.molconformers): self.results['moiety_composition_match'] = False print('cif_moiety_composition: {}'.format(self.results['cif_moiety_composition'])) for mc in check_config.molconformers: print('mc composition: {}'.format(mc.composition)) warnings.warn('moiety composition does not match that specified in cif file!') else: self.results['moiety_composition_match'] = True except (KeyError, CompositionError) as e: self.results['cif_moiety_composition'] = None self.results['moiety_composition_match'] = None
def __init__(self, struct): """ Args: struct: A pymatgen.core.structure.Structure object. """ block = CifFile.CifBlock() latt = struct.lattice comp = struct.composition no_oxi_comp = Composition.from_formula(comp.formula) block['_symmetry_space_group_name_H-M'] = 'P 1' block['_cell_length_a'] = str(latt.a) block['_cell_length_b'] = str(latt.b) block['_cell_length_c'] = str(latt.c) block['_cell_angle_alpha'] = str(latt.alpha) block['_cell_angle_beta'] = str(latt.beta) block['_cell_angle_gamma'] = str(latt.gamma) block['_chemical_name_systematic'] = "Generated by pymatgen" block['_symmetry_Int_Tables_number'] = 1 block['_chemical_formula_structural'] = str(no_oxi_comp.reduced_formula) block['_chemical_formula_sum'] = str(no_oxi_comp.formula) block['_cell_volume'] = str(latt.volume) reduced_comp = Composition.from_formula(no_oxi_comp.reduced_formula) el = no_oxi_comp.elements[0] amt = comp[el] fu = int(amt / reduced_comp[Element(el.symbol)]) block['_cell_formula_units_Z'] = str(fu) block.AddCifItem(([['_symmetry_equiv_pos_site_id', '_symmetry_equiv_pos_as_xyz']], [[['1'], ['x, y, z']]])) contains_oxidation = True symbol_to_oxinum = dict() try: symbol_to_oxinum = {str(el):el.oxi_state for el in comp.elements} except: symbol_to_oxinum = {el.symbol:0 for el in comp.elements} contains_oxidation = False if contains_oxidation: block.AddCifItem(([['_atom_type_symbol', '_atom_type_oxidation_number']], [[symbol_to_oxinum.keys(), symbol_to_oxinum.values()]])) atom_site_type_symbol = [] atom_site_symmetry_multiplicity = [] atom_site_fract_x = [] atom_site_fract_y = [] atom_site_fract_z = [] atom_site_attached_hydrogens = [] atom_site_B_iso_or_equiv = [] atom_site_label = [] atom_site_occupancy = [] count = 1 for site in struct: for sp, occu in site.species_and_occu.items(): atom_site_type_symbol.append(str(sp)) atom_site_symmetry_multiplicity.append('1') atom_site_fract_x.append(str('{0:f}'.format(site.a))) atom_site_fract_y.append(str('{0:f}'.format(site.b))) atom_site_fract_z.append(str('{0:f}'.format(site.c))) atom_site_attached_hydrogens.append('0') atom_site_B_iso_or_equiv.append('.') atom_site_label.append(str(sp.symbol) + str(count)) atom_site_occupancy.append(str(occu)) count += 1 block['_atom_site_type_symbol'] = atom_site_type_symbol block.AddToLoop('_atom_site_type_symbol', {'_atom_site_label':atom_site_label}) block.AddToLoop('_atom_site_type_symbol', {'_atom_site_symmetry_multiplicity':atom_site_symmetry_multiplicity}) block.AddToLoop('_atom_site_type_symbol', {'_atom_site_fract_x':atom_site_fract_x}) block.AddToLoop('_atom_site_type_symbol', {'_atom_site_fract_y':atom_site_fract_y}) block.AddToLoop('_atom_site_type_symbol', {'_atom_site_fract_z':atom_site_fract_z}) block.AddToLoop('_atom_site_type_symbol', {'_atom_site_attached_hydrogens':atom_site_attached_hydrogens}) block.AddToLoop('_atom_site_type_symbol', {'_atom_site_B_iso_or_equiv':atom_site_B_iso_or_equiv}) block.AddToLoop('_atom_site_type_symbol', {'_atom_site_occupancy':atom_site_occupancy}) self._cf = CifFile.CifFile() self._cf[comp.reduced_formula] = block
def apply_transformation(self, structure, return_ranked_list=False): # Make a mutable structure first mods = Structure.from_sites(structure) for sp, spin in self.mag_species_spin.items(): sp = get_el_sp(sp) oxi_state = getattr(sp, "oxi_state", 0) if spin: up = Specie(sp.symbol, oxi_state, {"spin": abs(spin)}) down = Specie(sp.symbol, oxi_state, {"spin": -abs(spin)}) mods.replace_species({ sp: Composition({ up: self.order_parameter, down: 1 - self.order_parameter }) }) else: mods.replace_species( {sp: Specie(sp.symbol, oxi_state, {"spin": spin})}) if mods.is_ordered: return [mods] if return_ranked_list > 1 else mods enum_args = self.kwargs enum_args["min_cell_size"] = max( int( MagOrderingTransformation.determine_min_cell( structure, self.mag_species_spin, self.order_parameter)), enum_args.get("min_cell_size", 1)) max_cell = enum_args.get('max_cell_size') if max_cell: if enum_args["min_cell_size"] > max_cell: raise ValueError('Specified max cell size is smaller' ' than the minimum enumerable cell size') else: enum_args["max_cell_size"] = enum_args["min_cell_size"] t = EnumerateStructureTransformation(**enum_args) alls = t.apply_transformation(mods, return_ranked_list=return_ranked_list) try: num_to_return = int(return_ranked_list) except ValueError: num_to_return = 1 if num_to_return == 1 or not return_ranked_list: return alls[0]["structure"] if num_to_return else alls m = StructureMatcher(comparator=SpinComparator()) key = lambda x: SpacegroupAnalyzer(x, 0.1).get_space_group_number() out = [] for _, g in groupby(sorted([d["structure"] for d in alls], key=key), key): g = list(g) grouped = m.group_structures(g) out.extend([{ "structure": g[0], "energy": self.energy_model.get_energy(g[0]) } for g in grouped]) self._all_structures = sorted(out, key=lambda d: d["energy"]) return self._all_structures[0:num_to_return]
Created on Wed Feb 28 15:40:02 2018 @author: jherfson """ from pymatgen.analysis.pourbaix.entry import PourbaixEntry, IonEntry #, MultiEntry from pymatgen.analysis.pourbaix.entry import PourbaixEntryIO from pymatgen.analysis.phase_diagram import PDEntry from pymatgen.core.ion import Ion from pymatgen.core.structure import Composition 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)
def test_get_decomposition(self): for entry in self.pd.stable_entries: self.assertEquals(len(self.analyzer.get_decomposition(entry.composition)), 1, "Stable composition should have only 1 decomposition!") dim = len(self.pd.elements) for entry in self.pd.all_entries: ndecomp = len(self.analyzer.get_decomposition(entry.composition)) self.assertTrue(ndecomp > 0 and ndecomp <= dim, "The number of decomposition phases can at most be equal to the number of components.") #Just to test decomp for a ficitious composition ansdict = {entry.composition.formula: amt for entry, amt in self.analyzer.get_decomposition(Composition.from_formula("Li3Fe7O11")).items()} expected_ans = {"Fe2 O2" : 0.0952380952380949, "Li1 Fe1 O2": 0.5714285714285714, "Fe6 O8": 0.33333333333333393} for k, v in expected_ans.items(): self.assertAlmostEqual(ansdict[k], v)
def test_sub(self): self.assertEqual((self.comp[0] - Composition.from_formula("Li2O")).formula, "Li1 Fe2 P3 O11", "Incorrect composition after addition!") self.assertEqual((self.comp[0] - {"Fe":2, "O":3}).formula, "Li3 P3 O9")
def setUp(self): self.comp = list() self.comp.append(Composition.from_formula("Li3Fe2(PO4)3")) self.comp.append(Composition.from_formula("Li3Fe(PO4)O")) self.comp.append(Composition.from_formula("LiMn2O4")) self.comp.append(Composition.from_formula("Li4O4")) self.comp.append(Composition.from_formula("Li3Fe2Mo3O12")) self.comp.append(Composition.from_formula("Li3Fe2((PO4)3(CO3)5)2")) self.comp.append(Composition.from_formula("Li1.5Si0.5")) self.indeterminate_comp = list() self.indeterminate_comp.append(Composition.ranked_compositions_from_indeterminate_formula("Co1", True)) self.indeterminate_comp.append(Composition.ranked_compositions_from_indeterminate_formula("Co1", False)) self.indeterminate_comp.append(Composition.ranked_compositions_from_indeterminate_formula("co2o3")) self.indeterminate_comp.append(Composition.ranked_compositions_from_indeterminate_formula("ncalu")) self.indeterminate_comp.append(Composition.ranked_compositions_from_indeterminate_formula("calun")) self.indeterminate_comp.append(Composition.ranked_compositions_from_indeterminate_formula("liCoo2n (pO4)2")) self.indeterminate_comp.append(Composition.ranked_compositions_from_indeterminate_formula("(co)2 (PO)4")) self.indeterminate_comp.append(Composition.ranked_compositions_from_indeterminate_formula("Fee3"))
def test_reduced_composition(self): correct_reduced_formulas = ['Li3Fe2(PO4)3', 'Li3FePO5', 'LiMn2O4', 'Li2O2', 'Li3Fe2(MoO4)3', 'Li3Fe2P6(C5O27)2', 'Li1.5Si0.5'] for i in xrange(len(self.comp)): self.assertEqual(self.comp[i].get_reduced_composition_and_factor()[0], Composition.from_formula(correct_reduced_formulas[i]))
def test_normalize_to(self): products = [Composition.from_formula("Fe"), Composition.from_formula("O2")] reactants = [Composition.from_formula("Fe2O3")] rxn = Reaction(reactants, products) rxn.normalize_to(Composition.from_formula("Fe"), 3) self.assertEquals(str(rxn), "1.500 Fe2O3 -> 3.000 Fe + 2.250 O2", "Wrong reaction obtained!")
def test_init(self): reactants = [Composition.from_formula("Fe"), Composition.from_formula("O2")] products = [Composition.from_formula("Fe2O3")] rxn = Reaction(reactants, products) self.assertEquals(str(rxn), "2.000 Fe + 1.500 O2 -> 1.000 Fe2O3", "Wrong reaction obtained!") self.assertEquals(rxn.normalized_repr, "4 Fe + 3 O2 -> 2 Fe2O3", "Wrong normalized reaction obtained!") reactants = [Composition.from_formula("Fe"), Composition.from_formula("O"), Composition.from_formula("Mn"), Composition.from_formula("P")] products = [Composition.from_formula("FeP"), Composition.from_formula("MnO")] rxn = Reaction(reactants, products) self.assertEquals(str(rxn), "1.000 Fe + 0.500 O2 + 1.000 Mn + 1.000 P -> 1.000 FeP + 1.000 MnO", "Wrong reaction obtained!") self.assertEquals(rxn.normalized_repr, "2 Fe + O2 + 2 Mn + 2 P -> 2 FeP + 2 MnO", "Wrong normalized reaction obtained!") reactants = [Composition.from_formula("FePO4")] products = [Composition.from_formula("FePO4")] rxn = Reaction(reactants, products) self.assertEquals(str(rxn), "1.000 FePO4 -> 1.000 FePO4", "Wrong reaction obtained!") products = [Composition.from_formula("Ti2 O4"), Composition.from_formula("O1")] reactants = [Composition.from_formula("Ti1 O2")] rxn = Reaction(reactants, products) self.assertEquals(str(rxn), "2.000 TiO2 -> 2.000 TiO2", "Wrong reaction obtained!") reactants = [Composition.from_formula("FePO4"), Composition.from_formula("Li")] products = [Composition.from_formula("LiFePO4")] rxn = Reaction(reactants, products) self.assertEquals(str(rxn), "1.000 FePO4 + 1.000 Li -> 1.000 LiFePO4", "Wrong reaction obtained!") reactants = [Composition.from_formula("MgO")] products = [Composition.from_formula("MgO")] rxn = Reaction(reactants, products) self.assertEquals(str(rxn), "1.000 MgO -> 1.000 MgO", "Wrong reaction obtained!") reactants = [Composition.from_formula("Mg")] products = [Composition.from_formula("Mg")] rxn = Reaction(reactants, products) self.assertEquals(str(rxn), "1.000 Mg -> 1.000 Mg", "Wrong reaction obtained!") reactants = [Composition.from_formula("FePO4"), Composition.from_formula("LiPO3")] products = [Composition.from_formula("LiFeP2O7")] rxn = Reaction(reactants, products) self.assertEquals(str(rxn), "1.000 FePO4 + 1.000 LiPO3 -> 1.000 LiFeP2O7", "Wrong reaction obtained!") reactants = [Composition.from_formula("Na"), Composition.from_formula("K2O")] products = [Composition.from_formula("Na2O"), Composition.from_formula("K")] rxn = Reaction(reactants, products) self.assertEquals(str(rxn), "1.000 Na + 0.500 K2O -> 0.500 Na2O + 1.000 K", "Wrong reaction obtained!") # Test for an old bug which has a problem when excess product is defined. products = [Composition.from_formula("FePO4"), Composition.from_formula("O")] reactants = [Composition.from_formula("FePO4")] rxn = Reaction(reactants, products) self.assertEquals(str(rxn), "1.000 FePO4 -> 1.000 FePO4", "Wrong reaction obtained!") products = map(Composition.from_formula, ['La8Ti8O12', 'O2', 'LiCrO2']) reactants = [Composition.from_formula('LiLa3Ti3CrO12')] rxn = Reaction(reactants, products) self.assertEquals(str(rxn), "1.000 LiLa3Ti3CrO12 -> 1.500 La2Ti2O3 + 2.750 O2 + 1.000 LiCrO2", "Wrong reaction obtained!")
def test_from_dict(self): sym_dict = {"Fe":6, "O" :8} self.assertEqual(Composition.from_dict(sym_dict).reduced_formula, "Fe3O4", "Creation form sym_amount dictionary failed!")