// // Copyright (C) 2002-2021 Greg Landrum and other RDKit contributors // // @@ All Rights Reserved @@ // This file is part of the RDKit. // The contents are covered by the terms of the BSD license // which is included in the file license.txt, found at the root // of the RDKit source tree. // #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifndef M_PI #define M_PI 3.14159265358979323846 #endif using namespace RDKit; using namespace std; RWMol _t; typedef class ROMol Mol; void test1() { string smi; Mol *m; INT_VECT iv; unsigned int count; std::vector frags; smi = "CCCC(=O)O"; m = SmilesToMol(smi); CHECK_INVARIANT(m, "smiles parse failed"); count = MolOps::getMolFrags(*m, iv); CHECK_INVARIANT(count == 1, "bad frag count"); frags = MolOps::getMolFrags(*m); CHECK_INVARIANT(frags.size() == 1, "bad frag count"); TEST_ASSERT(frags[0]->getNumAtoms() == 6); delete m; smi = "CCCC(=O)[O-].[Na+]"; m = SmilesToMol(smi); CHECK_INVARIANT(m, "smiles parse failed"); count = MolOps::getMolFrags(*m, iv); CHECK_INVARIANT(count == 2, "bad frag count"); frags = MolOps::getMolFrags(*m); CHECK_INVARIANT(frags.size() == 2, "bad frag count"); TEST_ASSERT(frags[0]->getNumAtoms() == 6); TEST_ASSERT(frags[1]->getNumAtoms() == 1); frags = MolOps::getMolFrags(*m, true, &iv); CHECK_INVARIANT(frags.size() == 2, "bad frag count"); TEST_ASSERT(frags[0]->getNumAtoms() == 6); TEST_ASSERT(frags[1]->getNumAtoms() == 1); TEST_ASSERT(iv.size() == 7); TEST_ASSERT(iv[0] == 0) TEST_ASSERT(iv[6] == 1) delete m; smi = "CCCC(=O)[O-].[Na+].[NH4+].[Cl-]"; m = SmilesToMol(smi); CHECK_INVARIANT(m, "smiles parse failed"); count = MolOps::getMolFrags(*m, iv); CHECK_INVARIANT(count == 4, "bad frag count"); frags = MolOps::getMolFrags(*m); CHECK_INVARIANT(frags.size() == 4, "bad frag count"); TEST_ASSERT(frags[0]->getNumAtoms() == 6); TEST_ASSERT(frags[1]->getNumAtoms() == 1); TEST_ASSERT(frags[2]->getNumAtoms() == 1); TEST_ASSERT(frags[3]->getNumAtoms() == 1); delete m; }; void test2() { string smi; Mol *m; INT_VECT iv; int count; smi = "CCCC(=O)O"; m = SmilesToMol(smi); CHECK_INVARIANT(m, "smiles parse failed"); count = MolOps::getMolFrags(*m, iv); CHECK_INVARIANT(count == 1, "bad frag count"); CHECK_INVARIANT(iv[0] == 0, "bad frag membership"); CHECK_INVARIANT(iv[1] == 0, "bad frag membership"); CHECK_INVARIANT(iv[4] == 0, "bad frag membership"); CHECK_INVARIANT(iv[5] == 0, "bad frag membership"); delete m; smi = "CCCC(=O)[O-].[Na+]"; m = SmilesToMol(smi); CHECK_INVARIANT(m, "smiles parse failed"); count = MolOps::getMolFrags(*m, iv); CHECK_INVARIANT(count == 2, "bad frag count"); CHECK_INVARIANT(iv[0] == 0, "bad frag membership"); CHECK_INVARIANT(iv[1] == 0, "bad frag membership"); CHECK_INVARIANT(iv[4] == 0, "bad frag membership"); CHECK_INVARIANT(iv[5] == 0, "bad frag membership"); CHECK_INVARIANT(iv[6] == 1, "bad frag membership"); delete m; }; void test3() { string smi; Mol *m; VECT_INT_VECT sssr; INT_VECT rings; int count; smi = "C1CC1"; m = SmilesToMol(smi); TEST_ASSERT(m); TEST_ASSERT(m->getRingInfo()->numRings() == 1); count = MolOps::findSSSR(*m, sssr); TEST_ASSERT(count == 1); TEST_ASSERT(sssr[0].size() == 3); TEST_ASSERT(m->getRingInfo()->numRings() == 1); for (unsigned int i = 0; i < m->getNumAtoms(); i++) { TEST_ASSERT(m->getRingInfo()->isAtomInRingOfSize(i, 3)); TEST_ASSERT(!m->getRingInfo()->isAtomInRingOfSize(i, 4)); TEST_ASSERT(m->getRingInfo()->numAtomRings(i) == 1); TEST_ASSERT(m->getRingInfo()->atomRingSizes(i) == (INT_VECT{3})); TEST_ASSERT(m->getRingInfo()->atomMembers(i).size() == 1 && m->getRingInfo()->atomMembers(i).at(0) == 0); } for (unsigned int i = 0; i < m->getNumBonds(); i++) { TEST_ASSERT(m->getRingInfo()->isBondInRingOfSize(i, 3)); TEST_ASSERT(!m->getRingInfo()->isBondInRingOfSize(i, 4)); TEST_ASSERT(m->getRingInfo()->numBondRings(i) == 1); TEST_ASSERT(m->getRingInfo()->bondRingSizes(i) == (INT_VECT{3})); TEST_ASSERT(m->getRingInfo()->bondMembers(i).size() == 1 && m->getRingInfo()->bondMembers(i).at(0) == 0); } TEST_ASSERT(m->getRingInfo()->areAtomsInSameRing(0, 1)); TEST_ASSERT(m->getRingInfo()->areAtomsInSameRingOfSize(0, 1, 3)); TEST_ASSERT(!m->getRingInfo()->areAtomsInSameRingOfSize(0, 1, 4)); TEST_ASSERT(m->getRingInfo()->areBondsInSameRing(0, 1)); TEST_ASSERT(m->getRingInfo()->areBondsInSameRingOfSize(0, 1, 3)); TEST_ASSERT(!m->getRingInfo()->areBondsInSameRingOfSize(0, 1, 4)); BOOST_LOG(rdInfoLog) << smi << "\n"; delete m; smi = "C1CCC1"; m = SmilesToMol(smi); TEST_ASSERT(m); TEST_ASSERT(m->getRingInfo()->numRings() == 1); count = MolOps::findSSSR(*m, sssr); TEST_ASSERT(count == 1); TEST_ASSERT(sssr[0].size() == 4); TEST_ASSERT(m->getRingInfo()->numRings() == 1); for (unsigned int i = 0; i < m->getNumAtoms(); i++) { TEST_ASSERT(m->getRingInfo()->isAtomInRingOfSize(i, 4)); TEST_ASSERT(!m->getRingInfo()->isAtomInRingOfSize(i, 3)); TEST_ASSERT(m->getRingInfo()->numAtomRings(i) == 1); TEST_ASSERT(m->getRingInfo()->atomRingSizes(i) == (INT_VECT{4})); TEST_ASSERT(m->getRingInfo()->atomMembers(i).size() == 1 && m->getRingInfo()->atomMembers(i).at(0) == 0); } TEST_ASSERT(m->getRingInfo()->isBondInRingOfSize(0, 4)); TEST_ASSERT(m->getRingInfo()->numBondRings(0) == 1); TEST_ASSERT(m->getRingInfo()->bondRingSizes(0) == (INT_VECT{4})); TEST_ASSERT(m->getRingInfo()->areBondsInSameRing(0, 1)); TEST_ASSERT(m->getRingInfo()->areBondsInSameRingOfSize(0, 1, 4)); TEST_ASSERT(!m->getRingInfo()->areBondsInSameRingOfSize(0, 1, 5)); BOOST_LOG(rdInfoLog) << smi << "\n"; delete m; smi = "C1CCCCCC1"; m = SmilesToMol(smi); TEST_ASSERT(m); TEST_ASSERT(m->getRingInfo()->numRings() == 1); count = MolOps::findSSSR(*m, sssr); TEST_ASSERT(count == 1); TEST_ASSERT(sssr[0].size() == 7); TEST_ASSERT(m->getRingInfo()->numRings() == 1); for (unsigned int i = 0; i < m->getNumAtoms(); i++) { TEST_ASSERT(m->getRingInfo()->isAtomInRingOfSize(i, 7)); TEST_ASSERT(m->getRingInfo()->numAtomRings(i) == 1); } TEST_ASSERT(m->getRingInfo()->isBondInRingOfSize(0, 7)); TEST_ASSERT(m->getRingInfo()->numBondRings(0) == 1); BOOST_LOG(rdInfoLog) << smi << "\n"; delete m; smi = "C1C(CCC)CC(C(C)CCC(CC))CCC1"; m = SmilesToMol(smi); TEST_ASSERT(m); count = MolOps::findSSSR(*m, sssr); TEST_ASSERT(count == 1); TEST_ASSERT(sssr[0].size() == 7); TEST_ASSERT(m->getRingInfo()->numAtomRings(0) == 1); TEST_ASSERT( m->getRingInfo()->numBondRings(m->getBondBetweenAtoms(0, 1)->getIdx())); TEST_ASSERT(m->getRingInfo()->atomMembers(0).size() == 1); TEST_ASSERT(m->getRingInfo()->atomMembers(2).empty()); TEST_ASSERT(m->getRingInfo()->atomRingSizes(2).empty()); TEST_ASSERT(m->getRingInfo()->atomRingSizes(5) == (INT_VECT{7})); TEST_ASSERT(m->getRingInfo()->atomRingSizes(1) == (INT_VECT{7})); TEST_ASSERT(m->getRingInfo()->atomRingSizes(99).empty()); TEST_ASSERT(m->getRingInfo()->areAtomsInSameRing(0, 1)); TEST_ASSERT(m->getRingInfo()->areAtomsInSameRingOfSize(0, 1, 7)); TEST_ASSERT(!m->getRingInfo()->areAtomsInSameRingOfSize(0, 1, 5)); TEST_ASSERT(!m->getRingInfo()->areAtomsInSameRing(1, 2)); TEST_ASSERT(!m->getRingInfo()->areAtomsInSameRingOfSize(1, 2, 4)); TEST_ASSERT(m->getRingInfo()->bondRingSizes(1).empty()); TEST_ASSERT(m->getRingInfo()->bondRingSizes(4) == (INT_VECT{7})); TEST_ASSERT(m->getRingInfo()->bondRingSizes(5) == (INT_VECT{7})); TEST_ASSERT(m->getRingInfo()->bondRingSizes(99).empty()); TEST_ASSERT(m->getRingInfo()->bondMembers(0).size() == 1); TEST_ASSERT(m->getRingInfo()->bondMembers(1).empty()); TEST_ASSERT( !m->getRingInfo()->numBondRings(m->getBondBetweenAtoms(1, 2)->getIdx())); BOOST_LOG(rdInfoLog) << smi << "\n"; delete m; smi = "CC1CCC1"; m = SmilesToMol(smi); TEST_ASSERT(m); count = MolOps::findSSSR(*m, sssr); TEST_ASSERT(count == 1); TEST_ASSERT(sssr[0].size() == 4); TEST_ASSERT(!m->getBondBetweenAtoms(0, 1)->hasProp( common_properties::ringMembership)); TEST_ASSERT( !m->getRingInfo()->numBondRings(m->getBondBetweenAtoms(0, 1)->getIdx())); TEST_ASSERT( m->getRingInfo()->numBondRings(m->getBondBetweenAtoms(1, 2)->getIdx())); BOOST_LOG(rdInfoLog) << smi << "\n"; delete m; smi = "CC1C(C2)CCC2C1"; m = SmilesToMol(smi); TEST_ASSERT(m); count = MolOps::findSSSR(*m, sssr); TEST_ASSERT(count == 2) TEST_ASSERT(sssr[0].size() == 5); TEST_ASSERT(sssr[1].size() == 5); TEST_ASSERT(!m->getRingInfo()->isAtomInRingOfSize(0, 5)); TEST_ASSERT(m->getRingInfo()->numAtomRings(0) == 0); TEST_ASSERT(m->getRingInfo()->isAtomInRingOfSize(1, 5)); TEST_ASSERT(m->getRingInfo()->numAtomRings(1) == 1); TEST_ASSERT(m->getRingInfo()->isAtomInRingOfSize(2, 5)); TEST_ASSERT(m->getRingInfo()->numAtomRings(2) == 2); TEST_ASSERT(m->getRingInfo()->atomRingSizes(0).empty()); TEST_ASSERT(m->getRingInfo()->atomRingSizes(1) == (INT_VECT{5})); TEST_ASSERT(m->getRingInfo()->atomRingSizes(2) == (INT_VECT{5, 5})); TEST_ASSERT(m->getRingInfo()->atomMembers(2).size() == 2); TEST_ASSERT(m->getRingInfo()->atomMembers(2).at(0) == 0); TEST_ASSERT(m->getRingInfo()->atomMembers(2).at(1) == 1); BOOST_LOG(rdInfoLog) << smi << "\n"; delete m; smi = "C(C1C2C3C41)(C2C35)C45"; // cubane // smi = "C1(C2C3C4C5C6C72)C3C4C5C6C71"; // from Figureras paper // smi = "C17C5C4C3C2C1C6C2C3C4C5C67"; // we cannot use the sanitization code, because that finds *symmetric* // rings, which will break this case: m = SmilesToMol(smi, 0, 0); int bfs = MolOps::findSSSR(*m); TEST_ASSERT(bfs == 5); BOOST_LOG(rdInfoLog) << "BFSR: " << bfs << "\n"; VECT_INT_VECT bfrs; bfrs.resize(0); bfs = MolOps::symmetrizeSSSR(*m, bfrs); TEST_ASSERT(bfs == 6); BOOST_LOG(rdInfoLog) << "BFSR: " << bfs << "\n"; // VECT_INT_VECT_I ri; // for (ri == bfrs.begin(); ri != bfrs.end(); ri++) { for (auto bring : bfrs) { INT_VECT_I mi; BOOST_LOG(rdInfoLog) << "( "; // for (mi = (*ri).begin(); mi != (*ri).end(); mi++) { for (mi = bring.begin(); mi != bring.end(); mi++) { BOOST_LOG(rdInfoLog) << " " << (*mi); } BOOST_LOG(rdInfoLog) << ")\n"; } BOOST_LOG(rdInfoLog) << smi << "\n"; delete m; // Figueras figure 4: // * The third ring is bigger, and shouldn't be accessed in symmetrizeSSSR smi = "C12CC(CC2)CC1"; m = SmilesToMol(smi, 0, 0); bfs = MolOps::findSSSR(*m); TEST_ASSERT(bfs == 2); bfrs.resize(0); bfs = MolOps::symmetrizeSSSR(*m, bfrs); TEST_ASSERT(bfs == 2); delete m; // Counterexamples in ring perception figure 4: // * The native Figueras algorithm cannot work on this molecule, it will // fail after finding one ring. Naive modified Figueras finds a 6 membered // ring, which is wrong. smi = "C123C4C5C6(C3)C7C1C8C2C4C5C6C78"; m = SmilesToMol(smi, 0, 0); bfs = MolOps::findSSSR(*m); TEST_ASSERT(bfs == 7); bfrs.resize(0); bfs = MolOps::symmetrizeSSSR(*m, bfrs); TEST_ASSERT(bfs == 8); for (auto bring : bfrs) { TEST_ASSERT(bring.size() < 6); } delete m; smi = "C1CC2C1CCC2"; m = SmilesToMol(smi); TEST_ASSERT(m); count = MolOps::findSSSR(*m, sssr); TEST_ASSERT(count == 2); TEST_ASSERT(sssr[0].size() == 4); TEST_ASSERT(sssr[1].size() == 5); BOOST_LOG(rdInfoLog) << smi << "\n"; delete m; smi = "C12=C3C=CC=C1C=CC2=CC=C3"; BOOST_LOG(rdInfoLog) << "\n" << smi << "\n"; m = SmilesToMol(smi, 0, 0); TEST_ASSERT(m); count = MolOps::findSSSR(*m, sssr); TEST_ASSERT(count == 3); TEST_ASSERT(sssr[0].size() == 6); TEST_ASSERT(sssr[1].size() == 5); TEST_ASSERT(sssr[2].size() == 6); BOOST_LOG(rdInfoLog) << smi << "\n"; delete m; smi = "C1(O)C(O)C(O)C1O"; m = SmilesToMol(smi, 0, 0); TEST_ASSERT(m); count = MolOps::findSSSR(*m, sssr); TEST_ASSERT(count == 1); TEST_ASSERT(sssr[0].size() == 4); for (unsigned i = 0; i < m->getNumAtoms(); i++) { if (!(i % 2)) { TEST_ASSERT(m->getRingInfo()->numAtomRings(i) == 1); TEST_ASSERT(m->getRingInfo()->isAtomInRingOfSize(i, 4)); } else { TEST_ASSERT(m->getRingInfo()->numAtomRings(i) == 0); } } BOOST_LOG(rdInfoLog) << smi << "\n"; delete m; // this molecule is from issue 134 // it should come up with three rings smi = "SC(C3C1CC(C3)CC(C2S)(O)C1)2S"; m = SmilesToMol(smi, 0, 0); TEST_ASSERT(m); count = MolOps::findSSSR(*m, sssr); TEST_ASSERT(count == 3); TEST_ASSERT(sssr[0].size() == 5); TEST_ASSERT(sssr[1].size() == 6); TEST_ASSERT(sssr[2].size() == 6); delete m; // this yet another painful case smi = "CC1=CC=C(C=C1)S(=O)(=O)O[CH]2[CH]3CO[CH](O3)[CH]4OC(C)(C)O[CH]24"; m = SmilesToMol(smi, 0, 0); TEST_ASSERT(m); count = MolOps::findSSSR(*m, sssr); TEST_ASSERT(count == 4); TEST_ASSERT(sssr[0].size() == 6); TEST_ASSERT(sssr[1].size() == 5); TEST_ASSERT(sssr[2].size() == 5); TEST_ASSERT(sssr[3].size() == 6); delete m; smi = "C1CC2C1C2"; m = SmilesToMol(smi, 0, 0); TEST_ASSERT(m); count = MolOps::findSSSR(*m, sssr); TEST_ASSERT(count == 2); TEST_ASSERT(sssr[0].size() == 4); TEST_ASSERT(sssr[1].size() == 3); TEST_ASSERT(m->getRingInfo()->numAtomRings(0) == 1); TEST_ASSERT(m->getRingInfo()->isAtomInRingOfSize(0, 4)); TEST_ASSERT(m->getRingInfo()->numAtomRings(1) == 1); TEST_ASSERT(m->getRingInfo()->isAtomInRingOfSize(1, 4)); TEST_ASSERT(m->getRingInfo()->numAtomRings(2) == 2); TEST_ASSERT(m->getRingInfo()->isAtomInRingOfSize(2, 3)); TEST_ASSERT(m->getRingInfo()->isAtomInRingOfSize(2, 4)); TEST_ASSERT(m->getRingInfo()->numAtomRings(3) == 2); TEST_ASSERT(m->getRingInfo()->isAtomInRingOfSize(3, 4)); TEST_ASSERT(m->getRingInfo()->isAtomInRingOfSize(3, 3)); TEST_ASSERT(m->getRingInfo()->numAtomRings(4) == 1); TEST_ASSERT(!m->getRingInfo()->isAtomInRingOfSize(4, 4)); TEST_ASSERT(m->getRingInfo()->isAtomInRingOfSize(4, 3)); TEST_ASSERT(m->getRingInfo()->atomMembers(2).size() == 2); TEST_ASSERT(m->getRingInfo()->areAtomsInSameRing(2, 3)); TEST_ASSERT(!m->getRingInfo()->areAtomsInSameRing(1, 4)); TEST_ASSERT(m->getRingInfo()->areAtomsInSameRingOfSize(2, 3, 3)); TEST_ASSERT(m->getRingInfo()->areAtomsInSameRingOfSize(2, 3, 4)); TEST_ASSERT(!m->getRingInfo()->areAtomsInSameRingOfSize(2, 3, 5)); TEST_ASSERT(m->getRingInfo()->bondMembers(2).size() == 2); TEST_ASSERT(m->getRingInfo()->bondMembers(0).size() == 1); TEST_ASSERT(m->getRingInfo()->areBondsInSameRing(1, 2)); TEST_ASSERT(m->getRingInfo()->areBondsInSameRing(2, 5)); TEST_ASSERT(!m->getRingInfo()->areBondsInSameRing(1, 3)); TEST_ASSERT(m->getRingInfo()->areBondsInSameRingOfSize(1, 2, 4)); TEST_ASSERT(m->getRingInfo()->areBondsInSameRingOfSize(2, 5, 3)); TEST_ASSERT(!m->getRingInfo()->areBondsInSameRingOfSize(1, 2, 3)); TEST_ASSERT(!m->getRingInfo()->areBondsInSameRingOfSize(1, 3, 4)); delete m; // This is a test of Issue 217 smi = "C=C1C2CC1C2"; m = SmilesToMol(smi, 0, 0); TEST_ASSERT(m); count = MolOps::findSSSR(*m, sssr); TEST_ASSERT(count == 2); TEST_ASSERT(sssr[0].size() == 4); TEST_ASSERT(sssr[1].size() == 4); count = MolOps::symmetrizeSSSR(*m, sssr); TEST_ASSERT(count == 3); TEST_ASSERT(sssr[0].size() == 4); TEST_ASSERT(sssr[1].size() == 4); TEST_ASSERT(sssr[2].size() == 4); TEST_ASSERT(m->getRingInfo()->numAtomRings(0) == 0); TEST_ASSERT(m->getRingInfo()->numAtomRings(1) == 2); TEST_ASSERT(m->getRingInfo()->isAtomInRingOfSize(1, 4)); TEST_ASSERT(m->getRingInfo()->numAtomRings(2) == 3); TEST_ASSERT(m->getRingInfo()->isAtomInRingOfSize(2, 4)); TEST_ASSERT(m->getRingInfo()->numAtomRings(3) == 2); TEST_ASSERT(m->getRingInfo()->isAtomInRingOfSize(3, 4)); TEST_ASSERT(m->getRingInfo()->numAtomRings(4) == 3); TEST_ASSERT(m->getRingInfo()->isAtomInRingOfSize(4, 4)); delete m; } void test4() { auto m = "C=C"_smiles; TEST_ASSERT(m); double *adjMat = MolOps::getAdjacencyMatrix(*m); TEST_ASSERT(adjMat); TEST_ASSERT(adjMat[0] == 0); TEST_ASSERT(adjMat[1] == 1); TEST_ASSERT(adjMat[2] == 1); TEST_ASSERT(adjMat[3] == 0); adjMat = MolOps::getAdjacencyMatrix(*m); TEST_ASSERT(adjMat); TEST_ASSERT(adjMat[0] == 0); TEST_ASSERT(adjMat[1] == 1); TEST_ASSERT(adjMat[2] == 1); TEST_ASSERT(adjMat[3] == 0); bool useBO = true; adjMat = MolOps::getAdjacencyMatrix(*m, useBO); TEST_ASSERT(adjMat); TEST_ASSERT(adjMat[0] == 0); TEST_ASSERT(adjMat[1] == 2.0); TEST_ASSERT(adjMat[2] == 2.0); TEST_ASSERT(adjMat[3] == 0); } void test5() { string smi; Mol *m; VECT_INT_VECT sssr; int count; smi = "C1C4C5C3C(=O)C2C5C1C2C34"; m = SmilesToMol(smi, 0, 0); count = MolOps::findSSSR(*m, sssr); BOOST_LOG(rdInfoLog) << "Count: " << count << "\n"; CHECK_INVARIANT(count == 5, ""); delete m; smi = "C1C(C2)CCC2C1"; m = SmilesToMol(smi); count = MolOps::findSSSR(*m, sssr); CHECK_INVARIANT(count == 2, ""); delete m; } /* void test6(){ string smi; Mol *m; VECT_INT_VECT sssr; int c1,c2; smi = "C1(Cl)C(Cl)C1Cl"; m = SmilesToMol(smi); INT_SET ringAtoms,ringBonds; //boost::tie(c1,c2) = MolOps::findRingAtomsAndBonds(*m,ringAtoms,ringBonds); CHECK_INVARIANT(c1==3,"bad nRingAtoms"); CHECK_INVARIANT(ringAtoms.count(0)==1,"bad RingAtoms"); CHECK_INVARIANT(ringAtoms.count(1)==0,"bad RingAtoms"); CHECK_INVARIANT(ringAtoms.count(2)==1,"bad RingAtoms"); CHECK_INVARIANT(ringAtoms.count(3)==0,"bad RingAtoms"); CHECK_INVARIANT(ringAtoms.count(4)==1,"bad RingAtoms"); CHECK_INVARIANT(ringAtoms.count(5)==0,"bad RingAtoms"); CHECK_INVARIANT(c2==3,"bad nRingBonds"); CHECK_INVARIANT(ringBonds.count(0)==0,""); CHECK_INVARIANT(ringBonds.count(1)==1,""); CHECK_INVARIANT(ringBonds.count(2)==0,""); CHECK_INVARIANT(ringBonds.count(3)==1,""); CHECK_INVARIANT(ringBonds.count(4)==0,""); CHECK_INVARIANT(ringBonds.count(5)==1,""); } */ void test7() { #if 0 string smi; Mol *m; INT_VECT tree; #if 1 smi = "C(CO)OCC"; m = SmilesToMol(smi); MolOps::findSpanningTree(*m,tree); CHECK_INVARIANT(tree.size()==5,"bad mst"); delete m; smi = "C1CC1"; m = SmilesToMol(smi); MolOps::findSpanningTree(*m,tree); CHECK_INVARIANT(tree.size()==2,"bad mst"); delete m; smi = "C1C=C1"; m = SmilesToMol(smi); MolOps::findSpanningTree(*m,tree); CHECK_INVARIANT(tree.size()==2,"bad mst"); CHECK_INVARIANT(std::find(tree.begin(),tree.end(),1)==tree.end(),"bogus idx in mst"); delete m; #endif smi = "C1C=CC=CC=1"; m = SmilesToMol(smi); MolOps::findSpanningTree(*m,tree); CHECK_INVARIANT(tree.size()==5,"bad mst"); delete m; smi = "C1C(=CC1)"; m = SmilesToMol(smi); MolOps::findSpanningTree(*m,tree); CHECK_INVARIANT(tree.size()==3,"bad mst"); delete m; smi = "C1C(C=C1)"; m = SmilesToMol(smi); MolOps::findSpanningTree(*m,tree); CHECK_INVARIANT(tree.size()==3,"bad mst"); delete m; smi = "C1C(C2)CCC2C1"; m = SmilesToMol(smi); MolOps::findSpanningTree(*m,tree); CHECK_INVARIANT(tree.size()==6,"bad mst"); delete m; smi = "C1C2CC3CCCCC3CC2CCC1"; m = SmilesToMol(smi); MolOps::findSpanningTree(*m,tree); CHECK_INVARIANT(tree.size()==13,"bad mst"); delete m; #endif } void test8() { BOOST_LOG(rdInfoLog) << "-----------------------\n Testing Hydrogen Ops" << std::endl; ROMol *m, *m2, *m3; INT_VECT tree; std::string smi = "CCC"; m = SmilesToMol(smi); CHECK_INVARIANT(m, ""); CHECK_INVARIANT(m->getNumAtoms() == 3, ""); // BOOST_LOG(rdInfoLog) << "1" << std::endl; m2 = MolOps::addHs(*m); CHECK_INVARIANT(m2->getNumAtoms() == 11, ""); delete m; delete m2; smi = "CC(=O)[OH]"; m = SmilesToMol(smi); CHECK_INVARIANT(m, ""); CHECK_INVARIANT(m->getNumAtoms() == 4, ""); // BOOST_LOG(rdInfoLog) << "2" << std::endl; m2 = MolOps::addHs(*m, true); CHECK_INVARIANT(m2->getNumAtoms() == 5, ""); // BOOST_LOG(rdInfoLog) << "3" << std::endl; m3 = MolOps::addHs(*m2, false); CHECK_INVARIANT(m3->getNumAtoms() == 8, ""); delete m2; delete m3; // BOOST_LOG(rdInfoLog) << "4" << std::endl; m2 = MolOps::addHs(*m, false); CHECK_INVARIANT(m2->getNumAtoms() == 8, ""); delete m; // remove all // BOOST_LOG(rdInfoLog) << "5" << std::endl; m3 = MolOps::removeHs(*m2, false); CHECK_INVARIANT(m3->getNumAtoms() == 4, ""); delete m3; // remove only implicit // BOOST_LOG(rdInfoLog) << "6" << std::endl; m3 = MolOps::removeHs(*m2, true); CHECK_INVARIANT(m3->getNumAtoms() == 5, ""); // BOOST_LOG(rdInfoLog) << "7" << std::endl; // remove all after removing only implicit MolOps::removeHs(static_cast(*m3), false); CHECK_INVARIANT(m3->getNumAtoms() == 4, ""); delete m2; delete m3; // this test is also done in the same order in the python tests: m = SmilesToMol(smi); CHECK_INVARIANT(m, ""); CHECK_INVARIANT(m->getNumAtoms() == 4, ""); m2 = MolOps::addHs(*m, true); CHECK_INVARIANT(m2->getNumAtoms() == 5, ""); // BOOST_LOG(rdInfoLog) << "8" << std::endl; m3 = MolOps::removeHs(*m2, true); CHECK_INVARIANT(m3->getNumAtoms() == 5, ""); delete m3; // BOOST_LOG(rdInfoLog) << "9" << std::endl; m3 = MolOps::removeHs(*m2, false); CHECK_INVARIANT(m3->getNumAtoms() == 4, ""); delete m2; delete m3; // BOOST_LOG(rdInfoLog) << "10" << std::endl; m2 = MolOps::addHs(*m, false); CHECK_INVARIANT(m2->getNumAtoms() == 8, ""); // BOOST_LOG(rdInfoLog) << "11" << std::endl; m3 = MolOps::removeHs(*m2, true); CHECK_INVARIANT(m3->getNumAtoms() == 5, ""); delete m3; // BOOST_LOG(rdInfoLog) << "12" << std::endl; m3 = MolOps::removeHs(*m2, false); CHECK_INVARIANT(m3->getNumAtoms() == 4, ""); delete m3; delete m2; delete m; // related to RDTrack Issues 109 and 110: smi = "C1C=C([C@H](N)C(=O)N[C@@]2([H])[C@]3([H])SC(C)(C)[C@@H](C(=O)O)N3C(=O)2)" "C=CC=1"; m = SmilesToMol(smi); CHECK_INVARIANT(m, ""); CHECK_INVARIANT(m->getNumAtoms() == 24, ""); // BOOST_LOG(rdInfoLog) << "13" << std::endl; m3 = MolOps::removeHs(*m, false); CHECK_INVARIANT(m3->getNumAtoms() == 24, ""); delete m; delete m3; // RDTrack Issue 130: smi = "[H][N+]([H])([H])[H]"; m = SmilesToMol(smi, false, false); CHECK_INVARIANT(m, ""); CHECK_INVARIANT(m->getNumAtoms() == 5, ""); // BOOST_LOG(rdInfoLog) << "14" << std::endl; m2 = MolOps::removeHs(*m, 0, false); CHECK_INVARIANT(m2->getNumAtoms() == 1, ""); delete m; delete m2; smi = "[H][N+]([H])([H])[H]"; m = SmilesToMol(smi); CHECK_INVARIANT(m, ""); CHECK_INVARIANT(m->getNumAtoms() == 1, ""); delete m; smi = "[H][H]"; m = SmilesToMol(smi, false, false); CHECK_INVARIANT(m, ""); CHECK_INVARIANT(m->getNumAtoms() == 2, ""); // BOOST_LOG(rdInfoLog) << "15" << std::endl; m2 = MolOps::removeHs(*m, 0, false); CHECK_INVARIANT(m2->getNumAtoms() == 2, ""); delete m; delete m2; std::string sma; smi = "C-C"; m = SmartsToMol(smi); MolOps::sanitizeMol(*((RWMol *)m)); TEST_ASSERT(m); TEST_ASSERT(m->getNumAtoms() == 2); sma = SmartsWrite::GetAtomSmarts( static_cast(m->getAtomWithIdx(0))); TEST_ASSERT(sma == "C"); // BOOST_LOG(rdInfoLog) << "16" << std::endl; m2 = MolOps::addHs(*m); TEST_ASSERT(m2->getNumAtoms() == 8); sma = SmartsWrite::GetAtomSmarts( static_cast(m2->getAtomWithIdx(0))); TEST_ASSERT(sma == "C"); delete m; // BOOST_LOG(rdInfoLog) << "17" << std::endl; m = MolOps::mergeQueryHs(*m2); TEST_ASSERT(m->getNumAtoms() == 2); sma = SmartsWrite::GetAtomSmarts( static_cast(m->getAtomWithIdx(0))); // BOOST_LOG(rdInfoLog) << "sma: " << sma<getNumAtoms() == 5, ""); // BOOST_LOG(rdInfoLog) << "18" << std::endl; m2 = MolOps::addHs(*m, false, false); CHECK_INVARIANT(m2->getNumAtoms() == 10, ""); // BOOST_LOG(rdInfoLog) << "19" << std::endl; delete m; m = MolOps::removeHs(*m2); CHECK_INVARIANT(m, ""); CHECK_INVARIANT(m->getNumAtoms() == 5, ""); delete m; delete m2; // labelling: smi = "c1cn([H])cc1"; m = SmilesToMol(smi); CHECK_INVARIANT(m, ""); CHECK_INVARIANT(m->getNumAtoms() == 5, ""); // BOOST_LOG(rdInfoLog) << "19" << std::endl; m2 = MolOps::removeHs(*m); CHECK_INVARIANT(m, ""); CHECK_INVARIANT(m->getNumAtoms() == 5, ""); delete m; delete m2; smi = "c1cn([2H])cc1"; m = SmilesToMol(smi); CHECK_INVARIANT(m, ""); CHECK_INVARIANT(m->getNumAtoms() == 6, ""); // BOOST_LOG(rdInfoLog) << "19" << std::endl; m2 = MolOps::removeHs(*m); CHECK_INVARIANT(m, ""); CHECK_INVARIANT(m->getNumAtoms() == 6, ""); delete m; delete m2; smi = "CC[H]"; m = SmilesToMol(smi, false, false); TEST_ASSERT(m); TEST_ASSERT(m->getNumAtoms() == 3); m2 = MolOps::mergeQueryHs(*m); TEST_ASSERT(m2->getNumAtoms() == 2); TEST_ASSERT(!m->getAtomWithIdx(1)->hasQuery()); TEST_ASSERT(m2->getAtomWithIdx(1)->hasQuery()); delete m; delete m2; // sf.net issue 3415206 smi = "CO[H]"; m = SmilesToMol(smi, false, false); TEST_ASSERT(m); TEST_ASSERT(m->getNumAtoms() == 3); m2 = MolOps::mergeQueryHs(*m); TEST_ASSERT(m2->getNumAtoms() == 2); sma = SmartsWrite::GetAtomSmarts( static_cast(m2->getAtomWithIdx(1))); // BOOST_LOG(rdInfoLog) << "sma: " << sma<getNumAtoms() == 4); m2 = MolOps::mergeQueryHs(*m); TEST_ASSERT(m2->getNumAtoms() == 2); sma = SmartsWrite::GetAtomSmarts( static_cast(m2->getAtomWithIdx(1))); // BOOST_LOG(rdInfoLog) << "sma: " << sma<getNumAtoms() == 3, ""); // BOOST_LOG(rdInfoLog) << "1" << std::endl; UINT_VECT onlyOn; onlyOn.push_back(0); onlyOn.push_back(2); m2 = MolOps::addHs(*m, false, false, &onlyOn); CHECK_INVARIANT(m2->getNumAtoms() == 9, ""); CHECK_INVARIANT(m2->getAtomWithIdx(0)->getDegree() == 4, ""); CHECK_INVARIANT(m2->getAtomWithIdx(1)->getDegree() == 2, ""); CHECK_INVARIANT(m2->getAtomWithIdx(2)->getDegree() == 4, ""); delete m; delete m2; } BOOST_LOG(rdInfoLog) << "Finished" << std::endl; } void test9() { BOOST_LOG(rdInfoLog) << "-----------------------\n Testing Distance Matrix Operations" << std::endl; auto m = "CC=O"_smiles; TEST_ASSERT(m); TEST_ASSERT(m->getNumAtoms() == 3); bool useBO = false; bool useAtomWts = false; double *dMat; dMat = MolOps::getDistanceMat(*m, useBO, useAtomWts); TEST_ASSERT(dMat); TEST_ASSERT(dMat[0] == 0.0); TEST_ASSERT(dMat[1] == 1.0); TEST_ASSERT(dMat[2] == 2.0); TEST_ASSERT(dMat[3] == 1.0); TEST_ASSERT(dMat[4] == 0.0); TEST_ASSERT(dMat[5] == 1.0); TEST_ASSERT(dMat[6] == 2.0); TEST_ASSERT(dMat[7] == 1.0); TEST_ASSERT(dMat[8] == 0.0); dMat = MolOps::getDistanceMat(*m, useBO, useAtomWts); TEST_ASSERT(dMat); TEST_ASSERT(dMat[0] == 0.0); TEST_ASSERT(dMat[1] == 1.0); TEST_ASSERT(dMat[2] == 2.0); TEST_ASSERT(dMat[3] == 1.0); TEST_ASSERT(dMat[4] == 0.0); TEST_ASSERT(dMat[5] == 1.0); TEST_ASSERT(dMat[6] == 2.0); TEST_ASSERT(dMat[7] == 1.0); TEST_ASSERT(dMat[8] == 0.0); // test Issue328: useBO = true; dMat = MolOps::getDistanceMat(*m, useBO, useAtomWts); TEST_ASSERT(dMat); TEST_ASSERT(dMat[0] == 0.0); TEST_ASSERT(dMat[1] == 1.0); TEST_ASSERT(dMat[2] == 1.5); TEST_ASSERT(dMat[3] == 1.0); TEST_ASSERT(dMat[4] == 0.0); TEST_ASSERT(dMat[5] == 0.5); TEST_ASSERT(dMat[6] == 1.5); TEST_ASSERT(dMat[7] == 0.5); TEST_ASSERT(dMat[8] == 0.0); useBO = false; dMat = MolOps::getDistanceMat(*m, useBO, useAtomWts); TEST_ASSERT(dMat); TEST_ASSERT(dMat[0] == 0.0); TEST_ASSERT(dMat[1] == 1.0); TEST_ASSERT(dMat[2] == 2.0); TEST_ASSERT(dMat[3] == 1.0); TEST_ASSERT(dMat[4] == 0.0); TEST_ASSERT(dMat[5] == 1.0); TEST_ASSERT(dMat[6] == 2.0); TEST_ASSERT(dMat[7] == 1.0); TEST_ASSERT(dMat[8] == 0.0); useBO = false; useAtomWts = true; dMat = MolOps::getDistanceMat(*m, useBO, useAtomWts); TEST_ASSERT(dMat); for (unsigned int i = 0; i < m->getNumAtoms(); ++i) { for (unsigned int j = 0; j < m->getNumAtoms(); ++j) { std::cerr << dMat[i * m->getNumAtoms() + j] << " "; } std::cerr << std::endl; } TEST_ASSERT(dMat[0] == 1.0); TEST_ASSERT(dMat[1] == 1.0); TEST_ASSERT(dMat[2] == 2.0); TEST_ASSERT(dMat[3] == 1.0); TEST_ASSERT(dMat[4] == 1.0); TEST_ASSERT(dMat[5] == 1.0); TEST_ASSERT(dMat[6] == 2.0); TEST_ASSERT(dMat[7] == 1.0); TEST_ASSERT(dMat[8] == 6. / 8.); useBO = true; useAtomWts = true; dMat = MolOps::getDistanceMat(*m, useBO, useAtomWts); TEST_ASSERT(dMat); TEST_ASSERT(dMat[0] == 1.0); TEST_ASSERT(dMat[1] == 1.0); TEST_ASSERT(dMat[2] == 1.5); TEST_ASSERT(dMat[3] == 1.0); TEST_ASSERT(dMat[4] == 1.0); TEST_ASSERT(dMat[5] == 0.5); TEST_ASSERT(dMat[6] == 1.5); TEST_ASSERT(dMat[7] == 0.5); TEST_ASSERT(dMat[8] == 6. / 8.); useBO = false; useAtomWts = false; dMat = MolOps::getDistanceMat(*m, useBO, useAtomWts); TEST_ASSERT(dMat); TEST_ASSERT(dMat[0] == 0.0); TEST_ASSERT(dMat[1] == 1.0); TEST_ASSERT(dMat[2] == 2.0); TEST_ASSERT(dMat[3] == 1.0); TEST_ASSERT(dMat[4] == 0.0); TEST_ASSERT(dMat[5] == 1.0); TEST_ASSERT(dMat[6] == 2.0); TEST_ASSERT(dMat[7] == 1.0); TEST_ASSERT(dMat[8] == 0.0); // limit participating atoms and bonds std::vector activeAtoms = {1, 2}; std::vector activeBonds = {m->getBondWithIdx(1)}; useBO = false; useAtomWts = false; std::unique_ptr dMat2{ MolOps::getDistanceMat(*m, activeAtoms, activeBonds, useBO, useAtomWts)}; TEST_ASSERT(dMat2); TEST_ASSERT(dMat2[0] == 0.0); TEST_ASSERT(dMat2[1] == 1.0); TEST_ASSERT(dMat2[2] == 1.0); TEST_ASSERT(dMat2[3] == 0.0); useBO = true; useAtomWts = true; dMat2.reset( MolOps::getDistanceMat(*m, activeAtoms, activeBonds, useBO, useAtomWts)); TEST_ASSERT(dMat2); TEST_ASSERT(dMat2[0] == 1.0); TEST_ASSERT(dMat2[1] == 0.5); TEST_ASSERT(dMat2[2] == 0.5); TEST_ASSERT(dMat2[3] == 6.0 / 8.0); BOOST_LOG(rdInfoLog) << "Finished" << std::endl; } void test10() { BOOST_LOG(rdInfoLog) << "-----------------------\n Testing Atom Ranking" << std::endl; ROMol *m; std::string smi = "FC(Cl)(Br)C"; m = SmilesToMol(smi); TEST_ASSERT(m); UINT_VECT ranks; ranks.resize(m->getNumAtoms()); Chirality::assignAtomCIPRanks(*m, ranks); unsigned int cip1, cip2; TEST_ASSERT(m->getAtomWithIdx(0)->hasProp(common_properties::_CIPRank)); m->getAtomWithIdx(0)->getProp(common_properties::_CIPRank, cip1); TEST_ASSERT(cip1 == ranks[0]); TEST_ASSERT(m->getAtomWithIdx(2)->hasProp(common_properties::_CIPRank)); m->getAtomWithIdx(2)->getProp(common_properties::_CIPRank, cip2); TEST_ASSERT(cip2 == ranks[2]); TEST_ASSERT(cip1 < cip2); TEST_ASSERT(m->getAtomWithIdx(4)->hasProp(common_properties::_CIPRank)); m->getAtomWithIdx(4)->getProp(common_properties::_CIPRank, cip2); TEST_ASSERT(cip1 > cip2); m->getAtomWithIdx(2)->getProp(common_properties::_CIPRank, cip1); TEST_ASSERT(m->getAtomWithIdx(3)->hasProp(common_properties::_CIPRank)); m->getAtomWithIdx(3)->getProp(common_properties::_CIPRank, cip2); TEST_ASSERT(cip1 < cip2); delete m; smi = "FC(Cl)(Br)C(F)(F)F"; m = SmilesToMol(smi); TEST_ASSERT(m); ranks.resize(m->getNumAtoms()); Chirality::assignAtomCIPRanks(*m, ranks); for (unsigned int i = 0; i < m->getNumAtoms(); i++) { unsigned int cip; TEST_ASSERT(m->getAtomWithIdx(i)->hasProp(common_properties::_CIPRank)); m->getAtomWithIdx(i)->getProp(common_properties::_CIPRank, cip); } delete m; BOOST_LOG(rdInfoLog) << "Finished" << std::endl; } void test11() { BOOST_LOG(rdInfoLog) << "-----------------------\n Testing CIP chirality" << std::endl; ROMol *m; std::string cip; std::string smi = "F[C@]([C@])(Cl)Br"; m = SmilesToMol(smi); TEST_ASSERT(m); MolOps::assignStereochemistry(*m, true); // make sure the cleanup worked: TEST_ASSERT(m->getAtomWithIdx(2)->getChiralTag() == Atom::CHI_UNSPECIFIED); TEST_ASSERT(!(m->getAtomWithIdx(0)->hasProp(common_properties::_CIPCode))); TEST_ASSERT(m->getAtomWithIdx(1)->hasProp(common_properties::_CIPCode)); TEST_ASSERT(!(m->getAtomWithIdx(2)->hasProp(common_properties::_CIPCode))); m->getAtomWithIdx(1)->getProp(common_properties::_CIPCode, cip); TEST_ASSERT(cip == "R"); delete m; smi = "F[C@H](C)C"; m = SmilesToMol(smi); TEST_ASSERT(m); MolOps::assignStereochemistry(*m, true); TEST_ASSERT(m->getAtomWithIdx(1)->getChiralTag() == Atom::CHI_UNSPECIFIED); TEST_ASSERT(!(m->getAtomWithIdx(0)->hasProp(common_properties::_CIPCode))); TEST_ASSERT(!(m->getAtomWithIdx(1)->hasProp(common_properties::_CIPCode))); TEST_ASSERT(!(m->getAtomWithIdx(2)->hasProp(common_properties::_CIPCode))); // test Issue 194: TEST_ASSERT(m->getAtomWithIdx(1)->getNumExplicitHs() == 0); delete m; smi = "F[C@]1CC(Cl)C1"; m = SmilesToMol(smi); TEST_ASSERT(m); MolOps::assignStereochemistry(*m, true); TEST_ASSERT(m->getAtomWithIdx(1)->getChiralTag() == Atom::CHI_UNSPECIFIED); TEST_ASSERT(!(m->getAtomWithIdx(1)->hasProp(common_properties::_CIPCode))); delete m; smi = "F[C@H]1C(Cl)CC1"; m = SmilesToMol(smi); TEST_ASSERT(m); MolOps::assignStereochemistry(*m, true); TEST_ASSERT(m->getAtomWithIdx(1)->getChiralTag() != Atom::CHI_UNSPECIFIED); TEST_ASSERT(m->getAtomWithIdx(1)->hasProp(common_properties::_CIPCode)); delete m; smi = "F[C@@](C)(Cl)Br"; m = SmilesToMol(smi); TEST_ASSERT(m); MolOps::assignStereochemistry(*m, true); TEST_ASSERT(!(m->getAtomWithIdx(0)->hasProp(common_properties::_CIPCode))); TEST_ASSERT(m->getAtomWithIdx(1)->hasProp(common_properties::_CIPCode)); TEST_ASSERT(!(m->getAtomWithIdx(2)->hasProp(common_properties::_CIPCode))); m->getAtomWithIdx(1)->getProp(common_properties::_CIPCode, cip); TEST_ASSERT(cip == "S"); delete m; smi = "F[C@](Br)(C)Cl"; m = SmilesToMol(smi); TEST_ASSERT(m); MolOps::assignStereochemistry(*m, true); TEST_ASSERT(m->getAtomWithIdx(1)->hasProp(common_properties::_CIPCode)); m->getAtomWithIdx(1)->getProp(common_properties::_CIPCode, cip); TEST_ASSERT(cip == "R"); delete m; smi = "F[C@](Cl)(Br)C"; m = SmilesToMol(smi); TEST_ASSERT(m); MolOps::assignStereochemistry(*m, true); TEST_ASSERT(m->getAtomWithIdx(1)->hasProp(common_properties::_CIPCode)); m->getAtomWithIdx(1)->getProp(common_properties::_CIPCode, cip); TEST_ASSERT(cip == "R"); delete m; smi = "FC(F)(F)[C@](Br)(F)C(Cl)(Cl)Cl"; m = SmilesToMol(smi); TEST_ASSERT(m); MolOps::assignStereochemistry(*m, true); TEST_ASSERT(m->getAtomWithIdx(4)->hasProp(common_properties::_CIPCode)); m->getAtomWithIdx(4)->getProp(common_properties::_CIPCode, cip); TEST_ASSERT(cip == "R"); delete m; smi = "C[C@](C=C)(F)Br"; m = SmilesToMol(smi); TEST_ASSERT(m); MolOps::assignStereochemistry(*m, true); TEST_ASSERT(m->getAtomWithIdx(1)->hasProp(common_properties::_CIPCode)); m->getAtomWithIdx(1)->getProp(common_properties::_CIPCode, cip); TEST_ASSERT(cip == "S"); delete m; smi = "CC[C@](C=C)(F)Br"; m = SmilesToMol(smi); TEST_ASSERT(m); MolOps::assignStereochemistry(*m, true); TEST_ASSERT(m->getAtomWithIdx(2)->hasProp(common_properties::_CIPCode)); m->getAtomWithIdx(2)->getProp(common_properties::_CIPCode, cip); TEST_ASSERT(cip == "S"); delete m; std::cerr << "-------------------------------" << std::endl; smi = "[CH2-][C@](C)(F)Br"; m = SmilesToMol(smi); TEST_ASSERT(m); MolOps::assignStereochemistry(*m, true); TEST_ASSERT(m->getAtomWithIdx(1)->hasProp(common_properties::_CIPCode)); m->getAtomWithIdx(1)->getProp(common_properties::_CIPCode, cip); TEST_ASSERT(cip == "S"); delete m; smi = "F[C@]([H])(Cl)Br"; m = SmilesToMol(smi); TEST_ASSERT(m); MolOps::assignStereochemistry(*m, true); TEST_ASSERT(m->getAtomWithIdx(1)->hasProp(common_properties::_CIPCode)); m->getAtomWithIdx(1)->getProp(common_properties::_CIPCode, cip); TEST_ASSERT(cip == "R"); delete m; smi = "[C@H](Cl)(F)Br"; m = SmilesToMol(smi); TEST_ASSERT(m); MolOps::assignStereochemistry(*m, true); TEST_ASSERT(m->getAtomWithIdx(0)->hasProp(common_properties::_CIPCode)); m->getAtomWithIdx(0)->getProp(common_properties::_CIPCode, cip); TEST_ASSERT(cip == "R"); delete m; smi = "[C@]([H])(Cl)(F)Br"; m = SmilesToMol(smi); TEST_ASSERT(m); MolOps::assignStereochemistry(*m, true); TEST_ASSERT(m->getAtomWithIdx(0)->hasProp(common_properties::_CIPCode)); m->getAtomWithIdx(0)->getProp(common_properties::_CIPCode, cip); TEST_ASSERT(cip == "R"); delete m; smi = "F[C@H](Cl)Br"; m = SmilesToMol(smi); TEST_ASSERT(m); MolOps::assignStereochemistry(*m, true); TEST_ASSERT(m->getAtomWithIdx(1)->hasProp(common_properties::_CIPCode)); m->getAtomWithIdx(1)->getProp(common_properties::_CIPCode, cip); TEST_ASSERT(cip == "R"); delete m; smi = "CC[C@H](C=C)C"; #ifdef VERBOSE_CANON BOOST_LOG(rdDebugLog) << " ----------------- ------------- ----------------" << std::endl; BOOST_LOG(rdDebugLog) << "\t>" << smi << std::endl; #endif m = SmilesToMol(smi); TEST_ASSERT(m); MolOps::assignStereochemistry(*m, true); TEST_ASSERT(m->getAtomWithIdx(2)->hasProp(common_properties::_CIPCode)); m->getAtomWithIdx(2)->getProp(common_properties::_CIPCode, cip); TEST_ASSERT(cip == "R"); delete m; smi = "OC[C@H](C=C)C"; #ifdef VERBOSE_CANON BOOST_LOG(rdDebugLog) << " ----------------- ------------- ----------------" << std::endl; BOOST_LOG(rdDebugLog) << "\t>" << smi << std::endl; #endif m = SmilesToMol(smi); TEST_ASSERT(m); MolOps::assignStereochemistry(*m, true); TEST_ASSERT(m->getAtomWithIdx(2)->hasProp(common_properties::_CIPCode)); m->getAtomWithIdx(2)->getProp(common_properties::_CIPCode, cip); TEST_ASSERT(cip == "S"); delete m; smi = "CC[C@H](C=C)O"; #ifdef VERBOSE_CANON BOOST_LOG(rdDebugLog) << " ----------------- ------------- ----------------" << std::endl; BOOST_LOG(rdDebugLog) << "\t>" << smi << std::endl; #endif m = SmilesToMol(smi); TEST_ASSERT(m); MolOps::assignStereochemistry(*m, true); TEST_ASSERT(m->getAtomWithIdx(2)->hasProp(common_properties::_CIPCode)); m->getAtomWithIdx(2)->getProp(common_properties::_CIPCode, cip); TEST_ASSERT(cip == "R"); delete m; smi = "OC[C@H](C=C)O"; #ifdef VERBOSE_CANON BOOST_LOG(rdDebugLog) << " ----------------- ------------- ----------------" << std::endl; BOOST_LOG(rdDebugLog) << "\t>" << smi << std::endl; #endif m = SmilesToMol(smi); TEST_ASSERT(m); MolOps::assignStereochemistry(*m, true); TEST_ASSERT(m->getAtomWithIdx(2)->hasProp(common_properties::_CIPCode)); m->getAtomWithIdx(2)->getProp(common_properties::_CIPCode, cip); TEST_ASSERT(cip == "S"); delete m; smi = "C[C@H]1C[C@H](C=C1)N"; #ifdef VERBOSE_CANON BOOST_LOG(rdDebugLog) << " ----------------- ------------- ----------------" << std::endl; BOOST_LOG(rdDebugLog) << "\t>" << smi << std::endl; #endif m = SmilesToMol(smi); TEST_ASSERT(m); MolOps::assignStereochemistry(*m, true); TEST_ASSERT(m->getAtomWithIdx(1)->hasProp(common_properties::_CIPCode)); m->getAtomWithIdx(1)->getProp(common_properties::_CIPCode, cip); TEST_ASSERT(cip == "S"); TEST_ASSERT(m->getAtomWithIdx(3)->hasProp(common_properties::_CIPCode)); m->getAtomWithIdx(3)->getProp(common_properties::_CIPCode, cip); TEST_ASSERT(cip == "R"); // a couple random molecules from the BBB data set delete m; smi = "OC[C@H]1C[C@@H](N2C=NC3=C2N=C(N)N=C3NC4CC4)C=C1"; #ifdef VERBOSE_CANON BOOST_LOG(rdDebugLog) << " ----------------- ------------- ----------------" << std::endl; BOOST_LOG(rdDebugLog) << "\t>" << smi << std::endl; #endif m = SmilesToMol(smi); TEST_ASSERT(m); MolOps::assignStereochemistry(*m, true); TEST_ASSERT(m->getAtomWithIdx(2)->hasProp(common_properties::_CIPCode)); m->getAtomWithIdx(2)->getProp(common_properties::_CIPCode, cip); TEST_ASSERT(cip == "S"); TEST_ASSERT(m->getAtomWithIdx(4)->hasProp(common_properties::_CIPCode)); m->getAtomWithIdx(4)->getProp(common_properties::_CIPCode, cip); TEST_ASSERT(cip == "R"); delete m; smi = "N[C@H]1O[C@@H](SC1)CO"; #ifdef VERBOSE_CANON BOOST_LOG(rdDebugLog) << " ----------------- ------------- ----------------" << std::endl; BOOST_LOG(rdDebugLog) << "\t>" << smi << std::endl; #endif m = SmilesToMol(smi); TEST_ASSERT(m); MolOps::assignStereochemistry(*m, true); TEST_ASSERT(m->getAtomWithIdx(1)->hasProp(common_properties::_CIPCode)); m->getAtomWithIdx(1)->getProp(common_properties::_CIPCode, cip); TEST_ASSERT(cip == "S"); TEST_ASSERT(m->getAtomWithIdx(3)->hasProp(common_properties::_CIPCode)); m->getAtomWithIdx(3)->getProp(common_properties::_CIPCode, cip); TEST_ASSERT(cip == "S"); delete m; smi = "C1(N([C@H]2O[C@H](CO)SC2)C=CC(N)=N1)=O"; #ifdef VERBOSE_CANON BOOST_LOG(rdDebugLog) << " ----------------- ------------- ----------------" << std::endl; BOOST_LOG(rdDebugLog) << "\t>" << smi << std::endl; #endif m = SmilesToMol(smi); TEST_ASSERT(m); MolOps::assignStereochemistry(*m, true); TEST_ASSERT(m->getAtomWithIdx(2)->hasProp(common_properties::_CIPCode)); m->getAtomWithIdx(2)->getProp(common_properties::_CIPCode, cip); TEST_ASSERT(cip == "S"); TEST_ASSERT(m->getAtomWithIdx(4)->hasProp(common_properties::_CIPCode)); m->getAtomWithIdx(4)->getProp(common_properties::_CIPCode, cip); TEST_ASSERT(cip == "S"); // this is Issue 152: smi = "C1[C@H](N)C[C@H](C)C=1"; #ifdef VERBOSE_CANON BOOST_LOG(rdDebugLog) << " ----------------- ------------- ----------------" << std::endl; BOOST_LOG(rdDebugLog) << "\t>" << smi << std::endl; #endif delete m; m = SmilesToMol(smi); TEST_ASSERT(m); MolOps::assignStereochemistry(*m, true); TEST_ASSERT(m->getAtomWithIdx(1)->hasProp(common_properties::_CIPCode)); m->getAtomWithIdx(1)->getProp(common_properties::_CIPCode, cip); TEST_ASSERT(cip == "R"); TEST_ASSERT(m->getAtomWithIdx(4)->hasProp(common_properties::_CIPCode)); m->getAtomWithIdx(4)->getProp(common_properties::_CIPCode, cip); TEST_ASSERT(cip == "S"); // ----------------------------------------------- // these are related to Issue 397: smi = "C(=O)[C@@H](C)N"; #ifdef VERBOSE_CANON BOOST_LOG(rdDebugLog) << " ----------------- ------------- ----------------" << std::endl; BOOST_LOG(rdDebugLog) << "\t>" << smi << std::endl; #endif delete m; m = SmilesToMol(smi); TEST_ASSERT(m); MolOps::assignStereochemistry(*m, true); TEST_ASSERT(m->getAtomWithIdx(2)->hasProp(common_properties::_CIPCode)); m->getAtomWithIdx(2)->getProp(common_properties::_CIPCode, cip); TEST_ASSERT(cip == "R"); smi = "C(=O)[C@@H](CO)N"; #ifdef VERBOSE_CANON BOOST_LOG(rdDebugLog) << " ----------------- ------------- ----------------" << std::endl; BOOST_LOG(rdDebugLog) << "\t>" << smi << std::endl; #endif delete m; m = SmilesToMol(smi); TEST_ASSERT(m); MolOps::assignStereochemistry(*m, true); TEST_ASSERT(m->getAtomWithIdx(2)->hasProp(common_properties::_CIPCode)); m->getAtomWithIdx(2)->getProp(common_properties::_CIPCode, cip); TEST_ASSERT(cip == "R"); smi = "C(O)[C@@H](C)N"; #ifdef VERBOSE_CANON BOOST_LOG(rdDebugLog) << " ----------------- ------------- ----------------" << std::endl; BOOST_LOG(rdDebugLog) << "\t>" << smi << std::endl; #endif delete m; m = SmilesToMol(smi); TEST_ASSERT(m); MolOps::assignStereochemistry(*m, true); TEST_ASSERT(m->getAtomWithIdx(2)->hasProp(common_properties::_CIPCode)); m->getAtomWithIdx(2)->getProp(common_properties::_CIPCode, cip); TEST_ASSERT(cip == "R"); // ----------------------------------------------- // NOTE: This test gives correct results according to the current // CIP ranking procedure, but the results are actually incorrect. // This arises because of the inclusion of hybridization in the // chiral atom invariants // (see the note in Chirality.cpp:buildCIPInvariants()) smi = "[H][C@@](O)(C=C)C(C)CC"; #ifdef VERBOSE_CANON BOOST_LOG(rdDebugLog) << " ----------------- ------------- ----------------" << std::endl; BOOST_LOG(rdDebugLog) << "\t>" << smi << std::endl; #endif delete m; m = SmilesToMol(smi); TEST_ASSERT(m); MolOps::assignStereochemistry(*m, true); TEST_ASSERT(m->getAtomWithIdx(0)->hasProp(common_properties::_CIPCode)); m->getAtomWithIdx(0)->getProp(common_properties::_CIPCode, cip); TEST_ASSERT(cip == "S"); smi = "[H][C@@](O)(C=C)C(C)CO"; #ifdef VERBOSE_CANON BOOST_LOG(rdDebugLog) << " ----------------- ------------- ----------------" << std::endl; BOOST_LOG(rdDebugLog) << "\t>" << smi << std::endl; #endif delete m; m = SmilesToMol(smi); TEST_ASSERT(m); MolOps::assignStereochemistry(*m, true); TEST_ASSERT(m->getAtomWithIdx(0)->hasProp(common_properties::_CIPCode)); m->getAtomWithIdx(0)->getProp(common_properties::_CIPCode, cip); TEST_ASSERT(cip == "R"); smi = "[H][C@@]12C[C@@](NC1)(OC2)[H]"; #ifdef VERBOSE_CANON BOOST_LOG(rdDebugLog) << " ----------------- ------------- ----------------" << std::endl; BOOST_LOG(rdDebugLog) << "\t>" << smi << std::endl; #endif delete m; m = SmilesToMol(smi); TEST_ASSERT(m); MolOps::assignStereochemistry(*m, true); TEST_ASSERT(m->getAtomWithIdx(0)->hasProp(common_properties::_CIPCode)); m->getAtomWithIdx(0)->getProp(common_properties::_CIPCode, cip); TEST_ASSERT(cip == "R"); TEST_ASSERT(m->getAtomWithIdx(2)->hasProp(common_properties::_CIPCode)); m->getAtomWithIdx(2)->getProp(common_properties::_CIPCode, cip); TEST_ASSERT(cip == "S"); smi = "[H][C@@]12C[C@@](C=C1)(CC2)[H]"; #ifdef VERBOSE_CANON BOOST_LOG(rdDebugLog) << " ----------------- ------------- ----------------" << std::endl; BOOST_LOG(rdDebugLog) << "\t>" << smi << std::endl; #endif delete m; m = SmilesToMol(smi); TEST_ASSERT(m); MolOps::assignStereochemistry(*m, true); TEST_ASSERT(m->getAtomWithIdx(0)->hasProp(common_properties::_CIPCode)); m->getAtomWithIdx(0)->getProp(common_properties::_CIPCode, cip); TEST_ASSERT(cip == "R"); TEST_ASSERT(m->getAtomWithIdx(2)->hasProp(common_properties::_CIPCode)); m->getAtomWithIdx(2)->getProp(common_properties::_CIPCode, cip); TEST_ASSERT(cip == "S"); smi = "[H][C@@]12O[C@@](CC1)(C3C2C(NC3=O)=O)[H]"; #ifdef VERBOSE_CANON BOOST_LOG(rdDebugLog) << " ----------------- ------------- ----------------" << std::endl; BOOST_LOG(rdDebugLog) << "\t>" << smi << std::endl; #endif delete m; m = SmilesToMol(smi); TEST_ASSERT(m); MolOps::assignStereochemistry(*m, true); TEST_ASSERT(m->getAtomWithIdx(0)->hasProp(common_properties::_CIPCode)); m->getAtomWithIdx(0)->getProp(common_properties::_CIPCode, cip); TEST_ASSERT(cip == "R"); TEST_ASSERT(m->getAtomWithIdx(2)->hasProp(common_properties::_CIPCode)); m->getAtomWithIdx(2)->getProp(common_properties::_CIPCode, cip); TEST_ASSERT(cip == "S"); smi = "[H][C@@]12O[C@@](C=C1)(C3C2C(NC3=O)=O)[H]"; #ifdef VERBOSE_CANON BOOST_LOG(rdDebugLog) << " ----------------- ------------- ----------------" << std::endl; BOOST_LOG(rdDebugLog) << "\t>" << smi << std::endl; #endif delete m; m = SmilesToMol(smi); TEST_ASSERT(m); MolOps::assignStereochemistry(*m, true); TEST_ASSERT(m->getAtomWithIdx(0)->hasProp(common_properties::_CIPCode)); m->getAtomWithIdx(0)->getProp(common_properties::_CIPCode, cip); TEST_ASSERT(cip == "R"); TEST_ASSERT(m->getAtomWithIdx(2)->hasProp(common_properties::_CIPCode)); m->getAtomWithIdx(2)->getProp(common_properties::_CIPCode, cip); TEST_ASSERT(cip == "S"); delete m; // ----------------------------------------------- BOOST_LOG(rdInfoLog) << "Finished" << std::endl; } void test12() { BOOST_LOG(rdInfoLog) << "-----------------------\n Testing double bond stereochemistry" << std::endl; ROMol *m; RWMol *m2; std::string smi = "F\\C=C/Cl"; std::string refSmi; m = SmilesToMol(smi); TEST_ASSERT(m); TEST_ASSERT(m->getBondWithIdx(0)->getStereo() == Bond::STEREONONE); TEST_ASSERT(m->getBondWithIdx(1)->getStereo() == Bond::STEREOZ); TEST_ASSERT(m->getBondWithIdx(2)->getStereo() == Bond::STEREONONE); delete m; smi = "F/C=CCl"; m = SmilesToMol(smi); TEST_ASSERT(m); TEST_ASSERT(m->getBondWithIdx(1)->getStereo() == Bond::STEREONONE); delete m; smi = "F/C=C/Cl"; m = SmilesToMol(smi); TEST_ASSERT(m); TEST_ASSERT(m->getBondWithIdx(1)->getStereo() == Bond::STEREOE); delete m; smi = "F/C=C(/Br)Cl"; m = SmilesToMol(smi); TEST_ASSERT(m); TEST_ASSERT(m->getBondWithIdx(1)->getStereo() == Bond::STEREOE); delete m; smi = "F/C=C(/Cl)Br"; m = SmilesToMol(smi); TEST_ASSERT(m); TEST_ASSERT(m->getBondWithIdx(1)->getStereo() == Bond::STEREOZ); delete m; smi = "F/C(Br)=C/Cl"; m = SmilesToMol(smi); TEST_ASSERT(m); TEST_ASSERT(m->getBondWithIdx(2)->getStereo() == Bond::STEREOZ); delete m; smi = "F/C=C(/Cl)Cl"; m = SmilesToMol(smi); TEST_ASSERT(m); TEST_ASSERT(m->getBondWithIdx(1)->getStereo() == Bond::STEREONONE); // build a molecule from scratch to test problems // around Issue 180. The molecule corresponds to SMILES // F/C=C(/Br)C delete m; m2 = new RWMol(); m2->addAtom(new Atom(9), true, true); m2->addAtom(new Atom(6), true, true); m2->addAtom(new Atom(6), true, true); m2->addAtom(new Atom(35), true, true); m2->addAtom(new Atom(6), true, true); m2->addBond(0, 1, Bond::SINGLE); m2->addBond(1, 2, Bond::DOUBLE); m2->addBond(2, 3, Bond::SINGLE); m2->addBond(2, 4, Bond::SINGLE); m2->getBondWithIdx(0)->setBondDir(Bond::ENDUPRIGHT); m2->getBondWithIdx(2)->setBondDir(Bond::ENDUPRIGHT); m2->getBondWithIdx(3)->setBondDir(Bond::ENDDOWNRIGHT); MolOps::sanitizeMol(*m2); MolOps::assignStereochemistry(*m2); TEST_ASSERT(m2->getBondWithIdx(1)->getStereo() == Bond::STEREOE); m2->getBondWithIdx(0)->setBondDir(Bond::ENDDOWNRIGHT); MolOps::assignStereochemistry(*m2, true, true); TEST_ASSERT(m2->getBondWithIdx(1)->getStereo() == Bond::STEREOZ); delete m2; m2 = new RWMol(); m2->addAtom(new Atom(9), true, true); m2->addAtom(new Atom(6), true, true); m2->addAtom(new Atom(6), true, true); m2->addAtom(new Atom(35), true, true); m2->addAtom(new Atom(6), true, true); m2->addBond(1, 0, Bond::SINGLE); m2->addBond(1, 2, Bond::DOUBLE); m2->addBond(2, 3, Bond::SINGLE); m2->addBond(2, 4, Bond::SINGLE); m2->getBondWithIdx(0)->setBondDir(Bond::ENDUPRIGHT); m2->getBondWithIdx(2)->setBondDir(Bond::ENDUPRIGHT); m2->getBondWithIdx(3)->setBondDir(Bond::ENDDOWNRIGHT); MolOps::sanitizeMol(*m2); MolOps::assignStereochemistry(*m2); TEST_ASSERT(m2->getBondWithIdx(1)->getStereo() == Bond::STEREOZ); m2->getBondWithIdx(0)->setBondDir(Bond::ENDDOWNRIGHT); MolOps::assignStereochemistry(*m2, true, true); TEST_ASSERT(m2->getBondWithIdx(1)->getStereo() == Bond::STEREOE); // ---------------------- // test Issue 174: delete m2; smi = "O\\N=C\\C=N/O"; m2 = SmilesToMol(smi); TEST_ASSERT(m2); TEST_ASSERT(m2->getBondWithIdx(1)->getStereo() == Bond::STEREOE); TEST_ASSERT(m2->getBondWithIdx(3)->getStereo() == Bond::STEREOZ); refSmi = MolToSmiles(*m2, 1); m = SmilesToMol(refSmi); TEST_ASSERT(m); smi = MolToSmiles(*m, 1); TEST_ASSERT(refSmi == smi); delete m; delete m2; BOOST_LOG(rdInfoLog) << "Finished" << std::endl; } void testIssue183() { // ---------------------- // test "unsetting" of redundant bond directions: BOOST_LOG(rdInfoLog) << "-----------------------\n Testing Issue 183\n" << std::endl; RWMol *m, *m2; std::string smi; std::string refSmi; smi = "Cl\\C(C)=C(\\C(F)=C(/F)C)/C(C)=C(\\F)C"; m2 = SmilesToMol(smi); TEST_ASSERT(m2); TEST_ASSERT(m2->getBondWithIdx(2)->getStereo() == Bond::STEREOE); TEST_ASSERT(m2->getBondWithIdx(5)->getStereo() == Bond::STEREOE); TEST_ASSERT(m2->getBondWithIdx(10)->getStereo() == Bond::STEREOZ); // m2->debugMol(std::cerr); refSmi = MolToSmiles(*m2, 1); BOOST_LOG(rdInfoLog) << "ref: " << refSmi << std::endl; m = SmilesToMol(refSmi); TEST_ASSERT(m); smi = MolToSmiles(*m, 1); BOOST_LOG(rdInfoLog) << "smi: " << smi << std::endl; TEST_ASSERT(refSmi == smi); int nEs = 0, nZs = 0, nDbl = 0; for (RWMol::BondIterator bondIt = m->beginBonds(); bondIt != m->endBonds(); bondIt++) { if ((*bondIt)->getBondType() == Bond::DOUBLE) { nDbl++; if ((*bondIt)->getStereo() == Bond::STEREOE) { nEs++; } else if ((*bondIt)->getStereo() == Bond::STEREOZ) { nZs++; } } } // BOOST_LOG(rdInfoLog) << ">> " << nDbl << " " << nEs << " " << nZs << // std::endl; TEST_ASSERT(nDbl == 3); TEST_ASSERT(nEs == 2); TEST_ASSERT(nZs == 1); delete m; delete m2; BOOST_LOG(rdInfoLog) << "Finished" << std::endl; } void testIssue188() { BOOST_LOG(rdInfoLog) << "-----------------------\n Testing Issue 188: bad CIP rankings" << std::endl; ROMol *m; std::string smi; unsigned int cip1, cip2, cip3; smi = "OC[C@H](C=C)C"; m = SmilesToMol(smi); UINT_VECT ranks; ranks.resize(m->getNumAtoms()); Chirality::assignAtomCIPRanks(*m, ranks); TEST_ASSERT(m); TEST_ASSERT(m->getAtomWithIdx(1)->hasProp(common_properties::_CIPRank)); m->getAtomWithIdx(1)->getProp(common_properties::_CIPRank, cip1); TEST_ASSERT(m->getAtomWithIdx(3)->hasProp(common_properties::_CIPRank)); m->getAtomWithIdx(3)->getProp(common_properties::_CIPRank, cip2); TEST_ASSERT(cip1 > cip2); TEST_ASSERT(m->getAtomWithIdx(5)->hasProp(common_properties::_CIPRank)); m->getAtomWithIdx(5)->getProp(common_properties::_CIPRank, cip3); TEST_ASSERT(cip1 > cip3); TEST_ASSERT(cip2 > cip3); delete m; smi = "CC(=N\\N)/C=N/N"; m = SmilesToMol(smi); TEST_ASSERT(m); ranks.resize(m->getNumAtoms()); Chirality::assignAtomCIPRanks(*m, ranks); TEST_ASSERT(m->getAtomWithIdx(0)->hasProp(common_properties::_CIPRank)); m->getAtomWithIdx(0)->getProp(common_properties::_CIPRank, cip1); TEST_ASSERT(m->getAtomWithIdx(1)->hasProp(common_properties::_CIPRank)); m->getAtomWithIdx(1)->getProp(common_properties::_CIPRank, cip2); TEST_ASSERT(cip2 > cip1); TEST_ASSERT(m->getAtomWithIdx(4)->hasProp(common_properties::_CIPRank)); m->getAtomWithIdx(4)->getProp(common_properties::_CIPRank, cip3); TEST_ASSERT(cip3 > cip1); TEST_ASSERT(cip2 > cip3); delete m; BOOST_LOG(rdInfoLog) << "Finished" << std::endl; } void testIssue189() { BOOST_LOG(rdInfoLog) << "-----------------------\n Testing Issue 189: " "BondDirs not getting properly cleared." << std::endl; ROMol *m; std::string smi, refSmi; int count; smi = "C(=S)/N=c(/n1C)scc1"; m = SmilesToMol(smi); TEST_ASSERT(m); TEST_ASSERT(m->getBondWithIdx(2)->getBondType() == Bond::DOUBLE); TEST_ASSERT(m->getBondWithIdx(2)->getStereo() == Bond::STEREOZ); count = 0; for (unsigned int i = 0; i < m->getNumBonds(); i++) { if (m->getBondWithIdx(i)->getBondDir() != Bond::NONE) { count++; } } TEST_ASSERT(count == 2); refSmi = MolToSmiles(*m, 1); count = 0; for (unsigned int i = 0; i < m->getNumBonds(); i++) { if (m->getBondWithIdx(i)->getBondDir() != Bond::NONE) { count++; } } TEST_ASSERT(count == 2); delete m; m = SmilesToMol(refSmi); TEST_ASSERT(m); count = 0; for (unsigned int i = 0; i < m->getNumBonds(); i++) { if (m->getBondWithIdx(i)->getBondDir() != Bond::NONE) { count++; } } TEST_ASSERT(count == 2); smi = MolToSmiles(*m, 1); count = 0; for (unsigned int i = 0; i < m->getNumBonds(); i++) { if (m->getBondWithIdx(i)->getBondDir() != Bond::NONE) { count++; } } TEST_ASSERT(count == 2); TEST_ASSERT(smi == refSmi); delete m; BOOST_LOG(rdInfoLog) << "Finished" << std::endl; } void testIssue190() { BOOST_LOG(rdInfoLog) << "-----------------------\n Testing Issue 190: " "BondDirs incorrectly cleared." << std::endl; ROMol *m; std::string smi, refSmi; int count; smi = "O\\N=C\\NC(\\C)=N/OC"; m = SmilesToMol(smi); TEST_ASSERT(m); TEST_ASSERT(m->getBondWithIdx(1)->getBondType() == Bond::DOUBLE); TEST_ASSERT(m->getBondWithIdx(1)->getStereo() == Bond::STEREOE); TEST_ASSERT(m->getBondWithIdx(5)->getBondType() == Bond::DOUBLE); TEST_ASSERT(m->getBondWithIdx(5)->getStereo() == Bond::STEREOZ); refSmi = MolToSmiles(*m, 1); count = 0; for (unsigned int i = 0; i < m->getNumBonds(); i++) { if (m->getBondWithIdx(i)->getBondDir() != Bond::NONE) { count++; } } TEST_ASSERT(count == 4); delete m; m = SmilesToMol(refSmi); TEST_ASSERT(m); count = 0; for (unsigned int i = 0; i < m->getNumBonds(); i++) { if (m->getBondWithIdx(i)->getBondDir() != Bond::NONE) { count++; } } TEST_ASSERT(count == 4); smi = MolToSmiles(*m, 1); count = 0; for (unsigned int i = 0; i < m->getNumBonds(); i++) { if (m->getBondWithIdx(i)->getBondDir() != Bond::NONE) { count++; } } TEST_ASSERT(count == 4); TEST_ASSERT(smi == refSmi); delete m; smi = "O\\N=C\\CC(\\C)=N/OC"; m = SmilesToMol(smi); TEST_ASSERT(m); TEST_ASSERT(m->getBondWithIdx(1)->getBondType() == Bond::DOUBLE); TEST_ASSERT(m->getBondWithIdx(1)->getStereo() == Bond::STEREOE); TEST_ASSERT(m->getBondWithIdx(5)->getBondType() == Bond::DOUBLE); TEST_ASSERT(m->getBondWithIdx(5)->getStereo() == Bond::STEREOZ); refSmi = MolToSmiles(*m, 1); count = 0; for (unsigned int i = 0; i < m->getNumBonds(); i++) { if (m->getBondWithIdx(i)->getBondDir() != Bond::NONE) { count++; } } TEST_ASSERT(count == 4); delete m; m = SmilesToMol(refSmi); TEST_ASSERT(m); count = 0; for (unsigned int i = 0; i < m->getNumBonds(); i++) { if (m->getBondWithIdx(i)->getBondDir() != Bond::NONE) { count++; } } TEST_ASSERT(count == 4); smi = MolToSmiles(*m, 1); count = 0; for (unsigned int i = 0; i < m->getNumBonds(); i++) { if (m->getBondWithIdx(i)->getBondDir() != Bond::NONE) { count++; } } TEST_ASSERT(count == 4); TEST_ASSERT(smi == refSmi); delete m; smi = "O\\N=C\\C(=O)C(\\C)=N/OC"; m = SmilesToMol(smi); TEST_ASSERT(m); TEST_ASSERT(m->getBondWithIdx(1)->getBondType() == Bond::DOUBLE); TEST_ASSERT(m->getBondWithIdx(1)->getStereo() == Bond::STEREOE); TEST_ASSERT(m->getBondWithIdx(6)->getBondType() == Bond::DOUBLE); TEST_ASSERT(m->getBondWithIdx(6)->getStereo() == Bond::STEREOZ); refSmi = MolToSmiles(*m, 1); count = 0; for (unsigned int i = 0; i < m->getNumBonds(); i++) { if (m->getBondWithIdx(i)->getBondDir() != Bond::NONE) { count++; } } TEST_ASSERT(count == 4); delete m; m = SmilesToMol(refSmi); TEST_ASSERT(m); count = 0; for (unsigned int i = 0; i < m->getNumBonds(); i++) { if (m->getBondWithIdx(i)->getBondDir() != Bond::NONE) { count++; } } TEST_ASSERT(count == 4); smi = MolToSmiles(*m, 1); count = 0; for (unsigned int i = 0; i < m->getNumBonds(); i++) { if (m->getBondWithIdx(i)->getBondDir() != Bond::NONE) { count++; } } TEST_ASSERT(count == 4); TEST_ASSERT(smi == refSmi); delete m; BOOST_LOG(rdInfoLog) << "Finished" << std::endl; } void testShortestPath() { BOOST_LOG(rdInfoLog) << "-----------------------\n Testing shortest path " "code. This should finish very quickly." << std::endl; { std::string smi = "CC(OC1C(CCCC3)C3C(CCCC2)C2C1OC(C)=O)=O"; ROMol *m = SmilesToMol(smi); INT_LIST path = MolOps::getShortestPath(*m, 1, 20); CHECK_INVARIANT(path.size() == 7, ""); INT_LIST_CI pi = path.begin(); CHECK_INVARIANT((*pi) == 1, ""); pi++; CHECK_INVARIANT((*pi) == 2, ""); pi++; CHECK_INVARIANT((*pi) == 3, ""); pi++; CHECK_INVARIANT((*pi) == 16, ""); pi++; CHECK_INVARIANT((*pi) == 17, ""); pi++; CHECK_INVARIANT((*pi) == 18, ""); pi++; CHECK_INVARIANT((*pi) == 20, ""); pi++; delete m; } { // issue 2219400 std::string smi = "CC.C"; ROMol *m = SmilesToMol(smi); INT_LIST path = MolOps::getShortestPath(*m, 0, 1); std::cerr << "path: " << path.size() << std::endl; CHECK_INVARIANT(path.size() == 2, ""); INT_LIST_CI pi = path.begin(); CHECK_INVARIANT((*pi) == 0, ""); pi++; CHECK_INVARIANT((*pi) == 1, ""); path = MolOps::getShortestPath(*m, 1, 2); CHECK_INVARIANT(path.size() == 0, ""); path = MolOps::getShortestPath(*m, 0, 2); CHECK_INVARIANT(path.size() == 0, ""); delete m; } // fused ring test { std::string smi = "[H]c1nc2c(C(=O)N([H])C2([H])Cl)c([H])c1Cl"; ROMol *m = SmilesToMol(smi); INT_LIST path = MolOps::getShortestPath(*m, 8, 11); CHECK_INVARIANT(path.size() == 7, ""); INT_LIST_CI pi = path.begin(); CHECK_INVARIANT((*pi) == 8, ""); pi++; CHECK_INVARIANT((*pi) == 7, ""); pi++; CHECK_INVARIANT((*pi) == 2, ""); pi++; pi++; // two equally long routes here pi++; // two equally long routes here CHECK_INVARIANT((*pi) == 10, ""); pi++; CHECK_INVARIANT((*pi) == 11, ""); pi++; delete m; } BOOST_LOG(rdInfoLog) << "Finished" << std::endl; } void testIssue210() { BOOST_LOG(rdInfoLog) << "-----------------------\n Testing Issue 210" << std::endl; ROMol *m, *m2; std::string smi = "C1CC1"; m = SmilesToMol(smi); TEST_ASSERT(m); TEST_ASSERT(m->getNumAtoms() == 3); TEST_ASSERT(m->getRingInfo()->isInitialized()); m2 = MolOps::addHs(*m); TEST_ASSERT(m2->getNumAtoms() == 9); TEST_ASSERT(m2->getRingInfo()->isInitialized()); BOOST_LOG(rdInfoLog) << "Finished" << std::endl; delete m; delete m2; } void testIssue211() { BOOST_LOG(rdInfoLog) << "-----------------------\n Testing Issue 211" << std::endl; ROMol *m; std::string smi = "P(c1ccccc1)(c1ccccc1)c1ccccc1"; m = SmilesToMol(smi); TEST_ASSERT(m); TEST_ASSERT(m->getNumAtoms() == 19); const Atom *at = m->getAtomWithIdx(0); TEST_ASSERT(at->getHybridization() == Atom::SP3); delete m; BOOST_LOG(rdInfoLog) << "Finished" << std::endl; } void testIssue212() { BOOST_LOG(rdInfoLog) << "-----------------------\n Testing Issue 212" << std::endl; ROMol *m, *m2; std::string smi, mb; smi = "C"; m = SmilesToMol(smi); TEST_ASSERT(m); TEST_ASSERT(m->getNumAtoms() == 1); auto *conf = new Conformer(1); m->addConformer(conf); conf->setAtomPos(0, RDGeom::Point3D(0, 0, 0)); m2 = MolOps::addHs(*m, false, true); TEST_ASSERT(m2->getNumAtoms() == 5); try { mb = MolToMolBlock(*m2); } catch (...) { TEST_ASSERT(0); //,"MolToMolBlock() failed"); } delete m; delete m2; BOOST_LOG(rdInfoLog) << "Finished" << std::endl; } void testAddHsCoords() { BOOST_LOG(rdInfoLog) << "-----------------------\n Testing AddHs with coordinates" << std::endl; ROMol *m, *m2; RDGeom::Point3D v; double bondLength = PeriodicTable::getTable()->getRb0(1) + PeriodicTable::getTable()->getRb0(6); double tetDist = 2. * sin((109.471 / 2.) * M_PI / 180) * bondLength; double sp2Dist = 2. * sin(60. * M_PI / 180) * bondLength; std::string smi; smi = "C"; m = SmilesToMol(smi); TEST_ASSERT(m); TEST_ASSERT(m->getNumAtoms() == 1); auto *conf = new Conformer(1); m->addConformer(conf); conf->setAtomPos(0, RDGeom::Point3D(0, 0, 0)); m2 = MolOps::addHs(*m, false, true); const Conformer *conf2 = &(m2->getConformer()); TEST_ASSERT(m2->getNumAtoms() == 5); TEST_ASSERT( feq((conf2->getAtomPos(0) - conf2->getAtomPos(1)).length(), bondLength)); TEST_ASSERT( feq((conf2->getAtomPos(0) - conf2->getAtomPos(2)).length(), bondLength)); TEST_ASSERT( feq((conf2->getAtomPos(0) - conf2->getAtomPos(3)).length(), bondLength)); TEST_ASSERT( feq((conf2->getAtomPos(0) - conf2->getAtomPos(4)).length(), bondLength)); TEST_ASSERT( feq((conf2->getAtomPos(1) - conf2->getAtomPos(2)).length(), tetDist)); TEST_ASSERT( feq((conf2->getAtomPos(1) - conf2->getAtomPos(3)).length(), tetDist)); TEST_ASSERT( feq((conf2->getAtomPos(1) - conf2->getAtomPos(4)).length(), tetDist)); TEST_ASSERT( feq((conf2->getAtomPos(2) - conf2->getAtomPos(3)).length(), tetDist)); TEST_ASSERT( feq((conf2->getAtomPos(2) - conf2->getAtomPos(4)).length(), tetDist)); TEST_ASSERT( feq((conf2->getAtomPos(3) - conf2->getAtomPos(4)).length(), tetDist)); delete m; delete m2; smi = "CC"; m = SmilesToMol(smi); TEST_ASSERT(m); TEST_ASSERT(m->getNumAtoms() == 2); conf = new Conformer(2); m->addConformer(conf); conf->setAtomPos(0, RDGeom::Point3D(0, 0, 0)); conf->setAtomPos(1, RDGeom::Point3D(1.54, 0, 0)); m2 = MolOps::addHs(*m, false, true); conf2 = &(m2->getConformer()); TEST_ASSERT(m2->getNumAtoms() == 8); TEST_ASSERT( feq((conf2->getAtomPos(0) - conf2->getAtomPos(2)).length(), bondLength)); TEST_ASSERT( feq((conf2->getAtomPos(0) - conf2->getAtomPos(3)).length(), bondLength)); TEST_ASSERT( feq((conf2->getAtomPos(0) - conf2->getAtomPos(4)).length(), bondLength)); TEST_ASSERT( feq((conf2->getAtomPos(2) - conf2->getAtomPos(3)).length(), tetDist)); TEST_ASSERT( feq((conf2->getAtomPos(2) - conf2->getAtomPos(4)).length(), tetDist)); TEST_ASSERT( feq((conf2->getAtomPos(3) - conf2->getAtomPos(4)).length(), tetDist)); TEST_ASSERT( feq((conf2->getAtomPos(1) - conf2->getAtomPos(5)).length(), bondLength)); TEST_ASSERT( feq((conf2->getAtomPos(1) - conf2->getAtomPos(6)).length(), bondLength)); TEST_ASSERT( feq((conf2->getAtomPos(1) - conf2->getAtomPos(7)).length(), bondLength)); TEST_ASSERT( feq((conf2->getAtomPos(5) - conf2->getAtomPos(6)).length(), tetDist)); TEST_ASSERT( feq((conf2->getAtomPos(5) - conf2->getAtomPos(7)).length(), tetDist)); TEST_ASSERT( feq((conf2->getAtomPos(6) - conf2->getAtomPos(7)).length(), tetDist)); delete m; delete m2; smi = "C=C"; m = SmilesToMol(smi); TEST_ASSERT(m); TEST_ASSERT(m->getNumAtoms() == 2); conf = new Conformer(2); m->addConformer(conf); conf->setAtomPos(0, RDGeom::Point3D(0, 0, 0)); conf->setAtomPos(1, RDGeom::Point3D(1.3, 0, 0)); m2 = MolOps::addHs(*m, false, true); conf2 = &(m2->getConformer()); TEST_ASSERT(m2->getNumAtoms() == 6); TEST_ASSERT( feq((conf2->getAtomPos(0) - conf2->getAtomPos(2)).length(), bondLength)); TEST_ASSERT( feq((conf2->getAtomPos(0) - conf2->getAtomPos(3)).length(), bondLength)); TEST_ASSERT( feq((conf2->getAtomPos(1) - conf2->getAtomPos(4)).length(), bondLength)); TEST_ASSERT( feq((conf2->getAtomPos(1) - conf2->getAtomPos(5)).length(), bondLength)); TEST_ASSERT( feq((conf2->getAtomPos(2) - conf2->getAtomPos(3)).length(), sp2Dist)); TEST_ASSERT( feq((conf2->getAtomPos(4) - conf2->getAtomPos(5)).length(), sp2Dist)); delete m; delete m2; { // make sure Hs are on the xy plane if conformer is 2D auto m = "C=C"_smiles; TEST_ASSERT(m.get()); TEST_ASSERT(m->getNumAtoms() == 2); conf = new Conformer(2); m->addConformer(conf); conf->setAtomPos(0, RDGeom::Point3D(0, 0, 0)); conf->setAtomPos(1, RDGeom::Point3D(1.3, 0, 0)); conf->set3D(false); MolOps::addHs(*m, false, true); conf = &(m->getConformer()); TEST_ASSERT(m->getNumAtoms() == 6); TEST_ASSERT(feq(conf->getAtomPos(2).z, 0.0)); TEST_ASSERT(feq(conf->getAtomPos(3).z, 0.0)); TEST_ASSERT(feq(conf->getAtomPos(4).z, 0.0)); TEST_ASSERT(feq(conf->getAtomPos(5).z, 0.0)); } { // make sure NHs are on the same plane as the double bond auto m = "NC=C"_smiles; TEST_ASSERT(m.get()); TEST_ASSERT(m->getNumAtoms() == 3); unsigned int nh2Idx = 0; unsigned int chIdx = 1; unsigned int ch2Idx = 2; conf = new Conformer(3); m->addConformer(conf); conf->setAtomPos(nh2Idx, RDGeom::Point3D(1.759236, 0.825542, 1.347849)); conf->setAtomPos(chIdx, RDGeom::Point3D(0.817392, 0.181048, 2.180373)); conf->setAtomPos(ch2Idx, RDGeom::Point3D(-0.070943, 0.888262, 2.875625)); MolOps::addHs(*m, false, true); conf = &(m->getConformer()); TEST_ASSERT(m->getNumAtoms() == 8); std::vector nh2HIdxs{3, 4}; unsigned int chHIdx = 5; std::vector ch2HIdxs{6, 7}; RDGeom::Point3D nccNormal = (conf->getAtomPos(nh2Idx) - conf->getAtomPos(chIdx)) .crossProduct((conf->getAtomPos(ch2Idx) - conf->getAtomPos(chIdx))); nccNormal.normalize(); RDGeom::Point3D hnhNormal = (conf->getAtomPos(nh2HIdxs[0]) - conf->getAtomPos(nh2Idx)) .crossProduct(conf->getAtomPos(nh2HIdxs[1]) - conf->getAtomPos(nh2Idx)); hnhNormal.normalize(); RDGeom::Point3D hchNormal = (conf->getAtomPos(ch2HIdxs[0]) - conf->getAtomPos(ch2Idx)) .crossProduct(conf->getAtomPos(nh2HIdxs[1]) - conf->getAtomPos(ch2Idx)); hchNormal.normalize(); RDGeom::Point3D hcnNormal = (conf->getAtomPos(chHIdx) - conf->getAtomPos(chIdx)) .crossProduct((conf->getAtomPos(nh2Idx) - conf->getAtomPos(chIdx))); hcnNormal.normalize(); TEST_ASSERT(feq(fabs(nccNormal.dotProduct(hnhNormal)), 1.0)); TEST_ASSERT(feq(fabs(nccNormal.dotProduct(hchNormal)), 1.0)); TEST_ASSERT(feq(fabs(nccNormal.dotProduct(hcnNormal)), 1.0)); } smi = "C#C"; m = SmilesToMol(smi); TEST_ASSERT(m); TEST_ASSERT(m->getNumAtoms() == 2); conf = new Conformer(2); m->addConformer(conf); conf->setAtomPos(0, RDGeom::Point3D(0, 0, 0)); conf->setAtomPos(1, RDGeom::Point3D(1.2, 0, 0)); m2 = MolOps::addHs(*m, false, true); conf2 = &(m2->getConformer()); TEST_ASSERT(m2->getNumAtoms() == 4); TEST_ASSERT( feq((conf2->getAtomPos(0) - conf2->getAtomPos(2)).length(), bondLength)); TEST_ASSERT( feq((conf2->getAtomPos(1) - conf2->getAtomPos(3)).length(), bondLength)); TEST_ASSERT(feq((conf2->getAtomPos(0) - conf2->getAtomPos(3)).length(), bondLength + 1.2)); TEST_ASSERT(feq((conf2->getAtomPos(1) - conf2->getAtomPos(2)).length(), bondLength + 1.2)); delete m; delete m2; BOOST_LOG(rdInfoLog) << "Finished" << std::endl; } void testSanitOps() { BOOST_LOG(rdInfoLog) << "-----------------------\n Sanitization special cases" << std::endl; ROMol *m; std::string smi, pathName; smi = "CN(=O)=O"; m = SmilesToMol(smi); TEST_ASSERT(m); TEST_ASSERT(m->getNumAtoms() == 4); TEST_ASSERT(m->getAtomWithIdx(1)->getFormalCharge() == 1); delete m; smi = "C[N+](=O)[O-]"; m = SmilesToMol(smi); TEST_ASSERT(m); TEST_ASSERT(m->getNumAtoms() == 4); TEST_ASSERT(m->getAtomWithIdx(1)->getFormalCharge() == 1); delete m; smi = "Cl(=O)(=O)(=O)[O-]"; m = SmilesToMol(smi); TEST_ASSERT(m); TEST_ASSERT(m->getNumAtoms() == 5); TEST_ASSERT(m->getAtomWithIdx(0)->getFormalCharge() == 3); delete m; smi = "Cl(=O)(=O)(=O)O"; m = SmilesToMol(smi); TEST_ASSERT(m); TEST_ASSERT(m->getNumAtoms() == 5); TEST_ASSERT(m->getAtomWithIdx(0)->getFormalCharge() == 3); delete m; smi = "Br(=O)(=O)(=O)[O-]"; m = SmilesToMol(smi); TEST_ASSERT(m); TEST_ASSERT(m->getNumAtoms() == 5); TEST_ASSERT(m->getAtomWithIdx(0)->getFormalCharge() == 3); delete m; smi = "Br(=O)(=O)(=O)O"; m = SmilesToMol(smi); TEST_ASSERT(m); TEST_ASSERT(m->getNumAtoms() == 5); TEST_ASSERT(m->getAtomWithIdx(0)->getFormalCharge() == 3); delete m; smi = "I(=O)(=O)(=O)[O-]"; m = SmilesToMol(smi); TEST_ASSERT(m); TEST_ASSERT(m->getNumAtoms() == 5); TEST_ASSERT(m->getAtomWithIdx(0)->getFormalCharge() == 3); delete m; smi = "I(=O)(=O)(=O)O"; m = SmilesToMol(smi); TEST_ASSERT(m); TEST_ASSERT(m->getNumAtoms() == 5); TEST_ASSERT(m->getAtomWithIdx(0)->getFormalCharge() == 3); delete m; pathName = getenv("RDBASE"); pathName += "/Code/GraphMol/test_data/"; m = MolFileToMol(pathName + "perchlorate1.mol"); TEST_ASSERT(m); TEST_ASSERT(m->getNumAtoms() == 51); TEST_ASSERT(m->getAtomWithIdx(7)->getFormalCharge() == 3); delete m; smi = "CN=N#N"; m = SmilesToMol(smi); TEST_ASSERT(m); TEST_ASSERT(m->getNumAtoms() == 4); TEST_ASSERT(m->getAtomWithIdx(1)->getFormalCharge() == 0); TEST_ASSERT(m->getAtomWithIdx(2)->getFormalCharge() == 1); TEST_ASSERT(m->getAtomWithIdx(3)->getFormalCharge() == -1); TEST_ASSERT(m->getBondBetweenAtoms(2, 3)->getBondType() == Bond::DOUBLE); delete m; smi = "N#N=NC"; m = SmilesToMol(smi); TEST_ASSERT(m); TEST_ASSERT(m->getNumAtoms() == 4); TEST_ASSERT(m->getAtomWithIdx(2)->getFormalCharge() == 0); TEST_ASSERT(m->getAtomWithIdx(1)->getFormalCharge() == 1); TEST_ASSERT(m->getAtomWithIdx(0)->getFormalCharge() == -1); TEST_ASSERT(m->getBondBetweenAtoms(0, 1)->getBondType() == Bond::DOUBLE); delete m; smi = "N#N"; m = SmilesToMol(smi); TEST_ASSERT(m); TEST_ASSERT(m->getNumAtoms() == 2); TEST_ASSERT(m->getAtomWithIdx(1)->getFormalCharge() == 0); TEST_ASSERT(m->getAtomWithIdx(0)->getFormalCharge() == 0); TEST_ASSERT(m->getBondBetweenAtoms(0, 1)->getBondType() == Bond::TRIPLE); delete m; smi = "Cl(=O)(=O)O"; m = SmilesToMol(smi); TEST_ASSERT(m); TEST_ASSERT(m->getNumAtoms() == 4); TEST_ASSERT(m->getAtomWithIdx(0)->getFormalCharge() == 2); delete m; smi = "Cl(=O)(=O)[O-]"; m = SmilesToMol(smi); TEST_ASSERT(m); TEST_ASSERT(m->getNumAtoms() == 4); TEST_ASSERT(m->getAtomWithIdx(0)->getFormalCharge() == 2); delete m; smi = "Br(=O)(=O)O"; m = SmilesToMol(smi); TEST_ASSERT(m); TEST_ASSERT(m->getNumAtoms() == 4); TEST_ASSERT(m->getAtomWithIdx(0)->getFormalCharge() == 2); delete m; smi = "Br(=O)(=O)[O-]"; m = SmilesToMol(smi); TEST_ASSERT(m); TEST_ASSERT(m->getNumAtoms() == 4); TEST_ASSERT(m->getAtomWithIdx(0)->getFormalCharge() == 2); delete m; smi = "I(=O)(=O)O"; m = SmilesToMol(smi); TEST_ASSERT(m); TEST_ASSERT(m->getNumAtoms() == 4); TEST_ASSERT(m->getAtomWithIdx(0)->getFormalCharge() == 2); delete m; smi = "I(=O)(=O)[O-]"; m = SmilesToMol(smi); TEST_ASSERT(m); TEST_ASSERT(m->getNumAtoms() == 4); TEST_ASSERT(m->getAtomWithIdx(0)->getFormalCharge() == 2); delete m; smi = "Cl(=O)O"; m = SmilesToMol(smi); TEST_ASSERT(m); TEST_ASSERT(m->getNumAtoms() == 3); TEST_ASSERT(m->getAtomWithIdx(0)->getFormalCharge() == 1); delete m; smi = "Cl(=O)[O-]"; m = SmilesToMol(smi); TEST_ASSERT(m); TEST_ASSERT(m->getNumAtoms() == 3); TEST_ASSERT(m->getAtomWithIdx(0)->getFormalCharge() == 1); delete m; smi = "Br(=O)O"; m = SmilesToMol(smi); TEST_ASSERT(m); TEST_ASSERT(m->getNumAtoms() == 3); TEST_ASSERT(m->getAtomWithIdx(0)->getFormalCharge() == 1); delete m; smi = "Br(=O)[O-]"; m = SmilesToMol(smi); TEST_ASSERT(m); TEST_ASSERT(m->getNumAtoms() == 3); TEST_ASSERT(m->getAtomWithIdx(0)->getFormalCharge() == 1); delete m; smi = "I(=O)O"; m = SmilesToMol(smi); TEST_ASSERT(m); TEST_ASSERT(m->getNumAtoms() == 3); TEST_ASSERT(m->getAtomWithIdx(0)->getFormalCharge() == 1); delete m; smi = "I(=O)[O-]"; m = SmilesToMol(smi); TEST_ASSERT(m); TEST_ASSERT(m->getNumAtoms() == 3); TEST_ASSERT(m->getAtomWithIdx(0)->getFormalCharge() == 1); delete m; smi = "I(O)(O)(O)(O)O"; m = SmilesToMol(smi); TEST_ASSERT(m); TEST_ASSERT(m->getNumAtoms() == 6); TEST_ASSERT(m->getAtomWithIdx(0)->getFormalCharge() == 0); delete m; smi = "I(O)(O)O"; m = SmilesToMol(smi); TEST_ASSERT(m); TEST_ASSERT(m->getNumAtoms() == 4); TEST_ASSERT(m->getAtomWithIdx(0)->getFormalCharge() == 0); delete m; smi = "I(=O)(O)(O)(O)"; m = SmilesToMol(smi); TEST_ASSERT(m); TEST_ASSERT(m->getNumAtoms() == 5); TEST_ASSERT(m->getAtomWithIdx(0)->getFormalCharge() == 1); delete m; smi = "CC(=O)O[IH2](O)OC(C)=O"; m = SmilesToMol(smi); TEST_ASSERT(m); TEST_ASSERT(m->getNumAtoms() == 10); TEST_ASSERT(m->getAtomWithIdx(4)->getFormalCharge() == 0); delete m; BOOST_LOG(rdInfoLog) << "Finished" << std::endl; } void testAddConformers() { BOOST_LOG(rdInfoLog) << "-----------------------\n Testing Add Confomers" << std::endl; std::string smi = "CC"; ROMol *m = SmilesToMol(smi); unsigned int i; for (i = 0; i < 5; i++) { auto *conf = new Conformer(2); conf->setAtomPos(0, RDGeom::Point3D(0.0, 0.0, 0.0)); conf->setAtomPos(1, RDGeom::Point3D(1.5, 0.0, 0.0)); m->addConformer(conf, true); } CHECK_INVARIANT(m->getNumConformers() == 5, ""); ROMol *m2 = MolOps::addHs(*m, false, true); CHECK_INVARIANT(m2->getNumConformers() == 5, ""); // const ROMol::CONF_SPTR_LIST &confs = m2->getConformers(); ROMol::ConstConformerIterator ci; i = 0; for (ci = m2->beginConformers(); ci != m2->endConformers(); ci++) { CHECK_INVARIANT((*ci)->getNumAtoms() == 8, ""); CHECK_INVARIANT((*ci)->getId() == i, ""); const ROMol *mn = &((*ci)->getOwningMol()); CHECK_INVARIANT(mn->getNumAtoms() == 8, ""); i++; } // std::cout << m2->getNumAtoms() << " " << m2->getNumConformers() << "\n"; delete m; delete m2; BOOST_LOG(rdInfoLog) << "Finished \n "; } void testIssue252() { // lets check if we can sanitize C60 std::string smi = "C12=C3C4=C5C6=C1C7=C8C9=C1C%10=C%11C(=C29)C3=C2C3=C4C4=C5C5=C9C6=C7C6=" "C7C8=C1C1=C8C%10=C%10C%11=C2C2=C3C3=C4C4=C5C5=C%11C%12=C(C6=C95)C7=C1C1=" "C%12C5=C%11C4=C3C3=C5C(=C81)C%10=C23"; ROMol *mol = SmilesToMol(smi); for (ROMol::BondIterator it = mol->beginBonds(); it != mol->endBonds(); it++) { TEST_ASSERT((*it)->getIsAromatic()); TEST_ASSERT((*it)->getBondType() == Bond::AROMATIC); } std::string asmi = MolToSmiles(*mol); // check if we can do it in the aromatic form ROMol *nmol = SmilesToMol(asmi); for (ROMol::BondIterator it = nmol->beginBonds(); it != nmol->endBonds(); it++) { TEST_ASSERT((*it)->getIsAromatic()); TEST_ASSERT((*it)->getBondType() == Bond::AROMATIC); } std::string nsmi = MolToSmiles(*nmol); delete mol; delete nmol; // This is a check for Issue253 CHECK_INVARIANT(asmi == nsmi, ""); } void testIssue276() { BOOST_LOG(rdInfoLog) << "-----------------------\n Issue 276" << std::endl; std::string smi = "CP1(C)=CC=CN=C1C"; ROMol *mol = SmilesToMol(smi); TEST_ASSERT(mol); // as of this writing, I'm not 100% sure what the right answer is here, // but the hybridization definitely should *not* be SP2: TEST_ASSERT(mol->getAtomWithIdx(1)->getHybridization() > Atom::SP2); delete mol; BOOST_LOG(rdInfoLog) << "Finished \n "; } void testHsAndAromaticity() { BOOST_LOG(rdInfoLog) << "-----------------------\n Additional Aromaticity Cases" << std::endl; std::string smi; ROMol *mol; smi = "[CH]1-[CH]-[CH]-[CH]-[CH]-[CH]-1"; mol = SmilesToMol(smi); TEST_ASSERT(mol); // std::cerr << mol->getAtomWithIdx(0)->getHybridization() << std::endl; TEST_ASSERT(mol->getAtomWithIdx(0)->getHybridization() == Atom::SP3); TEST_ASSERT(mol->getAtomWithIdx(0)->getImplicitValence() == 0); TEST_ASSERT(mol->getAtomWithIdx(0)->getNumImplicitHs() == 0); TEST_ASSERT(mol->getAtomWithIdx(0)->getNumRadicalElectrons() == 1); TEST_ASSERT(!mol->getAtomWithIdx(0)->getIsAromatic()); TEST_ASSERT(!mol->getBondBetweenAtoms(0, 1)->getIsAromatic()); delete mol; smi = "C1=CC(=C)C(=C)C=C1"; mol = SmilesToMol(smi); TEST_ASSERT(mol); TEST_ASSERT(mol->getAtomWithIdx(0)->getHybridization() == Atom::SP2); TEST_ASSERT(mol->getAtomWithIdx(2)->getHybridization() == Atom::SP2); TEST_ASSERT(mol->getAtomWithIdx(0)->getIsAromatic()); TEST_ASSERT(mol->getBondBetweenAtoms(0, 1)->getIsAromatic()); delete mol; BOOST_LOG(rdInfoLog) << "Finished \n "; } void testSFIssue1694023() { BOOST_LOG(rdInfoLog) << "-----------------------\n Testing sf.net issue " "1694023 (bad chiral smiles after removing Hs) " << std::endl; ROMol *m; std::string smi; smi = "[C@@]([H])(F)(Cl)Br"; m = SmilesToMol(smi); TEST_ASSERT(m); TEST_ASSERT(m->getNumAtoms() == 4); TEST_ASSERT(m->getAtomWithIdx(0)->getChiralTag() == Atom::CHI_TETRAHEDRAL_CCW); smi = "[C@@](F)([H])(Cl)Br"; delete m; m = SmilesToMol(smi); TEST_ASSERT(m); TEST_ASSERT(m->getNumAtoms() == 4); TEST_ASSERT(m->getAtomWithIdx(0)->getChiralTag() == Atom::CHI_TETRAHEDRAL_CW); smi = "[C@@](F)(Cl)([H])Br"; delete m; m = SmilesToMol(smi); TEST_ASSERT(m); TEST_ASSERT(m->getNumAtoms() == 4); TEST_ASSERT(m->getAtomWithIdx(0)->getChiralTag() == Atom::CHI_TETRAHEDRAL_CCW); smi = "[C@@](F)(Cl)(Br)[H]"; delete m; m = SmilesToMol(smi); TEST_ASSERT(m); TEST_ASSERT(m->getNumAtoms() == 4); TEST_ASSERT(m->getAtomWithIdx(0)->getChiralTag() == Atom::CHI_TETRAHEDRAL_CW); smi = "[H][C@@](F)(Cl)Br"; delete m; m = SmilesToMol(smi); TEST_ASSERT(m); TEST_ASSERT(m->getNumAtoms() == 4); TEST_ASSERT(m->getAtomWithIdx(0)->getChiralTag() == Atom::CHI_TETRAHEDRAL_CCW); smi = "F[C@@]([H])(Cl)Br"; delete m; m = SmilesToMol(smi); TEST_ASSERT(m); TEST_ASSERT(m->getNumAtoms() == 4); TEST_ASSERT(m->getAtomWithIdx(1)->getChiralTag() == Atom::CHI_TETRAHEDRAL_CW); smi = "F[C@@](Cl)([H])Br"; delete m; m = SmilesToMol(smi); TEST_ASSERT(m); TEST_ASSERT(m->getNumAtoms() == 4); TEST_ASSERT(m->getAtomWithIdx(1)->getChiralTag() == Atom::CHI_TETRAHEDRAL_CCW); smi = "F[C@@](Cl)(Br)[H]"; delete m; m = SmilesToMol(smi); TEST_ASSERT(m); TEST_ASSERT(m->getNumAtoms() == 4); TEST_ASSERT(m->getAtomWithIdx(1)->getChiralTag() == Atom::CHI_TETRAHEDRAL_CW); smi = "C1CO[C@@H]1Cl"; delete m; m = SmilesToMol(smi); TEST_ASSERT(m); TEST_ASSERT(m->getNumAtoms() == 5); TEST_ASSERT(m->getAtomWithIdx(3)->getChiralTag() == Atom::CHI_TETRAHEDRAL_CCW); smi = "C1CO[C@]1([H])Cl"; delete m; m = SmilesToMol(smi); TEST_ASSERT(m); TEST_ASSERT(m->getNumAtoms() == 5); TEST_ASSERT(m->getAtomWithIdx(3)->getChiralTag() == Atom::CHI_TETRAHEDRAL_CCW); smi = "C1CO[C@@]1(Cl)[H]"; delete m; m = SmilesToMol(smi); TEST_ASSERT(m); TEST_ASSERT(m->getNumAtoms() == 5); TEST_ASSERT(m->getAtomWithIdx(3)->getChiralTag() == Atom::CHI_TETRAHEDRAL_CCW); delete m; BOOST_LOG(rdInfoLog) << "Finished" << std::endl; } void testSFIssue1719053() { BOOST_LOG(rdInfoLog) << "-----------------------\n Testing sf.net issue " "1719053 (Ring stereochemistry incorrectly removed) " << std::endl; ROMol *m; std::string smi; smi = "C[C@@H]1CCCCC1"; m = SmilesToMol(smi); TEST_ASSERT(m); TEST_ASSERT(m->getAtomWithIdx(1)->getChiralTag() == Atom::CHI_UNSPECIFIED); delete m; smi = "C[C@@H]1CC[C@@H](C)CC1"; m = SmilesToMol(smi); TEST_ASSERT(m); TEST_ASSERT(m->getAtomWithIdx(1)->getChiralTag() != Atom::CHI_UNSPECIFIED); TEST_ASSERT(m->getAtomWithIdx(4)->getChiralTag() != Atom::CHI_UNSPECIFIED); delete m; smi = "C[C@@H]1C(C)CCCC1C"; m = SmilesToMol(smi); TEST_ASSERT(m); TEST_ASSERT(m->getAtomWithIdx(1)->getChiralTag() == Atom::CHI_UNSPECIFIED); delete m; smi = "C[C@@H]1[C@H](C)CCC[C@H]1C"; m = SmilesToMol(smi); TEST_ASSERT(m); // this is a truly symmetric case, so the stereochem should be removed: TEST_ASSERT(m->getAtomWithIdx(1)->getChiralTag() == Atom::CHI_UNSPECIFIED); TEST_ASSERT(m->getAtomWithIdx(2)->getChiralTag() != Atom::CHI_UNSPECIFIED); TEST_ASSERT(m->getAtomWithIdx(7)->getChiralTag() != Atom::CHI_UNSPECIFIED); delete m; smi = "C[C@@H]1C=C[C@@H](C)C=C1"; m = SmilesToMol(smi); TEST_ASSERT(m); TEST_ASSERT(m->getAtomWithIdx(1)->getChiralTag() != Atom::CHI_UNSPECIFIED); TEST_ASSERT(m->getAtomWithIdx(4)->getChiralTag() != Atom::CHI_UNSPECIFIED); delete m; smi = "C[N@@]1C=C[C@@H](C)C=C1"; m = SmilesToMol(smi); TEST_ASSERT(m); TEST_ASSERT(m->getAtomWithIdx(1)->getChiralTag() == Atom::CHI_UNSPECIFIED); TEST_ASSERT(m->getAtomWithIdx(4)->getChiralTag() == Atom::CHI_UNSPECIFIED); // N in rings that aren't 3 rings is not chiral delete m; smi = "C[N@@]1CC[C@@H](C)CC1"; m = SmilesToMol(smi); TEST_ASSERT(m); TEST_ASSERT(m->getAtomWithIdx(1)->getChiralTag() == Atom::CHI_UNSPECIFIED); TEST_ASSERT(m->getAtomWithIdx(4)->getChiralTag() == Atom::CHI_UNSPECIFIED); delete m; BOOST_LOG(rdInfoLog) << "Finished" << std::endl; } void testSFIssue1811276() { BOOST_LOG(rdInfoLog) << "-----------------------\n Testing sf.net issue " "1811276 (kekulization failing) " << std::endl; ROMol *m; std::string smi; smi = "[O-]N1C=C[N+](=O)C=C1"; m = SmilesToMol(smi); TEST_ASSERT(m); smi = MolToSmiles(*m); TEST_ASSERT(smi == "O=[n+]1ccn([O-])cc1"); delete m; smi = "o1ccc(=O)cc1"; m = SmilesToMol(smi); TEST_ASSERT(m); smi = MolToSmiles(*m); TEST_ASSERT(smi == "O=c1ccocc1"); delete m; smi = "O=[n+]1ccocc1"; m = SmilesToMol(smi); TEST_ASSERT(m); smi = MolToSmiles(*m); TEST_ASSERT(smi == "O=[n+]1ccocc1"); delete m; smi = "O=[n+]1ccn([O-])cc1"; m = SmilesToMol(smi); TEST_ASSERT(m); smi = MolToSmiles(*m); TEST_ASSERT(smi == "O=[n+]1ccn([O-])cc1"); delete m; smi = "O=n1ccccc1"; m = SmilesToMol(smi); TEST_ASSERT(m); smi = MolToSmiles(*m); TEST_ASSERT(smi == "[O-][n+]1ccccc1"); delete m; BOOST_LOG(rdInfoLog) << "Finished" << std::endl; } void testSFIssue1836576() { BOOST_LOG(rdInfoLog) << "-----------------------\n Testing sf.net issue " "1836576 (sanitization crash) " << std::endl; RWMol *m; std::string smi; bool ok; // the original form of the test runs foul of the rules for explicit // valence on B: smi = "[BH]123[BH]45[BH]167[BH]289[BH]312[BH]838[BH]966[Co]74479%10%11%12[CH]" "633[BH]811[CH]345[BH]21[BH]1234[BH]75[BH]911[BH]226[BH]%1011[BH]227[BH]" "633[BH]44[BH]322[CH]%1145[CH]%12271"; m = SmilesToMol(smi, false, false); TEST_ASSERT(m); unsigned int opThatFailed; ok = false; try { MolOps::sanitizeMol(*m, opThatFailed); } catch (MolSanitizeException &) { ok = true; } TEST_ASSERT(ok); TEST_ASSERT(opThatFailed == MolOps::SANITIZE_PROPERTIES); delete m; BOOST_LOG(rdInfoLog) << "Finished" << std::endl; } void testChiralityAndRemoveHs() { BOOST_LOG(rdInfoLog) << "-----------------------\n Testing impact of removeHs on chirality" << std::endl; ROMol *m, *m2; std::string smi, code; smi = "F[C@]([H])(Cl)Br"; m = SmilesToMol(smi, false, false); TEST_ASSERT(m); MolOps::assignStereochemistry(*m, true, true); TEST_ASSERT(m->getAtomWithIdx(1)->hasProp(common_properties::_CIPCode)); m->getAtomWithIdx(1)->getProp(common_properties::_CIPCode, code); TEST_ASSERT(code == "R"); m2 = MolOps::removeHs(*m); TEST_ASSERT(m2); MolOps::assignStereochemistry(*m2, true, true); TEST_ASSERT(m2->getAtomWithIdx(1)->hasProp(common_properties::_CIPCode)); m2->getAtomWithIdx(1)->getProp(common_properties::_CIPCode, code); TEST_ASSERT(code == "R"); delete m; delete m2; smi = "F[C@H](Cl)Br"; m = SmilesToMol(smi, false, false); TEST_ASSERT(m); MolOps::assignStereochemistry(*m, true, true); TEST_ASSERT(m->getAtomWithIdx(1)->hasProp(common_properties::_CIPCode)); m->getAtomWithIdx(1)->getProp(common_properties::_CIPCode, code); TEST_ASSERT(code == "R"); m2 = MolOps::removeHs(*m); TEST_ASSERT(m2); MolOps::assignStereochemistry(*m2, true, true); TEST_ASSERT(m2->getAtomWithIdx(1)->hasProp(common_properties::_CIPCode)); m2->getAtomWithIdx(1)->getProp(common_properties::_CIPCode, code); TEST_ASSERT(code == "R"); delete m; delete m2; smi = "[C@]([H])(Cl)(F)Br"; m = SmilesToMol(smi, false, false); TEST_ASSERT(m); MolOps::assignStereochemistry(*m, true, true); TEST_ASSERT(m->getAtomWithIdx(0)->hasProp(common_properties::_CIPCode)); m->getAtomWithIdx(0)->getProp(common_properties::_CIPCode, code); TEST_ASSERT(code == "R"); m2 = MolOps::removeHs(*m); TEST_ASSERT(m2); MolOps::assignStereochemistry(*m2, true, true); TEST_ASSERT(m2->getAtomWithIdx(0)->hasProp(common_properties::_CIPCode)); m2->getAtomWithIdx(0)->getProp(common_properties::_CIPCode, code); TEST_ASSERT(code == "R"); delete m; delete m2; smi = "[C@H](Cl)(F)Br"; m = SmilesToMol(smi, false, false); TEST_ASSERT(m); MolOps::assignStereochemistry(*m, true, true); TEST_ASSERT(m->getAtomWithIdx(0)->hasProp(common_properties::_CIPCode)); m->getAtomWithIdx(0)->getProp(common_properties::_CIPCode, code); TEST_ASSERT(code == "R"); m2 = MolOps::removeHs(*m); TEST_ASSERT(m2); MolOps::assignStereochemistry(*m2, true, true); TEST_ASSERT(m2->getAtomWithIdx(0)->hasProp(common_properties::_CIPCode)); m2->getAtomWithIdx(0)->getProp(common_properties::_CIPCode, code); TEST_ASSERT(code == "R"); delete m; delete m2; smi = "[H]1.F[C@]1(Cl)Br"; m = SmilesToMol(smi, false, false); TEST_ASSERT(m); MolOps::assignStereochemistry(*m, true, true); TEST_ASSERT(m->getAtomWithIdx(2)->hasProp(common_properties::_CIPCode)); m->getAtomWithIdx(2)->getProp(common_properties::_CIPCode, code); TEST_ASSERT(code == "R"); m2 = MolOps::removeHs(*m); TEST_ASSERT(m2); MolOps::assignStereochemistry(*m2, true, true); TEST_ASSERT(m2->getAtomWithIdx(1)->hasProp(common_properties::_CIPCode)); m2->getAtomWithIdx(1)->getProp(common_properties::_CIPCode, code); TEST_ASSERT(code == "R"); delete m; delete m2; smi = "F[C@]1(Cl)Br.[H]1"; m = SmilesToMol(smi, false, false); TEST_ASSERT(m); MolOps::assignStereochemistry(*m, true, true); TEST_ASSERT(m->getAtomWithIdx(1)->hasProp(common_properties::_CIPCode)); m->getAtomWithIdx(1)->getProp(common_properties::_CIPCode, code); TEST_ASSERT(code == "R"); m2 = MolOps::removeHs(*m); TEST_ASSERT(m2); MolOps::assignStereochemistry(*m2, true, true); TEST_ASSERT(m2->getAtomWithIdx(1)->hasProp(common_properties::_CIPCode)); m2->getAtomWithIdx(1)->getProp(common_properties::_CIPCode, code); TEST_ASSERT(code == "R"); delete m; delete m2; smi = "[H]1.[C@]1(Cl)(F)Br"; m = SmilesToMol(smi, false, false); TEST_ASSERT(m); MolOps::assignStereochemistry(*m, true, true); TEST_ASSERT(m->getAtomWithIdx(1)->hasProp(common_properties::_CIPCode)); m->getAtomWithIdx(1)->getProp(common_properties::_CIPCode, code); TEST_ASSERT(code == "R"); m2 = MolOps::removeHs(*m); TEST_ASSERT(m2); MolOps::assignStereochemistry(*m2, true, true); TEST_ASSERT(m2->getAtomWithIdx(0)->hasProp(common_properties::_CIPCode)); m2->getAtomWithIdx(0)->getProp(common_properties::_CIPCode, code); TEST_ASSERT(code == "R"); delete m; delete m2; smi = "[C@]1(Cl)(F)Br.[H]1"; m = SmilesToMol(smi, false, false); TEST_ASSERT(m); MolOps::assignStereochemistry(*m, true, true); TEST_ASSERT(m->getAtomWithIdx(0)->hasProp(common_properties::_CIPCode)); m->getAtomWithIdx(0)->getProp(common_properties::_CIPCode, code); TEST_ASSERT(code == "R"); m2 = MolOps::removeHs(*m); TEST_ASSERT(m2); MolOps::assignStereochemistry(*m2, true, true); TEST_ASSERT(m2->getAtomWithIdx(0)->hasProp(common_properties::_CIPCode)); m2->getAtomWithIdx(0)->getProp(common_properties::_CIPCode, code); TEST_ASSERT(code == "R"); delete m; delete m2; smi = "Cl1.F2.Br3.[C@H]123"; m = SmilesToMol(smi, false, false); TEST_ASSERT(m); MolOps::assignStereochemistry(*m, true, true); TEST_ASSERT(m->getAtomWithIdx(3)->hasProp(common_properties::_CIPCode)); m->getAtomWithIdx(3)->getProp(common_properties::_CIPCode, code); TEST_ASSERT(code == "R"); delete m; smi = "[C@H]123.Cl1.F2.Br3"; m = SmilesToMol(smi, false, false); TEST_ASSERT(m); MolOps::assignStereochemistry(*m, true, true); TEST_ASSERT(m->getAtomWithIdx(0)->hasProp(common_properties::_CIPCode)); m->getAtomWithIdx(0)->getProp(common_properties::_CIPCode, code); TEST_ASSERT(code == "R"); delete m; smi = "F2.Cl1.Br3.[C@H]123"; m = SmilesToMol(smi, false, false); TEST_ASSERT(m); MolOps::assignStereochemistry(*m, true, true); TEST_ASSERT(m->getAtomWithIdx(3)->hasProp(common_properties::_CIPCode)); m->getAtomWithIdx(3)->getProp(common_properties::_CIPCode, code); TEST_ASSERT(code == "R"); delete m; smi = "Cl2.F1.Br3.[C@H]213"; m = SmilesToMol(smi, false, false); TEST_ASSERT(m); MolOps::assignStereochemistry(*m, true, true); TEST_ASSERT(m->getAtomWithIdx(3)->hasProp(common_properties::_CIPCode)); m->getAtomWithIdx(3)->getProp(common_properties::_CIPCode, code); TEST_ASSERT(code == "R"); delete m; BOOST_LOG(rdInfoLog) << "Finished" << std::endl; } void testSFIssue1894348() { BOOST_LOG(rdInfoLog) << "-----------------------\n Testing SFIssue1894348 " "(impact of removeHs on bond stereo atoms)" << std::endl; RWMol *m, *m2; std::string smi; smi = "Cl/C([H])=C/Cl"; m = SmilesToMol(smi, false, false); TEST_ASSERT(m); MolOps::sanitizeMol(*m); MolOps::assignStereochemistry(*m); TEST_ASSERT(m->getBondWithIdx(2)->getStereoAtoms().size() == 2); TEST_ASSERT(m->getBondWithIdx(2)->getStereoAtoms()[0] == 0); TEST_ASSERT(m->getBondWithIdx(2)->getStereoAtoms()[1] == 4); // we remove an H attached to a stereo bond m2 = static_cast(MolOps::removeHs(static_cast(*m))); TEST_ASSERT(m->getBondWithIdx(2)->getStereoAtoms().size() == 2); TEST_ASSERT(m->getBondWithIdx(2)->getStereoAtoms()[0] == 0); TEST_ASSERT(m->getBondWithIdx(2)->getStereoAtoms()[1] == 4); // at first the stereoatoms are gone: TEST_ASSERT(m2->getBondWithIdx(2)->getStereoAtoms().size() == 0); // but they can be re-perceived: MolOps::assignStereochemistry(*m2, true, true); TEST_ASSERT(m2->getBondWithIdx(1)->getStereoAtoms().size() == 2); TEST_ASSERT(m2->getBondWithIdx(1)->getStereoAtoms()[0] == 0); TEST_ASSERT(m2->getBondWithIdx(1)->getStereoAtoms()[1] == 3); delete m; delete m2; smi = "Cl/C([H])=C/Cl"; m = SmilesToMol(smi, false, false); TEST_ASSERT(m); MolOps::sanitizeMol(*m); TEST_ASSERT(m->getBondWithIdx(2)->getStereoAtoms().size() == 0); m2 = static_cast(MolOps::removeHs(static_cast(*m))); // if we don't assign stereocodes in the original we shouldn't have them here: TEST_ASSERT(m2->getBondWithIdx(1)->getStereoAtoms().size() == 0); delete m; delete m2; BOOST_LOG(rdInfoLog) << "Finished" << std::endl; } void testAromaticityEdges() { BOOST_LOG(rdInfoLog) << "-----------------------\n Testing some aromaticity edge cases " << std::endl; RWMol *m; std::string smi; // ------ // this was sf.net bug 1934360 smi = "C1=C=NC=N1"; m = SmilesToMol(smi); TEST_ASSERT(m); TEST_ASSERT(!m->getAtomWithIdx(0)->getIsAromatic()); TEST_ASSERT(!m->getBondWithIdx(0)->getIsAromatic()); delete m; smi = "C1=CNC=N1"; m = SmilesToMol(smi); TEST_ASSERT(m); TEST_ASSERT(m->getAtomWithIdx(0)->getIsAromatic()); TEST_ASSERT(m->getBondWithIdx(0)->getIsAromatic()); delete m; smi = "C=[C+]1=CNC=N1"; m = SmilesToMol(smi); TEST_ASSERT(m); TEST_ASSERT(!m->getAtomWithIdx(1)->getIsAromatic()); TEST_ASSERT(!m->getBondWithIdx(1)->getIsAromatic()); delete m; // ------ // this was sf.net bug 1940646 smi = "C1#CC=C1"; m = SmilesToMol(smi); TEST_ASSERT(m); TEST_ASSERT(!m->getAtomWithIdx(0)->getIsAromatic()); TEST_ASSERT(!m->getBondWithIdx(0)->getIsAromatic()); delete m; smi = "C1#CC=CC=C1"; m = SmilesToMol(smi); TEST_ASSERT(m); TEST_ASSERT(m->getAtomWithIdx(0)->getIsAromatic()); TEST_ASSERT(m->getBondWithIdx(0)->getIsAromatic()); delete m; // ------ // this was sf.net bug 2091839 smi = "c1cccc[c]1"; m = SmilesToMol(smi); TEST_ASSERT(m); TEST_ASSERT(m->getAtomWithIdx(0)->getIsAromatic()); TEST_ASSERT(m->getBondWithIdx(0)->getIsAromatic()); delete m; smi = "C1=CC=CC=[C]1"; m = SmilesToMol(smi); TEST_ASSERT(m); TEST_ASSERT(m->getAtomWithIdx(0)->getIsAromatic()); TEST_ASSERT(m->getBondWithIdx(0)->getIsAromatic()); delete m; smi = "c1cccc[n+]1"; // disqualified because N has a radical m = SmilesToMol(smi); TEST_ASSERT(m); TEST_ASSERT(!m->getAtomWithIdx(0)->getIsAromatic()); TEST_ASSERT(!m->getBondWithIdx(0)->getIsAromatic()); delete m; smi = "[N]1C=CC=C1"; // disqualified because N has a radical m = SmilesToMol(smi); TEST_ASSERT(m); TEST_ASSERT(!m->getAtomWithIdx(0)->getIsAromatic()); TEST_ASSERT(m->getAtomWithIdx(0)->getNumRadicalElectrons() == 1); TEST_ASSERT(!m->getBondWithIdx(0)->getIsAromatic()); delete m; smi = "[n]1ccccc1"; m = SmilesToMol(smi); TEST_ASSERT(m); TEST_ASSERT(m->getAtomWithIdx(0)->getIsAromatic()); TEST_ASSERT(m->getAtomWithIdx(0)->getNumRadicalElectrons() == 0); TEST_ASSERT(m->getBondWithIdx(0)->getIsAromatic()); delete m; smi = "[H]n1cccc1"; m = SmilesToMol(smi, 0, 0); TEST_ASSERT(m); MolOps::sanitizeMol(*m); TEST_ASSERT(m->getAtomWithIdx(1)->getIsAromatic()); TEST_ASSERT(m->getAtomWithIdx(1)->getNumRadicalElectrons() == 0); TEST_ASSERT(!m->getAtomWithIdx(0)->getIsAromatic()); TEST_ASSERT(m->getAtomWithIdx(0)->getNumRadicalElectrons() == 0); delete m; smi = "[H]"; m = SmilesToMol(smi, 0, 0); TEST_ASSERT(m); MolOps::sanitizeMol(*m); TEST_ASSERT(m->getAtomWithIdx(0)->getNumRadicalElectrons() == 1); delete m; // ------ // this was sf.net bug 2787221. smi = "O=C1C(=O)C=C1"; m = SmilesToMol(smi); TEST_ASSERT(m); TEST_ASSERT(m->getAtomWithIdx(1)->getIsAromatic()); TEST_ASSERT(m->getBondBetweenAtoms(1, 2)->getIsAromatic()); delete m; BOOST_LOG(rdInfoLog) << "Finished" << std::endl; } void testSFIssue1942657() { BOOST_LOG(rdInfoLog) << "-----------------------\n Testing sf.net issue 1942657 " << std::endl; RWMol *m; std::string smi; smi = "C[C](C)(C)(C)C"; try { m = SmilesToMol(smi); } catch (MolSanitizeException &) { m = nullptr; } TEST_ASSERT(!m); smi = "C[CH](C)(C)C"; try { m = SmilesToMol(smi); } catch (MolSanitizeException &) { m = nullptr; } TEST_ASSERT(!m); smi = "C[C](=C)(C)C"; try { m = SmilesToMol(smi); } catch (MolSanitizeException &) { m = nullptr; } TEST_ASSERT(!m); smi = "C[Si](=C)(=C)=C"; try { m = SmilesToMol(smi); } catch (MolSanitizeException &) { m = nullptr; } TEST_ASSERT(!m); BOOST_LOG(rdInfoLog) << "Finished" << std::endl; } void testSFIssue1968608() { BOOST_LOG(rdInfoLog) << "-----------------------\n Testing sf.net issue 198608 " << std::endl; RWMol *m; std::string smi; smi = "C1CC1CC1CC1"; m = SmilesToMol(smi); TEST_ASSERT(m->getRingInfo()->minAtomRingSize(0) == 3); TEST_ASSERT(m->getRingInfo()->minAtomRingSize(3) == 0); TEST_ASSERT(m->getRingInfo()->minBondRingSize(0) == 3); TEST_ASSERT(m->getRingInfo()->minBondRingSize(3) == 0); delete m; BOOST_LOG(rdInfoLog) << "Finished" << std::endl; } void testHybridization() { BOOST_LOG(rdInfoLog) << "-----------------------\n Testing hybridization assignment " << std::endl; { RWMol *m; std::string smi = "CCC"; m = SmilesToMol(smi); TEST_ASSERT(m); TEST_ASSERT(m->getAtomWithIdx(1)->getHybridization() == Atom::SP3); TEST_ASSERT(m->getAtomWithIdx(0)->getHybridization() == Atom::SP3); delete m; } { RWMol *m; std::string smi = "CNC"; m = SmilesToMol(smi); TEST_ASSERT(m); TEST_ASSERT(m->getAtomWithIdx(1)->getHybridization() == Atom::SP3); TEST_ASSERT(m->getAtomWithIdx(0)->getHybridization() == Atom::SP3); delete m; } { RWMol *m; std::string smi = "COC"; m = SmilesToMol(smi); TEST_ASSERT(m); TEST_ASSERT(m->getAtomWithIdx(1)->getHybridization() == Atom::SP3); TEST_ASSERT(m->getAtomWithIdx(0)->getHybridization() == Atom::SP3); delete m; } { RWMol *m; std::string smi = "C[C-2]C"; m = SmilesToMol(smi); TEST_ASSERT(m); TEST_ASSERT(m->getAtomWithIdx(1)->getHybridization() == Atom::SP3); TEST_ASSERT(m->getAtomWithIdx(0)->getHybridization() == Atom::SP3); delete m; } { RWMol *m; std::string smi = "C[CH-]C"; m = SmilesToMol(smi); TEST_ASSERT(m); TEST_ASSERT(m->getAtomWithIdx(1)->getHybridization() == Atom::SP3); TEST_ASSERT(m->getAtomWithIdx(0)->getHybridization() == Atom::SP3); delete m; } { RWMol *m; std::string smi = "C[CH]C"; m = SmilesToMol(smi); TEST_ASSERT(m); TEST_ASSERT(m->getAtomWithIdx(1)->getHybridization() == Atom::SP3); TEST_ASSERT(m->getAtomWithIdx(0)->getHybridization() == Atom::SP3); delete m; } { RWMol *m; std::string smi = "C[C]C"; m = SmilesToMol(smi); TEST_ASSERT(m); TEST_ASSERT(m->getAtomWithIdx(1)->getHybridization() == Atom::SP3); TEST_ASSERT(m->getAtomWithIdx(0)->getHybridization() == Atom::SP3); delete m; } { RWMol *m; std::string smi = "C[C-]C"; m = SmilesToMol(smi); TEST_ASSERT(m); TEST_ASSERT(m->getAtomWithIdx(1)->getHybridization() == Atom::SP3); TEST_ASSERT(m->getAtomWithIdx(0)->getHybridization() == Atom::SP3); delete m; } { RWMol *m; std::string smi = "C[CH+]C"; m = SmilesToMol(smi); TEST_ASSERT(m); TEST_ASSERT(m->getAtomWithIdx(1)->getHybridization() == Atom::SP2); TEST_ASSERT(m->getAtomWithIdx(0)->getHybridization() == Atom::SP3); delete m; } { RWMol *m; std::string smi = "CC=C"; m = SmilesToMol(smi); TEST_ASSERT(m); TEST_ASSERT(m->getAtomWithIdx(1)->getHybridization() == Atom::SP2); TEST_ASSERT(m->getAtomWithIdx(0)->getHybridization() == Atom::SP3); delete m; } { RWMol *m; std::string smi = "CN=C"; m = SmilesToMol(smi); TEST_ASSERT(m); TEST_ASSERT(m->getAtomWithIdx(1)->getHybridization() == Atom::SP2); TEST_ASSERT(m->getAtomWithIdx(0)->getHybridization() == Atom::SP3); delete m; } { RWMol *m; std::string smi = "C[C-]=C"; m = SmilesToMol(smi); TEST_ASSERT(m); TEST_ASSERT(m->getAtomWithIdx(1)->getHybridization() == Atom::SP2); TEST_ASSERT(m->getAtomWithIdx(0)->getHybridization() == Atom::SP3); delete m; } { RWMol *m; std::string smi = "C[C]=C"; m = SmilesToMol(smi); TEST_ASSERT(m); TEST_ASSERT(m->getAtomWithIdx(1)->getHybridization() == Atom::SP2); TEST_ASSERT(m->getAtomWithIdx(0)->getHybridization() == Atom::SP3); delete m; } { RWMol *m; std::string smi = "C[N+]=C"; m = SmilesToMol(smi); TEST_ASSERT(m); TEST_ASSERT(m->getAtomWithIdx(1)->getHybridization() == Atom::SP2); TEST_ASSERT(m->getAtomWithIdx(0)->getHybridization() == Atom::SP3); delete m; } { RWMol *m; std::string smi = "C#C"; m = SmilesToMol(smi); TEST_ASSERT(m); TEST_ASSERT(m->getAtomWithIdx(1)->getHybridization() == Atom::SP); delete m; } { RWMol *m; std::string smi = "C#[C-]"; m = SmilesToMol(smi); TEST_ASSERT(m); TEST_ASSERT(m->getAtomWithIdx(1)->getHybridization() == Atom::SP); delete m; } { RWMol *m; std::string smi = "C#[C]"; m = SmilesToMol(smi); TEST_ASSERT(m); TEST_ASSERT(m->getAtomWithIdx(1)->getHybridization() == Atom::SP); delete m; } { RWMol *m; std::string smi = "C[O]"; m = SmilesToMol(smi); TEST_ASSERT(m); TEST_ASSERT(m->getAtomWithIdx(1)->getHybridization() == Atom::SP3); delete m; } { RWMol *m; std::string smi = "C[N-]"; m = SmilesToMol(smi); TEST_ASSERT(m); TEST_ASSERT(m->getAtomWithIdx(1)->getHybridization() == Atom::SP3); delete m; } BOOST_LOG(rdInfoLog) << "Finished" << std::endl; } void testSFNetIssue2196817() { BOOST_LOG(rdInfoLog) << "-----------------------\n Testing sf.net issue " "2196817: handling of aromatic dummies" << std::endl; { std::string pathName = getenv("RDBASE"); pathName += "/Code/GraphMol/test_data/"; RWMol *m = MolFileToMol(pathName + "dummyArom.mol"); TEST_ASSERT(m); TEST_ASSERT(m->getAtomWithIdx(0)->getAtomicNum() == 0); TEST_ASSERT(m->getAtomWithIdx(0)->getIsAromatic() == true); MolOps::Kekulize(*m); TEST_ASSERT(m->getBondBetweenAtoms(0, 1)->getBondType() == Bond::SINGLE); TEST_ASSERT(m->getBondBetweenAtoms(0, 4)->getBondType() == Bond::SINGLE); delete m; } { std::string smi = "*1cncc1"; RWMol *m = SmilesToMol(smi); TEST_ASSERT(m); TEST_ASSERT(m->getAtomWithIdx(0)->getAtomicNum() == 0); TEST_ASSERT(m->getBondWithIdx(0)->getIsAromatic() == true); delete m; } { std::string smi = "*1C=NC=C1"; RWMol *m = SmilesToMol(smi); TEST_ASSERT(m); TEST_ASSERT(m->getAtomWithIdx(0)->getAtomicNum() == 0); TEST_ASSERT(m->getBondWithIdx(0)->getIsAromatic() == true); delete m; } { // case where all must be ignored: std::string smi = "c1*ccc1-c1*ccc1-c1*ccc1"; RWMol *m = SmilesToMol(smi); TEST_ASSERT(m); delete m; } { std::string smi = "c1*[nH]*c1"; RWMol *m = SmilesToMol(smi); TEST_ASSERT(m); smi = MolToSmiles(*m); TEST_ASSERT(smi == "*1cc*[nH]1"); delete m; smi = "c1***c1"; m = SmilesToMol(smi); TEST_ASSERT(m); smi = MolToSmiles(*m); TEST_ASSERT(smi == "*1:*cc*:1"); delete m; smi = "c:1:*:*:*:*1"; m = SmilesToMol(smi); TEST_ASSERT(m); smi = MolToSmiles(*m); TEST_ASSERT(smi == "*1:*:*c*:1"); delete m; // we don't kekulize rings that are all dummies, this was github #1478 smi = "*:1:*:*:*:*:1"; m = SmilesToMol(smi); TEST_ASSERT(m); smi = MolToSmiles(*m); TEST_ASSERT(smi == "*1:*:*:*:*:1"); delete m; } { std::string smi = "c1*[nH]cc1-c1*[nH]cc1-c1*ccc1"; RWMol *m = SmilesToMol(smi); TEST_ASSERT(m); delete m; smi = "c1*[nH]cc1-c1*ccc1-c1*[nH]cc1"; m = SmilesToMol(smi); TEST_ASSERT(m); delete m; smi = "c1*ccc1-c1*[nH]cc1-c1*[nH1]cc1"; m = SmilesToMol(smi); TEST_ASSERT(m); delete m; } { std::string smi = "c1*[nH]cc1-c1*[nH]cc1-c1*[nH]cc1"; RWMol *m = SmilesToMol(smi); TEST_ASSERT(m); delete m; } { std::string smi = "c1ccc(C2CC(n4cc*c4=C2))cc1"; RWMol *m = SmilesToMol(smi); TEST_ASSERT(m); TEST_ASSERT(m->getBondBetweenAtoms(0, 1)->getIsAromatic()); TEST_ASSERT(m->getBondBetweenAtoms(0, 14)->getIsAromatic()); TEST_ASSERT(m->getBondBetweenAtoms(0, 1)->getBondType() == Bond::AROMATIC); TEST_ASSERT(m->getBondBetweenAtoms(0, 14)->getBondType() == Bond::AROMATIC); MolOps::Kekulize(*m); TEST_ASSERT(!m->getBondBetweenAtoms(0, 1)->getIsAromatic()); TEST_ASSERT(!m->getBondBetweenAtoms(0, 14)->getIsAromatic()); TEST_ASSERT(m->getBondBetweenAtoms(0, 1)->getBondType() == Bond::DOUBLE || m->getBondBetweenAtoms(0, 14)->getBondType() == Bond::DOUBLE); MolOps::setAromaticity(*m); TEST_ASSERT(m->getBondBetweenAtoms(0, 1)->getIsAromatic()); TEST_ASSERT(m->getBondBetweenAtoms(0, 14)->getIsAromatic()); TEST_ASSERT(m->getBondBetweenAtoms(0, 1)->getBondType() == Bond::AROMATIC); TEST_ASSERT(m->getBondBetweenAtoms(0, 14)->getBondType() == Bond::AROMATIC); delete m; } { for (auto smi : {"c12ccccc1**CC2", "c12ccccc1C**C2", "*12ccccc1CCCC2", "*12ccccc1***C2"}) { std::unique_ptr m(SmilesToMol(smi)); TEST_ASSERT(m); for (size_t i = 0; i < 6; ++i) { size_t j = i < 5 ? i + 1 : 0; TEST_ASSERT(m->getBondBetweenAtoms(i, j)->getIsAromatic()); TEST_ASSERT(m->getBondBetweenAtoms(i, j)->getBondType() == Bond::AROMATIC); } for (size_t i = 5; i < 10; ++i) { size_t j = i < 9 ? i + 1 : 0; TEST_ASSERT(!m->getBondBetweenAtoms(i, j)->getIsAromatic()); TEST_ASSERT(m->getBondBetweenAtoms(i, j)->getBondType() == Bond::SINGLE); } MolOps::Kekulize(*m); for (size_t i = 0; i < 6; ++i) { size_t j = i < 5 ? i + 1 : 0; TEST_ASSERT(!m->getBondBetweenAtoms(i, j)->getIsAromatic()); TEST_ASSERT( m->getBondBetweenAtoms(i, j)->getBondType() == Bond::SINGLE || m->getBondBetweenAtoms(i, j)->getBondType() == Bond::DOUBLE); } for (size_t i = 5; i < 10; ++i) { size_t j = i < 9 ? i + 1 : 0; TEST_ASSERT(!m->getBondBetweenAtoms(i, j)->getIsAromatic()); TEST_ASSERT(m->getBondBetweenAtoms(i, j)->getBondType() == Bond::SINGLE); } } } { for (auto smi : {"c12ccccc1****2", "c12ccccc1***2", "C12=CC=CC=C1****2", "c1ccccc1N1****1", "c1ccccc1C1****1", "c1ccccc1*1****1", "c1ccccc1*1*=**=*1"}) { std::unique_ptr m(SmilesToMol(smi)); TEST_ASSERT(m); for (unsigned int i = 6; i < m->getNumAtoms(); ++i) { TEST_ASSERT(!m->getAtomWithIdx(i)->getIsAromatic()); } } } { for (auto smi : {"C1CC*2ccccc21", "C1C**2ccccc21"}) { std::unique_ptr m(SmilesToMol(smi)); TEST_ASSERT(m); for (unsigned int i = 0; i < 3; ++i) { TEST_ASSERT(!m->getAtomWithIdx(i)->getIsAromatic()); } for (unsigned int i = 3; i < m->getNumAtoms(); ++i) { TEST_ASSERT(m->getAtomWithIdx(i)->getIsAromatic()); } } } { auto m = "*12ccccc1CCC2"_smiles; for (unsigned int i = 0; i < 6; ++i) { TEST_ASSERT(m->getAtomWithIdx(i)->getIsAromatic()); } for (unsigned int i = 6; i < m->getNumAtoms(); ++i) { TEST_ASSERT(!m->getAtomWithIdx(i)->getIsAromatic()); } } { for (auto smi : {"N1****1", "C1=C*2C=CC=C*2C=C1", "N1*C=CC=C1", "C1=CC2=CC=C3C=CC4=CC=C5C=CN1*1*2*3*4*51"}) { std::unique_ptr m(SmilesToMol(smi)); TEST_ASSERT(m); for (const auto b : m->bonds()) { TEST_ASSERT(!b->getIsAromatic()); } } } { ROMOL_SPTR m = "C1=CC2=CC=C3C=CC4=CC=C5C=CN1*1*2*3*4N51"_smiles; for (const auto a : m->atoms()) { TEST_ASSERT(a->getIsAromatic()); } unsigned int nNonAromaticBonds = 0; for (const auto b : m->bonds()) { if (!b->getIsAromatic()) { ++nNonAromaticBonds; } } TEST_ASSERT(nNonAromaticBonds == 5); } { for (auto smi : {"*1C=CC=C1", "N1*=**=*1", "C1=CC2=CC=C3C=CC4=CC=C5C=CN1N1N2N3N4N51", "C1=CC2=CC=C3C=CC4=CC=C5C=CN1*1N2*3N4N51"}) { std::unique_ptr m(SmilesToMol(smi)); TEST_ASSERT(m); for (const auto b : m->bonds()) { TEST_ASSERT(b->getIsAromatic()); } } } BOOST_LOG(rdInfoLog) << "Finished" << std::endl; } void testSFNetIssue2208994() { BOOST_LOG(rdInfoLog) << "-----------------------\n Testing sf.net issue " "2208994 : kekulization error" << std::endl; { std::string smi = "Cn1ccc(=O)n1C"; RWMol *m = SmilesToMol(smi); TEST_ASSERT(m); TEST_ASSERT(m->getAtomWithIdx(1)->getIsAromatic() == true); TEST_ASSERT(m->getBondWithIdx(1)->getIsAromatic() == true); delete m; } { std::string smi = "c:1:c:c:c:c:c1"; RWMol *m = SmilesToMol(smi); TEST_ASSERT(m); TEST_ASSERT(m->getAtomWithIdx(1)->getIsAromatic() == true); TEST_ASSERT(m->getBondWithIdx(1)->getIsAromatic() == true); delete m; } { std::string smi = "c1:c:c:c:c:c:1"; RWMol *m = SmilesToMol(smi); TEST_ASSERT(m); TEST_ASSERT(m->getAtomWithIdx(1)->getIsAromatic() == true); TEST_ASSERT(m->getBondWithIdx(1)->getIsAromatic() == true); delete m; } BOOST_LOG(rdInfoLog) << "Finished" << std::endl; } void testSFNetIssue2313979() { BOOST_LOG(rdInfoLog) << "-----------------------\n Testing sf.net issue " "2313979: aromaticity assignment hangs " << std::endl; { std::string pathName = getenv("RDBASE"); pathName += "/Code/GraphMol/test_data/"; SDMolSupplier suppl(pathName + "Issue2313979.sdf", false); while (!suppl.atEnd()) { ROMol *m = suppl.next(); TEST_ASSERT(m); std::string nm; m->getProp(common_properties::_Name, nm); BOOST_LOG(rdInfoLog) << " Doing molecule: " << nm << std::endl; BOOST_LOG(rdInfoLog) << " This should finish in a few seconds. >>>" << std::endl; MolOps::sanitizeMol(*(RWMol *)m); delete m; BOOST_LOG(rdInfoLog) << " <<< Done." << std::endl; } } BOOST_LOG(rdInfoLog) << "Finished" << std::endl; } void testSFNetIssue2316677() { BOOST_LOG(rdInfoLog) << "-----------------------\n Testing sf.net issue " "2316677 : canonicalization error" << std::endl; { std::string pathName = getenv("RDBASE"); pathName += "/Code/GraphMol/test_data/"; RWMol *m = MolFileToMol(pathName + "Issue2316677.mol"); TEST_ASSERT(m); std::string smi = MolToSmiles(*m, true); std::cerr << "smi: " << smi << std::endl; TEST_ASSERT(smi == "Cc1ccc(S(=O)(=O)/N=C2\\CC(=N\\C(C)(C)C)/C2=N\\C(C)(C)C)cc1"); delete m; } BOOST_LOG(rdInfoLog) << "Finished" << std::endl; } void testSanitizeNonringAromatics() { BOOST_LOG(rdInfoLog) << "-----------------------\n Testing sf.net issue " "2830244: make sure that non-ring aromatic atoms " "generate errors:" << std::endl; { std::string smi = "c-C"; RWMol *m = SmilesToMol(smi, 0, false); bool ok = false; try { MolOps::Kekulize(*m); } catch (MolSanitizeException &) { ok = true; } TEST_ASSERT(ok); delete m; } { std::string smi = "c-C"; RWMol *m = SmilesToMol(smi, 0, false); bool ok = false; unsigned int opThatFailed; try { MolOps::sanitizeMol(*m, opThatFailed); } catch (MolSanitizeException &) { ok = true; } TEST_ASSERT(ok); TEST_ASSERT(opThatFailed == MolOps::SANITIZE_KEKULIZE); delete m; } BOOST_LOG(rdInfoLog) << "Finished" << std::endl; } void testSFNetIssue2951221() { BOOST_LOG(rdInfoLog) << "-----------------------\n Testing sf.net issue " "2951221 : hydrogens added with bad coordinates" << std::endl; { std::string pathName = getenv("RDBASE"); pathName += "/Code/GraphMol/test_data/"; ROMol *m = MolFileToMol(pathName + "Issue2951221.1.mol"); TEST_ASSERT(m); TEST_ASSERT(m->getConformer().is3D()); ROMol *m2 = MolOps::addHs(*m, false, true); TEST_ASSERT(m2); delete m; TEST_ASSERT(m2->getNumAtoms(false) == 12); RDGeom::Point3D coords[4]; coords[0] = m2->getConformer().getAtomPos(2); coords[1] = m2->getConformer().getAtomPos(0); coords[2] = m2->getConformer().getAtomPos(1); coords[3] = m2->getConformer().getAtomPos(9); double dot = (coords[3] - coords[0]) .dotProduct( (coords[1] - coords[0]).crossProduct(coords[2] - coords[0])); TEST_ASSERT(dot > 1.0); delete m2; } { std::string pathName = getenv("RDBASE"); pathName += "/Code/GraphMol/test_data/"; ROMol *m = MolFileToMol(pathName + "Issue2951221.2.mol"); TEST_ASSERT(m); ROMol *m2 = MolOps::addHs(*m, false, true); TEST_ASSERT(m2); delete m; TEST_ASSERT(m2->getNumAtoms(false) == 5); MolOps::assignChiralTypesFrom3D(*m2); MolOps::assignStereochemistry(*m2, true, true); TEST_ASSERT(m2->getAtomWithIdx(1)->hasProp(common_properties::_CIPCode)); std::string cip; m2->getAtomWithIdx(1)->getProp(common_properties::_CIPCode, cip); TEST_ASSERT(cip == "S"); delete m2; } { std::string pathName = getenv("RDBASE"); pathName += "/Code/GraphMol/test_data/"; ROMol *m = MolFileToMol(pathName + "Issue2951221.3.mol"); TEST_ASSERT(m); ROMol *m2 = MolOps::addHs(*m, false, true); TEST_ASSERT(m2); delete m; TEST_ASSERT(m2->getNumAtoms(false) == 5); MolOps::assignChiralTypesFrom3D(*m2); MolOps::assignStereochemistry(*m2, true, true); TEST_ASSERT(m2->getAtomWithIdx(1)->hasProp(common_properties::_CIPCode)); std::string cip; m2->getAtomWithIdx(1)->getProp(common_properties::_CIPCode, cip); TEST_ASSERT(cip == "R"); delete m2; } BOOST_LOG(rdInfoLog) << "Finished" << std::endl; } void testSFNetIssue2952255() { BOOST_LOG(rdInfoLog) << "-----------------------\n Testing sf.net issue " "2952255 : bad assignment of radicals to early " "elements" << std::endl; { std::string smi = "[C](C)(C)C"; RWMol *m = SmilesToMol(smi); TEST_ASSERT(m); TEST_ASSERT(m->getAtomWithIdx(0)->getNumRadicalElectrons() == 1); delete m; } { std::string smi = "[C](C)C"; RWMol *m = SmilesToMol(smi); TEST_ASSERT(m); TEST_ASSERT(m->getAtomWithIdx(0)->getNumRadicalElectrons() == 2); delete m; } { std::string smi = "[CH](C)C"; RWMol *m = SmilesToMol(smi); TEST_ASSERT(m); TEST_ASSERT(m->getAtomWithIdx(0)->getNumRadicalElectrons() == 1); delete m; } { std::string smi = "[CH+](C)C"; RWMol *m = SmilesToMol(smi); TEST_ASSERT(m); TEST_ASSERT(m->getAtomWithIdx(0)->getNumRadicalElectrons() == 0); delete m; } { std::string smi = "[C-](C)C"; RWMol *m = SmilesToMol(smi); TEST_ASSERT(m); TEST_ASSERT(m->getAtomWithIdx(0)->getNumRadicalElectrons() == 1); delete m; } { std::string smi = "[C+](C)(C)(C)C"; RWMol *m = SmilesToMol(smi); TEST_ASSERT(m); TEST_ASSERT(m->getAtomWithIdx(0)->getNumRadicalElectrons() == 1); delete m; } { std::string smi = "C(C)(C)(C)C"; RWMol *m = SmilesToMol(smi); TEST_ASSERT(m); TEST_ASSERT(m->getAtomWithIdx(0)->getNumRadicalElectrons() == 0); delete m; } { std::string smi = "[N](C)C"; RWMol *m = SmilesToMol(smi); TEST_ASSERT(m); TEST_ASSERT(m->getAtomWithIdx(0)->getNumRadicalElectrons() == 1); delete m; } { std::string smi = "[N+](C)(C)C"; RWMol *m = SmilesToMol(smi); TEST_ASSERT(m); TEST_ASSERT(m->getAtomWithIdx(0)->getNumRadicalElectrons() == 1); delete m; } { std::string smi = "[Cl]"; RWMol *m = SmilesToMol(smi); TEST_ASSERT(m); TEST_ASSERT(m->getAtomWithIdx(0)->getNumRadicalElectrons() == 1); delete m; } { std::string smi = "[Cl-]"; RWMol *m = SmilesToMol(smi); TEST_ASSERT(m); TEST_ASSERT(m->getAtomWithIdx(0)->getNumRadicalElectrons() == 0); delete m; } { std::string smi = "[Cl]C"; RWMol *m = SmilesToMol(smi); TEST_ASSERT(m); TEST_ASSERT(m->getAtomWithIdx(0)->getNumRadicalElectrons() == 0); delete m; } { std::string smi = "[Na]"; RWMol *m = SmilesToMol(smi); TEST_ASSERT(m); TEST_ASSERT(m->getAtomWithIdx(0)->getNumRadicalElectrons() == 1); delete m; } { std::string smi = "[Na+]"; RWMol *m = SmilesToMol(smi); TEST_ASSERT(m); TEST_ASSERT(m->getAtomWithIdx(0)->getNumRadicalElectrons() == 0); delete m; } { std::string smi = "[Na]C"; RWMol *m = SmilesToMol(smi); TEST_ASSERT(m); TEST_ASSERT(m->getAtomWithIdx(0)->getNumRadicalElectrons() == 0); delete m; } { std::string smi = "[Mg+]C"; RWMol *m = SmilesToMol(smi); TEST_ASSERT(m); TEST_ASSERT(m->getAtomWithIdx(0)->getNumRadicalElectrons() == 0); delete m; } { std::string smi = "[Mg]C"; RWMol *m = SmilesToMol(smi); TEST_ASSERT(m); TEST_ASSERT(m->getAtomWithIdx(0)->getNumRadicalElectrons() == 1); delete m; } { std::string smi = "[Mg+]"; RWMol *m = SmilesToMol(smi); TEST_ASSERT(m); TEST_ASSERT(m->getAtomWithIdx(0)->getNumRadicalElectrons() == 1); delete m; } { std::string smi = "[Mg+2]"; RWMol *m = SmilesToMol(smi); TEST_ASSERT(m); TEST_ASSERT(m->getAtomWithIdx(0)->getNumRadicalElectrons() == 0); delete m; } BOOST_LOG(rdInfoLog) << "Finished" << std::endl; } void testSFNetIssue3185548() { BOOST_LOG(rdInfoLog) << "-----------------------\n Testing sf.net issue " "3185548 : problems with SSSR code" << std::endl; { std::string pathName = getenv("RDBASE"); pathName += "/Code/GraphMol/test_data/"; BOOST_LOG(rdInfoLog) << " Starting file read 1" << std::endl; RWMol *m = MolFileToMol(pathName + "Issue3185548.mol"); BOOST_LOG(rdInfoLog) << " finished" << std::endl; TEST_ASSERT(m); delete m; } { std::string pathName = getenv("RDBASE"); pathName += "/Code/GraphMol/test_data/"; BOOST_LOG(rdInfoLog) << " Starting file read 2" << std::endl; RWMol *m = MolFileToMol(pathName + "Issue3185548.2.mol"); BOOST_LOG(rdInfoLog) << " finished" << std::endl; TEST_ASSERT(m); m->getRingInfo()->reset(); unsigned int nsssr; VECT_INT_VECT sssrs; nsssr = MolOps::findSSSR(*m, sssrs); TEST_ASSERT(nsssr = 48); nsssr = MolOps::symmetrizeSSSR(*m, sssrs); TEST_ASSERT(nsssr = 56); delete m; } BOOST_LOG(rdInfoLog) << "Finished" << std::endl; } void testSFNetIssue3349243() { BOOST_LOG(rdInfoLog) << "-------------------------------------" << std::endl; BOOST_LOG(rdInfoLog) << "Testing Issue 3349243" << std::endl; { std::string smi = "c1cccc[n+]1"; RWMol *m = SmilesToMol(smi); TEST_ASSERT(m); MolOps::Kekulize(*m); // just finishing is good TEST_ASSERT(m->getBondWithIdx(0)->getBondType() != Bond::AROMATIC); delete m; } BOOST_LOG(rdInfoLog) << "\tdone" << std::endl; } void testFastFindRings() { BOOST_LOG(rdInfoLog) << "-------------------------------------" << std::endl; BOOST_LOG(rdInfoLog) << "Testing fast find rings" << std::endl; { std::string smi = "CCC"; RWMol *m = SmilesToMol(smi, 0, 0); TEST_ASSERT(m); MolOps::fastFindRings(*m); TEST_ASSERT(m->getRingInfo()); TEST_ASSERT(m->getRingInfo()->isInitialized()); TEST_ASSERT(m->getRingInfo()->numRings() == 0); delete m; } { std::string smi = "C1CC1"; RWMol *m = SmilesToMol(smi, 0, 0); TEST_ASSERT(m); MolOps::fastFindRings(*m); TEST_ASSERT(m->getRingInfo()); TEST_ASSERT(m->getRingInfo()->isInitialized()); TEST_ASSERT(m->getRingInfo()->numRings() == 1); delete m; } { std::string smi = "CC1CC1"; RWMol *m = SmilesToMol(smi, 0, 0); TEST_ASSERT(m); MolOps::fastFindRings(*m); TEST_ASSERT(m->getRingInfo()); TEST_ASSERT(m->getRingInfo()->isInitialized()); TEST_ASSERT(m->getRingInfo()->numRings() == 1); delete m; } { std::string smi = "C1CC1.C1CC1"; RWMol *m = SmilesToMol(smi, 0, 0); TEST_ASSERT(m); MolOps::fastFindRings(*m); TEST_ASSERT(m->getRingInfo()); TEST_ASSERT(m->getRingInfo()->isInitialized()); TEST_ASSERT(m->getRingInfo()->numRings() == 2); delete m; } { std::string smi = "C1C(C)C1"; RWMol *m = SmilesToMol(smi, 0, 0); TEST_ASSERT(m); MolOps::fastFindRings(*m); TEST_ASSERT(m->getRingInfo()); TEST_ASSERT(m->getRingInfo()->isInitialized()); TEST_ASSERT(m->getRingInfo()->numRings() == 1); delete m; } { std::string smi = "c1c(=O)nc2[nH]cnn2c1O"; RWMol *m = SmilesToMol(smi, 0, 0); TEST_ASSERT(m); MolOps::fastFindRings(*m); TEST_ASSERT(m->getRingInfo()); TEST_ASSERT(m->getRingInfo()->isInitialized()); TEST_ASSERT(m->getRingInfo()->numRings() == 2); delete m; } BOOST_LOG(rdInfoLog) << "\tdone" << std::endl; } void testSFNetIssue3487473() { BOOST_LOG(rdInfoLog) << "-------------------------------------" << std::endl; BOOST_LOG(rdInfoLog) << "Testing Issue 3487473" << std::endl; { std::string smi = "C*C"; RWMol *m = SmilesToMol(smi); TEST_ASSERT(m); TEST_ASSERT(m->getAtomWithIdx(1)->getHybridization() == Atom::UNSPECIFIED); delete m; } { std::string smi = "C*C"; RWMol *m = SmartsToMol(smi); TEST_ASSERT(m); m->updatePropertyCache(false); MolOps::setConjugation(*m); TEST_ASSERT(m->getAtomWithIdx(1)->getHybridization() == Atom::UNSPECIFIED); delete m; } BOOST_LOG(rdInfoLog) << "\tdone" << std::endl; } void testSFNetIssue3480481() { BOOST_LOG(rdInfoLog) << "-------------------------------------" << std::endl; BOOST_LOG(rdInfoLog) << "Testing Issue 3480481" << std::endl; { std::string pathName = getenv("RDBASE"); pathName += "/Code/GraphMol/test_data/"; RWMol *m = MolFileToMol(pathName + "Issue3480481.mol"); TEST_ASSERT(m); TEST_ASSERT(m->getAtomWithIdx(0)->getIsAromatic() == true); TEST_ASSERT(m->getAtomWithIdx(0)->getExplicitValence() == 4); TEST_ASSERT(m->getAtomWithIdx(0)->getImplicitValence() == 0); TEST_ASSERT(m->getAtomWithIdx(0)->getFormalCharge() == -1); delete m; } BOOST_LOG(rdInfoLog) << "\tdone" << std::endl; } void aamatchtest(std::string smi1, std::string smi2, bool shouldMatch, int idx1, int idx2) { RWMol *m1 = SmilesToMol(smi1); RWMol *m2 = SmilesToMol(smi2); TEST_ASSERT(m1); TEST_ASSERT(m2); // std::cerr<<" "<getAtomWithIdx(idx2)->Match(m1->getAtomWithIdx(idx1)) == shouldMatch); delete m1; delete m2; } void testAtomAtomMatch() { BOOST_LOG(rdInfoLog) << "-------------------------------------" << std::endl; BOOST_LOG(rdInfoLog) << "Testing Atom-Atom matching behavior" << std::endl; /* Here's what we're testing: | Molecule | Query | Match | | CCO | CCO | Yes | | CC[O-] | CCO | Yes | | CCO | CC[O-] | No | | CC[O-] | CC[O-] | Yes | | CC[O-] | CC[OH] | Yes | | CCOC | CC[OH] | Yes | | CCOC | CCO | Yes | | CCC | CCC | Yes | | CC[14C] | CCC | Yes | | CCC | CC[14C] | No | | CC[14C] | CC[14C] | Yes | | OCO | C | Yes | | OCO | [CH2] | Yes | | OCO | [CH3] | Yes | | O[CH2]O | C | Yes | | O[CH2]O | [CH2] | Yes | | OCO | [CH] | Yes | This is a large superset of issue 3495370 */ // note that in some cases here we have to be fairly careful about Hs on // the query to make sure that it doesn't have radicals (radical handling // added to fix github #165 aamatchtest("CCO", "O", true, 2, 0); aamatchtest("CC[O-]", "O", true, 2, 0); aamatchtest("CCO", "[OH-]", false, 2, 0); aamatchtest("CC[O-]", "[OH-]", true, 2, 0); aamatchtest("CC[O-]", "[OH2]", true, 2, 0); aamatchtest("CCOC", "[OH2]", true, 2, 0); aamatchtest("CCOC", "O", true, 2, 0); aamatchtest("CCC", "C", true, 2, 0); aamatchtest("CC[14C]", "C", true, 2, 0); aamatchtest("CCC", "[14CH4]", false, 2, 0); aamatchtest("CC[14C]", "[14CH4]", true, 2, 0); aamatchtest("CC[13C]", "[14CH4]", false, 2, 0); aamatchtest("OCO", "C", true, 1, 0); aamatchtest("OCO", "[CH4]", true, 1, 0); aamatchtest("O[CH2]O", "C", true, 1, 0); aamatchtest("O[CH2]O", "[CH4]", true, 1, 0); aamatchtest("OCO", "[CH2]", false, 1, 0); // doesn't match due to radical count aamatchtest("O[CH2]O", "[CH2]", false, 1, 0); // doesn't match due to radical count aamatchtest("O[CH]O", "[CH3]", true, 1, 0); aamatchtest("O[CH]O", "[CH2]", false, 1, 0); // doesn't match due to radical count aamatchtest("CC", "*", false, 1, 0); aamatchtest("C*", "*", true, 1, 0); aamatchtest("C[1*]", "*", true, 1, 0); aamatchtest("C[1*]", "[1*]", true, 1, 0); aamatchtest("C*", "[1*]", true, 1, 0); aamatchtest("C[2*]", "[1*]", false, 1, 0); BOOST_LOG(rdInfoLog) << "\tdone" << std::endl; } void testSFNetIssue3525076() { BOOST_LOG(rdInfoLog) << "-------------------------------------" << std::endl; BOOST_LOG(rdInfoLog) << "Testing Issue 3525076" << std::endl; { std::string pathName = getenv("RDBASE"); pathName += "/Code/GraphMol/test_data/"; RWMol *m = MolFileToMol(pathName + "Issue3525076.sdf"); TEST_ASSERT(m); TEST_ASSERT(m->getBondWithIdx(18)->getIsAromatic() == false); TEST_ASSERT(m->getBondWithIdx(18)->getBondType() == Bond::SINGLE); MolOps::Kekulize(*m); TEST_ASSERT(m->getBondWithIdx(18)->getIsAromatic() == false); TEST_ASSERT(m->getBondWithIdx(18)->getBondType() == Bond::SINGLE); MolOps::sanitizeMol(*m); TEST_ASSERT(m->getBondWithIdx(18)->getIsAromatic() == false); TEST_ASSERT(m->getBondWithIdx(18)->getBondType() == Bond::SINGLE); delete m; } BOOST_LOG(rdInfoLog) << "\tdone" << std::endl; } void testBasicCanon() { BOOST_LOG(rdInfoLog) << "-------------------------------------" << std::endl; BOOST_LOG(rdInfoLog) << "Testing canonicalization basics" << std::endl; // these are all cases that were problematic at one time or another during // the canonicalization rewrite. #if 1 { std::string smi = "FC1C(=C/Cl)\\C1"; RWMol *m = SmilesToMol(smi); TEST_ASSERT(m); TEST_ASSERT(m->getBondBetweenAtoms(2, 3)->getBondType() == Bond::DOUBLE); TEST_ASSERT(m->getBondBetweenAtoms(2, 3)->getStereo() == Bond::STEREOZ); // std::cerr<<"-------------\n"; std::string csmi1 = MolToSmiles(*m, true); // std::cerr< mmap; for (MatchVectType::const_iterator mvit = mv.begin(); mvit != mv.end(); ++mvit) { mmap[mvit->second] = mvit->first; } TEST_ASSERT(m2->getBondBetweenAtoms(mmap[2], mmap[3])->getBondType() == Bond::DOUBLE); TEST_ASSERT(m2->getBondBetweenAtoms(mmap[2], mmap[3])->getStereo() == Bond::STEREOZ); // std::cerr<<"-------------\n"; std::string csmi2 = MolToSmiles(*m2, true); // std::cerr<getBondBetweenAtoms(10, 11)->getBondType() == Bond::DOUBLE); TEST_ASSERT(m->getBondBetweenAtoms(10, 11)->getStereo() == Bond::STEREOZ); TEST_ASSERT(m->getBondBetweenAtoms(12, 21)->getBondType() == Bond::DOUBLE); TEST_ASSERT(m->getBondBetweenAtoms(12, 21)->getStereo() == Bond::STEREOE); TEST_ASSERT(m->getBondBetweenAtoms(13, 14)->getBondType() == Bond::DOUBLE); TEST_ASSERT(m->getBondBetweenAtoms(13, 14)->getStereo() == Bond::STEREONONE); // std::cerr<<"-------------\n"; std::string csmi1 = MolToSmiles(*m, true); RWMol *m2 = SmilesToMol(csmi1); TEST_ASSERT(m2); MatchVectType mv; TEST_ASSERT(SubstructMatch(*m, *m2, mv)); std::map mmap; for (MatchVectType::const_iterator mvit = mv.begin(); mvit != mv.end(); ++mvit) { mmap[mvit->second] = mvit->first; } TEST_ASSERT(m2->getBondBetweenAtoms(mmap[10], mmap[11])->getBondType() == Bond::DOUBLE); TEST_ASSERT(m2->getBondBetweenAtoms(mmap[10], mmap[11])->getStereo() == Bond::STEREOZ); TEST_ASSERT(m2->getBondBetweenAtoms(mmap[12], mmap[21])->getBondType() == Bond::DOUBLE); TEST_ASSERT(m2->getBondBetweenAtoms(mmap[12], mmap[21])->getStereo() == Bond::STEREOE); TEST_ASSERT(m2->getBondBetweenAtoms(mmap[13], mmap[14])->getBondType() == Bond::DOUBLE); TEST_ASSERT(m2->getBondBetweenAtoms(mmap[13], mmap[14])->getStereo() == Bond::STEREONONE); // std::cerr<<"-------------\n"; std::string csmi2 = MolToSmiles(*m2, true); // std::cerr<getBondBetweenAtoms(1, 2)->getBondType() == Bond::DOUBLE); TEST_ASSERT(m->getBondBetweenAtoms(1, 2)->getStereo() == Bond::STEREOZ); TEST_ASSERT(m->getBondBetweenAtoms(7, 8)->getBondType() == Bond::DOUBLE); TEST_ASSERT(m->getBondBetweenAtoms(7, 8)->getStereo() == Bond::STEREOZ); std::string smi = MolToSmiles(*m, true); // std::cerr<<"SMILES: "< mmap; for (MatchVectType::const_iterator mvit = mv.begin(); mvit != mv.end(); ++mvit) { mmap[mvit->second] = mvit->first; } TEST_ASSERT(m2->getBondBetweenAtoms(mmap[1], mmap[2])->getBondType() == Bond::DOUBLE); TEST_ASSERT(m2->getBondBetweenAtoms(mmap[7], mmap[8])->getBondType() == Bond::DOUBLE); TEST_ASSERT(m2->getBondBetweenAtoms(mmap[1], mmap[2])->getStereo() == Bond::STEREOZ); TEST_ASSERT(m2->getBondBetweenAtoms(mmap[7], mmap[8])->getStereo() == Bond::STEREOZ); delete m; delete m2; } { std::string pathName = getenv("RDBASE"); pathName += "/Code/GraphMol/test_data/"; RWMol *m = MolFileToMol(pathName + "zinc4235774.mol"); TEST_ASSERT(m); TEST_ASSERT(m->getBondBetweenAtoms(4, 5)->getBondType() == Bond::DOUBLE); TEST_ASSERT(m->getBondBetweenAtoms(4, 5)->getStereo() == Bond::STEREOZ); TEST_ASSERT(m->getBondBetweenAtoms(14, 15)->getBondType() == Bond::DOUBLE); TEST_ASSERT(m->getBondBetweenAtoms(14, 15)->getStereo() == Bond::STEREOZ); std::string smi = MolToSmiles(*m, true); RWMol *m2 = SmilesToMol(smi); MatchVectType mv; TEST_ASSERT(SubstructMatch(*m, *m2, mv)); std::map mmap; for (MatchVectType::const_iterator mvit = mv.begin(); mvit != mv.end(); ++mvit) { mmap[mvit->second] = mvit->first; } TEST_ASSERT(m2->getBondBetweenAtoms(mmap[4], mmap[5])->getBondType() == Bond::DOUBLE); TEST_ASSERT(m2->getBondBetweenAtoms(mmap[14], mmap[15])->getBondType() == Bond::DOUBLE); TEST_ASSERT(m2->getBondBetweenAtoms(mmap[4], mmap[5])->getStereo() == Bond::STEREOZ); TEST_ASSERT(m2->getBondBetweenAtoms(mmap[14], mmap[15])->getStereo() == Bond::STEREOZ); delete m; delete m2; } #endif { std::string pathName = getenv("RDBASE"); pathName += "/Code/GraphMol/test_data/"; RWMol *m = MolFileToMol(pathName + "zinc3850436piece.mol"); TEST_ASSERT(m); std::string csmi1 = MolToSmiles(*m, true); delete m; m = SmilesToMol(csmi1); // std::cerr<<"-------------\n"; std::string csmi2 = MolToSmiles(*m, true); // std::cerr<getBondBetweenAtoms(1, 2)->getBondType() == Bond::DOUBLE); TEST_ASSERT(m->getBondBetweenAtoms(1, 2)->getStereo() == Bond::STEREOZ); TEST_ASSERT(m->getBondBetweenAtoms(3, 7)->getBondType() == Bond::DOUBLE); TEST_ASSERT(m->getBondBetweenAtoms(3, 7)->getStereo() == Bond::STEREOE); TEST_ASSERT(m->getBondBetweenAtoms(4, 5)->getBondType() == Bond::DOUBLE); TEST_ASSERT(m->getBondBetweenAtoms(4, 5)->getStereo() == Bond::STEREOE); std::string csmi1 = MolToSmiles(*m, true); // std::cerr<<"SMI1: "< mmap; for (MatchVectType::const_iterator mvit = mv.begin(); mvit != mv.end(); ++mvit) { mmap[mvit->second] = mvit->first; } TEST_ASSERT(m2->getBondBetweenAtoms(mmap[1], mmap[2])->getBondType() == Bond::DOUBLE); TEST_ASSERT(m2->getBondBetweenAtoms(mmap[1], mmap[2])->getStereo() == Bond::STEREOZ); TEST_ASSERT(m2->getBondBetweenAtoms(mmap[3], mmap[7])->getBondType() == Bond::DOUBLE); TEST_ASSERT(m2->getBondBetweenAtoms(mmap[3], mmap[7])->getStereo() == Bond::STEREOE); TEST_ASSERT(m2->getBondBetweenAtoms(mmap[4], mmap[5])->getBondType() == Bond::DOUBLE); TEST_ASSERT(m2->getBondBetweenAtoms(mmap[4], mmap[5])->getStereo() == Bond::STEREOE); // std::cerr<<"-------------\n"; std::string csmi2 = MolToSmiles(*m2, true); // std::cerr<getBondBetweenAtoms(21, 13)->getBondType() == Bond::DOUBLE); TEST_ASSERT(m->getBondBetweenAtoms(21, 13)->getStereo() == Bond::STEREOE); TEST_ASSERT(m->getBondBetweenAtoms(5, 12)->getBondType() == Bond::DOUBLE); TEST_ASSERT(m->getBondBetweenAtoms(5, 12)->getStereo() == Bond::STEREOZ); std::string csmi1 = MolToSmiles(*m, true); // std::cerr<<"SMI1: "< mmap; for (MatchVectType::const_iterator mvit = mv.begin(); mvit != mv.end(); ++mvit) { mmap[mvit->second] = mvit->first; } TEST_ASSERT(m2->getBondBetweenAtoms(mmap[21], mmap[13])->getBondType() == Bond::DOUBLE); TEST_ASSERT(m2->getBondBetweenAtoms(mmap[21], mmap[13])->getStereo() == Bond::STEREOE); TEST_ASSERT(m2->getBondBetweenAtoms(mmap[5], mmap[12])->getBondType() == Bond::DOUBLE); TEST_ASSERT(m2->getBondBetweenAtoms(mmap[5], mmap[12])->getStereo() == Bond::STEREOZ); // std::cerr<<"-------------\n"; std::string csmi2 = MolToSmiles(*m2, true); // std::cerr<second] = mvit->first; } TEST_ASSERT(m2->getBondBetweenAtoms(mmap[21], mmap[13])->getBondType() == Bond::DOUBLE); TEST_ASSERT(m2->getBondBetweenAtoms(mmap[21], mmap[13])->getStereo() == Bond::STEREOE); TEST_ASSERT(m2->getBondBetweenAtoms(mmap[5], mmap[12])->getBondType() == Bond::DOUBLE); TEST_ASSERT(m2->getBondBetweenAtoms(mmap[5], mmap[12])->getStereo() == Bond::STEREOZ); // std::cerr<<"-------------\n"; csmi2 = MolToSmiles(*m2, true); // std::cerr<getBondBetweenAtoms(1, 2)->getBondType() == Bond::DOUBLE); TEST_ASSERT(m->getBondBetweenAtoms(1, 2)->getStereo() == Bond::STEREOE); TEST_ASSERT(m->getBondBetweenAtoms(3, 4)->getBondType() == Bond::DOUBLE); TEST_ASSERT(m->getBondBetweenAtoms(3, 4)->getStereo() == Bond::STEREOE); TEST_ASSERT(m->getBondBetweenAtoms(6, 7)->getBondType() == Bond::DOUBLE); TEST_ASSERT(m->getBondBetweenAtoms(6, 7)->getStereo() == Bond::STEREOE); std::string tsmi = MolToSmiles(*m, true, false, 3, false); // std::cerr<<"-------------\n"; // std::cerr<<"T:\n"< mmap; mmap.clear(); for (MatchVectType::const_iterator mvit = mv.begin(); mvit != mv.end(); ++mvit) { mmap[mvit->second] = mvit->first; } TEST_ASSERT(m2->getBondBetweenAtoms(mmap[1], mmap[2])->getBondType() == Bond::DOUBLE); TEST_ASSERT(m2->getBondBetweenAtoms(mmap[1], mmap[2])->getStereo() == Bond::STEREOE); TEST_ASSERT(m2->getBondBetweenAtoms(mmap[3], mmap[4])->getBondType() == Bond::DOUBLE); TEST_ASSERT(m2->getBondBetweenAtoms(mmap[3], mmap[4])->getStereo() == Bond::STEREOE); TEST_ASSERT(m2->getBondBetweenAtoms(mmap[6], mmap[7])->getBondType() == Bond::DOUBLE); TEST_ASSERT(m2->getBondBetweenAtoms(mmap[6], mmap[7])->getStereo() == Bond::STEREOE); std::string csmi1 = MolToSmiles(*m, true); // std::cerr<<"SMI1: "<getNumAtoms() == 16); ROMol *m2 = MolOps::mergeQueryHs(*m); TEST_ASSERT(m2); TEST_ASSERT(m2->getNumAtoms() == 13); TEST_ASSERT(!(m2->getRingInfo()->isInitialized())); delete m; delete m2; } { std::string smi = "CCC.C"; RWMol *m = SmilesToMol(smi); TEST_ASSERT(m); TEST_ASSERT(m->getNumAtoms() == 4); TEST_ASSERT((m->getRingInfo()->isInitialized())); m->addBond(1, 3, Bond::SINGLE); TEST_ASSERT((m->getRingInfo()->isInitialized())); m->addBond(0, 2, Bond::SINGLE); TEST_ASSERT(!(m->getRingInfo()->isInitialized())); delete m; } { std::string smi = "C1CC1C"; RWMol *m = SmilesToMol(smi); TEST_ASSERT(m); TEST_ASSERT(m->getNumAtoms() == 4); TEST_ASSERT((m->getRingInfo()->isInitialized())); m->removeBond(2, 3); TEST_ASSERT(!(m->getRingInfo()->isInitialized())); delete m; } { std::string smi = "C1CC1C"; RWMol *m = SmilesToMol(smi); TEST_ASSERT(m); TEST_ASSERT(m->getNumAtoms() == 4); TEST_ASSERT((m->getRingInfo()->isInitialized())); m->removeAtom(3); TEST_ASSERT(!(m->getRingInfo()->isInitialized())); delete m; } BOOST_LOG(rdInfoLog) << "Finished" << std::endl; } void testSFNetIssue249() { BOOST_LOG(rdInfoLog) << "-----------------------\n Testing sf.net issue 249: " "finding rings consumes all memory" << std::endl; { std::string smi = "Cc1cc2cc(c1)C(=O)NCc1cc-3cc(CNC(=O)c4cc(C)cc(c4)C(=O)NCc4cc(cc(CNC2=O)" "c4O)-c2cc4CNC(=O)c5cc(C)cc(c5)C(=O)NCc5cc-3cc(CNC(=O)c3cc(C)cc(c3)C(=" "O)NCc(c2)c4O)c5O)c1O"; ROMol *m = SmilesToMol(smi, 0, 0); TEST_ASSERT(m); TEST_ASSERT(m->getNumAtoms() == 88); m->updatePropertyCache(false); std::cerr << "starting ring finding" << std::endl; MolOps::findSSSR(*m); std::cerr << "done" << std::endl; delete m; } { std::string smi = "CCCOc1c2CNC(=O)c3cc(cc(c3)C(=O)NCc3cc4cc(CNC(=O)c5cc(C(=O)NCc1cc(c2)" "c1cc2CNC(=O)c6cc(cc(c6)C(=O)NCc6cc4cc(CNC(=O)c4cc(C(=O)NCc(c1)c2OCCC)" "cc(c4)C(=O)NC(COCCC(=O)O)(COCCC(=O)O)COCCC(=O)O)c6OCCC)C(=O)NC(COCCC(=" "O)O)(COCCC(=O)O)COCCC(=O)O)cc(c5)C(=O)NC(COCCC(=O)O)(COCCC(=O)O)COCCC(" "=O)O)c3OCCC)C(=O)NC(COCCC(=O)O)(COCCC(=O)O)COCCC(=O)O"; ROMol *m = SmilesToMol(smi, 0, 0); TEST_ASSERT(m); TEST_ASSERT(m->getNumAtoms() == 196); m->updatePropertyCache(false); std::cerr << "starting ring finding" << std::endl; MolOps::findSSSR(*m); std::cerr << "done" << std::endl; delete m; } { std::string smi = "CCn1nnc(c1)CN=C(C1CC2C3CCC4C5C3C3C6C2C2C1C1CCC7C(C1)C1C8C9C7C(C(=O)O)" "C(C(=O)O)C7C9C(C9C8C8C%10C1C1C(C2C2C6C6C%11C3C(C5)C3C(C(=O)O)C5C%" "12CC9C9C8C8C(C%10)C%10C%13C(C%14C(C2C1C(=O)O)C6C1C2C%11C3C3C5C(C5C%" "12C9C(C8CC%10)CC5)C(CC3C2C(C(C1C%14CC%13C(=NCc1nnn(c1)CC)O)C(=O)O)C(=" "O)O)C(=NCc1nnn(c1)CC)O)C(=O)O)C(=O)O)CC1C(C4C(CC71)C(=NCc1nnn(c1)CC)O)" "C(=O)O)O"; ROMol *m = SmilesToMol(smi, 0, 0); TEST_ASSERT(m); TEST_ASSERT(m->getNumAtoms() == 167); m->updatePropertyCache(false); std::cerr << "starting ring finding" << std::endl; MolOps::findSSSR(*m); std::cerr << "done" << std::endl; delete m; } { std::string smi = "C/C=N/" "c1ccc(cc1)c1cc2cc(c1)c1ccc(cc1)N=Cc1ccc(cc1)c1cc(cc(c1)c1ccc(cc1)C)" "c1ccc(cc1)C=Nc1ccc(cc1)c1cc(cc(c1)c1ccc(cc1)/N=C/" "C)c1ccc(cc1)N=Cc1ccc(cc1)c1cc(c3ccc(C=Nc4ccc(c5cc6c7ccc(N=Cc8ccc(c9cc(" "c%10ccc(C=Nc%11ccc2cc%11)cc%10)cc(c9)c2ccc(cc2)C=Nc2ccc(cc2)c2cc(cc(" "c2)c2ccc(cc2)N=Cc2ccc(cc2)c2cc(cc(c2)c2ccc(cc2)C)c2ccc(cc2)C=Nc2ccc(" "cc2)c2cc(c9ccc(N=Cc%10ccc(c%11cc(c%12ccc(C=Nc%13ccc(c(c6)c5)cc%13)cc%" "12)cc(c%11)c5ccc(cc5)C)cc%10)cc9)cc(c2)c2ccc(cc2)/N=C/C)c2ccc(cc2)/" "N=C/C)cc8)cc7)cc4)cc3)cc(c1)c1ccc(cc1)C"; RWMol *m = SmilesToMol(smi, 0, 0); TEST_ASSERT(m); TEST_ASSERT(m->getNumAtoms() == 278); std::cerr << smi << std::endl; std::cerr << "starting sanitization" << std::endl; MolOps::sanitizeMol(*m); std::cerr << "done" << std::endl; delete m; } { std::string smi = "COc1cc2ccc1n1c(=O)n(c3ccc(cc3OC)c3ccc(c(c3)OC)n3c(=O)n(c4ccc(cc4OC)" "c4ccc(c(c4)OC)n4c(=O)n(c5ccc(cc5OC)c5ccc(n6c(=O)n(c7ccc(c8ccc(n9c(=O)" "n(c%10ccc(c%11ccc(n%12c(=O)n(c%13ccc2cc%13OC)c(=O)n(c%12=O)c2ccc(" "cc2OC)C)c(OC)c%11)cc%10OC)c(=O)n(c9=O)c2ccc(cc2OC)C)c(OC)c8)cc7OC)c(=" "O)n(c6=O)c2ccc(cc2OC)C)c(c5)OC)c(=O)n(c4=O)c2ccc(cc2OC)C)c(=O)n(c3=O)" "c2ccc(cc2OC)C)c(=O)n(c1=O)c1ccc(cc1OC)C"; RWMol *m = SmilesToMol(smi, 0, 0); TEST_ASSERT(m); TEST_ASSERT(m->getNumAtoms() == 204); std::cerr << "starting sanitization" << std::endl; MolOps::sanitizeMol(*m); std::cerr << "done" << std::endl; delete m; } BOOST_LOG(rdInfoLog) << "Finished" << std::endl; } void testSFNetIssue256() { BOOST_LOG(rdInfoLog) << "-----------------------\n Testing sf.net issue 256: bad atom counts" << std::endl; { std::string smi = "*CC[H]"; ROMol *m = SmilesToMol(smi, 0, 0); TEST_ASSERT(m); m->updatePropertyCache(false); TEST_ASSERT(m->getNumAtoms() == 4); TEST_ASSERT(m->getNumAtoms(false) == 8); TEST_ASSERT(m->getNumHeavyAtoms() == 2); delete m; } { std::string smi = "*CC[2H]"; ROMol *m = SmilesToMol(smi); TEST_ASSERT(m); TEST_ASSERT(m->getNumAtoms() == 4); TEST_ASSERT(m->getNumAtoms(false) == 8); TEST_ASSERT(m->getNumHeavyAtoms() == 2); delete m; } BOOST_LOG(rdInfoLog) << "Finished" << std::endl; } void testSFNetIssue266() { BOOST_LOG(rdInfoLog) << "-----------------------\n Testing sf.net issue 266: " "ring finding error" << std::endl; { std::string pathName = getenv("RDBASE"); pathName += "/Code/GraphMol/test_data/"; RWMol *m = MolFileToMol(pathName + "Issue266.mol", false); TEST_ASSERT(m); TEST_ASSERT(m->getNumAtoms() == 19); TEST_ASSERT(m->getNumBonds() == 25); std::cerr << "starting ring finding" << std::endl; MolOps::findSSSR(*m); std::cerr << "done" << std::endl; TEST_ASSERT(m->getRingInfo()->numRings() == (m->getNumBonds() - m->getNumAtoms() + 1)); delete m; } BOOST_LOG(rdInfoLog) << "Finished" << std::endl; } void testSFNetIssue272() { BOOST_LOG(rdInfoLog) << "-----------------------\n Testing sf.net issue 272: " "removing two-coordinate Hs" << std::endl; { std::string smi = "C[H-]C"; ROMol *m = SmilesToMol(smi); TEST_ASSERT(m); TEST_ASSERT(m->getNumAtoms() == 3); delete m; } { std::string smi = "C[H].C"; ROMol *m = SmilesToMol(smi); TEST_ASSERT(m); TEST_ASSERT(m->getNumAtoms() == 2); delete m; } BOOST_LOG(rdInfoLog) << "Finished" << std::endl; } void testGitHubIssue8() { BOOST_LOG(rdInfoLog) << "-----------------------\n Testing Github issue 8 " "(impact of removeAtom on bond stereo atoms)" << std::endl; { std::string smi = "Cl/C=C/Cl"; RWMol *m = SmilesToMol(smi); TEST_ASSERT(m); MolOps::assignStereochemistry(*m); TEST_ASSERT(m->getBondWithIdx(1)->getStereoAtoms().size() == 2); m->removeAtom((unsigned int)0); TEST_ASSERT(m->getBondWithIdx(1)->getStereoAtoms().size() == 0); delete m; } { std::string smi = "CC/C=C/Cl"; RWMol *m = SmilesToMol(smi); TEST_ASSERT(m); MolOps::assignStereochemistry(*m); INT_VECT &sas = m->getBondWithIdx(2)->getStereoAtoms(); TEST_ASSERT(sas.size() == 2); TEST_ASSERT(std::find(sas.begin(), sas.end(), 1) != sas.end()); m->removeAtom((unsigned int)0); TEST_ASSERT(m->getBondWithIdx(1)->getStereoAtoms().size() == 2); TEST_ASSERT(std::find(sas.begin(), sas.end(), 0) != sas.end()); TEST_ASSERT(std::find(sas.begin(), sas.end(), 1) == sas.end()); delete m; } { std::string smi = "C/C=C/CC"; RWMol *m = SmilesToMol(smi); TEST_ASSERT(m); MolOps::assignStereochemistry(*m); INT_VECT &sas = m->getBondWithIdx(1)->getStereoAtoms(); TEST_ASSERT(sas.size() == 2); TEST_ASSERT(std::find(sas.begin(), sas.end(), 0) != sas.end()); TEST_ASSERT(std::find(sas.begin(), sas.end(), 3) != sas.end()); m->removeAtom((unsigned int)4); TEST_ASSERT(m->getBondWithIdx(1)->getStereoAtoms().size() == 2); TEST_ASSERT(std::find(sas.begin(), sas.end(), 0) != sas.end()); TEST_ASSERT(std::find(sas.begin(), sas.end(), 3) != sas.end()); delete m; } BOOST_LOG(rdInfoLog) << "Finished" << std::endl; } void testGitHubIssue42() { BOOST_LOG(rdInfoLog) << "-----------------------\n Testing Github issue 42 " "(impact of removeAtom on atom stereochem)" << std::endl; { std::string smi = "CCN1CCN(c2cc3[nH]c(C(=O)[C@@]4(CC)CC[C@](C)(O)CC4)nc3cc2Cl)CC1"; RWMol *m = SmilesToMol(smi); TEST_ASSERT(m); int indices[] = {29, 28, 27, 26, 25, 24, 8, 7, 6, 5, 4, 3, 2, 1, 0, -1}; for (unsigned int i = 0; indices[i] > -1; ++i) { m->removeAtom((unsigned int)indices[i]); } smi = MolToSmiles(*m, true); std::cerr << "smiles: " << smi << std::endl; delete m; } BOOST_LOG(rdInfoLog) << "Finished" << std::endl; } void testGitHubIssue65() { BOOST_LOG(rdInfoLog) << "-----------------------\n Testing Github issue 65 " "(kekulization of boron-containing aromatic rings)" << std::endl; { std::string smi = "C[B-]1=CC=CC=C1"; RWMol *m = SmilesToMol(smi); TEST_ASSERT(m); TEST_ASSERT(m->getAtomWithIdx(1)->getIsAromatic()); TEST_ASSERT(m->getBondWithIdx(1)->getIsAromatic()); MolOps::Kekulize(*m); delete m; } BOOST_LOG(rdInfoLog) << "Finished" << std::endl; } void testGitHubIssue72() { BOOST_LOG(rdInfoLog) << "-----------------------\n Testing Github issue 72 " "(problems with bad benzothiazolium structure)" << std::endl; { std::string pathName = getenv("RDBASE"); pathName += "/Code/GraphMol/test_data/"; RWMol *m = MolFileToMol(pathName + "github72.mol"); TEST_ASSERT(m); TEST_ASSERT(!m->getBondBetweenAtoms(0, 8)->getIsAromatic()); TEST_ASSERT(m->getBondBetweenAtoms(1, 6)->getIsAromatic()); delete m; } { std::string pathName = getenv("RDBASE"); pathName += "/Code/GraphMol/test_data/"; RWMol *m = MolFileToMol(pathName + "github72.2.mol"); TEST_ASSERT(m); TEST_ASSERT(m->getBondBetweenAtoms(0, 8)->getIsAromatic()); TEST_ASSERT(m->getBondBetweenAtoms(1, 6)->getIsAromatic()); delete m; } { std::string pathName = getenv("RDBASE"); pathName += "/Code/GraphMol/test_data/"; RWMol *m = MolFileToMol(pathName + "github72.3.mol"); TEST_ASSERT(m); TEST_ASSERT(!m->getBondBetweenAtoms(0, 8)->getIsAromatic()); TEST_ASSERT(m->getBondBetweenAtoms(1, 6)->getIsAromatic()); std::string smi = MolToSmiles(*m, true); delete m; m = SmilesToMol(smi); TEST_ASSERT(m); delete m; } BOOST_LOG(rdInfoLog) << "Finished" << std::endl; } namespace { void _renumberTest(const ROMol *m) { PRECONDITION(m, "no molecule"); std::vector idxV(m->getNumAtoms()); for (unsigned int i = 0; i < m->getNumAtoms(); ++i) { idxV[i] = i; } std::string refSmi = MolToSmiles(*m, true); for (unsigned int i = 0; i < m->getNumAtoms(); ++i) { std::vector nVect(idxV); std::shuffle(nVect.begin(), nVect.end(), std::mt19937(0xf00d)); // std::copy(nVect.begin(),nVect.end(),std::ostream_iterator(std::cerr,", // ")); // std::cerr<getNumAtoms() == m->getNumAtoms()); TEST_ASSERT(nm->getNumBonds() == m->getNumBonds()); // checking the SSS is a test for Github #317 MatchVectType mv; TEST_ASSERT(SubstructMatch(*m, *nm, mv)); TEST_ASSERT(mv.size() == nm->getNumAtoms()); for (unsigned int j = 0; j < m->getNumAtoms(); ++j) { TEST_ASSERT(m->getAtomWithIdx(nVect[j])->getAtomicNum() == nm->getAtomWithIdx(j)->getAtomicNum()); } // checking the conformation is a test for Github #441 TEST_ASSERT(m->getNumConformers() == nm->getNumConformers()); if (m->getNumConformers()) { for (unsigned int j = 0; j < m->getNumAtoms(); ++j) { RDGeom::Point3D po = m->getConformer().getAtomPos(nVect[j]); RDGeom::Point3D pn = nm->getConformer().getAtomPos(j); TEST_ASSERT(po.x == pn.x); TEST_ASSERT(po.y == pn.y); TEST_ASSERT(po.z == pn.z); } // checking conformer dimensionality is a test for Github #584 TEST_ASSERT(m->getConformer().is3D() == nm->getConformer().is3D()); } std::string nSmi = MolToSmiles(*nm, true); if (nSmi != refSmi) { std::cerr << refSmi << std::endl << nSmi << std::endl; } TEST_ASSERT(nSmi == refSmi); delete nm; } } } // namespace void testRenumberAtoms() { BOOST_LOG(rdInfoLog) << "-----------------------\n Testing renumbering atoms" << std::endl; { std::string smiles = "CC1CCCC(C)C1C"; ROMol *m = SmilesToMol(smiles); TEST_ASSERT(m); _renumberTest(m); delete m; } { std::string smiles = "C[C@H]1C[C@H](F)C1"; ROMol *m = SmilesToMol(smiles); TEST_ASSERT(m); _renumberTest(m); delete m; } { std::string smiles = "C[C@H]1CC[C@H](C/C=C/[C@H](F)Cl)CC1"; ROMol *m = SmilesToMol(smiles); TEST_ASSERT(m); _renumberTest(m); delete m; } { // github issue #441 and #584 std::string pathName = getenv("RDBASE"); pathName += "/Code/GraphMol/test_data/"; RWMol *m = MolFileToMol(pathName + "Issue266.mol"); // no significance to // choice of files, we // just need something // with 2D coords TEST_ASSERT(m); _renumberTest(m); delete m; } { // github issue 1735 renumber empty molecules auto *m = new ROMol; TEST_ASSERT(m); std::vector nVect; auto *m1 = MolOps::renumberAtoms(*m, nVect); delete m; delete m1; } BOOST_LOG(rdInfoLog) << "Finished" << std::endl; } void testGithubIssue141() { BOOST_LOG(rdInfoLog) << "-----------------------\n Testing github issue 141: " "Kekulization of molecule with aromatic N leaves the " "explicit H there." << std::endl; { std::string smiles = "N1C=CC=C1"; RWMol *m = SmilesToMol(smiles); TEST_ASSERT(m); MolOps::Kekulize(*m, true); m->updatePropertyCache(true); TEST_ASSERT(!m->getAtomWithIdx(0)->getIsAromatic()) TEST_ASSERT(m->getAtomWithIdx(0)->getNumImplicitHs() == 1) TEST_ASSERT(m->getAtomWithIdx(0)->getNumExplicitHs() == 0) delete m; } BOOST_LOG(rdInfoLog) << "Finished" << std::endl; } void testZBO() { BOOST_LOG(rdInfoLog) << "-----------------------\n"; BOOST_LOG(rdInfoLog) << "Testing ZBO basics" << std::endl; { auto *m = new RWMol(); m->addAtom(new Atom(26), true, true); m->addAtom(new Atom(6), true, true); m->addAtom(new Atom(6), true, true); m->addAtom(new Atom(6), true, true); m->addAtom(new Atom(6), true, true); m->addAtom(new Atom(6), true, true); m->addAtom(new Atom(6), true, true); m->addBond(1, 2, Bond::AROMATIC); m->addBond(2, 3, Bond::AROMATIC); m->addBond(3, 4, Bond::AROMATIC); m->addBond(4, 5, Bond::AROMATIC); m->addBond(5, 6, Bond::AROMATIC); m->addBond(1, 6, Bond::AROMATIC); m->addBond(1, 0, Bond::ZERO); m->addBond(2, 0, Bond::ZERO); m->addBond(3, 0, Bond::ZERO); m->addBond(4, 0, Bond::ZERO); m->addBond(5, 0, Bond::ZERO); m->addBond(6, 0, Bond::ZERO); MolOps::sanitizeMol(*m); TEST_ASSERT(m->getRingInfo()->numAtomRings(0) == 0); TEST_ASSERT(m->getRingInfo()->numAtomRings(1) == 1); TEST_ASSERT(m->getAtomWithIdx(1)->getHybridization() == Atom::SP2); TEST_ASSERT(m->getAtomWithIdx(2)->getHybridization() == Atom::SP2); TEST_ASSERT(m->getAtomWithIdx(3)->getHybridization() == Atom::SP2); TEST_ASSERT(m->getAtomWithIdx(4)->getHybridization() == Atom::SP2); TEST_ASSERT(m->getAtomWithIdx(5)->getHybridization() == Atom::SP2); TEST_ASSERT(m->getAtomWithIdx(6)->getHybridization() == Atom::SP2); TEST_ASSERT(m->getBondWithIdx(0)->getIsAromatic()); TEST_ASSERT(m->getBondWithIdx(1)->getIsAromatic()); TEST_ASSERT(m->getBondWithIdx(2)->getIsAromatic()); TEST_ASSERT(m->getBondWithIdx(3)->getIsAromatic()); TEST_ASSERT(m->getBondWithIdx(4)->getIsAromatic()); TEST_ASSERT(m->getBondWithIdx(5)->getIsAromatic()); delete m; } BOOST_LOG(rdInfoLog) << "Finished" << std::endl; } void testMolAssignment() { BOOST_LOG(rdInfoLog) << "-----------------------\n Testing operator= on molecules" << std::endl; { std::string smi = "CCN1CCN(c2cc3[nH]c(C(=O)[C@@]4(CC)CC[C@](C)(O)CC4)nc3cc2Cl)CC1"; RWMol *m = SmilesToMol(smi); TEST_ASSERT(m); std::string csmi = MolToSmiles(*m, true); RWMol m2 = *m; std::string nsmi = MolToSmiles(m2, true); TEST_ASSERT(nsmi == csmi); RWMol *m3 = SmilesToMol("C2CC2[C@H](F)Cl"); TEST_ASSERT(m3); *m3 = *m; nsmi = MolToSmiles(*m3, true); TEST_ASSERT(nsmi == csmi); delete m3; delete m; } BOOST_LOG(rdInfoLog) << "Finished" << std::endl; } void testGithubIssue190() { BOOST_LOG(rdInfoLog) << "-----------------------\n Testing github issue 190: " "Don't merge Hs onto dummy atoms." << std::endl; { std::string smiles = "*[H]"; RWMol *m = SmilesToMol(smiles); TEST_ASSERT(m); TEST_ASSERT(m->getNumAtoms() == 2); delete m; } BOOST_LOG(rdInfoLog) << "Finished" << std::endl; } namespace { int getAtNum(const ROMol &, const Atom *at) { return at->getAtomicNum(); } } // namespace void testMolFragsWithQuery() { BOOST_LOG(rdInfoLog) << "-----------------------\n Testing getMolFragsWithQuery()." << std::endl; { std::string smiles = "C1CCC1ONNC"; RWMol *m = SmilesToMol(smiles); TEST_ASSERT(m); TEST_ASSERT(m->getNumAtoms() == 8); std::map> res = MolOps::getMolFragsWithQuery(*m, getAtNum); TEST_ASSERT(res.size() == 3); TEST_ASSERT(res.find(6) != res.end()); TEST_ASSERT(res.find(7) != res.end()); TEST_ASSERT(res.find(8) != res.end()); TEST_ASSERT(res.find(5) == res.end()); TEST_ASSERT(res[6]->getNumAtoms() == 5); TEST_ASSERT(res[6]->getNumBonds() == 4); TEST_ASSERT(res[7]->getNumAtoms() == 2); TEST_ASSERT(res[7]->getNumBonds() == 1); TEST_ASSERT(res[8]->getNumAtoms() == 1); TEST_ASSERT(res[8]->getNumBonds() == 0); delete m; } { std::string smiles = "C1CCC1ONNC"; RWMol *m = SmilesToMol(smiles); TEST_ASSERT(m); TEST_ASSERT(m->getNumAtoms() == 8); std::vector keep; keep.push_back(6); keep.push_back(8); std::map> res = MolOps::getMolFragsWithQuery(*m, getAtNum, true, &keep); TEST_ASSERT(res.size() == 2); TEST_ASSERT(res.find(6) != res.end()); TEST_ASSERT(res.find(7) == res.end()); TEST_ASSERT(res.find(8) != res.end()); TEST_ASSERT(res[6]->getNumAtoms() == 5); TEST_ASSERT(res[6]->getNumBonds() == 4); TEST_ASSERT(res[8]->getNumAtoms() == 1); TEST_ASSERT(res[8]->getNumBonds() == 0); delete m; } { std::string smiles = "C1CCC1ONNC"; RWMol *m = SmilesToMol(smiles); TEST_ASSERT(m); TEST_ASSERT(m->getNumAtoms() == 8); std::vector keep; keep.push_back(6); keep.push_back(8); std::map> res = MolOps::getMolFragsWithQuery(*m, getAtNum, true, &keep, true); TEST_ASSERT(res.size() == 1); TEST_ASSERT(res.find(6) == res.end()); TEST_ASSERT(res.find(7) != res.end()); TEST_ASSERT(res.find(8) == res.end()); TEST_ASSERT(res[7]->getNumAtoms() == 2); TEST_ASSERT(res[7]->getNumBonds() == 1); delete m; } BOOST_LOG(rdInfoLog) << "Finished" << std::endl; } void testGithubIssue418() { BOOST_LOG(rdInfoLog) << "-----------------------\n Testing github issue 418: " "removeHs not updating H count." << std::endl; { auto *m2 = new RWMol(); m2->addAtom(new Atom(7), true, true); m2->addAtom(new Atom(1), true, true); m2->addAtom(new Atom(1), true, true); m2->addAtom(new Atom(1), true, true); m2->addAtom(new Atom(1), true, true); m2->addBond(0, 1, Bond::SINGLE); m2->addBond(0, 2, Bond::SINGLE); m2->addBond(0, 3, Bond::SINGLE); m2->addBond(0, 4, Bond::SINGLE); MolOps::removeHs(*m2, false, true, false); TEST_ASSERT(m2->getAtomWithIdx(0)->getNumExplicitHs() == 4); delete m2; } { std::string smiles = "[H][N+]([H])([H])[H]"; RWMol *m = SmilesToMol(smiles); TEST_ASSERT(m); TEST_ASSERT(m->getNumAtoms() == 1); TEST_ASSERT(m->getAtomWithIdx(0)->getNumExplicitHs() == 4); delete m; } { std::string smiles = "[H]N([H])([H])[H]"; bool ok = false; try { SmilesToMol(smiles); } catch (MolSanitizeException &) { ok = true; } TEST_ASSERT(ok); } BOOST_LOG(rdInfoLog) << "Finished" << std::endl; } void testGithubIssue432() { BOOST_LOG(rdInfoLog) << "-----------------------\n Testing github issue 432: " "problems caused by aromatic Ns with radical " "electrons." << std::endl; { std::string smiles = "C1=NN=N[N]1"; RWMol *m = SmilesToMol(smiles); TEST_ASSERT(m); TEST_ASSERT(m->getAtomWithIdx(4)->getNumRadicalElectrons() == 1); TEST_ASSERT(!m->getAtomWithIdx(4)->getIsAromatic()); TEST_ASSERT(!m->getAtomWithIdx(0)->getIsAromatic()); TEST_ASSERT(!m->getBondWithIdx(0)->getIsAromatic()); delete m; } { // test round-tripping: std::string smiles = "C1=NN=N[N]1"; RWMol *m = SmilesToMol(smiles); TEST_ASSERT(m); smiles = MolToSmiles(*m); delete m; m = SmilesToMol(smiles); TEST_ASSERT(m); delete m; } { // test round-tripping: std::string smiles = "OC(=O)C(=O)Nc1cccc(c1)C2=NN=N[N]2"; RWMol *m = SmilesToMol(smiles); TEST_ASSERT(m); smiles = MolToSmiles(*m); delete m; m = SmilesToMol(smiles); TEST_ASSERT(m); delete m; } { std::string smiles = "C1=C[N]C=C1"; RWMol *m = SmilesToMol(smiles); TEST_ASSERT(m); TEST_ASSERT(m->getAtomWithIdx(2)->getNumRadicalElectrons() == 1); TEST_ASSERT(!m->getAtomWithIdx(2)->getIsAromatic()); TEST_ASSERT(!m->getAtomWithIdx(0)->getIsAromatic()); TEST_ASSERT(!m->getBondWithIdx(0)->getIsAromatic()); delete m; } BOOST_LOG(rdInfoLog) << "Finished" << std::endl; } void testGithubIssue443() { BOOST_LOG(rdInfoLog) << "-----------------------\n Testing github issue 443: " "kekulization problems caused by any bonds." << std::endl; { std::string pathName = getenv("RDBASE"); pathName += "/Code/GraphMol/test_data/"; RWMol *m = MolFileToMol(pathName + "github443.min.mol"); TEST_ASSERT(m); TEST_ASSERT(!m->getAtomWithIdx(0)->getIsAromatic()); TEST_ASSERT(m->getBondBetweenAtoms(0, 3)); MolOps::Kekulize(*m); delete m; } { std::string pathName = getenv("RDBASE"); pathName += "/Code/GraphMol/test_data/"; RWMol *m = MolFileToMol(pathName + "github443.mol"); TEST_ASSERT(m); TEST_ASSERT(m->getAtomWithIdx(19)->getIsAromatic()); TEST_ASSERT(m->getAtomWithIdx(12)->getIsAromatic()); // we might normally expect these to be aromatic because the outer porphyrin // ring // is 4n+2 aromatic. However, the current fused ring aromaticity perception // uses // the symmetrized SSSR rings and only works if all atoms are aromatic. This // cannot // happen when the Mg is involved // TEST_ASSERT(m->getAtomWithIdx(13)->getIsAromatic()); // TEST_ASSERT(m->getAtomWithIdx(11)->getIsAromatic()); TEST_ASSERT(m->getBondBetweenAtoms(13, 20)); TEST_ASSERT(m->getBondBetweenAtoms(19, 20)); TEST_ASSERT(m->getBondBetweenAtoms(11, 20)); TEST_ASSERT(m->getBondBetweenAtoms(12, 20)); MolOps::Kekulize(*m); delete m; } BOOST_LOG(rdInfoLog) << "Finished" << std::endl; } void testGithubIssue447() { BOOST_LOG(rdInfoLog) << "-----------------------\n Testing github issue 447: " "Radicals are not correctly assigned when reading " "from SMILES." << std::endl; { std::string smiles = "C[S]"; RWMol *m = SmilesToMol(smiles); TEST_ASSERT(m); TEST_ASSERT(m->getAtomWithIdx(1)->getNoImplicit()); TEST_ASSERT(m->getAtomWithIdx(1)->getNumRadicalElectrons() == 1); delete m; } { std::string smiles = "C[SH]C"; RWMol *m = SmilesToMol(smiles); TEST_ASSERT(m); TEST_ASSERT(m->getAtomWithIdx(1)->getNoImplicit()); TEST_ASSERT(m->getAtomWithIdx(1)->getNumRadicalElectrons() == 1); delete m; } { std::string smiles = "C[SH3]C"; RWMol *m = SmilesToMol(smiles); TEST_ASSERT(m); TEST_ASSERT(m->getAtomWithIdx(1)->getNoImplicit()); TEST_ASSERT(m->getAtomWithIdx(1)->getNumRadicalElectrons() == 1); delete m; } { std::string smiles = "C[SH4]C"; RWMol *m = SmilesToMol(smiles); TEST_ASSERT(m); TEST_ASSERT(m->getAtomWithIdx(1)->getNoImplicit()); TEST_ASSERT(m->getAtomWithIdx(1)->getNumRadicalElectrons() == 0); delete m; } { std::string smiles = "C[SH4+]C"; RWMol *m = SmilesToMol(smiles); TEST_ASSERT(m); TEST_ASSERT(m->getAtomWithIdx(1)->getNoImplicit()); TEST_ASSERT(m->getAtomWithIdx(1)->getNumRadicalElectrons() == 1); delete m; } { std::string smiles = "C[P]C"; RWMol *m = SmilesToMol(smiles); TEST_ASSERT(m); TEST_ASSERT(m->getAtomWithIdx(1)->getNoImplicit()); TEST_ASSERT(m->getAtomWithIdx(1)->getNumRadicalElectrons() == 1); delete m; } { std::string smiles = "C[PH2]C"; RWMol *m = SmilesToMol(smiles); TEST_ASSERT(m); TEST_ASSERT(m->getAtomWithIdx(1)->getNoImplicit()); TEST_ASSERT(m->getAtomWithIdx(1)->getNumRadicalElectrons() == 1); delete m; } BOOST_LOG(rdInfoLog) << "Finished" << std::endl; } void testGetMolFrags() { BOOST_LOG(rdInfoLog) << "-----------------------\n Testing generation of new " "molecules from molecule fragments" << std::endl; { std::string smiles = "c1ccccc1.O.CCC(=O)O"; RWMol *m = SmilesToMol(smiles); TEST_ASSERT(m); INT_VECT fragsMapping; VECT_INT_VECT fragsMolAtomMapping; std::vector frags = MolOps::getMolFrags(*m, false, &fragsMapping, &fragsMolAtomMapping); TEST_ASSERT(frags.size() == 3) TEST_ASSERT(fragsMapping.size() == m->getNumAtoms()); TEST_ASSERT(fragsMapping[2] == 0); TEST_ASSERT(fragsMapping[6] == 1); TEST_ASSERT(fragsMapping[8] == 2); TEST_ASSERT(fragsMolAtomMapping[0].size() == frags[0]->getNumAtoms()); TEST_ASSERT(fragsMolAtomMapping[1].size() == frags[1]->getNumAtoms()); TEST_ASSERT(fragsMolAtomMapping[2].size() == frags[2]->getNumAtoms()); TEST_ASSERT(fragsMolAtomMapping[0][1] == 1); TEST_ASSERT(fragsMolAtomMapping[1][0] == 6); TEST_ASSERT(fragsMolAtomMapping[2][1] == 8); TEST_ASSERT(MolToSmiles(*frags[0], true) == "c1ccccc1"); TEST_ASSERT(MolToSmiles(*frags[1], true) == "O"); TEST_ASSERT(MolToSmiles(*frags[2], true) == "CCC(=O)O"); delete m; } { std::string pathName = getenv("RDBASE"); pathName += "/Code/GraphMol/test_data/"; RWMol *m = MolFileToMol(pathName + "chembl1203199.mol"); TEST_ASSERT(m); std::string smi = "C[C@H](NC(=O)[C@H]1Cc2c(sc3ccccc23)CN1)c1ccccc1.Cl"; TEST_ASSERT(MolToSmiles(*m, true) == smi); INT_VECT fragsMapping; VECT_INT_VECT fragsMolAtomMapping; std::vector frags = MolOps::getMolFrags( *m, false, &fragsMapping, &fragsMolAtomMapping, true); TEST_ASSERT(frags.size() == 2) TEST_ASSERT(fragsMapping.size() == m->getNumAtoms()); TEST_ASSERT(fragsMapping[2] == 0); TEST_ASSERT(fragsMapping[24] == 1); TEST_ASSERT(fragsMolAtomMapping[0].size() == frags[0]->getNumAtoms()); TEST_ASSERT(fragsMolAtomMapping[1].size() == frags[1]->getNumAtoms()); TEST_ASSERT(fragsMolAtomMapping[0][1] == 1); TEST_ASSERT(fragsMolAtomMapping[1][0] == 24); TEST_ASSERT(frags[0]->getNumConformers() == 1); TEST_ASSERT(frags[1]->getNumConformers() == 1); TEST_ASSERT(frags[0]->getConformer(0).getAtomPos(0).x == m->getConformer(0).getAtomPos(0).x); TEST_ASSERT(frags[0]->getConformer(0).getAtomPos(0).y == m->getConformer(0).getAtomPos(0).y); TEST_ASSERT(frags[0]->getConformer(0).getAtomPos(0).z == m->getConformer(0).getAtomPos(0).z); TEST_ASSERT(frags[0]->getConformer(0).getAtomPos(3).x == m->getConformer(0).getAtomPos(3).x); TEST_ASSERT(frags[0]->getConformer(0).getAtomPos(3).y == m->getConformer(0).getAtomPos(3).y); TEST_ASSERT(frags[0]->getConformer(0).getAtomPos(3).z == m->getConformer(0).getAtomPos(3).z); TEST_ASSERT(frags[1]->getConformer(0).getAtomPos(0).x == m->getConformer(0).getAtomPos(24).x); TEST_ASSERT(frags[1]->getConformer(0).getAtomPos(0).y == m->getConformer(0).getAtomPos(24).y); TEST_ASSERT(frags[1]->getConformer(0).getAtomPos(0).z == m->getConformer(0).getAtomPos(24).z); delete m; } BOOST_LOG(rdInfoLog) << "Finished" << std::endl; } namespace { void hypervalent_check(const char *smiles) { RWMol *m = SmilesToMol(smiles); TEST_ASSERT(m); TEST_ASSERT(m->getBondBetweenAtoms(0, 1)->getBondType() == Bond::SINGLE); TEST_ASSERT(m->getAtomWithIdx(1)->getFormalCharge() == -1); delete m; } } // namespace void testGithubIssue510() { BOOST_LOG(rdInfoLog) << "-----------------------\n Testing github issue 510: " "Hexafluorophosphate cannot be handled" << std::endl; hypervalent_check("F[P-](F)(F)(F)(F)F"); // test #1668 too, it's the same thing but with As, Sb, and Bi hypervalent_check("F[As-](F)(F)(F)(F)F"); hypervalent_check("F[Sb-](F)(F)(F)(F)F"); hypervalent_check("F[Bi-](F)(F)(F)(F)F"); hypervalent_check("F[Sb-](F)(F)(F)(F)F"); hypervalent_check("F[Bi-](F)(F)(F)(F)F"); // we also added a valence of 5 for Bi: hypervalent_check("F[Bi-](F)(F)F"); BOOST_LOG(rdInfoLog) << "Finished" << std::endl; } void testGithubIssue526() { BOOST_LOG(rdInfoLog) << "-----------------------\n Testing github issue 526: " "Bad ring finding in a complex fused ring" << std::endl; { std::string smiles = "N1C2[C@@H]3N[C@H]4[C@@H]5N[C@@H]([C@@H]1C35)C24"; RWMol *m = SmilesToMol(smiles); TEST_ASSERT(m); TEST_ASSERT(m->getRingInfo()->numRings() == 6); delete m; } { std::string smiles = "NN1C2C3[C@@H]4[C@@H]1C1[C@H]2N([C@H]3[C@@H]1N4N1C(=O)C2=C(C=CC=C2)C1=" "O)N1C(=O)C2=C(C=CC=C2)C1=O"; RWMol *m = SmilesToMol(smiles); TEST_ASSERT(m); TEST_ASSERT(m->getRingInfo()->numRings() == 10); delete m; } BOOST_LOG(rdInfoLog) << "Finished" << std::endl; } void testGithubIssue539() { BOOST_LOG(rdInfoLog) << "-----------------------\n Testing github issue 539: " "Lack of conjugation in allyl cations, " "lack of aromaticity perception/ability to kekulize " "aromatic carbocations such as cyclopropenyl and " "tropylium" << std::endl; { std::string smiles = "C=C-[CH2+]"; RWMol *m = SmilesToMol(smiles); TEST_ASSERT(m); bool allConjugated = true; for (unsigned int i = 0; allConjugated && i < m->getNumBonds(); ++i) { allConjugated = m->getBondWithIdx(i)->getIsConjugated(); } TEST_ASSERT(allConjugated); delete m; } { std::vector smilesVec; smilesVec.emplace_back("C1=C[CH+]1"); smilesVec.emplace_back("C1=CC=C[CH+]C=C1"); smilesVec.emplace_back("c1c[cH+]1"); smilesVec.emplace_back("c1ccc[cH+]cc1"); for (std::vector::const_iterator smiles = smilesVec.begin(); smiles != smilesVec.end(); ++smiles) { RWMol *m = SmilesToMol(*smiles); TEST_ASSERT(m); bool allConjugated = true; for (unsigned int i = 0; allConjugated && i < m->getNumBonds(); ++i) { allConjugated = m->getBondWithIdx(i)->getIsConjugated(); } TEST_ASSERT(allConjugated); bool allAromatic = true; for (unsigned int i = 0; allAromatic && i < m->getNumBonds(); ++i) { allAromatic = m->getBondWithIdx(i)->getIsAromatic(); } TEST_ASSERT(allAromatic); delete m; } } { std::string smiles = "C=C-C"; RWMol *m = SmilesToMol(smiles); TEST_ASSERT(m); TEST_ASSERT(m->getAtomWithIdx(2)->getHybridization() == Atom::SP3); TEST_ASSERT(m->getBondWithIdx(1)->getBondType() == Bond::SINGLE); TEST_ASSERT(!m->getBondWithIdx(1)->getIsConjugated()); delete m; } { std::string smiles = "C=C-O"; RWMol *m = SmilesToMol(smiles); TEST_ASSERT(m); TEST_ASSERT(m->getAtomWithIdx(2)->getHybridization() == Atom::SP2); TEST_ASSERT(m->getBondWithIdx(1)->getBondType() == Bond::SINGLE); TEST_ASSERT(m->getBondWithIdx(1)->getIsConjugated()); delete m; } { std::string smiles = "C=C-N"; RWMol *m = SmilesToMol(smiles); TEST_ASSERT(m); TEST_ASSERT(m->getAtomWithIdx(2)->getHybridization() == Atom::SP2); TEST_ASSERT(m->getBondWithIdx(1)->getBondType() == Bond::SINGLE); TEST_ASSERT(m->getBondWithIdx(1)->getIsConjugated()); delete m; } { std::string smiles = "C=C-[NH3+]"; RWMol *m = SmilesToMol(smiles); TEST_ASSERT(m); TEST_ASSERT(m->getAtomWithIdx(2)->getHybridization() == Atom::SP3); TEST_ASSERT(m->getBondWithIdx(1)->getBondType() == Bond::SINGLE); TEST_ASSERT(!m->getBondWithIdx(1)->getIsConjugated()); delete m; } { std::string smiles = "Cc1ccccc1"; RWMol *m = SmilesToMol(smiles); TEST_ASSERT(m); TEST_ASSERT(m->getBondWithIdx(0)->getBondType() == Bond::SINGLE); // the bond to the CH3 should not be conjugated, but the others are TEST_ASSERT(!m->getBondWithIdx(0)->getIsConjugated()); for (unsigned int i = 1; i < m->getNumBonds(); ++i) { TEST_ASSERT(m->getBondWithIdx(i)->getIsConjugated()); } delete m; } { std::string smiles = "Fc1c[nH]c(=O)[nH]c1=O"; RWMol *m = SmilesToMol(smiles); TEST_ASSERT(m); TEST_ASSERT(m->getBondWithIdx(0)->getBondType() == Bond::SINGLE); // the bond to the F should not be conjugated, but the others are TEST_ASSERT(!m->getBondWithIdx(0)->getIsConjugated()); for (unsigned int i = 1; i < m->getNumBonds(); ++i) { TEST_ASSERT(m->getBondWithIdx(i)->getIsConjugated()); } delete m; } BOOST_LOG(rdInfoLog) << "Finished" << std::endl; } void testAdjustQueryProperties() { BOOST_LOG(rdInfoLog) << "-----------------------\n Testing adjustQueryProperties()" << std::endl; #if 1 { // basics from SMILES std::string smiles = "C1CCC1C"; ROMol *qm = SmilesToMol(smiles); TEST_ASSERT(qm); TEST_ASSERT(qm->getNumAtoms() == 5); ROMol *aqm = MolOps::adjustQueryProperties(*qm); TEST_ASSERT(aqm); TEST_ASSERT(aqm->getNumAtoms() == 5); { smiles = "C1CCC1CC"; ROMol *m = SmilesToMol(smiles); TEST_ASSERT(m); MatchVectType match; TEST_ASSERT(SubstructMatch(*m, *qm, match)); TEST_ASSERT(SubstructMatch(*m, *aqm, match)); delete m; } { smiles = "C1C(C)CC1CC"; ROMol *m = SmilesToMol(smiles); TEST_ASSERT(m); MatchVectType match; TEST_ASSERT(SubstructMatch(*m, *qm, match)); TEST_ASSERT(!SubstructMatch(*m, *aqm, match)); delete m; } delete qm; delete aqm; } { // basics from SMARTS std::string smiles = "C1CCC1*"; ROMol *qm = SmartsToMol(smiles); TEST_ASSERT(qm); TEST_ASSERT(qm->getNumAtoms() == 5); ROMol *aqm = MolOps::adjustQueryProperties(*qm); TEST_ASSERT(aqm); TEST_ASSERT(aqm->getNumAtoms() == 5); { smiles = "C1CCC1CC"; ROMol *m = SmilesToMol(smiles); TEST_ASSERT(m); MatchVectType match; TEST_ASSERT(SubstructMatch(*m, *qm, match)); TEST_ASSERT(SubstructMatch(*m, *aqm, match)); delete m; } { smiles = "C1C(C)CC1CC"; ROMol *m = SmilesToMol(smiles); TEST_ASSERT(m); MatchVectType match; TEST_ASSERT(SubstructMatch(*m, *qm, match)); TEST_ASSERT(!SubstructMatch(*m, *aqm, match)); delete m; } delete qm; delete aqm; } { std::string smiles = "C1CC(*)C1*"; ROMol *qm = SmartsToMol(smiles); TEST_ASSERT(qm); TEST_ASSERT(qm->getNumAtoms() == 6); ROMol *aqm = MolOps::adjustQueryProperties(*qm); TEST_ASSERT(aqm); TEST_ASSERT(aqm->getNumAtoms() == 6); { smiles = "C1CC2C1CC2"; ROMol *m = SmilesToMol(smiles); TEST_ASSERT(m); MatchVectType match; TEST_ASSERT(SubstructMatch(*m, *qm, match)); TEST_ASSERT(SubstructMatch(*m, *aqm, match)); MolOps::AdjustQueryParameters aqp; delete aqm; aqp.adjustDegree = false; aqp.adjustRingCount = false; aqm = MolOps::adjustQueryProperties(*qm, &aqp); TEST_ASSERT(aqm); TEST_ASSERT(aqm->getNumAtoms() == 6); TEST_ASSERT(SubstructMatch(*m, *aqm, match)); delete aqm; aqp.adjustDegree = false; aqp.adjustRingCount = true; aqm = MolOps::adjustQueryProperties(*qm, &aqp); TEST_ASSERT(aqm); TEST_ASSERT(aqm->getNumAtoms() == 6); TEST_ASSERT(!SubstructMatch(*m, *aqm, match)); delete aqm; aqp.adjustDegree = true; aqp.adjustRingCount = false; aqp.adjustDegreeFlags = MolOps::ADJUST_IGNORENONE; aqm = MolOps::adjustQueryProperties(*qm, &aqp); TEST_ASSERT(aqm); TEST_ASSERT(aqm->getNumAtoms() == 6); TEST_ASSERT(!SubstructMatch(*m, *aqm, match)); delete m; } { smiles = "C1CC(C2CC2)C1C2CC2"; ROMol *m = SmilesToMol(smiles); TEST_ASSERT(m); MatchVectType match; delete aqm; aqm = MolOps::adjustQueryProperties(*qm); TEST_ASSERT(SubstructMatch(*m, *qm, match)); TEST_ASSERT(SubstructMatch(*m, *aqm, match)); MolOps::AdjustQueryParameters aqp; delete aqm; aqp.adjustRingCount = true; aqm = MolOps::adjustQueryProperties(*qm, &aqp); TEST_ASSERT(aqm); TEST_ASSERT(aqm->getNumAtoms() == 6); TEST_ASSERT(SubstructMatch(*m, *aqm, match)); delete aqm; aqp.adjustRingCountFlags = MolOps::ADJUST_IGNORENONE; // neither "not dummy" // nor "in ring" // restrictions aqm = MolOps::adjustQueryProperties(*qm, &aqp); TEST_ASSERT(aqm); TEST_ASSERT(aqm->getNumAtoms() == 6); TEST_ASSERT(!SubstructMatch(*m, *aqm, match)); delete aqm; aqp.adjustRingCountFlags = MolOps::ADJUST_IGNOREDUMMIES; // no "in ring" restrictions aqm = MolOps::adjustQueryProperties(*qm, &aqp); TEST_ASSERT(aqm); TEST_ASSERT(aqm->getNumAtoms() == 6); TEST_ASSERT(SubstructMatch(*m, *aqm, match)); delete m; } delete qm; delete aqm; } { // dummies from SMILES std::string smiles = "C1CCC1*"; ROMol *qm = SmilesToMol(smiles); TEST_ASSERT(qm); TEST_ASSERT(qm->getNumAtoms() == 5); ROMol *aqm = MolOps::adjustQueryProperties(*qm); TEST_ASSERT(aqm); TEST_ASSERT(aqm->getNumAtoms() == 5); smiles = "C1CCC1CC"; ROMol *m = SmilesToMol(smiles); TEST_ASSERT(m); MatchVectType match; TEST_ASSERT(!SubstructMatch(*m, *qm, match)); TEST_ASSERT(SubstructMatch(*m, *aqm, match)); delete aqm; MolOps::AdjustQueryParameters aqp; aqp.makeDummiesQueries = false; aqm = MolOps::adjustQueryProperties(*qm, &aqp); TEST_ASSERT(!SubstructMatch(*m, *aqm, match)); delete m; delete qm; delete aqm; } { // dummies from SMILES 2 std::string smiles = "C1CCC1[1*]"; ROMol *qm = SmilesToMol(smiles); TEST_ASSERT(qm); TEST_ASSERT(qm->getNumAtoms() == 5); ROMol *aqm = MolOps::adjustQueryProperties(*qm); TEST_ASSERT(aqm); TEST_ASSERT(aqm->getNumAtoms() == 5); { smiles = "C1CCC1CC"; ROMol *m = SmilesToMol(smiles); TEST_ASSERT(m); MatchVectType match; TEST_ASSERT(!SubstructMatch(*m, *qm, match)); TEST_ASSERT(!SubstructMatch(*m, *aqm, match)); delete m; } delete qm; delete aqm; } { // dummies from SMILES 2 std::string smiles = "C1CCC1[*:1]"; ROMol *qm = SmilesToMol(smiles); qm->getAtomWithIdx(4)->setProp("foo", 2); TEST_ASSERT(qm); TEST_ASSERT(qm->getNumAtoms() == 5); ROMol *aqm = MolOps::adjustQueryProperties(*qm); TEST_ASSERT(aqm); TEST_ASSERT(aqm->getNumAtoms() == 5); TEST_ASSERT(aqm->getAtomWithIdx(4)->getProp("foo") == 2); TEST_ASSERT(aqm->getAtomWithIdx(4)->getAtomMapNum() == 1); { smiles = "C1CCC1CC"; ROMol *m = SmilesToMol(smiles); TEST_ASSERT(m); MatchVectType match; TEST_ASSERT(!SubstructMatch(*m, *qm, match)); TEST_ASSERT(SubstructMatch(*m, *aqm, match)); delete m; } delete qm; delete aqm; } { // CTAB // -- only match rgroups std::string mb = "adjust.mol\n" " ChemDraw06271617272D\n" "\n" " 7 7 0 0 0 0 0 0 0 0999 V2000\n" " -1.0717 0.4125 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 " "0\n" " -1.0717 -0.4125 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 " "0\n" " -0.3572 -0.8250 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 " "0\n" " 0.3572 -0.4125 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 " "0\n" " 0.3572 0.4125 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 " "0\n" " -0.3572 0.8250 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 " "0\n" " 1.0717 0.8250 0.0000 R 0 0 0 0 0 0 0 0 0 1 0 " "0\n" " 1 2 1 0 \n" " 2 3 2 0 \n" " 3 4 1 0 \n" " 4 5 2 0 \n" " 5 6 1 0 \n" " 6 1 2 0 \n" " 5 7 1 0 \n" "M END\n"; MolOps::AdjustQueryParameters params; params.aromatizeIfPossible = false; params.makeDummiesQueries = true; params.adjustDegreeFlags = (MolOps::ADJUST_IGNOREDUMMIES | MolOps::ADJUST_IGNORECHAINS | MolOps::ADJUST_IGNOREMAPPED); RWMol *m = MolBlockToMol(mb, false, false); MolOps::adjustQueryProperties(*m, ¶ms); MatchVectType match; ROMol *t = SmilesToMol("c1ccccc1Cl"); // shouldn't match (aromaticity): TEST_ASSERT(!SubstructMatch(*t, *m, match)); // adjust aromaticity and then it should match: params.aromatizeIfPossible = true; MolOps::adjustQueryProperties(*m, ¶ms); TEST_ASSERT(SubstructMatch(*t, *m, match)); delete t; // shouldn't match (explicit degree) t = SmilesToMol("c1ccc(Cl)cc1Cl"); TEST_ASSERT(!SubstructMatch(*t, *m, match)); delete m; delete t; } { // CTAB // -- match non rgroups if mapped std::string mb = "adjust.mol\n" " ChemDraw06271617272D\n" "\n" " 7 7 0 0 0 0 0 0 0 0999 V2000\n" " -1.0717 0.4125 0.0000 C 0 0 0 0 0 0 0 0 0 2 0 " "0\n" " -1.0717 -0.4125 0.0000 C 0 0 0 0 0 0 0 0 0 3 0 " "0\n" " -0.3572 -0.8250 0.0000 C 0 0 0 0 0 0 0 0 0 4 0 " "0\n" " 0.3572 -0.4125 0.0000 C 0 0 0 0 0 0 0 0 0 5 0 " "0\n" " 0.3572 0.4125 0.0000 C 0 0 0 0 0 0 0 0 0 6 0 " "0\n" " -0.3572 0.8250 0.0000 C 0 0 0 0 0 0 0 0 0 7 0 " "0\n" " 1.0717 0.8250 0.0000 R 0 0 0 0 0 0 0 0 0 1 0 " "0\n" " 1 2 1 0 \n" " 2 3 2 0 \n" " 3 4 1 0 \n" " 4 5 2 0 \n" " 5 6 1 0 \n" " 6 1 2 0 \n" " 5 7 1 0 \n" "M END\n"; MolOps::AdjustQueryParameters params; params.aromatizeIfPossible = true; params.makeDummiesQueries = true; params.adjustDegreeFlags = (MolOps::ADJUST_IGNOREDUMMIES | MolOps::ADJUST_IGNORECHAINS | MolOps::ADJUST_IGNOREMAPPED); RWMol *m = MolBlockToMol(mb); MolOps::adjustQueryProperties(*m, ¶ms); MatchVectType match; ROMol *t = SmilesToMol("c1ccccc1Cl"); TEST_ASSERT(SubstructMatch(*t, *m, match)); delete t; // should match (mapped!) t = SmilesToMol("c1c(Cl)cc(Cl)cc1Cl"); TEST_ASSERT(SubstructMatch(*t, *m, match)); delete m; delete t; } #endif { // make atoms generic std::string smiles = "C1CC1CC"; ROMol *qm = SmilesToMol(smiles); TEST_ASSERT(qm); TEST_ASSERT(qm->getNumAtoms() == 5); { MolOps::AdjustQueryParameters params; params.makeAtomsGeneric = true; ROMol *aqm = MolOps::adjustQueryProperties(*qm, ¶ms); // std::cerr << MolToSmarts(*aqm) << std::endl; TEST_ASSERT(aqm); TEST_ASSERT(aqm->getNumAtoms() == qm->getNumAtoms()); { MatchVectType match; TEST_ASSERT(SubstructMatch(*qm, *aqm, match)); std::string smiles = "O1CN1NN"; ROMol *m = SmilesToMol(smiles); // std::cerr << MolToSmiles(*m) << std::endl; TEST_ASSERT(m); TEST_ASSERT(!SubstructMatch(*m, *qm, match)); TEST_ASSERT(SubstructMatch(*m, *aqm, match)); delete m; } delete aqm; } { MolOps::AdjustQueryParameters params; params.makeAtomsGeneric = true; params.makeAtomsGenericFlags = MolOps::ADJUST_IGNORECHAINS; ROMol *aqm = MolOps::adjustQueryProperties(*qm, ¶ms); TEST_ASSERT(aqm); TEST_ASSERT(aqm->getNumAtoms() == qm->getNumAtoms()); { MatchVectType match; TEST_ASSERT(SubstructMatch(*qm, *aqm, match)); std::string smiles = "O1CN1NN"; ROMol *m = SmilesToMol(smiles); TEST_ASSERT(m); TEST_ASSERT(!SubstructMatch(*m, *qm, match)); TEST_ASSERT(!SubstructMatch(*m, *aqm, match)); delete m; smiles = "O1CN1CC"; m = SmilesToMol(smiles); TEST_ASSERT(m); TEST_ASSERT(!SubstructMatch(*m, *qm, match)); TEST_ASSERT(SubstructMatch(*m, *aqm, match)); delete m; } delete aqm; } { MolOps::AdjustQueryParameters params; params.makeAtomsGeneric = true; params.makeAtomsGenericFlags = MolOps::ADJUST_IGNORERINGS; ROMol *aqm = MolOps::adjustQueryProperties(*qm, ¶ms); TEST_ASSERT(aqm); TEST_ASSERT(aqm->getNumAtoms() == qm->getNumAtoms()); { MatchVectType match; TEST_ASSERT(SubstructMatch(*qm, *aqm, match)); std::string smiles = "O1CN1NN"; ROMol *m = SmilesToMol(smiles); TEST_ASSERT(m); TEST_ASSERT(!SubstructMatch(*m, *qm, match)); TEST_ASSERT(!SubstructMatch(*m, *aqm, match)); delete m; smiles = "C1CC1NN"; m = SmilesToMol(smiles); TEST_ASSERT(m); TEST_ASSERT(!SubstructMatch(*m, *qm, match)); TEST_ASSERT(SubstructMatch(*m, *aqm, match)); delete m; } delete aqm; } delete qm; } { // make bonds generic std::string smiles = "N1C=C1C=C"; ROMol *qm = SmilesToMol(smiles); TEST_ASSERT(qm); TEST_ASSERT(qm->getNumAtoms() == 5); { MolOps::AdjustQueryParameters params; params.makeBondsGeneric = true; ROMol *aqm = MolOps::adjustQueryProperties(*qm, ¶ms); TEST_ASSERT(aqm); TEST_ASSERT(aqm->getNumAtoms() == qm->getNumAtoms()); { MatchVectType match; TEST_ASSERT(SubstructMatch(*qm, *aqm, match)); std::string smiles = "N1=CC1=CC"; ROMol *m = SmilesToMol(smiles); TEST_ASSERT(m); TEST_ASSERT(!SubstructMatch(*m, *qm, match)); TEST_ASSERT(SubstructMatch(*m, *aqm, match)); delete m; } delete aqm; } { MolOps::AdjustQueryParameters params; params.makeBondsGeneric = true; params.makeBondsGenericFlags = MolOps::ADJUST_IGNORECHAINS; ROMol *aqm = MolOps::adjustQueryProperties(*qm, ¶ms); TEST_ASSERT(aqm); TEST_ASSERT(aqm->getNumAtoms() == qm->getNumAtoms()); { MatchVectType match; TEST_ASSERT(SubstructMatch(*qm, *aqm, match)); std::string smiles = "N1=CC1=C=C"; ROMol *m = SmilesToMol(smiles); TEST_ASSERT(m); TEST_ASSERT(!SubstructMatch(*m, *qm, match)); TEST_ASSERT(!SubstructMatch(*m, *aqm, match)); delete m; smiles = "N1=CC1C=C"; m = SmilesToMol(smiles); TEST_ASSERT(m); TEST_ASSERT(!SubstructMatch(*m, *qm, match)); TEST_ASSERT(SubstructMatch(*m, *aqm, match)); delete m; } delete aqm; } { MolOps::AdjustQueryParameters params; params.makeBondsGeneric = true; params.makeBondsGenericFlags = MolOps::ADJUST_IGNORERINGS; ROMol *aqm = MolOps::adjustQueryProperties(*qm, ¶ms); TEST_ASSERT(aqm); TEST_ASSERT(aqm->getNumAtoms() == qm->getNumAtoms()); { MatchVectType match; TEST_ASSERT(SubstructMatch(*qm, *aqm, match)); std::string smiles = "N1=CC1=C=C"; ROMol *m = SmilesToMol(smiles); TEST_ASSERT(m); TEST_ASSERT(!SubstructMatch(*m, *qm, match)); TEST_ASSERT(!SubstructMatch(*m, *aqm, match)); delete m; smiles = "N1C=C1CC#C"; m = SmilesToMol(smiles); TEST_ASSERT(m); TEST_ASSERT(!SubstructMatch(*m, *qm, match)); TEST_ASSERT(SubstructMatch(*m, *aqm, match)); delete m; } delete aqm; } delete qm; } { // heavy atom degree std::string smiles = "C1CC(*)C1*"; ROMol *qm = SmartsToMol(smiles); TEST_ASSERT(qm); TEST_ASSERT(qm->getNumAtoms() == 6); MolOps::AdjustQueryParameters params; params.adjustDegree = false; params.adjustHeavyDegree = true; ROMol *aqm = MolOps::adjustQueryProperties(*qm, ¶ms); TEST_ASSERT(aqm); TEST_ASSERT(aqm->getNumAtoms() == 6); { smiles = "C1CC(C)C1(C)"; ROMol *m = SmilesToMol(smiles); TEST_ASSERT(m); MatchVectType match; TEST_ASSERT(SubstructMatch(*m, *qm, match)); TEST_ASSERT(SubstructMatch(*m, *aqm, match)); delete m; } { smiles = "C1CC([2H])C1(C)"; ROMol *m = SmilesToMol(smiles); TEST_ASSERT(m); MatchVectType match; TEST_ASSERT(SubstructMatch(*m, *qm, match)); TEST_ASSERT(!SubstructMatch(*m, *aqm, match)); delete m; } delete qm; delete aqm; } { // ring-chain membership std::string smiles = "CC1CCC1"; ROMol *qm = SmartsToMol(smiles); TEST_ASSERT(qm); TEST_ASSERT(qm->getNumAtoms() == 5); MolOps::AdjustQueryParameters params; params.adjustRingChain = true; params.adjustDegree = false; ROMol *aqm = MolOps::adjustQueryProperties(*qm, ¶ms); TEST_ASSERT(aqm); TEST_ASSERT(aqm->getNumAtoms() == 5); { smiles = "C1CCC12CCC2"; ROMol *m = SmilesToMol(smiles); TEST_ASSERT(m); MatchVectType match; TEST_ASSERT(SubstructMatch(*m, *qm, match)); TEST_ASSERT(!SubstructMatch(*m, *aqm, match)); delete m; } { smiles = "C1CCC1CCC"; ROMol *m = SmilesToMol(smiles); TEST_ASSERT(m); MatchVectType match; TEST_ASSERT(SubstructMatch(*m, *qm, match)); TEST_ASSERT(SubstructMatch(*m, *aqm, match)); delete m; } delete qm; delete aqm; } BOOST_LOG(rdInfoLog) << "Finished" << std::endl; } void testGithubIssue678() { BOOST_LOG(rdInfoLog) << "-----------------------\n Testing github issue 678: " "failure in AddHs when addCoords is true and coords are all zero" << std::endl; { std::string smiles = "CC"; RWMol *m = SmilesToMol(smiles); TEST_ASSERT(m); auto *conf = new Conformer(2); m->addConformer(conf); MolOps::addHs(*m, false, true); TEST_ASSERT(m->getNumAtoms() == 8); delete m; } { // single connected atom with degenerate coords std::string mb = "example\n" " Mrv0541 12171503572D \n" "\n" " 7 8 0 0 0 0 999 V2000\n" " -3.5063 2.5339 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 " "0\n" " -3.5063 1.7089 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 " "0\n" " -2.6813 2.5339 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 " "0\n" " -2.6813 1.7089 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 " "0\n" " -1.8563 2.5339 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 " "0\n" " -1.8563 1.7089 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 " "0\n" " -1.8563 2.5339 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 " "0\n" " 1 2 1 0 0 0 0\n" " 1 3 1 0 0 0 0\n" " 3 4 1 0 0 0 0\n" " 2 4 1 0 0 0 0\n" " 3 5 1 0 0 0 0\n" " 5 6 1 0 0 0 0\n" " 4 6 1 0 0 0 0\n" " 5 7 1 0 0 0 0\n" "M END\n"; RWMol *m = MolBlockToMol(mb); TEST_ASSERT(m); MolOps::addHs(*m, false, true); TEST_ASSERT(m->getNumAtoms() == 19); delete m; } { // doubly connected atom(s) with degenerate coords std::string mb = "example\n" " Mrv0541 12171503572D \n" "\n" " 7 8 0 0 0 0 999 V2000\n" " -3.5063 2.5339 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 " "0\n" " -3.5063 2.5339 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 " "0\n" " -2.6813 2.5339 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 " "0\n" " -2.6813 1.7089 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 " "0\n" " -1.8563 2.5339 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 " "0\n" " -1.8563 1.7089 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 " "0\n" " -1.2729 3.1173 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 " "0\n" " 1 2 1 0 0 0 0\n" " 1 3 1 0 0 0 0\n" " 3 4 1 0 0 0 0\n" " 2 4 1 0 0 0 0\n" " 3 5 1 0 0 0 0\n" " 5 6 1 0 0 0 0\n" " 4 6 1 0 0 0 0\n" " 5 7 1 0 0 0 0\n" "M END\n"; RWMol *m = MolBlockToMol(mb); TEST_ASSERT(m); MolOps::addHs(*m, false, true); TEST_ASSERT(m->getNumAtoms() == 19); delete m; } { // triply connected atom(s) with degenerate coords std::string mb = "example\n" " Mrv0541 12171503572D \n" "\n" " 7 8 0 0 0 0 999 V2000\n" " -3.5063 2.5339 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 " "0\n" " -3.5063 1.7089 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 " "0\n" " -3.5063 2.5339 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 " "0\n" " -3.5063 2.5339 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 " "0\n" " -1.8563 2.5339 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 " "0\n" " -1.8563 1.7089 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 " "0\n" " -1.2729 3.1173 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 " "0\n" " 1 2 1 0 0 0 0\n" " 1 3 1 0 0 0 0\n" " 3 4 1 0 0 0 0\n" " 2 4 1 0 0 0 0\n" " 3 5 1 0 0 0 0\n" " 5 6 1 0 0 0 0\n" " 4 6 1 0 0 0 0\n" " 5 7 1 0 0 0 0\n" "M END\n"; RWMol *m = MolBlockToMol(mb); TEST_ASSERT(m); MolOps::addHs(*m, false, true); TEST_ASSERT(m->getNumAtoms() == 19); delete m; } BOOST_LOG(rdInfoLog) << "Finished" << std::endl; } void testGithubIssue717() { BOOST_LOG(rdInfoLog) << "-----------------------\n Testing github issue 717: " "AddHs cip rank is declared should be unsigned int" << std::endl; { // single connected atom with degenerate coords std::string mb = "mol\n" " Mrv1561 01051606293D\n" "\n" " 4 3 0 0 0 0 999 V2000\n" " -0.0080 -0.0000 -0.0004 C 0 0 0 0 0 0 0 0 0 0 0 " "0\n" " 1.5343 -0.0050 0.0032 C 0 0 1 0 0 0 0 0 0 0 0 " "0\n" " 2.1517 -0.8276 1.4332 Cl 0 0 0 0 0 0 0 0 0 0 0 " "0\n" " 2.0123 -0.6447 -1.1142 F 0 0 0 0 0 0 0 0 0 0 0 " "0\n" " 2 3 1 0 0 0 0\n" " 2 4 1 0 0 0 0\n" " 2 1 1 0 0 0 0\n" "M END\n"; RWMol *m = MolBlockToMol(mb); TEST_ASSERT(m); MolOps::assignChiralTypesFrom3D(*m); MolOps::assignStereochemistry(*m, true, true); MolOps::addHs(*m, false, true); TEST_ASSERT(m->getNumAtoms() == 8); delete m; } BOOST_LOG(rdInfoLog) << "Finished" << std::endl; } void testPotentialStereoBonds() { BOOST_LOG(rdInfoLog) << "-----------------------\n Testing findPotentialStereoBonds" << std::endl; { // starting point: full sanitization auto m1 = "BrC(=NN=c1nn[nH][nH]1)c1ccncc1"_smiles; TEST_ASSERT(m1); std::string smiles = "Br/C(=N\\N=c1/nn[nH][nH]1)c1ccncc1"; // possible problem reported by // Steve Roughley ROMol *m = SmilesToMol(smiles); // m->updatePropertyCache(false); // m->debugMol(std::cerr); TEST_ASSERT(m); TEST_ASSERT(m->getNumAtoms() == 15); TEST_ASSERT(m->getBondWithIdx(1)->getBondType() == Bond::DOUBLE); TEST_ASSERT(m->getBondWithIdx(1)->getStereoAtoms().size() == 2); TEST_ASSERT(m->getBondWithIdx(3)->getBondType() == Bond::DOUBLE); TEST_ASSERT(m->getBondWithIdx(3)->getStereoAtoms().size() == 2); delete m; // partial sanitization: m = SmilesToMol(smiles, false, false); TEST_ASSERT(m); m->updatePropertyCache(true); MolOps::findSSSR(*m); MolOps::findPotentialStereoBonds(*m, false); TEST_ASSERT(m->getNumAtoms() == 15); TEST_ASSERT(m->getBondWithIdx(1)->getBondType() == Bond::DOUBLE); TEST_ASSERT(m->getBondWithIdx(1)->getStereoAtoms().size() == 2); TEST_ASSERT(m->getBondWithIdx(3)->getBondType() == Bond::DOUBLE); TEST_ASSERT(m->getBondWithIdx(3)->getStereoAtoms().size() == 2); delete m; } // this next block is for github1230: FindPotentialStereoBonds() failure { // simple std::string smiles = "CC=CC"; ROMol *m = SmilesToMol(smiles); TEST_ASSERT(m); TEST_ASSERT(m->getNumAtoms() == 4); TEST_ASSERT(m->getBondWithIdx(1)->getBondType() == Bond::DOUBLE); TEST_ASSERT(m->getBondWithIdx(1)->getStereo() == Bond::STEREONONE); MolOps::findPotentialStereoBonds(*m, true); TEST_ASSERT(m->getBondWithIdx(1)->getStereo() == Bond::STEREOANY); delete m; } { // simple2 std::string smiles = "CC=C(C)C"; ROMol *m = SmilesToMol(smiles); TEST_ASSERT(m); TEST_ASSERT(m->getNumAtoms() == 5); TEST_ASSERT(m->getBondWithIdx(1)->getBondType() == Bond::DOUBLE); TEST_ASSERT(m->getBondWithIdx(1)->getStereo() == Bond::STEREONONE); MolOps::findPotentialStereoBonds(*m, true); TEST_ASSERT(m->getBondWithIdx(1)->getStereo() == Bond::STEREONONE); delete m; } { // the real problem std::string smiles = "CC/C=C/C(\\C=C/CC)=C(CC)CO"; ROMol *m = SmilesToMol(smiles); TEST_ASSERT(m); TEST_ASSERT(m->getNumAtoms() == 14); TEST_ASSERT(m->getBondWithIdx(2)->getBondType() == Bond::DOUBLE); TEST_ASSERT(m->getBondWithIdx(2)->getStereo() == Bond::STEREOE); TEST_ASSERT(m->getBondWithIdx(5)->getBondType() == Bond::DOUBLE); TEST_ASSERT(m->getBondWithIdx(5)->getStereo() == Bond::STEREOZ); TEST_ASSERT(m->getBondWithIdx(8)->getBondType() == Bond::DOUBLE); TEST_ASSERT(m->getBondWithIdx(8)->getStereo() == Bond::STEREONONE); MolOps::findPotentialStereoBonds(*m, true); TEST_ASSERT(m->getBondWithIdx(2)->getBondType() == Bond::DOUBLE); TEST_ASSERT(m->getBondWithIdx(2)->getStereo() == Bond::STEREOANY); TEST_ASSERT(m->getBondWithIdx(5)->getBondType() == Bond::DOUBLE); TEST_ASSERT(m->getBondWithIdx(5)->getStereo() == Bond::STEREOANY); TEST_ASSERT(m->getBondWithIdx(8)->getBondType() == Bond::DOUBLE); TEST_ASSERT(m->getBondWithIdx(8)->getStereo() == Bond::STEREOANY); delete m; } { // repeat the real problem, but set the cleanIt argument to false std::string smiles = "CC/C=C/C(\\C=C/CC)=C(CC)CO"; ROMol *m = SmilesToMol(smiles); TEST_ASSERT(m); TEST_ASSERT(m->getNumAtoms() == 14); TEST_ASSERT(m->getBondWithIdx(2)->getBondType() == Bond::DOUBLE); TEST_ASSERT(m->getBondWithIdx(2)->getStereo() == Bond::STEREOE); TEST_ASSERT(m->getBondWithIdx(5)->getBondType() == Bond::DOUBLE); TEST_ASSERT(m->getBondWithIdx(5)->getStereo() == Bond::STEREOZ); TEST_ASSERT(m->getBondWithIdx(8)->getBondType() == Bond::DOUBLE); TEST_ASSERT(m->getBondWithIdx(8)->getStereo() == Bond::STEREONONE); MolOps::findPotentialStereoBonds(*m, false); TEST_ASSERT(m->getBondWithIdx(2)->getBondType() == Bond::DOUBLE); TEST_ASSERT(m->getBondWithIdx(2)->getStereo() == Bond::STEREOE); TEST_ASSERT(m->getBondWithIdx(5)->getBondType() == Bond::DOUBLE); TEST_ASSERT(m->getBondWithIdx(5)->getStereo() == Bond::STEREOZ); TEST_ASSERT(m->getBondWithIdx(8)->getBondType() == Bond::DOUBLE); TEST_ASSERT(m->getBondWithIdx(8)->getStereo() == Bond::STEREOANY); delete m; } { // just do document that we still don't do this one, which is much harder std::string smiles = "CC/C=C/C(/C=C/CC)=C(CC)CO"; ROMol *m = SmilesToMol(smiles); TEST_ASSERT(m); TEST_ASSERT(m->getNumAtoms() == 14); TEST_ASSERT(m->getBondWithIdx(2)->getBondType() == Bond::DOUBLE); TEST_ASSERT(m->getBondWithIdx(2)->getStereo() == Bond::STEREOE); TEST_ASSERT(m->getBondWithIdx(5)->getBondType() == Bond::DOUBLE); TEST_ASSERT(m->getBondWithIdx(5)->getStereo() == Bond::STEREOE); TEST_ASSERT(m->getBondWithIdx(8)->getBondType() == Bond::DOUBLE); TEST_ASSERT(m->getBondWithIdx(8)->getStereo() == Bond::STEREONONE); MolOps::findPotentialStereoBonds(*m, true); TEST_ASSERT(m->getBondWithIdx(2)->getBondType() == Bond::DOUBLE); TEST_ASSERT(m->getBondWithIdx(2)->getStereo() == Bond::STEREOANY); TEST_ASSERT(m->getBondWithIdx(5)->getBondType() == Bond::DOUBLE); TEST_ASSERT(m->getBondWithIdx(5)->getStereo() == Bond::STEREOANY); TEST_ASSERT(m->getBondWithIdx(8)->getBondType() == Bond::DOUBLE); TEST_ASSERT(m->getBondWithIdx(8)->getStereo() == Bond::STEREONONE); delete m; } BOOST_LOG(rdInfoLog) << "Finished" << std::endl; } void testSetBondStereo() { BOOST_LOG(rdInfoLog) << "-----------------------\n Testing " "Bond::setStereo(Bond::STEREOCIS / Bond::STEREOTRANS)" << std::endl; // tests to make sure neighboring bond stereo is handled properly { const char *smiles[] = {"CC=CC", "CC=C/C=C/C", "CC=C/C=C\\C", "CC=C\\C=C/C", "CC=C\\C=C\\C", "C(C)=CC", "C(C)=C/C=C/C", "C(C)=C/C=C\\C", "C(C)=C\\C=C/C", "C(C)=C\\C=C\\C"}; const Bond::BondStereo stereos[] = {Bond::STEREOCIS, Bond::STEREOTRANS}; const Bond::BondStereo ezstros[] = {Bond::STEREOZ, Bond::STEREOE}; for (auto &smile : smiles) { ROMol *m = SmilesToMol(smile); MolOps::findPotentialStereoBonds(*m); Bond *bond = m->getBondWithIdx(1); for (size_t j = 0; j < 2; ++j) { Bond::BondStereo desired_stereo = stereos[j]; bond->setStereo(desired_stereo); bool doIsomericSmiles = true; bool doKekule = false; int rootedAtAtom = -1; bool canonical = false; std::string isosmi = MolToSmiles(*m, doIsomericSmiles, doKekule, rootedAtAtom, canonical); ROMol *isomol = SmilesToMol(isosmi); Bond *isobond = isomol->getBondWithIdx(1); const Bond::BondStereo expected_ez_stereo = ezstros[j]; TEST_ASSERT(isobond->getStereo() == expected_ez_stereo); std::string round_trip_isosmi = MolToSmiles( *m, doIsomericSmiles, doKekule, rootedAtAtom, canonical); TEST_ASSERT(isosmi == round_trip_isosmi); BOOST_LOG(rdInfoLog) << isosmi << " == " << round_trip_isosmi << " " << desired_stereo << std::endl; delete isomol; } delete m; } } // tests enumerating all possible smiles with halogens still yield // the same isomeric canonical smiles strings. { const char *smiles[] = {"ClC=CF", "FC=CCl", "C(Cl)=CF", "C(F)=CCl"}; const Bond::BondStereo stereos[] = {Bond::STEREOCIS, Bond::STEREOTRANS}; for (auto desired_stereo : stereos) { std::string refSmiles; for (auto &smile : smiles) { ROMol *m = SmilesToMol(smile); MolOps::findPotentialStereoBonds(*m); TEST_ASSERT(m->getNumAtoms() == 4); Bond *doubleBond = m->getBondWithIdx(1); doubleBond->setStereo(desired_stereo); bool doIsomericSmiles = true; std::string isocansmi = MolToSmiles(*m, doIsomericSmiles); if (refSmiles.empty()) { refSmiles = isocansmi; } BOOST_LOG(rdInfoLog) << refSmiles << " == " << isocansmi << " " << desired_stereo << std::endl; TEST_ASSERT(refSmiles == isocansmi); delete m; } } } } void testBondSetStereoAtoms() { BOOST_LOG(rdInfoLog) << "-----------------------\n Testing Bond::setStereoAtoms(...)" << std::endl; // tests to make sure setStereoAtoms works as expected { bool doIsomericSmiles = true; std::string unspec_smiles = "FC(Cl)=C(Br)I"; ROMol *m = SmilesToMol(unspec_smiles); Bond *doubleBond = m->getBondWithIdx(2); TEST_ASSERT(doubleBond->getBondType() == 2); doubleBond->setStereoAtoms(0, 4); doubleBond->setStereo(Bond::STEREOCIS); BOOST_LOG(rdInfoLog) << MolToSmiles(*m, doIsomericSmiles) << std::endl; TEST_ASSERT(MolToSmiles(*m, doIsomericSmiles) == "F/C(Cl)=C(\\Br)I"); // this should be the same as the previous doubleBond->setStereoAtoms(0, 5); doubleBond->setStereo(Bond::STEREOTRANS); BOOST_LOG(rdInfoLog) << MolToSmiles(*m, doIsomericSmiles) << std::endl; TEST_ASSERT(MolToSmiles(*m, doIsomericSmiles) == "F/C(Cl)=C(\\Br)I"); doubleBond->setStereoAtoms(0, 4); doubleBond->setStereo(Bond::STEREOTRANS); BOOST_LOG(rdInfoLog) << MolToSmiles(*m, doIsomericSmiles) << std::endl; TEST_ASSERT(MolToSmiles(*m, doIsomericSmiles) == "F/C(Cl)=C(/Br)I"); // this should be the same as the previous doubleBond->setStereoAtoms(0, 5); doubleBond->setStereo(Bond::STEREOCIS); BOOST_LOG(rdInfoLog) << MolToSmiles(*m, doIsomericSmiles) << std::endl; TEST_ASSERT(MolToSmiles(*m, doIsomericSmiles) == "F/C(Cl)=C(/Br)I"); doubleBond->setStereoAtoms(3, 4); doubleBond->setStereo(Bond::STEREOTRANS); BOOST_LOG(rdInfoLog) << MolToSmiles(*m, doIsomericSmiles) << std::endl; TEST_ASSERT(MolToSmiles(*m, doIsomericSmiles) == "F/C(Cl)=C(\\Br)I"); // this should be the same as the previous doubleBond->setStereoAtoms(3, 5); doubleBond->setStereo(Bond::STEREOCIS); BOOST_LOG(rdInfoLog) << MolToSmiles(*m, doIsomericSmiles) << std::endl; TEST_ASSERT(MolToSmiles(*m, doIsomericSmiles) == "F/C(Cl)=C(\\Br)I"); doubleBond->setStereoAtoms(3, 4); doubleBond->setStereo(Bond::STEREOCIS); BOOST_LOG(rdInfoLog) << MolToSmiles(*m, doIsomericSmiles) << std::endl; TEST_ASSERT(MolToSmiles(*m, doIsomericSmiles) == "F/C(Cl)=C(/Br)I"); // this should be the same as the previous doubleBond->setStereoAtoms(3, 5); doubleBond->setStereo(Bond::STEREOTRANS); BOOST_LOG(rdInfoLog) << MolToSmiles(*m, doIsomericSmiles) << std::endl; TEST_ASSERT(MolToSmiles(*m, doIsomericSmiles) == "F/C(Cl)=C(/Br)I"); delete m; ; } } void testGithubIssue754() { BOOST_LOG(rdInfoLog) << "-----------------------\n Testing github #754 : " "loss of double bond geometry with removeHs" << std::endl; { // starting point: full sanitization std::string smiles = "[H]C([H])([H])/C([H])=C(/[H])C([H])([H])[H]"; // possible problem // reported by // Steve Roughley RWMol *m = SmilesToMol(smiles, false, false); TEST_ASSERT(m); MolOps::sanitizeMol(*m); MolOps::assignStereochemistry(*m, true, true); TEST_ASSERT(m->getNumAtoms() == 12); TEST_ASSERT(m->getBondWithIdx(5)->getBondType() == Bond::DOUBLE); TEST_ASSERT(m->getBondWithIdx(5)->getStereo() == Bond::STEREOZ); delete m; m = SmilesToMol(smiles); TEST_ASSERT(m); TEST_ASSERT(m->getNumAtoms() == 4); TEST_ASSERT(m->getBondWithIdx(1)->getBondType() == Bond::DOUBLE); TEST_ASSERT(m->getBondWithIdx(1)->getStereo() == Bond::STEREOZ); delete m; } { // another basic test std::string smiles = "[H]/C(C)=C/C"; RWMol *m = SmilesToMol(smiles); TEST_ASSERT(m); TEST_ASSERT(m->getNumAtoms() == 4); TEST_ASSERT(m->getBondBetweenAtoms(0, 2)->getBondType() == Bond::DOUBLE); TEST_ASSERT(m->getBondBetweenAtoms(0, 2)->getStereo() == Bond::STEREOZ); delete m; } { // H following the C: std::string smiles = "CC(\\[H])=C/C"; RWMol *m = SmilesToMol(smiles); TEST_ASSERT(m); TEST_ASSERT(m->getNumAtoms() == 4); TEST_ASSERT(m->getBondBetweenAtoms(1, 2)->getBondType() == Bond::DOUBLE); TEST_ASSERT(m->getBondBetweenAtoms(1, 2)->getStereo() == Bond::STEREOZ); delete m; } { // bond dir already set : std::string smiles = "[H]/C(/C)=C\\C"; RWMol *m = SmilesToMol(smiles); TEST_ASSERT(m); TEST_ASSERT(m->getNumAtoms() == 4); TEST_ASSERT(m->getBondBetweenAtoms(0, 2)->getBondType() == Bond::DOUBLE); TEST_ASSERT(m->getBondBetweenAtoms(0, 2)->getStereo() == Bond::STEREOE); delete m; } { // chained bonds : std::string smiles = "[H]/C(C=C/C)=C\\C"; RWMol *m = SmilesToMol(smiles); TEST_ASSERT(m); TEST_ASSERT(m->getNumAtoms() == 6); TEST_ASSERT(m->getBondBetweenAtoms(0, 4)->getBondType() == Bond::DOUBLE); TEST_ASSERT(m->getBondBetweenAtoms(0, 4)->getStereo() == Bond::STEREOE); TEST_ASSERT(m->getBondBetweenAtoms(1, 2)->getBondType() == Bond::DOUBLE); TEST_ASSERT(m->getBondBetweenAtoms(1, 2)->getStereo() == Bond::STEREOE); delete m; } BOOST_LOG(rdInfoLog) << "Finished" << std::endl; } void testGithubIssue805() { BOOST_LOG(rdInfoLog) << "-----------------------\n Testing github #805 : " "Pre-condition Violation: bad bond type" << std::endl; { std::string pathName = getenv("RDBASE"); pathName += "/Code/GraphMol/test_data/"; ROMol *m = MolFileToMol(pathName + "pubchem_87396055.sdf"); TEST_ASSERT(m); TEST_ASSERT(m->getNumAtoms() == 20); TEST_ASSERT(m->getBondBetweenAtoms(2, 6)->getBondType() == Bond::SINGLE); TEST_ASSERT(m->getAtomWithIdx(2)->getFormalCharge() == 1); TEST_ASSERT(m->getAtomWithIdx(6)->getFormalCharge() == -1); TEST_ASSERT(m->getBondBetweenAtoms(2, 9)->getBondType() == Bond::DOUBLE); TEST_ASSERT(m->getBondBetweenAtoms(2, 9)->getStereo() != Bond::STEREONONE); TEST_ASSERT(m->getBondBetweenAtoms(3, 10)->getBondType() == Bond::DOUBLE); TEST_ASSERT(m->getBondBetweenAtoms(3, 10)->getStereo() != Bond::STEREONONE); std::string smi = MolToSmiles(*m, true); TEST_ASSERT(smi == "CCO/[P+]([O-])=C1\\CSC(c2cccs2)\\C1=[P+](\\[O-])OCC"); delete m; } { std::string smi = "O=P(/O)=C/C"; ROMol *m = SmilesToMol(smi); TEST_ASSERT(m); TEST_ASSERT(m->getNumAtoms() == 5); TEST_ASSERT(m->getBondBetweenAtoms(0, 1)->getBondType() == Bond::SINGLE); TEST_ASSERT(m->getAtomWithIdx(1)->getFormalCharge() == 1); TEST_ASSERT(m->getAtomWithIdx(0)->getFormalCharge() == -1); TEST_ASSERT(m->getBondBetweenAtoms(1, 3)->getBondType() == Bond::DOUBLE); TEST_ASSERT(m->getBondBetweenAtoms(1, 3)->getStereo() != Bond::STEREONONE); smi = MolToSmiles(*m, true); TEST_ASSERT(smi == "C/C=[P+](/[O-])O"); delete m; } BOOST_LOG(rdInfoLog) << "Finished" << std::endl; } void testGithubIssue518() { BOOST_LOG(rdInfoLog) << "-----------------------\n Testing github #518 : " "Rings containing all dummy atoms with single bonds " "are flagged as aromatic" << std::endl; { std::string smi = "*-1-*-*-*-1"; ROMol *m = SmilesToMol(smi); TEST_ASSERT(m); TEST_ASSERT(m->getNumAtoms() == 4); TEST_ASSERT(!m->getBondWithIdx(0)->getIsAromatic()); TEST_ASSERT(m->getBondWithIdx(0)->getBondType() == Bond::SINGLE); TEST_ASSERT(!m->getAtomWithIdx(0)->getIsAromatic()); delete m; } { // in this case we leave it aromatic since it's all dummies std::string smi = "*:1:*:*:*:1"; ROMol *m = SmilesToMol(smi); TEST_ASSERT(m); TEST_ASSERT(m->getNumAtoms() == 4); TEST_ASSERT(!m->getBondWithIdx(0)->getIsAromatic()); TEST_ASSERT(m->getBondWithIdx(0)->getBondType() == Bond::AROMATIC); TEST_ASSERT(!m->getAtomWithIdx(0)->getIsAromatic()); delete m; } { std::string smi = "*-1-*-C-*-*-*-1"; ROMol *m = SmilesToMol(smi); TEST_ASSERT(m); TEST_ASSERT(m->getNumAtoms() == 6); TEST_ASSERT(!m->getBondWithIdx(0)->getIsAromatic()); TEST_ASSERT(m->getBondWithIdx(0)->getBondType() == Bond::SINGLE); TEST_ASSERT(!m->getAtomWithIdx(0)->getIsAromatic()); delete m; } { std::string smi = "C1=CC=*2*(=C1)*1=CC=CC=*1*1=CC=CC=*21"; ROMol *m = SmilesToMol(smi); TEST_ASSERT(m); TEST_ASSERT(m->getNumAtoms() == 18); TEST_ASSERT(!m->getBondBetweenAtoms(4, 6)->getIsAromatic()); TEST_ASSERT(m->getBondBetweenAtoms(4, 6)->getBondType() == Bond::SINGLE); TEST_ASSERT(!m->getBondBetweenAtoms(11, 12)->getIsAromatic()); TEST_ASSERT(m->getBondBetweenAtoms(11, 12)->getBondType() == Bond::SINGLE); TEST_ASSERT(!m->getBondBetweenAtoms(3, 17)->getIsAromatic()); TEST_ASSERT(m->getBondBetweenAtoms(3, 17)->getBondType() == Bond::SINGLE); delete m; } BOOST_LOG(rdInfoLog) << "Finished" << std::endl; } void testSimpleAromaticity() { BOOST_LOG(rdInfoLog) << "-----------------------\n Testing simple aromaticity" << std::endl; { std::string smiles = "c1ccccc1"; RWMol *m = SmilesToMol(smiles); TEST_ASSERT(m); TEST_ASSERT(m->getBondWithIdx(0)->getIsAromatic() == true); TEST_ASSERT(m->getAtomWithIdx(0)->getIsAromatic() == true); MolOps::Kekulize(*m, true); TEST_ASSERT(m->getBondWithIdx(0)->getIsAromatic() == false); TEST_ASSERT(m->getAtomWithIdx(0)->getIsAromatic() == false); MolOps::setAromaticity(*m, MolOps::AROMATICITY_SIMPLE); TEST_ASSERT(m->getBondWithIdx(0)->getIsAromatic() == true); TEST_ASSERT(m->getAtomWithIdx(0)->getIsAromatic() == true); delete m; } { std::string smiles = "c1[nH]ccc1"; RWMol *m = SmilesToMol(smiles); TEST_ASSERT(m); TEST_ASSERT(m->getBondWithIdx(0)->getIsAromatic() == true); TEST_ASSERT(m->getAtomWithIdx(0)->getIsAromatic() == true); MolOps::Kekulize(*m, true); TEST_ASSERT(m->getBondWithIdx(0)->getIsAromatic() == false); TEST_ASSERT(m->getAtomWithIdx(0)->getIsAromatic() == false); MolOps::setAromaticity(*m, MolOps::AROMATICITY_SIMPLE); TEST_ASSERT(m->getBondWithIdx(0)->getIsAromatic() == true); TEST_ASSERT(m->getAtomWithIdx(0)->getIsAromatic() == true); delete m; } { // ring size constraints std::string smiles = "c1cccoocc1"; RWMol *m = SmilesToMol(smiles); TEST_ASSERT(m); TEST_ASSERT(m->getBondWithIdx(0)->getIsAromatic() == true); TEST_ASSERT(m->getAtomWithIdx(0)->getIsAromatic() == true); MolOps::Kekulize(*m, true); TEST_ASSERT(m->getBondWithIdx(0)->getIsAromatic() == false); TEST_ASSERT(m->getAtomWithIdx(0)->getIsAromatic() == false); MolOps::setAromaticity(*m, MolOps::AROMATICITY_SIMPLE); TEST_ASSERT(m->getBondWithIdx(0)->getIsAromatic() == false); TEST_ASSERT(m->getAtomWithIdx(0)->getIsAromatic() == false); delete m; } { // ring size constraints std::string smiles = "c1coo1"; RWMol *m = SmilesToMol(smiles); TEST_ASSERT(m); TEST_ASSERT(m->getBondWithIdx(0)->getIsAromatic() == true); TEST_ASSERT(m->getAtomWithIdx(0)->getIsAromatic() == true); MolOps::Kekulize(*m, true); TEST_ASSERT(m->getBondWithIdx(0)->getIsAromatic() == false); TEST_ASSERT(m->getAtomWithIdx(0)->getIsAromatic() == false); MolOps::setAromaticity(*m, MolOps::AROMATICITY_SIMPLE); TEST_ASSERT(m->getBondWithIdx(0)->getIsAromatic() == false); TEST_ASSERT(m->getAtomWithIdx(0)->getIsAromatic() == false); delete m; } { // fused rings are not considered std::string smiles = "C1=CC2=CC=CC=CC2=C1"; // azulene RWMol *m = SmilesToMol(smiles); TEST_ASSERT(m); TEST_ASSERT(m->getBondWithIdx(0)->getIsAromatic() == true); TEST_ASSERT(m->getAtomWithIdx(0)->getIsAromatic() == true); MolOps::Kekulize(*m, true); TEST_ASSERT(m->getBondWithIdx(0)->getIsAromatic() == false); TEST_ASSERT(m->getAtomWithIdx(0)->getIsAromatic() == false); MolOps::setAromaticity(*m, MolOps::AROMATICITY_SIMPLE); TEST_ASSERT(m->getBondWithIdx(0)->getIsAromatic() == false); TEST_ASSERT(m->getAtomWithIdx(0)->getIsAromatic() == false); delete m; } BOOST_LOG(rdInfoLog) << "Finished" << std::endl; } //! really dumb aromaticity: any conjugated ring bond is aromatic int customAromaticity(RWMol &m) { m.updatePropertyCache(); MolOps::setConjugation(m); MolOps::fastFindRings(m); int res = 0; for (ROMol::BondIterator bIt = m.beginBonds(); bIt != m.endBonds(); ++bIt) { if ((*bIt)->getIsConjugated() && queryIsBondInRing(*bIt)) { (*bIt)->setIsAromatic(true); (*bIt)->getBeginAtom()->setIsAromatic(true); (*bIt)->getEndAtom()->setIsAromatic(true); ++res; } } return res; } void testCustomAromaticity() { BOOST_LOG(rdInfoLog) << "-----------------------\n Testing custom aromaticity" << std::endl; { std::string smiles = "C1=CC=CC=C1"; RWMol *m = SmilesToMol(smiles, 0, false); TEST_ASSERT(m); TEST_ASSERT(m->getBondWithIdx(0)->getIsAromatic() == false); TEST_ASSERT(m->getAtomWithIdx(0)->getIsAromatic() == false); MolOps::setAromaticity(*m, MolOps::AROMATICITY_CUSTOM, customAromaticity); TEST_ASSERT(m->getBondWithIdx(0)->getIsAromatic() == true); TEST_ASSERT(m->getAtomWithIdx(0)->getIsAromatic() == true); delete m; } { std::string smiles = "C1CC=CC=C1"; RWMol *m = SmilesToMol(smiles, 0, false); TEST_ASSERT(m); TEST_ASSERT(m->getBondWithIdx(0)->getIsAromatic() == false); TEST_ASSERT(m->getAtomWithIdx(0)->getIsAromatic() == false); MolOps::setAromaticity(*m, MolOps::AROMATICITY_CUSTOM, customAromaticity); TEST_ASSERT(m->getBondWithIdx(0)->getIsAromatic() == false); TEST_ASSERT(m->getAtomWithIdx(0)->getIsAromatic() == false); TEST_ASSERT(m->getBondWithIdx(2)->getIsAromatic() == true); TEST_ASSERT(m->getAtomWithIdx(2)->getIsAromatic() == true); delete m; } BOOST_LOG(rdInfoLog) << "Finished" << std::endl; } void testGithubIssue1730() { BOOST_LOG(rdInfoLog) << "-----------------------\n Testing github issue " "#1730: setAromaticity() should work even if " "there are aromatic atoms present" << std::endl; { std::string smiles = "C1=CC=CC=C1-c2ccccc2"; RWMol *m = SmilesToMol(smiles, 0, false); m->updatePropertyCache(); TEST_ASSERT(m); TEST_ASSERT(m->getBondWithIdx(0)->getIsAromatic() == false); TEST_ASSERT(m->getAtomWithIdx(0)->getIsAromatic() == false); TEST_ASSERT(m->getBondWithIdx(6)->getIsAromatic() == true); TEST_ASSERT(m->getAtomWithIdx(6)->getIsAromatic() == true); MolOps::setAromaticity(*m); TEST_ASSERT(m->getBondWithIdx(0)->getIsAromatic() == true); TEST_ASSERT(m->getAtomWithIdx(0)->getIsAromatic() == true); delete m; } BOOST_LOG(rdInfoLog) << "Finished" << std::endl; } void testKekulizeErrorReporting() { BOOST_LOG(rdInfoLog) << "-----------------------\n Testing error reporting for kekulization" << std::endl; std::stringstream sstrm; rdErrorLog->SetTee(sstrm); { sstrm.str(""); std::string smi = "c1ccccc1"; ROMol *m = SmilesToMol(smi); TEST_ASSERT(m); TEST_ASSERT(sstrm.str() == ""); delete m; } { sstrm.str(""); std::string smi = "c1cccc1"; ROMol *m; try { m = SmilesToMol(smi); } catch (MolSanitizeException &) { m = nullptr; } TEST_ASSERT(m == nullptr); TEST_ASSERT(sstrm.str().find("0 1 2 3 4") != std::string::npos); delete m; } { sstrm.str(""); std::string smi = "c1ccccc1.c1cccc1"; ROMol *m; try { m = SmilesToMol(smi); } catch (MolSanitizeException &) { m = nullptr; } TEST_ASSERT(m == nullptr); TEST_ASSERT(sstrm.str().find("6 7 8 9 10") != std::string::npos); delete m; } { sstrm.str(""); std::string smi = "c1cccc1.c1cccc1"; ROMol *m; try { m = SmilesToMol(smi); } catch (MolSanitizeException &) { m = nullptr; } TEST_ASSERT(m == nullptr); TEST_ASSERT(sstrm.str().find("0 1 2 3 4") != std::string::npos); delete m; } rdErrorLog->ClearTee(); BOOST_LOG(rdInfoLog) << "Finished" << std::endl; } void testGithubIssue868() { BOOST_LOG(rdInfoLog) << "-----------------------\n Testing github issue " "#868: inappropriate warning from MergeQueryHs" << std::endl; std::stringstream sstrm; rdWarningLog->SetTee(sstrm); { sstrm.str(""); std::string sma = "[SX3](=O)[O-,#1]"; RWMol *m = SmartsToMol(sma); TEST_ASSERT(m); TEST_ASSERT(m->getNumAtoms() == 3); MolOps::mergeQueryHs(*m); TEST_ASSERT( sstrm.str().find( "merging explicit H queries involved in ORs is not supported") != std::string::npos); TEST_ASSERT(sstrm.str().find("This query will not be merged") != std::string::npos); delete m; } { sstrm.str(""); std::string sma = "[SX3](=O)[O-,H1]"; RWMol *m = SmartsToMol(sma); TEST_ASSERT(m); TEST_ASSERT(m->getNumAtoms() == 3); MolOps::mergeQueryHs(*m); TEST_ASSERT(sstrm.str().find("merging explicit H queries involved in " "ORs is not supported") == std::string::npos); TEST_ASSERT(sstrm.str().find("This query will not be merged") == std::string::npos); delete m; } { sstrm.str(""); std::string sma = "[SX3](=O)[O-,H]"; RWMol *m = SmartsToMol(sma); TEST_ASSERT(m); TEST_ASSERT(m->getNumAtoms() == 3); MolOps::mergeQueryHs(*m); TEST_ASSERT(sstrm.str().find("merging explicit H queries involved in " "ORs is not supported") == std::string::npos); TEST_ASSERT(sstrm.str().find("This query will not be merged") == std::string::npos); delete m; } rdWarningLog->ClearTee(); BOOST_LOG(rdInfoLog) << "\tdone" << std::endl; } void testGithubIssue908() { BOOST_LOG(rdInfoLog) << "-----------------------\n Testing github issue 908: " "AddHs() using 3D coordinates with 2D conformations" << std::endl; { std::string mb = "\n RDKit 2D\n\n 4 3 0 0 0 0 0 0 0 0999 " "V2000\n " " -0.0000 -1.5000 0.0000 Br 0 0 0 0 0 0 0 0 0 0 0 " "0\n -0.0000 -0.0000 0.0000 C 0 0 0 0 0 0 0 0 0 0 " "0 " " 0\n 1.2990 0.7500 0.0000 F 0 0 0 0 0 0 0 0 0 0 " " " "0 0\n -1.2990 0.7500 0.0000 Cl 0 0 0 0 0 0 0 0 0 " "0 " " 0 0\n 2 1 1 1\n 2 3 1 0\n 2 4 1 0\nM END\n"; RWMol *m = MolBlockToMol(mb); TEST_ASSERT(m); TEST_ASSERT(m->getNumAtoms() == 4); MolOps::addHs(*m, false, true); TEST_ASSERT(m->getNumAtoms() == 5); TEST_ASSERT(feq(m->getConformer().getAtomPos(4).z, 0.0)); delete m; } BOOST_LOG(rdInfoLog) << "\tdone" << std::endl; } void testGithubIssue962() { BOOST_LOG(rdInfoLog) << "-----------------------\n Testing github issue 962: " "Kekulization issues post successful smiles parsing" << std::endl; { std::string smi = "C2*c1ccccc1C2"; RWMol *m = SmilesToMol(smi, 0, false); TEST_ASSERT(m); TEST_ASSERT(m->getNumAtoms() == 9); TEST_ASSERT(m->getBondBetweenAtoms(0, 1)); TEST_ASSERT(m->getBondBetweenAtoms(2, 1)); m->updatePropertyCache(); MolOps::Kekulize(*m); TEST_ASSERT(m->getBondBetweenAtoms(0, 1)->getBondType() == Bond::SINGLE); TEST_ASSERT(m->getBondBetweenAtoms(2, 1)->getBondType() == Bond::SINGLE); delete m; } { // this one did not cause problems before, but verify! std::string smi = "*2Cc1ccccc1C2"; RWMol *m = SmilesToMol(smi, 0, false); TEST_ASSERT(m); TEST_ASSERT(m->getNumAtoms() == 9); TEST_ASSERT(m->getBondBetweenAtoms(0, 1)); TEST_ASSERT(m->getBondBetweenAtoms(0, 8)); m->updatePropertyCache(); MolOps::Kekulize(*m); TEST_ASSERT(m->getBondBetweenAtoms(0, 1)->getBondType() == Bond::SINGLE); TEST_ASSERT(m->getBondBetweenAtoms(0, 8)->getBondType() == Bond::SINGLE); delete m; } BOOST_LOG(rdInfoLog) << "\tdone" << std::endl; } void testGithubIssue1021() { BOOST_LOG(rdInfoLog) << "-----------------------\n Testing github issue 1021: " "AssignStereochemistry() giving incorrect results after " "FastFindRings()" << std::endl; { std::string smi = "C[C@H]1CC2CCCC(C1)[C@H]2N"; RWMol *m = SmilesToMol(smi); TEST_ASSERT(m); TEST_ASSERT(m->getNumAtoms() == 11); TEST_ASSERT(m->getAtomWithIdx(1)->getChiralTag() != Atom::CHI_UNSPECIFIED); TEST_ASSERT(m->getAtomWithIdx(9)->getChiralTag() != Atom::CHI_UNSPECIFIED); m->clearComputedProps(); bool cleanit = true, force = true; MolOps::assignStereochemistry(*m, cleanit, force); TEST_ASSERT(m->getAtomWithIdx(1)->getChiralTag() != Atom::CHI_UNSPECIFIED); TEST_ASSERT(m->getAtomWithIdx(9)->getChiralTag() != Atom::CHI_UNSPECIFIED); m->clearComputedProps(); MolOps::fastFindRings(*m); MolOps::assignStereochemistry(*m, cleanit, force); TEST_ASSERT(m->getAtomWithIdx(1)->getChiralTag() != Atom::CHI_UNSPECIFIED); TEST_ASSERT(m->getAtomWithIdx(9)->getChiralTag() != Atom::CHI_UNSPECIFIED); delete m; } BOOST_LOG(rdInfoLog) << "\tdone" << std::endl; } void testGithubIssue607() { BOOST_LOG(rdInfoLog) << "-----------------------\n Testing github issue 607: " "AssignAtomChiralTagsFromStructure() not recognizing chiral S" << std::endl; { std::string pathName = getenv("RDBASE"); pathName += "/Code/GraphMol/test_data/"; ROMol *m = MolFileToMol(pathName + "1a9u.zwitterion.sdf"); TEST_ASSERT(m); TEST_ASSERT(m->getNumAtoms() == 27); MolOps::assignChiralTypesFrom3D(*m); TEST_ASSERT(m->getAtomWithIdx(26)->getAtomicNum() == 16); TEST_ASSERT(m->getAtomWithIdx(26)->getChiralTag() != Atom::CHI_UNSPECIFIED); delete m; } { std::string pathName = getenv("RDBASE"); pathName += "/Code/GraphMol/test_data/"; ROMol *m = MolFileToMol(pathName + "1a9u.sdf"); TEST_ASSERT(m); TEST_ASSERT(m->getNumAtoms() == 27); MolOps::assignChiralTypesFrom3D(*m); TEST_ASSERT(m->getAtomWithIdx(26)->getAtomicNum() == 16); TEST_ASSERT(m->getAtomWithIdx(26)->getChiralTag() != Atom::CHI_UNSPECIFIED); delete m; } { // convert S -> Se and test again std::string pathName = getenv("RDBASE"); pathName += "/Code/GraphMol/test_data/"; ROMol *m = MolFileToMol(pathName + "1a9u.zwitterion.sdf"); TEST_ASSERT(m); TEST_ASSERT(m->getNumAtoms() == 27); m->getAtomWithIdx(26)->setAtomicNum(34); MolOps::assignChiralTypesFrom3D(*m); TEST_ASSERT(m->getAtomWithIdx(26)->getChiralTag() != Atom::CHI_UNSPECIFIED); delete m; } { // convert S -> Se and test again std::string pathName = getenv("RDBASE"); pathName += "/Code/GraphMol/test_data/"; ROMol *m = MolFileToMol(pathName + "1a9u.sdf"); TEST_ASSERT(m); TEST_ASSERT(m->getNumAtoms() == 27); m->getAtomWithIdx(26)->setAtomicNum(34); MolOps::assignChiralTypesFrom3D(*m); TEST_ASSERT(m->getAtomWithIdx(26)->getChiralTag() != Atom::CHI_UNSPECIFIED); delete m; } BOOST_LOG(rdInfoLog) << "\tdone" << std::endl; } void testGithubIssue1204() { BOOST_LOG(rdInfoLog) << "-----------------------\n Testing github issue 1204: " "Support tetravalent and hexavalent Te" << std::endl; { std::string smiles = "F[Te](F)(F)(F)(F)F"; RWMol *m = SmilesToMol(smiles); TEST_ASSERT(m); delete m; } { std::string smiles = "F[Te](F)(F)(F)"; RWMol *m = SmilesToMol(smiles); TEST_ASSERT(m); delete m; } BOOST_LOG(rdInfoLog) << "Finished" << std::endl; } void testGithub1478() { BOOST_LOG(rdInfoLog) << "-----------------------\n Testing github issue 1478: " << std::endl << " Aromatic rings composed solely of dummy atoms should not be " "kekulized" << std::endl; { // basics std::string smiles = "*:1:*:*:*:*:*:1"; RWMol *m = SmilesToMol(smiles, false); TEST_ASSERT(m); m->updatePropertyCache(); MolOps::Kekulize(*m); for (unsigned int i = 0; i < m->getNumBonds(); ++i) { TEST_ASSERT(m->getBondWithIdx(i)->getBondType() == Bond::AROMATIC); } delete m; } { // fused rings where one is kekulized std::string smiles = "*:1:*:*:*:*:2:*:1cccc2"; RWMol *m = SmilesToMol(smiles, false); TEST_ASSERT(m); m->updatePropertyCache(); MolOps::Kekulize(*m); TEST_ASSERT(m->getBondBetweenAtoms(0, 1)->getBondType() == Bond::AROMATIC); TEST_ASSERT(m->getBondBetweenAtoms(6, 7)->getBondType() != Bond::AROMATIC); delete m; } BOOST_LOG(rdInfoLog) << "Finished" << std::endl; } void testGithub1439() { BOOST_LOG(rdInfoLog) << "-----------------------\n Testing github issue 1439: " << std::endl << "RemoveHs() removes H atom attached to dummy if it came from AddHs()" << std::endl; { // basics std::string smiles = "F"; RWMol *m = SmilesToMol(smiles); TEST_ASSERT(m); MolOps::addHs(*m); TEST_ASSERT(m->getNumAtoms() == 2); m->getAtomWithIdx(0)->setAtomicNum(0); MolOps::removeHs(*m); TEST_ASSERT(m->getNumAtoms() == 2); delete m; } BOOST_LOG(rdInfoLog) << "Finished" << std::endl; } void testGithub1281() { BOOST_LOG(rdInfoLog) << "-----------------------\n Testing github issue 1281: " << std::endl << "RDKit gets stuck on PubChem CID 102128817" << std::endl; { // basics std::string smiles = "COC1=CC=C(C=C1)C2C3=C(C=CC4=CC=CC=C43)OC5=CC6=C(C=C5)C7=NC8=C9C=CC1=" "CC9=C(N8)N=C3C4=C5C=CC(=C4)OC4=C(C(C8=C(C=CC9=CC=CC=C98)OC8=CC9=C(C=" "C8)C8=NC9=NC9=C%10C=C(C=CC%10=C(N9)N=C9C%10=C(C=C(C=C%10)OC%10=C2C2=" "CC=CC=C2C=C%10)C(=N9)NC2=NC(=N8)C8=C2C=C(C=C8)OC2=C(C(C8=C(C=CC9=CC=" "CC=C98)OC8=CC9=C(C=C8)C(=NC5=N3)N=C9NC6=N7)C3=CC=C(C=C3)OC)C3=CC=CC=" "C3C=C2)OC2=C(C(C3=C(O1)C=CC1=CC=CC=C13)C1=CC=C(C=C1)OC)C1=CC=CC=C1C=" "C2)C1=CC=C(C=C1)OC)C1=CC=CC=C1C=C4"; { RWMol *m = SmilesToMol(smiles, 0, false); TEST_ASSERT(m); TEST_ASSERT(m->getNumAtoms() == 204); TEST_ASSERT(m->getNumBonds() == 244); bool ok = false; try { MolOps::findSSSR(*m); } catch (const ValueErrorException &) { ok = true; } TEST_ASSERT(ok); delete m; } { bool ok = false; try { RWMol *m = SmilesToMol(smiles); // Can never get here: delete m; } catch (const ValueErrorException &) { ok = true; } TEST_ASSERT(ok); } } BOOST_LOG(rdInfoLog) << "Finished" << std::endl; } void testGithub1605() { BOOST_LOG(rdInfoLog) << "-----------------------\n Testing Github issue " "1605: Inappropriate bad valence exception during " "partial sanitization. " << std::endl; { std::string smiles = "C1=CC=CC=C1N(=O)=O"; { // easy to test; we shouldn't throw an exception. :-) RWMol *m = SmilesToMol(smiles, 0, false); TEST_ASSERT(m); unsigned int failed; MolOps::sanitizeMol( *m, failed, MolOps::SANITIZE_SETAROMATICITY | MolOps::SANITIZE_ADJUSTHS); TEST_ASSERT(!failed); delete m; } } BOOST_LOG(rdInfoLog) << "Finished" << std::endl; } void testGithub1622() { BOOST_LOG(rdInfoLog) << "-----------------------\n Testing Github issue " "1622: add MDL aromaticity perception" << std::endl; { // rings that should be aromatic string aromaticSmis[] = {"C1=CC=CC=C1", // benzene, of course // heterocyclics "N1=CC=CC=C1", // pyridine "N1=CC=CC=N1", // pyridazine "N1=CC=CN=C1", // pyrimidine "N1=CC=NC=C1", // pyrazine "N1=CN=CN=C1", // 1,3,5-triazine // polycyclic aromatics "C1=CC2=CC=CC=CC2=C1", // azulene "C1=CC=CC2=CC=CC=C12", // 6-6 fused "C1=CC2=CC=CC=CC=C12", // 4-8 fused "C1=CC=C2C(=C1)N=CC=N2", // 6-6 with Ns "C1=CN=CC2C=CC=CC1=2", // 6-6 "C1=CC=C2C(=C1)N=C3C=CC=CC3=N2", // 6-6-6 "C1=CN=NC2C=CC=CC1=2", // 6-6 with Ns // macrocycle aromatics "C1=CC=CC=CC=CC=C1", // 10 atoms "C1=CC=CC=CC=CC=CC=CC=CC=CC=C1", // 18 atoms "N1=CN=NC=CC=CC=CC=CC=CC=CC=CC=CC=CC=CC=CC=CC=C1", "EOS"}; unsigned int i = 0; while (aromaticSmis[i] != "EOS") { string smi = aromaticSmis[i]; // std::cerr << smi << std::endl; int debugParse = 0; bool sanitize = false; RWMol *mol = SmilesToMol(smi, debugParse, sanitize); TEST_ASSERT(mol); unsigned int whatFailed = 0; unsigned int sanitFlags = MolOps::SANITIZE_ALL ^ MolOps::SANITIZE_SETAROMATICITY; MolOps::sanitizeMol(*mol, whatFailed, sanitFlags); MolOps::setAromaticity(*mol, MolOps::AROMATICITY_MDL); TEST_ASSERT(mol->getAtomWithIdx(0)->getIsAromatic()) delete mol; ++i; } } { // rings that should not be aromatic string nonaromaticSmis[] = { "C1=C[N]C=C1", // radicals are not two electron donors // exocyclic double bonds disqualify us "C1(=O)C=CNC=C1", "C1(=C)C=CC(=C)C=C1", "C1(=O)C=CC(=O)C=C1", "C1#CC=CC=C1", // not benzyne // five-membered heterocycles "C1=COC=C1", // furan "C1=CSC=C1", // thiophene "C1=CNC=C1", // pyrrole "C1=COC=N1", // oxazole "C1=CSC=N1", // thiazole "C1=CNC=N1", // imidzole "C1=CNN=C1", // pyrazole "C1=CON=C1", // isoxazole "C1=CSN=C1", // isothiazole "C1=CON=N1", // 1,2,3-oxadiazole "C1=CNN=N1", // 1,2,3-triazole "N1=CSC=N1", // 1,3,4-thiadiazole "C1=CS(=O)C=C1", // not sure how to classify this example from the // OEChem docs // outside the second rows "C1=CC=C[Si]=C1", "C1=CC=CC=P1", // 5-membered heterocycles outside the second row "C1=C[Se]C=C1", "C1=C[Te]C=C1", "EOS"}; unsigned int i = 0; while (nonaromaticSmis[i] != "EOS") { string smi = nonaromaticSmis[i]; // std::cerr << smi << std::endl; int debugParse = 0; bool sanitize = false; RWMol *mol = SmilesToMol(smi, debugParse, sanitize); TEST_ASSERT(mol); unsigned int whatFailed = 0; unsigned int sanitFlags = MolOps::SANITIZE_ALL ^ MolOps::SANITIZE_SETAROMATICITY; MolOps::sanitizeMol(*mol, whatFailed, sanitFlags); MolOps::setAromaticity(*mol, MolOps::AROMATICITY_MDL); TEST_ASSERT(!(mol->getAtomWithIdx(0)->getIsAromatic())) delete mol; ++i; } } { // ring systems where part is aromatic, part not string mixedaromaticSmis[] = { "O1C=CC2=CC=CC=C12", "S1C=CC2=CC=CC=C12", "N1C2=CC=CC=C2C2=CC=CC=C12", "N1C=CC2=CC=CC=C12", "N1C=NC2=CC=CC=C12", "N1C=NC2=CN=CN=C12", "C1CCCC2=CC3=CCCCC3=CC2=1", "EOS"}; unsigned int i = 0; while (mixedaromaticSmis[i] != "EOS") { string smi = mixedaromaticSmis[i]; // std::cerr << smi << std::endl; int debugParse = 0; bool sanitize = false; RWMol *mol = SmilesToMol(smi, debugParse, sanitize); TEST_ASSERT(mol); unsigned int whatFailed = 0; unsigned int sanitFlags = MolOps::SANITIZE_ALL ^ MolOps::SANITIZE_SETAROMATICITY; MolOps::sanitizeMol(*mol, whatFailed, sanitFlags); MolOps::setAromaticity(*mol, MolOps::AROMATICITY_MDL); TEST_ASSERT(!(mol->getAtomWithIdx(0)->getIsAromatic())) TEST_ASSERT( (mol->getAtomWithIdx(mol->getNumAtoms() - 1)->getIsAromatic())) delete mol; ++i; } } BOOST_LOG(rdInfoLog) << "Finished" << std::endl; } void testGithub1703() { BOOST_LOG(rdInfoLog) << "-----------------------\n Testing Github issue " "1703: Dative bonds interfere with kekulization and " "the perception of aromaticity" << std::endl; { // start with zero-order bonds SmilesParserParams ps; ps.sanitize = false; std::unique_ptr mol(SmilesToMol("C1=CC=NC=N1.[Fe]", ps)); TEST_ASSERT(mol); mol->addBond(5, 6, Bond::ZERO); MolOps::sanitizeMol(*mol); TEST_ASSERT(mol->getBondBetweenAtoms(0, 1)->getIsAromatic()); TEST_ASSERT(mol->getAtomWithIdx(5)->getIsAromatic()); MolOps::Kekulize(*mol); } { // and dative bonds: SmilesParserParams ps; ps.sanitize = false; std::unique_ptr mol(SmilesToMol("C1=CC=NC=N1->[Fe]", ps)); TEST_ASSERT(mol); MolOps::sanitizeMol(*mol); TEST_ASSERT(mol->getBondBetweenAtoms(0, 1)->getIsAromatic()); TEST_ASSERT(mol->getAtomWithIdx(5)->getIsAromatic()); MolOps::Kekulize(*mol); } BOOST_LOG(rdInfoLog) << "Finished" << std::endl; } void testGithub1614() { BOOST_LOG(rdInfoLog) << "-----------------------\n Testing github issue " "1614: AssignStereochemistry incorrectly removing " "CIS/TRANS bond stereo" << std::endl; #if 1 { RWMol m; m.addAtom(new Atom(9), true, true); m.addAtom(new Atom(6), true, true); m.addAtom(new Atom(6), true, true); m.addAtom(new Atom(17), true, true); m.addBond(0, 1, Bond::SINGLE); m.addBond(2, 3, Bond::SINGLE); m.addBond(1, 2, Bond::DOUBLE); m.getBondBetweenAtoms(1, 2)->setStereoAtoms(0, 3); m.getBondBetweenAtoms(1, 2)->setStereo(Bond::STEREOTRANS); m.updatePropertyCache(); { RWMol nm(m); bool force = true, cleanIt = true; MolOps::setDoubleBondNeighborDirections(nm); MolOps::assignStereochemistry(nm, cleanIt, force); // nm.debugMol(std::cerr); TEST_ASSERT(nm.getBondBetweenAtoms(1, 2)->getStereo() > Bond::STEREOANY); std::string smi = MolToSmiles(nm, true); std::cerr << smi << std::endl; TEST_ASSERT(smi == "F/C=C/Cl"); } { RWMol nm(m); MolOps::addHs(nm); bool force = true, cleanIt = true; MolOps::setDoubleBondNeighborDirections(nm); MolOps::assignStereochemistry(nm, cleanIt, force); // nm.debugMol(std::cerr); TEST_ASSERT(nm.getBondBetweenAtoms(1, 2)->getStereo() > Bond::STEREOANY); std::string smi = MolToSmiles(nm, true); std::cerr << smi << std::endl; TEST_ASSERT(smi == "[H]/C(F)=C(/[H])Cl"); } } { RWMol m; m.addAtom(new Atom(9), true, true); m.addAtom(new Atom(6), true, true); m.addAtom(new Atom(6), true, true); m.addAtom(new Atom(17), true, true); m.addBond(0, 1, Bond::SINGLE); m.addBond(3, 2, Bond::SINGLE); m.addBond(1, 2, Bond::DOUBLE); m.getBondBetweenAtoms(1, 2)->setStereoAtoms(0, 3); m.getBondBetweenAtoms(1, 2)->setStereo(Bond::STEREOTRANS); m.updatePropertyCache(); { RWMol nm(m); bool force = true, cleanIt = true; MolOps::setDoubleBondNeighborDirections(nm); MolOps::assignStereochemistry(nm, cleanIt, force); // nm.debugMol(std::cerr); TEST_ASSERT(nm.getBondBetweenAtoms(1, 2)->getStereo() > Bond::STEREOANY); std::string smi = MolToSmiles(nm, true); std::cerr << smi << std::endl; TEST_ASSERT(smi == "F/C=C/Cl"); } } { RWMol m; m.addAtom(new Atom(9), true, true); m.addAtom(new Atom(6), true, true); m.addAtom(new Atom(6), true, true); m.addAtom(new Atom(17), true, true); m.addBond(1, 0, Bond::SINGLE); m.addBond(2, 3, Bond::SINGLE); m.addBond(1, 2, Bond::DOUBLE); m.getBondBetweenAtoms(1, 2)->setStereoAtoms(0, 3); m.getBondBetweenAtoms(1, 2)->setStereo(Bond::STEREOTRANS); m.updatePropertyCache(); { RWMol nm(m); bool force = true, cleanIt = true; MolOps::setDoubleBondNeighborDirections(nm); MolOps::assignStereochemistry(nm, cleanIt, force); // nm.debugMol(std::cerr); TEST_ASSERT(nm.getBondBetweenAtoms(1, 2)->getStereo() > Bond::STEREOANY); std::string smi = MolToSmiles(nm, true); std::cerr << smi << std::endl; TEST_ASSERT(smi == "F/C=C/Cl"); } } { RWMol m; m.addAtom(new Atom(9), true, true); m.addAtom(new Atom(6), true, true); m.addAtom(new Atom(6), true, true); m.addAtom(new Atom(17), true, true); m.addBond(0, 1, Bond::SINGLE); m.addBond(2, 3, Bond::SINGLE); m.addBond(1, 2, Bond::DOUBLE); m.addAtom(new Atom(6), true, true); m.addAtom(new Atom(6), true, true); m.addBond(1, 5, Bond::SINGLE); m.addBond(2, 4, Bond::SINGLE); m.getBondBetweenAtoms(1, 2)->setStereoAtoms(0, 3); m.getBondBetweenAtoms(1, 2)->setStereo(Bond::STEREOTRANS); m.updatePropertyCache(); { RWMol nm(m); bool force = true, cleanIt = true; MolOps::setDoubleBondNeighborDirections(nm); MolOps::assignStereochemistry(nm, cleanIt, force); // nm.debugMol(std::cerr); TEST_ASSERT(nm.getBondBetweenAtoms(1, 2)->getStereo() > Bond::STEREOANY); std::string smi = MolToSmiles(nm, true); std::cerr << smi << std::endl; TEST_ASSERT(smi == "C/C(F)=C(/C)Cl"); } } #endif #if 1 { RWMol *m = SmilesToMol("F/C=C(\\C/C=C/C)C/C=C\\F", false, false); TEST_ASSERT(m); MolOps::sanitizeMol(*m); { RWMol nm(*m); MolOps::setDoubleBondNeighborDirections(nm); // nm.debugMol(std::cerr); bool force = true, cleanIt = true; MolOps::assignStereochemistry(nm, cleanIt, force); // nm.debugMol(std::cerr); TEST_ASSERT(nm.getBondBetweenAtoms(4, 5)->getStereo() == Bond::STEREOE); TEST_ASSERT(nm.getBondBetweenAtoms(8, 9)->getStereo() == Bond::STEREOZ); TEST_ASSERT(nm.getBondBetweenAtoms(1, 2)->getStereo() == Bond::STEREOE); } delete m; } #endif { RWMol *m = SmilesToMol("FC=C(C/C=C/C)C/C=C\\F", false, false); TEST_ASSERT(m); MolOps::sanitizeMol(*m); TEST_ASSERT(m->getBondBetweenAtoms(1, 2)); m->getBondBetweenAtoms(1, 2)->setStereoAtoms(0, 3); m->getBondBetweenAtoms(1, 2)->setStereo(Bond::STEREOCIS); { RWMol nm(*m); MolOps::setDoubleBondNeighborDirections(nm); // nm.debugMol(std::cerr); bool force = true, cleanIt = true; MolOps::assignStereochemistry(nm, cleanIt, force); TEST_ASSERT(nm.getBondBetweenAtoms(4, 5)->getStereo() == Bond::STEREOE); TEST_ASSERT(nm.getBondBetweenAtoms(8, 9)->getStereo() == Bond::STEREOZ); TEST_ASSERT(nm.getBondBetweenAtoms(1, 2)->getStereo() == Bond::STEREOE); } delete m; } #if 1 { RWMol *m = SmilesToMol("F/C=C(\\C/C=C/C)C/C=C\\C", false, false); TEST_ASSERT(m); MolOps::sanitizeMol(*m); { RWMol nm(*m); MolOps::setDoubleBondNeighborDirections(nm); // nm.debugMol(std::cerr); bool force = true, cleanIt = true; MolOps::assignStereochemistry(nm, cleanIt, force); TEST_ASSERT(nm.getBondBetweenAtoms(4, 5)->getStereo() == Bond::STEREOE); TEST_ASSERT(nm.getBondBetweenAtoms(8, 9)->getStereo() == Bond::STEREOZ); TEST_ASSERT(nm.getBondBetweenAtoms(1, 2)->getStereo() == Bond::STEREOE); } delete m; } #endif { RWMol *m = SmilesToMol("FC=C(C/C=C/C)C/C=C\\C", false, false); TEST_ASSERT(m); MolOps::sanitizeMol(*m); TEST_ASSERT(m->getBondBetweenAtoms(1, 2)); m->getBondBetweenAtoms(1, 2)->setStereoAtoms(0, 3); m->getBondBetweenAtoms(1, 2)->setStereo(Bond::STEREOCIS); { RWMol nm(*m); MolOps::setDoubleBondNeighborDirections(nm); // nm.debugMol(std::cerr); bool force = true, cleanIt = true; MolOps::assignStereochemistry(nm, cleanIt, force); TEST_ASSERT(nm.getBondBetweenAtoms(4, 5)->getStereo() == Bond::STEREOE); TEST_ASSERT(nm.getBondBetweenAtoms(8, 9)->getStereo() == Bond::STEREOZ); TEST_ASSERT(nm.getBondBetweenAtoms(1, 2)->getStereo() == Bond::STEREOE); } delete m; } BOOST_LOG(rdInfoLog) << "Finished" << std::endl; } void testGithub1810() { BOOST_LOG(rdInfoLog) << "-----------------------\n Testing Github issue " "1810: removeHs() should not remove H atoms that are " "contributing to the definition of a stereo bond" << std::endl; { std::unique_ptr mol(SmilesToMol("F/C=C/[H]")); TEST_ASSERT(mol); TEST_ASSERT(mol->getNumAtoms() == 4); TEST_ASSERT(mol->getBondBetweenAtoms(1, 2)->getStereo() == Bond::STEREOE); } { std::unique_ptr mol(SmilesToMol("F/C=C(/F)[H]")); TEST_ASSERT(mol); TEST_ASSERT(mol->getNumAtoms() == 4); TEST_ASSERT(mol->getBondBetweenAtoms(1, 2)->getStereo() == Bond::STEREOE); } { std::unique_ptr mol(SmilesToMol("F/C=C(/[H])F")); TEST_ASSERT(mol); TEST_ASSERT(mol->getNumAtoms() == 4); TEST_ASSERT(mol->getBondBetweenAtoms(1, 2)->getStereo() == Bond::STEREOZ); } #if 1 { std::unique_ptr mol(SmilesToMol("FC=C(F)[H]", false, false)); TEST_ASSERT(mol); MolOps::sanitizeMol(*mol); TEST_ASSERT(mol->getNumAtoms() == 5); mol->getBondBetweenAtoms(1, 2)->setStereoAtoms(0, 4); mol->getBondBetweenAtoms(1, 2)->setStereo(Bond::STEREOTRANS); MolOps::removeHs(*mol); TEST_ASSERT(mol->getNumAtoms() == 4); TEST_ASSERT(mol->getBondBetweenAtoms(1, 2)->getStereoAtoms()[0] == 0); TEST_ASSERT(mol->getBondBetweenAtoms(1, 2)->getStereoAtoms()[1] == 3); } #endif BOOST_LOG(rdInfoLog) << "Finished" << std::endl; } void testGithub1936() { BOOST_LOG(rdInfoLog) << "-----------------------\n Testing Github issue " "1936: Bad aromaticity for rings with radical carbocations" << std::endl; { std::unique_ptr mol(SmilesToMol("C1=CC=C[C+]C=C1")); TEST_ASSERT(mol); TEST_ASSERT(mol->getNumAtoms() == 7); TEST_ASSERT(mol->getAtomWithIdx(4)->getNumRadicalElectrons() == 1); TEST_ASSERT(!mol->getAtomWithIdx(0)->getIsAromatic()); } { // the original report std::string pathName = getenv("RDBASE"); pathName += "/Code/GraphMol/test_data/"; std::unique_ptr mol(MolFileToMol(pathName + "github1936.mol")); TEST_ASSERT(mol); TEST_ASSERT(mol->getNumAtoms() == 7); TEST_ASSERT(mol->getAtomWithIdx(4)->getNumRadicalElectrons() == 1); TEST_ASSERT(!mol->getAtomWithIdx(0)->getIsAromatic()); } BOOST_LOG(rdInfoLog) << "Finished" << std::endl; } void testGithub1928() { BOOST_LOG(rdInfoLog) << "-----------------------\n Testing Github issue " "1928: incorrect aromatic SMILES generated for structure" << std::endl; { std::unique_ptr mol( SmilesToMol("N1C2=CC3=CC=CC=C3OC1=CC1=C(O2)C=CC=C1")); TEST_ASSERT(mol); TEST_ASSERT(mol->getNumAtoms() == 19); TEST_ASSERT(mol->getBondBetweenAtoms(0, 1)->getBondType() == Bond::SINGLE); TEST_ASSERT(!mol->getBondBetweenAtoms(0, 1)->getIsAromatic()); TEST_ASSERT(!mol->getAtomWithIdx(0)->getIsAromatic()); } { // the original report std::unique_ptr mol(SmilesToMol( "C12=C3C=CC=C1CCC(=O)C2=C4OC5=CC=CC6=C5C(=C(N4)O3)C(=O)CC6")); TEST_ASSERT(mol); TEST_ASSERT(mol->getNumAtoms() == 27); TEST_ASSERT(mol->getBondBetweenAtoms(20, 21)->getBondType() == Bond::SINGLE); TEST_ASSERT(!mol->getBondBetweenAtoms(20, 21)->getIsAromatic()); TEST_ASSERT(!mol->getAtomWithIdx(21)->getIsAromatic()); } BOOST_LOG(rdInfoLog) << "Finished" << std::endl; } void testGithub1990() { BOOST_LOG(rdInfoLog) << "-----------------------\n Testing Github issue " "1990: removeHs screws up bond stereo" << std::endl; { std::unique_ptr mol(SmilesToMol("F/C=C/F")); TEST_ASSERT(mol); MolOps::addHs(*mol); MolOps::removeHs(*mol); TEST_ASSERT(mol->getNumAtoms() == 4); TEST_ASSERT(mol->getBondWithIdx(1)->getStereoAtoms().size() == 2); } { // make sure that stereo is not removed when it comes from Hs: std::unique_ptr mol(SmilesToMol("F/C=C/F")); TEST_ASSERT(mol); MolOps::addHs(*mol); TEST_ASSERT(mol->getBondWithIdx(1)->getStereoAtoms().size() == 2); mol->getBondWithIdx(1)->getStereoAtoms()[0] = 4; MolOps::removeHs(*mol); TEST_ASSERT(mol->getBondWithIdx(1)->getStereoAtoms().size() == 2); mol->getBondWithIdx(1)->getStereoAtoms()[0] = 0; } { // make sure that stereo is not removed when it comes from Hs: std::unique_ptr mol(SmilesToMol("F/C=C/F")); TEST_ASSERT(mol); MolOps::addHs(*mol); TEST_ASSERT(mol->getBondWithIdx(1)->getStereoAtoms().size() == 2); mol->getBondWithIdx(1)->getStereoAtoms()[1] = 5; MolOps::removeHs(*mol); TEST_ASSERT(mol->getBondWithIdx(1)->getStereoAtoms().size() == 2); mol->getBondWithIdx(1)->getStereoAtoms()[1] = 3; } BOOST_LOG(rdInfoLog) << "Finished" << std::endl; } void testRemoveAndTrackIsotopes() { BOOST_LOG(rdInfoLog) << "-----------------------\n Testing removeAndTrackIsotopes parameter. " << std::endl; struct IsotopicHsCount { IsotopicHsCount(const ROMol &mol) { for (auto b : mol.bonds()) { const auto ba = b->getBeginAtom(); const auto ea = b->getEndAtom(); if (ba->getAtomicNum() == 1 && ba->getIsotope()) { ++d_map[ea->getIdx()]; } else if (ea->getAtomicNum() == 1 && ea->getIsotope()) { ++d_map[ba->getIdx()]; } } } // get the number of isotopic Hs attached to atom idx unsigned int at(unsigned int idx) { auto it = d_map.find(idx); return (it == d_map.end() ? 0 : it->second); } // count total number of isotopes in the molecule unsigned int total() { return std::accumulate( d_map.begin(), d_map.end(), 0U, [](unsigned int s, const std::pair &p) { return s + p.second; }); } static void countExplicitImplicitHs(const ROMol &m, unsigned int &expl, unsigned int &impl) { expl = 0; impl = 0; for (auto a : m.atoms()) { expl += a->getNumExplicitHs(); impl += a->getNumImplicitHs(); } } std::map d_map; }; // CHEMBL2024142 auto m = "[2H]C1=C(C(=C2C(=C1[2H])C(=O)C(=C(C2=O)C([2H])([2H])[2H])C/C=C(\\C)/CC([2H])([2H])/C=C(/CC/C=C(\\C)/CCC=C(C)C)\\C([2H])([2H])[2H])[2H])[2H]"_smiles; TEST_ASSERT(m.get()); std::unique_ptr m_isotopicHsPerHeavy( new IsotopicHsCount(*m)); unsigned int m_numExplicitHs; unsigned int m_numImplicitHs; IsotopicHsCount::countExplicitImplicitHs(*m, m_numExplicitHs, m_numImplicitHs); TEST_ASSERT(m_numExplicitHs == 0); TEST_ASSERT(m_numImplicitHs == 28); TEST_ASSERT(m_isotopicHsPerHeavy->total() == 12); MolOps::RemoveHsParameters ps; ps.removeAndTrackIsotopes = true; std::unique_ptr mNoH(removeHs(*static_cast(m.get()), ps)); TEST_ASSERT(mNoH->getAtomWithIdx(0)->getAtomicNum() == 6); TEST_ASSERT(mNoH->getAtomWithIdx(0)->hasProp(common_properties::_isotopicHs)); std::vector isoHs; TEST_ASSERT(mNoH->getAtomWithIdx(0)->getPropIfPresent( common_properties::_isotopicHs, isoHs)); TEST_ASSERT(isoHs.size() == 1); TEST_ASSERT(isoHs.front() == 2); TEST_ASSERT(mNoH->getAtomWithIdx(30)->getAtomicNum() == 6); TEST_ASSERT( !mNoH->getAtomWithIdx(30)->hasProp(common_properties::_isotopicHs)); IsotopicHsCount mNoH_isotopicHsPerHeavy(*mNoH); unsigned int mNoH_numExplicitHs; unsigned int mNoH_numImplicitHs; IsotopicHsCount::countExplicitImplicitHs(*mNoH, mNoH_numExplicitHs, mNoH_numImplicitHs); TEST_ASSERT(mNoH_numExplicitHs == 0); TEST_ASSERT(mNoH_numImplicitHs == 40); TEST_ASSERT(mNoH_isotopicHsPerHeavy.total() == 0); std::unique_ptr mH(MolOps::addHs(*mNoH)); std::unique_ptr mH_isotopicHsPerHeavy( new IsotopicHsCount(*mH)); unsigned int mH_numExplicitHs; unsigned int mH_numImplicitHs; IsotopicHsCount::countExplicitImplicitHs(*mH, mH_numExplicitHs, mH_numImplicitHs); TEST_ASSERT(mH_numExplicitHs == 0); TEST_ASSERT(mH_numImplicitHs == 0); MatchVectType match; TEST_ASSERT(SubstructMatch(*mH, *m, match)); TEST_ASSERT(match.size() == m->getNumAtoms()); TEST_ASSERT(mH_isotopicHsPerHeavy->total() == 12); std::unique_ptr mH2(MolOps::removeHs(*mH)); TEST_ASSERT(m->getNumAtoms() == mH2->getNumAtoms()); std::unique_ptr mH2_isotopicHsPerHeavy( new IsotopicHsCount(*mH2)); unsigned int mH2_numExplicitHs; unsigned int mH2_numImplicitHs; IsotopicHsCount::countExplicitImplicitHs(*mH2, mH2_numExplicitHs, mH2_numImplicitHs); MatchVectType matchH2; TEST_ASSERT(SubstructMatch(*m, *mH2, matchH2)); TEST_ASSERT(matchH2.size() == m->getNumAtoms()); TEST_ASSERT(mH2_isotopicHsPerHeavy->total() == 12); TEST_ASSERT(mH2_numExplicitHs == 28); TEST_ASSERT(mH2_numImplicitHs == 0); for (auto p : matchH2) { TEST_ASSERT(mH2_isotopicHsPerHeavy->at(p.first) == m_isotopicHsPerHeavy->at(p.second)); } // shuffle atoms before adding Hs; result should not change std::vector randomOrder(mNoH->getNumAtoms()); std::iota(randomOrder.begin(), randomOrder.end(), 0U); std::shuffle(randomOrder.begin(), randomOrder.end(), std::default_random_engine()); std::unique_ptr mNoHRen(MolOps::renumberAtoms(*mNoH, randomOrder)); mH.reset(MolOps::addHs(*mNoHRen)); mH_isotopicHsPerHeavy.reset(new IsotopicHsCount(*mH)); IsotopicHsCount::countExplicitImplicitHs(*mH, mH_numExplicitHs, mH_numImplicitHs); TEST_ASSERT(mH_numExplicitHs == 0); TEST_ASSERT(mH_numImplicitHs == 0); MatchVectType matchRen; TEST_ASSERT(SubstructMatch(*mH, *m, matchRen)); TEST_ASSERT(match != matchRen); TEST_ASSERT(match.size() == matchRen.size()); TEST_ASSERT(mH_isotopicHsPerHeavy->total() == 12); mH2.reset(MolOps::removeHs(*mH)); TEST_ASSERT(m->getNumAtoms() == mH2->getNumAtoms()); mH2_isotopicHsPerHeavy.reset(new IsotopicHsCount(*mH2)); IsotopicHsCount::countExplicitImplicitHs(*mH2, mH2_numExplicitHs, mH2_numImplicitHs); MatchVectType matchH2Ren; TEST_ASSERT(SubstructMatch(*m, *mH2, matchH2Ren)); TEST_ASSERT(matchH2 != matchH2Ren); TEST_ASSERT(matchH2.size() == matchH2Ren.size()); TEST_ASSERT(mH2_isotopicHsPerHeavy->total() == 12); TEST_ASSERT(mH2_numExplicitHs == 28); TEST_ASSERT(mH2_numImplicitHs == 0); for (auto p : matchH2Ren) { TEST_ASSERT(mH2_isotopicHsPerHeavy->at(p.first) == m_isotopicHsPerHeavy->at(p.second)); } // Add isotopes incrementally only on some atoms at a time // This should add 4 isotopes UINT_VECT onlyOnAtoms{0, 12}; mH.reset(MolOps::addHs(*mNoH, false, false, &onlyOnAtoms)); mH_isotopicHsPerHeavy.reset(new IsotopicHsCount(*mH)); TEST_ASSERT(mH_isotopicHsPerHeavy->total() == 4); TEST_ASSERT(mH_isotopicHsPerHeavy->at(0) == 1); TEST_ASSERT(mH_isotopicHsPerHeavy->at(12) == 3); // This should add 4 more isotopes onlyOnAtoms = UINT_VECT{1, 2, 18}; mH.reset(MolOps::addHs(*mH, false, false, &onlyOnAtoms)); mH_isotopicHsPerHeavy.reset(new IsotopicHsCount(*mH)); TEST_ASSERT(mH_isotopicHsPerHeavy->total() == 8); TEST_ASSERT(mH_isotopicHsPerHeavy->at(0) == 1); TEST_ASSERT(mH_isotopicHsPerHeavy->at(1) == 1); TEST_ASSERT(mH_isotopicHsPerHeavy->at(2) == 1); TEST_ASSERT(mH_isotopicHsPerHeavy->at(12) == 3); TEST_ASSERT(mH_isotopicHsPerHeavy->at(18) == 2); // This should add the last 4 isotopes onlyOnAtoms = UINT_VECT{5, 32}; mH.reset(MolOps::addHs(*mH, false, false, &onlyOnAtoms)); mH_isotopicHsPerHeavy.reset(new IsotopicHsCount(*mH)); TEST_ASSERT(mH_isotopicHsPerHeavy->total() == 12); TEST_ASSERT(mH_isotopicHsPerHeavy->at(0) == 1); TEST_ASSERT(mH_isotopicHsPerHeavy->at(1) == 1); TEST_ASSERT(mH_isotopicHsPerHeavy->at(2) == 1); TEST_ASSERT(mH_isotopicHsPerHeavy->at(5) == 1); TEST_ASSERT(mH_isotopicHsPerHeavy->at(12) == 3); TEST_ASSERT(mH_isotopicHsPerHeavy->at(18) == 2); TEST_ASSERT(mH_isotopicHsPerHeavy->at(32) == 3); match.clear(); TEST_ASSERT(SubstructMatch(*mH, *m, match)); TEST_ASSERT(match.size() == mH->getNumAtoms()); for (auto p : match) { auto m_nIso = m_isotopicHsPerHeavy->at(p.first); if (!m_nIso) { continue; } auto mH_nIso = mH_isotopicHsPerHeavy->at(p.second); TEST_ASSERT(m_nIso == mH_nIso); } // Check that chirality on centers which bear both non-isotopic // and isotopic Hs is preserved after... std::set chiralTypeSet; std::set chiralTypeSetAfterAddHs; std::set chiralTypeSetAfterRemoveAllHsAddHs; std::set chiralTypeSetAfterRemoveAllHsAddHsRemoveHs; for (unsigned int i : {0, 1}) { unsigned int hIdx = i + 24; std::string expectedCipCode(1, 'R' + i); std::unique_ptr mChiral(new ROMol(*m)); mChiral->getAtomWithIdx(23)->setChiralTag(Atom::CHI_TETRAHEDRAL_CW); mChiral->getAtomWithIdx(hIdx)->setIsotope(0); MolOps::assignStereochemistry(*mChiral, true, true); TEST_ASSERT(mChiral->getAtomWithIdx(23)->getProp( common_properties::_CIPCode) == expectedCipCode); chiralTypeSet.insert(mChiral->getAtomWithIdx(23)->getChiralTag()); // 1) ...Adding Hs mH.reset(MolOps::addHs(*mChiral)); MolOps::assignStereochemistry(*mH, true, true); match.clear(); TEST_ASSERT(mH->getAtomWithIdx(23)->getProp( common_properties::_CIPCode) == expectedCipCode); chiralTypeSetAfterAddHs.insert(mH->getAtomWithIdx(23)->getChiralTag()); // 2) ...Removing all Hs including isotopes and then putting them back mNoH.reset(MolOps::removeHs(*static_cast(mChiral.get()), ps)); mH.reset(MolOps::addHs(*mNoH)); MolOps::assignStereochemistry(*mH, true, true); match.clear(); TEST_ASSERT(SubstructMatch(*mH, *mChiral, match)); TEST_ASSERT(match.size() == mChiral->getNumAtoms()); TEST_ASSERT(mH->getAtomWithIdx(match[23].second) ->getProp(common_properties::_CIPCode) == expectedCipCode); chiralTypeSetAfterRemoveAllHsAddHs.insert( mH->getAtomWithIdx(match[23].second)->getChiralTag()); // 3) ...Removing non-isotopic Hs mNoH.reset(MolOps::removeHs(*mH)); MolOps::assignStereochemistry(*mNoH, true, true); TEST_ASSERT(mNoH->getAtomWithIdx(match[23].second) ->getProp(common_properties::_CIPCode) == expectedCipCode); chiralTypeSetAfterRemoveAllHsAddHsRemoveHs.insert( mNoH->getAtomWithIdx(match[23].second)->getChiralTag()); } // CIP chirality is preserved because when all Hs are removed // the parity is inverted on one of the enantiomers such that // chirality is preserved also when Hs are implicit. // So we must find a single parity before calling removeHs, // and two afterwards. TEST_ASSERT(chiralTypeSet.size() == 1 && *chiralTypeSet.begin() == Atom::CHI_TETRAHEDRAL_CW); TEST_ASSERT(chiralTypeSetAfterAddHs.size() == 1 && *chiralTypeSetAfterAddHs.begin() == Atom::CHI_TETRAHEDRAL_CW); TEST_ASSERT(chiralTypeSetAfterRemoveAllHsAddHs.size() == 2); TEST_ASSERT(chiralTypeSetAfterRemoveAllHsAddHsRemoveHs.size() == 2); // Check that chirality on centers which bear different // H isotopes is preserved after... chiralTypeSet.clear(); chiralTypeSetAfterAddHs.clear(); chiralTypeSetAfterRemoveAllHsAddHs.clear(); chiralTypeSetAfterRemoveAllHsAddHsRemoveHs.clear(); for (unsigned int i : {0, 1}) { unsigned int hIdx = i + 24; std::string expectedCipCode(1, 'R' + i); std::unique_ptr mChiral(new ROMol(*m)); mChiral->getAtomWithIdx(23)->setChiralTag(Atom::CHI_TETRAHEDRAL_CCW); mChiral->getAtomWithIdx(hIdx)->setIsotope(3); MolOps::assignStereochemistry(*mChiral, true, true); TEST_ASSERT(mChiral->getAtomWithIdx(23)->getProp( common_properties::_CIPCode) == expectedCipCode); chiralTypeSet.insert(mChiral->getAtomWithIdx(23)->getChiralTag()); // 1) ...Adding Hs mH.reset(MolOps::addHs(*mChiral)); MolOps::assignStereochemistry(*mH, true, true); match.clear(); TEST_ASSERT(mH->getAtomWithIdx(23)->getProp( common_properties::_CIPCode) == expectedCipCode); chiralTypeSetAfterAddHs.insert(mH->getAtomWithIdx(23)->getChiralTag()); // 2) ...Removing all Hs including isotopes and then putting them back mNoH.reset(MolOps::removeHs(*static_cast(mChiral.get()), ps)); mH.reset(MolOps::addHs(*mNoH)); MolOps::assignStereochemistry(*mH, true, true); match.clear(); TEST_ASSERT(SubstructMatch(*mH, *mChiral, match)); TEST_ASSERT(match.size() == mChiral->getNumAtoms()); TEST_ASSERT(mH->getAtomWithIdx(match[23].second) ->getProp(common_properties::_CIPCode) == expectedCipCode); chiralTypeSetAfterRemoveAllHsAddHs.insert( mH->getAtomWithIdx(match[23].second)->getChiralTag()); // 3) ...Removing non-isotopic Hs mNoH.reset(MolOps::removeHs(*mH)); MolOps::assignStereochemistry(*mNoH, true, true); TEST_ASSERT(mNoH->getAtomWithIdx(match[23].second) ->getProp(common_properties::_CIPCode) == expectedCipCode); chiralTypeSetAfterRemoveAllHsAddHsRemoveHs.insert( mNoH->getAtomWithIdx(match[23].second)->getChiralTag()); } // In this case we should find a single parity throughout // as inverting the positions of 2H and 3H will trigger // an inversion in CIP chirality without need for parity change TEST_ASSERT(chiralTypeSet.size() == 1 && *chiralTypeSet.begin() == Atom::CHI_TETRAHEDRAL_CCW); TEST_ASSERT(chiralTypeSetAfterAddHs.size() == 1 && *chiralTypeSetAfterAddHs.begin() == Atom::CHI_TETRAHEDRAL_CCW); TEST_ASSERT(chiralTypeSetAfterRemoveAllHsAddHs.size() == 1 && *chiralTypeSetAfterRemoveAllHsAddHs.begin() == Atom::CHI_TETRAHEDRAL_CCW); TEST_ASSERT(chiralTypeSetAfterRemoveAllHsAddHsRemoveHs.size() == 1 && *chiralTypeSetAfterRemoveAllHsAddHsRemoveHs.begin() == Atom::CHI_TETRAHEDRAL_CCW); BOOST_LOG(rdInfoLog) << "Finished" << std::endl; } void testGithub3854() { BOOST_LOG(rdInfoLog) << "-----------------------\n Testing github issue 3854: " "AddHs creates H atom with nan coordinates on edge case 2D structure" << std::endl; std::string molb = R"CTAB( RDKit 2D 7 8 0 0 1 0 0 0 0 0999 V2000 5.0014 0.4125 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 4.1764 0.4125 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 3.3514 0.4125 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 2.9389 1.1271 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 3.3514 1.8412 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 4.1764 1.8412 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 4.5889 1.1271 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 2 1 1 1 7 1 1 1 2 3 1 0 2 7 1 0 3 4 1 0 4 5 1 0 5 6 1 0 7 6 1 0 M END)CTAB"; bool sanitize = true; bool removeHs = false; std::unique_ptr m(MolBlockToMol(molb, sanitize, removeHs)); TEST_ASSERT(m); TEST_ASSERT(m->getNumAtoms() == 7); bool explicitOnly = false; bool addCoords = true; std::vector onlyOnAtoms = {1, 6}; std::unique_ptr m2( MolOps::addHs(*m, explicitOnly, addCoords, &onlyOnAtoms)); TEST_ASSERT(m2); TEST_ASSERT(m2->getNumAtoms() == 9); auto conf = m2->getConformer(); for (auto i = 7; i < 9; ++i) { auto atom_pos = conf.getAtomPos(i); TEST_ASSERT(!isnan(atom_pos.x) && !isnan(atom_pos.y) && !isnan(atom_pos.z)); } // check that we bisect the correct angle and point outside the rings auto v71 = conf.getAtomPos(7) - conf.getAtomPos(1); auto v21 = conf.getAtomPos(2) - conf.getAtomPos(1); auto v01 = conf.getAtomPos(0) - conf.getAtomPos(1); auto v61 = conf.getAtomPos(6) - conf.getAtomPos(1); TEST_ASSERT(fabs(fabs(v71.dotProduct(v01)) - fabs(v71.dotProduct(v21))) < 1e-3); TEST_ASSERT(v71.dotProduct(v61) < -1e-4); auto v86 = conf.getAtomPos(8) - conf.getAtomPos(6); auto v06 = conf.getAtomPos(0) - conf.getAtomPos(6); auto v56 = conf.getAtomPos(5) - conf.getAtomPos(6); auto v16 = conf.getAtomPos(1) - conf.getAtomPos(6); TEST_ASSERT(fabs(fabs(v86.dotProduct(v56)) - fabs(v86.dotProduct(v06))) < 1e-3); TEST_ASSERT(v86.dotProduct(v16) < -1e-4); } #ifdef RDK_USE_URF void testRingFamilies() { BOOST_LOG(rdInfoLog) << "-----------------------\n Testing ring family calculation. " << std::endl; { std::string smiles = "C(C1C2C3C41)(C2C35)C45"; // cubane ROMol *m = SmilesToMol(smiles); TEST_ASSERT(m); TEST_ASSERT(m->getNumAtoms() == 8); TEST_ASSERT(!m->getRingInfo()->areRingFamiliesInitialized()); MolOps::findRingFamilies(*m); TEST_ASSERT(m->getRingInfo()->isInitialized()); TEST_ASSERT(m->getRingInfo()->areRingFamiliesInitialized()); int numURF = RDL_getNofURF(m->getRingInfo()->dp_urfData.get()); int numRC = RDL_getNofRC(m->getRingInfo()->dp_urfData.get()); TEST_ASSERT(numRC == 6); TEST_ASSERT(numURF == 6); int numRings = m->getRingInfo()->numRingFamilies(); TEST_ASSERT(numRings == 6); numRings = m->getRingInfo()->numRings(); TEST_ASSERT(numRings == 6); delete m; } { std::string smiles = "C1CC2CCC1CC1CCC(CC1)CC1CCC(CC1)CC1CCC(CC1)C2"; ROMol *m = SmilesToMol(smiles); TEST_ASSERT(m); TEST_ASSERT(m->getNumAtoms() == 28); TEST_ASSERT(!m->getRingInfo()->areRingFamiliesInitialized()); MolOps::findRingFamilies(*m); TEST_ASSERT(m->getRingInfo()->isInitialized()); TEST_ASSERT(m->getRingInfo()->areRingFamiliesInitialized()); int numURF = RDL_getNofURF(m->getRingInfo()->dp_urfData.get()); int numRC = RDL_getNofRC(m->getRingInfo()->dp_urfData.get()); // std::cerr << " URF, RC " << numURF << " " << numRC << std::endl; TEST_ASSERT(numURF == 5); TEST_ASSERT(numRC == 20); int numRings = m->getRingInfo()->numRings(); // std::cerr << "num rings: " << numRings << std::endl; TEST_ASSERT(numRings == 14); TEST_ASSERT(m->getRingInfo()->numRingFamilies() == 5); delete m; } BOOST_LOG(rdInfoLog) << "\tdone" << std::endl; } #else void testRingFamilies() {} #endif void testSetTerminalAtomCoords() { BOOST_LOG(rdInfoLog) << "-----------------------\n Testing adding " "coordinates to a terminal atom. " << std::endl; auto mol = R"CTAB( RDKit 2D 6 6 0 0 0 0 0 0 0 0999 V2000 1.5000 0.0000 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 0.7500 -1.2990 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -0.7500 -1.2990 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -1.5000 0.0000 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -0.7500 1.2990 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 0.7500 1.2990 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 1 2 2 0 2 3 1 0 3 4 2 0 4 5 1 0 5 6 2 0 6 1 1 0 M END)CTAB"_ctab; auto atom = new Atom(0); auto idx = mol->addAtom(atom); delete atom; mol->addBond(idx, 0); MolOps::setTerminalAtomCoords(static_cast(*mol), idx, 0); auto &coord = mol->getConformer().getAtomPos(idx); TEST_ASSERT(coord.x > 2.499 && coord.x < 2.501); TEST_ASSERT(coord.y > -0.001 && coord.y < 0.001); TEST_ASSERT(coord.z > -0.001 && coord.z < 0.001); BOOST_LOG(rdInfoLog) << "\tdone" << std::endl; } void testGet3DDistanceMatrix() { BOOST_LOG(rdInfoLog) << "-----------------------\n testing get3DDistanceMat(). " << std::endl; auto mol = R"CTAB(bogus example RDKit 3D 3 2 0 0 0 0 0 0 0 0999 V2000 0.0000 0.0000 0.1000 C 0 0 0 0 0 0 0 0 0 0 0 0 1.2000 0.0000 0.1000 C 0 0 0 0 0 0 0 0 0 0 0 0 2.5000 0.0000 0.1000 O 0 0 0 0 0 0 0 0 0 0 0 0 1 2 2 0 2 3 1 0 M END)CTAB"_ctab; TEST_ASSERT(mol); double *dm = MolOps::get3DDistanceMat(*mol); TEST_ASSERT(dm); TEST_ASSERT(dm[0] == 0.0); TEST_ASSERT(dm[1] == 1.2); TEST_ASSERT(dm[2] == 2.5); TEST_ASSERT(dm[3] == 1.2); TEST_ASSERT(dm[4] == 0.0); TEST_ASSERT(dm[5] == 1.3); TEST_ASSERT(dm[6] == 2.5); TEST_ASSERT(dm[7] == 1.3); TEST_ASSERT(dm[8] == 0.0); // this will use a cached version: double *dm2 = MolOps::get3DDistanceMat(*mol); TEST_ASSERT(dm == dm2) int confId = -1; bool useAtomWts = true; dm = MolOps::get3DDistanceMat(*mol, confId, useAtomWts); TEST_ASSERT(dm); TEST_ASSERT(dm[0] == 1.0); TEST_ASSERT(dm[1] == 1.2); TEST_ASSERT(dm[2] == 2.5); TEST_ASSERT(dm[3] == 1.2); TEST_ASSERT(dm[4] == 1.0); TEST_ASSERT(dm[5] == 1.3); TEST_ASSERT(dm[6] == 2.5); TEST_ASSERT(dm[7] == 1.3); TEST_ASSERT(dm[8] == 6.0 / 8.0); BOOST_LOG(rdInfoLog) << "\tdone" << std::endl; } void testGithub5099() { BOOST_LOG(rdInfoLog) << "-----------------------\n Testing github issue 5099: " "Removing H preserving only wedged ones strips all H" << std::endl; std::string smi{"FC([H])(O)Cl"}; std::unique_ptr m{SmilesToMol(smi, false, false)}; TEST_ASSERT(m); TEST_ASSERT(m->getNumAtoms() == 5); m->getBondBetweenAtoms(1, 2)->setBondDir(Bond::BondDir::BEGINWEDGE); auto ps = MolOps::RemoveHsParameters(); ps.showWarnings = false; // Remove all but Wedged/dashed H ps.removeWithWedgedBond = false; ps.removeDefiningBondStereo = true; ps.removeDegreeZero = true; ps.removeDummyNeighbors = true; ps.removeHigherDegrees = true; ps.removeHydrides = true; ps.removeInSGroups = true; ps.removeIsotopes = true; ps.removeMapped = true; ps.removeNonimplicit = true; ps.removeOnlyHNeighbors = true; ps.removeWithQuery = true; removeHs(*m, ps); // H shouldn't be removed TEST_ASSERT(m->getNumAtoms() == 5); } int main() { RDLog::InitLogs(); // boost::logging::enable_logs("rdApp.debug"); test1(); test2(); test3(); test4(); test5(); // test6(); test7(); test8(); test9(); test10(); test12(); testIssue183(); testIssue188(); testIssue189(); testShortestPath(); testIssue190(); testIssue211(); testIssue210(); testIssue212(); testAddHsCoords(); testAddConformers(); testSanitOps(); testIssue252(); testIssue276(); testHsAndAromaticity(); testSFIssue1694023(); testSFIssue1719053(); testSFIssue1811276(); testSFIssue1836576(); testChiralityAndRemoveHs(); testSFIssue1894348(); testSFIssue1942657(); testSFIssue1968608(); testHybridization(); testAromaticityEdges(); testSFNetIssue2196817(); testSFNetIssue2208994(); testSFNetIssue2313979(); testSFNetIssue2316677(); testSFNetIssue2951221(); testSFNetIssue2952255(); testSFNetIssue3185548(); testSFNetIssue3349243(); testFastFindRings(); testSanitizeNonringAromatics(); testSFNetIssue3349243(); testBasicCanon(); testSFNetIssue3549146(); testSFNetIssue249(); testSFNetIssue256(); testSFNetIssue266(); testSFNetIssue266(); testSFNetIssue272(); testGitHubIssue8(); testGitHubIssue42(); testGitHubIssue65(); testGitHubIssue72(); testRenumberAtoms(); testGithubIssue141(); testZBO(); testMolAssignment(); testAtomAtomMatch(); testGithubIssue190(); testMolFragsWithQuery(); test11(); testGithubIssue418(); testGithubIssue432(); testGithubIssue443(); testGithubIssue447(); testGetMolFrags(); testGithubIssue510(); testGithubIssue526(); testGithubIssue539(); testGithubIssue678(); testGithubIssue717(); testGithubIssue754(); testGithubIssue805(); testGithubIssue518(); testKekulizeErrorReporting(); testGithubIssue868(); testSimpleAromaticity(); testGithubIssue1730(); testCustomAromaticity(); testGithubIssue908(); testGithubIssue962(); testGithubIssue1021(); testGithubIssue607(); testAdjustQueryProperties(); testGithubIssue1204(); testSetBondStereo(); testBondSetStereoAtoms(); testGithub1478(); testGithub1439(); testGithub1281(); testGithub1605(); testGithub1614(); testGithub1622(); testGithub1703(); testGithub1810(); testGithub1936(); testGithub1928(); testGithub1990(); testPotentialStereoBonds(); testRingFamilies(); testRemoveAndTrackIsotopes(); testGithub3854(); testSetTerminalAtomCoords(); testGet3DDistanceMatrix(); testGithub5099(); return 0; }