diff --git a/Code/GraphMol/Atom.cpp b/Code/GraphMol/Atom.cpp index 10226dac1..c80f4d4c4 100644 --- a/Code/GraphMol/Atom.cpp +++ b/Code/GraphMol/Atom.cpp @@ -22,12 +22,12 @@ #include namespace RDKit { -namespace { + // Determine whether or not a molecule is to the left of Carbon bool isEarlyAtom(int atomicNum) { return (4 - PeriodicTable::getTable()->getNouterElecs(atomicNum)) > 0; } -} // namespace + Atom::Atom() : RDProps() { d_atomicNum = 0; initAtom(); diff --git a/Code/GraphMol/Atom.h b/Code/GraphMol/Atom.h index 47f17b46f..30cadb56f 100644 --- a/Code/GraphMol/Atom.h +++ b/Code/GraphMol/Atom.h @@ -433,4 +433,8 @@ RDKIT_GRAPHMOL_EXPORT std::string getSupplementalSmilesLabel(const Atom *atom); RDKIT_GRAPHMOL_EXPORT std::ostream &operator<<(std::ostream &target, const RDKit::Atom &at); +namespace RDKit { +//! returns whether or not the atom is to the left of C +RDKIT_GRAPHMOL_EXPORT bool isEarlyAtom(int atomicNum); +} // namespace RDKit #endif diff --git a/Code/GraphMol/MolStandardize/Charge.cpp b/Code/GraphMol/MolStandardize/Charge.cpp index a343da4ef..6776281c0 100644 --- a/Code/GraphMol/MolStandardize/Charge.cpp +++ b/Code/GraphMol/MolStandardize/Charge.cpp @@ -318,13 +318,23 @@ ROMol *Uncharger::uncharge(const ROMol &mol) { unsigned int idx = n_atoms[midx++].second; if (!nonAcids[idx]) continue; Atom *atom = omol->getAtomWithIdx(idx); - // Add hydrogen to first negative acid atom, increase formal charge - // Until quaternary positive == negative total or no more negative acid - atom->setNoImplicit(true); - atom->setNumExplicitHs(atom->getNumExplicitHs() + 1); - atom->setFormalCharge(atom->getFormalCharge() + 1); - --neg_surplus; - BOOST_LOG(rdInfoLog) << "Removed negative charge.\n"; + + if (!isEarlyAtom(atom->getAtomicNum())) { + // Add hydrogen to negative atom, increase formal charge + // Until quaternary positive == negative total or no more negative + // acid + atom->setNoImplicit(true); + atom->setNumExplicitHs(atom->getNumExplicitHs() + 1); + atom->setFormalCharge(atom->getFormalCharge() + 1); + --neg_surplus; + BOOST_LOG(rdInfoLog) << "Removed negative charge.\n"; + } else if (atom->getNumExplicitHs()) { + atom->setNoImplicit(true); + atom->setNumExplicitHs(atom->getNumExplicitHs() - 1); + atom->setFormalCharge(atom->getFormalCharge() + 1); + --neg_surplus; + BOOST_LOG(rdInfoLog) << "Removed negative charge.\n"; + } } } @@ -350,11 +360,20 @@ ROMol *Uncharger::uncharge(const ROMol &mol) { for (const auto &pair : n_atoms) { auto idx = pair.second; Atom *atom = omol->getAtomWithIdx(idx); - atom->setNoImplicit(true); - while (atom->getFormalCharge() < 0) { - atom->setNumExplicitHs(atom->getNumExplicitHs() + 1); - atom->setFormalCharge(atom->getFormalCharge() + 1); - BOOST_LOG(rdInfoLog) << "Removed negative charge.\n"; + if (!isEarlyAtom(atom->getAtomicNum())) { + atom->setNoImplicit(true); + while (atom->getFormalCharge() < 0) { + atom->setNumExplicitHs(atom->getNumExplicitHs() + 1); + atom->setFormalCharge(atom->getFormalCharge() + 1); + BOOST_LOG(rdInfoLog) << "Removed negative charge.\n"; + } + } else if (atom->getNumExplicitHs()) { + atom->setNoImplicit(true); + while (atom->getFormalCharge() < 0 && atom->getNumExplicitHs()) { + atom->setNumExplicitHs(atom->getNumExplicitHs() - 1); + atom->setFormalCharge(atom->getFormalCharge() + 1); + BOOST_LOG(rdInfoLog) << "Removed negative charge.\n"; + } } } } diff --git a/Code/GraphMol/MolStandardize/catch_tests.cpp b/Code/GraphMol/MolStandardize/catch_tests.cpp index 933dd3cb5..f9a9871ad 100644 --- a/Code/GraphMol/MolStandardize/catch_tests.cpp +++ b/Code/GraphMol/MolStandardize/catch_tests.cpp @@ -143,4 +143,31 @@ TEST_CASE( REQUIRE(outm); CHECK(MolToSmiles(*outm) == "CN(C)(C)C"); } +} + +TEST_CASE( + "github #2452: incorrectly removing charge from boron anions" + "fragments ") { + SECTION("demo") { + auto m = "C[B-](C)(C)C"_smiles; + REQUIRE(m); + bool canonicalOrdering = true; + + MolStandardize::Uncharger uncharger(canonicalOrdering); + std::unique_ptr outm(uncharger.uncharge(*m)); + REQUIRE(outm); + CHECK(outm->getAtomWithIdx(1)->getFormalCharge() == -1); + CHECK(MolToSmiles(*outm) == "C[B-](C)(C)C"); + } + SECTION("should be removed") { + auto m = "C[BH-](C)(C)"_smiles; + REQUIRE(m); + bool canonicalOrdering = true; + + MolStandardize::Uncharger uncharger(canonicalOrdering); + std::unique_ptr outm(uncharger.uncharge(*m)); + REQUIRE(outm); + CHECK(outm->getAtomWithIdx(1)->getFormalCharge() == 0); + CHECK(MolToSmiles(*outm) == "CB(C)C"); + } } \ No newline at end of file