def substitution_structures(self, mp_struct, mpid): ''' Predicts substituted compounds from a given Materials Project structure (mp_struct) and ID (mpid) ''' ''' flat_predictions: a list of predicted structures as pymatgen.alchemy.materials.TransformedStructure objects ''' auto_oxi = AutoOxiStateDecorationTransformation() #initialize the automatic oxidation state transformation structures = [] try: oxi_struct = auto_oxi.apply_transformation(mp_struct) # get oxidized reference structure subs = SubstitutionPredictor().composition_prediction(oxi_struct.composition) # from a given charge-balanced trial_subs = [list(sub['substitutions'].keys()) for sub in subs] # returns the potential substitutions sbr = Substitutor() for sub in trial_subs: run = True for specie in sub: if str(specie.element) not in self.elements: run = False if run == True: structs = sbr.pred_from_structures(sub, [{'structure':oxi_struct, 'id':mpid}]) structures.append(structs) except: pass flat_predictions = [structure for substitution in structures for structure in substitution] return flat_predictions
def predict_structure(species, struc_list, check_dir=False, threshold=0.00001): """ Predicted structures for set of pymatgen species using the Pymatgen structure predictor and save as cif file. TODO: This will be superceded by our own implementation of the structure prediction algorithm in future versions of SMACT. Args: species (list): Pymatgen Species for which structure should be predicted. struc_list (list): Pymatgen Structure objects to consider as parent structures in the substitution algorithm. check_dir (bool): check if directory already exists and only carry out prediction and write new files if it doesn't. threshold (float): Log-probability threshold for the Pymatgen structure predictor. Returns: Saves cif files of possible structures in new directory along with a summary .txt file containing info including probabilities. """ sub = Substitutor(threshold=0.00001) print('{} ........'.format(species)) dirname = ''.join([str(i) for i in species]) path_exists = True if os.path.exists( './SP_results/{0}'.format(dirname)) else False if (check_dir and path_exists): print('Already exists') else: print('{} not already there'.format(dirname)) suggested_strucs = sub.pred_from_structures(target_species=species, structures_list=struc_list, remove_existing=False, remove_duplicates=True) suggested_strucs = sorted(suggested_strucs, key=lambda k: k.other_parameters['proba'], reverse=True) # Save the structures as cifs if not path_exists: os.makedirs('./SP_results/{0}'.format(dirname)) for i, d in enumerate(suggested_strucs): cw = CifWriter(d.final_structure) cw.write_file("SP_results/{0}/{1}_BasedOn_{2}.cif".format( dirname, i, d.history[0]['source'])) # Save the summary as a text file with open('SP_results/{0}/{0}_summary.txt'.format(dirname), 'w') as f: f.write('Formula, Probability, Based on, Substitutions \n') for i, struc in enumerate(suggested_strucs): f.write( ' {0}, {1:12}, {2:.5f}, {3:9}, {4} \n'.format( i, struc.composition.reduced_formula, struc.other_parameters['proba'], struc.history[0]['source'], re.sub('Specie', '', str(struc.history[1]['species_map'])))) print('Done.')
class SubstitutorTest(unittest.TestCase): def setUp(self): self.s = Substitutor(threshold=1e-3, lambda_table=get_table(), alpha=-5.) def test_substitutor(self): s_list = [Specie('O', -2), Specie('Li', 1)] subs = self.s.pred_from_list(s_list) self.assertEqual(len(subs), 4, 'incorrect number of substitutions') c = Composition({'O2-': 1, 'Li1+': 2}) subs = self.s.pred_from_comp(c) self.assertEqual(len(subs), 4, 'incorrect number of substitutions') def test_as_dict(self): Substitutor.from_dict(self.s.as_dict())
def predict_structure(species, struc_list, check_dir=False): """ Save cif files of predicted structures for set of pymatgen species. Args: species (list): pymatgen species to predict structures for struc_list (list): pymatgen structures to substitute species into check_dir (bool): check if directory already exists and only carry out prediction if it doesn't. """ sub = Substitutor(threshold=0.00001) print('{} ........'.format(species)) dirname = ''.join([str(i) for i in species]) path_exists = True if os.path.exists( './SP_results/{0}'.format(dirname)) else False if (check_dir and path_exists): print('Already exists') else: print('{} not already there'.format(dirname)) suggested_strucs = sub.pred_from_structures(target_species=species, structures_list=struc_list, remove_existing=False, remove_duplicates=True) suggested_strucs = sorted(suggested_strucs, key=lambda k: k.other_parameters['proba'], reverse=True) # Save the structures as cifs if not path_exists: os.makedirs('./SP_results/{0}'.format(dirname)) for i, d in enumerate(suggested_strucs): cw = CifWriter(d.final_structure) cw.write_file("SP_results/{0}/{1}_BasedOn_{2}.cif".format( dirname, i, d.history[0]['source'])) # Save the summary as a text file with open('SP_results/{0}/{0}_summary.txt'.format(dirname), 'w') as f: f.write('Formula, Probability, Based on, Substitutions \n') for i, struc in enumerate(suggested_strucs): f.write( ' {0}, {1:12}, {2:.5f}, {3:9}, {4} \n'.format( i, struc.composition.reduced_formula, struc.other_parameters['proba'], struc.history[0]['source'], re.sub('Specie', '', str(struc.history[1]['species_map'])))) print('Done.')
class SubstitutorTest(PymatgenTest): def setUp(self): self.s = Substitutor(threshold=1e-3, lambda_table=get_table(), alpha=-5.) def test_substitutor(self): s_list = [Specie('O', -2), Specie('Li', 1)] subs = self.s.pred_from_list(s_list) self.assertEqual(len(subs), 4, 'incorrect number of substitutions') c = Composition({'O2-': 1, 'Li1+': 2}) subs = self.s.pred_from_comp(c) self.assertEqual(len(subs), 4, 'incorrect number of substitutions') structures = [{ "structure": PymatgenTest.get_structure("Li2O"), "id": "pmgtest" }] subs = self.s.pred_from_structures(["Na+", "O2-"], structures) self.assertEqual(subs[0].formula, "Na2 O1") def test_as_dict(self): Substitutor.from_dict(self.s.as_dict())
class SubstitutorTest(PymatgenTest): def setUp(self): self.s = Substitutor(threshold=1e-3, lambda_table=get_table(), alpha= -5.) def test_substitutor(self): s_list = [Specie('O', -2), Specie('Li', 1)] subs = self.s.pred_from_list(s_list) self.assertEqual(len(subs), 4 , 'incorrect number of substitutions') c = Composition({'O2-': 1, 'Li1+': 2}) subs = self.s.pred_from_comp(c) self.assertEqual(len(subs), 4 , 'incorrect number of substitutions') structures = [{"structure": PymatgenTest.get_structure("Li2O"), "id": "pmgtest"}] subs = self.s.pred_from_structures(["Na+", "O2-"], structures) self.assertEqual(subs[0].formula, "Na2 O1") def test_as_dict(self): Substitutor.from_dict(self.s.as_dict())
def get_Probable_Substitutions(species, elements_to_include, threshold): Sub = Substitutor(threshold=threshold) subs_dict = {} for specie in species: subs = Sub.pred_from_list([specie]) try: subs.sort(key=lambda x: x['probability'], reverse=True) subs_dict_key = list(subs[0]['substitutions'].keys())[0] subs_dict[subs_dict_key] = [] for i in range(len(subs)): if list(subs[i]['substitutions'].values() )[0].symbol in elements_to_include: subs_dict[subs_dict_key].append( list(subs[i]['substitutions'].values())[0]) except: print('No probable substitutions at %s threshold for %s' % (threshold, specie)) continue return subs_dict
def test_as_dict(self): Substitutor.from_dict(self.s.as_dict())
def setUp(self): self.s = Substitutor(threshold=1e-3, lambda_table=get_table(), alpha=-5.)
def setUp(self): self.s = Substitutor(threshold=1e-3, lambda_table=get_table(), alpha= -5.)
def process_item(self, item): """ Finds all predicted structures for given item Args: item (dict): structure prediction request and relevant oxidation state-labeled structure templates Returns: (dict, dict): A tuple containing updated request doc and a list of crystal docs to update """ request = item["request"] templates = item["templates"] self.logger.info( f"Labeling oxidation states for {len(templates)} structure templates" ) oxi_labeled_templates = [] for template in templates: struct = Structure.from_dict(template["structure"]) try: oxi_labeled_templates.append({ "structure": self.auto_oxi.apply_transformation(struct), "id": template[self.structure_templates.key], }) except: continue # if auto-oxidation fails, try next structure self.logger.info( f"Successfully labeled oxidation states for {len(oxi_labeled_templates)} structures" ) self.logger.info("Substituting original species into structures") predicted_structs = Substitutor( threshold=request["threshold"]).pred_from_structures( request["original_species"], oxi_labeled_templates, remove_duplicates=True, remove_existing=True, ) predicted_structs.sort(key=lambda s: s.other_parameters["proba"], reverse=True) structure_prediction_id = request[self.requests.key] crystal_docs = [] summaries = [] self.logger.info( f"Found {len(predicted_structs)} predicted structures. Generating crystal docs (XRDs, CIFs, etc." ) for number_id, struct in enumerate(predicted_structs): crystal = {} summary = {} xrd_dict = {} final_structure = struct.final_structure sga = SpacegroupAnalyzer(final_structure, symprec=0.1) for rad_source in ["CuKa", "AgKa", "MoKa", "FeKa"]: xrdc = XRDCalculator(wavelength=rad_source) pattern = xrdc.get_pattern(final_structure, two_theta_range=None) xrd_dict[rad_source] = pattern transformed_structure = struct.to_snl( f"{request['name']} <{request['email']}>", remarks=["Created by MP Structure Predictor"], ) crystal[self.requests.key] = structure_prediction_id crystal[self.crystals.key] = number_id crystal["probability"] = struct.other_parameters["proba"] crystal["transformed_structure"] = transformed_structure crystal["xrd"] = xrd_dict crystal["space_group_info"] = { "symbol": sga.get_space_group_symbol(), "number": sga.get_space_group_number(), "hall": sga.get_hall(), "crystal_system": sga.get_crystal_system(), } summary[self.crystals.key] = number_id summary["probability"] = struct.other_parameters["proba"] summary[ "pretty_formula"] = final_structure.composition.reduced_formula summary["nsites"] = len(final_structure) summary["space_group"] = sga.get_space_group_symbol() summary["cif"] = str(CifWriter(final_structure)) crystal_docs.append(jsanitize(crystal, strict=True)) summaries.append(jsanitize(summary, strict=True)) self.logger.info( f"Successfully generated {len(crystal_docs)} crystal docs for request {request['original_species']}" ) request.update({ "state": "COMPLETE", "completed_at": datetime.utcnow(), "num_crystals": len(crystal_docs), "crystals": summaries, }) return request, crystal_docs