def test_undo_and_redo_last_change(self): trans = [ SubstitutionTransformation({"Li": "Na"}), SubstitutionTransformation({"Fe": "Mn"}) ] ts = TransformedStructure(self.structure, trans) self.assertEqual("NaMnPO4", ts.final_structure.composition.reduced_formula) ts.undo_last_change() self.assertEqual("NaFePO4", ts.final_structure.composition.reduced_formula) ts.undo_last_change() self.assertEqual("LiFePO4", ts.final_structure.composition.reduced_formula) self.assertRaises(IndexError, ts.undo_last_change) ts.redo_next_change() self.assertEqual("NaFePO4", ts.final_structure.composition.reduced_formula) ts.redo_next_change() self.assertEqual("NaMnPO4", ts.final_structure.composition.reduced_formula) self.assertRaises(IndexError, ts.redo_next_change) #Make sure that this works with filters. f3 = ContainsSpecieFilter(['O2-'], strict_compare=True, AND=False) ts.append_filter(f3) ts.undo_last_change() ts.redo_next_change()
def test_transmuter(self): tsc = PoscarTransmuter.from_filenames( [os.path.join(self.TEST_FILES_DIR, "POSCAR")]) tsc.append_transformation(RemoveSpeciesTransformation("O")) self.assertEqual(len(tsc[0].final_structure), 8) tsc.append_transformation( SubstitutionTransformation({ "Fe": { "Fe2+": 0.25, "Mn3+": 0.75 }, "P": "P5+" })) tsc.append_transformation(OrderDisorderedStructureTransformation(), extend_collection=50) self.assertEqual(len(tsc), 4) t = SuperTransformation([ SubstitutionTransformation({"Fe2+": "Mg2+"}), SubstitutionTransformation({"Fe2+": "Zn2+"}), SubstitutionTransformation({"Fe2+": "Be2+"}), ]) tsc.append_transformation(t, extend_collection=True) self.assertEqual(len(tsc), 12) for x in tsc: # should be 4 trans + starting structure self.assertEqual( len(x), 5, "something might be wrong with the number of transformations in the history", ) # test the filter tsc.apply_filter( ContainsSpecieFilter(["Zn2+", "Be2+", "Mn4+"], strict_compare=True, AND=False)) self.assertEqual(len(tsc), 8) self.assertEqual( tsc.transformed_structures[0].as_dict()["history"][-1]["@class"], "ContainsSpecieFilter", ) tsc.apply_filter(ContainsSpecieFilter(["Be2+"])) self.assertEqual(len(tsc), 4) # Test set_parameter and add_tag. tsc.set_parameter("para1", "hello") self.assertEqual( tsc.transformed_structures[0].as_dict()["other_parameters"] ["para1"], "hello", ) tsc.add_tags(["world", "universe"]) self.assertEqual( tsc.transformed_structures[0].as_dict()["other_parameters"] ["tags"], ["world", "universe"], )
def test_apply_transformation(self): tl = [SubstitutionTransformation({"Li+": "Na+"}), SubstitutionTransformation({"Li+": "K+"})] t = SuperTransformation(tl) coords = list() coords.append([0, 0, 0]) coords.append([0.375, 0.375, 0.375]) coords.append([.5, .5, .5]) coords.append([0.875, 0.875, 0.875]) coords.append([0.125, 0.125, 0.125]) coords.append([0.25, 0.25, 0.25]) coords.append([0.625, 0.625, 0.625]) coords.append([0.75, 0.75, 0.75]) lattice = Lattice([[3.8401979337, 0.00, 0.00], [1.9200989668, 3.3257101909, 0.00], [0.00, -2.2171384943, 3.1355090603]]) struct = Structure(lattice, ["Li+", "Li+", "Li+", "Li+", "Li+", "Li+", "O2-", "O2-"], coords) s = t.apply_transformation(struct, return_ranked_list=True) for s_and_t in s: self.assertEqual(s_and_t['transformation'] .apply_transformation(struct), s_and_t['structure'])
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(PymatgenTest.TEST_FILES_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 test_apply_transformation(self): enum_trans = EnumerateStructureTransformation(refine_structure=True) enum_trans2 = EnumerateStructureTransformation(refine_structure=True, sort_criteria="nsites") 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.assertEqual(len(alls), expected_ans[i]) self.assertIsInstance(trans.apply_transformation(s), Structure) for ss in alls: self.assertIn("energy", ss) alls = enum_trans2.apply_transformation(s, 100) self.assertEqual(len(alls), expected_ans[i]) self.assertIsInstance(trans.apply_transformation(s), Structure) for ss in alls: self.assertIn("num_sites", ss) # 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.assertEqual(len(alls), 3) self.assertIsInstance(trans.apply_transformation(s), Structure) for s in alls: self.assertNotIn("energy", s)
def test_transmuter(self): tsc = PoscarTransmuter.from_filenames( [os.path.join(test_dir, "POSCAR")]) tsc.append_transformation(RemoveSpeciesTransformation('O')) self.assertEqual(len(tsc[0].final_structure), 8) tsc.append_transformation( SubstitutionTransformation({ "Fe": { "Fe2+": 0.25, "Mn3+": .75 }, "P": "P5+" })) tsc.append_transformation(OrderDisorderedStructureTransformation(), extend_collection=50) self.assertEqual(len(tsc), 4) t = SuperTransformation([ SubstitutionTransformation({"Fe2+": "Mg2+"}), SubstitutionTransformation({"Fe2+": "Zn2+"}), SubstitutionTransformation({"Fe2+": "Be2+"}) ]) tsc.append_transformation(t, extend_collection=True) self.assertEqual(len(tsc), 12) for x in tsc: self.assertEqual( len(x), 5, 'something might be wrong with the number of transformations in the history' ) #should be 4 trans + starting structure #test the filter tsc.apply_filter( ContainsSpecieFilter(['Zn2+', 'Be2+', 'Mn4+'], strict_compare=True, AND=False)) self.assertEqual(len(tsc), 8) self.assertEqual( tsc.get_transformed_structures()[0].as_dict()['history'][-1] ['@class'], 'ContainsSpecieFilter') tsc.apply_filter(ContainsSpecieFilter(['Be2+'])) self.assertEqual(len(tsc), 4) #Test set_parameter and add_tag. tsc.set_parameter("para1", "hello") self.assertEqual( tsc.transformed_structures[0].as_dict()['other_parameters'] ['para1'], 'hello') tsc.add_tags(["world", "universe"]) self.assertEqual( tsc.transformed_structures[0].as_dict()['other_parameters'] ['tags'], ["world", "universe"])
def apply_substitution(orig_struct, sub): """Applies a substitution transformation to the structure. Args: orig_struct (Structure): A Pymatgen Structure of the original structure. sub (dict): {thing_to_sub: {thing_to_sub: new_am1, new_thing: new_amt2}} Returns: A now-substituted structure prior to ordering. """ subber = SubstitutionTransformation(sub) return subber.apply_transformation(orig_struct)
def apply_transformation(self, structure): charge = structure.charge specie = get_el_sp(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 test_apply_transformation(self): t = SubstitutionTransformation({"Li+": "Na+", "O2-": "S2-"}) coords = [] coords.append([0, 0, 0]) coords.append([0.75, 0.75, 0.75]) coords.append([0.5, 0.5, 0.5]) coords.append([0.25, 0.25, 0.25]) lattice = Lattice([ [3.8401979337, 0.00, 0.00], [1.9200989668, 3.3257101909, 0.00], [0.00, -2.2171384943, 3.1355090603], ]) struct = Structure(lattice, ["Li+", "Li+", "O2-", "O2-"], coords) s = t.apply_transformation(struct) self.assertEqual(s.composition.formula, "Na2 S2")
def run_task(self, fw_spec): db = SPStructuresMongoAdapter.auto_load() tstructs = [] species = fw_spec['species'] t = fw_spec['threshold'] for p in SubstitutionPredictor(threshold=t).list_prediction(species): subs = p['substitutions'] if len(set(subs.values())) < len(species): continue st = SubstitutionTransformation(subs) target = map(str, subs.keys()) for snl in db.get_snls(target): ts = TransformedStructure.from_snl(snl) ts.append_transformation(st) if ts.final_structure.charge == 0: tstructs.append(ts) transmuter = StandardTransmuter(tstructs) f = RemoveDuplicatesFilter(structure_matcher=StructureMatcher( comparator=ElementComparator(), primitive_cell=False)) transmuter.apply_filter(f) results = [] for ts in transmuter.transformed_structures: results.append(ts.to_snl([]).to_dict) submissions = SPSubmissionsMongoAdapter.auto_load() submissions.insert_results(fw_spec['submission_id'], results)
def setUp(self): structure_dict = { "lattice": {"a": 4.754150115, "volume": 302.935463898643, "c": 10.462573348, "b": 6.090300362, "matrix": [[4.754150115, 0.0, 0.0], [0.0, 6.090300362, 0.0], [0.0, 0.0, 10.462573348]], "alpha": 90.0, "beta": 90.0, "gamma": 90.0}, "sites": [{"occu": 1, "abc": [0.0, 0.0, 0.0], "xyz": [0.0, 0.0, 0.0], "species": [{"occu": 1, "element": "Li"}], "label": "Li"}, {"occu": 1, "abc": [0.5000010396179928, 0.0, 0.5000003178950235], "xyz": [2.37708, 0.0, 5.23129], "species": [{"occu": 1, "element": "Li"}], "label": "Li"}, {"occu": 1, "abc": [0.0, 0.49999997028061194, 0.0], "xyz": [0.0, 3.04515, 0.0], "species": [{"occu": 1, "element": "Li"}], "label": "Li"}, {"occu": 1, "abc": [0.5000010396179928, 0.49999997028061194, 0.5000003178950235], "xyz": [2.37708, 3.04515, 5.23129], "species": [{"occu": 1, "element": "Li"}], "label": "Li"}, {"occu": 1, "abc": [0.7885825876997996, 0.5473161916279229, 0.3339168944194627], "xyz": [3.74904, 3.33332, 3.4936300000000005], "species": [{"occu": 1, "element": "O"}], "label": "O"}, {"occu": 1, "abc": [0.2114173881108085, 0.452683748933301, 0.6660827855827808], "xyz": [1.00511, 2.75698, 6.968940000000001], "species": [{"occu": 1, "element": "O"}], "label": "O"}, {"occu": 1, "abc": [0.7114184277288014, 0.5473161916279229, 0.8339172123144861], "xyz": [3.38219, 3.33332, 8.72492], "species": [{"occu": 1, "element": "O"}], "label": "O"}, {"occu": 1, "abc": [0.7885825876997996, 0.9526820772587701, 0.3339168944194627], "xyz": [3.74904, 5.8021199999999995, 3.4936300000000005], "species": [{"occu": 1, "element": "O"}], "label": "O"}, {"occu": 1, "abc": [0.28858365150718424, 0.047317863302453654, 0.16608342347556082], "xyz": [1.37197, 0.28818, 1.73766], "species": [{"occu": 1, "element": "O"}], "label": "O"}, {"occu": 1, "abc": [0.7440972443925447, 0.25000080611787734, 0.09613791622232937], "xyz": [3.537549999999999, 1.52258, 1.00585], "species": [{"occu": 1, "element": "O"}], "label": "O"}, {"occu": 1, "abc": [0.28858365150718424, 0.452683748933301, 0.16608342347556082], "xyz": [1.37197, 2.75698, 1.73766], "species": [{"occu": 1, "element": "O"}], "label": "O"}, {"occu": 1, "abc": [0.2114173881108085, 0.047317863302453654, 0.6660827855827808], "xyz": [1.00511, 0.28818, 6.968940000000001], "species": [{"occu": 1, "element": "O"}], "label": "O"}, {"occu": 1, "abc": [0.2559006279926859, 0.7499991344433464, 0.9038627195677177], "xyz": [1.21659, 4.56772, 9.45673], "species": [{"occu": 1, "element": "O"}], "label": "O"}, {"occu": 1, "abc": [0.7559016676106785, 0.25000080611787734, 0.5961372783295493], "xyz": [3.5936699999999986, 1.52258, 6.2371300000000005], "species": [{"occu": 1, "element": "O"}], "label": "O"}, {"occu": 1, "abc": [0.7939989080466804, 0.7499991344433464, 0.5421304884886912], "xyz": [3.77479, 4.56772, 5.67208], "species": [{"occu": 1, "element": "O"}], "label": "O"}, {"occu": 1, "abc": [0.24409830819992942, 0.7499991344433464, 0.40386240167269416], "xyz": [1.16048, 4.56772, 4.22544], "species": [{"occu": 1, "element": "O"}], "label": "O"}, {"occu": 1, "abc": [0.7060021073819206, 0.7499991344433464, 0.04213017059366761], "xyz": [3.35644, 4.56772, 0.44079000000000007], "species": [{"occu": 1, "element": "O"}], "label": "O"}, {"occu": 1, "abc": [0.2939978684286875, 0.25000080611787734, 0.9578695094085758], "xyz": [1.3977099999999996, 1.52258, 10.02178], "species": [{"occu": 1, "element": "O"}], "label": "O"}, {"occu": 1, "abc": [0.20600106776392774, 0.25000080611787734, 0.4578701473013559], "xyz": [0.9793599999999998, 1.52258, 4.7905], "species": [{"occu": 1, "element": "O"}], "label": "O"}, {"occu": 1, "abc": [0.7114184277288014, 0.9526820772587701, 0.8339172123144861], "xyz": [3.38219, 5.8021199999999995, 8.72492], "species": [{"occu": 1, "element": "O"}], "label": "O"}, {"occu": 1, "abc": [0.5793611756830275, 0.7499991344433464, 0.9051119342269868], "xyz": [2.75437, 4.56772, 9.4698], "species": [{"occu": 1, "element": "P"}], "label": "P"}, {"occu": 1, "abc": [0.9206377363201961, 0.7499991344433464, 0.40511161633196324], "xyz": [4.37685, 4.56772, 4.23851], "species": [{"occu": 1, "element": "P"}], "label": "P"}, {"occu": 1, "abc": [0.42063880012758065, 0.25000080611787734, 0.09488774577525667], "xyz": [1.9997799999999994, 1.52258, 0.99277], "species": [{"occu": 1, "element": "P"}], "label": "P"}, {"occu": 1, "abc": [0.07936223949041206, 0.25000080611787734, 0.5948880636702801], "xyz": [0.3773, 1.52258, 6.22406], "species": [{"occu": 1, "element": "P"}], "label": "P"}, {"occu": 1, "abc": [0.021860899947623972, 0.7499991344433464, 0.7185507570598875], "xyz": [0.10393, 4.56772, 7.517890000000001], "species": [{"occu": 1, "element": "Fe"}], "label": "Fe"}, {"occu": 1, "abc": [0.478135932819614, 0.7499991344433464, 0.21855043916486389], "xyz": [2.27313, 4.56772, 2.2866], "species": [{"occu": 1, "element": "Fe"}], "label": "Fe"}, {"occu": 1, "abc": [0.9781369724376069, 0.25000080611787734, 0.2814489229423561], "xyz": [4.65021, 1.52258, 2.9446800000000004], "species": [{"occu": 1, "element": "Fe"}], "label": "Fe"}, {"occu": 1, "abc": [0.5218619395656168, 0.25000080611787734, 0.7814492408373795], "xyz": [2.48101, 1.52258, 8.17597], "species": [{"occu": 1, "element": "Fe"}], "label": "Fe"}]} structure = Structure.from_dict(structure_dict) self.structure = structure trans = [SubstitutionTransformation({"Li": "Na"})] self.trans = TransformedStructure(structure, trans)
def test_transmute(self): if QeTransmuterTest.qe is None: self.skipTest("No MongoDB present") crit = {} trans = [ SubstitutionTransformation({"Zn": "Mg"}), OxidationStateDecorationTransformation({ "B": 3, "O": -2, "Mg": 2, "Tb": 3 }), PartialRemoveSpecieTransformation( "Mg2+", 0.5, algo=PartialRemoveSpecieTransformation.ALGO_COMPLETE) ] self.qep = QeTransmuter(QeTransmuterTest.qe, crit, trans, extend_collection=10) trans_structures = self.qep.transformed_structures self.assertEqual(len(trans_structures), 3) for s in trans_structures: self.assertEqual(s.final_structure.composition.reduced_formula, "Tb2Mg(BO2)10")
def test_append_transformation(self): t = SubstitutionTransformation({"Fe": "Mn"}) self.trans.append_transformation(t) self.assertEqual( "NaMnPO4", self.trans.final_structure.composition.reduced_formula) self.assertEqual(len(self.trans.structures), 3) coords = list() coords.append([0, 0, 0]) coords.append([0.75, 0.5, 0.75]) lattice = [ [3.8401979337, 0.00, 0.00], [1.9200989668, 3.3257101909, 0.00], [0.00, -2.2171384943, 3.1355090603], ] struct = Structure(lattice, ["Si4+", "Si4+"], coords) ts = TransformedStructure(struct, []) ts.append_transformation( SupercellTransformation.from_scaling_factors(2, 1, 1)) alt = ts.append_transformation( PartialRemoveSpecieTransformation( "Si4+", 0.5, algo=PartialRemoveSpecieTransformation.ALGO_COMPLETE), 5, ) self.assertEqual(len(alt), 2)
def test_from_dict(self): d = json.load(open(os.path.join(PymatgenTest.TEST_FILES_DIR, "transformations.json"), "r")) d["other_parameters"] = {"tags": ["test"]} ts = TransformedStructure.from_dict(d) ts.other_parameters["author"] = "Will" ts.append_transformation(SubstitutionTransformation({"Fe": "Mn"})) self.assertEqual("MnPO4", ts.final_structure.composition.reduced_formula) self.assertEqual(ts.other_parameters, {"author": "Will", "tags": ["test"]})
def test_init(self): trans = [] trans.append(SubstitutionTransformation({"Fe": "Mn", "Fe2+": "Mn2+"})) tsc = CifTransmuter.from_filenames([os.path.join(self.TEST_FILES_DIR, "MultiStructure.cif")], trans) self.assertEqual(len(tsc), 2) expected_ans = set(["Mn", "O", "Li", "P"]) for s in tsc: els = set([el.symbol for el in s.final_structure.composition.elements]) self.assertEqual(expected_ans, els)
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 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 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 test_from_dict(self): d = json.load(open(os.path.join(test_dir, 'transformations.json'), 'r')) d['other_parameters'] = {'tags': ['test']} ts = TransformedStructure.from_dict(d) ts.other_parameters['author'] = 'Will' ts.append_transformation(SubstitutionTransformation({"Fe": "Mn"})) self.assertEqual("MnPO4", ts.final_structure.composition.reduced_formula) self.assertEqual(ts.other_parameters, {'author': 'Will', 'tags': ['test']})
def test_init(self): trans = [] trans.append(SubstitutionTransformation({"Fe": "Mn"})) tsc = PoscarTransmuter.from_filenames([ os.path.join(self.TEST_FILES_DIR, "POSCAR"), os.path.join(self.TEST_FILES_DIR, "POSCAR") ], trans) self.assertEqual(len(tsc), 2) expected_ans = {"Mn", "O", "P"} for s in tsc: els = {el.symbol for el in s.final_structure.composition.elements} self.assertEqual(expected_ans, els)
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_fractional_substitution(self): t = SubstitutionTransformation({ "Li+": "Na+", "O2-": { "S2-": 0.5, "Se2-": 0.5 } }) # test the to and from dict on the nested dictionary t = SubstitutionTransformation.from_dict(t.as_dict()) coords = [] coords.append([0, 0, 0]) coords.append([0.75, 0.75, 0.75]) coords.append([0.5, 0.5, 0.5]) coords.append([0.25, 0.25, 0.25]) lattice = Lattice([ [3.8401979337, 0.00, 0.00], [1.9200989668, 3.3257101909, 0.00], [0.00, -2.2171384943, 3.1355090603], ]) struct = Structure(lattice, ["Li+", "Li+", "O2-", "O2-"], coords) s = t.apply_transformation(struct) self.assertEqual(s.composition.formula, "Na2 Se1 S1")
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, 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_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.assertEqual(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.assertEqual(len(alls), 3) self.assertIsInstance(trans.apply_transformation(s), Structure) for s in alls: self.assertNotIn("energy", s)
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
key=lambda item: item[1]) } tar_d = { k: v for k, v in sorted(Composition(composition).as_dict().items(), key=lambda item: item[1]) } ori_t = tuple(ori_d) tar_t = tuple(tar_d) # print('original composition: ' + str(ori_t)) # print('target composition: ' + str(tar_t)) replace_syntax = find_diff(ori_t, tar_t) trans = [] for syn in replace_syntax: trans.append(SubstitutionTransformation(syn)) # os.system("cd ./CIF") # print(os.getcwd()) try: transmuter = CifTransmuter.from_filenames( ['./renamed_cif/' + str(mpid) + ".cif"], trans) structures = transmuter.transformed_structures #print(structures[0].final_structure) w = CifWriter(structures[0].final_structure) w.write_file("./inverse_design_target/" + name) except: continue print('success!!') # parser = CifParser('./file_name.cif')
from matplotlib.testing.compare import compare_images from pymatgen.core import Lattice, Structure from pymatgen.transformations.standard_transformations import SubstitutionTransformation from pymatviz.struct_vis import plot_structure_2d from .conftest import save_reference_img os.makedirs(fixt_dir := "tests/fixtures/struct_vis", exist_ok=True) latt = Lattice.cubic(5) struct = Structure(latt, ["Fe", "O"], [[0, 0, 0], [0.5, 0.5, 0.5]]) disord_struct: Structure = SubstitutionTransformation({ "Fe": { "Fe": 0.75, "C": 0.25 } }).apply_transformation(struct) @pytest.mark.parametrize("radii", [0.5, 1.2]) @pytest.mark.parametrize("rot", ["0x,0y,0z", "10x,-10y,0z"]) @pytest.mark.parametrize("labels", [True, False, {"P": "Phosphor"}]) def test_plot_structure_2d(radii, rot, labels, tmpdir): # set explicit size to avoid ImageComparisonFailure in CI: sizes do not match # expected (700, 1350, 3), actual (480, 640, 3) plt.figure(figsize=(5, 5)) ax = plot_structure_2d(disord_struct, atomic_radii=radii, rotation=rot,
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. """ target_species = get_el_sp(target_species) 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 setUp(self): structure = PymatgenTest.get_structure("LiFePO4") self.structure = structure trans = [SubstitutionTransformation({"Li": "Na"})] self.trans = TransformedStructure(structure, trans)
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): 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)
from pymatgen.io.cif import CifParser from pymatgen.transformations.standard_transformations import RemoveSpeciesTransformation from pymatgen.transformations.standard_transformations import SubstitutionTransformation if __name__ == '__main__': # Read in a LiFePO4 structure from a cif. parser = CifParser('/Users/derek/Downloads/LiFePO4_mp-19017_computed.cif') struct = parser.get_structures()[0] t = RemoveSpeciesTransformation(["Li"]) modified_structure = t.apply_transformation(struct) t2 = SubstitutionTransformation({"Li", "Na"}) print(modified_structure)
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")