|
12 | 12 | from openbabel import pybel |
13 | 13 | from rdkit import Chem |
14 | 14 | from rdkit.Chem import rdMolTransforms as rdMT |
15 | | -from rdkit.Chem import SDWriter |
| 15 | +from rdkit.Chem import SDWriter, AllChem |
16 | 16 | from rdkit.Chem.rdchem import AtomValenceException |
17 | 17 |
|
18 | 18 | from arkane.common import get_element_mass, mass_by_symbol, symbol_by_number |
@@ -1851,6 +1851,104 @@ def set_rdkit_dihedrals(conf, rd_mol, torsion, deg_increment=None, deg_abs=None) |
1851 | 1851 | new_xyz = xyz_from_data(coords=coords, symbols=symbols) |
1852 | 1852 | return new_xyz |
1853 | 1853 |
|
| 1854 | +def set_rdkit_ring_dihedrals(conf_original, rd_mol, ring_head, ring_tail, torsions, dihedrals): |
| 1855 | + """ |
| 1856 | + A helper function for setting dihedral angles in a ring using RDKit. |
| 1857 | +
|
| 1858 | + Args: |
| 1859 | + rd_mol: The respective RDKit molecule. |
| 1860 | + ring_head: The first atom index of the ring(0-indexed). |
| 1861 | + ring_tail: The last atom index of the ring(0-indexed). |
| 1862 | + torsions: A list of torsions, each corresponding to a dihedral. |
| 1863 | + dihedrals: A list of dihedral angles in degrees, each corresponding to a torsion. |
| 1864 | +
|
| 1865 | + Example of a 6-membered ring: |
| 1866 | + ring_head = 0 |
| 1867 | + ring_tail = 5 |
| 1868 | + torsions = [(0, 1, 2, 3), (1, 2, 3, 4), (2, 3, 4, 5)] |
| 1869 | + dihedrals = [30, 300, 30] |
| 1870 | + |
| 1871 | + Returns: |
| 1872 | + dict: The xyz with the new dihedral, ordered according to the map. |
| 1873 | + """ |
| 1874 | + |
| 1875 | + # Create a copy of the molecule to modify |
| 1876 | + rd_mol_mod = Chem.Mol(rd_mol) |
| 1877 | + |
| 1878 | + # Apply the original coordinates to the conformer |
| 1879 | + conf_mod = rd_mol_mod.GetConformer() |
| 1880 | + for i in range(rd_mol_mod.GetNumAtoms()): |
| 1881 | + pos = conf_original.GetAtomPosition(i) |
| 1882 | + conf_mod.SetAtomPosition(i, pos) |
| 1883 | + # Remove hydrogens |
| 1884 | + rd_mol_noH = Chem.RemoveHs(rd_mol_mod) |
| 1885 | + Chem.SanitizeMol(rd_mol_noH) |
| 1886 | + |
| 1887 | + # Map positions from conf_mod to conf_noH |
| 1888 | + conf_noH = Chem.Conformer(rd_mol_noH.GetNumAtoms()) |
| 1889 | + atom_map = {} # Map heavy atom indices between rd_mol_mod and rd_mol_noH |
| 1890 | + idx_noH = 0 |
| 1891 | + for idx in range(rd_mol_mod.GetNumAtoms()): |
| 1892 | + atom = rd_mol_mod.GetAtomWithIdx(idx) |
| 1893 | + if atom.GetAtomicNum() != 1: # Not hydrogen |
| 1894 | + pos = conf_mod.GetAtomPosition(idx) |
| 1895 | + conf_noH.SetAtomPosition(idx_noH, pos) |
| 1896 | + atom_map[idx] = idx_noH |
| 1897 | + idx_noH += 1 |
| 1898 | + rd_mol_noH.AddConformer(conf_noH) |
| 1899 | + |
| 1900 | + # Remove the bond to open the ring |
| 1901 | + rd_mol_noH = Chem.RWMol(rd_mol_noH) |
| 1902 | + rd_mol_noH.RemoveBond(atom_map[ring_head], atom_map[ring_tail]) |
| 1903 | + Chem.SanitizeMol(rd_mol_noH) |
| 1904 | + |
| 1905 | + # Set the specified dihedral angles |
| 1906 | + conf_noH = rd_mol_noH.GetConformer() |
| 1907 | + for torsion, dihedral in zip(torsions, dihedrals): |
| 1908 | + torsion_noH = [atom_map[atom_idx] for atom_idx in torsion] |
| 1909 | + rdMT.SetDihedralDeg(conf_noH, *torsion_noH, dihedral) |
| 1910 | + # Re-add the bond to close the ring |
| 1911 | + rd_mol_noH.AddBond( |
| 1912 | + atom_map[ring_head], atom_map[ring_tail], rd_mol.GetBondBetweenAtoms(ring_head, ring_tail).GetBondType() |
| 1913 | + ) |
| 1914 | + Chem.SanitizeMol(rd_mol_noH) |
| 1915 | + # Optimize the molecule |
| 1916 | + uff_ff = AllChem.UFFGetMoleculeForceField(rd_mol_noH) |
| 1917 | + if uff_ff is None: |
| 1918 | + raise ValueError("UFF force field could not be generated for the molecule.") |
| 1919 | + |
| 1920 | + # Add torsion constraints to keep dihedral angles fixed |
| 1921 | + force_constant = 1000.0 # A high force constant to strongly enforce the constraint |
| 1922 | + for torsion, dihedral in zip(torsions, dihedrals): |
| 1923 | + torsion_noH = [atom_map[atom_idx] for atom_idx in torsion] |
| 1924 | + i, j, k, l = torsion_noH |
| 1925 | + uff_ff.UFFAddTorsionConstraint( |
| 1926 | + i, j, k, l, False, dihedral, dihedral, force_constant |
| 1927 | + ) |
| 1928 | + |
| 1929 | + # Optimize the molecule |
| 1930 | + uff_ff.Minimize() |
| 1931 | + |
| 1932 | + # Retrieve the optimized conformer |
| 1933 | + conf_noH = rd_mol_noH.GetConformer() |
| 1934 | + # Add hydrogens back to the optimized molecule |
| 1935 | + rd_mol_opt_H = Chem.AddHs(rd_mol_noH) |
| 1936 | + # Generate new conformer with hydrogens |
| 1937 | + AllChem.EmbedMolecule(rd_mol_opt_H, coordMap={atom.GetIdx(): conf_noH.GetAtomPosition(atom.GetIdx()) for atom in rd_mol_noH.GetAtoms()}) |
| 1938 | + AllChem.UFFOptimizeMolecule(rd_mol_opt_H) |
| 1939 | + # Extract updated coordinates |
| 1940 | + conf_opt_H = rd_mol_opt_H.GetConformer() |
| 1941 | + coords = [] |
| 1942 | + symbols = [] |
| 1943 | + for i, atom in enumerate(rd_mol_opt_H.GetAtoms()): |
| 1944 | + pos = conf_opt_H.GetAtomPosition(i) |
| 1945 | + coords.append([pos.x, pos.y, pos.z]) |
| 1946 | + symbols.append(atom.GetSymbol()) |
| 1947 | + |
| 1948 | + # Create the new xyz dictionary |
| 1949 | + new_xyz = xyz_from_data(coords=coords, symbols=symbols) |
| 1950 | + return new_xyz |
| 1951 | + |
1854 | 1952 |
|
1855 | 1953 | def check_isomorphism(mol1, mol2, filter_structures=True, convert_to_single_bonds=False): |
1856 | 1954 | """ |
|
0 commit comments