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

from __future__ import annotations

import warnings
from collections import defaultdict

from emmet.core.summary import HasProps, SummaryDoc
from emmet.core.symmetry import CrystalSystem
from pymatgen.analysis.magnetism import Ordering

from mp_api.client.core import BaseRester, MPRestError, MPRestWarning
from mp_api.client.core.utils import validate_ids


[docs] class SummaryRester(BaseRester[SummaryDoc]): suffix = "materials/summary" document_model = SummaryDoc # type: ignore primary_key = "material_id"
[docs] def search( # noqa: D417 self, band_gap: tuple[float, float] | None = None, chemsys: str | list[str] | None = None, crystal_system: CrystalSystem | None = None, density: tuple[float, float] | None = None, deprecated: bool | None = None, e_electronic: tuple[float, float] | None = None, e_ionic: tuple[float, float] | None = None, e_total: tuple[float, float] | None = None, efermi: tuple[float, float] | None = None, elastic_anisotropy: tuple[float, float] | None = None, elements: list[str] | None = None, energy_above_hull: tuple[float, float] | None = None, equilibrium_reaction_energy: tuple[float, float] | None = None, exclude_elements: list[str] | None = None, formation_energy: tuple[float, float] | None = None, formula: str | list[str] | None = None, g_reuss: tuple[float, float] | None = None, g_voigt: tuple[float, float] | None = None, g_vrh: tuple[float, float] | None = None, has_props: list[HasProps] | list[str] | None = None, has_reconstructed: bool | None = None, is_gap_direct: bool | None = None, is_metal: bool | None = None, is_stable: bool | None = None, k_reuss: tuple[float, float] | None = None, k_voigt: tuple[float, float] | None = None, k_vrh: tuple[float, float] | None = None, magnetic_ordering: Ordering | None = None, material_ids: str | list[str] | None = None, n: tuple[float, float] | None = None, num_elements: tuple[int, int] | None = None, num_sites: tuple[int, int] | None = None, num_magnetic_sites: tuple[int, int] | None = None, num_unique_magnetic_sites: tuple[int, int] | None = None, piezoelectric_modulus: tuple[float, float] | None = None, poisson_ratio: tuple[float, float] | None = None, possible_species: list[str] | None = None, shape_factor: tuple[float, float] | None = None, spacegroup_number: int | None = None, spacegroup_symbol: str | None = None, surface_energy_anisotropy: tuple[float, float] | None = None, theoretical: bool | None = None, total_energy: tuple[float, float] | None = None, total_magnetization: tuple[float, float] | None = None, total_magnetization_normalized_formula_units: tuple[float, float] | None = None, total_magnetization_normalized_vol: tuple[float, float] | None = None, uncorrected_energy: tuple[float, float] | None = None, volume: tuple[float, float] | None = None, weighted_surface_energy: tuple[float, float] | None = None, weighted_work_function: tuple[float, float] | None = None, include_gnome: bool = True, num_chunks: int | None = None, chunk_size: int = 1000, all_fields: bool = True, fields: list[str] | None = None, **kwargs, ) -> list[SummaryDoc] | list[dict]: """Query core data using a variety of search criteria. Arguments: band_gap (Tuple[float,float]): Minimum and maximum band gap in eV to consider. 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. e_electronic (Tuple[float,float]): Minimum and maximum electronic dielectric constant to consider. e_ionic (Tuple[float,float]): Minimum and maximum ionic dielectric constant to consider. e_total (Tuple[float,float]): Minimum and maximum total dielectric constant to consider. efermi (Tuple[float,float]): Minimum and maximum fermi energy in eV to consider. elastic_anisotropy (Tuple[float,float]): Minimum and maximum value to consider for the elastic anisotropy. elements (List[str]): A list of elements. energy_above_hull (Tuple[int,int]): Minimum and maximum energy above the hull in eV/atom to consider. equilibrium_reaction_energy (Tuple[float,float]): Minimum and maximum equilibrium reaction energy in eV/atom to consider. exclude_elements (List(str)): List of elements to exclude. formation_energy (Tuple[int,int]): Minimum and maximum formation energy in eV/atom to consider. 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]). g_reuss (Tuple[float,float]): Minimum and maximum value in GPa to consider for the Reuss average of the shear modulus. g_voigt (Tuple[float,float]): Minimum and maximum value in GPa to consider for the Voigt average of the shear modulus. g_vrh (Tuple[float,float]): Minimum and maximum value in GPa to consider for the Voigt-Reuss-Hill average of the shear modulus. has_props: (List[HasProps], List[str]): The calculated properties available for the material. has_reconstructed (bool): Whether the entry has any reconstructed surfaces. is_gap_direct (bool): Whether the material has a direct band gap. is_metal (bool): Whether the material is considered a metal. is_stable (bool): Whether the material lies on the convex energy hull. k_reuss (Tuple[float,float]): Minimum and maximum value in GPa to consider for the Reuss average of the bulk modulus. k_voigt (Tuple[float,float]): Minimum and maximum value in GPa to consider for the Voigt average of the bulk modulus. k_vrh (Tuple[float,float]): Minimum and maximum value in GPa to consider for the Voigt-Reuss-Hill average of the bulk modulus. magnetic_ordering (Ordering): Magnetic ordering of the material. material_ids (str, List[str]): A single Material ID string or list of strings (e.g., mp-149, [mp-149, mp-13]). n (Tuple[float,float]): Minimum and maximum refractive index to consider. nelements (Tuple[int,int]): Minimum and maximum number of elements to consider. num_elements (Tuple[int,int]): Alias for `nelements`, deprecated. Minimum and maximum number of elements to consider. num_sites (Tuple[int,int]): Minimum and maximum number of sites to consider. num_magnetic_sites (Tuple[int,int]): Minimum and maximum number of magnetic sites to consider. num_unique_magnetic_sites (Tuple[int,int]): Minimum and maximum number of unique magnetic sites to consider. piezoelectric_modulus (Tuple[float,float]): Minimum and maximum piezoelectric modulus to consider. poisson_ratio (Tuple[float,float]): Minimum and maximum value to consider for Poisson's ratio. possible_species (List(str)): List of element symbols appended with oxidation states. (e.g. Cr2+,O2-) shape_factor (Tuple[float,float]): Minimum and maximum shape factor values to consider. spacegroup_number (int): Space group number of material. spacegroup_symbol (str): Space group symbol of the material in international short symbol notation. surface_energy_anisotropy (Tuple[float,float]): Minimum and maximum surface energy anisotropy values to consider. theoretical: (bool): Whether the material is theoretical. total_energy (Tuple[int,int]): Minimum and maximum corrected total energy in eV/atom to consider. total_magnetization (Tuple[float,float]): Minimum and maximum total magnetization values to consider. total_magnetization_normalized_formula_units (Tuple[float,float]): Minimum and maximum total magnetization values normalized by formula units to consider. total_magnetization_normalized_vol (Tuple[float,float]): Minimum and maximum total magnetization values normalized by volume to consider. uncorrected_energy (Tuple[int,int]): Minimum and maximum uncorrected total energy in eV/atom to consider. volume (Tuple[float,float]): Minimum and maximum volume to consider. weighted_surface_energy (Tuple[float,float]): Minimum and maximum weighted surface energy in J/m² to consider. weighted_work_function (Tuple[float,float]): Minimum and maximum weighted work function in eV to consider. include_gnome (bool): whether to include materials from GNoMe dataset 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 SummaryDoc to return data for. Default is material_id if all_fields is False. Returns: ([SummaryDoc], [dict]) List of SummaryDoc documents or dictionaries. """ query_params = defaultdict(dict) # type: dict not_aliased_kwargs = [ "energy_above_hull", "nsites", "volume", "density", "band_gap", "efermi", "total_magnetization", "total_magnetization_normalized_vol", "total_magnetization_normalized_formula_units", "num_magnetic_sites", "num_unique_magnetic_sites", "k_voigt", "k_reuss", "k_vrh", "g_voigt", "g_reuss", "g_vrh", "e_total", "e_ionic", "e_electronic", "n", "weighted_surface_energy", "weighted_work_function", "shape_factor", ] min_max_name_dict = { "total_energy": "energy_per_atom", "formation_energy": "formation_energy_per_atom", "uncorrected_energy": "uncorrected_energy_per_atom", "equilibrium_reaction_energy": "equilibrium_reaction_energy_per_atom", "elastic_anisotropy": "universal_anisotropy", "poisson_ratio": "homogeneous_poisson", "num_sites": "nsites", "num_elements": "nelements", "piezoelectric_modulus": "e_ij_max", "surface_energy_anisotropy": "surface_anisotropy", } min_max_name_dict.update({k: k for k in not_aliased_kwargs}) mmnd_inv = {v: k for k, v in min_max_name_dict.items() if k != v} # Set user query params from `locals` user_settings = { k: v for k, v in locals().items() if k in min_max_name_dict and v } # Check to see if user specified _search fields using **kwargs, # or if any of the **kwargs are unparsable db_keys = {k: [] for k in ("duplicate", "warn", "unknown")} for k, v in kwargs.items(): category = "unknown" if non_db_k := mmnd_inv.get(k): if user_settings.get(non_db_k): # Both a search and _search equivalent field are specified category = "duplicate" elif v: # Only the _search field is specified category = "warn" user_settings[non_db_k] = v db_keys[category].append(non_db_k or k) # If any _search or unknown fields were set, throw warnings/exceptions if any(db_keys.values()): warning_strs: list[str] = [] exc_strs: list[str] = [] def csrc(x): return f"\x1b[34m{x}\x1b[39m" def _csrc(x): return f"\x1b[31m{x}\x1b[39m" # Warn the user if they input any fields from _search without setting equivalent kwargs in search if db_keys["warn"]: warning_strs.extend( [ f"You have specified fields used by {_csrc('`_search`')} that can be understood by {csrc('`search`')}", f" {', '.join([_csrc(min_max_name_dict[k]) for k in db_keys['warn']])}", f"To ensure long term support, please use their {csrc('`search`')} equivalents:", f" {', '.join([csrc(k) for k in db_keys['warn']])}", ] ) # Throw an exception if the user input a field from _search and its equivalent search kwarg if db_keys["duplicate"]: dupe_pairs = "\n".join( f"{csrc(k)} and {_csrc(min_max_name_dict[k])}" for k in db_keys["duplicate"] ) exc_strs.extend( [ f"You have specified fields known to both {csrc('`search`')} and {_csrc('`_search`')}", f" {dupe_pairs}", f"To avoid query ambiguity, please check your {csrc('`search`')} query and only specify", f" {', '.join([csrc(k) for k in db_keys['duplicate']])}", ] ) # Throw an exception if any unknown kwargs were input if db_keys["unknown"]: exc_strs.extend( [ f"You have specified the following kwargs which are unknown to {csrc('`search`')}, " f"but may be known to {_csrc('`_search`')}", f" \x1b[36m{', '.join(db_keys['unknown'])}\x1b[39m", ] ) # Always print links to documentation on warning / exception warn_ref_strs = [ "Please see the documentation:", f" {csrc('`search`: https://materialsproject.github.io/api/_autosummary/mp_api.client.routes.materials.summary.SummaryRester.html#mp_api.client.routes.materials.summary.SummaryRester.search')}", f" {_csrc('`_search`: https://api.materialsproject.org/redoc#tag/Materials-Summary/operation/search_materials_summary__get')}", ] if exc_strs: raise MPRestError("\n".join([*warning_strs, *exc_strs, *warn_ref_strs])) if warn_ref_strs: warnings.warn( "\n".join([*warning_strs, *warn_ref_strs]), category=MPRestWarning ) for param, value in user_settings.items(): if isinstance(value, (int, float)): value = (value, value) query_params.update( { f"{min_max_name_dict[param]}_min": value[0], f"{min_max_name_dict[param]}_max": value[1], } ) if material_ids: if isinstance(material_ids, str): material_ids = [material_ids] query_params.update({"material_ids": ",".join(validate_ids(material_ids))}) if deprecated is not None: query_params.update({"deprecated": deprecated}) 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 is not None: query_params.update({"exclude_elements": ",".join(exclude_elements)}) if possible_species is not None: query_params.update({"possible_species": ",".join(possible_species)}) query_params.update( { "crystal_system": crystal_system, "spacegroup_number": spacegroup_number, "spacegroup_symbol": spacegroup_symbol, } ) if is_stable is not None: query_params.update({"is_stable": is_stable}) if is_gap_direct is not None: query_params.update({"is_gap_direct": is_gap_direct}) if is_metal is not None: query_params.update({"is_metal": is_metal}) if magnetic_ordering: query_params.update({"ordering": magnetic_ordering.value}) if has_reconstructed is not None: query_params.update({"has_reconstructed": has_reconstructed}) if has_props: has_props_clean = [] for prop in has_props: try: has_props_clean.append(HasProps(prop).value) except ValueError: raise MPRestError(f"'{prop}' is not a valid property.") query_params.update({"has_props": ",".join(has_props_clean)}) if theoretical is not None: query_params.update({"theoretical": theoretical}) if not include_gnome: query_params.update({"batch_id_not_eq": "gnome_r2scan_statics"}) 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, )