diff --git a/app.py b/app.py index 48bdb686..93b285d0 100644 --- a/app.py +++ b/app.py @@ -27,7 +27,6 @@ ) magnetism_store_json = os.environ.get("MAGNETISM_STORE", "magnetism_store.json") phonon_bs_store_json = os.environ.get("PHONON_BS_STORE", "phonon_bs_store.json") -phonon_img_store_json = os.environ.get("PHONON_IMG_STORE", "phonon_img_store.json") eos_store_json = os.environ.get("EOS_STORE", "eos_store.json") similarity_store_json = os.environ.get("SIMILARITY_STORE", "similarity_store.json") xas_store_json = os.environ.get("XAS_STORE", "xas_store.json") @@ -118,15 +117,8 @@ phonon_bs_store = MongoURIStore( uri=f"mongodb+srv://{db_uri}", database="mp_core", - key="task_id", - collection_name="phonon_bs", - ) - - phonon_img_store = MongoURIStore( - uri=f"mongodb+srv://{db_uri}", - database="mp_core", - key="task_id", - collection_name="phonon_img", + key="material_id", + collection_name="pmg_ph_bs", ) eos_store = MongoURIStore( @@ -317,7 +309,6 @@ dielectric_piezo_store = loadfn(dielectric_piezo_store_json) magnetism_store = loadfn(magnetism_store_json) phonon_bs_store = loadfn(phonon_bs_store_json) - phonon_img_store = loadfn(phonon_img_store_json) eos_store = loadfn(eos_store_json) similarity_store = loadfn(similarity_store_json) xas_store = loadfn(xas_store_json) @@ -405,16 +396,9 @@ resources.update({"piezoelectric": [piezo_resource(dielectric_piezo_store)]}) # Phonon -from mp_api.routes.phonon.resources import phonon_bs_resource, phonon_img_resource +from mp_api.routes.phonon.resources import phonon_bsdos_resource -resources.update( - { - "phonon": [ - phonon_img_resource(phonon_img_store), - phonon_bs_resource(phonon_bs_store), - ] - } -) +resources.update({"phonon": [phonon_bsdos_resource(phonon_bs_store),]}) # EOS from mp_api.routes.eos.resources import eos_resource diff --git a/src/mp_api/matproj.py b/src/mp_api/matproj.py index 353cf6c4..fc99a1fb 100644 --- a/src/mp_api/matproj.py +++ b/src/mp_api/matproj.py @@ -1,4 +1,4 @@ -from os import environ +from os import environ, path import warnings from typing import Optional, Tuple, List from enum import Enum, unique @@ -11,6 +11,7 @@ from emmet.core.symmetry import CrystalSystem from mp_api.core.client import BaseRester +from mp_api.routes.electronic_structure.models.core import BSPathType from mp_api.routes import * _DEPRECATION_WARNING = ( @@ -106,27 +107,18 @@ def __init__( self, cls.suffix.replace("/", "_"), rester, ) - self.find_structure = self.materials.find_structure - - self.get_bandstructure_by_material_id = ( - self.electronic_structure_bandstructure.get_bandstructure_from_material_id - ) - self.get_dos_by_material_id = ( - self.electronic_structure_dos.get_dos_from_material_id - ) - self.get_charge_density_from_calculation_id = ( self.charge_density.get_charge_density_from_calculation_id ) - self.get_charge_density_calculation_details = ( - self.charge_density.get_charge_density_calculation_details - ) - self.get_charge_density_calculation_ids_from_material_id = ( self.charge_density.get_charge_density_calculation_ids_from_material_id ) + self.get_charge_density_calculation_details = ( + self.charge_density.get_charge_density_calculation_details + ) + def __enter__(self): """ Support for "with" context. @@ -287,6 +279,32 @@ def get_structures(self, chemsys_formula, final=True): return structures + def find_structure( + self, filename_or_structure, ltol=0.2, stol=0.3, angle_tol=5, limit=1 + ): + """ + Finds matching structures on the Materials Project site. + Args: + filename_or_structure: filename or Structure object + ltol: fractional length tolerance + stol: site tolerance + angle_tol: angle tolerance in degrees + limit: amount of structure matches to limit to + Returns: + A list of matching materials project ids for structure, \ + including normalized rms distances and max distances between paired sites. + Raises: + MPRestError + """ + + return self.materials.find_structure( + filename_or_structure, + ltol=ltol, + stol=stol, + angle_tol=angle_tol, + limit=limit, + ) + def get_entries( self, chemsys_formula, sort_by_e_above_hull=False, ): @@ -343,6 +361,41 @@ def get_entry_by_material_id(self, material_id): ).entries.values() ) + def get_bandstructure_by_material_id( + self, + material_id: str, + path_type: BSPathType = BSPathType.setyawan_curtarolo, + line_mode=True, + ): + """ + Get the band structure pymatgen object associated with a Materials Project ID. + + Arguments: + materials_id (str): Materials Project ID for a material + path_type (BSPathType): k-point path selection convention + line_mode (bool): Whether to return data for a line-mode calculation + + Returns: + bandstructure (Union[BandStructure, BandStructureSymmLine]): BandStructure or BandStructureSymmLine object + """ + return self.electronic_structure_bandstructure.get_bandstructure_from_material_id( # type: ignore + material_id=material_id, path_type=path_type, line_mode=line_mode + ) + + def get_dos_by_material_id(self, material_id: str): + """ + Get the complete density of states pymatgen object associated with a Materials Project ID. + + Arguments: + materials_id (str): Materials Project ID for a material + + Returns: + dos (CompleteDos): CompleteDos object + """ + return self.electronic_structure_dos.get_dos_from_material_id( # type: ignore + material_id=material_id + ) + def get_phonon_dos_by_material_id(self, material_id): """ Get phonon density of states data corresponding to a material_id. @@ -354,7 +407,7 @@ def get_phonon_dos_by_material_id(self, material_id): CompletePhononDos: A phonon DOS object. """ - raise NotImplementedError + return self.phonon.get_document_by_id(material_id, fields=["ph_dos"]).ph_dos def get_phonon_bandstructure_by_material_id(self, material_id): """ @@ -366,9 +419,20 @@ def get_phonon_bandstructure_by_material_id(self, material_id): Returns: PhononBandStructureSymmLine: phonon band structure. """ - doc = self.phonon.get_document_by_id(material_id) + return self.phonon.get_document_by_id(material_id, fields=["ph_bs"]).ph_bs - return doc.ph_bs + def get_charge_density_by_material_id(self, material_id: str): + """ + Get charge density data for a given Materials Project ID. + + Arguments: + material_id (str): Material Project ID + + Returns: + chgcar (dict): Pymatgen CHGCAR object. + """ + + return self.charge_density.get_charge_density_from_material_id(material_id) # type: ignore def query( self, @@ -559,39 +623,6 @@ def submit_structures(self, structures, public_name, public_email): # TODO: call new MPComplete endpoint raise NotImplementedError - def get_stability(self, entries): - """ - Returns the stability of all entries. - """ - # TODO: discuss - raise NotImplementedError - - def get_cohesive_energy(self, material_id, per_atom=False): - """ - Gets the cohesive for a material (eV per formula unit). Cohesive energy - is defined as the difference between the bulk energy and the sum of - total DFT energy of isolated atoms for atom elements in the bulk. - Args: - material_id (str): Materials Project material_id, e.g. 'mp-123'. - per_atom (bool): Whether or not to return cohesive energy per atom - Returns: - Cohesive energy (eV). - """ - raise NotImplementedError - - def get_reaction(self, reactants, products): - """ - Gets a reaction from the Materials Project. - - Args: - reactants ([str]): List of formulas - products ([str]): List of formulas - - Returns: - rxn - """ - raise NotImplementedError - def get_substrates(self, material_id, orient=None): """ Get a substrate list for a material id. The list is in order of @@ -720,39 +751,3 @@ def get_gb_data( rotation_axis=rotation_axis, ) ] - - def get_interface_reactions( - self, - reactant1, - reactant2, - open_el=None, - relative_mu=None, - use_hull_energy=False, - ): - """ - Gets critical reactions between two reactants. - - Get critical reactions ("kinks" in the mixing ratio where - reaction products change) between two reactants. See the - `pymatgen.analysis.interface_reactions` module for more info. - - Args: - reactant1 (str): Chemical formula for reactant - reactant2 (str): Chemical formula for reactant - open_el (str): Element in reservoir available to system - relative_mu (float): Relative chemical potential of element in - reservoir with respect to pure substance. Must be non-positive. - use_hull_energy (bool): Whether to use the convex hull energy for a - given composition for the reaction energy calculation. If false, - the energy of the ground state structure will be preferred; if a - ground state can not be found for a composition, the convex hull - energy will be used with a warning message. - - Returns: - list: list of dicts of form {ratio,energy,rxn} where `ratio` is the - reactant mixing ratio, `energy` is the reaction energy - in eV/atom, and `rxn` is a - `pymatgen.analysis.reaction_calculator.Reaction`. - - """ - raise NotImplementedError diff --git a/src/mp_api/routes/__init__.py b/src/mp_api/routes/__init__.py index 6c8e4ae4..e0086a01 100644 --- a/src/mp_api/routes/__init__.py +++ b/src/mp_api/routes/__init__.py @@ -8,7 +8,7 @@ from mp_api.routes.substrates.client import SubstratesRester from mp_api.routes.surface_properties.client import SurfacePropertiesRester from mp_api.routes.wulff.client import WulffRester -from mp_api.routes.phonon.client import PhononRester, PhononImgRester +from mp_api.routes.phonon.client import PhononRester from mp_api.routes.elasticity.client import ElasticityRester from mp_api.routes.thermo.client import ThermoRester from mp_api.routes.dielectric.client import DielectricRester diff --git a/src/mp_api/routes/charge_density/client.py b/src/mp_api/routes/charge_density/client.py index 7a7e72cd..c22c32fc 100644 --- a/src/mp_api/routes/charge_density/client.py +++ b/src/mp_api/routes/charge_density/client.py @@ -77,7 +77,33 @@ def get_charge_density_calculation_ids_from_material_id(self, material_id: str): if len(calculation_ids) > 0: result = self.search( - task_ids=",".join(calculation_ids), fields=["task_id"], chunk_size=10 + task_ids=",".join(calculation_ids), + fields=["task_id", "last_updated"], + chunk_size=10, ) return result + + def get_charge_density_from_material_id(self, material_id: str): + """ + Get charge density data for a given Materials Project ID. + + Arguments: + material_id (str): Material Project ID + + Returns: + chgcar (dict): Pymatgen CHGCAR object. + """ + + task_id = sorted( + self.get_charge_density_calculation_ids_from_material_id(material_id), + key=lambda d: d["last_updated"], + reverse=True, + )[0]["task_id"] + + result = self.search(task_ids=task_id, fields=["task_id", "data"], chunk_size=1) + + if len(result) > 0: + return result[0]["data"] + else: + raise MPRestError("No charge density found") diff --git a/src/mp_api/routes/dielectric/resources.py b/src/mp_api/routes/dielectric/resources.py index 6ad11b9f..167ec8b1 100644 --- a/src/mp_api/routes/dielectric/resources.py +++ b/src/mp_api/routes/dielectric/resources.py @@ -13,9 +13,12 @@ def dielectric_resource(dielectric_store): DielectricQuery(), SortQuery(), PaginationQuery(), - SparseFieldsQuery(DielectricDoc, default_fields=["task_id", "last_updated"]), + SparseFieldsQuery( + DielectricDoc, default_fields=["task_id", "last_updated"] + ), ], tags=["Dielectric"], + monty_encoded_response=True, ) return resource diff --git a/src/mp_api/routes/dois/resources.py b/src/mp_api/routes/dois/resources.py index 5baf3704..c3a6548f 100644 --- a/src/mp_api/routes/dois/resources.py +++ b/src/mp_api/routes/dois/resources.py @@ -14,6 +14,7 @@ def dois_resource(dois_store): ], tags=["DOIs"], enable_default_search=False, + monty_encoded_response=True, ) return resource diff --git a/src/mp_api/routes/elasticity/resources.py b/src/mp_api/routes/elasticity/resources.py index 8e4ffd4b..06540dda 100644 --- a/src/mp_api/routes/elasticity/resources.py +++ b/src/mp_api/routes/elasticity/resources.py @@ -21,9 +21,12 @@ def elasticity_resource(elasticity_store): PoissonQuery(), SortQuery(), PaginationQuery(), - SparseFieldsQuery(ElasticityDoc, default_fields=["task_id", "pretty_formula"],), + SparseFieldsQuery( + ElasticityDoc, default_fields=["task_id", "pretty_formula"], + ), ], tags=["Elasticity"], + monty_encoded_response=True, ) return resource diff --git a/src/mp_api/routes/electronic_structure/resources.py b/src/mp_api/routes/electronic_structure/resources.py index 62a07278..6c09ed2e 100644 --- a/src/mp_api/routes/electronic_structure/resources.py +++ b/src/mp_api/routes/electronic_structure/resources.py @@ -33,6 +33,7 @@ def es_resource(es_store): ), ], tags=["Electronic Structure"], + monty_encoded_response=True, ) return resource @@ -54,6 +55,7 @@ def bs_resource(es_store): tags=["Electronic Structure"], enable_get_by_key=False, sub_path="/bandstructure/", + monty_encoded_response=True, ) return resource @@ -92,6 +94,7 @@ def dos_resource(es_store): tags=["Electronic Structure"], enable_get_by_key=False, sub_path="/dos/", + monty_encoded_response=True, ) return resource diff --git a/src/mp_api/routes/eos/resources.py b/src/mp_api/routes/eos/resources.py index c1dfc937..5da9e52b 100644 --- a/src/mp_api/routes/eos/resources.py +++ b/src/mp_api/routes/eos/resources.py @@ -16,6 +16,7 @@ def eos_resource(eos_store): SparseFieldsQuery(EOSDoc, default_fields=["task_id"]), ], tags=["EOS"], + monty_encoded_response=True, ) return resource diff --git a/src/mp_api/routes/fermi/resources.py b/src/mp_api/routes/fermi/resources.py index 76300ee8..c1737463 100644 --- a/src/mp_api/routes/fermi/resources.py +++ b/src/mp_api/routes/fermi/resources.py @@ -13,6 +13,7 @@ def fermi_resource(fermi_store): SparseFieldsQuery(FermiDoc, default_fields=["task_id", "last_updated"]), ], tags=["Electronic Structure"], + monty_encoded_response=True, ) return resource diff --git a/src/mp_api/routes/grain_boundary/resources.py b/src/mp_api/routes/grain_boundary/resources.py index a4a08f04..be51bf63 100644 --- a/src/mp_api/routes/grain_boundary/resources.py +++ b/src/mp_api/routes/grain_boundary/resources.py @@ -31,6 +31,7 @@ def gb_resource(gb_store): ], tags=["Grain Boundaries"], enable_get_by_key=False, + monty_encoded_response=True, ) return resource diff --git a/src/mp_api/routes/magnetism/resources.py b/src/mp_api/routes/magnetism/resources.py index cdcbe209..b08980ea 100644 --- a/src/mp_api/routes/magnetism/resources.py +++ b/src/mp_api/routes/magnetism/resources.py @@ -16,6 +16,7 @@ def magnetism_resource(magnetism_store): SparseFieldsQuery(MagnetismDoc, default_fields=["task_id", "last_updated"]), ], tags=["Magnetism"], + monty_encoded_response=True, ) return resource diff --git a/src/mp_api/routes/materials/resources.py b/src/mp_api/routes/materials/resources.py index c0dd35fd..85815305 100644 --- a/src/mp_api/routes/materials/resources.py +++ b/src/mp_api/routes/materials/resources.py @@ -73,6 +73,7 @@ def materials_resource(materials_store): ), ], tags=["Materials"], + monty_encoded_response=True, ) return resource diff --git a/src/mp_api/routes/molecules/resources.py b/src/mp_api/routes/molecules/resources.py index bcd02b86..4963a333 100644 --- a/src/mp_api/routes/molecules/resources.py +++ b/src/mp_api/routes/molecules/resources.py @@ -24,6 +24,7 @@ def molecules_resource(molecules_store): SparseFieldsQuery(MoleculesDoc, default_fields=["task_id"]), ], tags=["Molecules"], + monty_encoded_response=True, ) return resource diff --git a/src/mp_api/routes/oxidation_states/resources.py b/src/mp_api/routes/oxidation_states/resources.py index df9b7afd..e337154c 100644 --- a/src/mp_api/routes/oxidation_states/resources.py +++ b/src/mp_api/routes/oxidation_states/resources.py @@ -26,6 +26,7 @@ def oxi_states_resource(oxi_states_store): ), ], tags=["Oxidation States"], + monty_encoded_response=True, ) return resource diff --git a/src/mp_api/routes/phonon/client.py b/src/mp_api/routes/phonon/client.py index 6c272e56..18ff4581 100644 --- a/src/mp_api/routes/phonon/client.py +++ b/src/mp_api/routes/phonon/client.py @@ -1,16 +1,9 @@ from mp_api.core.client import BaseRester -from mp_api.routes.phonon.models import PhononBSDoc, PhononImgDoc +from mp_api.routes.phonon.models import PhononBSDOSDoc class PhononRester(BaseRester): suffix = "phonon" - document_model = PhononBSDoc # type: ignore - primary_key = "task_id" - - -class PhononImgRester(BaseRester): - - suffix = "phonon_img" - document_model = PhononImgDoc # type: ignore - primary_key = "task_id" + document_model = PhononBSDOSDoc # type: ignore + primary_key = "material_id" diff --git a/src/mp_api/routes/phonon/client.pyi b/src/mp_api/routes/phonon/client.pyi index 4db7bc17..29667661 100644 --- a/src/mp_api/routes/phonon/client.pyi +++ b/src/mp_api/routes/phonon/client.pyi @@ -1,5 +1,5 @@ from typing import List, Optional -from mp_api.routes.phonon.models import PhononBSDoc, PhononImgDoc +from mp_api.routes.phonon.models import PhononBSDOSDoc class PhononRester: @@ -9,14 +9,4 @@ class PhononRester: document_id: str, fields: Optional[List[str]] = None, monty_decode: bool = True, - ) -> PhononBSDoc: ... - - -class PhononImgRester: - - def get_document_by_id( - self, - document_id: str, - fields: Optional[List[str]] = None, - monty_decode: bool = True, - ) -> PhononImgDoc: ... + ) -> PhononBSDOSDoc: ... diff --git a/src/mp_api/routes/phonon/models.py b/src/mp_api/routes/phonon/models.py index d96b8c2a..3af655b0 100644 --- a/src/mp_api/routes/phonon/models.py +++ b/src/mp_api/routes/phonon/models.py @@ -3,54 +3,17 @@ from monty.json import MontyDecoder from pydantic import BaseModel, Field, validator -from pymatgen.core import Structure +from emmet.core.mpid import MPID from pymatgen.phonon.bandstructure import PhononBandStructureSymmLine +from pymatgen.phonon.dos import PhononDos -class PhononBS(BaseModel): +class PhononBSDOSDoc(BaseModel): """ - Model for a phonon band structure object + Phonon band structures and density of states data. """ - eigendisplacements: dict = Field( - None, description="Phonon eigendisplacements in cartesian coordinates", - ) - - has_nac: bool = Field( - None, description="Whether non-analytical corrections at Gamma are included", - ) - - bands: List[List[float]] = Field( - None, description="Phonon band eigenvalues in eV", - ) - - qpoints: List[List[float]] = Field( - None, - description="List of q-points in fractional coordinates of the reciprocal lattice", - ) - - labels_dict: dict = Field( - None, description="q-point labels dictionary", - ) - - lattice_rec: dict = Field( - None, description="Reciprocal lattice of the structure", - ) - - structure: Structure = Field( - None, description="Structure of the material", - ) - - class Config: - extra = "allow" - - -class PhononBSDoc(BaseModel): - """ - Phonon band structures. - """ - - task_id: str = Field( + material_id: MPID = Field( None, description="The Materials Project ID of the material. This comes in the form: mp-******", ) @@ -59,41 +22,16 @@ class PhononBSDoc(BaseModel): None, description="Phonon band structure object", ) - last_updated: datetime = Field( - None, - description="Timestamp for the most recent calculation for this Material document", - ) - - # Make sure that the datetime field is properly formatted - @validator("last_updated", pre=True) - def last_updated_dict_ok(cls, v): - return MontyDecoder().process_decoded(v) - - -class PhononImgDoc(BaseModel): - """ - Model for a document containing phonon image data. - """ - - plot: bytes = Field( - None, description="Plot image data.", - ) - - task_id: str = Field( - None, - description="The Materials Project ID of the material. This comes in the form: mp-******", + ph_dos: PhononDos = Field( + None, description="Phonon density of states object", ) last_updated: datetime = Field( - None, description="Timestamp for the most recent calculation for this document", + None, + description="Timestamp for the most recent calculation for this Material document", ) # Make sure that the datetime field is properly formatted @validator("last_updated", pre=True) def last_updated_dict_ok(cls, v): return MontyDecoder().process_decoded(v) - - # Make sure that the plot field is properly formatted - @validator("plot", pre=True) - def plot_bytes_ok(cls, v): - return str(v) diff --git a/src/mp_api/routes/phonon/resources.py b/src/mp_api/routes/phonon/resources.py index de710916..0ed24154 100644 --- a/src/mp_api/routes/phonon/resources.py +++ b/src/mp_api/routes/phonon/resources.py @@ -1,35 +1,23 @@ from maggma.api.resource import ReadOnlyResource -from mp_api.routes.phonon.models import PhononBSDoc, PhononImgDoc +from mp_api.routes.phonon.models import PhononBSDOSDoc from mp_api.routes.phonon.query_operators import PhononImgQuery from maggma.api.query_operator import PaginationQuery, SparseFieldsQuery -def phonon_bs_resource(phonon_bs_store): +def phonon_bsdos_resource(phonon_bs_store): resource = ReadOnlyResource( phonon_bs_store, - PhononBSDoc, + PhononBSDOSDoc, query_operators=[ PaginationQuery(), - SparseFieldsQuery(PhononBSDoc, default_fields=["task_id", "last_updated"]), + SparseFieldsQuery( + PhononBSDOSDoc, default_fields=["task_id", "last_updated"] + ), ], tags=["Phonon"], enable_default_search=False, - ) - - return resource - - -def phonon_img_resource(phonon_img_store): - - resource = ReadOnlyResource( - phonon_img_store, - PhononImgDoc, - tags=["Phonon"], - enable_default_search=False, - enable_get_by_key=True, - key_fields=["plot", "task_id", "last_updated"], - sub_path="/image/", + monty_encoded_response=True, ) return resource diff --git a/src/mp_api/routes/piezo/resources.py b/src/mp_api/routes/piezo/resources.py index b9e59bfd..0fa063cc 100644 --- a/src/mp_api/routes/piezo/resources.py +++ b/src/mp_api/routes/piezo/resources.py @@ -16,6 +16,7 @@ def piezo_resource(piezo_store): SparseFieldsQuery(PiezoDoc, default_fields=["task_id", "last_updated"]), ], tags=["Piezoelectric"], + monty_encoded_response=True, ) return resource diff --git a/src/mp_api/routes/search/resources.py b/src/mp_api/routes/search/resources.py index 1a0418fa..1e475883 100644 --- a/src/mp_api/routes/search/resources.py +++ b/src/mp_api/routes/search/resources.py @@ -46,6 +46,7 @@ def search_resource(search_store): SparseFieldsQuery(SearchDoc, default_fields=["material_id"]), ], tags=["Search"], + monty_encoded_response=True, ) return resource diff --git a/src/mp_api/routes/similarity/resources.py b/src/mp_api/routes/similarity/resources.py index 59150130..851be5e0 100644 --- a/src/mp_api/routes/similarity/resources.py +++ b/src/mp_api/routes/similarity/resources.py @@ -13,6 +13,7 @@ def similarity_resource(similarity_store): ], tags=["Similarity"], enable_default_search=False, + monty_encoded_response=True, ) return resource diff --git a/src/mp_api/routes/substrates/resources.py b/src/mp_api/routes/substrates/resources.py index 83433288..83a15923 100644 --- a/src/mp_api/routes/substrates/resources.py +++ b/src/mp_api/routes/substrates/resources.py @@ -27,6 +27,7 @@ def substrates_resource(substrates_store): ], tags=["Substrates"], enable_get_by_key=False, + monty_encoded_response=True, ) return resource diff --git a/src/mp_api/routes/surface_properties/resources.py b/src/mp_api/routes/surface_properties/resources.py index 2f43c32a..c2611705 100644 --- a/src/mp_api/routes/surface_properties/resources.py +++ b/src/mp_api/routes/surface_properties/resources.py @@ -18,6 +18,7 @@ def surface_props_resource(surface_prop_store): SparseFieldsQuery(SurfacePropDoc, default_fields=["task_id"]), ], tags=["Surface Properties"], + monty_encoded_response=True, ) return resource diff --git a/src/mp_api/routes/thermo/resources.py b/src/mp_api/routes/thermo/resources.py index 3c0ad7bf..78eda045 100644 --- a/src/mp_api/routes/thermo/resources.py +++ b/src/mp_api/routes/thermo/resources.py @@ -28,6 +28,7 @@ def thermo_resource(thermo_store): ), ], tags=["Thermo"], + monty_encoded_response=True, ) return resource diff --git a/src/mp_api/routes/wulff/resources.py b/src/mp_api/routes/wulff/resources.py index c39c16cf..983333d7 100644 --- a/src/mp_api/routes/wulff/resources.py +++ b/src/mp_api/routes/wulff/resources.py @@ -14,6 +14,7 @@ def wulff_resource(wulff_store): ], tags=["Surface Properties"], enable_default_search=False, + monty_encoded_response=True, ) return resource diff --git a/src/mp_api/routes/xas/resources.py b/src/mp_api/routes/xas/resources.py index dda6a927..d1352173 100644 --- a/src/mp_api/routes/xas/resources.py +++ b/src/mp_api/routes/xas/resources.py @@ -31,6 +31,7 @@ def xas_resource(xas_store): ), ], tags=["XAS"], + monty_encoded_response=True, ) return resource diff --git a/tests/core/test_client.py b/tests/core/test_client.py index ea546478..8de4200d 100644 --- a/tests/core/test_client.py +++ b/tests/core/test_client.py @@ -1,3 +1,4 @@ +import os import pytest from mp_api.core.client import BaseRester from mp_api.matproj import MPRester @@ -17,11 +18,17 @@ def mpr(): rester.session.close() +@pytest.mark.skipif( + os.environ.get("MP_API_KEY", None) is None, reason="No API key found." +) @pytest.mark.xfail def test_post_fail(rester): rester._post_resource({}, suburl="materials/find_structure") +@pytest.mark.skipif( + os.environ.get("MP_API_KEY", None) is None, reason="No API key found." +) def test_pagination(mpr): mpids = mpr.materials.search_material_docs( all_fields=False, fields=["material_id"], num_chunks=2, chunk_size=1000 @@ -29,6 +36,9 @@ def test_pagination(mpr): assert len(mpids) > 1000 +@pytest.mark.skipif( + os.environ.get("MP_API_KEY", None) is None, reason="No API key found." +) def test_count(mpr): count = mpr.materials.count( dict(task_ids="mp-149", all_fields=False, fields="material_id", limit=1000) @@ -36,11 +46,17 @@ def test_count(mpr): assert count == 1 +@pytest.mark.skipif( + os.environ.get("MP_API_KEY", None) is None, reason="No API key found." +) @pytest.mark.xfail def test_get_document_no_id(mpr): mpr.materials.get_document_by_id(None) +@pytest.mark.skipif( + os.environ.get("MP_API_KEY", None) is None, reason="No API key found." +) @pytest.mark.xfail def test_get_document_no_doc(mpr): mpr.materials.get_document_by_id("mp-1a") diff --git a/tests/dielectric/test_client.py b/tests/dielectric/test_client.py index 1723e825..09bad450 100644 --- a/tests/dielectric/test_client.py +++ b/tests/dielectric/test_client.py @@ -1,3 +1,4 @@ +import os import pytest from mp_api.routes.dielectric.client import DielectricRester @@ -22,6 +23,9 @@ custom_field_tests = {} # type: dict +@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 diff --git a/tests/elasticity/test_client.py b/tests/elasticity/test_client.py index 6b3dd6be..b3a3b89c 100644 --- a/tests/elasticity/test_client.py +++ b/tests/elasticity/test_client.py @@ -1,3 +1,4 @@ +import os import pytest from mp_api.routes.elasticity.client import ElasticityRester @@ -25,6 +26,9 @@ custom_field_tests = {} # type: dict +@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 diff --git a/tests/electrodes/test_client.py b/tests/electrodes/test_client.py index 0f543d0c..76cf3408 100644 --- a/tests/electrodes/test_client.py +++ b/tests/electrodes/test_client.py @@ -1,3 +1,4 @@ +import os import pytest from mp_api.routes.electrodes.client import ElectrodeRester @@ -23,7 +24,10 @@ custom_field_tests = {"working_ion": Element("Li")} # type: dict -@pytest.mark.xfail # TODO: Fix model validation +@pytest.mark.skipif( + os.environ.get("MP_API_KEY", None) is None, reason="No API key found." +) +@pytest.mark.xfail @pytest.mark.parametrize("rester", resters) def test_client(rester): # Get specific search method diff --git a/tests/electronic_structure/test_client.py b/tests/electronic_structure/test_client.py index 92312f92..171bf539 100644 --- a/tests/electronic_structure/test_client.py +++ b/tests/electronic_structure/test_client.py @@ -1,3 +1,4 @@ +import os import pytest from mp_api.routes.electronic_structure.client import ( BandStructureRester, @@ -39,6 +40,9 @@ def es_rester(): } # type: dict +@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 @@ -116,6 +120,9 @@ def bs_rester(): rester.session.close() +@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 @@ -165,6 +172,9 @@ def dos_rester(): rester.session.close() +@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 diff --git a/tests/eos/test_client.py b/tests/eos/test_client.py index 121a1771..0f501c2f 100644 --- a/tests/eos/test_client.py +++ b/tests/eos/test_client.py @@ -1,3 +1,4 @@ +import os import pytest from mp_api.routes.eos.client import EOSRester @@ -22,6 +23,9 @@ custom_field_tests = {} # type: dict +@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 diff --git a/tests/grain_boundary/test_client.py b/tests/grain_boundary/test_client.py index bb42e228..9970b2ea 100644 --- a/tests/grain_boundary/test_client.py +++ b/tests/grain_boundary/test_client.py @@ -1,3 +1,4 @@ +import os import pytest from mp_api.routes.grain_boundary.client import GrainBoundaryRester from mp_api.routes.grain_boundary.models import GBTypeEnum @@ -31,6 +32,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) def test_client(rester): # Get specific search method diff --git a/tests/magnetism/test_client.py b/tests/magnetism/test_client.py index 9eeac9b5..12082464 100644 --- a/tests/magnetism/test_client.py +++ b/tests/magnetism/test_client.py @@ -1,3 +1,4 @@ +import os import pytest from mp_api.routes.magnetism.client import MagnetismRester from pymatgen.analysis.magnetism import Ordering @@ -23,6 +24,9 @@ custom_field_tests = {"ordering": Ordering.FM} # type: dict +@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 diff --git a/tests/materials/test_client.py b/tests/materials/test_client.py index d15c2198..dc5e2408 100644 --- a/tests/materials/test_client.py +++ b/tests/materials/test_client.py @@ -1,3 +1,4 @@ +import os import pytest from mp_api.routes.materials.client import MaterialsRester from emmet.core.symmetry import CrystalSystem @@ -34,6 +35,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) def test_client(rester): # Get specific search method diff --git a/tests/materials/test_utils.py b/tests/materials/test_utils.py index af34d73d..ff80704c 100644 --- a/tests/materials/test_utils.py +++ b/tests/materials/test_utils.py @@ -15,3 +15,7 @@ def test_formula_to_criteria(): } # Anonymous element assert formula_to_criteria("A2B3") == {"formula_anonymous": "A2B3"} + + # Chemsys + assert formula_to_criteria("Si-O") == {"chemsys": "O-Si"} + assert formula_to_criteria("Si-*") == {"elements": {"$all": ["Si"]}, "nelements": 2} diff --git a/tests/molecules/test_client.py b/tests/molecules/test_client.py index cf64cd67..f4681077 100644 --- a/tests/molecules/test_client.py +++ b/tests/molecules/test_client.py @@ -1,3 +1,4 @@ +import os import pytest from mp_api.routes.molecules.client import MoleculesRester from pymatgen.core.periodic_table import Element @@ -28,6 +29,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) def test_client(rester): # Get specific search method diff --git a/tests/phonon/__init__.py b/tests/phonon/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/tests/phonon/test_query_operators.py b/tests/phonon/test_query_operators.py deleted file mode 100644 index 8b7343f6..00000000 --- a/tests/phonon/test_query_operators.py +++ /dev/null @@ -1,15 +0,0 @@ -from mp_api.routes.phonon.query_operators import PhononImgQuery - -from monty.tempfile import ScratchDir -from monty.serialization import loadfn, dumpfn - - -def test_phonon_image_query(): - op = PhononImgQuery() - - assert op.query(task_id="mp-149") == {"criteria": {"task_id": "mp-149"}} - - with ScratchDir("."): - dumpfn(op, "temp.json") - new_op = loadfn("temp.json") - assert new_op.query(task_id="mp-149") == {"criteria": {"task_id": "mp-149"}} diff --git a/tests/piezo/test_client.py b/tests/piezo/test_client.py index acb0097a..dc326752 100644 --- a/tests/piezo/test_client.py +++ b/tests/piezo/test_client.py @@ -1,3 +1,4 @@ +import os import pytest from mp_api.routes.piezo.client import PiezoRester @@ -22,6 +23,9 @@ custom_field_tests = {} # type: dict +@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 diff --git a/tests/robocrys/test_client.py b/tests/robocrys/test_client.py index 64849d3f..72674ca1 100644 --- a/tests/robocrys/test_client.py +++ b/tests/robocrys/test_client.py @@ -1,3 +1,4 @@ +import os import pytest from mp_api.routes.robocrys.client import RobocrysRester @@ -13,6 +14,9 @@ def rester(): rester.session.close() +@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 diff --git a/tests/substrates/test_client.py b/tests/substrates/test_client.py index 32d2ada7..c1450bfc 100644 --- a/tests/substrates/test_client.py +++ b/tests/substrates/test_client.py @@ -1,3 +1,4 @@ +import os import pytest from mp_api.routes.substrates.client import SubstratesRester @@ -33,6 +34,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) def test_client(rester): # Get specific search method diff --git a/tests/surface_properties/test_client.py b/tests/surface_properties/test_client.py index 43375739..e68b1439 100644 --- a/tests/surface_properties/test_client.py +++ b/tests/surface_properties/test_client.py @@ -1,3 +1,4 @@ +import os import pytest from mp_api.routes.surface_properties.client import SurfacePropertiesRester @@ -22,6 +23,9 @@ custom_field_tests = {} # type: dict +@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 diff --git a/tests/synthesis/test_client.py b/tests/synthesis/test_client.py index 69284c7f..025e8687 100644 --- a/tests/synthesis/test_client.py +++ b/tests/synthesis/test_client.py @@ -1,3 +1,4 @@ +import os import pytest from mp_api.routes.synthesis.client import SynthesisRester @@ -11,6 +12,9 @@ def rester(): rester.session.close() +@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 @@ -27,4 +31,3 @@ def test_client(rester): assert doc.doi is not None assert doc.paragraph_string is not None assert doc.synthesis_type is not None - diff --git a/tests/tasks/test_client.py b/tests/tasks/test_client.py index 7b3049c5..7b24920d 100644 --- a/tests/tasks/test_client.py +++ b/tests/tasks/test_client.py @@ -1,3 +1,4 @@ +import os import pytest from mp_api.routes.tasks.client import TaskRester @@ -22,6 +23,9 @@ custom_field_tests = {"chemsys_formula": "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) def test_client(rester): # Get specific search method diff --git a/tests/test_client.py b/tests/test_client.py index e1b0523f..e1af4e57 100644 --- a/tests/test_client.py +++ b/tests/test_client.py @@ -34,6 +34,9 @@ mpr = MPRester() +@pytest.mark.skipif( + os.environ.get("MP_API_KEY", None) is None, reason="No API key found." +) @pytest.mark.parametrize("rester", mpr._all_resters) def test_generic_get_methods(rester): diff --git a/tests/test_mprester.py b/tests/test_mprester.py index 781b3031..23980081 100644 --- a/tests/test_mprester.py +++ b/tests/test_mprester.py @@ -16,14 +16,11 @@ from pymatgen.entries.computed_entries import ComputedEntry from pymatgen.core.periodic_table import Element from pymatgen.phonon.bandstructure import PhononBandStructureSymmLine +from pymatgen.phonon.dos import PhononDos from pymatgen.io.vasp import Incar, Chgcar from pymatgen.analysis.magnetism import Ordering from pymatgen.analysis.wulff import WulffShape -api_is_up = ( - requests.get("https://api.materialsproject.org/heartbeat").status_code == 200 -) - @pytest.fixture() def mpr(): @@ -33,8 +30,7 @@ def mpr(): @pytest.mark.skipif( - ((os.environ.get("MP_API_KEY", None) is None) or (not api_is_up)), - reason="API is down", + 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): @@ -115,10 +111,14 @@ def test_get_entries(self, mpr): assert sorted_entries != entries + @pytest.mark.xfail def test_get_phonon_data_by_material_id(self, mpr): - bs = mpr.get_phonon_bandstructure_by_material_id("mp-661") + bs = mpr.get_phonon_bandstructure_by_material_id("mp-11659") assert isinstance(bs, PhononBandStructureSymmLine) + dos = mpr.get_phonon_dos_by_material_id("mp-11659") + assert isinstance(dos, PhononDos) + def test_get_charge_density_data(self, mpr): task_ids = mpr.get_charge_density_calculation_ids_from_material_id("mp-149") assert len(task_ids) > 0 @@ -131,6 +131,9 @@ def test_get_charge_density_data(self, mpr): chgcar = mpr.get_charge_density_from_calculation_id(task_ids[0]["task_id"]) assert isinstance(chgcar, Chgcar) + chgcar = mpr.get_charge_density_by_material_id("mp-149") + assert isinstance(chgcar, Chgcar) + def test_get_substrates(self, mpr): substrate_data = mpr.get_substrates("mp-123", [1, 0, 0]) substrates = [sub_dict["sub_id"] for sub_dict in substrate_data] @@ -150,7 +153,6 @@ def test_get_surface_data(self, mpr): assert "is_reconstructed" in surface assert "structure" in surface - @pytest.mark.xfail # temporary def test_get_gb_data(self, mpr): mo_gbs = mpr.get_gb_data(chemsys="Mo") assert len(mo_gbs) == 10 diff --git a/tests/thermo/test_client.py b/tests/thermo/test_client.py index 961f5a74..9f645aeb 100644 --- a/tests/thermo/test_client.py +++ b/tests/thermo/test_client.py @@ -1,3 +1,4 @@ +import os import pytest from mp_api.routes.thermo.client import ThermoRester @@ -32,6 +33,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) def test_client(rester): # Get specific search method diff --git a/tests/xas/test_client.py b/tests/xas/test_client.py index b7c2f3c7..a24a3a17 100644 --- a/tests/xas/test_client.py +++ b/tests/xas/test_client.py @@ -1,3 +1,4 @@ +import os import pytest from mp_api.routes.xas.client import XASRester from emmet.core.xas import Edge, Type @@ -33,6 +34,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) def test_client(rester): # Get specific search method