diff --git a/Code/GraphMol/ChemReactions/ReactionRunner.cpp b/Code/GraphMol/ChemReactions/ReactionRunner.cpp index 26639feb6..0affb57b3 100644 --- a/Code/GraphMol/ChemReactions/ReactionRunner.cpp +++ b/Code/GraphMol/ChemReactions/ReactionRunner.cpp @@ -819,7 +819,8 @@ void checkProductChirality(Atom::ChiralType reactantChirality, void setReactantAtomPropertiesToProduct(Atom *productAtom, const Atom &reactantAtom, - bool setImplicitProperties) { + bool setImplicitProperties, + unsigned int reactantId) { // which properties need to be set from the reactant? if (productAtom->getAtomicNum() <= 0 || productAtom->hasProp(common_properties::_MolFileAtomQuery)) { @@ -838,8 +839,6 @@ void setReactantAtomPropertiesToProduct(Atom *productAtom, if (productAtom->hasProp(common_properties::_MolFileRLabel)) { productAtom->clearProp(common_properties::_MolFileRLabel); } - productAtom->setProp(common_properties::reactantAtomIdx, - reactantAtom.getIdx()); productAtom->setProp(WAS_DUMMY, true); } else { // remove bookkeeping labels (if present) @@ -849,6 +848,8 @@ void setReactantAtomPropertiesToProduct(Atom *productAtom, } productAtom->setProp(common_properties::reactantAtomIdx, reactantAtom.getIdx()); + productAtom->setProp(common_properties::reactantIdx, + reactantId); if (setImplicitProperties) { updateImplicitAtomProperties(productAtom, &reactantAtom); } @@ -905,7 +906,7 @@ void addMissingProductBonds(const Bond &origB, RWMOL_SPTR product, void addMissingProductAtom(const Atom &reactAtom, unsigned reactNeighborIdx, unsigned prodNeighborIdx, RWMOL_SPTR product, const ROMol &reactant, - ReactantProductAtomMapping *mapping) { + ReactantProductAtomMapping *mapping, unsigned int reactantId) { Atom *newAtom = nullptr; if (!reactAtom.hasQuery()) { newAtom = new Atom(reactAtom); @@ -915,6 +916,8 @@ void addMissingProductAtom(const Atom &reactAtom, unsigned reactNeighborIdx, unsigned reactAtomIdx = reactAtom.getIdx(); newAtom->setProp(common_properties::reactantAtomIdx, reactAtomIdx); + newAtom->setProp(common_properties::reactantIdx, + reactantId); unsigned productIdx = product->addAtom(newAtom, false, true); mapping->reactProdAtomMap[reactAtomIdx].push_back(productIdx); mapping->prodReactAtomMap[productIdx] = reactAtomIdx; @@ -939,7 +942,7 @@ void addReactantNeighborsToProduct( const ROMol &reactant, const Atom &reactantAtom, RWMOL_SPTR product, boost::dynamic_bitset<> &visitedAtoms, std::vector &chiralAtomsToCheck, - ReactantProductAtomMapping *mapping) { + ReactantProductAtomMapping *mapping, unsigned int reactantId) { std::list atomStack; atomStack.push_back(&reactantAtom); @@ -1048,7 +1051,7 @@ void addReactantNeighborsToProduct( const Atom *neighbor = reactant.getAtomWithIdx(*nbrIdx); for (unsigned int i : lReactantAtomProductIndex) { addMissingProductAtom(*neighbor, lreactIdx, i, product, reactant, - mapping); + mapping, reactantId); } // update the stack: atomStack.push_back(neighbor); @@ -1323,7 +1326,8 @@ void addReactantAtomsAndBonds(const ChemicalReaction &rxn, RWMOL_SPTR product, const ROMOL_SPTR reactantSptr, const MatchVectType &match, const ROMOL_SPTR reactantTemplate, - Conformer *productConf) { + Conformer *productConf, + unsigned int reactantId) { // 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 @@ -1363,7 +1367,7 @@ void addReactantAtomsAndBonds(const ChemicalReaction &rxn, RWMOL_SPTR product, unsigned productAtomIdx = mapping->reactProdAtomMap[reactantAtomIdx][i]; Atom *productAtom = product->getAtomWithIdx(productAtomIdx); setReactantAtomPropertiesToProduct(productAtom, *reactantAtom, - rxn.getImplicitPropertiesFlag()); + rxn.getImplicitPropertiesFlag(), reactantId); if (reactantAtom->hasQuery()) { // finally: if the reactant atom is a query we should copy over the // query information. We need to replace the atom to do this @@ -1375,7 +1379,7 @@ void addReactantAtomsAndBonds(const ChemicalReaction &rxn, RWMOL_SPTR product, } // now traverse: addReactantNeighborsToProduct(*reactant, *reactantAtom, product, - visitedAtoms, chiralAtomsToCheck, mapping); + visitedAtoms, chiralAtomsToCheck, mapping, reactantId); // 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 @@ -1525,7 +1529,7 @@ generateOneProductSet(const ChemicalReaction &rxn, for (auto iter = rxn.beginReactantTemplates(); iter != rxn.endReactantTemplates(); ++iter, reactantId++) { addReactantAtomsAndBonds(rxn, product, reactants.at(reactantId), - reactantsMatch.at(reactantId), *iter, conf); + reactantsMatch.at(reactantId), *iter, conf, reactantId); } if (doConfs) { diff --git a/Code/GraphMol/ChemReactions/catch_tests.cpp b/Code/GraphMol/ChemReactions/catch_tests.cpp index 5b116402b..6e9ef301f 100644 --- a/Code/GraphMol/ChemReactions/catch_tests.cpp +++ b/Code/GraphMol/ChemReactions/catch_tests.cpp @@ -2228,3 +2228,41 @@ TEST_CASE("Structural fingerprints values") { CHECK(TanimotoSimilarity(*fp1, *fp2) == 0.4); } } + +TEST_CASE( + "Github #6015: react_idx property") { + SECTION("Ensure that atoms are marked with their reactant idx") { + std::unique_ptr rxn{ + RxnSmartsToChemicalReaction("[C:1][O:2].[N:3][S:4]>>[C:1][O:2][N:3][S:4]C")}; + REQUIRE(rxn); + rxn->initReactantMatchers(); + ROMOL_SPTR mol1(SmilesToMol("CO")); + ROMOL_SPTR mol2(SmilesToMol("NSP")); + std::vector reactants{mol1, mol2}; + REQUIRE(reactants.size() == 2); + REQUIRE(reactants[0]); + REQUIRE(reactants[1]); + + auto products = rxn->runReactants(reactants); + REQUIRE(products.size() == 1); + + CHECK(products[0][0]->getAtomWithIdx(0)->getProp("react_idx") == 0); + CHECK(products[0][0]->getAtomWithIdx(0)->getProp("react_atom_idx") == 0); + + CHECK(products[0][0]->getAtomWithIdx(1)->getProp("react_idx") == 0); + CHECK(products[0][0]->getAtomWithIdx(1)->getProp("react_atom_idx") == 1); + + CHECK(products[0][0]->getAtomWithIdx(2)->getProp("react_idx") == 1); + CHECK(products[0][0]->getAtomWithIdx(2)->getProp("react_atom_idx") == 0); + + CHECK(products[0][0]->getAtomWithIdx(3)->getProp("react_idx") == 1); + CHECK(products[0][0]->getAtomWithIdx(3)->getProp("react_atom_idx") == 1); + + CHECK(products[0][0]->getAtomWithIdx(4)->hasProp("react_atom_idx") == false); + CHECK(products[0][0]->getAtomWithIdx(4)->hasProp("react_idx") == false); + + CHECK(products[0][0]->getAtomWithIdx(5)->getProp("react_idx") == 1); + CHECK(products[0][0]->getAtomWithIdx(5)->getProp("react_atom_idx") == 2); + + } +} diff --git a/Code/GraphMol/MolEnumerator/MolEnumerator.cpp b/Code/GraphMol/MolEnumerator/MolEnumerator.cpp index f01035aa5..d2246e975 100644 --- a/Code/GraphMol/MolEnumerator/MolEnumerator.cpp +++ b/Code/GraphMol/MolEnumerator/MolEnumerator.cpp @@ -70,6 +70,7 @@ void clearReactionProps(ROMol &mol) { mol.clearComputedProps(includeRings); for (auto atom : mol.atoms()) { atom->clearProp(common_properties::reactantAtomIdx); + atom->clearProp(common_properties::reactantIdx); atom->clearProp("was_dummy"); atom->clearProp(common_properties::reactionMapNum); } diff --git a/Code/RDGeneral/types.cpp b/Code/RDGeneral/types.cpp index 764976e42..dd8569137 100644 --- a/Code/RDGeneral/types.cpp +++ b/Code/RDGeneral/types.cpp @@ -58,6 +58,7 @@ const std::string _QueryMass = "_QueryMass"; const std::string _ReactionDegreeChanged = "_ReactionDegreeChanged"; const std::string reactantAtomIdx = "react_atom_idx"; const std::string reactionMapNum = "old_mapno"; +const std::string reactantIdx = "react_idx"; const std::string _RingClosures = "_RingClosures"; const std::string _SLN_s = "_SLN_s"; diff --git a/Code/RDGeneral/types.h b/Code/RDGeneral/types.h index 9c37a4971..5befd7210 100644 --- a/Code/RDGeneral/types.h +++ b/Code/RDGeneral/types.h @@ -220,6 +220,7 @@ RDKIT_RDGENERAL_EXPORT extern const std::string _rgroupTargetAtoms; RDKIT_RDGENERAL_EXPORT extern const std::string _rgroupTargetBonds; RDKIT_RDGENERAL_EXPORT extern const std::string reactantAtomIdx; RDKIT_RDGENERAL_EXPORT extern const std::string reactionMapNum; +RDKIT_RDGENERAL_EXPORT extern const std::string reactantIdx; // SLN RDKIT_RDGENERAL_EXPORT extern const std::string