diff --git a/Code/GraphMol/FindStereo.cpp b/Code/GraphMol/FindStereo.cpp index 0015098f4..85a1e616a 100644 --- a/Code/GraphMol/FindStereo.cpp +++ b/Code/GraphMol/FindStereo.cpp @@ -28,8 +28,9 @@ namespace detail { bool isAtomPotentialNontetrahedralCenter(const Atom *atom) { PRECONDITION(atom, "atom is null"); - auto tnzdegree = - Chirality::detail::getAtomNonzeroDegree(atom) + atom->getTotalNumHs(); + auto nzdegree = Chirality::detail::getAtomNonzeroDegree(atom); + auto impHDegree = atom->getTotalNumHs(); + auto tnzdegree = nzdegree + impHDegree; auto anum = atom->getAtomicNum(); if (tnzdegree > 6 || tnzdegree < 2 || (anum < 12 && anum != 4)) { return false; @@ -59,8 +60,8 @@ bool isAtomPotentialTetrahedralCenter(const Atom *atom) { if (nzDegree == 4) { // chirality is always possible with 4 nbrs return true; - } else if (nzDegree == 1) { - // chirality is never possible with 1 nbr + } else if (nzDegree <= 1) { + // chirality is never possible with 0 or 1 nbr return false; } else if (nzDegree < 3 && (atom->getAtomicNum() != 15 && atom->getAtomicNum() != 33)) { @@ -106,9 +107,15 @@ bool isAtomPotentialTetrahedralCenter(const Atom *atom) { } } -bool isAtomPotentialStereoAtom(const Atom *atom) { +bool isAtomPotentialStereoAtom(const Atom *atom, + bool allowNontetrahehdralStereo) { return isAtomPotentialTetrahedralCenter(atom) || - isAtomPotentialNontetrahedralCenter(atom); + (allowNontetrahehdralStereo && + isAtomPotentialNontetrahedralCenter(atom)); +} + +bool isAtomPotentialStereoAtom(const Atom *atom) { + return isAtomPotentialStereoAtom(atom, getAllowNontetrahedralChirality()); } StereoInfo getStereoInfo(const Bond *bond) { @@ -452,10 +459,11 @@ void initAtomInfo(ROMol &mol, bool flagPossible, bool cleanIt, boost::dynamic_bitset<> &knownAtoms, std::vector &atomSymbols, boost::dynamic_bitset<> &possibleAtoms) { + bool allowNontetrahedralStereo = getAllowNontetrahedralChirality(); for (const auto atom : mol.atoms()) { auto aidx = atom->getIdx(); atomSymbols[aidx] = getAtomCompareSymbol(*atom); - if (detail::isAtomPotentialStereoAtom(atom)) { + if (detail::isAtomPotentialStereoAtom(atom, allowNontetrahedralStereo)) { auto sinfo = detail::getStereoInfo(atom); switch (sinfo.specified) { case Chirality::StereoSpecified::Unknown: diff --git a/Code/GraphMol/catch_chirality.cpp b/Code/GraphMol/catch_chirality.cpp index 6b579630f..592df325a 100644 --- a/Code/GraphMol/catch_chirality.cpp +++ b/Code/GraphMol/catch_chirality.cpp @@ -3253,3 +3253,45 @@ TEST_CASE("double bond stereo with new chirality perception") { } } } + +TEST_CASE("false positives from new stereo code") { + SECTION("elements") { + std::vector examples{"P", "PC", "S", "SC", "S(F)C"}; + for (auto &smi : examples) { + INFO(smi); + std::unique_ptr m{SmilesToMol(smi)}; + REQUIRE(m); + auto si = Chirality::findPotentialStereo(*m); + CHECK(si.empty()); + } + } + SECTION("non-tetrahedral and implicit Hs") { + std::vector examples{ + "[SiH4]", "[SiH3]C", "[SH4]", "[PH5]", + "[PH4]C", "[SH6]", "[SH5]C", "[SiH2](C)C", + "[PH3](C)C", "[PH2](C)(C)C", "[SH4](C)C", "[SH3](C)(C)C", + "[SH2](C)(C)(C)C"}; + { + AllowNontetrahedralChiralityFixture reset_non_tetrahedral_allowed; + Chirality::setAllowNontetrahedralChirality(false); + for (auto &smi : examples) { + INFO(smi); + std::unique_ptr m{SmilesToMol(smi)}; + REQUIRE(m); + auto si = Chirality::findPotentialStereo(*m); + CHECK(si.empty()); + } + } + { + AllowNontetrahedralChiralityFixture reset_non_tetrahedral_allowed; + Chirality::setAllowNontetrahedralChirality(true); + for (auto &smi : examples) { + INFO(smi); + std::unique_ptr m{SmilesToMol(smi)}; + REQUIRE(m); + auto si = Chirality::findPotentialStereo(*m); + CHECK(si.size() == 1); + } + } + } +} \ No newline at end of file