def _getFlippers(mol, options): Chem.FindPotentialStereoBonds(mol) flippers = [] if not options.onlyStereoGroups: for atom in mol.GetAtoms(): if atom.HasProp("_ChiralityPossible"): if (not options.onlyUnassigned or atom.GetChiralTag() == Chem.ChiralType.CHI_UNSPECIFIED): flippers.append(_AtomFlipper(atom)) for bond in mol.GetBonds(): bstereo = bond.GetStereo() if bstereo != Chem.BondStereo.STEREONONE: if (not options.onlyUnassigned or bstereo == Chem.BondStereo.STEREOANY): flippers.append(_BondFlipper(bond)) if options.onlyUnassigned: # otherwise these will be counted twice for group in mol.GetStereoGroups(): if group.GetGroupType() != Chem.StereoGroupType.STEREO_ABSOLUTE: flippers.append(_StereoGroupFlipper(group)) return flippers
def change_stereobond_in_imine_to_cis(mol: Chem.Mol) -> Chem.Mol: Chem.FindPotentialStereoBonds(mol) for bond in mol.GetBonds(): if bond.GetStereo() == Chem.BondStereo.STEREOANY: logger.debug( f"{bond.GetBeginAtom().GetSymbol()} {bond.GetSmarts()} {bond.GetEndAtom().GetSymbol()}" ) bond.SetStereo(Chem.BondStereo.STEREOZ) return mol
def has_stereo_defined(molecule): #ToDo Fix this function for carbons with explicit hydrogen and imine with implicit hydrogens """ Parameters ---------- molecule Returns ------- """ unspec_chiral = False unspec_db = False problematic_atoms = list() problematic_bonds = list() # remove map indices and store. Then create molecule with SMILES without map indices and check for stereo on that. # This step is needed because even when map indices are stored in the data of the atoms, it is used for assigning potential # stereo centers. If a molecule does not have map indices but has data on the atoms, it will get flagged had_atom_map = False if has_atom_map(molecule): had_atom_map = True remove_atom_map(molecule) a = Chem.rdmolfiles.SmilesParserParams() a.removeHs = False mol_copy = Chem.MolFromSmiles(Chem.MolToSmiles(molecule), a) # restore map indices if had_atom_map: restore_atom_map(molecule) chiral_centers = Chem.FindMolChiralCenters(mol_copy, includeUnassigned=True) for center in chiral_centers: atom_id = center[0] if center[-1] == '?': unspec_chiral = True problematic_atoms.append( (atom_id, mol_copy.GetAtomWithIdx(atom_id).GetSmarts())) # Find potential stereo bonds that are unspecified Chem.FindPotentialStereoBonds(mol_copy) for bond in mol_copy.GetBonds(): if bond.GetStereo() == Chem.BondStereo.STEREOANY: unspec_db = True problematic_bonds.append( (bond.GetBeginAtom().GetSmarts(), bond.GetSmarts(), bond.GetEndAtom().GetSmarts())) if unspec_chiral or unspec_db: warnings.warn( "Stereochemistry is unspecified. Problematic atoms {}, problematic bonds {}" .format(problematic_atoms, problematic_bonds)) return False else: return True
def enumerate_stereoisomers( mol, n_variants: int = 20, undefined_only: bool = False, rationalise: bool = True, ): """Enumerate the stereocenters and bonds of the current molecule. Original source: the `openff-toolkit` lib. Warning: this function can be computationnaly intensive. Args: mol: The molecule whose state we should enumerate. n_variants: The maximum amount of molecules that should be returned. undefined_only: If we should enumerate all stereocenters and bonds or only those with undefined stereochemistry. rationalise: If we should try to build and rationalise the molecule to ensure it can exist. """ from rdkit.Chem.EnumerateStereoisomers import EnumerateStereoisomers from rdkit.Chem.EnumerateStereoisomers import StereoEnumerationOptions # safety first mol = copy_mol(mol) # in case any bonds/centers are missing stereo chem flag it here Chem.AssignStereochemistry(mol, force=False, flagPossibleStereoCenters=True, cleanIt=True) # type: ignore Chem.FindPotentialStereoBonds(mol, cleanIt=True) # type: ignore # set up the options stereo_opts = StereoEnumerationOptions( tryEmbedding=rationalise, onlyUnassigned=undefined_only, maxIsomers=n_variants, ) try: isomers = tuple(EnumerateStereoisomers(mol, options=stereo_opts)) except: # NOTE(hadim): often got "Stereo atoms should be specified before specifying CIS/TRANS bond stereochemistry" # for the ligand of reference (coming from the PDB). Not sure how to handle that. isomers = [] variants = [] for isomer in isomers: # isomer has CIS/TRANS tags so convert back to E/Z Chem.SetDoubleBondNeighborDirections(isomer) # type: ignore Chem.AssignStereochemistry(isomer, force=True, cleanIt=True) # type: ignore variants.append(isomer) return variants
def _find_rd_stereocenters( molecule: Molecule, ) -> Tuple[List[int], List[Tuple[int, int]]]: """A method which returns the of the stereogenic atom and bonds of a molecule using the RDKit. Parameters ---------- molecule The molecule whose stereocenters should be returned. Notes ----- * This method currently deals with RDKit directly and should be removed once the API points suggested in openff-toolkit issue #903 have been added. Returns ------- The indices of the stereogenic atoms in the molecule and the indices of the atoms involved in stereogenic bonds in the molecule. """ from rdkit import Chem rd_molecule = molecule.to_rdkit() Chem.AssignStereochemistry( rd_molecule, cleanIt=True, force=True, flagPossibleStereoCenters=True ) stereogenic_atoms = { atom.GetIdx() for atom in rd_molecule.GetAtoms() if atom.HasProp("_ChiralityPossible") } # Clear any previous assignments on the bonds, since FindPotentialStereo # may not overwrite it for bond in rd_molecule.GetBonds(): bond.SetStereo(Chem.BondStereo.STEREONONE) Chem.FindPotentialStereoBonds(rd_molecule, cleanIt=True) stereogenic_bonds = { (bond.GetBeginAtomIdx(), bond.GetEndAtomIdx()) for bond in rd_molecule.GetBonds() if bond.GetStereo() == Chem.BondStereo.STEREOANY } return sorted(stereogenic_atoms), sorted(stereogenic_bonds)
def decide_unspec_stereo(smiles: str) -> str: m = Chem.MolFromSmiles(smiles) m = Chem.AddHs(m) Chem.FindPotentialStereoBonds(m) for bond in m.GetBonds(): if bond.GetStereo() == Chem.BondStereo.STEREOANY: print( bond.GetBeginAtom().GetSymbol(), bond.GetSmarts(), bond.GetEndAtom().GetSymbol(), ) bond.SetStereo(Chem.BondStereo.STEREOE) return Chem.MolToSmiles(m)
def flag_unspec_stereo(smiles: str) -> bool: m = Chem.MolFromSmiles(smiles) m = Chem.AddHs(m) unspec = False Chem.FindPotentialStereoBonds(m) for bond in m.GetBonds(): if bond.GetStereo() == Chem.BondStereo.STEREOANY: logger.debug( bond.GetBeginAtom().GetSymbol(), bond.GetSmarts(), bond.GetEndAtom().GetSymbol(), ) unspec = True return unspec
def _getFlippers(mol, options): Chem.FindPotentialStereoBonds(mol) flippers = [] for atom in mol.GetAtoms(): if atom.HasProp("_ChiralityPossible"): if (not options.onlyUnassigned or atom.GetChiralTag() == Chem.ChiralType.CHI_UNSPECIFIED): flippers.append(_AtomFlipper(atom)) for bond in mol.GetBonds(): bstereo = bond.GetStereo() if bstereo != Chem.BondStereo.STEREONONE: if (not options.onlyUnassigned or bstereo == Chem.BondStereo.STEREOANY): flippers.append(_BondFlipper(bond)) return flippers
def has_stereo_defined(molecule): """ Parameters ---------- molecule Returns ------- """ unspec_chiral = False unspec_db = False problematic_atoms = list() problematic_bonds = list() chiral_centers = Chem.FindMolChiralCenters(molecule, includeUnassigned=True) for center in chiral_centers: atom_id = center[0] if center[-1] == '?': unspec_chiral = True problematic_atoms.append( (atom_id, molecule.GetAtomWithIdx(atom_id).GetSmarts())) # Find potential stereo bonds that are unspecified Chem.FindPotentialStereoBonds(molecule) for bond in molecule.GetBonds(): if bond.GetStereo() == Chem.BondStereo.STEREOANY: unspec_db = True problematic_bonds.append( (bond.GetBeginAtom().GetSmarts(), bond.GetSmarts(), bond.GetEndAtom().GetSmarts())) if unspec_chiral or unspec_db: warnings.warn( "Stereochemistry is unspecified. Problematic atoms {}, problematic bonds {}" .format(problematic_atoms, problematic_bonds)) return False else: return True