def find_all_chemsys(original_species, threshold=0.0001, max_num_subs=5): """ Determines chemical systems to consider via data-mined ionic substitution probabilities (see lambda.json) Args: original_species (list): a list of species, e.g. [Specie('Li',1), Specie('Ni',2), Specie('O',-2)] threshold (float): Probability threshold for generating ionic substitutions. Defaults to 0.0001 max_num_subs (int): Limits maximum number of substitutions wanted. Defaults to 5 Returns: (set): A set of all chemical systems to predict from """ num_species = len(original_species) sub_elems = set() for specie in original_species: subs = SubstitutionPredictor(threshold=threshold).list_prediction( [specie]) # sort and cap number of substitutions subs.sort(key=lambda x: x["probability"], reverse=True) subs = subs[0:max_num_subs] for sub in subs: for new_species in sub["substitutions"]: sub_elems.add(new_species.element) all_chemsys = set() for chemsys in combinations(sub_elems, num_species): all_chemsys.add("-".join(sorted([str(el) for el in chemsys]))) return all_chemsys
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 get_dopants_from_substitution_probabilities(structure, num_dopants=5, threshold=0.001, match_oxi_sign=False): """ Get dopant suggestions based on substitution probabilities. Args: structure (Structure): A pymatgen structure decorated with oxidation states. num_dopants (int): The number of suggestions to return for n- and p-type dopants. threshold (float): Probability threshold for substitutions. match_oxi_sign (bool): Whether to force the dopant and original species to have the same sign of oxidation state. E.g. If the original site is in a negative charge state, then only negative dopants will be returned. Returns: (dict): Dopant suggestions, given as a dictionary with keys "n_type" and "p_type". The suggestions for each doping type are given as a list of dictionaries, each with they keys: - "probability": The probability of substitution. - "dopant_species": The dopant species. - "original_species": The substituted species. """ els_have_oxi_states = [hasattr(s, "oxi_state") for s in structure.species] if not all(els_have_oxi_states): raise ValueError( "All sites in structure must have oxidation states to " "predict dopants.") sp = SubstitutionPredictor(threshold=threshold) subs = [sp.list_prediction([s]) for s in set(structure.species)] subs = [{ 'probability': pred['probability'], 'dopant_species': list(pred['substitutions'].keys())[0], 'original_species': list(pred['substitutions'].values())[0] } for species_preds in subs for pred in species_preds] subs.sort(key=lambda x: x['probability'], reverse=True) return _get_dopants(subs, num_dopants, match_oxi_sign)
def get_dopants_from_substitution_probabilities(structure, num_dopants=5, threshold=0.001, match_oxi_sign=False): """ Get dopant suggestions based on substitution probabilities. Args: structure (Structure): A pymatgen structure decorated with oxidation states. num_dopants (int): The number of suggestions to return for n- and p-type dopants. threshold (float): Probability threshold for substitutions. match_oxi_sign (bool): Whether to force the dopant and original species to have the same sign of oxidation state. E.g. If the original site is in a negative charge state, then only negative dopants will be returned. Returns: (dict): Dopant suggestions, given as a dictionary with keys "n_type" and "p_type". The suggestions for each doping type are given as a list of dictionaries, each with they keys: - "probability": The probability of substitution. - "dopant_species": The dopant species. - "original_species": The substituted species. """ els_have_oxi_states = [hasattr(s, "oxi_state") for s in structure.species] if not all(els_have_oxi_states): raise ValueError("All sites in structure must have oxidation states to " "predict dopants.") sp = SubstitutionPredictor(threshold=threshold) subs = [sp.list_prediction([s]) for s in set(structure.species)] subs = [{'probability': pred['probability'], 'dopant_species': list(pred['substitutions'].keys())[0], 'original_species': list(pred['substitutions'].values())[0]} for species_preds in subs for pred in species_preds] subs.sort(key=lambda x: x['probability'], reverse=True) return _get_dopants(subs, num_dopants, match_oxi_sign)
def test_prediction(self): sp = SubstitutionPredictor(threshold = 8e-3) result = sp.list_prediction(['Na+', 'Cl-'], to_this_composition = True)[5] cprob = sp.p.cond_prob_list(result['substitutions'].keys(), result['substitutions'].values()) self.assertAlmostEqual(result['probability'], cprob) self.assertEqual(set(result['substitutions'].values()), set(['Na+', 'Cl-'])) result = sp.list_prediction(['Na+', 'Cl-'], to_this_composition = False)[5] cprob = sp.p.cond_prob_list(result['substitutions'].keys(), result['substitutions'].values()) self.assertAlmostEqual(result['probability'], cprob) self.assertNotEqual(set(result['substitutions'].values()), set(['Na+', 'Cl-'])) c = Composition({'Ag2+' : 1, 'Cl-' : 2}) result = sp.composition_prediction(c, to_this_composition = True)[2] self.assertEqual(set(result['substitutions'].values()), set(c.elements)) result = sp.composition_prediction(c, to_this_composition = False)[2] self.assertEqual(set(result['substitutions'].keys()), set(c.elements))
class SubstitutionPredictorTransformation(AbstractTransformation): """ This transformation takes a structure and uses the structure prediction module to find likely site substitutions. Args: threshold: Threshold for substitution. **kwargs: Args for SubstitutionProbability class lambda_table, alpha """ def __init__(self, threshold=1e-2, **kwargs): self.kwargs = kwargs self.threshold = threshold self._substitutor = SubstitutionPredictor(threshold=threshold, **kwargs) def apply_transformation(self, structure, return_ranked_list=False): if not return_ranked_list: raise ValueError("SubstitutionPredictorTransformation doesn't" " support returning 1 structure") preds = self._substitutor.composition_prediction( structure.composition, to_this_composition=False) preds.sort(key=lambda x: x['probability'], reverse=True) outputs = [] for pred in preds: st = SubstitutionTransformation(pred['substitutions']) output = { 'structure': st.apply_transformation(structure), 'probability': pred['probability'], 'threshold': self.threshold, 'substitutions': {} } # dictionary keys have to be converted to strings for JSON for key, value in pred['substitutions'].items(): output['substitutions'][str(key)] = str(value) outputs.append(output) return outputs def __str__(self): return "SubstitutionPredictorTransformation" def __repr__(self): return self.__str__() @property def inverse(self): return None @property def is_one_to_many(self): return True
class SubstitutionPredictorTransformation(AbstractTransformation): """ This transformation takes a structure and uses the structure prediction module to find likely site substitutions. Args: threshold: Threshold for substitution. **kwargs: Args for SubstitutionProbability class lambda_table, alpha """ def __init__(self, threshold=1e-2, scale_volumes=True, **kwargs): self.kwargs = kwargs self.threshold = threshold self.scale_volumes = scale_volumes self._substitutor = SubstitutionPredictor(threshold=threshold, **kwargs) def apply_transformation(self, structure, return_ranked_list=False): if not return_ranked_list: raise ValueError("SubstitutionPredictorTransformation doesn't" " support returning 1 structure") preds = self._substitutor.composition_prediction( structure.composition, to_this_composition=False) preds.sort(key=lambda x: x['probability'], reverse=True) outputs = [] for pred in preds: st = SubstitutionTransformation(pred['substitutions']) output = {'structure': st.apply_transformation(structure), 'probability': pred['probability'], 'threshold': self.threshold, 'substitutions': {}} # dictionary keys have to be converted to strings for JSON for key, value in pred['substitutions'].items(): output['substitutions'][str(key)] = str(value) outputs.append(output) return outputs def __str__(self): return "SubstitutionPredictorTransformation" def __repr__(self): return self.__str__() @property def inverse(self): return None @property def is_one_to_many(self): return True
def test_prediction(self): sp = SubstitutionPredictor(threshold=8e-3) result = sp.list_prediction(["Na+", "Cl-"], to_this_composition=True)[5] cprob = sp.p.cond_prob_list(result["substitutions"].keys(), result["substitutions"].values()) self.assertAlmostEqual(result["probability"], cprob) self.assertEqual(set(result["substitutions"].values()), {"Na+", "Cl-"}) result = sp.list_prediction(["Na+", "Cl-"], to_this_composition=False)[5] cprob = sp.p.cond_prob_list(result["substitutions"].keys(), result["substitutions"].values()) self.assertAlmostEqual(result["probability"], cprob) self.assertNotEqual(set(result["substitutions"].values()), {"Na+", "Cl-"}) c = Composition({"Ag2+": 1, "Cl-": 2}) result = sp.composition_prediction(c, to_this_composition=True)[2] self.assertEqual(set(result["substitutions"].values()), set(c.elements)) result = sp.composition_prediction(c, to_this_composition=False)[2] self.assertEqual(set(result["substitutions"].keys()), set(c.elements))
def test_prediction(self): sp = SubstitutionPredictor(threshold=8e-3) result = sp.list_prediction(['Na+', 'Cl-'], to_this_composition=True)[5] cprob = sp.p.cond_prob_list(result['substitutions'].keys(), result['substitutions'].values()) self.assertAlmostEqual(result['probability'], cprob) self.assertEqual(set(result['substitutions'].values()), set(['Na+', 'Cl-'])) result = sp.list_prediction(['Na+', 'Cl-'], to_this_composition=False)[5] cprob = sp.p.cond_prob_list(result['substitutions'].keys(), result['substitutions'].values()) self.assertAlmostEqual(result['probability'], cprob) self.assertNotEqual(set(result['substitutions'].values()), set(['Na+', 'Cl-'])) c = Composition({'Ag2+': 1, 'Cl-': 2}) result = sp.composition_prediction(c, to_this_composition=True)[2] self.assertEqual(set(result['substitutions'].values()), set(c.elements)) result = sp.composition_prediction(c, to_this_composition=False)[2] self.assertEqual(set(result['substitutions'].keys()), set(c.elements))
def __init__(self, threshold=1e-2, **kwargs): self.kwargs = kwargs self.threshold = threshold self._substitutor = SubstitutionPredictor(threshold=threshold, **kwargs)
def __init__(self, threshold=1e-2, scale_volumes=True, **kwargs): self.kwargs = kwargs self.threshold = threshold self.scale_volumes = scale_volumes self._substitutor = SubstitutionPredictor(threshold=threshold, **kwargs)