def test_apply_transformation(self): enum_trans = EnumerateStructureTransformation(refine_structure=True) p = Poscar.from_file(os.path.join(test_dir, 'POSCAR.LiFePO4'), check_for_POTCAR=False) struct = p.structure expected_ans = [1, 3, 1] for i, frac in enumerate([0.25, 0.5, 0.75]): trans = SubstitutionTransformation({'Fe': {'Fe': frac}}) s = trans.apply_transformation(struct) oxitrans = OxidationStateDecorationTransformation({'Li': 1, 'Fe': 2, 'P': 5, 'O': -2}) s = oxitrans.apply_transformation(s) alls = enum_trans.apply_transformation(s, 100) self.assertEquals(len(alls), expected_ans[i]) self.assertIsInstance(trans.apply_transformation(s), Structure) for s in alls: self.assertIn("energy", s) #make sure it works for non-oxidation state decorated structure trans = SubstitutionTransformation({'Fe': {'Fe': 0.5}}) s = trans.apply_transformation(struct) alls = enum_trans.apply_transformation(s, 100) self.assertEquals(len(alls), 3) self.assertIsInstance(trans.apply_transformation(s), Structure) for s in alls: self.assertNotIn("energy", s)
def apply_transformation(self, structure, return_ranked_list=False): if not return_ranked_list: raise ValueError( "MultipleSubstitutionTransformation has no single" " best structure output. Must use" " return_ranked_list." ) outputs = [] for charge, el_list in self._substitution_dict.items(): mapping = {} if charge > 0: sign = "+" else: sign = "-" dummy_sp = "X{}{}".format(str(charge), sign) mapping[self._sp_to_replace] = {self._sp_to_replace: 1 - self._r_fraction, dummy_sp: self._r_fraction} trans = SubstitutionTransformation(mapping) dummy_structure = trans.apply_transformation(structure) if self._charge_balance_species is not None: cbt = ChargeBalanceTransformation(self._charge_balance_species) dummy_structure = cbt.apply_transformation(dummy_structure) if self._order: trans = OrderDisorderedStructureTransformation() dummy_structure = trans.apply_transformation(dummy_structure) for el in el_list: if charge > 0: sign = "+" else: sign = "-" st = SubstitutionTransformation({"X{}+".format(str(charge)): "{}{}{}".format(el, charge, sign)}) new_structure = st.apply_transformation(dummy_structure) outputs.append({"structure": new_structure}) return outputs
def test_init(self): if not enumlib_present: raise SkipTest("enumlib not present. Skipping...") test_dir = os.path.join(os.path.dirname(__file__), "..", "..", "..", 'test_files') parser = CifParser(os.path.join(test_dir, "LiFePO4.cif")) struct = parser.get_structures(False)[0] subtrans = SubstitutionTransformation({'Li': {'Li': 0.5}}) adaptor = EnumlibAdaptor(subtrans.apply_transformation(struct), 1, 2) adaptor.run() structures = adaptor.structures self.assertEqual(len(structures), 86) for s in structures: self.assertAlmostEqual(s.composition .get_atomic_fraction(Element("Li")), 0.5 / 6.5) adaptor = EnumlibAdaptor(subtrans.apply_transformation(struct), 1, 2, refine_structure=True) adaptor.run() structures = adaptor.structures self.assertEqual(len(structures), 52) subtrans = SubstitutionTransformation({'Li': {'Li': 0.25}}) adaptor = EnumlibAdaptor(subtrans.apply_transformation(struct), 1, 1, refine_structure=True) adaptor.run() structures = adaptor.structures self.assertEqual(len(structures), 1) for s in structures: self.assertAlmostEqual(s.composition .get_atomic_fraction(Element("Li")), 0.25 / 6.25) #Make sure it works for completely disordered structures. struct = Structure([[10, 0, 0], [0, 10, 0], [0, 0, 10]], [{'Fe':0.5}], [[0, 0, 0]]) adaptor = EnumlibAdaptor(struct, 1, 2) adaptor.run() self.assertEqual(len(adaptor.structures), 3) #Make sure it works properly when symmetry is broken by ordered sites. parser = CifParser(os.path.join(test_dir, "LiFePO4.cif")) struct = parser.get_structures(False)[0] subtrans = SubstitutionTransformation({'Li': {'Li': 0.25}}) s = subtrans.apply_transformation(struct) #REmove some ordered sites to break symmetry. removetrans = RemoveSitesTransformation([4, 7]) s = removetrans.apply_transformation(s) adaptor = EnumlibAdaptor(s, 1, 1, enum_precision_parameter=0.01) adaptor.run() structures = adaptor.structures self.assertEqual(len(structures), 4) struct = Structure([[3, 0, 0], [0, 3, 0], [0, 0, 3]], [{"Si": 0.5}] * 2, [[0, 0, 0], [0.5, 0.5, 0.5]]) adaptor = EnumlibAdaptor(struct, 1, 3, enum_precision_parameter=0.01) adaptor.run() structures = adaptor.structures self.assertEqual(len(structures), 10)
def test_init(self): test_dir = os.path.join(os.path.dirname(__file__), "..", "..", "..", "test_files") struct = self.get_structure("LiFePO4") subtrans = SubstitutionTransformation({"Li": {"Li": 0.5}}) adaptor = EnumlibAdaptor(subtrans.apply_transformation(struct), 1, 2) adaptor.run() structures = adaptor.structures self.assertEqual(len(structures), 86) for s in structures: self.assertAlmostEqual(s.composition.get_atomic_fraction(Element("Li")), 0.5 / 6.5) adaptor = EnumlibAdaptor(subtrans.apply_transformation(struct), 1, 2, refine_structure=True) adaptor.run() structures = adaptor.structures self.assertEqual(len(structures), 52) subtrans = SubstitutionTransformation({"Li": {"Li": 0.25}}) adaptor = EnumlibAdaptor(subtrans.apply_transformation(struct), 1, 1, refine_structure=True) adaptor.run() structures = adaptor.structures self.assertEqual(len(structures), 1) for s in structures: self.assertAlmostEqual(s.composition.get_atomic_fraction(Element("Li")), 0.25 / 6.25) # Make sure it works for completely disordered structures. struct = Structure([[10, 0, 0], [0, 10, 0], [0, 0, 10]], [{"Fe": 0.5}], [[0, 0, 0]]) adaptor = EnumlibAdaptor(struct, 1, 2) adaptor.run() self.assertEqual(len(adaptor.structures), 3) # Make sure it works properly when symmetry is broken by ordered sites. struct = self.get_structure("LiFePO4") subtrans = SubstitutionTransformation({"Li": {"Li": 0.25}}) s = subtrans.apply_transformation(struct) # REmove some ordered sites to break symmetry. removetrans = RemoveSitesTransformation([4, 7]) s = removetrans.apply_transformation(s) adaptor = EnumlibAdaptor(s, 1, 1, enum_precision_parameter=0.01) adaptor.run() structures = adaptor.structures self.assertEqual(len(structures), 4) struct = Structure([[3, 0, 0], [0, 3, 0], [0, 0, 3]], [{"Si": 0.5}] * 2, [[0, 0, 0], [0.5, 0.5, 0.5]]) adaptor = EnumlibAdaptor(struct, 1, 3, enum_precision_parameter=0.01) adaptor.run() structures = adaptor.structures self.assertEqual(len(structures), 10) struct = Structure.from_file(os.path.join(test_dir, "EnumerateTest.json")) adaptor = EnumlibAdaptor(struct, 1, 1) adaptor.run() structures = adaptor.structures self.assertEqual(len(structures), 2)
def apply_transformation(self, structure): charge = structure.charge specie = smart_element_or_specie(self._charge_balance_sp) num_to_remove = charge / specie.oxi_state num_in_structure = structure.composition[specie] removal_fraction = num_to_remove / num_in_structure if removal_fraction < 0: raise ValueError("addition of specie not yet supported by " "ChargeBalanceTransformation") trans = SubstitutionTransformation({self._charge_balance_sp: {self._charge_balance_sp: 1 - removal_fraction}}) return trans.apply_transformation(structure)
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.pred_from_comp(structure.composition) 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 pred_from_structures(self, target_species, structures_list, remove_duplicates=True): """ performs a structure prediction targeting compounds containing the target_species and based on a list of structure (those structures can for instance come from a database like the ICSD). It will return all the structures formed by ionic substitutions with a probability higher than the threshold Args: target_species: a list of species with oxidation states e.g., [Specie('Li',1),Specie('Ni',2), Specie('O',-2)] structures_list: a list of dictionnary of the form {'structure':Structure object ,'id':some id where it comes from} the id can for instance refer to an ICSD id Returns: a list of TransformedStructure objects. """ result = [] transmuter = StandardTransmuter([]) if len(list(set(target_species) & set(self.get_allowed_species()))) \ != len(target_species): raise ValueError("the species in target_species are not allowed" + "for the probability model you are using") for permut in itertools.permutations(target_species): for s in structures_list: #check if: species are in the domain, #and the probability of subst. is above the threshold els = s['structure'].composition.elements if len(els) == len(permut) and \ len(list(set(els) & set(self.get_allowed_species()))) == \ len(els) and self._sp.cond_prob_list(permut, els) > \ self._threshold: clean_subst = {els[i]: permut[i] for i in xrange(0, len(els)) if els[i] != permut[i]} if len(clean_subst) == 0: continue transf = SubstitutionTransformation(clean_subst) if Substitutor._is_charge_balanced( transf.apply_transformation(s['structure'])): ts = TransformedStructure( s['structure'], [transf], history=[s['id']], other_parameters={ 'type': 'structure_prediction', 'proba': self._sp.cond_prob_list(permut, els)} ) result.append(ts) transmuter.append_transformed_structures([ts]) if remove_duplicates: transmuter.apply_filter(RemoveDuplicatesFilter( symprec=self._symprec)) return transmuter.transformed_structures
def test_init(self): with warnings.catch_warnings(): warnings.simplefilter("ignore") struct = self.get_structure("LiFePO4") subtrans = SubstitutionTransformation({'Li': {'Li': 0.5}}) adaptor = EnumlibAdaptor(subtrans.apply_transformation(struct), 1, 2) adaptor.run() structures = adaptor.structures self.assertEqual(len(structures), 86) for s in structures: self.assertAlmostEqual( s.composition.get_atomic_fraction(Element("Li")), 0.5 / 6.5) adaptor = EnumlibAdaptor(subtrans.apply_transformation(struct), 1, 2, refine_structure=True) adaptor.run() structures = adaptor.structures self.assertEqual(len(structures), 52) subtrans = SubstitutionTransformation({'Li': {'Li': 0.25}}) adaptor = EnumlibAdaptor(subtrans.apply_transformation(struct), 1, 1, refine_structure=True) adaptor.run() structures = adaptor.structures self.assertEqual(len(structures), 1) for s in structures: self.assertAlmostEqual( s.composition.get_atomic_fraction(Element("Li")), 0.25 / 6.25) # Make sure it works for completely disordered structures. struct = Structure([[10, 0, 0], [0, 10, 0], [0, 0, 10]], [{ 'Fe': 0.5 }], [[0, 0, 0]]) adaptor = EnumlibAdaptor(struct, 1, 2) adaptor.run() self.assertEqual(len(adaptor.structures), 3) # Make sure it works properly when symmetry is broken by ordered sites. struct = self.get_structure("LiFePO4") subtrans = SubstitutionTransformation({'Li': {'Li': 0.25}}) s = subtrans.apply_transformation(struct) # REmove some ordered sites to break symmetry. removetrans = RemoveSitesTransformation([4, 7]) s = removetrans.apply_transformation(s) adaptor = EnumlibAdaptor(s, 1, 1, enum_precision_parameter=0.01) adaptor.run() structures = adaptor.structures self.assertEqual(len(structures), 4) struct = Structure([[3, 0, 0], [0, 3, 0], [0, 0, 3]], [{ "Si": 0.5 }] * 2, [[0, 0, 0], [0.5, 0.5, 0.5]]) adaptor = EnumlibAdaptor(struct, 1, 3, enum_precision_parameter=0.01) adaptor.run() structures = adaptor.structures self.assertEqual(len(structures), 10) struct = Structure.from_file( os.path.join(test_dir, "EnumerateTest.json")) adaptor = EnumlibAdaptor(struct, 1, 1) adaptor.run() structures = adaptor.structures self.assertEqual(len(structures), 2)
def test_init(self): test_dir = os.path.join(os.path.dirname(__file__), "..", "..", "..", 'test_files') parser = CifParser(os.path.join(test_dir, "LiFePO4.cif")) struct = parser.get_structures(False)[0] subtrans = SubstitutionTransformation({'Li': {'Li': 0.5}}) adaptor = EnumlibAdaptor(subtrans.apply_transformation(struct), 1, 2) adaptor.run() structures = adaptor.structures self.assertEqual(len(structures), 86) for s in structures: self.assertAlmostEqual( s.composition.get_atomic_fraction(Element("Li")), 0.5 / 6.5) adaptor = EnumlibAdaptor(subtrans.apply_transformation(struct), 1, 2, refine_structure=True) adaptor.run() structures = adaptor.structures self.assertEqual(len(structures), 52) subtrans = SubstitutionTransformation({'Li': {'Li': 0.25}}) adaptor = EnumlibAdaptor(subtrans.apply_transformation(struct), 1, 1, refine_structure=True) adaptor.run() structures = adaptor.structures self.assertEqual(len(structures), 1) for s in structures: self.assertAlmostEqual( s.composition.get_atomic_fraction(Element("Li")), 0.25 / 6.25) #Make sure it works for completely disordered structures. struct = Structure([[10, 0, 0], [0, 10, 0], [0, 0, 10]], [{ 'Fe': 0.5 }], [[0, 0, 0]]) adaptor = EnumlibAdaptor(struct, 1, 2) adaptor.run() self.assertEqual(len(adaptor.structures), 3) #Make sure it works properly when symmetry is broken by ordered sites. parser = CifParser(os.path.join(test_dir, "LiFePO4.cif")) struct = parser.get_structures(False)[0] subtrans = SubstitutionTransformation({'Li': {'Li': 0.25}}) s = subtrans.apply_transformation(struct) #REmove some ordered sites to break symmetry. removetrans = RemoveSitesTransformation([4, 7]) s = removetrans.apply_transformation(s) adaptor = EnumlibAdaptor(s, 1, 1, enum_precision_parameter=0.01) adaptor.run() structures = adaptor.structures self.assertEqual(len(structures), 4) struct = Structure([[3, 0, 0], [0, 3, 0], [0, 0, 3]], [{ "Si": 0.5 }] * 2, [[0, 0, 0], [0.5, 0.5, 0.5]]) adaptor = EnumlibAdaptor(struct, 1, 3, enum_precision_parameter=0.01) adaptor.run() structures = adaptor.structures self.assertEqual(len(structures), 10)
def pred_from_structures(self, target_species, structures_list, remove_duplicates=True, remove_existing=False): """ performs a structure prediction targeting compounds containing all of the target_species, based on a list of structure (those structures can for instance come from a database like the ICSD). It will return all the structures formed by ionic substitutions with a probability higher than the threshold Notes: If the default probability model is used, input structures must be oxidation state decorated. See AutoOxiStateDecorationTransformation This method does not change the number of species in a structure. i.e if the number of target species is 3, only input structures containing 3 species will be considered. Args: target_species: a list of species with oxidation states e.g., [Specie('Li',1),Specie('Ni',2), Specie('O',-2)] structures_list: a list of dictionnary of the form {'structure':Structure object ,'id':some id where it comes from} the id can for instance refer to an ICSD id. remove_duplicates: if True, the duplicates in the predicted structures will be removed remove_existing: if True, the predicted structures that already exist in the structures_list will be removed Returns: a list of TransformedStructure objects. """ result = [] transmuter = StandardTransmuter([]) if len(list(set(target_species) & set(self.get_allowed_species()))) \ != len(target_species): raise ValueError("the species in target_species are not allowed " + "for the probability model you are using") for permut in itertools.permutations(target_species): for s in structures_list: #check if: species are in the domain, #and the probability of subst. is above the threshold els = s['structure'].composition.elements if len(els) == len(permut) and \ len(list(set(els) & set(self.get_allowed_species()))) == \ len(els) and self._sp.cond_prob_list(permut, els) > \ self._threshold: clean_subst = { els[i]: permut[i] for i in range(0, len(els)) if els[i] != permut[i] } if len(clean_subst) == 0: continue transf = SubstitutionTransformation(clean_subst) if Substitutor._is_charge_balanced( transf.apply_transformation(s['structure'])): ts = TransformedStructure(s['structure'], [transf], history=[{ "source": s['id'] }], other_parameters={ 'type': 'structure_prediction', 'proba': self._sp.cond_prob_list( permut, els) }) result.append(ts) transmuter.append_transformed_structures([ts]) if remove_duplicates: transmuter.apply_filter( RemoveDuplicatesFilter(symprec=self._symprec)) if remove_existing: #Make the list of structures from structures_list that corresponds to the #target species chemsys = list(set([sp.symbol for sp in target_species])) structures_list_target = [ st['structure'] for st in structures_list if Substitutor._is_from_chemical_system( chemsys, st['structure']) ] transmuter.apply_filter( RemoveExistingFilter(structures_list_target, symprec=self._symprec)) return transmuter.transformed_structures
def pred_from_structures(self, target_species, structures_list, remove_duplicates=True): """ performs a structure prediction targeting compounds containing the target_species and based on a list of structure (those structures can for instance come from a database like the ICSD). It will return all the structures formed by ionic substitutions with a probability higher than the threshold Args: target_species: a list of species with oxidation states e.g., [Specie('Li',1),Specie('Ni',2), Specie('O',-2)] structures_list: a list of dictionnary of the form {'structure':Structure object ,'id':some id where it comes from} the id can for instance refer to an ICSD id Returns: a list of TransformedStructure objects. """ result = [] transmuter = StandardTransmuter([]) if len(list(set(target_species) & set(self.get_allowed_species()))) \ != len(target_species): return ValueError("the species in target_species are not allowed" + "for the probability model you are using") for permut in itertools.permutations(target_species): for s in structures_list: #check if: species are in the domain, #and the probability of subst. is above the threshold els = s['structure'].composition.elements if len(els) == len(permut) and \ len(list(set(els) & set(self.get_allowed_species()))) == \ len(els) and self._sp.cond_prob_list(permut, els) > \ self._threshold: clean_subst = { els[i]: permut[i] for i in xrange(0, len(els)) if els[i] != permut[i] } if len(clean_subst) == 0: continue transf = SubstitutionTransformation(clean_subst) if Substitutor._is_charge_balanced( transf.apply_transformation(s['structure'])): ts = TransformedStructure(s['structure'], [transf], history=[s['id']], other_parameters={ 'type': 'structure_prediction', 'proba': self._sp.cond_prob_list( permut, els) }) result.append(ts) transmuter.append_transformed_structures([ts]) if remove_duplicates: transmuter.apply_filter( RemoveDuplicatesFilter(symprec=self._symprec)) return transmuter.transformed_structures
def pred_from_structures(self, target_species, structures_list, remove_duplicates=True, remove_existing=False): """ performs a structure prediction targeting compounds containing all of the target_species, based on a list of structure (those structures can for instance come from a database like the ICSD). It will return all the structures formed by ionic substitutions with a probability higher than the threshold Notes: If the default probability model is used, input structures must be oxidation state decorated. This method does not change the number of species in a structure. i.e if the number of target species is 3, only input structures containing 3 species will be considered. Args: target_species: a list of species with oxidation states e.g., [Specie('Li',1),Specie('Ni',2), Specie('O',-2)] structures_list: a list of dictionnary of the form {'structure':Structure object ,'id':some id where it comes from} the id can for instance refer to an ICSD id. remove_duplicates: if True, the duplicates in the predicted structures will be removed remove_existing: if True, the predicted structures that already exist in the structures_list will be removed Returns: a list of TransformedStructure objects. """ result = [] transmuter = StandardTransmuter([]) if len(list(set(target_species) & set(self.get_allowed_species()))) != len(target_species): raise ValueError( "the species in target_species are not allowed" + "for the probability model you are using" ) for permut in itertools.permutations(target_species): for s in structures_list: # check if: species are in the domain, # and the probability of subst. is above the threshold els = s["structure"].composition.elements if ( len(els) == len(permut) and len(list(set(els) & set(self.get_allowed_species()))) == len(els) and self._sp.cond_prob_list(permut, els) > self._threshold ): clean_subst = {els[i]: permut[i] for i in xrange(0, len(els)) if els[i] != permut[i]} if len(clean_subst) == 0: continue transf = SubstitutionTransformation(clean_subst) if Substitutor._is_charge_balanced(transf.apply_transformation(s["structure"])): ts = TransformedStructure( s["structure"], [transf], history=[{"source": s["id"]}], other_parameters={ "type": "structure_prediction", "proba": self._sp.cond_prob_list(permut, els), }, ) result.append(ts) transmuter.append_transformed_structures([ts]) if remove_duplicates: transmuter.apply_filter(RemoveDuplicatesFilter(symprec=self._symprec)) if remove_existing: # Make the list of structures from structures_list that corresponds to the # target species chemsys = list(set([sp.symbol for sp in target_species])) structures_list_target = [ st["structure"] for st in structures_list if Substitutor._is_from_chemical_system(chemsys, st["structure"]) ] transmuter.apply_filter(RemoveExistingFilter(structures_list_target, symprec=self._symprec)) return transmuter.transformed_structures
from pymatgen import Structure from pymatgen.transformations.standard_transformations import SubstitutionTransformation from pymatgen.transformations.standard_transformations import OrderDisorderedStructureTransformation structure = Structure.from_file("POSCAR") substitution = SubstitutionTransformation({"Nb3+": {"Nb3+":0.5, "Fe3+":0.5}}) result = substitution.apply_transformation(structure) order = OrderDisorderedStructureTransformation(algo=2) ResultOrder = order.apply_transformation(result, return_ranked_list=True) for i, item in enumerate(ResultOrder): item['structure'].to(filename="POSCAR{:02d}".format(i)) #ResultOrder[0]['structure'].to(filename="POSCAR1")