diff --git a/src/mp_api/client.py b/src/mp_api/client.py index 2ee6bddb..7bd15d5f 100644 --- a/src/mp_api/client.py +++ b/src/mp_api/client.py @@ -246,7 +246,7 @@ def get_database_version(self): """ return get(url=self.endpoint + "heartbeat").json()["db_version"] - def get_materials_id_from_task_id(self, task_id: str) -> Union[str, None]: + def get_material_id_from_task_id(self, task_id: str) -> Union[str, None]: """ Returns the current material_id from a given task_id. The material_id should rarely change, and is usually chosen from @@ -273,9 +273,19 @@ def get_materials_id_from_task_id(self, task_id: str) -> Union[str, None]: ) return None - def get_materials_id_references(self, material_id: str) -> List[str]: + def get_materials_id_from_task_id(self, task_id: str) -> Union[str, None]: """ - Returns all references for a materials id. + This method is deprecated, please use get_material_id_from_task_id. + """ + warnings.warn( + "This method is deprecated, please use get_material_id_from_task_id.", + DeprecationWarning, + ) + return self.get_material_id_from_task_id(task_id) + + def get_material_id_references(self, material_id: str) -> List[str]: + """ + Returns all references for a material id. Args: material_id (str): A material id. @@ -285,10 +295,17 @@ def get_materials_id_references(self, material_id: str) -> List[str]: """ return self.provenance.get_data_by_id(material_id).references - def get_materials_ids( - self, - chemsys_formula: Union[str, List[str]], - ) -> List[MPID]: + def get_materials_id_references(self, material_id: str) -> List[str]: + """ + This method is deprecated, please use get_material_id_references. + """ + warnings.warn( + "This method is deprecated, please use get_material_id_references instead.", + DeprecationWarning, + ) + return self.get_material_id_references(material_id) + + def get_material_ids(self, chemsys_formula: Union[str, List[str]],) -> List[MPID]: """ Get all materials ids for a formula or chemsys. @@ -309,13 +326,21 @@ def get_materials_ids( return sorted( doc.material_id - for doc in self.materials.search_material_docs( - **input_params, # type: ignore - all_fields=False, - fields=["material_id"], + for doc in self.materials.search( + **input_params, all_fields=False, fields=["material_id"], # type: ignore ) ) + def get_materials_ids(self, chemsys_formula: Union[str, List[str]],) -> List[MPID]: + """ + This method is deprecated, please use get_material_ids. + """ + warnings.warn( + "This method is deprecated, please use get_material_ids.", + DeprecationWarning, + ) + return self.get_material_ids(chemsys_formula) + def get_structures( self, chemsys_formula: Union[str, List[str]], final=True ) -> List[Structure]: @@ -342,19 +367,15 @@ def get_structures( if final: return [ doc.structure - for doc in self.materials.search_material_docs( - **input_params, # type: ignore - all_fields=False, - fields=["structure"], + for doc in self.materials.search( + **input_params, all_fields=False, fields=["structure"], # type: ignore ) ] else: structures = [] - for doc in self.materials.search_material_docs( - **input_params, # type: ignore - all_fields=False, - fields=["initial_structures"], + for doc in self.materials.search( + **input_params, all_fields=False, fields=["initial_structures"], # type: ignore ): structures.extend(doc.initial_structures) @@ -428,7 +449,7 @@ def get_entries( if sort_by_e_above_hull: - for doc in self.thermo.search_thermo_docs( + for doc in self.thermo.search( **input_params, # type: ignore all_fields=False, fields=["entries"], @@ -439,10 +460,8 @@ def get_entries( return entries else: - for doc in self.thermo.search_thermo_docs( - **input_params, # type: ignore - all_fields=False, - fields=["entries"], + for doc in self.thermo.search( + **input_params, all_fields=False, fields=["entries"], # type: ignore ): entries.extend(list(doc.entries.values())) @@ -866,202 +885,6 @@ def get_phonon_bandstructure_by_material_id(self, material_id: str): """ return self.phonon.get_data_by_id(material_id, fields=["ph_bs"]).ph_bs - def query( - self, - material_ids: Optional[List[MPID]] = None, - formula: Optional[str] = None, - chemsys: Optional[Union[str, List[str]]] = None, - elements: Optional[List[str]] = None, - exclude_elements: Optional[List[str]] = None, - possible_species: Optional[List[str]] = None, - nsites: Optional[Tuple[int, int]] = None, - volume: Optional[Tuple[float, float]] = None, - density: Optional[Tuple[float, float]] = None, - crystal_system: Optional[CrystalSystem] = None, - spacegroup_number: Optional[int] = None, - spacegroup_symbol: Optional[str] = None, - deprecated: Optional[bool] = None, - total_energy: Optional[Tuple[float, float]] = None, - formation_energy: Optional[Tuple[float, float]] = None, - energy_above_hull: Optional[Tuple[float, float]] = None, - equilibrium_reaction_energy: Optional[Tuple[float, float]] = None, - uncorrected_energy: Optional[Tuple[float, float]] = None, - is_stable: Optional[bool] = None, - band_gap: Optional[Tuple[float, float]] = None, - efermi: Optional[Tuple[float, float]] = None, - is_gap_direct: Optional[bool] = None, - is_metal: Optional[bool] = None, - magnetic_ordering: Optional[Ordering] = None, - total_magnetization: Optional[Tuple[float, float]] = None, - total_magnetization_normalized_vol: Optional[Tuple[float, float]] = None, - total_magnetization_normalized_formula_units: Optional[ - Tuple[float, float] - ] = None, - num_magnetic_sites: Optional[Tuple[int, int]] = None, - num_unique_magnetic_sites: Optional[Tuple[int, int]] = None, - k_voigt: Optional[Tuple[float, float]] = None, - k_reuss: Optional[Tuple[float, float]] = None, - k_vrh: Optional[Tuple[float, float]] = None, - g_voigt: Optional[Tuple[float, float]] = None, - g_reuss: Optional[Tuple[float, float]] = None, - g_vrh: Optional[Tuple[float, float]] = None, - elastic_anisotropy: Optional[Tuple[float, float]] = None, - poisson_ratio: Optional[Tuple[float, float]] = None, - e_total: Optional[Tuple[float, float]] = None, - e_ionic: Optional[Tuple[float, float]] = None, - e_electronic: Optional[Tuple[float, float]] = None, - n: Optional[Tuple[float, float]] = None, - piezoelectric_modulus: Optional[Tuple[float, float]] = None, - weighted_surface_energy: Optional[Tuple[float, float]] = None, - weighted_work_function: Optional[Tuple[float, float]] = None, - surface_energy_anisotropy: Optional[Tuple[float, float]] = None, - shape_factor: Optional[Tuple[float, float]] = None, - has_reconstructed: Optional[bool] = None, - has_props: Optional[List[HasProps]] = None, - theoretical: Optional[bool] = None, - sort_fields: Optional[List[str]] = None, - num_chunks: Optional[int] = None, - chunk_size: int = 1000, - all_fields: bool = True, - fields: Optional[List[str]] = None, - ): - """ - Query core data using a variety of search criteria. - - Arguments: - material_ids (List[MPID]): List of Materials Project IDs to return data for. - formula (str): A formula including anonomyzed formula - or wild cards (e.g., Fe2O3, ABO3, Si*). - chemsys (str, List[str]): A chemical system, list of chemical systems - (e.g., Li-Fe-O, Si-*, [Si-O, Li-Fe-P]), or single formula (e.g., Fe2O3, Si*). - elements (List[str]): A list of elements. - exclude_elements (List(str)): List of elements to exclude. - possible_species (List(str)): List of element symbols appended with oxidation states. - (e.g. Cr2+,O2-) - crystal_system (CrystalSystem): Crystal system of material. - spacegroup_number (int): Space group number of material. - spacegroup_symbol (str): Space group symbol of the material in international short symbol notation. - nsites (Tuple[int,int]): Minimum and maximum number of sites to consider. - volume (Tuple[float,float]): Minimum and maximum volume to consider. - density (Tuple[float,float]): Minimum and maximum density to consider. - deprecated (bool): Whether the material is tagged as deprecated. - total_energy (Tuple[int,int]): Minimum and maximum corrected total energy in eV/atom to consider. - equilibrium_reaction_energy (Tuple[float,float]): Minimum and maximum equilibrium reaction energy - in eV/atom to consider. - formation_energy (Tuple[int,int]): Minimum and maximum formation energy in eV/atom to consider. - energy_above_hull (Tuple[int,int]): Minimum and maximum energy above the hull in eV/atom to consider. - uncorrected_energy (Tuple[int,int]): Minimum and maximum uncorrected total energy in eV/atom to consider. - band_gap (Tuple[float,float]): Minimum and maximum band gap in eV to consider. - efermi (Tuple[float,float]): Minimum and maximum fermi energy in eV to consider. - is_gap_direct (bool): Whether the material has a direct band gap. - is_metal (bool): Whether the material is considered a metal. - magnetic_ordering (Ordering): Magnetic ordering of the material. - total_magnetization (Tuple[float,float]): Minimum and maximum total magnetization values to consider. - total_magnetization_normalized_vol (Tuple[float,float]): Minimum and maximum total magnetization values - normalized by volume to consider. - total_magnetization_normalized_formula_units (Tuple[float,float]): Minimum and maximum total magnetization - values normalized by formula units 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. - k_voigt (Tuple[float,float]): Minimum and maximum value in GPa to consider for - the Voigt average of the bulk modulus. - k_reuss (Tuple[float,float]): Minimum and maximum value in GPa to consider for - the Reuss 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. - g_voigt (Tuple[float,float]): Minimum and maximum value in GPa to consider for - the Voigt average of the shear modulus. - g_reuss (Tuple[float,float]): Minimum and maximum value in GPa to consider for - the Reuss 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. - elastic_anisotropy (Tuple[float,float]): Minimum and maximum value to consider for - the elastic anisotropy. - poisson_ratio (Tuple[float,float]): Minimum and maximum value to consider for - Poisson's ratio. - e_total (Tuple[float,float]): Minimum and maximum total dielectric constant to consider. - e_ionic (Tuple[float,float]): Minimum and maximum ionic dielectric constant to consider. - e_electronic (Tuple[float,float]): Minimum and maximum electronic dielectric constant to consider. - n (Tuple[float,float]): Minimum and maximum refractive index to consider. - piezoelectric_modulus (Tuple[float,float]): Minimum and maximum piezoelectric modulus 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. - surface_energy_anisotropy (Tuple[float,float]): Minimum and maximum surface energy anisotropy values to - consider. - shape_factor (Tuple[float,float]): Minimum and maximum shape factor values to consider. - has_reconstructed (bool): Whether the entry has any reconstructed surfaces. - has_props: (List[HasProps]): The calculated properties available for the material. - theoretical: (bool): Whether the material is theoretical. - sort_fields (List[str]): Fields used to sort results. Prefixing with '-' will sort in descending order. - 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 SearchDoc to return data for. - Default is material_id if all_fields is False. - - Returns: - ([SummaryDoc]) List of SummaryDoc documents - """ - - return self.summary.search_summary_docs( # type: ignore - material_ids=material_ids, - formula=formula, - chemsys=chemsys, - elements=elements, - exclude_elements=exclude_elements, - possible_species=possible_species, - nsites=nsites, - volume=volume, - density=density, - crystal_system=crystal_system, - spacegroup_number=spacegroup_number, - spacegroup_symbol=spacegroup_symbol, - deprecated=deprecated, - total_energy=total_energy, - formation_energy=formation_energy, - energy_above_hull=energy_above_hull, - equilibrium_reaction_energy=equilibrium_reaction_energy, - uncorrected_energy=uncorrected_energy, - is_stable=is_stable, - band_gap=band_gap, - efermi=efermi, - is_gap_direct=is_gap_direct, - is_metal=is_metal, - magnetic_ordering=magnetic_ordering, - total_magnetization=total_magnetization, - total_magnetization_normalized_vol=total_magnetization_normalized_vol, - total_magnetization_normalized_formula_units=total_magnetization_normalized_formula_units, - num_magnetic_sites=num_magnetic_sites, - num_unique_magnetic_sites=num_unique_magnetic_sites, - k_voigt=k_voigt, - k_reuss=k_reuss, - k_vrh=k_vrh, - g_voigt=g_voigt, - g_reuss=g_reuss, - g_vrh=g_vrh, - elastic_anisotropy=elastic_anisotropy, - poisson_ratio=poisson_ratio, - e_total=e_total, - e_ionic=e_ionic, - e_electronic=e_electronic, - n=n, - piezoelectric_modulus=piezoelectric_modulus, - weighted_surface_energy=weighted_surface_energy, - weighted_work_function=weighted_work_function, - surface_energy_anisotropy=surface_energy_anisotropy, - shape_factor=shape_factor, - has_reconstructed=has_reconstructed, - has_props=has_props, - theoretical=theoretical, - sort_fields=sort_fields, - num_chunks=num_chunks, - chunk_size=chunk_size, - all_fields=all_fields, - fields=fields, - ) - def submit_structures(self, structures, public_name, public_email): """ Submits a list of structures to the Materials Project. @@ -1142,3 +965,17 @@ def get_charge_density_from_material_id( return chgcar, task_doc return chgcar + + def query(*args, **kwargs): + """ + The MPRester().query method has been replaced with the MPRester().summary.search method. + Note this method also no longer supports direct MongoDB-type queries. For more information, + please see the new documentation. + """ + raise NotImplementedError( + """ + The MPRester().query method has been replaced with the MPRester().summary.search method. + Note this method also no longer supports direct MongoDB-type queries. For more information, + please see the new documentation. + """ + ) diff --git a/src/mp_api/core/client.py b/src/mp_api/core/client.py index ab5c1542..211659bc 100644 --- a/src/mp_api/core/client.py +++ b/src/mp_api/core/client.py @@ -25,8 +25,8 @@ from matplotlib import use from monty.json import MontyDecoder from mp_api.core.settings import MAPIClientSettings +from pydantic import BaseModel, create_model from mp_api.core.utils import validate_ids -from pydantic import BaseModel from requests.adapters import HTTPAdapter from requests.exceptions import RequestException from tqdm.auto import tqdm @@ -567,14 +567,18 @@ def _multi_thread( with ThreadPoolExecutor(max_workers=MAPIClientSettings().NUM_PARALLEL_REQUESTS) as executor: # Get list of initial futures defined by max number of parallel requests - futures = set({}) - for params in itertools.islice(params_gen, MAPIClientSettings().NUM_PARALLEL_REQUESTS): + futures = set() + + for params in itertools.islice( + params_gen, MAPIClientSettings().NUM_PARALLEL_REQUESTS + ): future = executor.submit( self._submit_request_and_process, use_document_model=use_document_model, **params, ) + setattr(future, "crit_ind", params_ind) futures.add(future) params_ind += 1 @@ -584,18 +588,22 @@ def _multi_thread( finished, futures = wait(futures, return_when=FIRST_COMPLETED) for future in finished: + data, subtotal = future.result() + if progress_bar is not None: progress_bar.update(len(data["data"])) return_data.append((data, subtotal, future.crit_ind)) # type: ignore # Populate more futures to replace finished for params in itertools.islice(params_gen, len(finished)): + new_future = executor.submit( self._submit_request_and_process, use_document_model=use_document_model, **params, ) + setattr(new_future, "crit_ind", params_ind) futures.add(new_future) params_ind += 1 @@ -630,7 +638,12 @@ def _submit_request_and_process( # other sub-urls may use different document models # the client does not handle this in a particularly smart way currently if self.document_model and use_document_model: - data["data"] = [self.document_model.parse_obj(d) for d in data["data"]] # type: ignore + raw_doc_list = [self.document_model.parse_obj(d) for d in data["data"]] # type: ignore + + # Temporarily removed until user-testing completed + # data["data"] = self._generate_returned_model(raw_doc_list) + + data["data"] = raw_doc_list meta_total_doc_num = data.get("meta", {}).get("total_doc", 1) @@ -654,6 +667,45 @@ def _submit_request_and_process( f"on URL {response.url} with message:\n{message}" ) + def _generate_returned_model(self, data): + + new_data = [] + + for doc in data: + set_data = { + field: value + for field, value in doc + if field in doc.dict(exclude_unset=True) + } + unset_fields = [field for field in doc.__fields__ if field not in set_data] + + data_model = create_model( + "MPDataEntry", + fields_not_requested=unset_fields, + __base__=self.document_model, + ) + + data_model.__fields__ = { + **{ + name: description + for name, description in data_model.__fields__.items() + if name in set_data + }, + "fields_not_requested": data_model.__fields__["fields_not_requested"], + } + + def new_repr(self) -> str: + extra = ", ".join( + f"{n}={getattr(self, n)!r}" for n in data_model.__fields__ + ) + return f"{self.__class__.__name__}<{self.__class__.__base__.__name__}>({extra})" + + data_model.__repr__ = new_repr + + new_data.append(data_model(**set_data)) + + return new_data + def _query_resource_data( self, criteria: Optional[Dict] = None, @@ -752,7 +804,7 @@ def get_data_by_id( else: return results[0] - def search( + def _search( self, num_chunks: Optional[int] = None, chunk_size: int = 1000, diff --git a/src/mp_api/routes/bonds.py b/src/mp_api/routes/bonds.py index 07701a28..b5c2af19 100644 --- a/src/mp_api/routes/bonds.py +++ b/src/mp_api/routes/bonds.py @@ -4,6 +4,8 @@ from mp_api.core.client import BaseRester from emmet.core.bonds import BondingDoc +import warnings + class BondsRester(BaseRester[BondingDoc]): @@ -11,13 +13,26 @@ class BondsRester(BaseRester[BondingDoc]): document_model = BondingDoc # type: ignore primary_key = "material_id" - def search_bonds_docs( + def search_bonds_docs(self, *args, **kwargs): # pragma: no cover + """ + Deprecated + """ + + warnings.warn( + "MPRester.bonds.search_bonds_docs is deprecated. Please use MPRester.bonds.search instead.", + DeprecationWarning, + stacklevel=2, + ) + + return self.search(*args, **kwargs) + + def search( self, - max_bond_length: Optional[Tuple[float, float]] = None, - min_bond_length: Optional[Tuple[float, float]] = None, - mean_bond_length: Optional[Tuple[float, float]] = None, coordination_envs: Optional[List[str]] = None, coordination_envs_anonymous: Optional[List[str]] = None, + max_bond_length: Optional[Tuple[float, float]] = None, + mean_bond_length: Optional[Tuple[float, float]] = None, + min_bond_length: Optional[Tuple[float, float]] = None, sort_fields: Optional[List[str]] = None, num_chunks: Optional[int] = None, chunk_size: int = 1000, @@ -28,15 +43,15 @@ def search_bonds_docs( Query bonding docs using a variety of search criteria. Arguments: + coordination_envs (List[str]): List of coordination environments to consider (e.g. ['Mo-S(6)', 'S-Mo(3)']). + coordination_envs_anonymous (List[str]): List of anonymous coordination environments to consider + (e.g. ['A-B(6)', 'A-B(3)']). max_bond_length (Tuple[float,float]): Minimum and maximum value for the maximum bond length in the structure to consider. - min_bond_length (Tuple[float,float]): Minimum and maximum value for the minimum bond length - in the structure to consider. mean_bond_length (Tuple[float,float]): Minimum and maximum value for the mean bond length in the structure to consider. - coordination_envs (List[str]): List of coordination environments to consider (e.g. ['Mo-S(6)', 'S-Mo(3)']). - coordination_envs_anonymous (List[str]): List of anonymous coordination environments to consider - (e.g. ['A-B(6)', 'A-B(3)']). + min_bond_length (Tuple[float,float]): Minimum and maximum value for the minimum bond length + in the structure to consider. sort_fields (List[str]): Fields used to sort results. Prefixing with '-' will sort in descending order. 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. @@ -93,7 +108,7 @@ def search_bonds_docs( if query_params[entry] is not None } - return super().search( + return super()._search( num_chunks=num_chunks, chunk_size=chunk_size, all_fields=all_fields, diff --git a/src/mp_api/routes/charge_density.py b/src/mp_api/routes/charge_density.py index 177a0dd6..15fec3f7 100644 --- a/src/mp_api/routes/charge_density.py +++ b/src/mp_api/routes/charge_density.py @@ -67,7 +67,7 @@ def search( # type: ignore A list of ChgcarDataDoc that contain task_id references. """ - return super().search( + return super()._search( num_chunks=num_chunks, chunk_size=chunk_size, all_fields=False, diff --git a/src/mp_api/routes/dielectric.py b/src/mp_api/routes/dielectric.py index 71af1b8a..a253a81c 100644 --- a/src/mp_api/routes/dielectric.py +++ b/src/mp_api/routes/dielectric.py @@ -4,6 +4,8 @@ from emmet.core.polar import DielectricDoc from mp_api.core.client import BaseRester +import warnings + class DielectricRester(BaseRester[DielectricDoc]): @@ -11,7 +13,20 @@ class DielectricRester(BaseRester[DielectricDoc]): document_model = DielectricDoc # type: ignore primary_key = "material_id" - def search_dielectric_docs( + def search_dielectric_docs(self, *args, **kwargs): # pragma: no cover + """ + Deprecated + """ + + warnings.warn( + "MPRester.dielectric.search_dielectric_docs is deprecated. Please use MPRester.dielectric.search instead.", + DeprecationWarning, + stacklevel=2, + ) + + return self.search(*args, **kwargs) + + def search( self, e_total: Optional[Tuple[float, float]] = None, e_ionic: Optional[Tuple[float, float]] = None, @@ -72,7 +87,7 @@ def search_dielectric_docs( if query_params[entry] is not None } - return super().search( + return super()._search( num_chunks=num_chunks, chunk_size=chunk_size, all_fields=all_fields, diff --git a/src/mp_api/routes/doi.py b/src/mp_api/routes/doi.py index 64c9de50..4807971e 100644 --- a/src/mp_api/routes/doi.py +++ b/src/mp_api/routes/doi.py @@ -7,3 +7,10 @@ class DOIRester(BaseRester[DOIDoc]): suffix = "doi" document_model = DOIDoc # type: ignore primary_key = "task_id" + + def search(*args, **kwargs): # pragma: no cover + raise NotImplementedError( + """ + The DOIRester.search method does not exist as no search endpoint is present. Use get_data_by_id instead. + """ + ) diff --git a/src/mp_api/routes/elasticity.py b/src/mp_api/routes/elasticity.py index 87330934..1c36aabf 100644 --- a/src/mp_api/routes/elasticity.py +++ b/src/mp_api/routes/elasticity.py @@ -4,6 +4,8 @@ from emmet.core.elasticity import ElasticityDoc from mp_api.core.client import BaseRester +import warnings + class ElasticityRester(BaseRester[ElasticityDoc]): @@ -11,15 +13,28 @@ class ElasticityRester(BaseRester[ElasticityDoc]): document_model = ElasticityDoc # type: ignore primary_key = "task_id" - def search_elasticity_docs( + def search_elasticity_docs(self, *args, **kwargs): # pragma: no cover + """ + Deprecated + """ + + warnings.warn( + "MPRester.elasticity.search_elasticity_docs is deprecated. Please use MPRester.elasticity.search instead.", + DeprecationWarning, + stacklevel=2, + ) + + return self.search(*args, **kwargs) + + def search( self, - k_voigt: Optional[Tuple[float, float]] = None, - k_reuss: Optional[Tuple[float, float]] = None, - k_vrh: Optional[Tuple[float, float]] = None, + elastic_anisotropy: Optional[Tuple[float, float]] = None, g_voigt: Optional[Tuple[float, float]] = None, g_reuss: Optional[Tuple[float, float]] = None, g_vrh: Optional[Tuple[float, float]] = None, - elastic_anisotropy: Optional[Tuple[float, float]] = None, + k_voigt: Optional[Tuple[float, float]] = None, + k_reuss: Optional[Tuple[float, float]] = None, + k_vrh: Optional[Tuple[float, float]] = None, poisson_ratio: Optional[Tuple[float, float]] = None, sort_fields: Optional[List[str]] = None, num_chunks: Optional[int] = None, @@ -31,20 +46,20 @@ def search_elasticity_docs( Query elasticity docs using a variety of search criteria. Arguments: - k_voigt (Tuple[float,float]): Minimum and maximum value in GPa to consider for - the Voigt average of the bulk modulus. - k_reuss (Tuple[float,float]): Minimum and maximum value in GPa to consider for - the Reuss 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. + elastic_anisotropy (Tuple[float,float]): Minimum and maximum value to consider for + the elastic anisotropy. g_voigt (Tuple[float,float]): Minimum and maximum value in GPa to consider for the Voigt average of the shear modulus. g_reuss (Tuple[float,float]): Minimum and maximum value in GPa to consider for the Reuss 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. - elastic_anisotropy (Tuple[float,float]): Minimum and maximum value to consider for - the elastic anisotropy. + k_voigt (Tuple[float,float]): Minimum and maximum value in GPa to consider for + the Voigt average of the bulk modulus. + k_reuss (Tuple[float,float]): Minimum and maximum value in GPa to consider for + the Reuss 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. poisson_ratio (Tuple[float,float]): Minimum and maximum value to consider for Poisson's ratio. sort_fields (List[str]): Fields used to sort results. Prefix with '-' to sort in descending order. @@ -102,7 +117,7 @@ def search_elasticity_docs( if query_params[entry] is not None } - return super().search( + return super()._search( num_chunks=num_chunks, chunk_size=chunk_size, all_fields=all_fields, diff --git a/src/mp_api/routes/electrodes.py b/src/mp_api/routes/electrodes.py index a48ae183..2d527c97 100644 --- a/src/mp_api/routes/electrodes.py +++ b/src/mp_api/routes/electrodes.py @@ -1,9 +1,11 @@ from pymatgen.core.periodic_table import Element from mp_api.core.client import BaseRester from emmet.core.electrode import InsertionElectrodeDoc -from typing import Optional, Tuple, List +from typing import Optional, Tuple, List, Union from collections import defaultdict +import warnings + class ElectrodeRester(BaseRester[InsertionElectrodeDoc]): @@ -11,24 +13,38 @@ class ElectrodeRester(BaseRester[InsertionElectrodeDoc]): document_model = InsertionElectrodeDoc # type: ignore primary_key = "battery_id" - def search_electrode_docs( # pragma: ignore + def search_electrode_docs(self, *args, **kwargs): # pragma: no cover + """ + Deprecated + """ + + warnings.warn( + "MPRester.electrode.search_electrode_docs is deprecated. Please use MPRester.electrode.search instead.", + DeprecationWarning, + stacklevel=2, + ) + + return self.search(*args, **kwargs) + + def search( # pragma: ignore self, - working_ion: Optional[Element] = None, - formula: Optional[str] = None, - elements: Optional[List[str]] = None, - exclude_elements: Optional[List[str]] = None, - max_delta_volume: Optional[Tuple[float, float]] = None, average_voltage: Optional[Tuple[float, float]] = None, capacity_grav: Optional[Tuple[float, float]] = None, capacity_vol: Optional[Tuple[float, float]] = None, + elements: Optional[List[str]] = None, energy_grav: Optional[Tuple[float, float]] = None, energy_vol: Optional[Tuple[float, float]] = None, + exclude_elements: Optional[List[str]] = None, + formula: Optional[Union[str, List[str]]] = None, fracA_charge: Optional[Tuple[float, float]] = None, fracA_discharge: Optional[Tuple[float, float]] = None, + max_delta_volume: Optional[Tuple[float, float]] = None, + max_voltage_step: Optional[Tuple[float, float]] = None, + num_elements: Optional[Tuple[int, int]] = None, + num_steps: Optional[Tuple[int, int]] = None, stability_charge: Optional[Tuple[float, float]] = None, stability_discharge: Optional[Tuple[float, float]] = None, - num_steps: Optional[Tuple[int, int]] = None, - max_voltage_step: Optional[Tuple[float, float]] = None, + working_ion: Optional[Element] = None, sort_fields: Optional[List[str]] = None, num_chunks: Optional[int] = None, chunk_size: int = 1000, @@ -39,28 +55,33 @@ def search_electrode_docs( # pragma: ignore Query equations of state docs using a variety of search criteria. Arguments: - working_ion (Element): Element of the working ion. - formula (str): Chemical formula of the framework material. - elements (List[str]): A list of elements for the framework material. - exclude_elements (List[str]): A list of elements to exclude for the framework material. - max_delta_volume (Tuple[float,float]): Minimum and maximum value of the max volume change in percent for a - particular voltage step. average_voltage (Tuple[float,float]): Minimum and maximum value of the average voltage for a particular voltage step in V. - max_voltage_step (Tuple[float,float]): Minimum and maximum value of the maximum voltage for a particular - voltage step in V. capacity_grav (Tuple[float,float]): Minimum and maximum value of the gravimetric capacity in maH/g. capacity_vol (Tuple[float,float]): Minimum and maximum value of the volumetric capacity in maH/cc. + elements (List[str]): A list of elements for the framework material. energy_grav (Tuple[float,float]): Minimum and maximum value of the gravimetric energy (specific energy) in Wh/kg. + energy_vol (Tuple[float,float]): Minimum and maximum value of the volumetric energy (energy density) + in Wh/l. + exclude_elements (List[str]): A list of elements to exclude for the framework material. + formula (str, List[str]): Chemical formula or list of chemical formulas of any of the materials + associated with the electrode system. This includes materials partially along the charge-discharge path. fracA_charge (Tuple[float,float]): Minimum and maximum value of the atomic fraction of the working ion in the charged state. fracA_discharge (Tuple[float,float]): Minimum and maximum value of the atomic fraction of the working ion in the discharged state. + max_delta_volume (Tuple[float,float]): Minimum and maximum value of the max volume change in percent for a + particular voltage step. + max_voltage_step (Tuple[float,float]): Minimum and maximum value of the maximum voltage for a particular + voltage step in V. + num_elements (Tuple[int,int]): Minimum and maximum number of elements to consider. + num_steps (int): Number of distinct voltage steps from charged to discharged based on stable intermediates. stability_charge (Tuple[float,float]): Minimum and maximum value of the energy above hull of the charged material. stability_discharge (Tuple[float,float]): Minimum and maximum value of the energy above hull of the discharged material. + working_ion (Element, List[Element]): Element or list of elements of the working ion. sort_fields (List[str]): Fields used to sort results. Prefix with '-' to sort in descending order. 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. @@ -74,14 +95,27 @@ def search_electrode_docs( # pragma: ignore query_params = defaultdict(dict) # type: dict if working_ion: - query_params.update({"working_ion": str(working_ion)}) + if isinstance(working_ion, str) or isinstance(working_ion, Element): + working_ion = [working_ion] # type: ignore + + query_params.update( + {"working_ion": ",".join([str(ele) for ele in working_ion])} # type: ignore + ) if formula: - query_params.update({"formula": formula}) + if isinstance(formula, str): + formula = [formula] + + query_params.update({"formula": ",".join(formula)}) if elements: query_params.update({"elements": ",".join(elements)}) + if num_elements: + query_params.update( + {"nelements_min": num_elements[0], "nelements_max": num_elements[1]} + ) + if exclude_elements: query_params.update({"exclude_elements": ",".join(exclude_elements)}) @@ -92,7 +126,14 @@ def search_electrode_docs( # pragma: ignore for param, value in locals().items(): if ( - param not in ["__class__", "self", "working_ion", "query_params"] + param + not in [ + "__class__", + "self", + "working_ion", + "query_params", + "num_elements", + ] and value ): if isinstance(value, tuple): @@ -108,4 +149,4 @@ def search_electrode_docs( # pragma: ignore if query_params[entry] is not None } - return super().search(**query_params) + return super()._search(**query_params) diff --git a/src/mp_api/routes/electronic_structure.py b/src/mp_api/routes/electronic_structure.py index f175f191..adaf7418 100644 --- a/src/mp_api/routes/electronic_structure.py +++ b/src/mp_api/routes/electronic_structure.py @@ -4,13 +4,19 @@ from typing import List, Optional, Tuple, Union import msgpack -from emmet.core.electronic_structure import BSPathType, DOSProjectionType, ElectronicStructureDoc +from emmet.core.electronic_structure import ( + BSPathType, + DOSProjectionType, + ElectronicStructureDoc, +) from monty.serialization import MontyDecoder from mp_api.core.client import BaseRester, MPRestError from pymatgen.analysis.magnetism.analyzer import Ordering from pymatgen.core.periodic_table import Element from pymatgen.electronic_structure.core import OrbitalType, Spin +import warnings + class ElectronicStructureRester(BaseRester[ElectronicStructureDoc]): @@ -18,17 +24,32 @@ class ElectronicStructureRester(BaseRester[ElectronicStructureDoc]): document_model = ElectronicStructureDoc # type: ignore primary_key = "material_id" - def search_electronic_structure_docs( + def search_electronic_structure_docs(self, *args, **kwargs): # pragma: no cover + """ + Deprecated + """ + + warnings.warn( + "MPRester.electronic_structure.search_electronic_structure_docs is deprecated. " + "Please use MPRester.electronic_structure.search instead.", + DeprecationWarning, + stacklevel=2, + ) + + return self.search(*args, **kwargs) + + def search( self, - formula: Optional[str] = None, + band_gap: Optional[Tuple[float, float]] = None, chemsys: Optional[Union[str, List[str]]] = None, + efermi: Optional[Tuple[float, float]] = None, elements: Optional[List[str]] = None, exclude_elements: Optional[List[str]] = None, - band_gap: Optional[Tuple[float, float]] = None, - efermi: Optional[Tuple[float, float]] = None, - magnetic_ordering: Optional[Ordering] = None, + formula: Optional[Union[str, List[str]]] = None, is_gap_direct: bool = None, is_metal: bool = None, + magnetic_ordering: Optional[Ordering] = None, + num_elements: Optional[Tuple[int, int]] = None, sort_fields: Optional[List[str]] = None, num_chunks: Optional[int] = None, chunk_size: int = 1000, @@ -39,17 +60,19 @@ def search_electronic_structure_docs( Query electronic structure docs using a variety of search criteria. Arguments: - formula (str): A formula including anonomyzed formula - or wild cards (e.g., Fe2O3, ABO3, Si*). + 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]). + efermi (Tuple[float,float]): Minimum and maximum fermi energy in eV to consider. elements (List[str]): A list of elements. exclude_elements (List[str]): A list of elements to exclude. - band_gap (Tuple[float,float]): Minimum and maximum band gap in eV to consider. - efermi (Tuple[float,float]): Minimum and maximum fermi energy in eV to consider. - magnetic_ordering (Ordering): Magnetic ordering of the material. + formula (str, List[str]): A formula including anonomyzed formula + or wild cards (e.g., Fe2O3, ABO3, Si*). A list of chemical formulas can also be passed + (e.g., [Fe2O3, ABO3]). is_gap_direct (bool): Whether the material has a direct band gap. is_metal (bool): Whether the material is considered a metal. + magnetic_ordering (Ordering): Magnetic ordering of the material. + num_elements (Tuple[int,int]): Minimum and maximum number of elements to consider. sort_fields (List[str]): Fields used to sort results. Prefix with '-' to sort in descending order. 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. @@ -64,7 +87,10 @@ def search_electronic_structure_docs( query_params = defaultdict(dict) # type: dict if formula: - query_params.update({"formula": formula}) + if isinstance(formula, str): + formula = [formula] + + query_params.update({"formula": ",".join(formula)}) if chemsys: if isinstance(chemsys, str): @@ -79,7 +105,9 @@ def search_electronic_structure_docs( query_params.update({"exclude_elements": ",".join(exclude_elements)}) if band_gap: - query_params.update({"band_gap_min": band_gap[0], "band_gap_max": band_gap[1]}) + query_params.update( + {"band_gap_min": band_gap[0], "band_gap_max": band_gap[1]} + ) if efermi: query_params.update({"efermi_min": efermi[0], "efermi_max": efermi[1]}) @@ -87,6 +115,11 @@ def search_electronic_structure_docs( if magnetic_ordering: query_params.update({"magnetic_ordering": magnetic_ordering.value}) + if num_elements: + query_params.update( + {"nelements_min": num_elements[0], "nelements_max": num_elements[1]} + ) + if is_gap_direct is not None: query_params.update({"is_gap_direct": is_gap_direct}) @@ -98,10 +131,18 @@ def search_electronic_structure_docs( {"_sort_fields": ",".join([s.strip() for s in sort_fields])} ) - query_params = {entry: query_params[entry] for entry in query_params if query_params[entry] is not None} + 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 + return super()._search( + num_chunks=num_chunks, + chunk_size=chunk_size, + all_fields=all_fields, + fields=fields, + **query_params ) @@ -110,14 +151,28 @@ class BandStructureRester(BaseRester): suffix = "electronic_structure/bandstructure" document_model = ElectronicStructureDoc # type: ignore - def search_bandstructure_summary( + def search_bandstructure_summary(self, *args, **kwargs): # pragma: no cover + """ + Deprecated + """ + + warnings.warn( + "MPRester.electronic_structure_bandstructure.search_bandstructure_summary is deprecated. " + "Please use MPRester.electronic_structure_bandstructure.search instead.", + DeprecationWarning, + stacklevel=2, + ) + + return self.search(*args, **kwargs) + + def search( self, - path_type: BSPathType = BSPathType.setyawan_curtarolo, band_gap: Optional[Tuple[float, float]] = None, efermi: Optional[Tuple[float, float]] = None, - magnetic_ordering: Optional[Ordering] = None, is_gap_direct: bool = None, is_metal: bool = None, + magnetic_ordering: Optional[Ordering] = None, + path_type: BSPathType = BSPathType.setyawan_curtarolo, sort_fields: Optional[List[str]] = None, num_chunks: Optional[int] = None, chunk_size: int = 1000, @@ -128,12 +183,12 @@ def search_bandstructure_summary( Query band structure summary data in electronic structure docs using a variety of search criteria. Arguments: - path_type (BSPathType): k-path selection convention for the band structure. band_gap (Tuple[float,float]): Minimum and maximum band gap in eV to consider. efermi (Tuple[float,float]): Minimum and maximum fermi energy in eV to consider. - magnetic_ordering (Ordering): Magnetic ordering of the material. is_gap_direct (bool): Whether the material has a direct band gap. is_metal (bool): Whether the material is considered a metal. + magnetic_ordering (Ordering): Magnetic ordering of the material. + path_type (BSPathType): k-path selection convention for the band structure. sort_fields (List[str]): Fields used to sort results. Prefix with '-' to sort in descending order. 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. @@ -150,7 +205,9 @@ def search_bandstructure_summary( query_params["path_type"] = path_type.value if band_gap: - query_params.update({"band_gap_min": band_gap[0], "band_gap_max": band_gap[1]}) + query_params.update( + {"band_gap_min": band_gap[0], "band_gap_max": band_gap[1]} + ) if efermi: query_params.update({"efermi_min": efermi[0], "efermi_max": efermi[1]}) @@ -169,10 +226,18 @@ def search_bandstructure_summary( {"_sort_fields": ",".join([s.strip() for s in sort_fields])} ) - query_params = {entry: query_params[entry] for entry in query_params if query_params[entry] is not None} + 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 + return super()._search( + num_chunks=num_chunks, + chunk_size=chunk_size, + all_fields=all_fields, + fields=fields, + **query_params ) def get_bandstructure_from_task_id(self, task_id: str): @@ -217,32 +282,50 @@ def get_bandstructure_from_material_id( bandstructure (Union[BandStructure, BandStructureSymmLine]): BandStructure or BandStructureSymmLine object """ - es_rester = ElectronicStructureRester(endpoint=self.base_endpoint, api_key=self.api_key) + 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 + 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)) + 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)) + 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 + 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)) + 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)) + raise MPRestError( + "No uniform band structure data found for {}".format(material_id) + ) bs_obj = self.get_bandstructure_from_task_id(bs_task_id) @@ -262,15 +345,29 @@ class DosRester(BaseRester): suffix = "electronic_structure/dos" document_model = ElectronicStructureDoc # type: ignore - def search_dos_summary( + def search_dos_summary(self, *args, **kwargs): # pragma: no cover + """ + Deprecated + """ + + warnings.warn( + "MPRester.electronic_structure_dos.search_dos_summary is deprecated. " + "Please use MPRester.electronic_structure_dos.search instead.", + DeprecationWarning, + stacklevel=2, + ) + + return self.search(*args, **kwargs) + + def search( self, - projection_type: DOSProjectionType = DOSProjectionType.total, - spin: Spin = Spin.up, - element: Optional[Element] = None, - orbital: Optional[OrbitalType] = None, band_gap: Optional[Tuple[float, float]] = None, efermi: Optional[Tuple[float, float]] = None, + element: Optional[Element] = None, magnetic_ordering: Optional[Ordering] = None, + orbital: Optional[OrbitalType] = None, + projection_type: DOSProjectionType = DOSProjectionType.total, + spin: Spin = Spin.up, sort_fields: Optional[List[str]] = None, num_chunks: Optional[int] = None, chunk_size: int = 1000, @@ -281,13 +378,13 @@ def search_dos_summary( Query density of states summary data in electronic structure docs using a variety of search criteria. Arguments: - projection_type (DOSProjectionType): Projection type of dos data. Default is the total dos. - spin (Spin): Spin channel of dos data. If non spin-polarized data is stored in Spin.up - element (Element): Element for element-projected dos data. - orbital (OrbitalType): Orbital for orbital-projected dos data. band_gap (Tuple[float,float]): Minimum and maximum band gap in eV to consider. efermi (Tuple[float,float]): Minimum and maximum fermi energy in eV to consider. + element (Element): Element for element-projected dos data. magnetic_ordering (Ordering): Magnetic ordering of the material. + orbital (OrbitalType): Orbital for orbital-projected dos data. + projection_type (DOSProjectionType): Projection type of dos data. Default is the total dos. + spin (Spin): Spin channel of dos data. If non spin-polarized data is stored in Spin.up sort_fields (List[str]): Fields used to sort results. Prefix with '-' to sort in descending order. 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. @@ -311,7 +408,9 @@ def search_dos_summary( query_params["orbital"] = orbital.value if band_gap: - query_params.update({"band_gap_min": band_gap[0], "band_gap_max": band_gap[1]}) + query_params.update( + {"band_gap_min": band_gap[0], "band_gap_max": band_gap[1]} + ) if efermi: query_params.update({"efermi_min": efermi[0], "efermi_max": efermi[1]}) @@ -330,8 +429,12 @@ def search_dos_summary( 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 + return super()._search( + num_chunks=num_chunks, + chunk_size=chunk_size, + all_fields=all_fields, + fields=fields, + **query_params ) def get_dos_from_task_id(self, task_id: str): @@ -369,14 +472,20 @@ def get_dos_from_material_id(self, material_id: str): dos (CompleteDos): CompleteDos object """ - es_rester = ElectronicStructureRester(endpoint=self.base_endpoint, api_key=self.api_key) + 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() + 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)) + 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: diff --git a/src/mp_api/routes/eos.py b/src/mp_api/routes/eos.py index eb50fdd6..15b24ff0 100644 --- a/src/mp_api/routes/eos.py +++ b/src/mp_api/routes/eos.py @@ -4,6 +4,8 @@ from emmet.core.eos import EOSDoc from mp_api.core.client import BaseRester +import warnings + class EOSRester(BaseRester[EOSDoc]): @@ -11,10 +13,24 @@ class EOSRester(BaseRester[EOSDoc]): document_model = EOSDoc # type: ignore primary_key = "task_id" - def search_eos_docs( + def search_eos_docs(self, *args, **kwargs): # pragma: no cover + """ + Deprecated + """ + + warnings.warn( + "MPRester.eos.search_eos_docs is deprecated. " + "Please use MPRester.eos.search instead.", + DeprecationWarning, + stacklevel=2, + ) + + return self.search(*args, **kwargs) + + def search( self, - volumes: Optional[Tuple[float, float]] = None, energies: Optional[Tuple[float, float]] = None, + volumes: Optional[Tuple[float, float]] = None, sort_fields: Optional[List[str]] = None, num_chunks: Optional[int] = None, chunk_size: int = 1000, @@ -25,8 +41,8 @@ def search_eos_docs( Query equations of state docs using a variety of search criteria. Arguments: - volumes (Tuple[float,float]): Minimum and maximum volume in A³/atom to consider for EOS plot range. energies (Tuple[float,float]): Minimum and maximum energy in eV/atom to consider for EOS plot range. + volumes (Tuple[float,float]): Minimum and maximum volume in A³/atom to consider for EOS plot range. sort_fields (List[str]): Fields used to sort results. Prefix with '-' to sort in descending order. 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. @@ -59,7 +75,7 @@ def search_eos_docs( if query_params[entry] is not None } - return super().search( + return super()._search( num_chunks=num_chunks, chunk_size=chunk_size, all_fields=all_fields, diff --git a/src/mp_api/routes/fermi.py b/src/mp_api/routes/fermi.py index 652862fb..91a010c3 100644 --- a/src/mp_api/routes/fermi.py +++ b/src/mp_api/routes/fermi.py @@ -1,5 +1,6 @@ from emmet.core.fermi import FermiDoc from mp_api.core.client import BaseRester +from typing import Optional, List class FermiRester(BaseRester[FermiDoc]): @@ -7,3 +8,31 @@ class FermiRester(BaseRester[FermiDoc]): suffix = "fermi" document_model = FermiDoc # type: ignore primary_key = "task_id" + + def search( + self, + num_chunks: Optional[int] = None, + chunk_size: int = 1000, + all_fields: bool = True, + fields: Optional[List[str]] = None, + ): + """ + Query fermi surface docs using a variety of search criteria. + + Arguments: + 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 FermiDoc to return data for. + Default is material_id, last_updated, and formula_pretty if all_fields is False. + + Returns: + ([FermiDoc]) List of material documents + """ + + return super()._search( + num_chunks=num_chunks, + chunk_size=chunk_size, + all_fields=all_fields, + fields=fields, + ) diff --git a/src/mp_api/routes/grain_boundary.py b/src/mp_api/routes/grain_boundary.py index e95dba25..661c31f6 100644 --- a/src/mp_api/routes/grain_boundary.py +++ b/src/mp_api/routes/grain_boundary.py @@ -6,6 +6,8 @@ from emmet.core.grain_boundary import GBTypeEnum, GrainBoundaryDoc +import warnings + class GrainBoundaryRester(BaseRester[GrainBoundaryDoc]): @@ -13,18 +15,32 @@ class GrainBoundaryRester(BaseRester[GrainBoundaryDoc]): document_model = GrainBoundaryDoc # type: ignore primary_key = "task_id" - def search_grain_boundary_docs( + def search_grain_boundary_docs(self, *args, **kwargs): # pragma: no cover + """ + Deprecated + """ + + warnings.warn( + "MPRester.grain_boundary.search_grain_boundary_docs is deprecated. " + "Please use MPRester.grain_boundary.search instead.", + DeprecationWarning, + stacklevel=2, + ) + + return self.search(*args, **kwargs) + + def search( self, - material_ids: Optional[List[str]] = None, + chemsys: Optional[str] = None, gb_plane: Optional[List[str]] = None, gb_energy: Optional[Tuple[float, float]] = None, - separation_energy: Optional[Tuple[float, float]] = None, + material_ids: Optional[List[str]] = None, + pretty_formula: Optional[str] = None, rotation_axis: Optional[List[str]] = None, rotation_angle: Optional[Tuple[float, float]] = None, + separation_energy: Optional[Tuple[float, float]] = None, sigma: Optional[int] = None, type: Optional[GBTypeEnum] = None, - chemsys: Optional[str] = None, - pretty_formula: Optional[str] = None, sort_fields: Optional[List[str]] = None, num_chunks: Optional[int] = None, chunk_size: int = 1000, @@ -32,28 +48,29 @@ def search_grain_boundary_docs( fields: Optional[List[str]] = None, ): """ - Query grain boundary docs using a variety of search criteria. - - Arguments: - material_ids (List[str]): List of Materials Project IDs to query with. - gb_plane(List[str]): The Miller index of grain boundary plane. e.g., [1, 1, 1] - gb_energy (Tuple[float,float]): Minimum and maximum grain boundary energy in J/m³ to consider. - separation_energy (Tuple[float,float]): Minimum and maximum work of separation energy in J/m³ to consider. - rotation_angle (Tuple[float,float]): Minimum and maximum rotation angle in degrees to consider. - rotation_axis(List[str]): The Miller index of rotation axis. e.g., [1, 0, 0], [1, 1, 0], and [1, 1, 1] - sigma (int): Sigma value of grain boundary. - type (GBTypeEnum): Grain boundary type. - chemsys (str): Dash-delimited string of elements in the material. - pretty_formula (str): Formula of the material. - sort_fields (List[str]): Fields used to sort results. Prefix with '-' to sort in descending order. - 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 GrainBoundaryDoc to return data for. - Default is material_id and last_updated if all_fields is False. - - Returns: - ([GrainBoundaryDoc]) List of grain boundary documents + Query grain boundary docs using a variety of search criteria. + + Arguments: + chemsys (str): Dash-delimited string of elements in the material. + gb_plane(List[str]): The Miller index of grain boundary plane. e.g., [1, 1, 1] + gb_energy (Tuple[float,float]): Minimum and maximum grain boundary energy in J/m³ to consider. + material_ids (List[str]): List of Materials Project IDs to query with. + pretty_formula (str): Formula of the material. + rotation_angle (Tuple[float,float]): Minimum and maximum rotation angle in degrees to consider. + rotation_axis(List[str]): The Miller index of rotation axis. e.g., [1, 0, 0], [1, 1, 0], and [1, 1, 1] + sigma (int): Sigma value of grain boundary. + separation_energy (Tuple[float,float]): Minimum and maximum work of separation energy in J/m³ to consider. + sigma (int): Sigma value of the boundary. + type (GBTypeEnum): Grain boundary type. + sort_fields (List[str]): Fields used to sort results. Prefix with '-' to sort in descending order. + 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 GrainBoundaryDoc to return data for. + Default is material_id and last_updated if all_fields is False. + + Returns: + ([GrainBoundaryDoc]) List of grain boundary documents """ query_params = defaultdict(dict) # type: dict @@ -110,7 +127,7 @@ def search_grain_boundary_docs( if query_params[entry] is not None } - return super().search( + return super()._search( num_chunks=num_chunks, chunk_size=chunk_size, all_fields=all_fields, diff --git a/src/mp_api/routes/magnetism.py b/src/mp_api/routes/magnetism.py index 723990bf..966822e9 100644 --- a/src/mp_api/routes/magnetism.py +++ b/src/mp_api/routes/magnetism.py @@ -6,6 +6,8 @@ from pymatgen.analysis.magnetism import Ordering +import warnings + class MagnetismRester(BaseRester[MagnetismDoc]): @@ -13,16 +15,30 @@ class MagnetismRester(BaseRester[MagnetismDoc]): document_model = MagnetismDoc # type: ignore primary_key = "material_id" - def search_magnetism_docs( + def search_magnetism_docs(self, *args, **kwargs): # pragma: no cover + """ + Deprecated + """ + + warnings.warn( + "MPRester.magnetism.search_magnetism_docs is deprecated. " + "Please use MPRester.magnetism.search instead.", + DeprecationWarning, + stacklevel=2, + ) + + return self.search(*args, **kwargs) + + def search( self, + num_magnetic_sites: Optional[Tuple[int, int]] = None, + num_unique_magnetic_sites: Optional[Tuple[int, int]] = None, ordering: Optional[Ordering] = None, total_magnetization: Optional[Tuple[float, float]] = None, total_magnetization_normalized_vol: Optional[Tuple[float, float]] = None, total_magnetization_normalized_formula_units: Optional[ Tuple[float, float] ] = None, - num_magnetic_sites: Optional[Tuple[int, int]] = None, - num_unique_magnetic_sites: Optional[Tuple[int, int]] = None, sort_fields: Optional[List[str]] = None, num_chunks: Optional[int] = None, chunk_size: int = 1000, @@ -33,15 +49,15 @@ def search_magnetism_docs( Query magnetism docs using a variety of search criteria. Arguments: + 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. ordering (Ordering]): The magnetic ordering of the material. total_magnetization (Tuple[float,float]): Minimum and maximum total magnetization values to consider. total_magnetization_normalized_vol (Tuple[float,float]): Minimum and maximum total magnetization values normalized by volume to consider. total_magnetization_normalized_formula_units (Tuple[float,float]): Minimum and maximum total magnetization values normalized by formula units 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. sort_fields (List[str]): Fields used to sort results. Prefix with '-' to sort in descending order. 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. @@ -117,7 +133,7 @@ def search_magnetism_docs( if query_params[entry] is not None } - return super().search( + return super()._search( num_chunks=num_chunks, chunk_size=chunk_size, all_fields=all_fields, diff --git a/src/mp_api/routes/materials.py b/src/mp_api/routes/materials.py index 8c33f2fc..a4b92b36 100644 --- a/src/mp_api/routes/materials.py +++ b/src/mp_api/routes/materials.py @@ -8,6 +8,8 @@ from mp_api.core.client import BaseRester, MPRestError from mp_api.core.utils import validate_ids +import warnings + _EMMET_SETTINGS = EmmetSettings() @@ -40,20 +42,35 @@ def get_structure_by_material_id( response = self.get_data_by_id(material_id, fields=["initial_structures"]) return response.initial_structures if response is not None else response # type: ignore - def search_material_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) + + def search( self, - formula: Optional[str] = None, chemsys: Optional[Union[str, List[str]]] = None, + crystal_system: Optional[CrystalSystem] = None, + density: Optional[Tuple[float, float]] = None, + deprecated: Optional[bool] = False, elements: Optional[List[str]] = None, exclude_elements: Optional[List[str]] = None, - task_ids: Optional[List[str]] = None, - crystal_system: Optional[CrystalSystem] = None, + formula: Optional[Union[str, List[str]]] = None, + num_elements: Optional[Tuple[int, int]] = None, + num_sites: Optional[Tuple[int, int]] = None, spacegroup_number: Optional[int] = None, spacegroup_symbol: Optional[str] = None, - nsites: Optional[Tuple[int, int]] = None, + task_ids: Optional[List[str]] = None, volume: Optional[Tuple[float, float]] = None, - density: Optional[Tuple[float, float]] = None, - deprecated: Optional[bool] = False, sort_fields: Optional[List[str]] = None, num_chunks: Optional[int] = None, chunk_size: int = 1000, @@ -64,20 +81,22 @@ def search_material_docs( Query core material docs using a variety of search criteria. Arguments: - formula (str): A formula including anonomyzed formula - or wild cards (e.g., Fe2O3, ABO3, Si*). 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. - task_ids (List[str]): List of Materials Project IDs to return data for. - crystal_system (CrystalSystem): Crystal system of material. + formula (str, List[str]): A formula including anonomyzed 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. - nsites (Tuple[int,int]): Minimum and maximum number of sites to consider. + task_ids (List[str]): List of Materials Project IDs to return data for. volume (Tuple[float,float]): Minimum and maximum volume to consider. - density (Tuple[float,float]): Minimum and maximum density to consider. - deprecated (bool): Whether the material is tagged as deprecated. sort_fields (List[str]): Fields used to sort results. Prefix with '-' to sort in descending order. 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. @@ -92,7 +111,10 @@ def search_material_docs( query_params = {"deprecated": deprecated} # type: dict if formula: - query_params.update({"formula": formula}) + if isinstance(formula, str): + formula = [formula] + + query_params.update({"formula": ",".join(formula)}) if chemsys: if isinstance(chemsys, str): @@ -117,8 +139,15 @@ def search_material_docs( } ) - if nsites: - query_params.update({"nsites_min": nsites[0], "nsites_max": nsites[1]}) + if num_sites: + query_params.update( + {"nsites_min": num_sites[0], "nsites_max": num_sites[1]} + ) + + if 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]}) @@ -137,7 +166,7 @@ def search_material_docs( if query_params[entry] is not None } - return super().search( + return super()._search( num_chunks=num_chunks, chunk_size=chunk_size, all_fields=all_fields, diff --git a/src/mp_api/routes/molecules.py b/src/mp_api/routes/molecules.py index f2157ce3..97eb8cc5 100644 --- a/src/mp_api/routes/molecules.py +++ b/src/mp_api/routes/molecules.py @@ -6,6 +6,8 @@ from mp_api.core.client import BaseRester from emmet.core.molecules_jcesr import MoleculesDoc +import warnings + class MoleculesRester(BaseRester[MoleculesDoc]): @@ -13,13 +15,27 @@ class MoleculesRester(BaseRester[MoleculesDoc]): document_model = MoleculesDoc # type: ignore primary_key = "task_id" - def search_molecules_docs( + def search_molecules_docs(self, *args, **kwargs): # pragma: no cover + """ + Deprecated + """ + + warnings.warn( + "MPRester.molecules.search_molecules_docs is deprecated. " + "Please use MPRester.molecules.search instead.", + DeprecationWarning, + stacklevel=2, + ) + + return self.search(*args, **kwargs) + + def search( self, + charge: Optional[Tuple[float, float]] = None, elements: Optional[List[Element]] = None, - nelements: Optional[Tuple[float, float]] = None, EA: Optional[Tuple[float, float]] = None, IE: Optional[Tuple[float, float]] = None, - charge: Optional[Tuple[float, float]] = None, + nelements: Optional[Tuple[float, float]] = None, pointgroup: Optional[str] = None, smiles: Optional[str] = None, sort_fields: Optional[List[str]] = None, @@ -32,11 +48,12 @@ def search_molecules_docs( Query equations of state docs using a variety of search criteria. Arguments: + charge (Tuple[float,float]): Minimum and maximum value of the charge in +e to consider. + elements (List[Element]): A list of elements. film_orientation (List[Elements]): List of elements that are in the molecule. - nelements (Tuple[float,float]): Minimum and maximum number of elements in the molecule to consider. EA (Tuple[float,float]): Minimum and maximum value of the electron affinity in eV to consider. IE (Tuple[float,float]): Minimum and maximum value of the ionization energy in eV to consider. - charge (Tuple[float,float]): Minimum and maximum value of the charge in +e to consider. + nelements (Tuple[float,float]): Minimum and maximum number of elements in the molecule to consider. pointgroup (str): Point group of the molecule in Schoenflies notation. smiles (str): The simplified molecular input line-entry system (SMILES) representation of the molecule. sort_fields (List[str]): Fields used to sort results. Prefix with '-' to sort in descending order. @@ -86,7 +103,7 @@ def search_molecules_docs( if query_params[entry] is not None } - return super().search( + return super()._search( num_chunks=num_chunks, chunk_size=chunk_size, all_fields=all_fields, diff --git a/src/mp_api/routes/oxidation_states.py b/src/mp_api/routes/oxidation_states.py index 145137ea..d32be675 100644 --- a/src/mp_api/routes/oxidation_states.py +++ b/src/mp_api/routes/oxidation_states.py @@ -1,5 +1,7 @@ from mp_api.core.client import BaseRester from emmet.core.oxidation_states import OxidationStateDoc +from typing import Optional, Union, List +from collections import defaultdict class OxidationStatesRester(BaseRester[OxidationStateDoc]): @@ -7,3 +9,71 @@ class OxidationStatesRester(BaseRester[OxidationStateDoc]): suffix = "oxidation_states" document_model = OxidationStateDoc # type: ignore primary_key = "material_id" + + def search( + self, + chemsys: Optional[Union[str, List[str]]] = None, + formula: Optional[Union[str, List[str]]] = None, + possible_species: Optional[Union[str, List[str]]] = None, + sort_fields: Optional[List[str]] = None, + num_chunks: Optional[int] = None, + chunk_size: int = 1000, + all_fields: bool = True, + fields: Optional[List[str]] = None, + ): + """ + Query oxidation state docs using a variety of search criteria. + + Arguments: + chemsys (str, List[str]): A chemical system or list of chemical systems + (e.g., Li-Fe-O, Si-*, [Si-O, Li-Fe-P]). + formula (str, List[str]): A formula including anonomyzed formula + or wild cards (e.g., Fe2O3, ABO3, Si*). A list of chemical formulas can also be passed + (e.g., [Fe2O3, ABO3]). + possible_species (List[str]): A list of element symbols appended with oxidation states (e.g. [Cr2+, O2-]]). + sort_fields (List[str]): Fields used to sort results. Prefix with '-' to sort in descending order. + 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 OxidationStateDoc to return data for. + Default is material_id, last_updated, and formula_pretty if all_fields is False. + + Returns: + ([OxidationStateDoc]) List of oxidation state documents + """ + + query_params = defaultdict(dict) # type: dict + + 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 possible_species: + query_params.update({"possible_species": ",".join(possible_species)}) + + if sort_fields: + query_params.update( + {"_sort_fields": ",".join([s.strip() for s in sort_fields])} + ) + + 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 + ) diff --git a/src/mp_api/routes/phonon.py b/src/mp_api/routes/phonon.py index a052f79c..4180b845 100644 --- a/src/mp_api/routes/phonon.py +++ b/src/mp_api/routes/phonon.py @@ -7,3 +7,11 @@ class PhononRester(BaseRester[PhononBSDOSDoc]): suffix = "phonon" document_model = PhononBSDOSDoc # type: ignore primary_key = "material_id" + + def search(*args, **kwargs): # pragma: no cover + raise NotImplementedError( + """ + The PhononRester.search method does not exist as no search endpoint is present. + Use get_data_by_id instead. + """ + ) diff --git a/src/mp_api/routes/piezo.py b/src/mp_api/routes/piezo.py index e57fa6d8..baef2290 100644 --- a/src/mp_api/routes/piezo.py +++ b/src/mp_api/routes/piezo.py @@ -4,6 +4,8 @@ from mp_api.core.client import BaseRester from emmet.core.polar import PiezoelectricDoc +import warnings + class PiezoRester(BaseRester[PiezoelectricDoc]): @@ -11,7 +13,21 @@ class PiezoRester(BaseRester[PiezoelectricDoc]): document_model = PiezoelectricDoc # type: ignore primary_key = "material_id" - def search_piezoelectric_docs( + def search_piezoelectric_docs(self, *args, **kwargs): # pragma: no cover + """ + Deprecated + """ + + warnings.warn( + "MPRester.piezoelectric.search_piezoelectric_docs is deprecated. " + "Please use MPRester.piezoelectric.search instead.", + DeprecationWarning, + stacklevel=2, + ) + + return self.search(*args, **kwargs) + + def search( self, piezoelectric_modulus: Optional[Tuple[float, float]] = None, sort_fields: Optional[List[str]] = None, @@ -58,7 +74,7 @@ def search_piezoelectric_docs( if query_params[entry] is not None } - return super().search( + return super()._search( num_chunks=num_chunks, chunk_size=chunk_size, all_fields=all_fields, diff --git a/src/mp_api/routes/provenance.py b/src/mp_api/routes/provenance.py index af700435..d2ff8dc5 100644 --- a/src/mp_api/routes/provenance.py +++ b/src/mp_api/routes/provenance.py @@ -1,5 +1,6 @@ from mp_api.core.client import BaseRester from emmet.core.provenance import ProvenanceDoc +from typing import Optional, List class ProvenanceRester(BaseRester[ProvenanceDoc]): @@ -7,3 +8,36 @@ class ProvenanceRester(BaseRester[ProvenanceDoc]): suffix = "provenance" document_model = ProvenanceDoc # type: ignore primary_key = "material_id" + + def search( + self, + deprecated: Optional[bool] = False, + num_chunks: Optional[int] = None, + chunk_size: int = 1000, + all_fields: bool = True, + fields: Optional[List[str]] = None, + ): + """ + Query provenance docs using a variety of search criteria. + + Arguments: + deprecated (bool): Whether the material is tagged as deprecated. + 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 Provenance to return data for. + Default is material_id, last_updated, and formula_pretty if all_fields is False. + + Returns: + ([ProvenanceDoc]) List of provenance documents + """ + + query_params = {"deprecated": deprecated} # type: dict + + return super()._search( + num_chunks=num_chunks, + chunk_size=chunk_size, + all_fields=all_fields, + fields=fields, + **query_params + ) diff --git a/src/mp_api/routes/robocrys.py b/src/mp_api/routes/robocrys.py index e6270445..6a6f2b7d 100644 --- a/src/mp_api/routes/robocrys.py +++ b/src/mp_api/routes/robocrys.py @@ -3,6 +3,8 @@ from mp_api.core.client import BaseRester, MPRestError from emmet.core.robocrys import RobocrystallogapherDoc +import warnings + class RobocrysRester(BaseRester[RobocrystallogapherDoc]): @@ -10,7 +12,20 @@ class RobocrysRester(BaseRester[RobocrystallogapherDoc]): document_model = RobocrystallogapherDoc # type: ignore primary_key = "material_id" - def search_robocrys_text( + def search_robocrys_text(self, *args, **kwargs): # pragma: no cover + """ + Deprecated + """ + + warnings.warn( + "search_robocrys_text is deprecated. " "Please use search instead.", + DeprecationWarning, + stacklevel=2, + ) + + return self.search(*args, **kwargs) + + def search( self, keywords: List[str], num_chunks: Optional[int] = None, diff --git a/src/mp_api/routes/similarity.py b/src/mp_api/routes/similarity.py index 1d1ea28b..ab3eae85 100644 --- a/src/mp_api/routes/similarity.py +++ b/src/mp_api/routes/similarity.py @@ -7,3 +7,11 @@ class SimilarityRester(BaseRester[SimilarityDoc]): suffix = "similarity" document_model = SimilarityDoc # type: ignore primary_key = "material_id" + + def search(*args, **kwargs): # pragma: no cover + raise NotImplementedError( + """ + The SimilarityRester.search method does not exist as no search endpoint is present. + Use get_data_by_id instead. + """ + ) diff --git a/src/mp_api/routes/substrates.py b/src/mp_api/routes/substrates.py index a44b598f..74c23093 100644 --- a/src/mp_api/routes/substrates.py +++ b/src/mp_api/routes/substrates.py @@ -12,15 +12,29 @@ class SubstratesRester(BaseRester[SubstratesDoc]): document_model = SubstratesDoc # type: ignore primary_key = "film_id" - def search_substrates_docs( + def search_substrates_docs(self, *args, **kwargs): # pragma: no cover + """ + Deprecated + """ + + warnings.warn( + "MPRester.substrates.search_substrates_docs is deprecated. " + "Please use MPRester.substrates.search instead.", + DeprecationWarning, + stacklevel=2, + ) + + return self.search(*args, **kwargs) + + def search( self, + area: Optional[Tuple[float, float]] = None, + energy: Optional[Tuple[float, float]] = None, film_id: Optional[str] = None, + film_orientation: Optional[List[int]] = None, substrate_id: Optional[str] = None, substrate_formula: Optional[str] = None, - film_orientation: Optional[List[int]] = None, substrate_orientation: Optional[List[int]] = None, - area: Optional[Tuple[float, float]] = None, - energy: Optional[Tuple[float, float]] = None, sort_fields: Optional[List[str]] = None, num_chunks: Optional[int] = None, chunk_size: int = 1000, @@ -31,14 +45,14 @@ def search_substrates_docs( Query equations of state docs using a variety of search criteria. Arguments: + area (Tuple[float,float]): Minimum and maximum volume in Ų to consider for the minimim coincident + interface area range. + energy (Tuple[float,float]): Minimum and maximum energy in meV to consider for the elastic energy range. film_id (str): Materials Project ID of the film material. + film_orientation (List[int]): Vector indicating the surface orientation of the film material. substrate_id (str): Materials Project ID of the substrate material. substrate_formula (str): Reduced formula of the substrate material. - film_orientation (List[int]): Vector indicating the surface orientation of the film material. substrate_orientation (List[int]): Vector indicating the surface orientation of the substrate material. - area (Tuple[float,float]): Minimum and maximum volume in Ų to consider for the minimim coincident - interface area range. - energy (Tuple[float,float]): Minimum and maximum energy in meV to consider for the elastic energy range. sort_fields (List[str]): Fields used to sort results. Prefix with '-' to sort in descending order. 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. @@ -92,7 +106,7 @@ def search_substrates_docs( if query_params[entry] is not None } - return super().search( + return super()._search( **query_params, num_chunks=num_chunks, chunk_size=chunk_size, diff --git a/src/mp_api/routes/summary.py b/src/mp_api/routes/summary.py index 6fb11e02..3e0f58e7 100644 --- a/src/mp_api/routes/summary.py +++ b/src/mp_api/routes/summary.py @@ -8,6 +8,8 @@ from mp_api.core.utils import validate_ids from pymatgen.analysis.magnetism import Ordering +import warnings + class SummaryRester(BaseRester[SummaryDoc]): @@ -15,57 +17,74 @@ class SummaryRester(BaseRester[SummaryDoc]): document_model = SummaryDoc # type: ignore primary_key = "material_id" - def search_summary_docs( + def search_summary_docs(self, *args, **kwargs): # pragma: no cover + """ + Deprecated + """ + + warnings.warn( + "MPRester.summary.search_summary_docs is deprecated. " + "Please use MPRester.summary.search instead.", + DeprecationWarning, + stacklevel=2, + ) + + return self.search(*args, **kwargs) + + def search( self, - material_ids: Optional[List[MPID]] = None, - formula: Optional[str] = None, + band_gap: Optional[Tuple[float, float]] = None, chemsys: Optional[Union[str, List[str]]] = None, - elements: Optional[List[str]] = None, - exclude_elements: Optional[List[str]] = None, - possible_species: Optional[List[str]] = None, - nsites: Optional[Tuple[int, int]] = None, - volume: Optional[Tuple[float, float]] = None, - density: Optional[Tuple[float, float]] = None, crystal_system: Optional[CrystalSystem] = None, - spacegroup_number: Optional[int] = None, - spacegroup_symbol: Optional[str] = None, + density: Optional[Tuple[float, float]] = None, deprecated: Optional[bool] = None, - total_energy: Optional[Tuple[float, float]] = None, - formation_energy: Optional[Tuple[float, float]] = None, + e_electronic: Optional[Tuple[float, float]] = None, + e_ionic: Optional[Tuple[float, float]] = None, + e_total: Optional[Tuple[float, float]] = None, + efermi: Optional[Tuple[float, float]] = None, + elastic_anisotropy: Optional[Tuple[float, float]] = None, + elements: Optional[List[str]] = None, energy_above_hull: Optional[Tuple[float, float]] = None, equilibrium_reaction_energy: Optional[Tuple[float, float]] = None, - uncorrected_energy: Optional[Tuple[float, float]] = None, - is_stable: Optional[bool] = None, - band_gap: Optional[Tuple[float, float]] = None, - efermi: Optional[Tuple[float, float]] = None, + exclude_elements: Optional[List[str]] = None, + formation_energy: Optional[Tuple[float, float]] = None, + formula: Optional[Union[str, List[str]]] = None, + g_reuss: Optional[Tuple[float, float]] = None, + g_voigt: Optional[Tuple[float, float]] = None, + g_vrh: Optional[Tuple[float, float]] = None, + has_props: Optional[List[HasProps]] = None, + has_reconstructed: Optional[bool] = None, is_gap_direct: Optional[bool] = None, is_metal: Optional[bool] = None, - magnetic_ordering: Optional[Ordering] = None, - total_magnetization: Optional[Tuple[float, float]] = None, - total_magnetization_normalized_vol: Optional[Tuple[float, float]] = None, - total_magnetization_normalized_formula_units: Optional[Tuple[float, float]] = None, - num_magnetic_sites: Optional[Tuple[int, int]] = None, - num_unique_magnetic_sites: Optional[Tuple[int, int]] = None, - k_voigt: Optional[Tuple[float, float]] = None, + is_stable: Optional[bool] = None, k_reuss: Optional[Tuple[float, float]] = None, + k_voigt: Optional[Tuple[float, float]] = None, k_vrh: Optional[Tuple[float, float]] = None, - g_voigt: Optional[Tuple[float, float]] = None, - g_reuss: Optional[Tuple[float, float]] = None, - g_vrh: Optional[Tuple[float, float]] = None, - elastic_anisotropy: Optional[Tuple[float, float]] = None, - poisson_ratio: Optional[Tuple[float, float]] = None, - e_total: Optional[Tuple[float, float]] = None, - e_ionic: Optional[Tuple[float, float]] = None, - e_electronic: Optional[Tuple[float, float]] = None, + magnetic_ordering: Optional[Ordering] = None, + material_ids: Optional[List[MPID]] = None, n: Optional[Tuple[float, float]] = None, + num_elements: Optional[Tuple[int, int]] = None, + num_sites: Optional[Tuple[int, int]] = None, + num_magnetic_sites: Optional[Tuple[int, int]] = None, + num_unique_magnetic_sites: Optional[Tuple[int, int]] = None, piezoelectric_modulus: Optional[Tuple[float, float]] = None, - weighted_surface_energy: Optional[Tuple[float, float]] = None, - weighted_work_function: Optional[Tuple[float, float]] = None, - surface_energy_anisotropy: Optional[Tuple[float, float]] = None, + poisson_ratio: Optional[Tuple[float, float]] = None, + possible_species: Optional[List[str]] = None, shape_factor: Optional[Tuple[float, float]] = None, - has_reconstructed: Optional[bool] = None, - has_props: Optional[List[HasProps]] = None, + spacegroup_number: Optional[int] = None, + spacegroup_symbol: Optional[str] = None, + surface_energy_anisotropy: Optional[Tuple[float, float]] = None, theoretical: Optional[bool] = None, + total_energy: Optional[Tuple[float, float]] = None, + total_magnetization: Optional[Tuple[float, float]] = None, + total_magnetization_normalized_formula_units: Optional[ + Tuple[float, float] + ] = None, + total_magnetization_normalized_vol: Optional[Tuple[float, float]] = None, + uncorrected_energy: Optional[Tuple[float, float]] = None, + volume: Optional[Tuple[float, float]] = None, + weighted_surface_energy: Optional[Tuple[float, float]] = None, + weighted_work_function: Optional[Tuple[float, float]] = None, sort_fields: Optional[List[str]] = None, num_chunks: Optional[int] = None, chunk_size: int = 1000, @@ -76,71 +95,69 @@ def search_summary_docs( Query core data using a variety of search criteria. Arguments: - material_ids (List[MPID]): List of Materials Project IDs to return data for. - formula (str): A formula including anonomyzed formula - or wild cards (e.g., Fe2O3, ABO3, Si*). - chemsys (str, List[str]): A chemical system or list of chemical systems - (e.g., Li-Fe-O, Si-*, [Si-O, Li-Fe-P]). - elements (List[str]): A list of elements. - exclude_elements (List(str)): List of elements to exclude. - possible_species (List(str)): List of element symbols appended with oxidation states. - (e.g. Cr2+,O2-) + band_gap (Tuple[float,float]): Minimum and maximum band gap in eV to consider. + chemsys (str, List[str]): A chemical system, list of chemical systems + (e.g., Li-Fe-O, Si-*, [Si-O, Li-Fe-P]), or single formula (e.g., Fe2O3, Si*). crystal_system (CrystalSystem): Crystal system of material. - spacegroup_number (int): Space group number of material. - spacegroup_symbol (str): Space group symbol of the material in international short symbol notation. - nsites (Tuple[int,int]): Minimum and maximum number of sites to consider. - volume (Tuple[float,float]): Minimum and maximum volume to consider. density (Tuple[float,float]): Minimum and maximum density to consider. deprecated (bool): Whether the material is tagged as deprecated. - total_energy (Tuple[int,int]): Minimum and maximum corrected total energy in eV/atom to consider. - equilibrium_reaction_energy (Tuple[float,float]): Minimum and maximum equilibrium reaction energy - in eV/atom to consider. - formation_energy (Tuple[int,int]): Minimum and maximum formation energy in eV/atom to consider. - energy_above_hull (Tuple[int,int]): Minimum and maximum energy above the hull in eV/atom to consider. - uncorrected_energy (Tuple[int,int]): Minimum and maximum uncorrected total energy in eV/atom to consider. - band_gap (Tuple[float,float]): Minimum and maximum band gap in eV to consider. + 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 anonomyzed 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]): 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. + 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. - total_magnetization (Tuple[float,float]): Minimum and maximum total magnetization values to consider. - total_magnetization_normalized_vol (Tuple[float,float]): Minimum and maximum total magnetization values - normalized by volume to consider. - total_magnetization_normalized_formula_units (Tuple[float,float]): Minimum and maximum total magnetization - values normalized by formula units 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. - k_voigt (Tuple[float,float]): Minimum and maximum value in GPa to consider for - the Voigt average of the bulk modulus. - k_reuss (Tuple[float,float]): Minimum and maximum value in GPa to consider for - the Reuss 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. - g_voigt (Tuple[float,float]): Minimum and maximum value in GPa to consider for - the Voigt average of the shear modulus. - g_reuss (Tuple[float,float]): Minimum and maximum value in GPa to consider for - the Reuss 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. - elastic_anisotropy (Tuple[float,float]): Minimum and maximum value to consider for - the elastic anisotropy. - poisson_ratio (Tuple[float,float]): Minimum and maximum value to consider for - Poisson's ratio. - e_total (Tuple[float,float]): Minimum and maximum total dielectric constant to consider. - e_ionic (Tuple[float,float]): Minimum and maximum ionic dielectric constant to consider. - e_electronic (Tuple[float,float]): Minimum and maximum electronic dielectric constant to consider. + material_ids (List[MPID]): List of Materials Project IDs to return data for. n (Tuple[float,float]): Minimum and maximum refractive index to consider. + 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. + 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. - 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. - surface_energy_anisotropy (Tuple[float,float]): Minimum and maximum surface energy anisotropy values 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. - has_reconstructed (bool): Whether the entry has any reconstructed surfaces. - has_props: (List[HasProps]): The calculated properties available for the material. + 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. sort_fields (List[str]): Fields used to sort results. Prefixing with '-' will sort in descending order. 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. @@ -182,6 +199,8 @@ def search_summary_docs( "e_ionic": "e_ionic", "e_electronic": "e_electronic", "n": "n", + "num_sites": "nsites", + "num_elements": "nelements", "piezoelectric_modulus": "e_ij_max", "weighted_surface_energy": "weighted_surface_energy", "weighted_work_function": "weighted_work_function", @@ -205,7 +224,10 @@ def search_summary_docs( query_params.update({"deprecated": deprecated}) if formula: - query_params.update({"formula": formula}) + if isinstance(formula, str): + formula = [formula] + + query_params.update({"formula": ",".join(formula)}) if chemsys: if isinstance(chemsys, str): @@ -256,6 +278,10 @@ def search_summary_docs( 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 + return super()._search( + num_chunks=num_chunks, + chunk_size=chunk_size, + all_fields=all_fields, + fields=fields, + **query_params ) diff --git a/src/mp_api/routes/surface_properties.py b/src/mp_api/routes/surface_properties.py index 4ab70e85..82377843 100644 --- a/src/mp_api/routes/surface_properties.py +++ b/src/mp_api/routes/surface_properties.py @@ -4,6 +4,8 @@ from mp_api.core.client import BaseRester from emmet.core.surface_properties import SurfacePropDoc +import warnings + class SurfacePropertiesRester(BaseRester[SurfacePropDoc]): @@ -11,13 +13,27 @@ class SurfacePropertiesRester(BaseRester[SurfacePropDoc]): document_model = SurfacePropDoc # type: ignore primary_key = "task_id" - def search_surface_properties_docs( + def search_surface_properties_docs(self, *args, **kwargs): # pragma: no cover + """ + Deprecated + """ + + warnings.warn( + "MPRester.surface_properties.search_surface_properties_docs is deprecated. " + "Please use MPRester.surface_properties.search instead.", + DeprecationWarning, + stacklevel=2, + ) + + return self.search(*args, **kwargs) + + def search( self, + has_reconstructed: Optional[bool] = None, + shape_factor: Optional[Tuple[float, float]] = None, + surface_energy_anisotropy: Optional[Tuple[float, float]] = None, weighted_surface_energy: Optional[Tuple[float, float]] = None, weighted_work_function: Optional[Tuple[float, float]] = None, - surface_energy_anisotropy: Optional[Tuple[float, float]] = None, - shape_factor: Optional[Tuple[float, float]] = None, - has_reconstructed: Optional[bool] = None, sort_fields: Optional[List[str]] = None, num_chunks: Optional[int] = None, chunk_size: int = 1000, @@ -28,13 +44,13 @@ def search_surface_properties_docs( Query surface properties docs using a variety of search criteria. Arguments: + has_reconstructed (bool): Whether the entry has any reconstructed surfaces. + shape_factor (Tuple[float,float]): Minimum and maximum shape factor values to consider. + surface_energy_anisotropy (Tuple[float,float]): Minimum and maximum surface energy anisotropy values 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. - surface_energy_anisotropy (Tuple[float,float]): Minimum and maximum surface energy anisotropy values to - consider. - shape_factor (Tuple[float,float]): Minimum and maximum shape factor values to consider. - has_reconstructed (bool): Whether the entry has any reconstructed surfaces. sort_fields (List[str]): Fields used to sort results. Prefix with '-' to sort in descending order. 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. @@ -94,7 +110,7 @@ def search_surface_properties_docs( if query_params[entry] is not None } - return super().search( + return super()._search( num_chunks=num_chunks, chunk_size=chunk_size, all_fields=all_fields, diff --git a/src/mp_api/routes/synthesis.py b/src/mp_api/routes/synthesis.py index b28c3b64..f93d8f30 100644 --- a/src/mp_api/routes/synthesis.py +++ b/src/mp_api/routes/synthesis.py @@ -7,12 +7,27 @@ OperationTypeEnum, ) +import warnings + class SynthesisRester(BaseRester[SynthesisSearchResultModel]): suffix = "synthesis" document_model = SynthesisSearchResultModel # type: ignore - def search_synthesis_text( + def search_synthesis_text(self, *args, **kwargs): # pragma: no cover + """ + Deprecated + """ + + warnings.warn( + "search_synthesis_text is deprecated. " "Please use search instead.", + DeprecationWarning, + stacklevel=2, + ) + + return self.search(*args, **kwargs) + + def search( self, keywords: Optional[List[str]] = None, synthesis_type: Optional[List[SynthesisTypeEnum]] = None, @@ -58,6 +73,9 @@ def search_synthesis_text( 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, diff --git a/src/mp_api/routes/tasks.py b/src/mp_api/routes/tasks.py index e5a38b53..0ae1ebe3 100644 --- a/src/mp_api/routes/tasks.py +++ b/src/mp_api/routes/tasks.py @@ -3,6 +3,8 @@ from emmet.core.tasks import TaskDoc from mp_api.core.client import BaseRester, MPRestError +import warnings + class TaskRester(BaseRester[TaskDoc]): @@ -29,10 +31,24 @@ def get_trajectory(self, task_id): return traj_data - def search_task_docs( + def search_task_docs(self, *args, **kwargs): # pragma: no cover + """ + Deprecated + """ + + warnings.warn( + "MPRester.tasks.search_task_docs is deprecated. " + "Please use MPRester.tasks.search instead.", + DeprecationWarning, + stacklevel=2, + ) + + return self.search(*args, **kwargs) + + def search( self, - formula: Optional[str] = None, chemsys: Optional[Union[str, List[str]]] = None, + formula: Optional[Union[str, List[str]]] = None, num_chunks: Optional[int] = None, chunk_size: int = 1000, all_fields: bool = True, @@ -42,10 +58,11 @@ def search_task_docs( Query core task docs using a variety of search criteria. Arguments: - formula (str): A formula including anonomyzed formula - or wild cards (e.g., Fe2O3, ABO3, Si*). chemsys (str, List[str]): A chemical system or list of chemical systems (e.g., Li-Fe-O, Si-*, [Si-O, Li-Fe-P]). + formula (str, List[str]): A formula including anonomyzed formula + or wild cards (e.g., Fe2O3, ABO3, Si*). A list of chemical formulas can also be passed + (e.g., [Fe2O3, ABO3]). 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. Max size is 100. all_fields (bool): Whether to return all fields in the document. Defaults to True. @@ -67,7 +84,7 @@ def search_task_docs( query_params.update({"chemsys": ",".join(chemsys)}) - return super().search( + return super()._search( num_chunks=num_chunks, chunk_size=chunk_size, all_fields=all_fields, diff --git a/src/mp_api/routes/thermo.py b/src/mp_api/routes/thermo.py index 81077cef..993c9862 100644 --- a/src/mp_api/routes/thermo.py +++ b/src/mp_api/routes/thermo.py @@ -5,6 +5,8 @@ from emmet.core.thermo import ThermoDoc from pymatgen.analysis.phase_diagram import PhaseDiagram +import warnings + class ThermoRester(BaseRester[ThermoDoc]): @@ -13,17 +15,31 @@ class ThermoRester(BaseRester[ThermoDoc]): supports_versions = True primary_key = "material_id" - def search_thermo_docs( + def search_thermo_docs(self, *args, **kwargs): # pragma: no cover + """ + Deprecated + """ + + warnings.warn( + "MPRester.thermo.search_thermo_docs is deprecated. " + "Please use MPRester.thermo.search instead.", + DeprecationWarning, + stacklevel=2, + ) + + return self.search(*args, **kwargs) + + def search( self, - material_ids: Optional[List[str]] = None, - formula: Optional[str] = None, chemsys: Optional[Union[str, List[str]]] = None, - nelements: Optional[Tuple[int, int]] = None, - is_stable: Optional[bool] = None, - total_energy: Optional[Tuple[float, float]] = None, - formation_energy: Optional[Tuple[float, float]] = None, energy_above_hull: Optional[Tuple[float, float]] = None, equilibrium_reaction_energy: Optional[Tuple[float, float]] = None, + formation_energy: Optional[Tuple[float, float]] = None, + formula: Optional[Union[str, List[str]]] = None, + is_stable: Optional[bool] = None, + material_ids: Optional[List[str]] = None, + num_elements: Optional[Tuple[int, int]] = None, + total_energy: Optional[Tuple[float, float]] = None, uncorrected_energy: Optional[Tuple[float, float]] = None, sort_fields: Optional[List[str]] = None, num_chunks: Optional[int] = None, @@ -35,18 +51,19 @@ def search_thermo_docs( Query core material docs using a variety of search criteria. Arguments: - material_ids (List[str]): List of Materials Project IDs to return data for. - formula (str): A formula including anonomyzed formula - or wild cards (e.g., Fe2O3, ABO3, Si*). chemsys (str, List[str]): A chemical system or list of chemical systems (e.g., Li-Fe-O, Si-*, [Si-O, Li-Fe-P]). - nelements (Tuple[int,int]): Minimum and maximum number of elements in the material to consider. - is_stable (bool): Whether the material is stable. - total_energy (Tuple[float,float]): Minimum and maximum corrected total energy in eV/atom to consider. - formation_energy (Tuple[float,float]): Minimum and maximum formation energy in eV/atom to consider. energy_above_hull (Tuple[float,float]): 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. + formation_energy (Tuple[float,float]): Minimum and maximum formation energy in eV/atom to consider. + formula (str, List[str]): A formula including anonomyzed formula + or wild cards (e.g., Fe2O3, ABO3, Si*). A list of chemical formulas can also be passed + (e.g., [Fe2O3, ABO3]). + is_stable (bool): Whether the material is stable. + material_ids (List[str]): List of Materials Project IDs to return data for. + num_elements (Tuple[int,int]): Minimum and maximum number of elements in the material to consider. + total_energy (Tuple[float,float]): Minimum and maximum corrected total energy in eV/atom to consider. uncorrected_energy (Tuple[float,float]): Minimum and maximum uncorrected total energy in eV/atom to consider. sort_fields (List[str]): Fields used to sort results. Prefix with '-' to sort in descending order. @@ -63,7 +80,10 @@ def search_thermo_docs( query_params = defaultdict(dict) # type: dict if formula: - query_params.update({"formula": formula}) + if isinstance(formula, str): + formula = [formula] + + query_params.update({"formula": ",".join(formula)}) if chemsys: if isinstance(chemsys, str): @@ -74,9 +94,9 @@ def search_thermo_docs( if material_ids: query_params.update({"material_ids": ",".join(validate_ids(material_ids))}) - if nelements: + if num_elements: query_params.update( - {"nelements_min": nelements[0], "nelements_max": nelements[1]} + {"nelements_min": num_elements[0], "nelements_max": num_elements[1]} ) if is_stable is not None: @@ -110,7 +130,7 @@ def search_thermo_docs( if query_params[entry] is not None } - return super().search( + return super()._search( num_chunks=num_chunks, chunk_size=chunk_size, all_fields=all_fields, diff --git a/src/mp_api/routes/xas.py b/src/mp_api/routes/xas.py index 19c78668..32db020f 100644 --- a/src/mp_api/routes/xas.py +++ b/src/mp_api/routes/xas.py @@ -5,6 +5,8 @@ from mp_api.core.utils import validate_ids from pymatgen.core.periodic_table import Element +import warnings + class XASRester(BaseRester[XASDoc]): @@ -12,7 +14,21 @@ class XASRester(BaseRester[XASDoc]): document_model = XASDoc # type: ignore primary_key = "spectrum_id" - def search_xas_docs( + def search_xas_docs(self, *args, **kwargs): # pragma: no cover + """ + Deprecated + """ + + warnings.warn( + "MPRester.xas.search_xas_docs is deprecated. " + "Please use MPRester.xas.search instead.", + DeprecationWarning, + stacklevel=2, + ) + + return self.search(*args, **kwargs) + + def search( self, edge: Optional[Edge] = None, absorbing_element: Optional[Element] = None, @@ -75,7 +91,7 @@ def search_xas_docs( {"_sort_fields": ",".join([s.strip() for s in sort_fields])} ) - return super().search( + return super()._search( num_chunks=num_chunks, chunk_size=chunk_size, all_fields=all_fields, diff --git a/tests/test_bonds.py b/tests/test_bonds.py index 746b886a..aa11782b 100644 --- a/tests/test_bonds.py +++ b/tests/test_bonds.py @@ -2,10 +2,15 @@ import pytest from mp_api.routes.bonds import BondsRester -import inspect import typing -resters = [BondsRester()] + +@pytest.fixture +def rester(): + rester = BondsRester() + yield rester + rester.session.close() + excluded_params = [ "sort_fields", @@ -32,13 +37,9 @@ @pytest.mark.skipif( os.environ.get("MP_API_KEY", None) is None, reason="No API key found." ) -@pytest.mark.parametrize("rester", resters) def test_client(rester): - # Get specific search method - search_method = None - for entry in inspect.getmembers(rester, predicate=inspect.ismethod): - if "search" in entry[0] and entry[0] != "search": - search_method = entry[1] + + search_method = rester.search if search_method is not None: # Get list of parameters @@ -47,7 +48,7 @@ def test_client(rester): # Query API for each numeric and boolean parameter and check if returned for entry in param_tuples: param = entry[0] - print(param) + if param not in excluded_params: param_type = entry[1].__args__[0] q = None @@ -79,8 +80,7 @@ def test_client(rester): "chunk_size": 1, "num_chunks": 1, } - print(q) - print(param_type == typing.Tuple[float, float]) + doc = search_method(**q)[0].dict() for sub_field in sub_doc_fields: diff --git a/tests/test_charge_density.py b/tests/test_charge_density.py index a0de9ee8..7ea126d5 100644 --- a/tests/test_charge_density.py +++ b/tests/test_charge_density.py @@ -3,10 +3,15 @@ from emmet.core.charge_density import ChgcarDataDoc from mp_api.routes.charge_density import ChargeDensityRester -import inspect import typing -resters = [ChargeDensityRester()] + +@pytest.fixture +def rester(): + rester = ChargeDensityRester() + yield rester + rester.session.close() + excluded_params = [ "sort_fields", @@ -14,6 +19,7 @@ "num_chunks", "all_fields", "fields", + "return", ] sub_doc_fields = [] # type: list @@ -26,13 +32,8 @@ @pytest.mark.skipif( os.environ.get("MP_API_KEY", None) is None, reason="No API key found." ) -@pytest.mark.parametrize("rester", resters) def test_client(rester): - # Get specific search method - search_method = None - for entry in inspect.getmembers(rester, predicate=inspect.ismethod): - if "search" in entry[0] and entry[0] != "search": - search_method = entry[1] + search_method = rester.search if search_method is not None: # Get list of parameters @@ -84,8 +85,7 @@ def test_client(rester): ) -def test_download_for_task_ids(tmpdir): - rester = resters[0] +def test_download_for_task_ids(tmpdir, rester): n = rester.download_for_task_ids( task_ids=["mp-655585", "mp-1057373", "mp-1059589", "mp-1440634", "mp-1791788"], @@ -96,8 +96,7 @@ def test_download_for_task_ids(tmpdir): assert "mp-1791788.json.gz" in files -def test_extract_s3_url_info(): - rester = resters[0] +def test_extract_s3_url_info(rester): url_doc_dict = { "task_id": "mp-1896591", diff --git a/tests/test_core_client.py b/tests/test_core_client.py index ef236115..af87229c 100644 --- a/tests/test_core_client.py +++ b/tests/test_core_client.py @@ -30,7 +30,7 @@ def test_post_fail(rester): os.environ.get("MP_API_KEY", None) is None, reason="No API key found." ) def test_pagination(mpr): - mpids = mpr.materials.search_material_docs( + mpids = mpr.materials.search( all_fields=False, fields=["material_id"], num_chunks=2, chunk_size=1000 ) assert len(mpids) > 1000 diff --git a/tests/test_dielectric.py b/tests/test_dielectric.py index 2b60a3c8..bdf77f91 100644 --- a/tests/test_dielectric.py +++ b/tests/test_dielectric.py @@ -2,10 +2,13 @@ import pytest from mp_api.routes.dielectric import DielectricRester -import inspect import typing -resters = [DielectricRester()] +@pytest.fixture +def rester(): + rester = DielectricRester() + yield rester + rester.session.close() excluded_params = [ "sort_fields", @@ -25,13 +28,8 @@ @pytest.mark.skipif( os.environ.get("MP_API_KEY", None) is None, reason="No API key found." ) -@pytest.mark.parametrize("rester", resters) def test_client(rester): - # Get specific search method - search_method = None - for entry in inspect.getmembers(rester, predicate=inspect.ismethod): - if "search" in entry[0] and entry[0] != "search": - search_method = entry[1] + search_method = rester.search if search_method is not None: # Get list of parameters diff --git a/tests/test_elasticity.py b/tests/test_elasticity.py index 1863183d..77044263 100644 --- a/tests/test_elasticity.py +++ b/tests/test_elasticity.py @@ -2,10 +2,13 @@ import pytest from mp_api.routes.elasticity import ElasticityRester -import inspect import typing -resters = [ElasticityRester()] +@pytest.fixture +def rester(): + rester = ElasticityRester() + yield rester + rester.session.close() excluded_params = [ "sort_fields", @@ -28,13 +31,8 @@ @pytest.mark.skipif( os.environ.get("MP_API_KEY", None) is None, reason="No API key found." ) -@pytest.mark.parametrize("rester", resters) def test_client(rester): - # Get specific search method - search_method = None - for entry in inspect.getmembers(rester, predicate=inspect.ismethod): - if "search" in entry[0] and entry[0] != "search": - search_method = entry[1] + search_method = rester.search if search_method is not None: # Get list of parameters diff --git a/tests/test_electrodes.py b/tests/test_electrodes.py index 7a508d5e..90eb15bf 100644 --- a/tests/test_electrodes.py +++ b/tests/test_electrodes.py @@ -2,11 +2,16 @@ import pytest from mp_api.routes.electrodes import ElectrodeRester -import inspect import typing from pymatgen.core.periodic_table import Element -resters = [ElectrodeRester()] + +@pytest.fixture +def rester(): + rester = ElectrodeRester() + yield rester + rester.session.close() + excluded_params = [ "sort_fields", @@ -21,6 +26,8 @@ alt_name_dict = { "formula": "battery_id", "exclude_elements": "battery_id", + "num_elements": "nelements", + "num_sites": "nsites", } # type: dict custom_field_tests = { @@ -35,9 +42,8 @@ @pytest.mark.skipif( os.environ.get("MP_API_KEY", None) is None, reason="No API key found." ) -@pytest.mark.parametrize("rester", resters) def test_client(rester): - search_method = rester.search_electrode_docs + search_method = rester.search if search_method is not None: # Get list of parameters diff --git a/tests/test_electronic_structure.py b/tests/test_electronic_structure.py index 66f5e725..34782972 100644 --- a/tests/test_electronic_structure.py +++ b/tests/test_electronic_structure.py @@ -32,6 +32,8 @@ def es_rester(): es_alt_name_dict = { "exclude_elements": "material_id", "formula": "material_id", + "num_elements": "nelements", + "num_sites": "nsites", } # type: dict es_custom_field_tests = { @@ -43,15 +45,9 @@ def es_rester(): } # type: dict -@pytest.mark.skipif( - os.environ.get("MP_API_KEY", None) is None, reason="No API key found." -) +@pytest.mark.skipif(os.environ.get("MP_API_KEY", None) is None, reason="No API key found.") def test_es_client(es_rester): - # Get specific search method - search_method = None - for entry in inspect.getmembers(es_rester, predicate=inspect.ismethod): - if "search" in entry[0] and entry[0] != "search": - search_method = entry[1] + search_method = es_rester.search if search_method is not None: # Get list of parameters @@ -96,10 +92,7 @@ def test_es_client(es_rester): doc = search_method(**q)[0].dict() - assert ( - doc[project_field if project_field is not None else param] - is not None - ) + assert doc[project_field if project_field is not None else param] is not None bs_custom_field_tests = { @@ -122,15 +115,10 @@ def bs_rester(): rester.session.close() -@pytest.mark.skipif( - os.environ.get("MP_API_KEY", None) is None, reason="No API key found." -) +@pytest.mark.skipif(os.environ.get("MP_API_KEY", None) is None, reason="No API key found.") def test_bs_client(bs_rester): # Get specific search method - search_method = None - for entry in inspect.getmembers(bs_rester, predicate=inspect.ismethod): - if "search" in entry[0] and entry[0] != "search": - search_method = entry[1] + search_method = bs_rester.search # Query fields for param in bs_custom_field_tests: @@ -141,8 +129,7 @@ def test_bs_client(bs_rester): "num_chunks": 1, } doc = search_method(**q)[0].dict() - print(q) - print(doc) + for sub_field in bs_sub_doc_fields: if sub_field in doc: doc = doc[sub_field] @@ -150,9 +137,6 @@ def test_bs_client(bs_rester): if param != "path_type": doc = doc["setyawan_curtarolo"] - print("=====") - print(doc) - assert doc[project_field if project_field is not None else param] is not None @@ -176,15 +160,9 @@ def dos_rester(): rester.session.close() -@pytest.mark.skipif( - os.environ.get("MP_API_KEY", None) is None, reason="No API key found." -) +@pytest.mark.skipif(os.environ.get("MP_API_KEY", None) is None, reason="No API key found.") def test_dos_client(dos_rester): - # Get specific search method - search_method = None - for entry in inspect.getmembers(dos_rester, predicate=inspect.ismethod): - if "search" in entry[0] and entry[0] != "search": - search_method = entry[1] + search_method = dos_rester.search # Query fields for param in dos_custom_field_tests: @@ -203,7 +181,5 @@ def test_dos_client(dos_rester): if param != "projection_type" and param != "magnetic_ordering": doc = doc["total"]["1"] - assert ( - doc[project_field if project_field is not None else param] is not None - ) + assert doc[project_field if project_field is not None else param] is not None diff --git a/tests/test_eos.py b/tests/test_eos.py index 2b0fb3b4..86dec2f1 100644 --- a/tests/test_eos.py +++ b/tests/test_eos.py @@ -2,10 +2,13 @@ import pytest from mp_api.routes.eos import EOSRester -import inspect import typing -resters = [EOSRester()] +@pytest.fixture +def rester(): + rester = EOSRester() + yield rester + rester.session.close() excluded_params = [ "sort_fields", @@ -25,13 +28,8 @@ @pytest.mark.skipif( os.environ.get("MP_API_KEY", None) is None, reason="No API key found." ) -@pytest.mark.parametrize("rester", resters) def test_client(rester): - # Get specific search method - search_method = None - for entry in inspect.getmembers(rester, predicate=inspect.ismethod): - if "search" in entry[0] and entry[0] != "search": - search_method = entry[1] + search_method = rester.search if search_method is not None: # Get list of parameters diff --git a/tests/test_grain_boundary.py b/tests/test_grain_boundary.py index d0e13820..79cd255a 100644 --- a/tests/test_grain_boundary.py +++ b/tests/test_grain_boundary.py @@ -3,10 +3,13 @@ from mp_api.routes.grain_boundary import GrainBoundaryRester from emmet.core.grain_boundary import GBTypeEnum -import inspect import typing -resters = [GrainBoundaryRester()] +@pytest.fixture +def rester(): + rester = GrainBoundaryRester() + yield rester + rester.session.close() excluded_params = [ "sort_fields", @@ -34,13 +37,8 @@ @pytest.mark.skipif( os.environ.get("MP_API_KEY", None) is None, reason="No API key found." ) -@pytest.mark.parametrize("rester", resters) def test_client(rester): - # Get specific search method - search_method = None - for entry in inspect.getmembers(rester, predicate=inspect.ismethod): - if "search" in entry[0] and entry[0] != "search": - search_method = entry[1] + search_method = rester.search if search_method is not None: # Get list of parameters diff --git a/tests/test_magnetism.py b/tests/test_magnetism.py index a4cdb0a9..2f99e752 100644 --- a/tests/test_magnetism.py +++ b/tests/test_magnetism.py @@ -3,10 +3,13 @@ from mp_api.routes.magnetism import MagnetismRester from pymatgen.analysis.magnetism import Ordering -import inspect import typing -resters = [MagnetismRester()] +@pytest.fixture +def rester(): + rester = MagnetismRester() + yield rester + rester.session.close() excluded_params = [ "sort_fields", @@ -26,13 +29,8 @@ @pytest.mark.skipif( os.environ.get("MP_API_KEY", None) is None, reason="No API key found." ) -@pytest.mark.parametrize("rester", resters) def test_client(rester): - # Get specific search method - search_method = None - for entry in inspect.getmembers(rester, predicate=inspect.ismethod): - if "search" in entry[0] and entry[0] != "search": - search_method = entry[1] + search_method = rester.search if search_method is not None: # Get list of parameters diff --git a/tests/test_materials.py b/tests/test_materials.py index 7bd9d4d1..66f5a0db 100644 --- a/tests/test_materials.py +++ b/tests/test_materials.py @@ -3,10 +3,15 @@ from mp_api.routes.materials import MaterialsRester from emmet.core.symmetry import CrystalSystem -import inspect import typing -resters = [MaterialsRester()] + +@pytest.fixture +def rester(): + rester = MaterialsRester() + yield rester + rester.session.close() + excluded_params = [ "sort_fields", @@ -24,6 +29,8 @@ "spacegroup_number": "symmetry", "spacegroup_symbol": "symmetry", "exclude_elements": "material_id", + "num_elements": "nelements", + "num_sites": "nsites", } # type: dict custom_field_tests = { @@ -38,16 +45,9 @@ } # type: dict -@pytest.mark.skipif( - os.environ.get("MP_API_KEY", None) is None, reason="No API key found." -) -@pytest.mark.parametrize("rester", resters) +@pytest.mark.skipif(os.environ.get("MP_API_KEY", None) is None, reason="No API key found.") def test_client(rester): - # Get specific search method - search_method = None - for entry in inspect.getmembers(rester, predicate=inspect.ismethod): - if "search" in entry[0] and entry[0] != "search": - search_method = entry[1] + search_method = rester.search if search_method is not None: # Get list of parameters @@ -94,7 +94,4 @@ def test_client(rester): if sub_field in doc: doc = doc[sub_field] - assert ( - doc[project_field if project_field is not None else param] - is not None - ) + assert doc[project_field if project_field is not None else param] is not None diff --git a/tests/test_molecules.py b/tests/test_molecules.py index fa0dea90..669e5626 100644 --- a/tests/test_molecules.py +++ b/tests/test_molecules.py @@ -3,10 +3,14 @@ from mp_api.routes.molecules import MoleculesRester from pymatgen.core.periodic_table import Element -import inspect import typing -resters = [MoleculesRester()] + +@pytest.fixture +def rester(): + rester = MoleculesRester() + yield rester + rester.session.close() excluded_params = [ "sort_fields", @@ -31,13 +35,8 @@ @pytest.mark.skipif( os.environ.get("MP_API_KEY", None) is None, reason="No API key found." ) -@pytest.mark.parametrize("rester", resters) def test_client(rester): - # Get specific search method - search_method = None - for entry in inspect.getmembers(rester, predicate=inspect.ismethod): - if "search" in entry[0] and entry[0] != "search": - search_method = entry[1] + search_method = rester.search if search_method is not None: # Get list of parameters diff --git a/tests/test_mprester.py b/tests/test_mprester.py index aed28f14..eb508f4c 100644 --- a/tests/test_mprester.py +++ b/tests/test_mprester.py @@ -37,9 +37,7 @@ def mpr(): rester.session.close() -@pytest.mark.skipif( - os.environ.get("MP_API_KEY", None) is None, reason="No API key found." -) +@pytest.mark.skipif(os.environ.get("MP_API_KEY", None) is None, reason="No API key found.") class TestMPRester: def test_get_structure_by_material_id(self, mpr): s1 = mpr.get_structure_by_material_id("mp-149") @@ -68,7 +66,7 @@ def test_get_task_ids_associated_with_material_id(self, mpr): ) assert len(results) > 0 - def test_get_materials_id_references(self, mpr): + def test_get_materials_ids_references(self, mpr): data = mpr.get_materials_id_references("mp-123") assert len(data) > 5 @@ -251,111 +249,10 @@ def test_get_charge_density_data(self, mpr): chgcar = mpr.get_charge_density_from_material_id("mp-149") assert isinstance(chgcar, Chgcar) - chgcar, task_doc = mpr.get_charge_density_from_material_id( - "mp-149", inc_task_doc=True - ) + chgcar, task_doc = mpr.get_charge_density_from_material_id("mp-149", inc_task_doc=True) assert isinstance(chgcar, Chgcar) assert isinstance(task_doc, TaskDoc) def test_get_wulff_shape(self, mpr): ws = mpr.get_wulff_shape("mp-126") assert isinstance(ws, WulffShape) - - def test_query(self, mpr): - - excluded_params = [ - "sort_fields", - "chunk_size", - "num_chunks", - "all_fields", - "fields", - "equilibrium_reaction_energy", # temp until data update - ] - - alt_name_dict = { - "material_ids": "material_id", - "formula": "formula_pretty", - "exclude_elements": "formula_pretty", - "piezoelectric_modulus": "e_ij_max", - "crystal_system": "symmetry", - "spacegroup_symbol": "symmetry", - "spacegroup_number": "symmetry", - "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", - "magnetic_ordering": "ordering", - "elastic_anisotropy": "universal_anisotropy", - "poisson_ratio": "homogeneous_poisson", - "piezoelectric_modulus": "e_ij_max", - "surface_energy_anisotropy": "surface_anisotropy", - } # type: dict - - custom_field_tests = { - "material_ids": ["mp-149"], - "formula": "SiO2", - "chemsys": "Si-O", - "elements": ["Si", "O"], - "exclude_elements": ["Si"], - "possible_species": ["O2-"], - "crystal_system": CrystalSystem.cubic, - "spacegroup_number": 38, - "spacegroup_symbol": "Amm2", - "has_props": [HasProps.dielectric], - "theoretical": True, - "has_reconstructed": False, - "magnetic_ordering": Ordering.FM, - } # type: dict - - search_method = mpr.query - - # Get list of parameters - param_tuples = list(typing.get_type_hints(search_method).items()) - - # Query API for each numeric and boolean parameter and check if returned - for entry in param_tuples: - param = entry[0] - if param not in excluded_params: - param_type = entry[1].__args__[0] - q = None - - if param in custom_field_tests: - project_field = alt_name_dict.get(param, None) - q = { - param: custom_field_tests[param], - "chunk_size": 1, - "num_chunks": 1, - } - elif param_type == typing.Tuple[int, int]: - project_field = alt_name_dict.get(param, None) - q = { - param: (-100, 100), - "chunk_size": 1, - "num_chunks": 1, - } - elif param_type == typing.Tuple[float, float]: - project_field = alt_name_dict.get(param, None) - q = { - param: (-100.12, 100.12), - "chunk_size": 1, - "num_chunks": 1, - } - elif param_type is bool: - project_field = alt_name_dict.get(param, None) - q = { - param: False, - "chunk_size": 1, - "num_chunks": 1, - } - - docs = search_method(**q) - - if len(docs) > 0: - doc = docs[0].dict() - else: - raise ValueError("No documents returned") - - assert ( - doc[project_field if project_field is not None else param] - is not None - ) diff --git a/tests/test_oxidation_states.py b/tests/test_oxidation_states.py new file mode 100644 index 00000000..9f6b5764 --- /dev/null +++ b/tests/test_oxidation_states.py @@ -0,0 +1,92 @@ +import os +import pytest +from mp_api.routes.oxidation_states import OxidationStatesRester +from pymatgen.analysis.magnetism import Ordering + +import typing + + +@pytest.fixture +def rester(): + rester = OxidationStatesRester() + yield rester + rester.session.close() + + +excluded_params = [ + "sort_fields", + "chunk_size", + "num_chunks", + "all_fields", + "fields", +] + +sub_doc_fields = [] # type: list + +alt_name_dict = {"formula": "material_id"} # type: dict + +custom_field_tests = { + "formula": "Si", + "chemsys": "Si-O", + "possible_species": ["Cr2+", "O2-"], +} # type: dict + + +@pytest.mark.skipif( + os.environ.get("MP_API_KEY", None) is None, reason="No API key found." +) +def test_client(rester): + search_method = rester.search + + if search_method is not None: + # Get list of parameters + param_tuples = list(typing.get_type_hints(search_method).items()) + + # Query API for each numeric and boolean parameter and check if returned + for entry in param_tuples: + param = entry[0] + if param not in excluded_params: + + param_type = entry[1].__args__[0] + q = None + + if param_type == typing.Tuple[int, int]: + project_field = alt_name_dict.get(param, None) + q = { + param: (-100, 100), + "chunk_size": 1, + "num_chunks": 1, + } + elif param_type == typing.Tuple[float, float]: + project_field = alt_name_dict.get(param, None) + q = { + param: (-100.12, 100.12), + "chunk_size": 1, + "num_chunks": 1, + } + elif param_type is bool: + project_field = alt_name_dict.get(param, None) + q = { + param: False, + "chunk_size": 1, + "num_chunks": 1, + } + elif param in custom_field_tests: + project_field = alt_name_dict.get(param, None) + q = { + param: custom_field_tests[param], + "chunk_size": 1, + "num_chunks": 1, + } + + print(q) + + doc = search_method(**q)[0].dict() + for sub_field in sub_doc_fields: + if sub_field in doc: + doc = doc[sub_field] + + assert ( + doc[project_field if project_field is not None else param] + is not None + ) diff --git a/tests/test_piezo.py b/tests/test_piezo.py index b4bde91b..3e98abbf 100644 --- a/tests/test_piezo.py +++ b/tests/test_piezo.py @@ -2,10 +2,15 @@ import pytest from mp_api.routes.piezo import PiezoRester -import inspect import typing -resters = [PiezoRester()] + +@pytest.fixture +def rester(): + rester = PiezoRester() + yield rester + rester.session.close() + excluded_params = [ "sort_fields", @@ -25,13 +30,8 @@ @pytest.mark.skipif( os.environ.get("MP_API_KEY", None) is None, reason="No API key found." ) -@pytest.mark.parametrize("rester", resters) def test_client(rester): - # Get specific search method - search_method = None - for entry in inspect.getmembers(rester, predicate=inspect.ismethod): - if "search" in entry[0] and entry[0] != "search": - search_method = entry[1] + search_method = rester.search if search_method is not None: # Get list of parameters diff --git a/tests/test_provenance.py b/tests/test_provenance.py new file mode 100644 index 00000000..0e156311 --- /dev/null +++ b/tests/test_provenance.py @@ -0,0 +1,84 @@ +import os +import pytest +from mp_api.routes.provenance import ProvenanceRester + +import typing + + +@pytest.fixture +def rester(): + rester = ProvenanceRester() + yield rester + rester.session.close() + + +excluded_params = [ + "sort_fields", + "chunk_size", + "num_chunks", + "all_fields", + "fields", +] + +sub_doc_fields = [] # type: list + +alt_name_dict = {} # type: dict + +custom_field_tests = {} # type: dict + + +@pytest.mark.skipif( + os.environ.get("MP_API_KEY", None) is None, reason="No API key found." +) +def test_client(rester): + search_method = rester.search + + if search_method is not None: + # Get list of parameters + param_tuples = list(typing.get_type_hints(search_method).items()) + + # Query API for each numeric and boolean parameter and check if returned + for entry in param_tuples: + param = entry[0] + if param not in excluded_params: + param_type = entry[1].__args__[0] + q = None + + if param_type == typing.Tuple[int, int]: + project_field = alt_name_dict.get(param, None) + q = { + param: (-100, 100), + "chunk_size": 1, + "num_chunks": 1, + } + elif param_type == typing.Tuple[float, float]: + project_field = alt_name_dict.get(param, None) + q = { + param: (-100.12, 100.12), + "chunk_size": 1, + "num_chunks": 1, + } + elif param_type is bool: + project_field = alt_name_dict.get(param, None) + q = { + param: False, + "chunk_size": 1, + "num_chunks": 1, + } + elif param in custom_field_tests: + project_field = alt_name_dict.get(param, None) + q = { + param: custom_field_tests[param], + "chunk_size": 1, + "num_chunks": 1, + } + + doc = search_method(**q)[0].dict() + for sub_field in sub_doc_fields: + if sub_field in doc: + doc = doc[sub_field] + + assert ( + doc[project_field if project_field is not None else param] + is not None + ) diff --git a/tests/test_robocrys.py b/tests/test_robocrys.py index 8331de0a..4f0b52fd 100644 --- a/tests/test_robocrys.py +++ b/tests/test_robocrys.py @@ -2,8 +2,6 @@ import pytest from mp_api.routes.robocrys import RobocrysRester -import inspect -import typing from pymatgen.core.periodic_table import Element @@ -18,18 +16,13 @@ def rester(): os.environ.get("MP_API_KEY", None) is None, reason="No API key found." ) def test_client(rester): - # Get specific search method - search_method = None - for entry in inspect.getmembers(rester, predicate=inspect.ismethod): - if "search" in entry[0] and entry[0] != "search": - search_method = entry[1] + search_method = rester.search if search_method is not None: q = {"keywords": ["silicon"], "num_chunks": 1} doc = search_method(**q)[0] - print(doc) assert doc.description is not None assert doc.condensed_structure is not None diff --git a/tests/test_substrates.py b/tests/test_substrates.py index 34d55010..470bf333 100644 --- a/tests/test_substrates.py +++ b/tests/test_substrates.py @@ -2,10 +2,15 @@ import pytest from mp_api.routes.substrates import SubstratesRester -import inspect import typing -resters = [SubstratesRester()] + +@pytest.fixture +def rester(): + rester = SubstratesRester() + yield rester + rester.session.close() + excluded_params = [ "sort_fields", @@ -36,13 +41,8 @@ @pytest.mark.skipif( os.environ.get("MP_API_KEY", None) is None, reason="No API key found." ) -@pytest.mark.parametrize("rester", resters) def test_client(rester): - # Get specific search method - search_method = None - for entry in inspect.getmembers(rester, predicate=inspect.ismethod): - if "search" in entry[0] and entry[0] != "search": - search_method = entry[1] + search_method = rester.search if search_method is not None: # Get list of parameters @@ -88,7 +88,7 @@ def test_client(rester): for sub_field in sub_doc_fields: if sub_field in doc: doc = doc[sub_field] - print(doc) + assert ( doc[project_field if project_field is not None else param] is not None diff --git a/tests/test_summary.py b/tests/test_summary.py new file mode 100644 index 00000000..5b28234e --- /dev/null +++ b/tests/test_summary.py @@ -0,0 +1,102 @@ +from emmet.core.symmetry import CrystalSystem +from emmet.core.summary import HasProps +from pymatgen.analysis.magnetism import Ordering +from mp_api.routes.summary import SummaryRester + +import typing + +excluded_params = [ + "sort_fields", + "chunk_size", + "num_chunks", + "all_fields", + "fields", + "equilibrium_reaction_energy", # temp until data update +] + +alt_name_dict = { + "material_ids": "material_id", + "formula": "formula_pretty", + "exclude_elements": "formula_pretty", + "piezoelectric_modulus": "e_ij_max", + "crystal_system": "symmetry", + "spacegroup_symbol": "symmetry", + "spacegroup_number": "symmetry", + "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", + "magnetic_ordering": "ordering", + "elastic_anisotropy": "universal_anisotropy", + "poisson_ratio": "homogeneous_poisson", + "num_sites": "nsites", + "num_elements": "nelements", + "piezoelectric_modulus": "e_ij_max", + "surface_energy_anisotropy": "surface_anisotropy", +} # type: dict + +custom_field_tests = { + "material_ids": ["mp-149"], + "formula": "SiO2", + "chemsys": "Si-O", + "elements": ["Si", "O"], + "exclude_elements": ["Si"], + "possible_species": ["O2-"], + "crystal_system": CrystalSystem.cubic, + "spacegroup_number": 38, + "spacegroup_symbol": "Amm2", + "has_props": [HasProps.dielectric], + "theoretical": True, + "has_reconstructed": False, + "magnetic_ordering": Ordering.FM, +} # type: dict + +search_method = SummaryRester().search + +# Get list of parameters +param_tuples = list(typing.get_type_hints(search_method).items()) + +# Query API for each numeric and boolean parameter and check if returned +for entry in param_tuples: + param = entry[0] + if param not in excluded_params: + param_type = entry[1].__args__[0] + q = None + + if param in custom_field_tests: + project_field = alt_name_dict.get(param, None) + q = { + param: custom_field_tests[param], + "chunk_size": 1, + "num_chunks": 1, + } + elif param_type == typing.Tuple[int, int]: + project_field = alt_name_dict.get(param, None) + q = { + param: (-100, 100), + "chunk_size": 1, + "num_chunks": 1, + } + elif param_type == typing.Tuple[float, float]: + project_field = alt_name_dict.get(param, None) + q = { + param: (-100.12, 100.12), + "chunk_size": 1, + "num_chunks": 1, + } + elif param_type is bool: + project_field = alt_name_dict.get(param, None) + q = { + param: False, + "chunk_size": 1, + "num_chunks": 1, + } + + docs = search_method(**q) + + if len(docs) > 0: + doc = docs[0].dict() + else: + raise ValueError("No documents returned") + + assert doc[project_field if project_field is not None else param] is not None diff --git a/tests/test_surface_properties.py b/tests/test_surface_properties.py index 5c7d4468..2d4d6e4b 100644 --- a/tests/test_surface_properties.py +++ b/tests/test_surface_properties.py @@ -2,10 +2,15 @@ import pytest from mp_api.routes.surface_properties import SurfacePropertiesRester -import inspect import typing -resters = [SurfacePropertiesRester()] + +@pytest.fixture +def rester(): + rester = SurfacePropertiesRester() + yield rester + rester.session.close() + excluded_params = [ "sort_fields", @@ -25,13 +30,8 @@ @pytest.mark.skipif( os.environ.get("MP_API_KEY", None) is None, reason="No API key found." ) -@pytest.mark.parametrize("rester", resters) def test_client(rester): - # Get specific search method - search_method = None - for entry in inspect.getmembers(rester, predicate=inspect.ismethod): - if "search" in entry[0] and entry[0] != "search": - search_method = entry[1] + search_method = rester.search if search_method is not None: # Get list of parameters diff --git a/tests/test_synthesis.py b/tests/test_synthesis.py index f38e72c4..43d7d38b 100644 --- a/tests/test_synthesis.py +++ b/tests/test_synthesis.py @@ -19,11 +19,7 @@ def rester(): os.environ.get("MP_API_KEY", None) is None, reason="No API key found." ) def test_client(rester): - # Get specific search method - search_method = None - for entry in inspect.getmembers(rester, predicate=inspect.ismethod): - if "search" in entry[0] and entry[0] != "search": - search_method = entry[1] + search_method = rester.search if search_method is not None: q = {"keywords": ["silicon"]} @@ -39,10 +35,7 @@ def test_client(rester): os.environ.get("MP_API_KEY", None) is None, reason="No API key found." ) def test_filters_keywords(rester): - search_method = None - for entry in inspect.getmembers(rester, predicate=inspect.ismethod): - if "search" in entry[0] and entry[0] != "search": - search_method = entry[1] + search_method = rester.search if search_method is not None: doc = search_method(keywords=["silicon"])[0] @@ -56,10 +49,7 @@ def test_filters_keywords(rester): os.environ.get("MP_API_KEY", None) is None, reason="No API key found." ) def test_filters_synthesis_type(rester): - search_method = None - for entry in inspect.getmembers(rester, predicate=inspect.ismethod): - if "search" in entry[0] and entry[0] != "search": - search_method = entry[1] + search_method = rester.search if search_method is not None: doc = search_method( @@ -76,10 +66,7 @@ def test_filters_synthesis_type(rester): ) @pytest.mark.xfail # Needs fixing def test_filters_temperature_range(rester): - search_method = None - for entry in inspect.getmembers(rester, predicate=inspect.ismethod): - if "search" in entry[0] and entry[0] != "search": - search_method = entry[1] + search_method = rester.search if search_method is not None: docs: List[SynthesisRecipe] = search_method( @@ -99,10 +86,7 @@ def test_filters_temperature_range(rester): ) @pytest.mark.xfail # Needs fixing def test_filters_time_range(rester): - search_method = None - for entry in inspect.getmembers(rester, predicate=inspect.ismethod): - if "search" in entry[0] and entry[0] != "search": - search_method = entry[1] + search_method = rester.search if search_method is not None: docs: List[SynthesisRecipe] = search_method( @@ -119,10 +103,7 @@ def test_filters_time_range(rester): os.environ.get("MP_API_KEY", None) is None, reason="No API key found." ) def test_filters_atmosphere(rester): - search_method = None - for entry in inspect.getmembers(rester, predicate=inspect.ismethod): - if "search" in entry[0] and entry[0] != "search": - search_method = entry[1] + search_method = rester.search if search_method is not None: docs: List[SynthesisRecipe] = search_method( @@ -141,10 +122,7 @@ def test_filters_atmosphere(rester): os.environ.get("MP_API_KEY", None) is None, reason="No API key found." ) def test_filters_mixing_device(rester): - search_method = None - for entry in inspect.getmembers(rester, predicate=inspect.ismethod): - if "search" in entry[0] and entry[0] != "search": - search_method = entry[1] + search_method = rester.search if search_method is not None: docs: List[SynthesisRecipe] = search_method( @@ -162,10 +140,7 @@ def test_filters_mixing_device(rester): os.environ.get("MP_API_KEY", None) is None, reason="No API key found." ) def test_filters_mixing_media(rester): - search_method = None - for entry in inspect.getmembers(rester, predicate=inspect.ismethod): - if "search" in entry[0] and entry[0] != "search": - search_method = entry[1] + search_method = rester.search if search_method is not None: docs: List[SynthesisRecipe] = search_method( diff --git a/tests/test_tasks.py b/tests/test_tasks.py index 01f45d87..6c43d923 100644 --- a/tests/test_tasks.py +++ b/tests/test_tasks.py @@ -3,10 +3,15 @@ from pymatgen.core.trajectory import Trajectory from mp_api.routes.tasks import TaskRester -import inspect import typing -resters = [TaskRester()] + +@pytest.fixture +def rester(): + rester = TaskRester() + yield rester + rester.session.close() + excluded_params = [ "sort_fields", @@ -23,16 +28,9 @@ custom_field_tests = {"formula": "Si", "chemsys": "Si-O"} # type: dict -@pytest.mark.skipif( - os.environ.get("MP_API_KEY", None) is None, reason="No API key found." -) -@pytest.mark.parametrize("rester", resters) +@pytest.mark.skipif(os.environ.get("MP_API_KEY", None) is None, reason="No API key found.") def test_client(rester): - # Get specific search method - search_method = None - for entry in inspect.getmembers(rester, predicate=inspect.ismethod): - if "search" in entry[0] and entry[0] != "search": - search_method = entry[1] + search_method = rester.search if search_method is not None: # Get list of parameters @@ -51,9 +49,7 @@ def test_client(rester): param: (-100, 100), "chunk_size": 1, "num_chunks": 1, - "fields": [ - project_field if project_field is not None else param - ], + "fields": [project_field if project_field is not None else param], } elif param_type == typing.Tuple[float, float]: project_field = alt_name_dict.get(param, None) @@ -61,9 +57,7 @@ def test_client(rester): param: (-100.12, 100.12), "chunk_size": 1, "num_chunks": 1, - "fields": [ - project_field if project_field is not None else param - ], + "fields": [project_field if project_field is not None else param], } elif param_type is bool: project_field = alt_name_dict.get(param, None) @@ -71,9 +65,7 @@ def test_client(rester): param: False, "chunk_size": 1, "num_chunks": 1, - "fields": [ - project_field if project_field is not None else param - ], + "fields": [project_field if project_field is not None else param], } elif param in custom_field_tests: project_field = alt_name_dict.get(param, None) @@ -81,9 +73,7 @@ def test_client(rester): param: custom_field_tests[param], "chunk_size": 1, "num_chunks": 1, - "fields": [ - project_field if project_field is not None else param - ], + "fields": [project_field if project_field is not None else param], } doc = search_method(**q)[0].dict() @@ -91,15 +81,12 @@ def test_client(rester): if sub_field in doc: doc = doc[sub_field] - assert ( - doc[project_field if project_field is not None else param] - is not None - ) + assert doc[project_field if project_field is not None else param] is not None -def test_get_trajectories(): +def test_get_trajectories(rester): - trajectories = resters[0].get_trajectory("mp-149") + trajectories = rester.get_trajectory("mp-149") for traj in trajectories: assert isinstance(traj, Trajectory) diff --git a/tests/test_thermo.py b/tests/test_thermo.py index 796630ba..aa830913 100644 --- a/tests/test_thermo.py +++ b/tests/test_thermo.py @@ -3,10 +3,15 @@ import pytest from mp_api.routes.thermo import ThermoRester -import inspect import typing -resters = [ThermoRester()] + +@pytest.fixture +def rester(): + rester = ThermoRester() + yield rester + rester.session.close() + excluded_params = [ "sort_fields", @@ -26,6 +31,8 @@ "formation_energy": "formation_energy_per_atom", "uncorrected_energy": "uncorrected_energy_per_atom", "equilibirum_reaction_energy": "equilibirum_reaction_energy_per_atom", + "num_elements": "nelements", + "num_sites": "nsites", } # type: dict custom_field_tests = { @@ -35,16 +42,9 @@ } # type: dict -@pytest.mark.skipif( - os.environ.get("MP_API_KEY", None) is None, reason="No API key found." -) -@pytest.mark.parametrize("rester", resters) +@pytest.mark.skipif(os.environ.get("MP_API_KEY", None) is None, reason="No API key found.") def test_client(rester): - # Get specific search method - search_method = None - for entry in inspect.getmembers(rester, predicate=inspect.ismethod): - if "search" in entry[0] and entry[0] != "search": - search_method = entry[1] + search_method = rester.search if search_method is not None: # Get list of parameters @@ -63,9 +63,7 @@ def test_client(rester): param: (-100, 100), "chunk_size": 1, "num_chunks": 1, - "fields": [ - project_field if project_field is not None else param - ], + "fields": [project_field if project_field is not None else param], } elif param_type == typing.Tuple[float, float]: project_field = alt_name_dict.get(param, None) @@ -73,9 +71,7 @@ def test_client(rester): param: (-100.12, 100.12), "chunk_size": 1, "num_chunks": 1, - "fields": [ - project_field if project_field is not None else param - ], + "fields": [project_field if project_field is not None else param], } elif param_type is bool: project_field = alt_name_dict.get(param, None) @@ -83,9 +79,7 @@ def test_client(rester): param: False, "chunk_size": 1, "num_chunks": 1, - "fields": [ - project_field if project_field is not None else param - ], + "fields": [project_field if project_field is not None else param], } elif param in custom_field_tests: project_field = alt_name_dict.get(param, None) @@ -93,9 +87,7 @@ def test_client(rester): param: custom_field_tests[param], "chunk_size": 1, "num_chunks": 1, - "fields": [ - project_field if project_field is not None else param - ], + "fields": [project_field if project_field is not None else param], } doc = search_method(**q)[0].dict() @@ -103,16 +95,11 @@ def test_client(rester): if sub_field in doc: doc = doc[sub_field] - assert ( - doc[project_field if project_field is not None else param] - is not None - ) + assert doc[project_field if project_field is not None else param] is not None @pytest.mark.xfail(reason="Monty decode issue with phase diagram") def test_get_phase_diagram_from_chemsys(): # Test that a phase diagram is returned - assert isinstance( - ThermoRester().get_phase_diagram_from_chemsys("Hf-Pm"), PhaseDiagram - ) + assert isinstance(ThermoRester().get_phase_diagram_from_chemsys("Hf-Pm"), PhaseDiagram) diff --git a/tests/test_xas.py b/tests/test_xas.py index 4ecdb965..a6ee7c5a 100644 --- a/tests/test_xas.py +++ b/tests/test_xas.py @@ -4,10 +4,15 @@ from emmet.core.xas import Edge from pymatgen.core.periodic_table import Element -import inspect import typing -resters = [XASRester()] + +@pytest.fixture +def rester(): + rester = XASRester() + yield rester + rester.session.close() + excluded_params = [ "sort_fields", @@ -39,13 +44,8 @@ @pytest.mark.skipif( os.environ.get("MP_API_KEY", None) is None, reason="No API key found." ) -@pytest.mark.parametrize("rester", resters) def test_client(rester): - # Get specific search method - search_method = None - for entry in inspect.getmembers(rester, predicate=inspect.ismethod): - if "search" in entry[0] and entry[0] != "search": - search_method = entry[1] + search_method = rester.search if search_method is not None: # Get list of parameters