void BuildSimpleMolecule() { // build the molecule: C/C=C\C RWMol *mol = new RWMol(); // add atoms and bonds: mol->addAtom(new Atom(6)); // atom 0 mol->addAtom(new Atom(6)); // atom 1 mol->addAtom(new Atom(6)); // atom 2 mol->addAtom(new Atom(6)); // 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(*(static_cast<ROMol *>(mol)), true); BOOST_LOG(rdInfoLog) << " sample 1 SMILES: " << smiles << std::endl; }
// Test the following GitHub issue: https://github.com/rdkit/rdkit/issues/114 void testIssue114(std::string rdbase) { BOOST_LOG(rdInfoLog) << "-----------------------------------" << std::endl; BOOST_LOG(rdInfoLog) << "-- testing GitHub issue #114 --" << std::endl; BOOST_LOG(rdInfoLog) << "-----------------------------------" << std::endl; std::string fName = rdbase + "/Code/GraphMol/FileParsers/test_data/EZ_mol2_issue114.mol2"; RWMol *mol = Mol2FileToMol(fName); TEST_ASSERT(mol); TEST_ASSERT(mol->getBondWithIdx(1)->getStereo() == Bond::STEREOZ); delete mol; BOOST_LOG(rdInfoLog) << "------------------------------------" << std::endl; BOOST_LOG(rdInfoLog) << "-- DONE --" << std::endl; BOOST_LOG(rdInfoLog) << "------------------------------------" << std::endl; }
ROMol *fragmentOnBonds( const ROMol &mol, const std::vector<unsigned int> &bondIndices, bool addDummies, const std::vector<std::pair<unsigned int, unsigned int> > *dummyLabels, const std::vector<Bond::BondType> *bondTypes, std::vector<unsigned int> *nCutsPerAtom) { PRECONDITION((!dummyLabels || dummyLabels->size() == bondIndices.size()), "bad dummyLabel vector"); PRECONDITION((!bondTypes || bondTypes->size() == bondIndices.size()), "bad bondType vector"); PRECONDITION((!nCutsPerAtom || nCutsPerAtom->size() == mol.getNumAtoms()), "bad nCutsPerAtom vector"); if (nCutsPerAtom) { BOOST_FOREACH (unsigned int &nCuts, *nCutsPerAtom) { nCuts = 0; } } RWMol *res = new RWMol(mol); if (!mol.getNumAtoms()) return res; std::vector<Bond *> bondsToRemove; bondsToRemove.reserve(bondIndices.size()); BOOST_FOREACH (unsigned int bondIdx, bondIndices) { bondsToRemove.push_back(res->getBondWithIdx(bondIdx)); }
bool kekulizeWorker(RWMol &mol, const INT_VECT &allAtms, boost::dynamic_bitset<> dBndCands, boost::dynamic_bitset<> dBndAdds, INT_VECT done, unsigned int maxBackTracks) { INT_DEQUE astack; INT_INT_DEQ_MAP options; int lastOpt = -1; boost::dynamic_bitset<> localBondsAdded(mol.getNumBonds()); // ok the algorithm goes something like this // - start with an atom that has been marked aromatic before // - check if it can have a double bond // - add its neighbors to the stack // - check if one of its neighbors can also have a double bond // - if yes add a double bond. // - if multiple neighbors can have double bonds - add them to a // options stack we may have to retrace out path if we chose the // wrong neighbor to add the double bond // - if double bond added update the candidates for double bond // - move to the next atom on the stack and repeat the process // - if an atom that can have multiple a double bond has no // neighbors that can take double bond - we made a mistake // earlier by picking a wrong candidate for double bond // - in this case back track to where we made the mistake int curr; INT_DEQUE btmoves; unsigned int numBT = 0; // number of back tracks so far while ((done.size() < allAtms.size()) || (astack.size() > 0)) { // pick a curr atom to work with if (astack.size() > 0) { curr = astack.front(); astack.pop_front(); } else { for (int allAtm : allAtms) { if (std::find(done.begin(), done.end(), allAtm) == done.end()) { curr = allAtm; break; } } } done.push_back(curr); // loop over the neighbors if we can add double bonds or // simply push them onto the stack INT_DEQUE opts; bool cCand = false; if (dBndCands[curr]) { cCand = true; } int ncnd; // if we are here because of backtracking if (options.find(curr) != options.end()) { opts = options[curr]; CHECK_INVARIANT(opts.size() > 0, ""); } else { RWMol::ADJ_ITER nbrIdx, endNbrs; boost::tie(nbrIdx, endNbrs) = mol.getAtomNeighbors(mol.getAtomWithIdx(curr)); while (nbrIdx != endNbrs) { // ignore if the neighbor has already been dealt with before if (std::find(done.begin(), done.end(), static_cast<int>(*nbrIdx)) != done.end()) { ++nbrIdx; continue; } // ignore if the neighbor is not part of the fused system if (std::find(allAtms.begin(), allAtms.end(), static_cast<int>(*nbrIdx)) == allAtms.end()) { ++nbrIdx; continue; } // if the neighbor is not on the stack add it if (std::find(astack.begin(), astack.end(), static_cast<int>(*nbrIdx)) == astack.end()) { astack.push_back(rdcast<int>(*nbrIdx)); } // check if the neighbor is also a candidate for a double bond // the refinement that we'll make to the candidate check we've already // done is to make sure that the bond is either flagged as aromatic // or involves a dummy atom. This was Issue 3525076. // This fix is not really 100% of the way there: a situation like // that for Issue 3525076 but involving a dummy atom in the cage // could lead to the same failure. The full fix would require // a fairly detailed analysis of all bonds in the molecule to determine // which of them is eligible to be converted. if (cCand && dBndCands[*nbrIdx] && (mol.getBondBetweenAtoms(curr, *nbrIdx)->getIsAromatic() || mol.getAtomWithIdx(curr)->getAtomicNum() == 0 || mol.getAtomWithIdx(*nbrIdx)->getAtomicNum() == 0)) { opts.push_back(rdcast<int>(*nbrIdx)); } // end of curr atoms can have a double bond ++nbrIdx; } // end of looping over neighbors } // now add a double bond from current to one of the neighbors if we can if (cCand) { if (opts.size() > 0) { ncnd = opts.front(); opts.pop_front(); Bond *bnd = mol.getBondBetweenAtoms(curr, ncnd); bnd->setBondType(Bond::DOUBLE); // remove current and the neighbor from the dBndCands list dBndCands[curr] = 0; dBndCands[ncnd] = 0; // add them to the list of bonds to which have been made double dBndAdds[bnd->getIdx()] = 1; localBondsAdded[bnd->getIdx()] = 1; // if this is an atom we previously visted and picked we // simply tried a different option now, overwrite the options // stored for this atoms if (options.find(curr) != options.end()) { if (opts.size() == 0) { options.erase(curr); btmoves.pop_back(); if (btmoves.size() > 0) { lastOpt = btmoves.back(); } else { lastOpt = -1; } } else { options[curr] = opts; } } else { // this is new atoms we are trying and have other // neighbors as options to add double bond store this to // the options stack, we may have made a mistake in // which one we chose and have to return here if (opts.size() > 0) { lastOpt = curr; btmoves.push_back(lastOpt); options[curr] = opts; } } } // end of adding a double bond else { // we have an atom that should be getting a double bond // but none of the neighbors can take one. Most likely // because of a wrong choice earlier so back track if ((lastOpt >= 0) && (numBT < maxBackTracks)) { // std::cerr << "PRE BACKTRACK" << std::endl; // mol.debugMol(std::cerr); backTrack(mol, options, lastOpt, done, astack, dBndCands, dBndAdds); // std::cerr << "POST BACKTRACK" << std::endl; // mol.debugMol(std::cerr); numBT++; } else { // undo any remaining changes we made while here // this was github #962 for (unsigned int bidx = 0; bidx < mol.getNumBonds(); ++bidx) { if (localBondsAdded[bidx]) { mol.getBondWithIdx(bidx)->setBondType(Bond::SINGLE); } } return false; } } // end of else try to backtrack } // end of curr atom atom being a cand for double bond } // end of while we are not done with all atoms return true; }