#include #include #include #include #include #include #include #include #include #include #include using namespace RDKit; void addFormalChargeIndices(const ROMol *mol, std::map &fcMap) { for (ROMol::ConstAtomIterator it = mol->beginAtoms(); it != mol->endAtoms(); ++it) { int fc = (*it)->getFormalCharge(); unsigned int idx = (*it)->getIdx(); if (fc) { if (fcMap.find(idx) == fcMap.end()) { fcMap[idx] = fc; } else { fcMap[idx] += fc; } } } } int getTotalFormalCharge(const ROMol *mol) { int totalFormalCharge = 0; for (ROMol::ConstAtomIterator it = mol->beginAtoms(); it != mol->endAtoms(); ++it) { totalFormalCharge += (*it)->getFormalCharge(); } return totalFormalCharge; } void cmpFormalChargeBondOrder(const ROMol *mol1, const ROMol *mol2) { TEST_ASSERT(mol1->getNumAtoms() == mol2->getNumAtoms()); TEST_ASSERT(mol1->getNumBonds() == mol2->getNumBonds()); for (unsigned int i = 0; i < mol1->getNumAtoms(); ++i) { TEST_ASSERT(mol1->getAtomWithIdx(i)->getFormalCharge() == mol2->getAtomWithIdx(i)->getFormalCharge()); } for (unsigned int i = 0; i < mol1->getNumBonds(); ++i) { TEST_ASSERT(mol1->getBondWithIdx(i)->getBondType() == mol2->getBondWithIdx(i)->getBondType()); } } void testBaseFunctionality() { BOOST_LOG(rdInfoLog) << "-----------------------\n" << "testBaseFunctionality" << std::endl; ResonanceMolSupplier *resMolSuppl; RWMol *mol; mol = SmilesToMol("CC"); resMolSuppl = new ResonanceMolSupplier((ROMol &)*mol); TEST_ASSERT((resMolSuppl->getAtomConjGrpIdx(0) == -1) && (resMolSuppl->getAtomConjGrpIdx(1) == -1)) delete resMolSuppl; resMolSuppl = new ResonanceMolSupplier((ROMol &)*mol); TEST_ASSERT(resMolSuppl->getNumConjGrps() == 0); TEST_ASSERT(resMolSuppl->length() == 1); TEST_ASSERT(resMolSuppl->getNumConjGrps() == 0); delete resMolSuppl; delete mol; mol = SmilesToMol("NC(=[NH2+])c1ccc(cc1)C(=O)[O-]"); int totalFormalCharge = getTotalFormalCharge(mol); resMolSuppl = new ResonanceMolSupplier((ROMol &)*mol); TEST_ASSERT(!resMolSuppl->getIsEnumerated()); TEST_ASSERT(resMolSuppl->length() == 4); TEST_ASSERT(resMolSuppl->getIsEnumerated()); delete resMolSuppl; resMolSuppl = new ResonanceMolSupplier((ROMol &)*mol); TEST_ASSERT(!resMolSuppl->getIsEnumerated()); resMolSuppl->enumerate(); TEST_ASSERT(resMolSuppl->getIsEnumerated()); auto *smol0 = (*resMolSuppl)[0]; auto *smol1 = (*resMolSuppl)[1]; TEST_ASSERT((smol0->getBondBetweenAtoms(0, 1)->getBondType() != smol1->getBondBetweenAtoms(0, 1)->getBondType()) || (smol0->getBondBetweenAtoms(9, 10)->getBondType() != smol1->getBondBetweenAtoms(9, 10)->getBondType())); delete smol0; delete smol1; delete resMolSuppl; resMolSuppl = new ResonanceMolSupplier((ROMol &)*mol, ResonanceMolSupplier::KEKULE_ALL); TEST_ASSERT(resMolSuppl->length() == 8); std::set bondTypeSet; smol0 = (*resMolSuppl)[0]; smol1 = (*resMolSuppl)[1]; // check that we actually have two alternate Kekule structures bondTypeSet.insert(smol0->getBondBetweenAtoms(3, 4)->getBondType()); bondTypeSet.insert(smol1->getBondBetweenAtoms(3, 4)->getBondType()); TEST_ASSERT(bondTypeSet.size() == 2); bondTypeSet.clear(); delete smol0; delete smol1; delete resMolSuppl; resMolSuppl = new ResonanceMolSupplier( (ROMol &)*mol, ResonanceMolSupplier::UNCONSTRAINED_CATIONS | ResonanceMolSupplier::UNCONSTRAINED_ANIONS); TEST_ASSERT(resMolSuppl->length() == 32); for (unsigned int i = 0; i < resMolSuppl->length(); ++i) { ROMol *resMol = (*resMolSuppl)[i]; TEST_ASSERT(getTotalFormalCharge(resMol) == totalFormalCharge); delete resMol; } while (!resMolSuppl->atEnd()) { ROMol *resMol = resMolSuppl->next(); TEST_ASSERT(getTotalFormalCharge(resMol) == totalFormalCharge); delete resMol; } resMolSuppl->reset(); smol0 = (*resMolSuppl)[0]; smol1 = resMolSuppl->next(); cmpFormalChargeBondOrder(smol0, smol1); delete smol0; delete smol1; resMolSuppl->moveTo(12); smol0 = (*resMolSuppl)[12]; smol1 = resMolSuppl->next(); cmpFormalChargeBondOrder(smol0, smol1); delete resMolSuppl; delete smol0; delete smol1; resMolSuppl = new ResonanceMolSupplier((ROMol &)*mol, ResonanceMolSupplier::ALLOW_INCOMPLETE_OCTETS | ResonanceMolSupplier::UNCONSTRAINED_CATIONS | ResonanceMolSupplier::UNCONSTRAINED_ANIONS, 10); TEST_ASSERT(resMolSuppl->length() == 10); delete resMolSuppl; resMolSuppl = new ResonanceMolSupplier((ROMol &)*mol, ResonanceMolSupplier::ALLOW_INCOMPLETE_OCTETS | ResonanceMolSupplier::UNCONSTRAINED_CATIONS | ResonanceMolSupplier::UNCONSTRAINED_ANIONS, 0); TEST_ASSERT(resMolSuppl->length() == 0); delete resMolSuppl; delete mol; mol = SmilesToMol("CC(C)C(C(=O)OC(C#N)c1cccc(Oc2ccccc2)c1)c3ccc(OC(F)F)cc3"); resMolSuppl = new ResonanceMolSupplier((ROMol &)*mol, ResonanceMolSupplier::KEKULE_ALL, 3); TEST_ASSERT(!resMolSuppl->getIsEnumerated()); TEST_ASSERT(resMolSuppl->length() == 3); TEST_ASSERT(resMolSuppl->getIsEnumerated()); delete resMolSuppl; delete mol; } void testBenzylCation() { BOOST_LOG(rdInfoLog) << "-----------------------\n" << "testBenzylCation" << std::endl; RWMol *mol = SmilesToMol("[CH2+]c1ccccc1"); ResonanceMolSupplier *resMolSuppl; std::map fcMap; resMolSuppl = new ResonanceMolSupplier((ROMol &)*mol); TEST_ASSERT(resMolSuppl->length() == 4); while (!resMolSuppl->atEnd()) { ROMol *resMol = resMolSuppl->next(); addFormalChargeIndices(resMol, fcMap); delete resMol; } unsigned int indices[] = {0, 2, 4, 6}; for (unsigned int &idx : indices) { TEST_ASSERT(fcMap.find(idx) != fcMap.end()); TEST_ASSERT(fcMap[idx] == 1); } delete resMolSuppl; delete mol; } void testBenzylAnion() { BOOST_LOG(rdInfoLog) << "-----------------------\n" << "testBenzylAnion" << std::endl; RWMol *mol = SmilesToMol("[CH2-]c1ccccc1"); ResonanceMolSupplier *resMolSuppl; std::map fcMap; resMolSuppl = new ResonanceMolSupplier((ROMol &)*mol); TEST_ASSERT(resMolSuppl->length() == 4); while (!resMolSuppl->atEnd()) { ROMol *resMol = resMolSuppl->next(); addFormalChargeIndices(resMol, fcMap); delete resMol; } unsigned int indices[] = {0, 2, 4, 6}; for (unsigned int &idx : indices) { TEST_ASSERT(fcMap.find(idx) != fcMap.end()); TEST_ASSERT(fcMap[idx] == -1); } delete resMolSuppl; delete mol; } void testButadiene() { BOOST_LOG(rdInfoLog) << "-----------------------\n" << "testButadiene" << std::endl; RWMol *mol = SmilesToMol("C=CC=C"); ResonanceMolSupplier *resMolSuppl; resMolSuppl = new ResonanceMolSupplier((ROMol &)*mol); TEST_ASSERT(resMolSuppl->length() == 1); delete resMolSuppl; resMolSuppl = new ResonanceMolSupplier( (ROMol &)*mol, ResonanceMolSupplier::UNCONSTRAINED_CATIONS | ResonanceMolSupplier::UNCONSTRAINED_ANIONS); TEST_ASSERT(resMolSuppl->length() == 3); std::map fcMap; while (!resMolSuppl->atEnd()) { ROMol *resMol = resMolSuppl->next(); addFormalChargeIndices(resMol, fcMap); delete resMol; } unsigned int indices[] = {0, 3}; for (unsigned int &idx : indices) { TEST_ASSERT(fcMap.find(idx) != fcMap.end()); TEST_ASSERT(fcMap[idx] == 0); } delete resMolSuppl; delete mol; } void testZwitterion() { BOOST_LOG(rdInfoLog) << "-----------------------\n" << "testZwitterion" << std::endl; RWMol *mol = SmilesToMol("NC=CC=CC=O"); ResonanceMolSupplier *resMolSuppl; resMolSuppl = new ResonanceMolSupplier((ROMol &)*mol); TEST_ASSERT(resMolSuppl->length() == 1); ROMol *mol0 = (*resMolSuppl)[0]; TEST_ASSERT(mol0->getAtomWithIdx(0)->getFormalCharge() == 0); TEST_ASSERT(mol0->getAtomWithIdx(6)->getFormalCharge() == 0); delete mol0; delete resMolSuppl; resMolSuppl = new ResonanceMolSupplier( (ROMol &)*mol, ResonanceMolSupplier::ALLOW_CHARGE_SEPARATION); TEST_ASSERT(resMolSuppl->length() == 2); mol0 = (*resMolSuppl)[0]; ROMol *mol1 = (*resMolSuppl)[1]; TEST_ASSERT(mol1->getAtomWithIdx(0)->getFormalCharge() == 1); TEST_ASSERT(mol1->getAtomWithIdx(6)->getFormalCharge() == -1); delete mol0; delete mol1; delete resMolSuppl; delete mol; } void testChargeMigration() { BOOST_LOG(rdInfoLog) << "-----------------------\n" << "testChargeMigration" << std::endl; RWMol *mol = SmilesToMol("C=CC=CC=C[CH2+]"); ResonanceMolSupplier *resMolSuppl; resMolSuppl = new ResonanceMolSupplier((ROMol &)*mol); TEST_ASSERT(resMolSuppl->length() == 4); std::map fcMap; while (!resMolSuppl->atEnd()) { ROMol *resMol = resMolSuppl->next(); addFormalChargeIndices(resMol, fcMap); delete resMol; } unsigned int indices[] = {0, 2, 4, 6}; for (unsigned int &idx : indices) { TEST_ASSERT(fcMap.find(idx) != fcMap.end()); TEST_ASSERT(fcMap[idx] == 1); } delete resMolSuppl; delete mol; } void testChargeSeparation1() { BOOST_LOG(rdInfoLog) << "-----------------------\n" << "testChargeSeparation1" << std::endl; RWMol *mol = SmilesToMol("[NH2+]=C1C=CC(C=C1)=CN=CN"); ResonanceMolSupplier *resMolSuppl; resMolSuppl = new ResonanceMolSupplier((ROMol &)*mol); TEST_ASSERT(resMolSuppl->length() == 3); std::map fcMap; for (unsigned int i = 0; i < 3; ++i) { ROMol *resMol = (*resMolSuppl)[i]; addFormalChargeIndices(resMol, fcMap); delete resMol; } unsigned int indices[] = {0, 8, 10}; for (unsigned int &idx : indices) { TEST_ASSERT(fcMap.find(idx) != fcMap.end()); TEST_ASSERT(fcMap[idx] == 1); } delete resMolSuppl; resMolSuppl = new ResonanceMolSupplier( (ROMol &)*mol, ResonanceMolSupplier::ALLOW_CHARGE_SEPARATION); TEST_ASSERT(resMolSuppl->length() == 4); resMolSuppl->moveTo(3); ROMol *resMol = resMolSuppl->next(); TEST_ASSERT(resMol->getAtomWithIdx(0)->getFormalCharge() == 1); TEST_ASSERT(resMol->getAtomWithIdx(10)->getFormalCharge() == 1); TEST_ASSERT(resMol->getAtomWithIdx(8)->getFormalCharge() == -1); delete resMol; delete resMolSuppl; delete mol; } void testChargeSeparation2() { BOOST_LOG(rdInfoLog) << "-----------------------\n" << "testChargeSeparation2" << std::endl; RWMol *mol = SmilesToMol("NC(C(=O)[O-])=CC=CC=O"); ResonanceMolSupplier *resMolSuppl; resMolSuppl = new ResonanceMolSupplier((ROMol &)*mol); TEST_ASSERT(resMolSuppl->length() == 2); delete resMolSuppl; resMolSuppl = new ResonanceMolSupplier( (ROMol &)*mol, ResonanceMolSupplier::ALLOW_CHARGE_SEPARATION); TEST_ASSERT(resMolSuppl->length() == 4); // less charge separation in the first 2, // more charge separation in the last 6 // the last 4 feature carbanions delete resMolSuppl; resMolSuppl = new ResonanceMolSupplier( (ROMol &)*mol, ResonanceMolSupplier::UNCONSTRAINED_ANIONS); TEST_ASSERT(resMolSuppl->length() == 8); std::map fcMap; for (unsigned int i = 0; i < 2; ++i) { ROMol *resMol = (*resMolSuppl)[i]; addFormalChargeIndices(resMol, fcMap); TEST_ASSERT(fcMap.size() == 1); fcMap.clear(); delete resMol; } for (unsigned int i = 2; i < 8; ++i) { ROMol *resMol = (*resMolSuppl)[i]; addFormalChargeIndices(resMol, fcMap); TEST_ASSERT(fcMap.size() == 3); fcMap.clear(); delete resMol; } for (unsigned int i = 0; i < 4; ++i) { const ROMol *resMol = (*resMolSuppl)[i]; bool haveCarbanion = false; for (ROMol::ConstAtomIterator it = resMol->beginAtoms(); it != resMol->endAtoms(); ++it) { if (((*it)->getAtomicNum() == 6) && ((*it)->getFormalCharge() == -1)) { haveCarbanion = true; } } TEST_ASSERT(!haveCarbanion); delete resMol; } for (unsigned int i = 4; i < 8; ++i) { const ROMol *resMol = (*resMolSuppl)[i]; bool haveCarbanion = false; for (ROMol::ConstAtomIterator it = resMol->beginAtoms(); it != resMol->endAtoms(); ++it) { if (((*it)->getAtomicNum() == 6) && ((*it)->getFormalCharge() == -1)) { haveCarbanion = true; } } TEST_ASSERT(haveCarbanion); delete resMol; } delete resMolSuppl; delete mol; } void testMultipleConjGroups1() { BOOST_LOG(rdInfoLog) << "-----------------------\n" << "testMultipleConjGroups1" << std::endl; RWMol *mol = SmilesToMol("NC(C(=O)[O-])=CC=C(CCC(=O)[O-])C=O"); ResonanceMolSupplier *resMolSuppl; resMolSuppl = new ResonanceMolSupplier( (ROMol &)*mol, ResonanceMolSupplier::ALLOW_CHARGE_SEPARATION); TEST_ASSERT(resMolSuppl->length() == 8); delete resMolSuppl; resMolSuppl = new ResonanceMolSupplier( (ROMol &)*mol, ResonanceMolSupplier::UNCONSTRAINED_ANIONS); TEST_ASSERT(resMolSuppl->length() == 16); for (unsigned int i = 0; i < 8; ++i) { const ROMol *resMol = (*resMolSuppl)[i]; bool haveCarbanion = false; for (ROMol::ConstAtomIterator it = resMol->beginAtoms(); it != resMol->endAtoms(); ++it) { if (((*it)->getAtomicNum() == 6) && ((*it)->getFormalCharge() == -1)) { haveCarbanion = true; } } TEST_ASSERT(!haveCarbanion); delete resMol; } for (unsigned int i = 8; i < 16; ++i) { const ROMol *resMol = (*resMolSuppl)[i]; bool haveCarbanion = false; for (ROMol::ConstAtomIterator it = resMol->beginAtoms(); it != resMol->endAtoms(); ++it) { if (((*it)->getAtomicNum() == 6) && ((*it)->getFormalCharge() == -1)) { haveCarbanion = true; } } TEST_ASSERT(haveCarbanion); delete resMol; } delete resMolSuppl; resMolSuppl = new ResonanceMolSupplier( (ROMol &)*mol, ResonanceMolSupplier::UNCONSTRAINED_ANIONS, 8); TEST_ASSERT(resMolSuppl->length() == 8); for (unsigned int i = 0; i < 8; ++i) { const ROMol *resMol = (*resMolSuppl)[i]; bool haveCarbanion = false; for (ROMol::ConstAtomIterator it = resMol->beginAtoms(); it != resMol->endAtoms(); ++it) { if (((*it)->getAtomicNum() == 6) && ((*it)->getFormalCharge() == -1)) { haveCarbanion = true; } } TEST_ASSERT(!haveCarbanion); delete resMol; } delete resMolSuppl; delete mol; } void testMultipleConjGroups2() { // if breadth-first resonance structure enumeration works properly // all N(2-) should be at the end of the supplier BOOST_LOG(rdInfoLog) << "-----------------------\n" << "testMultipleConjGroups2" << std::endl; RWMol *mol = SmilesToMol("[N-]=[N+]=NCN=[N+]=[N-]"); ResonanceMolSupplier *resMolSuppl; resMolSuppl = new ResonanceMolSupplier( (ROMol &)*mol, ResonanceMolSupplier::ALLOW_CHARGE_SEPARATION); TEST_ASSERT(resMolSuppl->length() == 9); bool haveFirstDoubleNeg = false; for (unsigned int i = 0; i < resMolSuppl->length(); ++i) { const ROMol *resMol = (*resMolSuppl)[i]; bool haveDoubleNeg = false; for (ROMol::ConstAtomIterator it = resMol->beginAtoms(); it != resMol->endAtoms(); ++it) { if (((*it)->getAtomicNum() == 7) && ((*it)->getFormalCharge() == -2)) { haveDoubleNeg = true; } } if (!haveFirstDoubleNeg && haveDoubleNeg) { haveFirstDoubleNeg = true; } TEST_ASSERT(!haveFirstDoubleNeg || (haveFirstDoubleNeg && haveDoubleNeg)); delete resMol; } delete resMolSuppl; delete mol; } void testDimethylMalonate() { // carbanion should be last BOOST_LOG(rdInfoLog) << "-----------------------\n" << "testDimethylMalonate" << std::endl; RWMol *mol = SmilesToMol("COC(=O)[CH-]C(=O)OC"); ResonanceMolSupplier *resMolSuppl; resMolSuppl = new ResonanceMolSupplier((ROMol &)*mol); TEST_ASSERT(resMolSuppl->length() == 3); for (unsigned int i = 0; i < 3; ++i) { const ROMol *resMol = (*resMolSuppl)[i]; bool haveCarbanion = false; for (ROMol::ConstAtomIterator it = resMol->beginAtoms(); it != resMol->endAtoms(); ++it) { if (((*it)->getAtomicNum() == 6) && ((*it)->getFormalCharge() == -1)) { haveCarbanion = true; } } TEST_ASSERT(((i < 2) && !haveCarbanion) || ((i == 2) && haveCarbanion)); delete resMol; } delete resMolSuppl; delete mol; } void testMethylAcetate() { // cation on oxygen should appear only when UNCONSTRAINED_CATIONS is set BOOST_LOG(rdInfoLog) << "-----------------------\n" << "testMethylAcetate" << std::endl; RWMol *mol = SmilesToMol("CC(=O)OC"); ResonanceMolSupplier *resMolSuppl; resMolSuppl = new ResonanceMolSupplier((ROMol &)*mol); TEST_ASSERT(resMolSuppl->length() == 1); const ROMol *resMol = (*resMolSuppl)[0]; bool haveCation = false; for (ROMol::ConstAtomIterator it = resMol->beginAtoms(); it != resMol->endAtoms(); ++it) { if (((*it)->getAtomicNum() == 8) && ((*it)->getFormalCharge() == 1)) { haveCation = true; } } delete resMol; TEST_ASSERT(!haveCation); delete resMolSuppl; resMolSuppl = new ResonanceMolSupplier( (ROMol &)*mol, ResonanceMolSupplier::UNCONSTRAINED_CATIONS); TEST_ASSERT(resMolSuppl->length() == 2); for (unsigned int i = 0; i < 2; ++i) { const ROMol *resMol = (*resMolSuppl)[i]; bool haveCation = false; for (ROMol::ConstAtomIterator it = resMol->beginAtoms(); it != resMol->endAtoms(); ++it) { if (((*it)->getAtomicNum() == 8) && ((*it)->getFormalCharge() == 1)) { haveCation = true; } } TEST_ASSERT(((i == 0) && !haveCation) || ((i == 1) && haveCation)); delete resMol; } delete resMolSuppl; delete mol; } void testPyranium2_6dicarboxylate() { // when there is no alternative cation on elements right of N // should be accepted even though the total formal charge is not // positive BOOST_LOG(rdInfoLog) << "-----------------------\n" << "testPyranium2_6dicarboxylate" << std::endl; RWMol *mol = SmilesToMol("[o+]1c(C(=O)[O-])cccc1C(=O)[O-]"); ResonanceMolSupplier *resMolSuppl; resMolSuppl = new ResonanceMolSupplier((ROMol &)*mol); TEST_ASSERT(resMolSuppl->length() == 4); for (unsigned int i = 0; i < 4; ++i) { const ROMol *resMol = (*resMolSuppl)[i]; TEST_ASSERT(resMol->getAtomWithIdx(0)->getFormalCharge() == 1); delete resMol; } delete resMolSuppl; delete mol; } void testSubstructMatchAcetate() { BOOST_LOG(rdInfoLog) << "-----------------------\n" << "testSubstructMatchAcetate" << std::endl; RWMol *mol = SmilesToMol("CC(=O)[O-]"); RWMol *query = SmartsToMol("C(=O)[O-]"); ResonanceMolSupplier *resMolSuppl; unsigned int n; resMolSuppl = new ResonanceMolSupplier((ROMol &)*mol); std::vector matchVect; n = SubstructMatch(*mol, *query, matchVect, false, true, false, false); TEST_ASSERT(n == 1); matchVect.clear(); n = SubstructMatch(*resMolSuppl, *query, matchVect, false, true, false, false); TEST_ASSERT(n == 2); delete mol; delete query; delete resMolSuppl; } void testSubstructMatchDMAP() { BOOST_LOG(rdInfoLog) << "-----------------------\n" << "testSubstructMatchDMAP" << std::endl; RWMol *mol = SmilesToMol("C(C)Nc1cc[nH+]cc1"); RWMol *query = SmartsToMol("[#7+]"); ResonanceMolSupplier *resMolSuppl; unsigned int n; MatchVectType p; resMolSuppl = new ResonanceMolSupplier((ROMol &)*mol); std::vector matchVect; n = SubstructMatch(*mol, *query, matchVect, false, true, false, false); TEST_ASSERT((n == 1) && (matchVect.size() == 1)); p = matchVect[0]; TEST_ASSERT((p.size() == 1) && (p[0].second == 6)); matchVect.clear(); n = SubstructMatch(*resMolSuppl, *query, matchVect, false, true, false, false); TEST_ASSERT((n == 2) && (matchVect.size() == 2)); std::vector v; p = matchVect[0]; TEST_ASSERT(p.size() == 1); v.push_back(p[0].second); p = matchVect[1]; TEST_ASSERT(p.size() == 1); v.push_back(p[0].second); std::sort(v.begin(), v.end()); TEST_ASSERT(v[0] == 2); TEST_ASSERT(v[1] == 6); delete mol; delete query; delete resMolSuppl; } void setResidueFormalCharge(RWMol *mol, std::vector &res, int fc) { for (std::vector::const_iterator it = res.begin(); it != res.end(); ++it) { std::vector matchVect; SubstructMatch(*mol, *(*it), matchVect); for (std::vector::const_iterator it = matchVect.begin(); it != matchVect.end(); ++it) { mol->getAtomWithIdx((*it).back().second)->setFormalCharge(fc); } } } void getBtVectVect(ResonanceMolSupplier *resMolSuppl, std::vector> &btVect2) { while (!resMolSuppl->atEnd()) { ROMol *resMol = resMolSuppl->next(); std::vector bt; bt.reserve(resMol->getNumBonds()); for (ROMol::BondIterator bi = resMol->beginBonds(); bi != resMol->endBonds(); ++bi) { bt.push_back(static_cast((*bi)->getBondTypeAsDouble())); } btVect2.push_back(bt); delete resMol; } for (unsigned int i = 0; i < btVect2.size(); ++i) { bool same = true; for (unsigned int j = 0; same && (j < btVect2[i].size()); ++j) { if (!i) { continue; } if (same) { same = (btVect2[i][j] == btVect2[i - 1][j]); } } if (i) { TEST_ASSERT(!same); } } } void testCrambin() { BOOST_LOG(rdInfoLog) << "-----------------------\n" << "testCrambin" << std::endl; std::string pathName = getenv("RDBASE"); pathName += "/Code/GraphMol/FileParsers/test_data/1CRN.pdb"; RWMol *crambin = PDBFileToMol(pathName); TEST_ASSERT(crambin); RWMol *query; unsigned int n; std::vector res; // protonate NH2 query = SmartsToMol("[Nh2][Ch;Ch2]"); TEST_ASSERT(query); res.push_back(query); // protonate Arg query = SmartsToMol("[Nh][C]([Nh2])=[Nh]"); TEST_ASSERT(query); res.push_back(query); setResidueFormalCharge(crambin, res, 1); for (std::vector::const_iterator it = res.begin(); it != res.end(); ++it) { delete *it; } res.clear(); // deprotonate COOH query = SmartsToMol("C(=O)[Oh]"); TEST_ASSERT(query); res.push_back(query); setResidueFormalCharge(crambin, res, -1); for (std::vector::const_iterator it = res.begin(); it != res.end(); ++it) { delete *it; } auto *resMolSupplST = new ResonanceMolSupplier((ROMol &)*crambin); TEST_ASSERT(resMolSupplST); // crambin has 2 Arg (3 resonance structures each); 1 Asp, 1 Glu // and 1 terminal COO- (2 resonance structures each) // so possible resonance structures are 3^2 * 2^3 = 72 TEST_ASSERT(resMolSupplST->length() == 72); TEST_ASSERT(resMolSupplST->getNumConjGrps() == 56); RWMol *carboxylateQuery = SmartsToMol("C(=O)[O-]"); RWMol *guanidiniumQuery = SmartsToMol("NC(=[NH2+])N"); std::vector matchVect; n = SubstructMatch(*crambin, *carboxylateQuery, matchVect, false, true, false, false, 1000); TEST_ASSERT(n == 3); n = SubstructMatch(*crambin, *carboxylateQuery, matchVect, true, true, false, false, 1000); TEST_ASSERT(n == 3); n = SubstructMatch(*crambin, *guanidiniumQuery, matchVect, false, true, false, false, 1000); TEST_ASSERT(n == 0); n = SubstructMatch(*crambin, *guanidiniumQuery, matchVect, true, true, false, false, 1000); TEST_ASSERT(n == 0); n = SubstructMatch(*resMolSupplST, *carboxylateQuery, matchVect, false, true, false, false, 1000); TEST_ASSERT(n == 6); n = SubstructMatch(*resMolSupplST, *carboxylateQuery, matchVect, true, true, false, false, 1000); TEST_ASSERT(n == 3); n = SubstructMatch(*resMolSupplST, *guanidiniumQuery, matchVect, false, true, false, false, 1000); TEST_ASSERT(n == 8); n = SubstructMatch(*resMolSupplST, *guanidiniumQuery, matchVect, true, true, false, false, 1000); TEST_ASSERT(n == 2); #ifdef RDK_TEST_MULTITHREADED std::vector> btVect2ST; getBtVectVect(resMolSupplST, btVect2ST); auto *resMolSupplMT = new ResonanceMolSupplier((ROMol &)*crambin, 0, 1000); TEST_ASSERT(resMolSupplMT); resMolSupplMT->setNumThreads(0); TEST_ASSERT(resMolSupplST->length() == resMolSupplMT->length()); std::vector> btVect2MT; getBtVectVect(resMolSupplMT, btVect2MT); TEST_ASSERT(btVect2ST.size() == btVect2MT.size()); for (unsigned int i = 0; i < btVect2ST.size(); ++i) { for (unsigned int j = 0; j < btVect2ST[i].size(); ++j) { TEST_ASSERT(btVect2ST[i][j] == btVect2MT[i][j]); } } ResonanceMolSupplier *ptr[2] = {resMolSupplST, resMolSupplMT}; for (auto &i : ptr) { n = SubstructMatch(*i, *carboxylateQuery, matchVect, false, true, false, false, 1000, 0); TEST_ASSERT(n == 6); n = SubstructMatch(*i, *carboxylateQuery, matchVect, true, true, false, false, 1000, 0); TEST_ASSERT(n == 3); n = SubstructMatch(*i, *guanidiniumQuery, matchVect, false, true, false, false, 1000, 0); TEST_ASSERT(n == 8); n = SubstructMatch(*i, *guanidiniumQuery, matchVect, true, true, false, false, 1000, 0); TEST_ASSERT(n == 2); } delete resMolSupplMT; #endif delete resMolSupplST; delete carboxylateQuery; delete guanidiniumQuery; delete crambin; } void testConjGrpPerception() { BOOST_LOG(rdInfoLog) << "-----------------------\n" << "testConjGrpPerception" << std::endl; RWMol *mol1 = MolBlockToMol(R"SDF( RDKit 2D 14 15 0 0 0 0 0 0 0 0999 V2000 3.7539 -1.2744 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 2.4317 -0.5660 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 1.1571 -1.3568 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -0.1651 -0.6484 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -1.4397 -1.4393 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -1.3921 -2.9385 0.0000 F 0 0 0 0 0 0 0 0 0 0 0 0 -2.7619 -0.7309 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -2.8095 0.7684 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -4.1316 1.4768 0.0000 F 0 0 0 0 0 0 0 0 0 0 0 0 -1.5349 1.5592 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -0.2127 0.8508 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 1.0619 1.6417 0.0000 N 0 0 0 0 0 0 0 0 0 0 0 0 2.3841 0.9333 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 3.6587 1.7241 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 1 2 1 0 2 3 4 0 3 4 4 0 4 5 4 0 5 6 1 0 5 7 4 0 7 8 4 0 8 9 1 0 8 10 4 0 10 11 4 0 11 12 4 0 12 13 4 0 13 14 1 0 13 2 4 0 11 4 4 0 M END )SDF"); RWMol *mol2 = MolBlockToMol(R"SDF( RDKit 2D 14 15 0 0 0 0 0 0 0 0999 V2000 1.0619 -1.6417 0.0000 N 0 0 0 0 0 0 0 0 0 0 0 0 -0.2127 -0.8508 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -1.5349 -1.5592 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -2.8095 -0.7684 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -2.7619 0.7309 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -1.4397 1.4393 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -0.1651 0.6484 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 1.1571 1.3568 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 2.4317 0.5660 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 3.7539 1.2744 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 2.3841 -0.9333 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 3.6587 -1.7241 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -4.1316 -1.4768 0.0000 F 0 0 0 0 0 0 0 0 0 0 0 0 -1.3921 2.9385 0.0000 F 0 0 0 0 0 0 0 0 0 0 0 0 1 2 4 0 3 4 4 0 4 5 4 0 5 6 4 0 2 3 4 0 2 7 4 0 7 8 4 0 8 9 4 0 9 10 1 0 9 11 4 0 11 12 1 0 11 1 4 0 6 7 4 0 4 13 1 0 6 14 1 0 M END )SDF"); auto *resMolSuppl1 = new ResonanceMolSupplier( static_cast(*mol1), ResonanceMolSupplier::KEKULE_ALL); TEST_ASSERT(resMolSuppl1->length() == 3); auto *resMolSuppl2 = new ResonanceMolSupplier( static_cast(*mol2), ResonanceMolSupplier::KEKULE_ALL); TEST_ASSERT(resMolSuppl2->length() == 3); delete resMolSuppl1; delete resMolSuppl2; delete mol1; delete mol2; } void testGitHub1166() { BOOST_LOG(rdInfoLog) << "-----------------------\n" << "testGitHub1166" << std::endl; RWMol *mol = SmilesToMol("NC(=[NH2+])c1ccc(cc1)C(=O)[O-]"); auto *resMolSuppl = new ResonanceMolSupplier( *mol, ResonanceMolSupplier::ALLOW_CHARGE_SEPARATION | ResonanceMolSupplier::KEKULE_ALL); TEST_ASSERT(resMolSuppl->length() == 8); // check that formal charges on odd indices are in the same position // as on even indices for (unsigned int i = 0; i < resMolSuppl->length(); i += 2) { auto *smol0 = (*resMolSuppl)[i]; auto *smol1 = (*resMolSuppl)[i + 1]; TEST_ASSERT(smol0->getNumAtoms() == smol1->getNumAtoms()); for (unsigned int atomIdx = 0; atomIdx < smol0->getNumAtoms(); ++atomIdx) { TEST_ASSERT(smol0->getAtomWithIdx(atomIdx)->getFormalCharge() == smol1->getAtomWithIdx(atomIdx)->getFormalCharge()); } // check that bond orders are alternate on aromatic bonds between // structures on odd indices and structures on even indices TEST_ASSERT(smol0->getNumBonds() == smol1->getNumBonds()); for (unsigned int bondIdx = 0; bondIdx < smol0->getNumBonds(); ++bondIdx) { TEST_ASSERT( (!smol0->getBondWithIdx(bondIdx)->getIsAromatic() && !smol1->getBondWithIdx(bondIdx)->getIsAromatic() && (smol0->getBondWithIdx(bondIdx)->getBondType() == smol1->getBondWithIdx(bondIdx)->getBondType())) || (smol0->getBondWithIdx(bondIdx)->getIsAromatic() && smol1->getBondWithIdx(bondIdx)->getIsAromatic() && (static_cast( smol0->getBondWithIdx(bondIdx)->getBondTypeAsDouble() + smol1->getBondWithIdx(bondIdx)->getBondTypeAsDouble()) == 3))); } delete smol0; delete smol1; } delete resMolSuppl; delete mol; } void testGitHub3048() { BOOST_LOG(rdInfoLog) << "-----------------------\n" << "testGitHub3048" << std::endl; RWMol *mol = SmilesToMol("C1CN3N(C1)c2ccccc2N=C3N"); auto *resMolSuppl = new ResonanceMolSupplier(*mol, ResonanceMolSupplier::KEKULE_ALL); // This caused a segfault due to a null ptr being accessed (#3048) TEST_ASSERT(resMolSuppl->length() == 2); delete resMolSuppl; delete mol; } void testGitHub2597() { BOOST_LOG(rdInfoLog) << "-----------------------\n" << "testGitHub2597" << std::endl; { class MyCallBack : public ResonanceMolSupplierCallback { bool operator()() override { TEST_ASSERT(getNumConjGrps() == 1); return (getNumStructures(0) < 12); } }; class MyCallBack2 : public ResonanceMolSupplierCallback { bool operator()() override { TEST_ASSERT(getNumConjGrps() == 1); return (getNumDiverseStructures(0) < 8); } }; RWMol *mol = SmilesToMol( "ClC1=NC(NC2=CC=CC3=C2C(=O)C2=CC=CC=C2C3=O)=NC(NC2=CC=CC3=C2C(=O)C2=CC=" "CC=C2C3=O)=N1"); auto *resMolSuppl = new ResonanceMolSupplier(*mol); TEST_ASSERT(resMolSuppl->length() == 1); delete resMolSuppl; resMolSuppl = new ResonanceMolSupplier(*mol, ResonanceMolSupplier::KEKULE_ALL); TEST_ASSERT(resMolSuppl->length() == 32); delete resMolSuppl; resMolSuppl = new ResonanceMolSupplier( *mol, ResonanceMolSupplier::ALLOW_CHARGE_SEPARATION, 10); TEST_ASSERT(!resMolSuppl->getProgressCallback()); TEST_ASSERT(resMolSuppl->length() == 10); TEST_ASSERT(!resMolSuppl->wasCanceled()); delete resMolSuppl; resMolSuppl = new ResonanceMolSupplier( *mol, ResonanceMolSupplier::ALLOW_CHARGE_SEPARATION); MyCallBack *callback = new MyCallBack(); resMolSuppl->setProgressCallback(callback); TEST_ASSERT(resMolSuppl->getProgressCallback() == callback); TEST_ASSERT(resMolSuppl->length() == 12); TEST_ASSERT(resMolSuppl->wasCanceled()); delete resMolSuppl; resMolSuppl = new ResonanceMolSupplier( *mol, ResonanceMolSupplier::ALLOW_CHARGE_SEPARATION); resMolSuppl->setProgressCallback(new MyCallBack2()); TEST_ASSERT(resMolSuppl->getProgressCallback()); resMolSuppl->setProgressCallback(nullptr); TEST_ASSERT(!resMolSuppl->getProgressCallback()); resMolSuppl->setProgressCallback(new MyCallBack2()); TEST_ASSERT(resMolSuppl->length() == 9); TEST_ASSERT(resMolSuppl->wasCanceled()); delete resMolSuppl; delete mol; } } void testGitHub3349() { BOOST_LOG(rdInfoLog) << "-----------------------\n" << "testGitHub3349" << std::endl; RWMol *mol = SmilesToMol("CC(=O)[O-]->[*]"); auto *resMolSuppl = new ResonanceMolSupplier(*mol, ResonanceMolSupplier::KEKULE_ALL); // This erroneously returned a single resonance structure // as dative and zero-order bonds were not accounted for (#3349) TEST_ASSERT(resMolSuppl->length() == 2); delete resMolSuppl; delete mol; } int main() { RDLog::InitLogs(); #if 1 testBaseFunctionality(); testBenzylCation(); testBenzylAnion(); testButadiene(); testZwitterion(); testChargeMigration(); testChargeSeparation1(); testChargeSeparation2(); testMultipleConjGroups1(); testMultipleConjGroups2(); testDimethylMalonate(); testMethylAcetate(); testPyranium2_6dicarboxylate(); testSubstructMatchAcetate(); testSubstructMatchDMAP(); testCrambin(); testGitHub1166(); testConjGrpPerception(); testGitHub3048(); testGitHub2597(); testGitHub3349(); #endif return 0; }