def test_composition_to_oxidcomposition(self): df = DataFrame(data={"composition": [Composition("Fe2O3")]}) cto = CompositionToOxidComposition() df = cto.featurize_dataframe(df, 'composition') self.assertEqual(df["composition_oxid"].tolist()[0], Composition({ "Fe3+": 2, "O2-": 3 })) # test error handling df = DataFrame(data={"composition": [Composition("Fe2O3")]}) cto = CompositionToOxidComposition(return_original_on_error=False, max_sites=2) self.assertRaises(ValueError, cto.featurize_dataframe, df, 'composition') # check non oxi state structure returned correctly cto = CompositionToOxidComposition(return_original_on_error=True, max_sites=2) df = cto.featurize_dataframe(df, 'composition') self.assertEqual(df["composition_oxid"].tolist()[0], Composition({ "Fe": 2, "O": 3 }))
def std_rel_energies(self) -> Tuple["StandardEnergies", "RelativeEnergies"]: std, rel = StandardEnergies(), RelativeEnergies() abs_energies_per_atom = {k.reduced_formula: v.energy / k.num_atoms for k, v in self.items()} std_energies_list = [] for vertex_element in self.elements: # This target is needed as some reduced formulas shows molecule # ones such as H2 and O2. reduced_formula = Composition({vertex_element: 1.0}).reduced_formula candidates = filter(lambda x: x[0] == reduced_formula, abs_energies_per_atom.items()) try: min_abs_energy = min([abs_energy_per_atom[1] for abs_energy_per_atom in candidates]) except ValueError: print(f"Element {vertex_element} does not exist in " f"CompositionEnergies.") raise NoElementEnergyError std[vertex_element] = min_abs_energy std_energies_list.append(min_abs_energy) for formula, abs_energy_per_atom in abs_energies_per_atom.items(): if Composition(formula).is_element: continue frac = atomic_fractions(formula, self.elements) offset = sum([a * b for a, b in zip(frac, std_energies_list)]) rel[formula] = abs_energy_per_atom - offset return std, rel
def test_fit(self): """ Take two known matched structures 1) Ensure match 2) Ensure match after translation and rotations 3) Ensure no-match after large site translation 4) Ensure match after site shuffling """ sm = StructureMatcher() self.assertTrue(sm.fit(self.struct_list[0], self.struct_list[1])) # Test rotational/translational invariance op = SymmOp.from_axis_angle_and_translation([0, 0, 1], 30, False, np.array([0.4, 0.7, 0.9])) self.struct_list[1].apply_operation(op) self.assertTrue(sm.fit(self.struct_list[0], self.struct_list[1])) #Test failure under large atomic translation self.struct_list[1].translate_sites([0], [.4, .4, .2], frac_coords=True) self.assertFalse(sm.fit(self.struct_list[0], self.struct_list[1])) self.struct_list[1].translate_sites([0], [-.4, -.4, -.2], frac_coords=True) # random.shuffle(editor._sites) self.assertTrue(sm.fit(self.struct_list[0], self.struct_list[1])) #Test FrameworkComporator sm2 = StructureMatcher(comparator=FrameworkComparator()) lfp = read_structure(os.path.join(test_dir, "LiFePO4.cif")) nfp = read_structure(os.path.join(test_dir, "NaFePO4.cif")) self.assertTrue(sm2.fit(lfp, nfp)) self.assertFalse(sm.fit(lfp, nfp)) #Test anonymous fit. self.assertEqual(sm.fit_anonymous(lfp, nfp), {Composition("Li"): Composition("Na")}) self.assertAlmostEqual(sm.get_minimax_rms_anonymous(lfp, nfp)[0], 0.096084154118549828) #Test partial occupancies. s1 = Structure([[3, 0, 0], [0, 3, 0], [0, 0, 3]], [{"Fe": 0.5}, {"Fe": 0.5}, {"Fe": 0.5}, {"Fe": 0.5}], [[0, 0, 0], [0.25, 0.25, 0.25], [0.5, 0.5, 0.5], [0.75, 0.75, 0.75]]) s2 = Structure([[3, 0, 0], [0, 3, 0], [0, 0, 3]], [{"Fe": 0.25}, {"Fe": 0.5}, {"Fe": 0.5}, {"Fe": 0.75}], [[0, 0, 0], [0.25, 0.25, 0.25], [0.5, 0.5, 0.5], [0.75, 0.75, 0.75]]) self.assertFalse(sm.fit(s1, s2)) self.assertFalse(sm.fit(s2, s1)) s2 = Structure([[3, 0, 0], [0, 3, 0], [0, 0, 3]], [{"Fe": 0.25}, {"Fe": 0.25}, {"Fe": 0.25}, {"Fe": 0.25}], [[0, 0, 0], [0.25, 0.25, 0.25], [0.5, 0.5, 0.5], [0.75, 0.75, 0.75]]) self.assertEqual(sm.fit_anonymous(s1, s2), {Composition("Fe0.5"): Composition("Fe0.25")}) self.assertAlmostEqual(sm.get_minimax_rms_anonymous(s1, s2)[0], 0)
def test_electronegativity(self): sm = StructureMatcher(ltol=0.2, stol=0.3, angle_tol=5) s1 = read_structure(os.path.join(test_dir, "Na2Fe2PAsO4S4.cif")) s2 = read_structure(os.path.join(test_dir, "Na2Fe2PNO4Se4.cif")) self.assertAlmostEqual(sm.fit_with_electronegativity(s1, s2), {Composition('S'): Composition('Se'), Composition('As'): Composition('N')})
def mp_query_mock(mocker): mock = mocker.patch( "pydefect.cli.vasp.make_composition_energies_from_mp.MpEntries") mock.return_value.materials = \ [ComputedEntry(Composition("O8"), -39.58364375, entry_id="mp-1"), ComputedEntry(Composition("Mg3"), -4.79068775, entry_id="mp-2"), ComputedEntry(Composition("Mg1O1"), -11.96742144, entry_id="mp-3")] return mock
def side_effect_structure(key): mock_structure = mocker.Mock() if key == Path("Mg") / defaults.contcar: mock_structure.composition = Composition("Mg2") elif key == Path("O") / defaults.contcar: mock_structure.composition = Composition("O2") else: raise ValueError return mock_structure
def comp_energies(): return CompositionEnergies({ Composition('O8'): CompositionEnergy(-39.58364375, "mp-1"), Composition('Mg3'): CompositionEnergy(-4.79068775, "mp-2"), Composition('Mg1O1'): CompositionEnergy(-11.96742144, "mp-3") })
def comp_energies_corr(): return CompositionEnergies({ Composition('O8'): CompositionEnergy(-39.58364375 + diff["O"] * 8, "mp-1"), Composition('Mg3'): CompositionEnergy(-4.79068775 + diff["Mg"] * 3, "mp-2"), Composition('Mg1O1'): CompositionEnergy(-11.96742144 + diff["Mg"] + diff["O"], "mp-3") })
def formula_to_criteria(formula: str) -> Dict: """ Santizes formula into a dictionary to search with wild cards Arguments: formula: a chemical formula with wildcards in it for unknown elements Returns: Mongo style search criteria for this formula """ dummies = "ADEGJLMQRXZ" if "*" in formula: # Wild card in formula nstars = formula.count("*") formula_dummies = formula.replace("*", "{}").format(*dummies[:nstars]) integer_formula = Composition( formula_dummies).get_integer_formula_and_factor()[0] comp = Composition(integer_formula).reduced_composition crit = dict() crit["formula_anonymous"] = comp.anonymized_formula real_elts = [ str(e) for e in comp.elements if not e.as_dict().get("element", "A") in dummies ] for el, n in comp.to_reduced_dict.items(): if el in real_elts: crit[f"composition_reduced.{el}"] = n return crit elif "-" in formula: crit = {} eles = formula.split("-") chemsys = "-".join(sorted(eles)) crit["chemsys"] = chemsys return crit elif any(isinstance(el, DummySpecies) for el in Composition(formula)): # Assume fully anonymized formula return {"formula_anonymous": Composition(formula).anonymized_formula} else: comp = Composition(formula) # Paranoia below about floating-point "equality" crit = {} crit["nelements"] = len(comp) for el, n in comp.to_reduced_dict.items(): crit[f"composition_reduced.{el}"] = n return crit
def test_composition_to_structurefromMP(self): df = DataFrame(data={"composition": [Composition("Fe2O3"), Composition("N9Al34Fe234")]}) cto = CompositionToStructureFromMP() df = cto.featurize_dataframe(df, 'composition') structures = df["structure"].tolist() self.assertTrue(isinstance(structures[0], Structure)) self.assertGreaterEqual(len(structures[0]), 5) # has at least 5 sites self.assertTrue(math.isnan(structures[1]))
def setUp(self): self.df = pd.DataFrame({ "composition": [ Composition("Fe2O3"), Composition({ Specie("Fe", 2): 1, Specie("O", -2): 1 }) ] })
def test_get_sc_structures(self): dist_ref = self.m_hop.length start, end, b_sc = self.m_hop.get_sc_structures(vac_mode=False) start_site = next( filter(lambda x: x.species_string == "Li", start.sites)) end_site = next(filter(lambda x: x.species_string == "Li", end.sites)) assert start.composition == end.composition == Composition( "Li1 Fe24 P24 O96") assert b_sc.composition == Composition("Fe24 P24 O96") self.assertAlmostEqual(start_site.distance(end_site), dist_ref, 3)
def test_remove_higher_energy_comp(): comp_es = CompositionEnergies( {Composition('Mg1'): CompositionEnergy(-1.9, "mp-2"), Composition('Mg2'): CompositionEnergy(-4.0, "mp-3"), Composition('O2'): CompositionEnergy(-4.0, "mp-4")}) actual = remove_higher_energy_comp(comp_es) expected = CompositionEnergies( {Composition('Mg2'): CompositionEnergy(-4.0, "mp-3"), Composition('O2'): CompositionEnergy(-4.0, "mp-4"),}) assert actual == expected
def side_effect(key): mock_vasprun = mocker.Mock() if key == Path("Mg") / defaults.vasprun: mock_vasprun.final_structure.composition = Composition("Mg2") mock_vasprun.final_energy = -10 elif key == Path("O") / defaults.vasprun: mock_vasprun.final_structure.composition = Composition("O2") mock_vasprun.final_energy = -20 else: raise ValueError return mock_vasprun
def remove_higher_energy_comp(comp_energies: Dict[Composition, CompositionEnergy]): _l = [[k, v] for k, v in comp_energies.items()] result = {} for _, grouped_k_v in groupby( _l, key=lambda x: Composition(x[0]).reduced_formula): formula, comp_e = min(list(grouped_k_v), key=lambda y: (y[1].energy / Composition(y[0]).num_atoms)) result[formula] = comp_e return result
def test_str_to_composition(self): d = {'comp_str': ["Fe2", "MnO2"]} df = DataFrame(data=d) df["composition"] = str_to_composition(df["comp_str"]) self.assertEqual(df["composition"].tolist(), [Composition("Fe2"), Composition("MnO2")]) df["composition_red"] = str_to_composition(df["comp_str"], reduce=True) self.assertEqual(df["composition_red"].tolist(), [Composition("Fe"), Composition("MnO2")])
def test_is_ionic(self): """Test checking whether a compound is ionic""" self.assertTrue( is_ionic(Composition({ Specie("Fe", 2): 1, Specie("O", -2): 1 }))) self.assertFalse( is_ionic(Composition({ Specie("Fe", 0): 1, Specie("Al", 0): 1 })))
def test_structure_to_composition(self): coords = [[0, 0, 0], [0.75, 0.5, 0.75]] lattice = Lattice([[3.8401979337, 0.00, 0.00], [1.9200989668, 3.3257101909, 0.00], [0.00, -2.2171384943, 3.1355090603]]) struct = Structure(lattice, ["Si"] * 2, coords) df = DataFrame(data={'structure': [struct]}) df["composition"] = structure_to_composition(df["structure"]) self.assertEqual(df["composition"].tolist()[0], Composition("Si2")) df["composition_red"] = structure_to_composition(df["structure"], reduce=True) self.assertEqual(df["composition_red"].tolist()[0], Composition("Si"))
def test_str_to_composition(self): d = {'comp_str': ["Fe2", "MnO2"]} df = DataFrame(data=d) df = StrToComposition().featurize_dataframe(df, 'comp_str') self.assertEqual(df["composition"].tolist(), [Composition("Fe2"), Composition("MnO2")]) stc = StrToComposition(reduce=True, target_col_id='composition_red') df = stc.featurize_dataframe(df, 'comp_str') self.assertEqual(df["composition_red"].tolist(), [Composition("Fe"), Composition("MnO2")])
def test_stoich(self): featurizer = Stoichiometry(num_atoms=True) df_stoich = Stoichiometry(num_atoms=True).featurize_dataframe( self.df, col_id="composition") self.assertAlmostEqual(df_stoich["num atoms"][0], 5) self.assertAlmostEqual(df_stoich["0-norm"][0], 2) self.assertAlmostEqual(df_stoich["7-norm"][0], 0.604895199) # Test whether the number of formula units affects result original_value = featurizer.featurize(Composition("FeO")) self.assertArrayAlmostEqual( featurizer.featurize(Composition("Fe0.5O0.5")), original_value) self.assertArrayAlmostEqual(featurizer.featurize(Composition("Fe2O2")), original_value)
def get_composition_from_string(comp_str): """validate and return composition from string `comp_str`.""" from pymatgen.core import Composition, Element comp = Composition(comp_str) for element in comp.elements: Element(element) formula = comp.get_integer_formula_and_factor()[0] comp = Composition(formula) return "".join( [ "{}{}".format(key, int(value) if value > 1 else "") for key, value in comp.as_dict().items() ] )
def test_miedema_all(self): df = pd.DataFrame({ "composition": [ Composition("TiZr"), Composition("Mg10Cu50Ca40"), Composition("Fe2O3") ] }) miedema = Miedema(struct_types='all') self.assertTrue(miedema.precheck(df["composition"].iloc[0])) self.assertFalse(miedema.precheck(df["composition"].iloc[-1])) self.assertAlmostEqual(miedema.precheck_dataframe(df, "composition"), 2 / 3) # test precheck for oxidation-state decorated compositions df = CompositionToOxidComposition(return_original_on_error=True).\ featurize_dataframe(df, 'composition') self.assertTrue(miedema.precheck(df["composition_oxid"].iloc[0])) self.assertFalse(miedema.precheck(df["composition_oxid"].iloc[-1])) self.assertAlmostEqual( miedema.precheck_dataframe(df, "composition_oxid"), 2 / 3) mfps = miedema.featurize_dataframe(df, col_id="composition") self.assertAlmostEqual(mfps['Miedema_deltaH_inter'][0], -0.003445022152) self.assertAlmostEqual(mfps['Miedema_deltaH_amor'][0], 0.0707658836300) self.assertAlmostEqual(mfps['Miedema_deltaH_ss_min'][0], 0.03663599755) self.assertAlmostEqual(mfps['Miedema_deltaH_inter'][1], -0.235125978427) self.assertAlmostEqual(mfps['Miedema_deltaH_amor'][1], -0.164541848271) self.assertAlmostEqual(mfps['Miedema_deltaH_ss_min'][1], -0.05280843311) self.assertAlmostEqual(math.isnan(mfps['Miedema_deltaH_inter'][2]), True) self.assertAlmostEqual(math.isnan(mfps['Miedema_deltaH_amor'][2]), True) self.assertAlmostEqual(math.isnan(mfps['Miedema_deltaH_ss_min'][2]), True) # make sure featurization works equally for compositions with or without # oxidation states mfps = miedema.featurize_dataframe(df, col_id="composition_oxid") self.assertAlmostEqual(mfps['Miedema_deltaH_inter'][0], -0.003445022152) self.assertAlmostEqual(mfps['Miedema_deltaH_amor'][0], 0.0707658836300) self.assertAlmostEqual(mfps['Miedema_deltaH_ss_min'][0], 0.03663599755)
def update_sites(self, directory, ignore_magmom=False): """ Based on the CONTCAR and OUTCAR of a geometry optimization, update the site coordinates and magnetic moments that were optimized. Note that this method relies on the cation configuration of the cathode not having changed. Args: directory (str): Directory in which the geometry optimization output files (i.e. CONTCAR and OUTCAR) are stored. ignore_magmom (bool): Flag that indicates that the final magnetic moments of the optimized structure should be ignored. This means that the magnetic moments of the Cathode structure will remain the same. Returns: None """ new_cathode = Cathode.from_file(os.path.join(directory, "CONTCAR")) out = Outcar(os.path.join(directory, "OUTCAR")) if ignore_magmom: magmom = [site.properties["magmom"] for site in self.sites if site.species != Composition()] else: magmom = [site["tot"] for site in out.magnetization] if len(out.magnetization) != 0: new_cathode.add_site_property("magmom", magmom) # Update the lattice self.lattice = new_cathode.lattice # Update the coordinates of the occupied sites. new_index = 0 for i, site in enumerate(self): # If the site is not empty if site.species != Composition(): new_site = new_cathode.sites[new_index] # Update the site coordinates self.replace(i, species=new_site.species, coords=new_site.frac_coords, properties=new_site.properties) new_index += 1
def run(self): logger.info("Starting FileMaterials Builder.") with open(self._data_file, 'rt') as f: line_no = 0 lines = [line for line in f] # only good for smaller files pbar = tqdm(lines) for line in pbar: line = line.strip() if line and not line.startswith("#"): line_no += 1 if line_no > self.header_lines: line = line.split(self._delimiter) if "-" in line[0]: search_val = line[0] search_key = "material_id" else: search_key = "formula_reduced_abc" search_val = Composition(line[0]).\ reduced_composition.alphabetical_formula key = line[1] val = line[2] try: val = float(val) except: pass self._materials.update({search_key: search_val}, {"$set": {key: val}}) logger.info("FileMaterials Builder finished processing")
def prepare_entry(structure_type, tot_e, species): """ Prepare entries from total energy and species. Args: structure_type(str): "garnet" or "perovskite" tot_e (float): total energy in eV/f.u. species (dict): species in dictionary. Returns: ce (ComputedEntry) """ formula = spe2form(structure_type, species) composition = Composition(formula) elements = [el.name for el in composition] potcars = ["pbe %s" % CONFIG['POTCAR'][el] for el in elements] parameters = {"potcar_symbols": list(potcars), "oxide_type": 'oxide'} for el in elements: if el in LDAUU: parameters.update({"hubbards": {el: LDAUU[el]}}) ce = ComputedEntry(composition=composition, energy=0, parameters=parameters) ce.uncorrected_energy = tot_e compat = MaterialsProjectCompatibility() ce = compat.process_entry(ce) # Correction added return ce
def update_heatmap_choices(entries): if not entries: raise PreventUpdate options = [] for entry in entries: if entry["entry_id"].startswith("mp"): composition = Composition(entry["entry"]["composition"]) formula = unicodeify(composition.reduced_formula) mpid = entry["entry_id"] options.append({ "label": f"{formula} ({mpid})", "value": mpid }) heatmap_options = self.get_choice_input( "heatmap_choice", state={}, label="Heatmap Entry", help_str="Choose the entry to use for heatmap generation.", options=options, disabled=False, ) return heatmap_options
def make_poscars_from_query(materials_query: List[dict], path: Path) -> None: for query in materials_query: reduced_formula = Composition(query["full_formula"]).reduced_formula if reduced_formula in MOLECULE_DATA.keys(): _make_molecular_directory(path, reduced_formula) else: _make_solid_directory(path, reduced_formula, query)
def test_band_center(self): df_band_center = BandCenter().featurize_dataframe(self.df, col_id="composition") self.assertAlmostEqual(df_band_center["band center"][0], -2.672486385) self.assertAlmostEqual( BandCenter().featurize(Composition('Ag33O500V200'))[0], -2.7337150991)
def test_structure_to_composition(self): coords = [[0, 0, 0], [0.75, 0.5, 0.75]] lattice = Lattice([[3.8401979337, 0.00, 0.00], [1.9200989668, 3.3257101909, 0.00], [0.00, -2.2171384943, 3.1355090603]]) struct = Structure(lattice, ["Si"] * 2, coords) df = DataFrame(data={'structure': [struct]}) stc = StructureToComposition() df = stc.featurize_dataframe(df, 'structure') self.assertEqual(df["composition"].tolist()[0], Composition("Si2")) stc = StructureToComposition(reduce=True, target_col_id='composition_red') df = stc.featurize_dataframe(df, 'structure') self.assertEqual(df["composition_red"].tolist()[0], Composition("Si"))
def FullChemicalPotentialWindow(target_phase, key_element): chemsys = key_element + '-' + Composition(target_phase).chemical_system with MPRester(api_key='') as mpr: entries = mpr.get_entries_in_chemsys(chemsys) pd_closed = PhaseDiagram(entries) transition_chempots = pd_closed.get_transition_chempots( Element(key_element)) # The exact mu value used to construct the plot for each range is # the average of the endpoints of the range, with the exception of the last range, # which is plotted at the value of the last endpoint minus 0.1. # https://matsci.org/t/question-on-phase-diagram-app-chemical-potential/511 average_chempots = [] if len(transition_chempots) > 1: for i in range(len(transition_chempots) - 1): ave = (transition_chempots[i] + transition_chempots[i + 1]) / 2 average_chempots.append(ave) average_chempots.append(transition_chempots[-1] - 0.1) elif len(transition_chempots) == 1: # prepare for binary systems, of which two endnodes tielined directly, like Li-Zr average_chempots.append(transition_chempots[0]) average_chempots = np.round(average_chempots, 3) boolean_list = [] for chempot in average_chempots: # GrandPotentialPhaseDiagram works good even for binary systems to find stable phases pd_open = GrandPotentialPhaseDiagram(entries, {Element(key_element): chempot}) stable_phases = [entry.name for entry in pd_open.stable_entries] boolean_list.append(target_phase in stable_phases) return (False not in boolean_list)