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
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
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