Example #1
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
Example #2
0
    def apply(self, mol: RWMol) -> RWMol:
        atom1 = get_atom_ind(mol, self.atom_map1)
        atom2 = get_atom_ind(mol, self.atom_map2)

        if self.bond_type is None:  # delete bond
            bond = mol.GetBondBetweenAtoms(atom1, atom2)
            if bond is not None:
                mol.RemoveBond(atom1, atom2)
        else:
            b_type = rdchem.BondType.values[self.bond_type]
            b_stereo = rdchem.BondStereo.values[self.bond_stereo]

            bond = mol.GetBondBetweenAtoms(atom1, atom2)
            if bond is None:  # add new bond
                bond_ind = mol.AddBond(atom1, atom2, order=b_type) - 1
                bond = mol.GetBondWithIdx(bond_ind)
            else:  # change an existing bond
                bond.SetBondType(b_type)
            bond.SetStereo(b_stereo)
            bond.SetBoolProp('is_edited', True)

            if b_type == BondType.AROMATIC:
                bond.SetIsAromatic(True)
                mol.GetAtomWithIdx(atom1).SetIsAromatic(True)
                mol.GetAtomWithIdx(atom2).SetIsAromatic(True)

        return mol
Example #3
0
    def fragment(self, scaffold):
        """Fragment a scaffold into its next set of Murcko fragments.

        Parameters
        ----------
        scaffold : scaffoldgraph.core.Scaffold
            Child scaffold to be fragmented.

        Returns
        -------
        list
            A list of parent scaffolds representing the next hierarchy.

        """
        parents = []  # container for parent scaffolds
        rings = scaffold.rings  # ring information

        for rix, ring in enumerate(rings):  # Loop through all rings and remove
            edit = RWMol(scaffold.mol)  # Editable molecule

            # Collect all removable atoms in the molecule
            remove_atoms = set()
            for index, atom in zip(ring.aix, ring.atoms):
                if rings.info.NumAtomRings(index) == 1:
                    if atom.GetDegree() > 2:  # Evoke linker collection
                        collect_linker_atoms(edit.GetAtomWithIdx(index),
                                             remove_atoms)
                    else:  # Add ring atom to removable set
                        remove_atoms.add(index)
                else:  # Atom is shared between multiple rings
                    correct_atom_props(edit.GetAtomWithIdx(index))

            # Collect removable bonds (this needs to be done to prevent the case where when deleting
            # a ring two atoms belonging to the same bond are also part of separate other rings.
            # This bond must be broken to prevent an incorrect output)
            remove_bonds = set()
            for bix in {
                    x
                    for x in ring.bix if rings.info.NumBondRings(x) == 1
            }:
                bond = edit.GetBondWithIdx(bix)
                b_x, b_y = bond.GetBeginAtomIdx(), bond.GetEndAtomIdx()
                if b_x not in remove_atoms and b_y not in remove_atoms:
                    remove_bonds.add((b_x, b_y))
                    correct_atom_props(edit.GetAtomWithIdx(b_x))
                    correct_atom_props(edit.GetAtomWithIdx(b_y))

            # Scheme 4 (scaffold tree rule)
            if self.use_scheme_4 is not False and len(ring) == 3:
                atomic_nums = [a.GetAtomicNum() for a in ring.atoms]
                if len([a for a in atomic_nums if a != 1 and a != 6]) == 1:
                    shared = {
                        x
                        for x in ring.bix if rings.info.NumBondRings(x) > 1
                    }
                    if len(shared) == 1:
                        bond = edit.GetBondWithIdx(shared.pop())
                        bond.SetBondType(BondType.DOUBLE)

            # Remove collected atoms and bonds
            for bix in remove_bonds:
                edit.RemoveBond(*bix)
            for aix in sorted(remove_atoms, reverse=True):
                edit.RemoveAtom(aix)

            # Add new parent scaffolds to parent list
            for parent in get_scaffold_frags(edit):
                if parent.rings.count == len(rings) - 1:
                    parent.removed_ring_idx = rix
                    parents.append(parent)

        return parents