예제 #1
0
    def _prevent_two_bonds_on_dummy(self, mol: Chem.RWMol):
        """
        The case '*(C)C' is seen legitimately in some warheads... but in most cases these are not.

        :param mol:
        :return:
        """
        for atom in mol.GetAtoms():
            if atom.GetSymbol() != '*':
                pass
            elif len(atom.GetNeighbors()) <= 1:
                pass
            elif len(atom.GetNeighbors()) >= 2:
                self.journal.info(
                    f'Dummy atom (idx={atom.GetIdx()}) has {len(atom.GetNeighbors())} bonds!'
                )
                neighs = atom.GetNeighbors()
                first = neighs[0]
                for second in neighs[1:]:
                    rejected = second.GetIdx(
                    )  # that will be absorbed (deleted)
                    keeper = first.GetIdx()  # that absorbs (kept)
                    self._copy_bonding(mol, keeper, rejected)
                    self._mark_for_deletion(mol, rejected)
                self._delete_marked(mol)
                return self._prevent_two_bonds_on_dummy(mol)
예제 #2
0
    def join_overclose(self, mol: Chem.RWMol, to_check, cutoff=2.2): # was 1.8
        """
        Cutoff is adapted to element.

        :param mol:
        :param to_check: list of atoms indices that need joining (but not to each other)
        :param cutoff: CC bond
        :return:
        """
        pt = Chem.GetPeriodicTable()
        dm = Chem.Get3DDistanceMatrix(mol)
        for i in to_check:
            atom_i = mol.GetAtomWithIdx(i)
            for j, atom_j in enumerate(mol.GetAtoms()):
                # calculate cutoff if not C-C
                if atom_i.GetSymbol() == '*' or atom_j.GetSymbol() == '*':
                    ij_cutoff = cutoff
                elif atom_i.GetSymbol() == 'C' and atom_j.GetSymbol() == 'C':
                    ij_cutoff = cutoff
                else:
                    ij_cutoff = cutoff - 1.36 + sum([pt.GetRcovalent(atom.GetAtomicNum()) for atom in (atom_i, atom_j)])
                # determine if to join
                if i == j or j in to_check:
                    continue
                elif dm[i, j] > ij_cutoff:
                    continue
                else:
                    self._add_bond_if_possible(mol, atom_i, atom_j)
예제 #3
0
    def apply(self, mol: RWMol) -> RWMol:
        num_atoms = mol.GetNumAtoms()
        if self.detach:
            for i, a in enumerate(mol.GetAtoms()):
                m = a.GetAtomMapNum()
                if m == self.atom_map2:
                    for bond in a.GetBonds():
                        mol.RemoveBond(bond.GetBeginAtomIdx(),
                                       bond.GetEndAtomIdx())
                    mol.RemoveAtom(i)
                    num_atoms -= 1
                    break

        atom_ind = get_atom_ind(mol, self.atom_map1)
        b_type = rdchem.BondType.values[self.bond_type]
        b_stereo = rdchem.BondStereo.values[self.bond_stereo]

        old_atom = mol.GetAtomWithIdx(atom_ind)
        if old_atom.HasProp('in_reactant'):
            self.new_a.SetBoolProp('in_reactant',
                                   old_atom.GetBoolProp('in_reactant'))
        if old_atom.HasProp('mol_id'):
            self.new_a.SetIntProp('mol_id', old_atom.GetIntProp('mol_id'))

        mol.AddAtom(self.new_a)
        new_atom_ind = num_atoms

        bond_ind = mol.AddBond(atom_ind, new_atom_ind, order=b_type) - 1
        new_bond = mol.GetBondWithIdx(bond_ind)
        new_bond.SetStereo(b_stereo)
        new_bond.SetBoolProp('is_edited', True)

        return mol
예제 #4
0
def remove_exocyclic_attachments(mol):
    """
    Remove exocyclic and exolinker attachments from
    a molecule.

    Parameters
    ----------
    mol : rdkit.Chem.rdchem.Mol

    Returns
    -------
    rdkit.Chem.rdchem.Mol
        Molecule with exocyclic/exolinker attachments
        removed.

    """
    edit = RWMol(mol)
    remove_atoms = set()
    for atom in edit.GetAtoms():
        degree = atom.GetDegree()
        if degree == 1:
            bond = atom.GetBonds()[0]
            if bond.GetBondTypeAsDouble() == 2.0:
                nbr = bond.GetOtherAtom(atom)
                hcount = nbr.GetTotalNumHs()
                nbr.SetNumExplicitHs(hcount + 2)
                nbr.SetNoImplicit(True)
                remove_atoms.add(atom.GetIdx())
    for aix in sorted(remove_atoms, reverse=True):
        edit.RemoveAtom(aix)
    rdmolops.AssignRadicals(edit)
    GetSymmSSSR(edit)
    return edit.GetMol()
예제 #5
0
 def _prevent_two_bonds_on_dummy(self, mol: Chem.RWMol):
     for atom in mol.GetAtoms():
         if atom.GetSymbol() != '*':
             pass
         elif len(atom.GetNeighbors()) <= 1:
             pass
         elif len(atom.GetNeighbors()) >= 2:
             neighs = atom.GetNeighbors()
             for second in neighs[1:]:
                 self._absorb(mol, atom.GetIdx(), second.GetIdx())
                 mol.RemoveAtom(second.GetIdx())
                 self._prevent_two_bonds_on_dummy(mol)
                 break
예제 #6
0
 def complement_reaction(rxn_template):
     if rxn_template.GetNumProductTemplates() != 1:
         print("[ERROR] A reaction template has only one product template.")
         sys.exit(1)
     pro = rxn_template.GetProductTemplate(0)
     rw_pro = RWMol(pro)
     amaps_pro = {a.GetAtomMapNum() for a in pro.GetAtoms()}
     amaps_rcts = {a.GetAtomMapNum() for rct in rxn_template.GetReactants() for a in rct.GetAtoms()}
     amaps_not_in_rcts = amaps_pro.intersection(amaps_rcts)
     for amap in amaps_not_in_rcts:
         aidx = [a.GetIdx() for a in rw_pro.GetAtoms() if a.GetAtomMapNum() == amap][0]
         rw_pro.RemoveAtom(aidx)
     m = rw_pro.GetMol()
     if '.' in Chem.MolToSmarts(m):
         return
     if (m.GetNumAtoms() == 0) or (m.GetNumAtoms() == 1 and m.GetAtomWithIdx(0).GetSymbol() in {"*", None}):
         return
     rxn_template.AddReactantTemplate(m)
예제 #7
0
def _minimize_rings(mol):
    """Private: Minimize rings in a scaffold.

    In this process, all remaining vertices/atoms of degree two are
    removed by performing an edge merging operation. The only
    exception being when both vertices neighbours are connected
    (i.e. we have a triangle), when edge merging would lead to the
    loss of a cycle. The result is a minimum cycle topological
    representation of the original molecule. This function is used
    in the computation of ring topology scaffolds (Oprea).

    If a ring contains a non-carbon atom, this atom is maintained.
    Neighbouring ring atoms which are of the same type are merged
    together into a single atom of the corresponding type.

    Parameters
    ----------
    mol : rdkit.Chem.rdchem.Mol

    Returns
    -------
    rdkit.Chem.rdchem.RWMol
        Minimum cycle topological graph.

    """
    edit = RWMol(mol)
    remove_atoms = set()
    for atom in edit.GetAtoms():
        if atom.GetDegree() == 2:
            n1, n2 = atom.GetNeighbors()
            n1_idx, n2_idx = n1.GetIdx(), n2.GetIdx()
            connected = edit.GetBondBetweenAtoms(n1_idx, n2_idx)
            if not connected and (n1.GetAtomicNum() == atom.GetAtomicNum()
                                  or n2.GetAtomicNum() == atom.GetAtomicNum()):
                a_idx = atom.GetIdx()
                edit.RemoveBond(n1_idx, a_idx)
                edit.RemoveBond(n2_idx, a_idx)
                edit.AddBond(n1_idx, n2_idx, BondType.SINGLE)
                remove_atoms.add(a_idx)
    for a_idx in sorted(remove_atoms, reverse=True):
        edit.RemoveAtom(a_idx)
    return edit