Source code for mp_api.client.routes.materials.materials

from __future__ import annotations

import warnings

from emmet.core.settings import EmmetSettings
from emmet.core.symmetry import CrystalSystem
from emmet.core.vasp.material import MaterialsDoc
from pymatgen.core.structure import Structure

from mp_api.client.core import BaseRester, MPRestError
from mp_api.client.core.utils import validate_ids
from mp_api.client.routes.materials import (
    AbsorptionRester,
    AlloysRester,
    BandStructureRester,
    BondsRester,
    ChargeDensityRester,
    ChemenvRester,
    DielectricRester,
    DosRester,
    ElasticityRester,
    ElectrodeRester,
    ElectronicStructureRester,
    EOSRester,
    FermiRester,
    GrainBoundaryRester,
    MagnetismRester,
    OxidationStatesRester,
    PhononRester,
    PiezoRester,
    ProvenanceRester,
    RobocrysRester,
    SimilarityRester,
    SubstratesRester,
    SummaryRester,
    SurfacePropertiesRester,
    SynthesisRester,
    TaskRester,
    ThermoRester,
    XASRester,
)

_EMMET_SETTINGS = EmmetSettings()


[docs] class MaterialsRester(BaseRester[MaterialsDoc]): suffix = "materials/core" document_model = MaterialsDoc # type: ignore supports_versions = True primary_key = "material_id" _sub_resters = [ "eos", "similarity", "tasks", "xas", "fermi", "grain_boundary", "substrates", "surface_properties", "phonon", "elasticity", "thermo", "dielectric", "piezoelectric", "magnetism", "summary", "robocrys", "synthesis", "insertion_electrodes", "charge_density", "electronic_structure", "electronic_structure_bandstructure", "electronic_structure_dos", "oxidation_states", "provenance", "bonds", "alloys", "absorption", "chemenv", ] # Materials subresters eos: EOSRester materials: MaterialsRester similarity: SimilarityRester tasks: TaskRester xas: XASRester fermi: FermiRester grain_boundary: GrainBoundaryRester substrates: SubstratesRester surface_properties: SurfacePropertiesRester phonon: PhononRester elasticity: ElasticityRester thermo: ThermoRester dielectric: DielectricRester piezoelectric: PiezoRester magnetism: MagnetismRester summary: SummaryRester robocrys: RobocrysRester synthesis: SynthesisRester insertion_electrodes: ElectrodeRester charge_density: ChargeDensityRester electronic_structure: ElectronicStructureRester electronic_structure_bandstructure: BandStructureRester electronic_structure_dos: DosRester oxidation_states: OxidationStatesRester provenance: ProvenanceRester bonds: BondsRester alloys: AlloysRester absorption: AbsorptionRester chemenv: ChemenvRester def __dir__(self): return dir(MaterialsRester) + self._sub_resters
[docs] def get_structure_by_material_id( self, material_id: str, final: bool = True ) -> Structure | list[Structure]: """Get a structure for a given Materials Project ID. Arguments: material_id (str): Materials project ID final (bool): Whether to get the final structure, or the list of initial (pre-relaxation) structures. Defaults to True. Returns: structure (Union[Structure, List[Structure]]): Pymatgen structure object or list of pymatgen structure objects. """ if final: response = self.get_data_by_id(material_id, fields=["structure"]) return response.structure if response is not None else response # type: ignore else: response = self.get_data_by_id(material_id, fields=["initial_structures"]) return response.initial_structures if response is not None else response # type: ignore
[docs] def search_material_docs(self, *args, **kwargs): # pragma: no cover """Deprecated.""" warnings.warn( "MPRester.materials.search_material_docs is deprecated. " "Please use MPRester.materials.search instead.", DeprecationWarning, stacklevel=2, ) return self.search(*args, **kwargs)
[docs] def search( self, material_ids: str | list[str] | None = None, chemsys: str | list[str] | None = None, crystal_system: CrystalSystem | None = None, density: tuple[float, float] | None = None, deprecated: bool | None = False, elements: list[str] | None = None, exclude_elements: list[str] | None = None, formula: str | list[str] | None = None, num_elements: tuple[int, int] | None = None, num_sites: tuple[int, int] | None = None, spacegroup_number: int | None = None, spacegroup_symbol: str | None = None, task_ids: list[str] | None = None, volume: tuple[float, float] | None = None, num_chunks: int | None = None, chunk_size: int = 1000, all_fields: bool = True, fields: list[str] | None = None, ): """Query core material docs using a variety of search criteria. Arguments: material_ids (str, List[str]): A single Material ID string or list of strings (e.g., mp-149, [mp-149, mp-13]). chemsys (str, List[str]): A chemical system or list of chemical systems (e.g., Li-Fe-O, Si-*, [Si-O, Li-Fe-P]). crystal_system (CrystalSystem): Crystal system of material. density (Tuple[float,float]): Minimum and maximum density to consider. deprecated (bool): Whether the material is tagged as deprecated. elements (List[str]): A list of elements. exclude_elements (List[str]): A list of elements to exclude. formula (str, List[str]): A formula including anonymized formula or wild cards (e.g., Fe2O3, ABO3, Si*). A list of chemical formulas can also be passed (e.g., [Fe2O3, ABO3]). num_elements (Tuple[int,int]): Minimum and maximum number of elements to consider. num_sites (Tuple[int,int]): Minimum and maximum number of sites to consider. spacegroup_number (int): Space group number of material. spacegroup_symbol (str): Space group symbol of the material in international short symbol notation. task_ids (List[str]): List of Materials Project IDs to return data for. volume (Tuple[float,float]): Minimum and maximum volume to consider. num_chunks (int): Maximum number of chunks of data to yield. None will yield all possible. chunk_size (int): Number of data entries per chunk. all_fields (bool): Whether to return all fields in the document. Defaults to True. fields (List[str]): List of fields in MaterialsCoreDoc to return data for. Default is material_id, last_updated, and formula_pretty if all_fields is False. Returns: ([MaterialsDoc]) List of material documents """ query_params = {"deprecated": deprecated} # type: dict if material_ids: if isinstance(material_ids, str): material_ids = [material_ids] query_params.update({"material_ids": ",".join(validate_ids(material_ids))}) if formula: if isinstance(formula, str): formula = [formula] query_params.update({"formula": ",".join(formula)}) if chemsys: if isinstance(chemsys, str): chemsys = [chemsys] query_params.update({"chemsys": ",".join(chemsys)}) if elements: query_params.update({"elements": ",".join(elements)}) if exclude_elements: query_params.update({"exclude_elements": ",".join(exclude_elements)}) if task_ids: query_params.update({"task_ids": ",".join(validate_ids(task_ids))}) query_params.update( { "crystal_system": crystal_system, "spacegroup_number": spacegroup_number, "spacegroup_symbol": spacegroup_symbol, } ) if num_sites: query_params.update( {"nsites_min": num_sites[0], "nsites_max": num_sites[1]} ) if num_elements: if isinstance(num_elements, int): num_elements = (num_elements, num_elements) query_params.update( {"nelements_min": num_elements[0], "nelements_max": num_elements[1]} ) if volume: query_params.update({"volume_min": volume[0], "volume_max": volume[1]}) if density: query_params.update({"density_min": density[0], "density_max": density[1]}) query_params = { entry: query_params[entry] for entry in query_params if query_params[entry] is not None } return super()._search( num_chunks=num_chunks, chunk_size=chunk_size, all_fields=all_fields, fields=fields, **query_params, )
[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, ) -> 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 []