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()
void addReactantAtomsAndBonds(const ChemicalReaction &rxn, RWMOL_SPTR product, const ROMOL_SPTR reactantSptr, const MatchVectType &match, const ROMOL_SPTR reactantTemplate, Conformer *productConf) { // start by looping over all matches and marking the reactant atoms that // have already been "added" by virtue of being in the product. We'll also // mark "skipped" atoms: those that are in the match, but not in this // particular product (or, perhaps, not in any product) // At the same time we'll set up a map between the indices of those // atoms and their index in the product. ReactantProductAtomMapping *mapping = getAtomMappingsReactantProduct( match, *reactantTemplate, product, reactantSptr->getNumAtoms()); boost::dynamic_bitset<> visitedAtoms(reactantSptr->getNumAtoms()); const ROMol *reactant = reactantSptr.get(); // ---------- ---------- ---------- ---------- ---------- ---------- // Loop over the bonds in the product and look for those that have // the NullBond property set. These are bonds for which no information // (other than their existance) was provided in the template: setReactantBondPropertiesToProduct(product, *reactant, mapping); // ---------- ---------- ---------- ---------- ---------- ---------- // Loop over the atoms in the match that were added to the product // From the corresponding atom in the reactant, do a graph traversal // to find other connected atoms that should be added: std::vector<const Atom *> chiralAtomsToCheck; for (const auto &matchIdx : match) { int reactantAtomIdx = matchIdx.second; if (mapping->mappedAtoms[reactantAtomIdx]) { CHECK_INVARIANT(mapping->reactProdAtomMap.find(reactantAtomIdx) != mapping->reactProdAtomMap.end(), "mapped reactant atom not present in product."); const Atom *reactantAtom = reactant->getAtomWithIdx(reactantAtomIdx); for (unsigned i = 0; i < mapping->reactProdAtomMap[reactantAtomIdx].size(); i++) { // here's a pointer to the atom in the product: unsigned productAtomIdx = mapping->reactProdAtomMap[reactantAtomIdx][i]; Atom *productAtom = product->getAtomWithIdx(productAtomIdx); setReactantAtomPropertiesToProduct(productAtom, *reactantAtom, rxn.getImplicitPropertiesFlag()); } // now traverse: addReactantNeighborsToProduct(*reactant, *reactantAtom, product, visitedAtoms, chiralAtomsToCheck, mapping); // now that we've added all the reactant's neighbors, check to see if // it is chiral in the reactant but is not in the reaction. If so // we need to worry about its chirality checkAndCorrectChiralityOfMatchingAtomsInProduct( *reactant, reactantAtomIdx, *reactantAtom, product, mapping); } } // end of loop over matched atoms // ---------- ---------- ---------- ---------- ---------- ---------- // now we need to loop over atoms from the reactants that were chiral but not // directly involved in the reaction in order to make sure their chirality // hasn't been disturbed checkAndCorrectChiralityOfProduct(chiralAtomsToCheck, product, mapping); // ---------- ---------- ---------- ---------- ---------- ---------- // Copy enhanced StereoGroup data from reactant to product if it is // still valid. Uses ChiralTag checks above. copyEnhancedStereoGroups(*reactant, product, *mapping); // ---------- ---------- ---------- ---------- ---------- ---------- // finally we may need to set the coordinates in the product conformer: if (productConf) { productConf->resize(product->getNumAtoms()); generateProductConformers(productConf, *reactant, mapping); } delete (mapping); } // end of addReactantAtomsAndBonds
BBS removeNonmatchingReagents(const ChemicalReaction &rxn, BBS bbs, const EnumerationParams ¶ms) { PRECONDITION(bbs.size() <= rxn.getNumReactantTemplates(), "Number of Reagents not compatible with reaction templates"); BBS result; result.resize(bbs.size()); for (size_t reactant_idx = 0; reactant_idx < bbs.size(); ++reactant_idx) { size_t removedCount = 0; const unsigned int maxMatches = (params.reagentMaxMatchCount == INT_MAX) ? 0 : rdcast<unsigned int>(params.reagentMaxMatchCount); ROMOL_SPTR reactantTemplate = rxn.getReactants()[reactant_idx]; for (size_t reagent_idx = 0; reagent_idx < bbs[reactant_idx].size(); ++reagent_idx) { ROMOL_SPTR mol = bbs[reactant_idx][reagent_idx]; size_t matches = countMatches(*mol.get(), *reactantTemplate.get(), maxMatches); bool removeReagent = false; if (!matches || matches > rdcast<size_t>(params.reagentMaxMatchCount)) { removeReagent = true; } if (!removeReagent && params.sanePartialProducts) { // see if we have any sane products in the results std::vector<MOL_SPTR_VECT> partialProducts = rxn.runReactant(mol, reactant_idx); for (size_t productTemplate_idx = 0; productTemplate_idx < partialProducts.size(); ++productTemplate_idx) { int saneProducts = 0; for (size_t product_idx = 0; product_idx < partialProducts[productTemplate_idx].size(); ++product_idx) { try { RWMol *m = dynamic_cast<RWMol *>( partialProducts[productTemplate_idx][product_idx].get()); MolOps::sanitizeMol(*m); saneProducts++; } catch (...) { } } if (!saneProducts) { // if any product template has no sane products, we bail removeReagent = true; break; } } } if (removeReagent) removedCount++; else result[reactant_idx].push_back(mol); } if (removedCount) { BOOST_LOG(rdInfoLog) << "Removed " << removedCount << " non matching reagents at template " << reactant_idx << std::endl; } } return result; }