diff --git a/Code/GraphMol/Chirality.cpp b/Code/GraphMol/Chirality.cpp index c39346b13..273e063a8 100644 --- a/Code/GraphMol/Chirality.cpp +++ b/Code/GraphMol/Chirality.cpp @@ -326,7 +326,7 @@ void findAtomNeighborDirHelper(const ROMol &mol, const Atom *atom, ROMol::OEDGE_ITER beg, end; boost::tie(beg, end) = mol.getAtomBonds(atom); while (beg != end) { - const Bond* bond = mol[*beg]; + const Bond *bond = mol[*beg]; // check whether this bond is explictly set to have unknown stereo if (!hasExplicitUnknownStereo) { int explicit_unknown_stereo; @@ -396,7 +396,7 @@ void findAtomNeighborsHelper(const ROMol &mol, const Atom *atom, ROMol::OEDGE_ITER beg, end; boost::tie(beg, end) = mol.getAtomBonds(atom); while (beg != end) { - const Bond* bond = mol[*beg]; + const Bond *bond = mol[*beg]; Bond::BondDir dir = bond->getBondDir(); if (bond->getBondType() == Bond::SINGLE && bond->getIdx() != refBond->getIdx()) { @@ -432,7 +432,7 @@ bool atomIsCandidateForRingStereochem(const ROMol &mol, const Atom *atom) { std::vector nonRingNbrs; std::vector ringNbrs; while (beg != end) { - const Bond* bond = mol[*beg]; + const Bond *bond = mol[*beg]; if (!ringInfo->numBondRings(bond->getIdx())) { nonRingNbrs.push_back(bond->getOtherAtom(atom)); } else { @@ -933,7 +933,7 @@ void rerankAtoms(const ROMol &mol, UINT_VECT &ranks) { ROMol::OEDGE_ITER beg, end; boost::tie(beg, end) = mol.getAtomBonds(atom); while (beg != end) { - const Bond* oBond = mol[*beg]; + const Bond *oBond = mol[*beg]; if (oBond->getBondType() == Bond::DOUBLE) { if (oBond->getStereo() == Bond::STEREOE) { invars[i] += 1; @@ -957,7 +957,7 @@ void rerankAtoms(const ROMol &mol, UINT_VECT &ranks) { } #endif } -} // end of chirality namespace +} // namespace Chirality namespace MolOps { @@ -1025,7 +1025,7 @@ void assignStereochemistry(ROMol &mol, bool cleanIt, bool force, ROMol::OEDGE_ITER beg, end; boost::tie(beg, end) = mol.getAtomBonds((*bondIt)->getBeginAtom()); while (!hasStereoBonds && beg != end) { - const Bond* nbond = mol[*beg]; + const Bond *nbond = mol[*beg]; ++beg; if (nbond->getBondDir() == Bond::ENDDOWNRIGHT || nbond->getBondDir() == Bond::ENDUPRIGHT) { @@ -1034,7 +1034,7 @@ void assignStereochemistry(ROMol &mol, bool cleanIt, bool force, } boost::tie(beg, end) = mol.getAtomBonds((*bondIt)->getEndAtom()); while (!hasStereoBonds && beg != end) { - const Bond* nbond = mol[*beg]; + const Bond *nbond = mol[*beg]; ++beg; if (nbond->getBondDir() == Bond::ENDDOWNRIGHT || nbond->getBondDir() == Bond::ENDUPRIGHT) { @@ -1878,6 +1878,8 @@ void detectBondStereochemistry(ROMol &mol, int confId) { void assignStereochemistryFrom3D(ROMol &mol, int confId, bool replaceExistingTags) { + if (!mol.getNumConformers() || !mol.getConformer(confId).is3D()) return; + detectBondStereochemistry(mol, confId); assignChiralTypesFrom3D(mol, confId, replaceExistingTags); bool force = true; diff --git a/Code/GraphMol/MolOps.h b/Code/GraphMol/MolOps.h index 9dc56a4e8..f364d56de 100644 --- a/Code/GraphMol/MolOps.h +++ b/Code/GraphMol/MolOps.h @@ -839,6 +839,7 @@ RDKIT_GRAPHMOL_EXPORT void assignChiralTypesFrom3D( \param replaceExistingTags if this flag is true, any existing info about stereochemistry will be replaced + If the conformer provided is not a 3D conformer, nothing will be done. */ RDKIT_GRAPHMOL_EXPORT void assignStereochemistryFrom3D( ROMol &mol, int confId = -1, bool replaceExistingTags = true); diff --git a/Code/GraphMol/Wrap/MolOps.cpp b/Code/GraphMol/Wrap/MolOps.cpp index 1260f18eb..823947835 100644 --- a/Code/GraphMol/Wrap/MolOps.cpp +++ b/Code/GraphMol/Wrap/MolOps.cpp @@ -1575,6 +1575,23 @@ struct molops_wrapper { python::arg("flagPossibleStereoCenters") = false), docString.c_str()); + // ------------------------------------------------------------------------ + docString = + "Uses a conformer (should be 3D) to assign ChiralTypes to a molecule's atoms\n\ + and stereo flags to its bonds\n\ +\n\ + ARGUMENTS:\n\ +\n\ + - mol: the molecule to use\n\ + - confId: (optional) the conformation to use \n\ + - replaceExistingTags: (optional) replace any existing information about stereochemistry\n\ +\n"; + python::def("AssignStereochemistryFrom3D", + MolOps::assignStereochemistryFrom3D, + (python::arg("mol"), python::arg("confId") = -1, + python::arg("replaceExistingTags") = true), + docString.c_str()); + // ------------------------------------------------------------------------ docString = "Find bonds than can be cis/trans in a molecule and mark them as 'any'.\n\ diff --git a/Code/GraphMol/Wrap/rough_test.py b/Code/GraphMol/Wrap/rough_test.py index 1fa2e6260..5d8ec00bb 100644 --- a/Code/GraphMol/Wrap/rough_test.py +++ b/Code/GraphMol/Wrap/rough_test.py @@ -4891,6 +4891,32 @@ width='200px' height='200px' > with self.assertRaises(RuntimeError): mol = Chem.MolFromRDKitSVG("bad svg") + def testAssignStereochemistryFrom3D(self): + def _stereoTester(mol,expectedCIP,expectedStereo): + mol.UpdatePropertyCache() + self.assertEqual(mol.GetNumAtoms(),9) + self.assertFalse(mol.GetAtomWithIdx(1).HasProp("_CIPCode")) + self.assertEqual(mol.GetBondWithIdx(3).GetStereo(),Chem.BondStereo.STEREONONE) + for bond in mol.GetBonds(): + bond.SetBondDir(Chem.BondDir.NONE) + Chem.AssignStereochemistryFrom3D(mol) + self.assertTrue(mol.GetAtomWithIdx(1).HasProp("_CIPCode")) + self.assertEqual(mol.GetAtomWithIdx(1).GetProp("_CIPCode"),expectedCIP) + self.assertEqual(mol.GetBondWithIdx(3).GetStereo(),expectedStereo) + + fileN = os.path.join(RDConfig.RDBaseDir, 'Code', 'GraphMol', 'test_data', + 'stereochem.sdf') + suppl = Chem.SDMolSupplier(fileN, sanitize=False) + expected = ( + ("R",Chem.BondStereo.STEREOZ), + ("R",Chem.BondStereo.STEREOE), + ("S",Chem.BondStereo.STEREOZ), + ("S",Chem.BondStereo.STEREOE), + ) + for i,mol in enumerate(suppl): + cip,stereo = expected[i] + _stereoTester(mol,cip,stereo) + def testGitHub2082(self): ctab=""" MJ150720