2828 Field ,
2929 ValidationError ,
3030 model_validator ,
31+ field_validator ,
3132 conlist ,
3233 PrivateAttr ,
3334)
@@ -180,6 +181,22 @@ class MeltingTemperature(BaseModel, title="Input options for melting temperature
180181 step : Annotated [int , Field (default = 200 , ge = 20 )]
181182 attempts : Annotated [int , Field (default = 5 , ge = 1 )]
182183
184+ class MaterialsProject (BaseModel , title = 'Input options for materials project' ):
185+ api_key : Annotated [str , Field (default = "" , exclude = True )]
186+ conventional : Annotated [bool , Field (default = True )]
187+ target_natoms : Annotated [int , Field (default = 1500 , description = 'The structure parsed from materials project would be repeated to approximately this value' )]
188+
189+ @field_validator ("api_key" , mode = "after" )
190+ def resolve_api_key (cls , v : str ) -> str :
191+ if not v :
192+ return v
193+ value = os .getenv (v )
194+ if not value :
195+ raise ValueError (
196+ f"Environment variable '{ v } ' not found or empty. "
197+ f"Set it before running, e.g.:\n export { v } ='your_api_key_here'"
198+ )
199+ return value
183200
184201class Calculation (BaseModel , title = "Main input class" ):
185202 monte_carlo : Optional [MonteCarlo ] = MonteCarlo ()
@@ -191,6 +208,8 @@ class Calculation(BaseModel, title="Main input class"):
191208 tolerance : Optional [Tolerance ] = Tolerance ()
192209 uhlenbeck_ford_model : Optional [UFMP ] = UFMP ()
193210 melting_temperature : Optional [MeltingTemperature ] = MeltingTemperature ()
211+ materials_project : Optional [MaterialsProject ] = MaterialsProject ()
212+
194213 element : Annotated [List [str ], BeforeValidator (to_list ), Field (default = [])]
195214 n_elements : Annotated [int , Field (default = 0 )]
196215 mass : Annotated [List [float ], BeforeValidator (to_list ), Field (default = [])]
@@ -496,6 +515,44 @@ def _validate_all(self) -> "Input":
496515 self ._original_lattice = self .lattice .lower ()
497516 write_structure_file = True
498517
518+ elif self .lattice .split ('-' )[0 ] == 'mp' :
519+ #confirm here that API key exists
520+ if not self .materials_project .api_key :
521+ raise ValueError ('could not find API KEY, pls set it.' )
522+ #now we need to fetch the structure
523+ try :
524+ from mp_api .client import MPRester
525+ except ImportError :
526+ raise ImportError ('Could not import mp_api, make sure you install mp_api package!' )
527+ #now all good
528+ rest = {
529+ "use_document_model" : False ,
530+ "include_user_agent" : True ,
531+ "api_key" : self .materials_project .api_key ,
532+ }
533+ with MPRester (** rest ) as mpr :
534+ docs = mpr .materials .summary .search (material_ids = [self .lattice ])
535+
536+ structures = []
537+ for doc in docs :
538+ struct = doc ['structure' ]
539+ if self .materials_project .conventional :
540+ aseatoms = struct .to_conventional ().to_ase_atoms ()
541+ else :
542+ aseatoms = struct .to_primitive ().to_ase_atoms ()
543+ structures .append (aseatoms )
544+ structure = structures [0 ]
545+
546+ if np .prod (self .repeat ) == 1 :
547+ x = int (np .ceil ((self .materials_project .target_natoms / len (structure ))** (1 / 3 )))
548+ structure = structure .repeat (x )
549+ else :
550+ structure = structure .repeat (self .repeat )
551+
552+ self ._natoms = len (structure )
553+ self ._original_lattice = self .lattice .lower ()
554+ write_structure_file = True
555+
499556 else :
500557 # this is a file
501558 if not os .path .exists (self .lattice ):
0 commit comments