// $Id$ // // Copyright (C) 2008-2011 Greg Landrum // @@ 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. // // Can be built with: // g++ -o sample.exe sample.cpp -I$RDBASE/Code -I$RDBASE/Extern \ // -L$RDBASE/lib -lChemReactions -lFileParsers -lSmilesParse -lDepictor \ // -lSubstructMatch -lGraphMol -lDataStructs -lRDGeometryLib -lRDGeneral // #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace RDKit; void BuildSimpleMolecule() { // build the molecule: C/C=C\C auto mol = std::make_unique(); // add atoms and bonds: constexpr bool updateLabel = false; constexpr bool takeOwnership = true; mol->addAtom(new Atom(6), updateLabel, takeOwnership); // atom 0 mol->addAtom(new Atom(6), updateLabel, takeOwnership); // atom 1 mol->addAtom(new Atom(6), updateLabel, takeOwnership); // atom 2 mol->addAtom(new Atom(6), updateLabel, takeOwnership); // atom 3 mol->addBond(0, 1, Bond::SINGLE); // bond 0 mol->addBond(1, 2, Bond::DOUBLE); // bond 1 mol->addBond(2, 3, Bond::SINGLE); // bond 2 // setup the stereochem: mol->getBondWithIdx(0)->setBondDir(Bond::ENDUPRIGHT); mol->getBondWithIdx(2)->setBondDir(Bond::ENDDOWNRIGHT); // do the chemistry perception: RDKit::MolOps::sanitizeMol(*mol); // Get the canonical SMILES, include stereochemistry: std::string smiles; smiles = MolToSmiles(*mol, true); BOOST_LOG(rdInfoLog) << " sample 1 SMILES: " << smiles << std::endl; } void WorkWithRingInfo() { // use a more complicated molecule to demonstrate querying about // ring information ROMol *mol = SmilesToMol("OC1CCC2C1CCCC2"); // the molecule from SmilesToMol is already sanitized, so we don't // need to worry about that. // work with ring information RingInfo *ringInfo = mol->getRingInfo(); TEST_ASSERT(ringInfo->numRings() == 2); // can ask how many rings an atom is in: TEST_ASSERT(ringInfo->numAtomRings(0) == 0); TEST_ASSERT(ringInfo->numAtomRings(1) == 1); TEST_ASSERT(ringInfo->numAtomRings(4) == 2); // same with bonds: TEST_ASSERT(ringInfo->numBondRings(0) == 0); TEST_ASSERT(ringInfo->numBondRings(1) == 1); // can check if an atom is in a ring of a particular size: TEST_ASSERT(!ringInfo->isAtomInRingOfSize(0, 5)); TEST_ASSERT(ringInfo->isAtomInRingOfSize(1, 5)); TEST_ASSERT(ringInfo->isAtomInRingOfSize(4, 5)); TEST_ASSERT(ringInfo->isAtomInRingOfSize(4, 6)); // same with bonds: TEST_ASSERT(!ringInfo->isBondInRingOfSize(0, 5)); TEST_ASSERT(ringInfo->isBondInRingOfSize(1, 5)); // can also get the full list of rings as atom indices: VECT_INT_VECT atomRings; // VECT_INT_VECT is vector< vector > atomRings = ringInfo->atomRings(); TEST_ASSERT(atomRings.size() == 2); TEST_ASSERT(atomRings[0].size() == 5); TEST_ASSERT(atomRings[1].size() == 6); // this sort is just here for test/demo purposes: std::sort(atomRings[0].begin(), atomRings[0].end()); TEST_ASSERT(atomRings[0][0] == 1); TEST_ASSERT(atomRings[0][1] == 2); TEST_ASSERT(atomRings[0][2] == 3); TEST_ASSERT(atomRings[0][3] == 4); TEST_ASSERT(atomRings[0][4] == 5); // same with bonds: VECT_INT_VECT bondRings; // VECT_INT_VECT is vector< vector > bondRings = ringInfo->bondRings(); TEST_ASSERT(bondRings.size() == 2); TEST_ASSERT(bondRings[0].size() == 5); TEST_ASSERT(bondRings[1].size() == 6); // the same trick played above with the contents of each ring // can be played, but we won't // count the number of rings of size 5: auto nRingsSize5 = std::count_if(atomRings.begin(), atomRings.end(), [](const INT_VECT &ring) { return ring.size() == 5; }); TEST_ASSERT(nRingsSize5 == 1); delete mol; // count the number of atoms in 5-rings where all the atoms // are aromatic: mol = SmilesToMol("C1CC2=C(C1)C1=C(NC3=C1C=CC=C3)C=C2"); ringInfo = mol->getRingInfo(); atomRings = ringInfo->atomRings(); unsigned int nMatchingAtoms = 0; for (const auto &ring : atomRings) { if (ring.size() != 5) { continue; } bool isAromatic = true; for (auto atom : ring) { if (!mol->getAtomWithIdx(atom)->getIsAromatic()) { isAromatic = false; break; } } if (isAromatic) { nMatchingAtoms += 5; } } TEST_ASSERT(nMatchingAtoms == 5); delete mol; // count the number of rings where all the bonds // are aromatic. mol = SmilesToMol("c1cccc2c1CCCC2"); ringInfo = mol->getRingInfo(); bondRings = ringInfo->bondRings(); unsigned int nAromaticRings = 0; for (const auto &ring : bondRings) { bool isAromatic = true; for (auto bond : ring) { if (!mol->getBondWithIdx(bond)->getIsAromatic()) { isAromatic = false; break; } } if (isAromatic) { ++nAromaticRings; } } TEST_ASSERT(nAromaticRings == 1); delete mol; } void WorkWithSmarts() { // demonstrate the use of substructure searching ROMol *mol = SmilesToMol("ClCC=CCC"); // a simple SMARTS pattern for rotatable bonds: ROMol *pattern = SmartsToMol("[!$(*#*)&!D1]-&!@[!$(*#*)&!D1]"); std::vector matches; unsigned int nMatches; nMatches = SubstructMatch(*mol, *pattern, matches); TEST_ASSERT(nMatches == 2); TEST_ASSERT(matches.size() == 2); // <- there are two rotatable bonds // a MatchVect is a vector of std::pairs with (patternIdx, molIdx): TEST_ASSERT(matches[0].size() == 2); TEST_ASSERT(matches[0][0].first == 0); TEST_ASSERT(matches[0][0].second == 1); TEST_ASSERT(matches[0][1].first == 1); TEST_ASSERT(matches[0][1].second == 2); delete pattern; delete mol; } void DepictDemo() { // demonstrate the use of the depiction-generation code2D coordinates: ROMol *mol = SmilesToMol("ClCC=CCC"); // generate the 2D coordinates: RDDepict::compute2DCoords(*mol); // generate a mol block (could also go to a file): std::string molBlock = MolToMolBlock(*mol); BOOST_LOG(rdInfoLog) << molBlock; delete mol; } void CleanupMolecule() { // an example of doing some cleaning up of a molecule before // calling the sanitizeMol function() // build: C1CC1C(:O):O auto mol = std::make_unique(); // add atoms and bonds: constexpr bool updateLabel = false; constexpr bool takeOwnership = true; mol->addAtom(new Atom(6), updateLabel, takeOwnership); // atom 0 mol->addAtom(new Atom(6), updateLabel, takeOwnership); // atom 1 mol->addAtom(new Atom(6), updateLabel, takeOwnership); // atom 2 mol->addAtom(new Atom(6), updateLabel, takeOwnership); // atom 3 mol->addAtom(new Atom(8), updateLabel, takeOwnership); // atom 4 mol->addAtom(new Atom(8), updateLabel, takeOwnership); // atom 5 mol->addBond(3, 4, Bond::AROMATIC); // bond 0 mol->addBond(3, 5, Bond::AROMATIC); // bond 1 mol->addBond(3, 2, Bond::SINGLE); // bond 2 mol->addBond(2, 1, Bond::SINGLE); // bond 3 mol->addBond(1, 0, Bond::SINGLE); // bond 4 mol->addBond(0, 2, Bond::SINGLE); // bond 5 // instead of calling sanitize mol, which would generate an error, // we'll perceive the rings, then take care of aromatic bonds // that aren't in a ring, then sanitize: MolOps::findSSSR(*mol); for (ROMol::BondIterator bondIt = mol->beginBonds(); bondIt != mol->endBonds(); ++bondIt) { if (((*bondIt)->getIsAromatic() || (*bondIt)->getBondType() == Bond::AROMATIC) && !mol->getRingInfo()->numBondRings((*bondIt)->getIdx())) { // remove the aromatic flag on the bond: (*bondIt)->setIsAromatic(false); // and cleanup its attached atoms as well (they were // also marked aromatic when the bond was added) (*bondIt)->getBeginAtom()->setIsAromatic(false); (*bondIt)->getEndAtom()->setIsAromatic(false); // NOTE: this isn't really reasonable: (*bondIt)->setBondType(Bond::SINGLE); } } // now it's safe to sanitize: RDKit::MolOps::sanitizeMol(*mol); // Get the canonical SMILES, include stereochemistry: std::string smiles; smiles = MolToSmiles(*mol, true); BOOST_LOG(rdInfoLog) << " fixed SMILES: " << smiles << std::endl; } void ReactionDemo() { // reaction smarts for a crude amide-bond formation definition: std::string sma = "[C:1](=[O:2])[OH].[N:3]>>[O:2]=[C:1][N:3]"; // construct the reaction: ChemicalReaction *rxn = RxnSmartsToChemicalReaction(sma); // now initialize it and check for errors: rxn->initReactantMatchers(); unsigned int nWarn, nError; rxn->validate(nWarn, nError); ROMol *mol; MOL_SPTR_VECT reacts; // build the list of reactants: ROMOL_SPTR react1(SmilesToMol("CC(=O)O")); ROMOL_SPTR react2(SmilesToMol("CCNCC1NC1")); reacts.push_back(react1); reacts.push_back(react2); // run the reaction, it returns a vector of vectors of product molecules: std::vector prods; prods = rxn->runReactants(reacts); // for each of the possible applications of the reaction to the reactants: for (unsigned int i = 0; i < prods.size(); ++i) { BOOST_LOG(rdInfoLog) << " product set: " << i << std::endl; // for each product of that application: for (unsigned int j = 0; j < prods[i].size(); ++j) { std::string psmiles = MolToSmiles(*prods[i][j], true); BOOST_LOG(rdInfoLog) << " product : " << j << " " << psmiles << std::endl; } } } int main(int argc, char *argv[]) { RDLog::InitLogs(); BuildSimpleMolecule(); WorkWithRingInfo(); WorkWithSmarts(); DepictDemo(); CleanupMolecule(); ReactionDemo(); }