def molecule_to_schrodinger_struct(molecule: Molecule): """ Convert a pymatgen Molecule object to a Schrodinger Structure object Args: molecule (pymatgen.core.structure.Molecule): Molecule to be converted Returns: struct: schrodinger.structure.Structure object """ # First need to convert molecule to file to use StructureReader file_suffix = random.randint(1, 100000000) molecule.to('sdf', "temp_conversion{}.sdf".format(file_suffix)) reader = StructureReader("temp_conversion{}.sdf".format(file_suffix)) # Assume only one structure (should be the case for a single SDF file) struct = [r for r in reader][0] for atom in struct.atom: atom.formal_charge = 0 struct.atom[1].formal_charge = molecule.charge Path("temp_conversion{}.sdf".format(file_suffix)) return struct
def write_structure(self, candidate, filename="result.xyz"): fragments_coords = self.decode_solution(candidate) mol_elements = self.get_mol_species(self.molecule) fragments_elements = [] for frag, num_frag in zip(self.fragments, self.nums_fragments): fragments_elements.extend([self.get_mol_species(frag)] * num_frag) species = [] coords_au = [] species.extend(mol_elements) coords_au.extend(self.mol_coords) for elements, c in zip(fragments_elements, fragments_coords): species.extend(elements) coords_au.extend(c) coords_ang = [[x / AtomicRadiusUtils.angstrom2au for x in c] for c in coords_au] pmg_mol = Molecule(species, coords_ang) file_format = os.path.splitext(filename)[1][1:] pmg_mol.to(file_format, filename)
class MoleculeTest(PymatgenTest): def setUp(self): coords = [[0.000000, 0.000000, 0.000000], [0.000000, 0.000000, 1.089000], [1.026719, 0.000000, -0.363000], [-0.513360, -0.889165, -0.363000], [-0.513360, 0.889165, -0.363000]] self.mol = Molecule(["C", "H", "H", "H", "H"], coords) def test_mutable_sequence_methods(self): s = self.mol s[1] = ("F", [0.5, 0.5, 0.5]) self.assertEqual(s.formula, "H3 C1 F1") self.assertArrayAlmostEqual(s[1].coords, [0.5, 0.5, 0.5]) s.reverse() self.assertEqual(s[0].specie, Element("H")) self.assertArrayAlmostEqual(s[0].coords, [-0.513360, 0.889165, -0.363000]) del s[1] self.assertEqual(s.formula, "H2 C1 F1") s[3] = "N", [0,0,0], {"charge": 4} self.assertEqual(s.formula, "H2 N1 F1") self.assertEqual(s[3].charge, 4) def test_insert_remove_append(self): mol = self.mol mol.insert(1, "O", [0.5, 0.5, 0.5]) self.assertEqual(mol.formula, "H4 C1 O1") del mol[2] self.assertEqual(mol.formula, "H3 C1 O1") mol.set_charge_and_spin(0) self.assertEqual(mol.spin_multiplicity, 2) mol.append("N", [1, 1, 1]) self.assertEqual(mol.formula, "H3 C1 N1 O1") self.assertRaises(TypeError, dict, [(mol, 1)]) mol.remove_sites([0, 1]) self.assertEqual(mol.formula, "H3 N1") def test_translate_sites(self): self.mol.translate_sites([0, 1], [0.5, 0.5, 0.5]) self.assertArrayEqual(self.mol.cart_coords[0], [0.5, 0.5, 0.5]) def test_replace(self): self.mol[0] = "Ge" self.assertEqual(self.mol.formula, "Ge1 H4") self.mol.replace_species({Element("Ge"): {Element("Ge"): 0.5, Element("Si"): 0.5}}) self.assertEqual(self.mol.formula, "Si0.5 Ge0.5 H4") #this should change the .5Si .5Ge sites to .75Si .25Ge self.mol.replace_species({Element("Ge"): {Element("Ge"): 0.5, Element("Si"): 0.5}}) self.assertEqual(self.mol.formula, "Si0.75 Ge0.25 H4") d = 0.1 pre_perturbation_sites = self.mol.sites[:] self.mol.perturb(distance=d) post_perturbation_sites = self.mol.sites for i, x in enumerate(pre_perturbation_sites): self.assertAlmostEqual(x.distance(post_perturbation_sites[i]), d, 3, "Bad perturbation distance") def test_add_site_property(self): self.mol.add_site_property("charge", [4.1, -2, -2, -2, -2]) self.assertEqual(self.mol[0].charge, 4.1) self.assertEqual(self.mol[1].charge, -2) self.mol.add_site_property("magmom", [3, 2, 2, 2, 2]) self.assertEqual(self.mol[0].charge, 4.1) self.assertEqual(self.mol[0].magmom, 3) def test_to_from_dict(self): d = self.mol.as_dict() mol2 = Molecule.from_dict(d) self.assertEqual(type(mol2), Molecule) def test_apply_operation(self): op = SymmOp.from_axis_angle_and_translation([0, 0, 1], 90) self.mol.apply_operation(op) self.assertArrayAlmostEqual(self.mol[2].coords, [0.000000, 1.026719, -0.363000]) def test_substitute(self): coords = [[0.000000, 0.000000, 1.08], [0.000000, 0.000000, 0.000000], [1.026719, 0.000000, -0.363000], [-0.513360, -0.889165, -0.363000], [-0.513360, 0.889165, -0.363000]] sub = Molecule(["X", "C", "H", "H", "H"], coords) self.mol.substitute(1, sub) self.assertAlmostEqual(self.mol.get_distance(0, 4), 1.54) f = Molecule(["X", "F"], [[0, 0, 0], [0, 0, 1.11]]) self.mol.substitute(2, f) self.assertAlmostEqual(self.mol.get_distance(0, 7), 1.35) oh = Molecule(["X", "O", "H"], [[0, 0.780362, -.456316], [0, 0, .114079], [0, -.780362, -.456316]]) self.mol.substitute(1, oh) self.assertAlmostEqual(self.mol.get_distance(0, 7), 1.43) self.mol.substitute(3, "methyl") self.assertEqual(self.mol.formula, "H7 C3 O1 F1") coords = [[0.00000, 1.40272, 0.00000], [0.00000, 2.49029, 0.00000], [-1.21479, 0.70136, 0.00000], [-2.15666, 1.24515, 0.00000], [-1.21479, -0.70136, 0.00000], [-2.15666, -1.24515, 0.00000], [0.00000, -1.40272, 0.00000], [0.00000, -2.49029, 0.00000], [1.21479, -0.70136, 0.00000], [2.15666, -1.24515, 0.00000], [1.21479, 0.70136, 0.00000], [2.15666, 1.24515, 0.00000]] benzene = Molecule(["C", "H", "C", "H", "C", "H", "C", "H", "C", "H", "C", "H"], coords) benzene.substitute(1, sub) self.assertEqual(benzene.formula, "H8 C7") #Carbon attached should be in plane. self.assertAlmostEqual(benzene[11].coords[2], 0) def test_to_from_file_string(self): for fmt in ["xyz", "json", "g03"]: s = self.mol.to(fmt=fmt) self.assertIsNotNone(s) m = Molecule.from_str(s, fmt=fmt) self.assertEqual(m, self.mol) self.assertIsInstance(m, Molecule) self.mol.to(filename="CH4_testing.xyz") self.assertTrue(os.path.exists("CH4_testing.xyz")) os.remove("CH4_testing.xyz")
class IMoleculeTest(PymatgenTest): def setUp(self): coords = [[0.000000, 0.000000, 0.000000], [0.000000, 0.000000, 1.089000], [1.026719, 0.000000, -0.363000], [-0.513360, -0.889165, -0.363000], [-0.513360, 0.889165, -0.363000]] self.coords = coords self.mol = Molecule(["C", "H", "H", "H", "H"], coords) def test_bad_molecule(self): coords = [[0.000000, 0.000000, 0.000000], [0.000000, 0.000000, 1.089000], [1.026719, 0.000000, -0.363000], [-0.513360, -0.889165, -0.363000], [-0.513360, 0.889165, -0.363000], [-0.513360, 0.889165, -0.36301]] self.assertRaises(StructureError, Molecule, ["C", "H", "H", "H", "H", "H"], coords, validate_proximity=True) def test_get_angle_dihedral(self): self.assertAlmostEqual(self.mol.get_angle(1, 0, 2), 109.47122144618737) self.assertAlmostEqual(self.mol.get_angle(3, 1, 2), 60.00001388659683) self.assertAlmostEqual(self.mol.get_dihedral(0, 1, 2, 3), - 35.26438851071765) coords = list() coords.append([0, 0, 0]) coords.append([0, 0, 1]) coords.append([0, 1, 1]) coords.append([1, 1, 1]) self.mol2 = Molecule(["C", "O", "N", "S"], coords) self.assertAlmostEqual(self.mol2.get_dihedral(0, 1, 2, 3), -90) def test_get_covalent_bonds(self): self.assertEqual(len(self.mol.get_covalent_bonds()), 4) def test_properties(self): self.assertEqual(len(self.mol), 5) self.assertTrue(self.mol.is_ordered) self.assertEqual(self.mol.formula, "H4 C1") def test_repr_str(self): ans = """Full Formula (H4 C1) Reduced Formula: H4C Charge = 0, Spin Mult = 1 Sites (5) 0 C 0.000000 0.000000 0.000000 1 H 0.000000 0.000000 1.089000 2 H 1.026719 0.000000 -0.363000 3 H -0.513360 -0.889165 -0.363000 4 H -0.513360 0.889165 -0.363000""" self.assertEqual(self.mol.__str__(), ans) ans = """Molecule Summary Site: C (0.0000, 0.0000, 0.0000) Site: H (0.0000, 0.0000, 1.0890) Site: H (1.0267, 0.0000, -0.3630) Site: H (-0.5134, -0.8892, -0.3630) Site: H (-0.5134, 0.8892, -0.3630)""" self.assertEqual(repr(self.mol), ans) def test_site_properties(self): propertied_mol = Molecule(["C", "H", "H", "H", "H"], self.coords, site_properties={'magmom': [0.5, -0.5, 1, 2, 3]}) self.assertEqual(propertied_mol[0].magmom, 0.5) self.assertEqual(propertied_mol[1].magmom, -0.5) def test_get_boxed_structure(self): s = self.mol.get_boxed_structure(9, 9, 9) # C atom should be in center of box. self.assertArrayAlmostEqual(s[4].frac_coords, [0.50000001, 0.5, 0.5]) self.assertArrayAlmostEqual(s[1].frac_coords, [0.6140799, 0.5, 0.45966667]) self.assertRaises(ValueError, self.mol.get_boxed_structure, 1, 1, 1) s2 = self.mol.get_boxed_structure(5, 5, 5, (2, 3, 4)) self.assertEqual(len(s2), 24 * 5) self.assertEqual(s2.lattice.abc, (10, 15, 20)) def test_get_distance(self): self.assertAlmostEqual(self.mol.get_distance(0, 1), 1.089) def test_get_neighbors(self): nn = self.mol.get_neighbors(self.mol[0], 1) self.assertEqual(len(nn), 0) nn = self.mol.get_neighbors(self.mol[0], 2) self.assertEqual(len(nn), 4) def test_get_neighbors_in_shell(self): nn = self.mol.get_neighbors_in_shell([0, 0, 0], 0, 1) self.assertEqual(len(nn), 1) nn = self.mol.get_neighbors_in_shell([0, 0, 0], 1, 0.9) self.assertEqual(len(nn), 4) nn = self.mol.get_neighbors_in_shell([0, 0, 0], 1, 0.9) self.assertEqual(len(nn), 4) nn = self.mol.get_neighbors_in_shell([0, 0, 0], 2, 0.1) self.assertEqual(len(nn), 0) def test_get_dist_matrix(self): ans = [[0.0, 1.089, 1.08899995636, 1.08900040717, 1.08900040717], [1.089, 0.0, 1.77832952654, 1.7783298026, 1.7783298026], [1.08899995636, 1.77832952654, 0.0, 1.77833003783, 1.77833003783], [1.08900040717, 1.7783298026, 1.77833003783, 0.0, 1.77833], [1.08900040717, 1.7783298026, 1.77833003783, 1.77833, 0.0]] self.assertArrayAlmostEqual(self.mol.distance_matrix, ans) def test_break_bond(self): (mol1, mol2) = self.mol.break_bond(0, 1) self.assertEqual(mol1.formula, "H3 C1") self.assertEqual(mol2.formula, "H1") def test_prop(self): self.assertEqual(self.mol.charge, 0) self.assertEqual(self.mol.spin_multiplicity, 1) self.assertEqual(self.mol.nelectrons, 10) self.assertArrayAlmostEqual(self.mol.center_of_mass, [0, 0, 0]) self.assertRaises(ValueError, Molecule, ["C", "H", "H", "H", "H"], self.coords, charge=1, spin_multiplicity=1) mol = Molecule(["C", "H", "H", "H", "H"], self.coords, charge=1) self.assertEqual(mol.spin_multiplicity, 2) self.assertEqual(mol.nelectrons, 9) #Triplet O2 mol = IMolecule(["O"] * 2, [[0, 0, 0], [0, 0, 1.2]], spin_multiplicity=3) self.assertEqual(mol.spin_multiplicity, 3) def test_equal(self): mol = IMolecule(["C", "H", "H", "H", "H"], self.coords, charge=1) self.assertNotEqual(mol, self.mol) def test_get_centered_molecule(self): mol = IMolecule(["O"] * 2, [[0, 0, 0], [0, 0, 1.2]], spin_multiplicity=3) centered = mol.get_centered_molecule() self.assertArrayAlmostEqual(centered.center_of_mass, [0, 0, 0]) def test_to_from_dict(self): d = self.mol.as_dict() mol2 = IMolecule.from_dict(d) self.assertEqual(type(mol2), IMolecule) propertied_mol = Molecule(["C", "H", "H", "H", "H"], self.coords, charge=1, site_properties={'magmom': [0.5, -0.5, 1, 2, 3]}) d = propertied_mol.as_dict() self.assertEqual(d['sites'][0]['properties']['magmom'], 0.5) mol = Molecule.from_dict(d) self.assertEqual(propertied_mol, mol) self.assertEqual(mol[0].magmom, 0.5) self.assertEqual(mol.formula, "H4 C1") self.assertEqual(mol.charge, 1) def test_to_from_file_string(self): for fmt in ["xyz", "json", "g03", "yaml"]: s = self.mol.to(fmt=fmt) self.assertIsNotNone(s) m = IMolecule.from_str(s, fmt=fmt) self.assertEqual(m, self.mol) self.assertIsInstance(m, IMolecule) self.mol.to(filename="CH4_testing.xyz") self.assertTrue(os.path.exists("CH4_testing.xyz")) os.remove("CH4_testing.xyz") self.mol.to(filename="CH4_testing.yaml") self.assertTrue(os.path.exists("CH4_testing.yaml")) mol = Molecule.from_file("CH4_testing.yaml") self.assertEqual(self.mol, mol) os.remove("CH4_testing.yaml")
if len(argv) < 3: exit("Usage: convert_trajectory_xyzs.py initial_molecule_file, trajectory, to_dir") initial_molecule_file = argv[1] trajectory = argv[2] to_dir = argv[3] init_mol = Molecule.from_file(initial_molecule_file) species = [str(e) for e in init_mol.species] num_atoms = len(species) if to_dir not in os.listdir(): os.mkdir(to_dir) iteration = 0 num_agent = 0 with open(trajectory) as traj_file: lines = traj_file.readlines() num_agents = int(lines[1].split()[1]) for line in lines[2:]: if line == "\n": iteration += 1 num_agent = 0 elif "." not in line: continue else: coords = np.array([float(e) for e in line.strip().split(" ")]).reshape(num_atoms, 3) mol = Molecule(species, coords) mol.to("xyz", os.path.join(to_dir, "{}_{}.xyz".format(iteration,num_agent))) num_agent += 1
class IMoleculeTest(PymatgenTest): def setUp(self): coords = [[0.000000, 0.000000, 0.000000], [0.000000, 0.000000, 1.089000], [1.026719, 0.000000, -0.363000], [-0.513360, -0.889165, -0.363000], [-0.513360, 0.889165, -0.363000]] self.coords = coords self.mol = Molecule(["C", "H", "H", "H", "H"], coords) def test_set_item(self): s = self.mol.copy() s[0] = "Si" self.assertEqual(s.formula, "Si1 H4") s[(0, 1)] = "Ge" self.assertEqual(s.formula, "Ge2 H3") s[0:2] = "Sn" self.assertEqual(s.formula, "Sn2 H3") s = self.mol.copy() s["H"] = "F" self.assertEqual(s.formula, "C1 F4") s["C"] = "C0.25Si0.5" self.assertEqual(s.formula, "Si0.5 C0.25 F4") s["C"] = "C0.25Si0.5" self.assertEqual(s.formula, "Si0.625 C0.0625 F4") def test_bad_molecule(self): coords = [[0.000000, 0.000000, 0.000000], [0.000000, 0.000000, 1.089000], [1.026719, 0.000000, -0.363000], [-0.513360, -0.889165, -0.363000], [-0.513360, 0.889165, -0.363000], [-0.513360, 0.889165, -0.36301]] self.assertRaises(StructureError, Molecule, ["C", "H", "H", "H", "H", "H"], coords, validate_proximity=True) def test_get_angle_dihedral(self): self.assertAlmostEqual(self.mol.get_angle(1, 0, 2), 109.47122144618737) self.assertAlmostEqual(self.mol.get_angle(3, 1, 2), 60.00001388659683) self.assertAlmostEqual(self.mol.get_dihedral(0, 1, 2, 3), -35.26438851071765) coords = list() coords.append([0, 0, 0]) coords.append([0, 0, 1]) coords.append([0, 1, 1]) coords.append([1, 1, 1]) self.mol2 = Molecule(["C", "O", "N", "S"], coords) self.assertAlmostEqual(self.mol2.get_dihedral(0, 1, 2, 3), -90) def test_get_covalent_bonds(self): self.assertEqual(len(self.mol.get_covalent_bonds()), 4) def test_properties(self): self.assertEqual(len(self.mol), 5) self.assertTrue(self.mol.is_ordered) self.assertEqual(self.mol.formula, "H4 C1") def test_repr_str(self): ans = """Full Formula (H4 C1) Reduced Formula: H4C Charge = 0, Spin Mult = 1 Sites (5) 0 C 0.000000 0.000000 0.000000 1 H 0.000000 0.000000 1.089000 2 H 1.026719 0.000000 -0.363000 3 H -0.513360 -0.889165 -0.363000 4 H -0.513360 0.889165 -0.363000""" self.assertEqual(self.mol.__str__(), ans) ans = """Molecule Summary Site: C (0.0000, 0.0000, 0.0000) Site: H (0.0000, 0.0000, 1.0890) Site: H (1.0267, 0.0000, -0.3630) Site: H (-0.5134, -0.8892, -0.3630) Site: H (-0.5134, 0.8892, -0.3630)""" self.assertEqual(repr(self.mol), ans) def test_site_properties(self): propertied_mol = Molecule( ["C", "H", "H", "H", "H"], self.coords, site_properties={'magmom': [0.5, -0.5, 1, 2, 3]}) self.assertEqual(propertied_mol[0].magmom, 0.5) self.assertEqual(propertied_mol[1].magmom, -0.5) def test_get_boxed_structure(self): s = self.mol.get_boxed_structure(9, 9, 9) # C atom should be in center of box. self.assertArrayAlmostEqual(s[4].frac_coords, [0.50000001, 0.5, 0.5]) self.assertArrayAlmostEqual(s[1].frac_coords, [0.6140799, 0.5, 0.45966667]) self.assertRaises(ValueError, self.mol.get_boxed_structure, 1, 1, 1) s2 = self.mol.get_boxed_structure(5, 5, 5, (2, 3, 4)) self.assertEqual(len(s2), 24 * 5) self.assertEqual(s2.lattice.abc, (10, 15, 20)) # Test offset option s3 = self.mol.get_boxed_structure(9, 9, 9, offset=[0.5, 0.5, 0.5]) self.assertArrayAlmostEqual(s3[4].coords, [5, 5, 5]) # Test no_cross option self.assertRaises(ValueError, self.mol.get_boxed_structure, 5, 5, 5, offset=[10, 10, 10], no_cross=True) def test_get_distance(self): self.assertAlmostEqual(self.mol.get_distance(0, 1), 1.089) def test_get_neighbors(self): nn = self.mol.get_neighbors(self.mol[0], 1) self.assertEqual(len(nn), 0) nn = self.mol.get_neighbors(self.mol[0], 2) self.assertEqual(len(nn), 4) def test_get_neighbors_in_shell(self): nn = self.mol.get_neighbors_in_shell([0, 0, 0], 0, 1) self.assertEqual(len(nn), 1) nn = self.mol.get_neighbors_in_shell([0, 0, 0], 1, 0.9) self.assertEqual(len(nn), 4) nn = self.mol.get_neighbors_in_shell([0, 0, 0], 1, 0.9) self.assertEqual(len(nn), 4) nn = self.mol.get_neighbors_in_shell([0, 0, 0], 2, 0.1) self.assertEqual(len(nn), 0) def test_get_dist_matrix(self): ans = [[0.0, 1.089, 1.08899995636, 1.08900040717, 1.08900040717], [1.089, 0.0, 1.77832952654, 1.7783298026, 1.7783298026], [ 1.08899995636, 1.77832952654, 0.0, 1.77833003783, 1.77833003783 ], [1.08900040717, 1.7783298026, 1.77833003783, 0.0, 1.77833], [1.08900040717, 1.7783298026, 1.77833003783, 1.77833, 0.0]] self.assertArrayAlmostEqual(self.mol.distance_matrix, ans) def test_break_bond(self): (mol1, mol2) = self.mol.break_bond(0, 1) self.assertEqual(mol1.formula, "H3 C1") self.assertEqual(mol2.formula, "H1") def test_prop(self): self.assertEqual(self.mol.charge, 0) self.assertEqual(self.mol.spin_multiplicity, 1) self.assertEqual(self.mol.nelectrons, 10) self.assertArrayAlmostEqual(self.mol.center_of_mass, [0, 0, 0]) self.assertRaises(ValueError, Molecule, ["C", "H", "H", "H", "H"], self.coords, charge=1, spin_multiplicity=1) mol = Molecule(["C", "H", "H", "H", "H"], self.coords, charge=1) self.assertEqual(mol.spin_multiplicity, 2) self.assertEqual(mol.nelectrons, 9) #Triplet O2 mol = IMolecule(["O"] * 2, [[0, 0, 0], [0, 0, 1.2]], spin_multiplicity=3) self.assertEqual(mol.spin_multiplicity, 3) def test_equal(self): mol = IMolecule(["C", "H", "H", "H", "H"], self.coords, charge=1) self.assertNotEqual(mol, self.mol) def test_get_centered_molecule(self): mol = IMolecule(["O"] * 2, [[0, 0, 0], [0, 0, 1.2]], spin_multiplicity=3) centered = mol.get_centered_molecule() self.assertArrayAlmostEqual(centered.center_of_mass, [0, 0, 0]) def test_to_from_dict(self): d = self.mol.as_dict() mol2 = IMolecule.from_dict(d) self.assertEqual(type(mol2), IMolecule) propertied_mol = Molecule( ["C", "H", "H", "H", "H"], self.coords, charge=1, site_properties={'magmom': [0.5, -0.5, 1, 2, 3]}) d = propertied_mol.as_dict() self.assertEqual(d['sites'][0]['properties']['magmom'], 0.5) mol = Molecule.from_dict(d) self.assertEqual(propertied_mol, mol) self.assertEqual(mol[0].magmom, 0.5) self.assertEqual(mol.formula, "H4 C1") self.assertEqual(mol.charge, 1) def test_to_from_file_string(self): for fmt in ["xyz", "json", "g03", "yaml"]: s = self.mol.to(fmt=fmt) self.assertIsNotNone(s) m = IMolecule.from_str(s, fmt=fmt) self.assertEqual(m, self.mol) self.assertIsInstance(m, IMolecule) self.mol.to(filename="CH4_testing.xyz") self.assertTrue(os.path.exists("CH4_testing.xyz")) os.remove("CH4_testing.xyz") self.mol.to(filename="CH4_testing.yaml") self.assertTrue(os.path.exists("CH4_testing.yaml")) mol = Molecule.from_file("CH4_testing.yaml") self.assertEqual(self.mol, mol) os.remove("CH4_testing.yaml")
class MoleculeTest(PymatgenTest): def setUp(self): coords = [[0.000000, 0.000000, 0.000000], [0.000000, 0.000000, 1.089000], [1.026719, 0.000000, -0.363000], [-0.513360, -0.889165, -0.363000], [-0.513360, 0.889165, -0.363000]] self.mol = Molecule(["C", "H", "H", "H", "H"], coords) def test_mutable_sequence_methods(self): s = self.mol s[1] = ("F", [0.5, 0.5, 0.5]) self.assertEqual(s.formula, "H3 C1 F1") self.assertArrayAlmostEqual(s[1].coords, [0.5, 0.5, 0.5]) s.reverse() self.assertEqual(s[0].specie, Element("H")) self.assertArrayAlmostEqual(s[0].coords, [-0.513360, 0.889165, -0.363000]) del s[1] self.assertEqual(s.formula, "H2 C1 F1") s[3] = "N", [0, 0, 0], {"charge": 4} self.assertEqual(s.formula, "H2 N1 F1") self.assertEqual(s[3].charge, 4) def test_insert_remove_append(self): mol = self.mol mol.insert(1, "O", [0.5, 0.5, 0.5]) self.assertEqual(mol.formula, "H4 C1 O1") del mol[2] self.assertEqual(mol.formula, "H3 C1 O1") mol.set_charge_and_spin(0) self.assertEqual(mol.spin_multiplicity, 2) mol.append("N", [1, 1, 1]) self.assertEqual(mol.formula, "H3 C1 N1 O1") self.assertRaises(TypeError, dict, [(mol, 1)]) mol.remove_sites([0, 1]) self.assertEqual(mol.formula, "H3 N1") def test_translate_sites(self): self.mol.translate_sites([0, 1], [0.5, 0.5, 0.5]) self.assertArrayEqual(self.mol.cart_coords[0], [0.5, 0.5, 0.5]) def test_rotate_sites(self): self.mol.rotate_sites(theta=np.radians(30)) self.assertArrayAlmostEqual(self.mol.cart_coords[2], [0.889164737, 0.513359500, -0.363000000]) def test_replace(self): self.mol[0] = "Ge" self.assertEqual(self.mol.formula, "Ge1 H4") self.mol.replace_species( {Element("Ge"): { Element("Ge"): 0.5, Element("Si"): 0.5 }}) self.assertEqual(self.mol.formula, "Si0.5 Ge0.5 H4") #this should change the .5Si .5Ge sites to .75Si .25Ge self.mol.replace_species( {Element("Ge"): { Element("Ge"): 0.5, Element("Si"): 0.5 }}) self.assertEqual(self.mol.formula, "Si0.75 Ge0.25 H4") d = 0.1 pre_perturbation_sites = self.mol.sites[:] self.mol.perturb(distance=d) post_perturbation_sites = self.mol.sites for i, x in enumerate(pre_perturbation_sites): self.assertAlmostEqual(x.distance(post_perturbation_sites[i]), d, 3, "Bad perturbation distance") def test_add_site_property(self): self.mol.add_site_property("charge", [4.1, -2, -2, -2, -2]) self.assertEqual(self.mol[0].charge, 4.1) self.assertEqual(self.mol[1].charge, -2) self.mol.add_site_property("magmom", [3, 2, 2, 2, 2]) self.assertEqual(self.mol[0].charge, 4.1) self.assertEqual(self.mol[0].magmom, 3) def test_to_from_dict(self): d = self.mol.as_dict() mol2 = Molecule.from_dict(d) self.assertEqual(type(mol2), Molecule) def test_apply_operation(self): op = SymmOp.from_axis_angle_and_translation([0, 0, 1], 90) self.mol.apply_operation(op) self.assertArrayAlmostEqual(self.mol[2].coords, [0.000000, 1.026719, -0.363000]) def test_substitute(self): coords = [[0.000000, 0.000000, 1.08], [0.000000, 0.000000, 0.000000], [1.026719, 0.000000, -0.363000], [-0.513360, -0.889165, -0.363000], [-0.513360, 0.889165, -0.363000]] sub = Molecule(["X", "C", "H", "H", "H"], coords) self.mol.substitute(1, sub) self.assertAlmostEqual(self.mol.get_distance(0, 4), 1.54) f = Molecule(["X", "F"], [[0, 0, 0], [0, 0, 1.11]]) self.mol.substitute(2, f) self.assertAlmostEqual(self.mol.get_distance(0, 7), 1.35) oh = Molecule(["X", "O", "H"], [[0, 0.780362, -.456316], [0, 0, .114079], [0, -.780362, -.456316]]) self.mol.substitute(1, oh) self.assertAlmostEqual(self.mol.get_distance(0, 7), 1.43) self.mol.substitute(3, "methyl") self.assertEqual(self.mol.formula, "H7 C3 O1 F1") coords = [[0.00000, 1.40272, 0.00000], [0.00000, 2.49029, 0.00000], [-1.21479, 0.70136, 0.00000], [-2.15666, 1.24515, 0.00000], [-1.21479, -0.70136, 0.00000], [-2.15666, -1.24515, 0.00000], [0.00000, -1.40272, 0.00000], [0.00000, -2.49029, 0.00000], [1.21479, -0.70136, 0.00000], [2.15666, -1.24515, 0.00000], [1.21479, 0.70136, 0.00000], [2.15666, 1.24515, 0.00000]] benzene = Molecule( ["C", "H", "C", "H", "C", "H", "C", "H", "C", "H", "C", "H"], coords) benzene.substitute(1, sub) self.assertEqual(benzene.formula, "H8 C7") # Carbon attached should be in plane. self.assertAlmostEqual(benzene[11].coords[2], 0) benzene[14] = "Br" benzene.substitute(13, sub) self.assertEqual(benzene.formula, "H9 C8 Br1") def test_to_from_file_string(self): for fmt in ["xyz", "json", "g03"]: s = self.mol.to(fmt=fmt) self.assertIsNotNone(s) m = Molecule.from_str(s, fmt=fmt) self.assertEqual(m, self.mol) self.assertIsInstance(m, Molecule) self.mol.to(filename="CH4_testing.xyz") self.assertTrue(os.path.exists("CH4_testing.xyz")) os.remove("CH4_testing.xyz")
class structure_from_ext(): def __init__(self, struc, ref_mol=None, tol=0.2, relax_h=False): """ extract the mol_site information from the give cif file and reference molecule Args: struc: cif/poscar file or a Pymatgen Structure object ref_mol: xyz file or a reference Pymatgen molecule object tol: scale factor for covalent bond distance relax_h: whether or not relax the position for hydrogen atoms in structure """ if isinstance(ref_mol, str): ref_mol = Molecule.from_file(ref_mol) elif isinstance(ref_mol, Molecule): ref_mol = ref_mol else: print(type(ref_mol)) raise NameError("reference molecule cannot be defined") if isinstance(struc, str): pmg_struc = Structure.from_file(struc) elif isinstance(struc, Structure): pmg_struc = struc else: print(type(struc)) raise NameError("input structure cannot be intepretted") self.props = ref_mol.site_properties self.ref_mol = ref_mol.get_centered_molecule() self.tol = tol self.diag = False self.relax_h = relax_h sga = SpacegroupAnalyzer(pmg_struc) ops = sga.get_space_group_operations() self.wyc, perm = Wyckoff_position.from_symops( ops, sga.get_space_group_number()) if self.wyc is not None: self.group = Group(self.wyc.number) if isinstance(perm, list): if perm != [0, 1, 2]: lattice = Lattice.from_matrix(pmg_struc.lattice.matrix, self.group.lattice_type) latt = lattice.swap_axis(ids=perm, random=False).get_matrix() coor = pmg_struc.frac_coords[:, perm] pmg_struc = Structure(latt, pmg_struc.atomic_numbers, coor) else: self.diag = True self.perm = perm coords, numbers = search_molecule_in_crystal(pmg_struc, self.tol) #coords -= np.mean(coords, axis=0) if self.relax_h: self.molecule = self.addh(Molecule(numbers, coords)) else: self.molecule = Molecule(numbers, coords) self.pmg_struc = pmg_struc self.lattice = Lattice.from_matrix(pmg_struc.lattice.matrix, self.group.lattice_type) else: raise ValueError( "Cannot find the space group matching the symmetry operation") def addh(self, mol): #if len(mol) < len(self.ref_mol): from pymatgen.io.babel import BabelMolAdaptor ad = BabelMolAdaptor(mol) ad.add_hydrogen() ad.localopt() mol = ad.pymatgen_mol return mol def add_site_props(self, mo): if len(self.props) > 0: for key in self.props.keys(): mo.add_site_property(key, self.props[key]) return mo def make_mol_site(self, ref=False): if ref: mol = self.ref_mol ori = self.ori else: mol = self.molecule ori = Orientation(np.eye(3)) mol = self.add_site_props(mol) pmol = pyxtal_molecule(mol) site = mol_site(pmol, self.position, ori, self.wyc, self.lattice, self.diag) return site def align(self): """ compute the orientation wrt the reference molecule """ try: from openbabel import pybel, openbabel except: import pybel, openbabel m1 = pybel.readstring('xyz', self.ref_mol.to('xyz')) m2 = pybel.readstring('xyz', self.molecule.to('xyz')) aligner = openbabel.OBAlign(True, False) aligner.SetRefMol(m1.OBMol) aligner.SetTargetMol(m2.OBMol) aligner.Align() print("RMSD: ", aligner.GetRMSD()) rot = np.zeros([3, 3]) for i in range(3): for j in range(3): rot[i, j] = aligner.GetRotMatrix().Get(i, j) coord2 = self.molecule.cart_coords coord2 -= np.mean(coord2, axis=0) coord3 = rot.dot(coord2.T).T + np.mean(self.ref_mol.cart_coords, axis=0) self.mol_aligned = Molecule(self.ref_mol.atomic_numbers, coord3) self.ori = Orientation(rot) def match(self): """ Check the two molecular graphs are isomorphic """ match, mapping = compare_mol_connectivity(self.ref_mol, self.molecule) if not match: print(self.ref_mol.to("xyz")) print(self.molecule.to("xyz")) import pickle with open('wrong.pkl', "wb") as f: pickle.dump([self.ref_mol, self.molecule], f) return False else: # resort the atomic number for molecule 1 order = [mapping[i] for i in range(len(self.ref_mol))] numbers = np.array(self.molecule.atomic_numbers) numbers = numbers[order].tolist() coords = self.molecule.cart_coords[order] position = np.mean(coords, axis=0).dot(self.lattice.inv_matrix) position -= np.floor(position) # check if molecule is on the special wyckoff position if len(self.pmg_struc) / len(self.molecule) < len(self.wyc): if self.diag: #Transform it to the conventional representation position = np.dot(self.perm, position).T position, wp, _ = WP_merge(position, self.lattice.matrix, self.wyc, 2.0) #print("After Mergey:---------------") #print(position) #print(wp) self.wyc = wp self.position = position self.molecule = Molecule(numbers, coords - np.mean(coords, axis=0)) #self.align() return True def show(self, overlay=True): from pyxtal.viz import display_molecules if overlay: return display_molecules([self.ref_mol, self.mol_aligned]) else: return display_molecules([self.ref_mol, self.molecule])