def get_chemsys_from_struct_mpid(mpid, struct, chemsys): ctx = dash.callback_context if ctx is None or not ctx.triggered: raise PreventUpdate trigger = ctx.triggered[0] if trigger["value"] is None: raise PreventUpdate # mpid trigger if trigger["prop_id"] == self.id("mpid") + ".data": with MPRester() as mpr: entry = mpr.get_entry_by_material_id(mpid) chemsys = [str(elem) for elem in entry.composition.elements] # struct trigger elif trigger["prop_id"] == self.id("struct") + ".data": chemsys = [ str(elem) for elem in self.from_data(struct).composition.elements ] if len(set(chemsys) - {"O", "H"}) > SUPPORTED_N_ELEMENTS: return "too_many_elements" with MPRester() as mpr: pourbaix_entries = mpr.get_pourbaix_entries(chemsys) return pourbaix_entries
def get_poscar_from_mp(args: Namespace) -> None: s = MPRester().get_structure_by_material_id(args.mpid) s.to(fmt="poscar", filename=args.poscar) data = MPRester().get_data(args.mpid)[0] d = {"total_magnetization": data["total_magnetization"], "band_gap": data["band_gap"], "data_source": args.mpid, "icsd_ids": data["icsd_ids"]} args.prior_info.write_text(yaml.dump(d), None)
def get_lowest_en_from_mp(formula, MAPI_KEY="", all_structs=False): """ Lowest energy/chemical potential of an element from the materialsproject/jarvis database. Note: Get the api key from materialsproject/jarvis website. Args: formula: say Al, Ni etc. MAPI_KEY: should be defines in the environment all_structs: all structures or just stable ones (True/False) Returns: enp: energy per atom """ if not MAPI_KEY: MAPI_KEY = os.environ.get("MAPI_KEY", "") if not MAPI_KEY: print('API key not provided') print( 'get API KEY from materialsproject and set it to the MAPI_KEY environment variable. aborting ... ' ) sys.exit() with MPRester(MAPI_KEY) as m: data = m.get_data(formula) structures = [] x = {} print("\nnumber of structures matching the chemical formula {0} = {1}". format(formula, len(data))) print( "The one with the the lowest energy above the hull is returned, unless all_structs is set to True" ) for d in data: mpid = str(d['material_id']) x[mpid] = d['e_above_hull'] if all_structs: structure = m.get_structure_by_material_id(mpid) structures.append(structure) else: mineah_key = sorted(x.items(), key=operator.itemgetter(1))[0][0] print( "The id of the material corresponding to the lowest energy above the hull = {0}" .format(mineah_key)) if mineah_key: with MPRester(MAPI_KEY) as m: data = m.get_data(mineah_key) x = {} for d in data: x['energy_per_atom'] = str(d['energy_per_atom']) enp = x['energy_per_atom'] #return m.get_structure_by_material_id(mineah_key) return enp else: return None
def test(self, structure): failures = [] if self.is_valid: if not structure.is_valid(): failures.append("IS_VALID=False") if self.potcar_exists: elements = structure.composition.elements if set(elements).intersection(set(self.NO_POTCARS)): failures.append("POTCAR_EXISTS=False") if self.max_natoms: if structure.num_sites > self.max_natoms: failures.append("MAX_NATOMS=Exceeded") if self.is_ordered: if not structure.is_ordered: failures.append("IS_ORDERED=False") if self.not_in_MP: mpr = MPRester(self.MAPI_KEY) mpids = mpr.find_structure(structure) if mpids: if self.require_bandstructure: for mpid in mpids: try: bs = mpr.get_bandstructure_by_material_id(mpid) if bs: failures.append(f"NOT_IN_MP=False ({mpid})") except: pass else: failures.append("NOT_IN_MP=False ({})".format(mpids[0])) return True if not failures else False
def validate_mpids(self): # add in manual vs automatic read in from .yml file print('Add/remove MPIDs; existing mp-ids are %s' % self.new_dictionary['MPIDs']) add_or_remove = input('Add or remove mpids?\n') if add_or_remove.lower() == 'add': mpid = input('Name of mpid to add\n') if self.is_mpid(mpid): with MPRester(MP_api_key) as m: structure = m.get_structures(mpid, final=True)[0] formula = structure.formula self.new_dictionary['MPIDs'][mpid] = formula else: pass self.validate_mpids() elif add_or_remove.lower() == 'remove': mpid = input('mpid to remove\n') if self.is_mpid(mpid): try: del self.new_dictionary['MPIDs'][mpid] except KeyError: print('%s not in the list of current mpids' % mpid) pass else: print('Invalid mpid; try again') self.validate_mpids() elif add_or_remove.lower() in self.exit_commands: print('Exiting: existing tags will be used') return else: print('Not a valid option; try again') self.validate_mpids()
def pattern_from_mpid(element, mpid, elements): if not element or not elements: raise PreventUpdate url_path = "/materials/" + mpid["mpid"] + "/xas/" + element with MPRester() as mpr: data = mpr._make_request( url_path) # querying MP database via MAPI if len(data) == 0: plotdata = "error" else: x = data[0]["spectrum"].x y = data[0]["spectrum"].y plotdata = [ go.Scatter( x=x, y=y, line=dict( color=self.line_colors[elements.index(element)]), ) ] return plotdata
def create_table(chemsys, pd_time, n_clicks, pd, rows): ctx = dash.callback_context if ctx is None or not ctx.triggered or chemsys is None: raise PreventUpdate trigger = ctx.triggered[0] # PD update trigger if trigger["prop_id"] == self.id() + ".modified_timestamp": table_content = self.create_table_content(self.from_data(pd)) return table_content elif trigger["prop_id"] == self.id( "editing-rows-button") + ".n_clicks": if n_clicks > 0 and rows: rows.append(self.empty_row) return rows with MPRester() as mpr: entries = mpr.get_entries_in_chemsys(chemsys) pd = PhaseDiagram(entries) table_content = self.create_table_content(pd) return table_content
def get_mprester(): if settings.use_mapi_db: with MongoMPRester(settings.mongodb_uri) as mpr: yield mpr else: with MPRester() as mpr: yield mpr
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
def OnQuery(self, event): key='NdHi2bTnJ9WDS1sU' a=MPRester(key) formula='' for element, number in self.extracted_elements: if element.startswith('^'): element=element.split('}')[-1] formula+='%s%g'%(element, number) res=a.get_data(formula) if type(res) is not list: return if len(res)>1: # more then one structure available, ask for user input to select appropriate items=[] for i, ri in enumerate(res): cs=ri['spacegroup']['crystal_system'] sgs=ri['spacegroup']['symbol'] frm=ri['full_formula'] v=ri['volume'] dens=ri['density'] items.append( '%i: %s (%s) | UC Formula: %s\n Density: %s g/cm³ | UC Volume: %s'% (i+1, sgs, cs, frm, dens, v)) if ri['tags'] is not None: items[-1]+='\n '+';'.join(ri['tags'][:3]) dia=wx.SingleChoiceDialog(self, 'Several entries have been found, please select appropriate:', 'Select correct database entry', items) if not dia.ShowModal()==wx.ID_OK: return None res=res[dia.GetSelection()] else: res=res[0] return self.analyze_cif(res['cif'])
def test_include_user_agent(self): headers = self.rester.session.headers self.assertIn("user-agent", headers, msg="Include user-agent header by default") m = re.match( r"pymatgen/(\d+)\.(\d+)\.(\d+)\.?(\d+)? \(Python/(\d+)\.(\d)+\.(\d+) ([^\/]*)/([^\)]*)\)", headers["user-agent"], ) self.assertIsNotNone(m, msg="Unexpected user-agent value {}".format( headers["user-agent"])) self.assertEqual(m.groups()[:3], tuple(pmg_version.split("."))) self.assertEqual( m.groups()[3:6], tuple( str(n) for n in ( sys.version_info.major, sys.version_info.minor, sys.version_info.micro, )), ) self.rester = MPRester(include_user_agent=False) self.assertNotIn("user-agent", self.rester.session.headers, msg="user-agent header unwanted")
def _download(self): """ Downloads dataset provided it does not exist in self.path Returns: works (bool): true if download succeeded or file already exists """ try: from pymatgen.ext.matproj import MPRester from pymatgen.core import Structure import pymatgen as pmg except: raise ImportError('In order to download Materials Project data, you have to install pymatgen') with connect(self.dbpath) as con: with MPRester(self.apikey) as m: for N in range(1, 9): for nsites in range(0, 300, 30): ns = {"$lt": nsites + 31, "$gt": nsites} query = m.query(criteria={'nelements': N, 'is_compatible': True, 'nsites': ns}, properties=['structure', 'energy_per_atom', 'formation_energy_per_atom', 'total_magnetization', 'material_id', 'warnings']) for k, q in enumerate(query): s = q['structure'] # .get_primitive_structure(tolerance=0.1) if type(s) is Structure: at = Atoms(numbers=s.atomic_numbers, positions=s.cart_coords, cell=s.lattice.matrix, pbc=True) con.write(at, data={MaterialsProject.EPerAtom: q['energy_per_atom'], MaterialsProject.EformationPerAtom: q['formation_energy_per_atom'], MaterialsProject.TotalMagnetization: q['total_magnetization']})
def get_struct_from_mp(formula, MAPI_KEY="", all_structs=False): if not MAPI_KEY: MAPI_KEY = os.environ.get("MAPI_KEY", "") if not MAPI_KEY: print('API key not provided') print('get API KEY from materialsproject and set it to the MAPI_KEY environment variable. aborting ... ') sys.exit() with MPRester(MAPI_KEY) as m: data = m.get_data(formula) structures = [] x = {} print("\nnumber of structures matching the chemical formula {0} = {1}".format(formula, len(data))) print("The one with the the lowest energy above the hull is returned, unless all_structs is set to True") for d in data: mpid = str(d['material_id']) x[mpid] = d['e_above_hull'] if all_structs: structure = m.get_structure_by_material_id(mpid) structure.sort() structures.append(structure) if all_structs: return structures else: key = sorted(x.items(), key=operator.itemgetter(1))[0][0] print("The id of the material corresponding to the lowest energy above the hull = {0}".format(key)) if key: return key,m.get_structure_by_material_id(key) else: return None
def test_mpr_pipeline(self): from pymatgen.ext.matproj import MPRester mpr = MPRester() data = mpr.get_pourbaix_entries(["Zn"]) pbx = PourbaixDiagram(data, filter_solids=True, conc_dict={"Zn": 1e-8}) pbx.find_stable_entry(10, 0) data = mpr.get_pourbaix_entries(["Ag", "Te"]) pbx = PourbaixDiagram(data, filter_solids=True, conc_dict={"Ag": 1e-8, "Te": 1e-8}) self.assertEqual(len(pbx.stable_entries), 30) test_entry = pbx.find_stable_entry(8, 2) self.assertAlmostEqual(test_entry.energy, 2.3894017960000009, 1) # Test custom ions entries = mpr.get_pourbaix_entries(["Sn", "C", "Na"]) ion = IonEntry(Ion.from_formula("NaO28H80Sn12C24+"), -161.676) custom_ion_entry = PourbaixEntry(ion, entry_id="my_ion") pbx = PourbaixDiagram( entries + [custom_ion_entry], filter_solids=True, comp_dict={"Na": 1, "Sn": 12, "C": 24}, ) self.assertAlmostEqual(pbx.get_decomposition_energy(custom_ion_entry, 5, 2), 2.1209002582, 1) # Test against ion sets with multiple equivalent ions (Bi-V regression) entries = mpr.get_pourbaix_entries(["Bi", "V"]) pbx = PourbaixDiagram(entries, filter_solids=True, conc_dict={"Bi": 1e-8, "V": 1e-8}) self.assertTrue(all(["Bi" in entry.composition and "V" in entry.composition for entry in pbx.all_entries]))
def get_electrochemical_stability(mpid, pH, potential): ''' A wrapper for pymatgen to construct Pourbaix amd calculate electrochemical stability under reaction condition (i.e. at a given pH and applied potential). Arg: mpid Materials project ID of a bulk composition. e.g. Pt: 'mp-126'. pH: pH at reaction condition. Commonly ones are: acidic: pH=0, neutral: pH=7, and basic pH=14. potential: Applied potential at reaction condition. Returns: stability Electrochemical stability of a composition under reaction condition, unit is eV/atom. ''' mpr = MPRester(read_rc('matproj_api_key')) try: entry = mpr.get_entries(mpid)[0] composition = entry.composition comp_dict = {str(key): value for key, value in composition.items() if key not in ELEMENTS_HO} entries = mpr.get_pourbaix_entries(list(comp_dict.keys())) entry = [entry for entry in entries if entry.entry_id == mpid][0] pbx = PourbaixDiagram(entries, comp_dict=comp_dict, filter_solids=False) stability = pbx.get_decomposition_energy(entry, pH=pH, V=potential) stability = round(stability, 3) # Some mpid's stability are not available except IndexError: stability = np.nan return stability
def e_above(formula, energy): #formula = 'Li6AsN' #energy = -27.53 comp=Composition(formula) target = PDEntry(Composition(formula), energy) elements = list(comp.as_dict().keys()) #print(elements) a = MPRester("API_KEY") #Go to materialsproject.org and create account to get API_KEY #Entries are the basic unit for thermodynamic and other analyses in pymatgen. #This gets all entries belonging to the Ca-C-O system. # entries = a.get_entries_in_chemsys(['Ca', 'C', 'O']) entries = a.get_entries_in_chemsys(elements) #print(entries) pd=PD(entries) # pd.get_decomposition(comp) ehull = pd.get_e_above_hull(target) #plotter = PDPlotter(pd) #plotter.show() return ehull
def __init__(self, element_list: List[str], e_above_hull: float = defaults.e_above_hull, properties: List[str] = None): default_properties = [ "task_id", "full_formula", "final_energy", "structure", "spacegroup", "band_gap", "total_magnetization", "magnetic_type" ] self.element_list = element_list self.properties = properties or default_properties self.e_above_hull = e_above_hull excluded = list(set(elements) - set(element_list)) criteria = ({ "elements": { "$in": element_list, "$nin": excluded }, "e_above_hull": { "$lte": e_above_hull } }) # API key is parsed via .pmgrc.yaml with MPRester() as m: # Due to mp_decode=True by default, class objects are restored. self.materials = m.query(criteria=criteria, properties=self.properties)
def gen_element(ele_name, key): assert (type(ele_name) == str) mpr = MPRester(key) data = mpr.query({ 'elements': [ele_name], 'nelements': 1 }, properties=[ "task_id", "pretty_formula", 'formula', "anonymous_formula", 'formation_energy_per_atom', 'energy_per_atom', 'structure' ]) for ii in data: work_path = make_path_mp(ii) os.makedirs(work_path, exist_ok=True) fposcar = os.path.join(work_path, 'POSCAR') fjson = os.path.join(work_path, 'data.json') ii['structure'].to('poscar', fposcar) ii['structure'].to('json', fjson) m = StructureMatcher() for ii in global_std_crystal.keys(): ss = gen_ele_std(ele_name, ii) find = False for jj in data: if m.fit(ss, jj['structure']): find = True break if find: work_path = make_path_mp(jj) with open(os.path.join(work_path, 'std-crys'), 'w') as fp: fp.write(ii + '\n')
def __init__(self, api_key=None): """ Args: api_key: (str) Your Materials Project API key, or None if you've set up your pymatgen config. """ self.mprester = MPRester(api_key=api_key)
def gen_alloy(eles, key): mpr = MPRester(key) data = mpr.query({ 'elements': { '$all': eles }, 'nelements': len(eles) }, properties=[ "task_id", "pretty_formula", 'formula', "anonymous_formula", 'formation_energy_per_atom', 'energy_per_atom', 'structure' ]) if len(data) == 0: return alloy_file = make_path_mp(data[0]) os.makedirs(alloy_file, exist_ok=True) alloy_file = os.path.join(alloy_file, '..') alloy_file = os.path.join(alloy_file, 'alloy') with open(alloy_file, 'w') as fp: None for ii in data: work_path = make_path_mp(ii) os.makedirs(work_path, exist_ok=True) fposcar = os.path.join(work_path, 'POSCAR') fjson = os.path.join(work_path, 'data.json') ii['structure'].to('poscar', fposcar) ii['structure'].to('json', fjson)
def run(self): with MPRester(utils.read_rc('matproj_api_key')) as rester: structure = rester.get_structure_by_material_id(self.mpid) atoms = AseAtomsAdaptor.get_atoms(structure) doc = make_doc_from_atoms(atoms) save_task_output(self, doc)
def get_chempots_from_composition(self, bulk_composition): """ A simple method for getting GGA-PBE chemical potentials JUST from the composition information (Note: this only works if the composition already exists in the MP database) Args: bulk_composition : Composition of bulk as a pymatgen Composition object. This and mapi_key are only actual required input for generating set of chemical potentials from Materials Project database """ logger = logging.getLogger(__name__) redcomp = bulk_composition.reduced_composition if not self.entries: self.bulk_species_symbol = [s.symbol for s in redcomp.elements] with MPRester(api_key=self.mapi_key) as mp: self.entries['bulk_derived'] = mp.get_entries_in_chemsys( self.bulk_species_symbol) pd = PhaseDiagram(self.entries['bulk_derived']) chem_lims = pd.get_all_chempots(redcomp) return chem_lims
def get_chemsys_from_mpid_or_chemsys(mpid, chemsys_external: str): """ :param mpid: mpid :param chemsys_external: chemsys, e.g. "Co-O" :return: chemsys """ ctx = dash.callback_context if ctx is None or not ctx.triggered: raise PreventUpdate trigger = ctx.triggered[0] if trigger["value"] is None: raise PreventUpdate chemsys = None # get entries by mpid if trigger["prop_id"] == self.id("mpid") + ".data": with MPRester() as mpr: entry = mpr.get_entry_by_material_id(mpid) chemsys = entry.composition.chemical_system # get entries by chemsys if trigger["prop_id"] == self.id("chemsys-external") + ".data": chemsys = chemsys_external return chemsys
def get_vbm_bandgap(self): """ Returns the valence band maxiumum (float) of the structure with MP-ID mpid. Args: mpid (str): MP-ID for which the valence band maximum is to be fetched from the Materials Project database """ logger = logging.getLogger(__name__) vbm, bandgap = None, None if self._mpid is not None: with MPRester(api_key=self._mapi_key) as mp: bs = mp.get_bandstructure_by_material_id(self._mpid) if bs: vbm = bs.get_vbm()["energy"] bandgap = bs.get_band_gap()["energy"] if vbm is None or bandgap is None: if self._mpid: logger.warning("Mpid {} was provided, but no bandstructure entry currently exists for it. " "Reverting to use of bulk calculation.".format( self._mpid)) else: logger.warning( "No mp-id provided, will fetch CBM/VBM details from the " "bulk calculation.") logger.warning("Note that it would be better to " "perform real band structure calculation...") vr = Vasprun(os.path.join(self._root_fldr, "bulk", "vasprun.xml"), parse_potcar_file=False) bandgap = vr.eigenvalue_band_properties[0] vbm = vr.eigenvalue_band_properties[2] return (vbm, bandgap)
def surfer(mpid='', vacuum=15, layers=2, mat=None, max_index=1, write_file=True): """ ASE surface bulder Args: vacuum: vacuum region mat: Structure object max_index: maximum miller index min_slab_size: minimum slab size Returns: structures: list of surface Structure objects """ if mat == None: with MPRester() as mp: mat = mp.get_structure_by_material_id(mpid) if mpid == '': print('Provide structure') sg_mat = SpacegroupAnalyzer(mat) mat_cvn = sg_mat.get_conventional_standard_structure() mat_cvn.sort() indices = get_symmetrically_distinct_miller_indices(mat_cvn, max_index) ase_atoms = AseAtomsAdaptor().get_atoms(mat_cvn) structures = [] pos = Poscar(mat_cvn) try: pos.comment = str('sbulk') + str('@') + str('vac') + str(vacuum) + str( '@') + str('layers') + str(layers) except: pass structures.append(pos) if write_file == True: mat_cvn.to(fmt='poscar', filename=str('POSCAR-') + str('cvn') + str('.vasp')) for i in indices: ase_slab = surface(ase_atoms, i, layers) ase_slab.center(vacuum=vacuum, axis=2) slab_pymatgen = AseAtomsAdaptor().get_structure(ase_slab) slab_pymatgen.sort() surf_name = '_'.join(map(str, i)) pos = Poscar(slab_pymatgen) try: pos.comment = str("Surf-") + str(surf_name) + str('@') + str( 'vac') + str(vacuum) + str('@') + str('layers') + str(layers) except: pass if write_file == True: pos.write_file(filename=str('POSCAR-') + str("Surf-") + str(surf_name) + str('.vasp')) structures.append(pos) return structures
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)
def make_figure(pourbaix_diagram, pourbaix_display_options, pourbaix_entries, struct): if pourbaix_entries == "too_many_elements": return "too_many_elements" if pourbaix_diagram is None: raise PreventUpdate pourbaix_display_options = pourbaix_display_options or [] pourbaix_diagram = self.from_data(pourbaix_diagram) pourbaix_entries = self.from_data(pourbaix_entries) # Get heatmap id if "show_heatmap" in pourbaix_display_options: struct = self.from_data(struct) with MPRester() as mpr: # Should probably enable fetching pourbaix entry # by mpid in MPRester heatmap_id = mpr.find_structure(struct)[0] # Find entry heatmap_entry = [ entry for entry in pourbaix_entries if heatmap_id in entry.entry_id ][0] else: heatmap_entry = None show_labels = "show_labels" in pourbaix_display_options fig = self.get_figure(pourbaix_diagram, heatmap_entry=heatmap_entry, show_labels=show_labels) return fig
def get_phase_diagram_in_chemsys(chemsys): with MPRester(api_key='') as mpr: # using GGA and GGA+U mixed scheme as default, namely compatible_only=True entries = mpr.get_entries_in_chemsys(chemsys) phase_diagram = PhaseDiagram(entries) return phase_diagram
def _get_mpid_cache(self): path = os.path.join(os.path.dirname(module_path), "mpid_cache.json") if os.path.isfile(path): mpid_cache = loadfn(path) else: with MPRester() as mpr: # restrict random mpids to those likely experimentally known # and not too large entries = mpr.query( {"nsites": { "$lte": 16 }}, ["task_id", "icsd_ids"], chunk_size=0, mp_decode=False, ) mpid_cache = [ entry["task_id"] for entry in entries if len(entry["icsd_ids"]) > 2 ] dumpfn(mpid_cache, path) self.mpid_cache = mpid_cache
def setup_phase_diagram_calculations(self, full_phase_diagram = False, energy_above_hull = 0, struct_fmt = 'poscar'): """ This method allows for setting up local phase diagram calculations so a user can calculate chemical potentials on a level of interest beyond PBE-GGA/GGA+U Method is to pull the MP phase diagram and use PBE-GGA level data to decide which phases need to be computed full_phase_diagram flag has two options: False: set up the structures/phases which are stable in GGA phase diagram and are relevant for defining the chemical potentials (exist to define the facets adjacent to composition of interest) True: set up the full phase diagram according to all the entries in the MP database with elements of interest entry_above_hull: allows for a range of energies above hull for each composition being set up default is 0, meaning just the PBE-GGA ground state phases are set up. If you set value to 0.5 then all phases within 0.5 eV/atom of PBE-GGA ground state hull will be set up etc. struct_fmt: is file format you want structure to be written as. Options are “cif”, “poscar”, “cssr”, and “json” """ #while GGA chem pots won't be used here; use this method for quickly gathering phase diagram object entries # AND to find phases of interest if you just want to re-calculate local facets MPgga_muvals = self.MPC.get_chempots_from_composition(self.bulk_composition) if full_phase_diagram: setupphases = set([localentry.name for entrykey in self.MPC.entries.keys() for localentry in self.MPC.entries[entrykey]]) #all elements in phase diagram else: if len(self.bulk_composition)==2: #neccessary because binary species have chempots written as "A-rich, B-rich" setupphases = set([phase.split('_')[0] for facet in MPgga_muvals.keys() for phase in facet.split('-')]) else: setupphases = set([phase for facet in MPgga_muvals.keys() for phase in facet.split('-')]) #just local facets structures_to_setup = {} #this will be a list of structure objects which need to be setup locally #create phase diagram object for analyzing PBE-GGA energetics of structures computed in MP database full_structure_entries = [struct for entrykey in self.MPC.entries.keys() for struct in self.MPC.entries[entrykey]] pd = PhaseDiagram(full_structure_entries) for entry in full_structure_entries: if (entry.name in setupphases) and (pd.get_decomp_and_e_above_hull(entry, allow_negative=True)[1] <= energy_above_hull): with MPRester(api_key=self.mapi_key) as mp: localstruct = mp.get_structure_by_material_id(entry.entry_id) structures_to_setup[str(entry.entry_id)+'_'+str(entry.name)] = localstruct #Set up structure files locally if desired if os.path.exists(os.path.join(self.path_base,'PhaseDiagram')): print ('phase diagram already exists! Dont overwrite...') else: os.makedirs(os.path.join(self.path_base,'PhaseDiagram')) for localname,localstruct in structures_to_setup.items(): filename = os.path.join(self.path_base,'PhaseDiagram',localname) os.makedirs(filename) if struct_fmt == 'poscar': outputname = 'POSCAR' else: outputname = 'structfile' localstruct.to(fmt=struct_fmt,filename=os.path.join(filename, outputname)) #NOTE TO USER. Can use pymatgen here to setup additional calculation files if interested... return structures_to_setup