def get_dos_from_material_id(self, material_id: str): """ Get the complete density of states pymatgen object associated with a Materials Project ID. Arguments: materials_id (str): Materials Project ID for a material Returns: dos (CompleteDos): CompleteDos object """ es_rester = ElectronicStructureRester(endpoint=self.base_endpoint, api_key=self.api_key) dos_data = es_rester.get_data_by_id(document_id=material_id, fields=["dos"]).dict() if dos_data["dos"]: dos_task_id = dos_data["dos"]["total"]["1"]["task_id"] else: raise MPRestError( "No density of states data found for {}".format(material_id)) dos_obj = self.get_dos_from_task_id(dos_task_id) if dos_obj: b64_bytes = base64.b64decode(dos_obj[0], validate=True) packed_bytes = zlib.decompress(b64_bytes) json_data = msgpack.unpackb(packed_bytes, raw=False) data = MontyDecoder().process_decoded(json_data["data"]) return data else: raise MPRestError("No density of states object found.")
def get_dos_from_material_id(self, material_id: str): """ Get the complete density of states pymatgen object associated with a Materials Project ID. Arguments: materials_id (str): Materials Project ID for a material Returns: dos (CompleteDos): CompleteDos object """ es_rester = ElectronicStructureRester(endpoint=self.base_endpoint, api_key=self.api_key) dos_data = es_rester.get_document_by_id(document_id=material_id, fields=["dos"]).dict() if dos_data["dos"]: dos_calc_id = dos_data["dos"]["total"]["1"]["task_id"] else: raise MPRestError( "No density of states data found for {}".format(material_id)) dos_obj = self.get_dos_from_calculation_id(dos_calc_id) if dos_obj: return dos_obj[0]["data"] else: raise MPRestError("No density of states object found.")
def find_structure(self, filename_or_structure, ltol=0.2, stol=0.3, angle_tol=5): """ Finds matching structures on the Materials Project site. Args: filename_or_structure: filename or Structure object Returns: A list of matching materials project ids for structure, \ including normalized rms distances and max distances between paired sites. Raises: MPRestError """ params = {"ltol": ltol, "stol": stol, "angle_tol": angle_tol} try: if isinstance(filename_or_structure, str): s = Structure.from_file(filename_or_structure) elif isinstance(filename_or_structure, PMGStructure): s = filename_or_structure else: raise MPRestError("Provide filename or Structure object.") payload = json.dumps(s.as_dict(), cls=MontyEncoder) response = self.session.post( self.endpoint + "find_structure", data=payload, params=params ) if response.status_code == 200: data = json.loads(response.text, cls=MontyDecoder) if len(data.get("data", [])) > 0: return data else: raise MPRestError("No matching structures found.") else: try: data = json.loads(response.text)["detail"] except (json.JSONDecodeError, KeyError): data = "Response {}".format(response.text) if isinstance(data, str): message = data else: try: message = ", ".join( "{} - {}".format(entry["loc"][1], entry["msg"]) for entry in data ) except (KeyError, IndexError): message = str(data) raise MPRestError( f"REST query returned with error status code {response.status_code} " f"on URL {response.url} with message:\n{message}" ) except Exception as ex: raise MPRestError(str(ex))
def get_bandstructure_from_material_id( self, material_id: str, path_type: BSPathType = BSPathType.setyawan_curtarolo, line_mode=True, ): """ Get the band structure pymatgen object associated with a Materials Project ID. Arguments: materials_id (str): Materials Project ID for a material path_type (BSPathType): k-point path selection convention line_mode (bool): Whether to return data for a line-mode calculation Returns: bandstructure (Union[BandStructure, BandStructureSymmLine]): BandStructure or BandStructureSymmLine object """ es_rester = ElectronicStructureRester(endpoint=self.base_endpoint, api_key=self.api_key) if line_mode: bs_data = es_rester.get_document_by_id( document_id=material_id, fields=["bandstructure"]).bandstructure.dict() if bs_data.get(path_type.value, None): bs_calc_id = bs_data[path_type.value]["task_id"] else: raise MPRestError( "No {} band structure data found for {}".format( path_type.value, material_id)) else: bs_data = es_rester.get_document_by_id(document_id=material_id, fields=["dos"]).dos.dict() if bs_data.get("total", None): bs_calc_id = bs_data["total"]["1"]["task_id"] else: raise MPRestError( "No uniform band structure data found for {}".format( material_id)) bs_obj = self.get_bandstructure_from_calculation_id(bs_calc_id) if bs_obj: return bs_obj[0]["data"] else: raise MPRestError("No band structure object found.")
def get_charge_density_from_material_id(self, material_id: str): """ Get charge density data for a given Materials Project ID. Arguments: material_id (str): Material Project ID Returns: chgcar (dict): Pymatgen CHGCAR object. """ task_id = sorted( self.get_charge_density_calculation_ids_from_material_id( material_id), key=lambda d: d["last_updated"], reverse=True, )[0]["task_id"] result = self.search(task_ids=task_id, fields=["task_id", "data"], chunk_size=1) if len(result) > 0: return result[0]["data"] else: raise MPRestError("No charge density found")
def search( self, keywords: List[str], num_chunks: Optional[int] = None, chunk_size: Optional[int] = 100, ): """ Search text generated from Robocrystallographer. Arguments: keywords (List[str]): List of search keywords num_chunks (Optional[int]): Maximum number of chunks of data to yield. None will yield all possible. chunk_size (Optional[int]): Number of data entries per chunk. Returns: robocrys_docs (List[RobocrystallogapherDoc]): List of robocrystallographer documents """ keyword_string = ",".join(keywords) robocrys_docs = self._query_resource( criteria={ "keywords": keyword_string, "_limit": chunk_size }, suburl="text_search", use_document_model=True, chunk_size=100, ).get("data", None) if robocrys_docs is None: raise MPRestError("Cannot find any matches.") return robocrys_docs
def search_robocrys_text(self, keywords: List[str]): """ Search text generated from Robocrystallographer. Arguments: keywords (List[str]): List of search keywords Returns: robocrys_docs (List[RobocrysDoc]): List of robocrystallographer documents """ keyword_string = ",".join(keywords) robocrys_docs = self._query_resource( criteria={ "keywords": keyword_string }, suburl="text_search", use_document_model=True, ).get("data", None) if robocrys_docs is None: raise MPRestError("Cannot find any matches.") return robocrys_docs
def get_dos_from_task_id(self, task_id: str): """ Get the density of states pymatgen object associated with a given calculation ID. Arguments: task_id (str): Task ID for the density of states calculation Returns: bandstructure (CompleteDos): CompleteDos object """ result = self._query_resource( criteria={ "task_id": task_id, "_all_fields": True }, suburl="object", use_document_model=False, num_chunks=1, chunk_size=1, ) if result.get("data", None) is not None: return result["data"] else: raise MPRestError("No object found")
def get_structure_by_material_id( self, material_id: str, version: Optional[str] = None ) -> Structure: """ Get a structure for a given Materials Project ID. Arguments: material_id (str): Materials project ID version (str): Version of data to query on in the format 'YYYY.MM.DD'. Defaults to None which will return data from the most recent database release. Returns: structure (Structure): Pymatgen structure object """ if version is None: result = self._make_request("{}/?fields=structure".format(material_id)) else: result = self._make_request( "{}/?version={}&fields=structure".format(material_id, version) ) if len(result.get("data", [])) > 0: return result else: raise MPRestError(f"No document found for {material_id}")
def get_xas_doc(self, xas_id: str): # TODO do some checking here for sub-components query_params = {"all_fields": True} result = self._query_resource(query_params, suburl=xas_id) if len(result.get("data", [])) > 0: return result["data"][0] else: raise MPRestError("No document found")
def get_database_versions(self): """ Get version tags available for the Materials Project core materials data. These can be used to request data from previous releases. Returns: versions (List[str]): List of database versions as strings. """ result = self._make_request("versions") if len(result.get("data", [])) > 0: return result else: raise MPRestError("No data found")
def get_similar_structures(self, material_id: str): """ Get similar structures for a given Materials Project ID. Arguments: material_id (str): Materials project ID Returns: results (Dict): Dictionary containing structure similarity data. """ result = self._make_request("{}/?all_fields=true".format(material_id)) if len(result.get("data", [])) > 0: return result else: raise MPRestError("No document found")
def get_bandstructure_summary_from_material_id(self, material_id: str): """ Get a band structure summary doc for a given Materials Project ID. Arguments: material_id (str): Materials project ID Returns: doc (dict): Band structure summary doc. """ result = self._make_request("{}/?all_fields=true".format(material_id)) if len(result.get("data", [])) > 0: return result else: raise MPRestError("No document found")
def get_wulff_image_data_from_material_id(self, material_id: str): """ Get wulff image data for a given Materials Project ID. Arguments: material_id (str): Materials project ID Returns: results (Dict): Dictionary containing wulff image data. """ result = self._make_request("{}/?all_fields=true".format(material_id)) if len(result.get("data", [])) > 0: return result else: raise MPRestError("No document found")
def get_trajectory(self, task_id): """ Returns a Trajectory object containing the geometry of the material throughout a calculation. This is most useful for observing how a material relaxes during a geometry optimization. :param task_id: A specified task_id :return: List of trajectory objects """ traj_data = self._query_resource_data(suburl=f"trajectory/{task_id}/", use_document_model=False)[0].get( "trajectories", None) if traj_data is None: raise MPRestError(f"No trajectory data for {task_id} found") return traj_data
def get_charge_density_from_material_id(self, material_id: str): """ Get charge density data for a given Materials Project ID. Arguments: material_id (str): Materials project ID Returns: chgcar (dict): Pymatgen CHGCAR object. """ result = self._make_request( "{}/?fields=data&all_fields=false".format(material_id)) if result.get("data", None) is not None: return result["data"] else: raise MPRestError("No document found")
def get_dos_from_material_id(self, material_id: str): """ Get a density of states for a given Materials Project ID. Arguments: material_id (str): Materials project ID Returns: dos (CompleteDos): Pymatgen density of states object """ result = self._make_request( "object/?task_id={}&all_fields=true".format(material_id)) if result.get("object", None) is not None: return result["object"] else: raise MPRestError("No document found")
def get_charge_density_from_calculation_id(self, task_id: str): """ Get charge density data for a given calculation ID. Arguments: task_id (str): Calculation ID Returns: chgcar (dict): Pymatgen CHGCAR object. """ result = self.search(task_ids=task_id, fields=["task_id", "data"], chunk_size=1) if len(result) > 0: return result[0]["data"] else: raise MPRestError("No charge density found")
def get_charge_density_from_material_id( self, material_id: str, inc_task_doc: bool = False) -> Optional[Chgcar]: """ Get charge density data for a given Materials Project ID. Arguments: material_id (str): Material Project ID inc_task_doc (bool): Whether to include the task document in the returned data. Returns: chgcar: Pymatgen Chgcar object. """ # TODO: really we want a recommended task_id for charge densities here # this could potentially introduce an ambiguity task_ids = self.get_task_ids_associated_with_material_id( material_id, calc_types=[CalcType.GGA_Static, CalcType.GGA_U_Static]) results: List[ChgcarDataDoc] = self.charge_density.search( task_ids=task_ids) # type: ignore if len(results) == 0: return None latest_doc = max(results, key=lambda x: x.last_updated) chgcar = self.charge_density.get_charge_density_from_file_id( latest_doc.fs_id) if chgcar is None: raise MPRestError(f"No charge density fetched for {material_id}.") if inc_task_doc: task_doc = self.tasks.get_data_by_id(latest_doc.task_id) return chgcar, task_doc return chgcar
def get_bandstructure_from_material_id(self, material_id: str, path_type: BSPathType): """ Get a band structure for a given Materials Project ID. Arguments: material_id (str): Materials project ID path_type (BSPathType): Band structure type determined by the k-path convention used. Returns: bandstructure (BandStructureSymmLine): Pymatgen band structure object """ result = self._make_request( "object/?task_id={}&path_type={}&all_fields=true".format( material_id, path_type.value)) if result.get("object", None) is not None: return result["object"] else: raise MPRestError("No document found")
def get_task_from_material_id( self, material_id: str, fields: Iterable[str] = ("task_id", "formula_pretty", "last_updated"), ): """ Get task document data for a given Materials Project ID. Arguments: material_id (str): Materials project ID Returns: data (dict): Task doc data for keys in fields. Defaults to task_id, formula_pretty, and last_updated. """ field_vals = ",".join(fields) result = self._make_request("{}/?fields={}".format( material_id, field_vals)) if len(result.get("data", [])) > 0: return result else: raise MPRestError("No document found")
def find_structure(self, filename_or_structure, ltol=0.2, stol=0.3, angle_tol=5, limit=1): """ Finds matching structures on the Materials Project site. Args: filename_or_structure: filename or Structure object Returns: A list of matching materials project ids for structure, \ including normalized rms distances and max distances between paired sites. Raises: MPRestError """ params = { "ltol": ltol, "stol": stol, "angle_tol": angle_tol, "limit": limit } if isinstance(filename_or_structure, str): s = Structure.from_file(filename_or_structure) elif isinstance(filename_or_structure, Structure): s = filename_or_structure else: raise MPRestError("Provide filename or Structure object.") return self._post_resource( body=s.as_dict(), params=params, suburl="find_structure", use_document_model=False, ).get("data")
def get_bandstructure_from_calculation_id(self, task_id: str): """ Get the band structure pymatgen object associated with a given calculation ID. Arguments: task_id (str): Calculation ID for the band structure calculation Returns: bandstructure (BandStructure): BandStructure or BandStructureSymmLine object """ result = self._query_resource( criteria={ "task_id": task_id, "all_fields": True }, suburl="object", use_document_model=False, ) if result.get("data", None) is not None: return result["data"] else: raise MPRestError("No object found")
def search( self, keywords: Optional[List[str]] = None, synthesis_type: Optional[List[SynthesisTypeEnum]] = None, target_formula: Optional[str] = None, precursor_formula: Optional[str] = None, operations: Optional[List[OperationTypeEnum]] = None, condition_heating_temperature_min: Optional[float] = None, condition_heating_temperature_max: Optional[float] = None, condition_heating_time_min: Optional[float] = None, condition_heating_time_max: Optional[float] = None, condition_heating_atmosphere: Optional[List[str]] = None, condition_mixing_device: Optional[List[str]] = None, condition_mixing_media: Optional[List[str]] = None, num_chunks: Optional[int] = None, chunk_size: Optional[int] = 10, ): """ Search synthesis recipe text. Arguments: keywords (Optional[List[str]]): List of string keywords to search synthesis paragraph text with synthesis_type (Optional[List[SynthesisTypeEnum]]): Type of synthesis to include target_formula (Optional[str]): Chemical formula of the target material precursor_formula (Optional[str]): Chemical formula of the precursor material operations (Optional[List[OperationTypeEnum]]): List of operations that syntheses must have condition_heating_temperature_min (Optional[float]): Minimal heating temperature condition_heating_temperature_max (Optional[float]): Maximal heating temperature condition_heating_time_min (Optional[float]): Minimal heating time condition_heating_time_max (Optional[float]): Maximal heating time condition_heating_atmosphere (Optional[List[str]]): Required heating atmosphere, such as "air", "argon" condition_mixing_device (Optional[List[str]]): Required mixing device, such as "zirconia", "Al2O3". condition_mixing_media (Optional[List[str]]): Required mixing media, such as "alcohol", "water" num_chunks (Optional[int]): Maximum number of chunks of data to yield. None will yield all possible. chunk_size (Optional[int]): Number of data entries per chunk. Returns: synthesis_docs ([SynthesisDoc]): List of synthesis documents """ # Turn None and empty list into None keywords = keywords or None synthesis_type = synthesis_type or None operations = operations or None condition_heating_atmosphere = condition_heating_atmosphere or None condition_mixing_device = condition_mixing_device or None condition_mixing_media = condition_mixing_media or None if keywords: keywords = ",".join([word.strip() for word in keywords]) # type: ignore synthesis_docs = self._query_resource( criteria={ "keywords": keywords, "synthesis_type": synthesis_type, "target_formula": target_formula, "precursor_formula": precursor_formula, "operations": operations, "condition_heating_temperature_min": condition_heating_temperature_min, "condition_heating_temperature_max": condition_heating_temperature_max, "condition_heating_time_min": condition_heating_time_min, "condition_heating_time_max": condition_heating_time_max, "condition_heating_atmosphere": condition_heating_atmosphere, "condition_mixing_device": condition_mixing_device, "condition_mixing_media": condition_mixing_media, "_limit": chunk_size, }, chunk_size=chunk_size, num_chunks=num_chunks, ).get("data", None) if synthesis_docs is None: raise MPRestError("Cannot find any matches.") return synthesis_docs
def get_bandstructure_from_material_id( self, material_id: str, path_type: BSPathType = BSPathType.setyawan_curtarolo, line_mode=True, ): """ Get the band structure pymatgen object associated with a Materials Project ID. Arguments: materials_id (str): Materials Project ID for a material path_type (BSPathType): k-point path selection convention line_mode (bool): Whether to return data for a line-mode calculation Returns: bandstructure (Union[BandStructure, BandStructureSymmLine]): BandStructure or BandStructureSymmLine object """ es_rester = ElectronicStructureRester(endpoint=self.base_endpoint, api_key=self.api_key) if line_mode: bs_data = es_rester.get_data_by_id(document_id=material_id, fields=["bandstructure" ]).bandstructure if bs_data is None: raise MPRestError( "No {} band structure data found for {}".format( path_type.value, material_id)) else: bs_data = bs_data.dict() if bs_data.get(path_type.value, None): bs_task_id = bs_data[path_type.value]["task_id"] else: raise MPRestError( "No {} band structure data found for {}".format( path_type.value, material_id)) else: bs_data = es_rester.get_data_by_id(document_id=material_id, fields=["dos"]).dos if bs_data is None: raise MPRestError( "No uniform band structure data found for {}".format( material_id)) else: bs_data = bs_data.dict() if bs_data.get("total", None): bs_task_id = bs_data["total"]["1"]["task_id"] else: raise MPRestError( "No uniform band structure data found for {}".format( material_id)) bs_obj = self.get_bandstructure_from_task_id(bs_task_id) if bs_obj: b64_bytes = base64.b64decode(bs_obj[0], validate=True) packed_bytes = zlib.decompress(b64_bytes) json_data = msgpack.unpackb(packed_bytes, raw=False) data = MontyDecoder().process_decoded(json_data["data"]) return data else: raise MPRestError("No band structure object found.")
def search_synthesis_text( self, keywords: Optional[List[str]] = None, synthesis_type: Optional[List[SynthesisTypeEnum]] = None, target_formula: Optional[str] = None, precursor_formula: Optional[str] = None, operations: Optional[List[OperationTypeEnum]] = None, condition_heating_temperature_min: Optional[float] = None, condition_heating_temperature_max: Optional[float] = None, condition_heating_time_min: Optional[float] = None, condition_heating_time_max: Optional[float] = None, condition_heating_atmosphere: Optional[List[str]] = None, condition_mixing_device: Optional[List[str]] = None, condition_mixing_media: Optional[List[str]] = None, ): """ Search synthesis recipe text. Arguments: keywords (Optional[List[str]]): List of string keywords to search synthesis paragraph text with synthesis_type (Optional[List[SynthesisTypeEnum]]): Type of synthesis to include target_formula (Optional[str]): Chemical formula of the target material precursor_formula (Optional[str]): Chemical formula of the precursor material operations (Optional[List[OperationTypeEnum]]): List of operations that syntheses must have condition_heating_temperature_min (Optional[float]): Minimal heating temperature condition_heating_temperature_max (Optional[float]): Maximal heating temperature condition_heating_time_min (Optional[float]): Minimal heating time condition_heating_time_max (Optional[float]): Maximal heating time condition_heating_atmosphere (Optional[List[str]]): Required heating atmosphere, such as "air", "argon" condition_mixing_device (Optional[List[str]]): Required mixing device, such as "zirconia", "Al2O3". condition_mixing_media (Optional[List[str]]): Required mixing media, such as "alcohol", "water" Returns: synthesis_docs ([SynthesisDoc]): List of synthesis documents """ # Turn None and empty list into None keywords = keywords or None synthesis_type = synthesis_type or None operations = operations or None condition_heating_atmosphere = condition_heating_atmosphere or None condition_mixing_device = condition_mixing_device or None condition_mixing_media = condition_mixing_media or None synthesis_docs = self._query_resource( criteria={ "keywords": keywords, "synthesis_type": synthesis_type, "target_formula": target_formula, "precursor_formula": precursor_formula, "operations": operations, "condition_heating_temperature_min": condition_heating_temperature_min, "condition_heating_temperature_max": condition_heating_temperature_max, "condition_heating_time_min": condition_heating_time_min, "condition_heating_time_max": condition_heating_time_max, "condition_heating_atmosphere": condition_heating_atmosphere, "condition_mixing_device": condition_mixing_device, "condition_mixing_media": condition_mixing_media, }, use_document_model=True, ).get("data", None) if synthesis_docs is None: raise MPRestError("Cannot find any matches.") return synthesis_docs
def find_structure( self, filename_or_structure, ltol=_EMMET_SETTINGS.LTOL, stol=_EMMET_SETTINGS.STOL, angle_tol=_EMMET_SETTINGS.ANGLE_TOL, allow_multiple_results=False, ) -> Union[List[str], str]: """ Finds matching structures from the Materials Project database. Multiple results may be returned of "similar" structures based on distance using the pymatgen StructureMatcher algorithm, however only a single result should match with the same spacegroup, calculated to the default tolerances. Args: filename_or_structure: filename or Structure object ltol: fractional length tolerance stol: site tolerance angle_tol: angle tolerance in degrees allow_multiple_results: changes return type for either a single material_id or list of material_ids Returns: A matching material_id if one is found or list of results if allow_multiple_results is True Raises: MPRestError """ params = { "ltol": ltol, "stol": stol, "angle_tol": angle_tol, "_limit": 1 } if isinstance(filename_or_structure, str): s = Structure.from_file(filename_or_structure) elif isinstance(filename_or_structure, Structure): s = filename_or_structure else: raise MPRestError("Provide filename or Structure object.") results = self._post_resource( body=s.as_dict(), params=params, suburl="find_structure", use_document_model=False, ).get("data") if len(results) > 1: # type: ignore if not allow_multiple_results: raise ValueError( "Multiple matches found for this combination of tolerances, but " "`allow_multiple_results` set to False.") return results # type: ignore if results: return results[0]["material_id"] else: return []