def setUp(self): self.temps = [300, 600, 900, 1200, 1500, 1800] self.struct = vasprun.final_structure self.num_atoms = self.struct.composition.num_atoms self.entries_with_temps = { temp: GibbsComputedStructureEntry( self.struct, -2.436, temp=temp, gibbs_model="SISSO", parameters=vasprun.incar, entry_id="test", ) for temp in self.temps } with open( os.path.join(PymatgenTest.TEST_FILES_DIR, "Mn-O_entries.json"), "r") as f: data = json.load(f) with open( os.path.join(PymatgenTest.TEST_FILES_DIR, "structure_CO2.json"), "r") as f: self.co2_struct = MontyDecoder().process_decoded(json.load(f)) self.mp_entries = [MontyDecoder().process_decoded(d) for d in data]
def get_gibbs_computed_structure_entries(self, entries): entries_comp = [entry.composition for entry in entries] ele_entries = self.get_element_entries(entries) gibbs_computed_structure_entries = GibbsComputedStructureEntry.from_entries( list(set(entries + ele_entries)), temp=self.temp) return [ entry for entry in gibbs_computed_structure_entries if entry.composition in entries_comp ]
def setUp(self): self.temps = [300, 600, 900, 1200, 1500, 1800] self.struct = vasprun.final_structure self.num_atoms = self.struct.composition.num_atoms self.temp_entries = { temp: GibbsComputedStructureEntry( self.struct, -2.436 * self.num_atoms, temp=temp, parameters=vasprun.incar, entry_id="test", ) for temp in self.temps } with open(os.path.join(test_dir, "Mn-O_entries.json"), "r") as f: data = json.load(f) self.mp_entries = [MontyDecoder().process_decoded(d) for d in data]
def _filter_entries(all_entries, e_above_hull, temp, include_polymorphs=False): """ Helper method for filtering entries by specified energy above hull Args: all_entries ([ComputedEntry]): List of ComputedEntry-like objects to be filtered e_above_hull (float): Thermodynamic stability threshold (energy above hull) [eV/atom] include_polymorphs (bool): whether to include higher energy polymorphs of existing structures Returns: [ComputedEntry]: list of all entries with energies above hull equal to or less than the specified e_above_hull. """ pd_dict = expand_pd(all_entries) pd_dict = { chemsys: PhaseDiagram(GibbsComputedStructureEntry.from_pd(pd, temp)) for chemsys, pd in pd_dict.items() } filtered_entries = set() all_comps = dict() for chemsys, pd in pd_dict.items(): for entry in pd.all_entries: if (entry in filtered_entries or pd.get_e_above_hull(entry) > e_above_hull): continue formula = entry.composition.reduced_formula if not include_polymorphs and (formula in all_comps): if all_comps[ formula].energy_per_atom < entry.energy_per_atom: continue filtered_entries.remove(all_comps[formula]) all_comps[formula] = entry filtered_entries.add(entry) return pd_dict, list(filtered_entries)
def get_entries_in_chemsys( self, elements: Union[str, List[str]], use_gibbs: Optional[int] = None, ): """ Helper method to get a list of ComputedEntries in a chemical system. For example, elements = ["Li", "Fe", "O"] will return a list of all entries in the Li-Fe-O chemical system, i.e., all LixOy, FexOy, LixFey, LixFeyOz, Li, Fe and O phases. Extremely useful for creating phase diagrams of entire chemical systems. Args: elements (str or [str]): Chemical system string comprising element symbols separated by dashes, e.g., "Li-Fe-O" or List of element symbols, e.g., ["Li", "Fe", "O"]. use_gibbs: If None (default), DFT energy is returned. If a number, return the free energy of formation estimated using a machine learning model (see GibbsComputedStructureEntry). The number is the temperature in Kelvin at which to estimate the free energy. Must be between 300 K and 2000 K. Returns: List of ComputedEntries. """ if isinstance(elements, str): elements = elements.split("-") all_chemsyses = [] for i in range(len(elements)): for els in itertools.combinations(elements, i + 1): all_chemsyses.append("-".join(sorted(els))) entries = [] # type: List[ComputedEntry] entries.extend(self.get_entries(all_chemsyses)) if use_gibbs: # replace the entries with GibbsComputedStructureEntry from pymatgen.entries.computed_entries import GibbsComputedStructureEntry entries = GibbsComputedStructureEntry.from_entries(entries, temp=use_gibbs) return entries
def setUp(self): with pytest.warns(FutureWarning, match="MaterialsProjectCompatibility will be updated"): self.temps = [300, 600, 900, 1200, 1500, 1800] self.struct = vasprun.final_structure self.num_atoms = self.struct.composition.num_atoms self.temp_entries = { temp: GibbsComputedStructureEntry( self.struct, -2.436 * self.num_atoms, temp=temp, parameters=vasprun.incar, entry_id="test", ) for temp in self.temps } with open(os.path.join(PymatgenTest.TEST_FILES_DIR, "Mn-O_entries.json"), "r") as f: data = json.load(f) self.mp_entries = [MontyDecoder().process_decoded(d) for d in data]
def test_to_from_dict(self): test_entry = self.temp_entries[300] d = test_entry.as_dict() e = GibbsComputedStructureEntry.from_dict(d) self.assertAlmostEqual(e.energy, test_entry.energy)
def test_from_pd(self): pd = PhaseDiagram(self.mp_entries) gibbs_entries = GibbsComputedStructureEntry.from_pd(pd) self.assertIsNotNone(gibbs_entries)
def test_from_entries(self): gibbs_entries = GibbsComputedStructureEntry.from_entries( self.mp_entries) self.assertIsNotNone(gibbs_entries)
def test_interpolation(self): temp = 450 e = GibbsComputedStructureEntry(self.struct, -2.436 * self.num_atoms, temp=temp) self.assertAlmostEqual(e.energy, -53.7243542548528)
def test_expt_gas_entry(self): co2_entry = GibbsComputedStructureEntry(self.co2_struct, 0, temp=900) self.assertAlmostEqual(co2_entry.energy, -16.406560223724014) self.assertAlmostEqual(co2_entry.energy_per_atom, -1.3672133519770011)
def get_pourbaix_entries( self, chemsys: Union[str, List], solid_compat="MaterialsProject2020Compatibility", use_gibbs: Optional[Literal[300]] = None, ): """ A helper function to get all entries necessary to generate a Pourbaix diagram from the rest interface. Args: chemsys (str or [str]): Chemical system string comprising element symbols separated by dashes, e.g., "Li-Fe-O" or List of element symbols, e.g., ["Li", "Fe", "O"]. solid_compat: Compatiblity scheme used to pre-process solid DFT energies prior to applying aqueous energy adjustments. May be passed as a class (e.g. MaterialsProject2020Compatibility) or an instance (e.g., MaterialsProject2020Compatibility()). If None, solid DFT energies are used as-is. Default: MaterialsProject2020Compatibility use_gibbs: Set to 300 (for 300 Kelvin) to use a machine learning model to estimate solid free energy from DFT energy (see GibbsComputedStructureEntry). This can slightly improve the accuracy of the Pourbaix diagram in some cases. Default: None. Note that temperatures other than 300K are not permitted here, because MaterialsProjectAqueousCompatibility corrections, used in Pourbaix diagram construction, are calculated based on 300 K data. """ # imports are not top-level due to expense from pymatgen.analysis.pourbaix_diagram import PourbaixEntry from pymatgen.entries.compatibility import ( Compatibility, MaterialsProject2020Compatibility, MaterialsProjectAqueousCompatibility, MaterialsProjectCompatibility, ) from pymatgen.entries.computed_entries import ComputedEntry if solid_compat == "MaterialsProjectCompatibility": solid_compat = MaterialsProjectCompatibility() elif solid_compat == "MaterialsProject2020Compatibility": solid_compat = MaterialsProject2020Compatibility() elif isinstance(solid_compat, Compatibility): solid_compat = solid_compat else: raise ValueError( "Solid compatibility can only be 'MaterialsProjectCompatibility', " "'MaterialsProject2020Compatibility', or an instance of a Compatability class" ) pbx_entries = [] if isinstance(chemsys, str): chemsys = chemsys.split("-") # capitalize and sort the elements chemsys = sorted(e.capitalize() for e in chemsys) # Get ion entries first, because certain ions have reference # solids that aren't necessarily in the chemsys (Na2SO4) # download the ion reference data from MPContribs ion_data = self.get_ion_reference_data_for_chemsys(chemsys) # build the PhaseDiagram for get_ion_entries ion_ref_comps = [ Ion.from_formula(d["data"]["RefSolid"]).composition for d in ion_data ] ion_ref_elts = set( itertools.chain.from_iterable(i.elements for i in ion_ref_comps)) # TODO - would be great if the commented line below would work # However for some reason you cannot process GibbsComputedStructureEntry with # MaterialsProjectAqueousCompatibility ion_ref_entries = self.get_entries_in_chemsys( list([str(e) for e in ion_ref_elts] + ["O", "H"]), # use_gibbs=use_gibbs ) # suppress the warning about supplying the required energies; they will be calculated from the # entries we get from MPRester with warnings.catch_warnings(): warnings.filterwarnings( "ignore", message="You did not provide the required O2 and H2O energies.", ) compat = MaterialsProjectAqueousCompatibility( solid_compat=solid_compat) # suppress the warning about missing oxidation states with warnings.catch_warnings(): warnings.filterwarnings( "ignore", message="Failed to guess oxidation states.*") ion_ref_entries = compat.process_entries(ion_ref_entries) # TODO - if the commented line above would work, this conditional block # could be removed if use_gibbs: # replace the entries with GibbsComputedStructureEntry from pymatgen.entries.computed_entries import GibbsComputedStructureEntry ion_ref_entries = GibbsComputedStructureEntry.from_entries( ion_ref_entries, temp=use_gibbs) ion_ref_pd = PhaseDiagram(ion_ref_entries) ion_entries = self.get_ion_entries(ion_ref_pd, ion_ref_data=ion_data) pbx_entries = [ PourbaixEntry(e, f"ion-{n}") for n, e in enumerate(ion_entries) ] # Construct the solid pourbaix entries from filtered ion_ref entries extra_elts = (set(ion_ref_elts) - {Element(s) for s in chemsys} - {Element("H"), Element("O")}) for entry in ion_ref_entries: entry_elts = set(entry.composition.elements) # Ensure no OH chemsys or extraneous elements from ion references if not (entry_elts <= {Element("H"), Element("O")} or extra_elts.intersection(entry_elts)): # Create new computed entry form_e = ion_ref_pd.get_form_energy(entry) new_entry = ComputedEntry(entry.composition, form_e, entry_id=entry.entry_id) pbx_entry = PourbaixEntry(new_entry) pbx_entries.append(pbx_entry) return pbx_entries