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()