void setReactantBondPropertiesToProduct(RWMOL_SPTR product, const ROMol &reactant, ReactantProductAtomMapping *mapping) { ROMol::BOND_ITER_PAIR bondItP = product->getEdges(); while (bondItP.first != bondItP.second) { Bond *pBond = (*product)[*(bondItP.first)]; ++bondItP.first; if (pBond->hasProp(common_properties::NullBond) || pBond->hasProp(common_properties::_MolFileBondQuery)) { if (mapping->prodReactAtomMap.find(pBond->getBeginAtomIdx()) != mapping->prodReactAtomMap.end() && mapping->prodReactAtomMap.find(pBond->getEndAtomIdx()) != mapping->prodReactAtomMap.end()) { // the bond is between two mapped atoms from this reactant: unsigned begIdx = mapping->prodReactAtomMap[pBond->getBeginAtomIdx()]; unsigned endIdx = mapping->prodReactAtomMap[pBond->getEndAtomIdx()]; const Bond *rBond = reactant.getBondBetweenAtoms(begIdx, endIdx); if (!rBond) continue; pBond->setBondType(rBond->getBondType()); pBond->setBondDir(rBond->getBondDir()); pBond->setIsAromatic(rBond->getIsAromatic()); if (pBond->hasProp(common_properties::NullBond)) { pBond->clearProp(common_properties::NullBond); } } } } }
void StandardPDBResidueBondOrders(RWMol *mol) { RWMol::BondIterator bondIt; for (bondIt=mol->beginBonds(); bondIt!=mol->endBonds(); ++bondIt) { Bond *bond = *bondIt; if (bond->getBondType() == Bond::SINGLE) { Atom *beg = bond->getBeginAtom(); Atom *end = bond->getEndAtom(); if (StandardPDBDoubleBond(mol,beg,end)) bond->setBondType(Bond::DOUBLE); } } }
RWMOL_SPTR convertTemplateToMol(const ROMOL_SPTR prodTemplateSptr) { const ROMol *prodTemplate = prodTemplateSptr.get(); auto *res = new RWMol(); // --------- --------- --------- --------- --------- --------- // Initialize by making a copy of the product template as a normal molecule. // NOTE that we can't just use a normal copy because we do not want to end up // with query atoms or bonds in the product. // copy in the atoms: ROMol::ATOM_ITER_PAIR atItP = prodTemplate->getVertices(); while (atItP.first != atItP.second) { const Atom *oAtom = (*prodTemplate)[*(atItP.first++)]; auto *newAtom = new Atom(*oAtom); res->addAtom(newAtom, false, true); int mapNum; if (newAtom->getPropIfPresent(common_properties::molAtomMapNumber, mapNum)) { // set bookmarks for the mapped atoms: res->setAtomBookmark(newAtom, mapNum); // now clear the molAtomMapNumber property so that it doesn't // end up in the products (this was bug 3140490): newAtom->clearProp(common_properties::molAtomMapNumber); newAtom->setProp<int>(common_properties::reactionMapNum, mapNum); } newAtom->setChiralTag(Atom::CHI_UNSPECIFIED); // if the product-template atom has the inversion flag set // to 4 (=SET), then bring its stereochem over, otherwise we'll // ignore it: int iFlag; if (oAtom->getPropIfPresent(common_properties::molInversionFlag, iFlag)) { if (iFlag == 4) newAtom->setChiralTag(oAtom->getChiralTag()); } // check for properties we need to set: int val; if (newAtom->getPropIfPresent(common_properties::_QueryFormalCharge, val)) { newAtom->setFormalCharge(val); } if (newAtom->getPropIfPresent(common_properties::_QueryHCount, val)) { newAtom->setNumExplicitHs(val); newAtom->setNoImplicit(true); // this was github #1544 } if (newAtom->getPropIfPresent(common_properties::_QueryMass, val)) { // FIX: technically should do something with this // newAtom->setMass(val); } if (newAtom->getPropIfPresent(common_properties::_QueryIsotope, val)) { newAtom->setIsotope(val); } } // and the bonds: ROMol::BOND_ITER_PAIR bondItP = prodTemplate->getEdges(); while (bondItP.first != bondItP.second) { const Bond *oldB = (*prodTemplate)[*(bondItP.first++)]; unsigned int bondIdx; bondIdx = res->addBond(oldB->getBeginAtomIdx(), oldB->getEndAtomIdx(), oldB->getBondType()) - 1; // make sure we don't lose the bond dir information: Bond *newB = res->getBondWithIdx(bondIdx); newB->setBondDir(oldB->getBondDir()); // Special case/hack: // The product has been processed by the SMARTS parser. // The SMARTS parser tags unspecified bonds as single, but then adds // a query so that they match single or double // This caused Issue 1748846 // http://sourceforge.net/tracker/index.php?func=detail&aid=1748846&group_id=160139&atid=814650 // We need to fix that little problem now: if (oldB->hasQuery()) { // remember that the product has been processed by the SMARTS parser. std::string queryDescription = oldB->getQuery()->getDescription(); if (queryDescription == "BondOr" && oldB->getBondType() == Bond::SINGLE) { // We need to fix that little problem now: if (newB->getBeginAtom()->getIsAromatic() && newB->getEndAtom()->getIsAromatic()) { newB->setBondType(Bond::AROMATIC); newB->setIsAromatic(true); } else { newB->setBondType(Bond::SINGLE); newB->setIsAromatic(false); } } else if (queryDescription == "BondNull") { newB->setProp(common_properties::NullBond, 1); } } // copy properties over: bool preserveExisting = true; newB->updateProps(*static_cast<const RDProps *>(oldB), preserveExisting); } return RWMOL_SPTR(res); } // end of convertTemplateToMol()
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; }