FragCatalogEntry::FragCatalogEntry(const ROMol *omol, const PATH_TYPE &path, const MatchVectType &aidToFid) { PRECONDITION(omol, "bad mol"); // start with the assumption that this entry is not participating in // any find of fingerprinting d_aToFmap.clear(); setBitId(-1); INT_MAP_INT aIdxMap; // a map from atom id in omol to the new atoms id in mol dp_mol = Subgraphs::pathToSubmol(*omol, path, false, aIdxMap); // Using Subgraphs functionality d_order = path.size(); // using aIdxMap initialize the location (and their IDs) of the // functional groups on dp_mol for (const auto &mvtci : aidToFid) { int oldAid = mvtci.first; if (aIdxMap.find(oldAid) != aIdxMap.end()) { int newAid = aIdxMap[oldAid]; if (d_aToFmap.find(newAid) != d_aToFmap.end()) { d_aToFmap[newAid].push_back(mvtci.second); } else { INT_VECT tmpVect; tmpVect.clear(); tmpVect.push_back(mvtci.second); d_aToFmap[newAid] = tmpVect; } } } dp_props = new Dict(); d_descrip = ""; }
// // Determine bond wedge state /// Bond::BondDir DetermineBondWedgeState(const Bond *bond, const INT_MAP_INT &wedgeBonds, const Conformer *conf) { PRECONDITION(bond, "no bond"); PRECONDITION(bond->getBondType() == Bond::SINGLE, "bad bond order for wedging"); const ROMol *mol = &(bond->getOwningMol()); PRECONDITION(mol, "no mol"); Bond::BondDir res = bond->getBondDir(); if (!conf) { return res; } int bid = bond->getIdx(); INT_MAP_INT_CI wbi = wedgeBonds.find(bid); if (wbi == wedgeBonds.end()) { return res; } unsigned int waid = wbi->second; Atom *atom, *bondAtom; // = bond->getBeginAtom(); if (bond->getBeginAtom()->getIdx() == waid) { atom = bond->getBeginAtom(); bondAtom = bond->getEndAtom(); } else { atom = bond->getEndAtom(); bondAtom = bond->getBeginAtom(); } Atom::ChiralType chiralType = atom->getChiralTag(); CHECK_INVARIANT(chiralType == Atom::CHI_TETRAHEDRAL_CW || chiralType == Atom::CHI_TETRAHEDRAL_CCW, ""); // if we got this far, we really need to think about it: INT_LIST neighborBondIndices; DOUBLE_LIST neighborBondAngles; RDGeom::Point3D centerLoc, tmpPt; centerLoc = conf->getAtomPos(atom->getIdx()); tmpPt = conf->getAtomPos(bondAtom->getIdx()); centerLoc.z = 0.0; tmpPt.z = 0.0; RDGeom::Point3D refVect = centerLoc.directionVector(tmpPt); neighborBondIndices.push_back(bond->getIdx()); neighborBondAngles.push_back(0.0); ROMol::OEDGE_ITER beg, end; boost::tie(beg, end) = mol->getAtomBonds(atom); while (beg != end) { Bond *nbrBond = (*mol)[*beg].get(); Atom *otherAtom = nbrBond->getOtherAtom(atom); if (nbrBond != bond) { tmpPt = conf->getAtomPos(otherAtom->getIdx()); tmpPt.z = 0.0; RDGeom::Point3D tmpVect = centerLoc.directionVector(tmpPt); double angle = refVect.signedAngleTo(tmpVect); if (angle < 0.0) angle += 2. * M_PI; INT_LIST::iterator nbrIt = neighborBondIndices.begin(); DOUBLE_LIST::iterator angleIt = neighborBondAngles.begin(); // find the location of this neighbor in our angle-sorted list // of neighbors: while (angleIt != neighborBondAngles.end() && angle > (*angleIt)) { ++angleIt; ++nbrIt; } neighborBondAngles.insert(angleIt, angle); neighborBondIndices.insert(nbrIt, nbrBond->getIdx()); } ++beg; } // at this point, neighborBondIndices contains a list of bond // indices from the central atom. They are arranged starting // at the reference bond in CCW order (based on the current // depiction). int nSwaps = atom->getPerturbationOrder(neighborBondIndices); // in the case of three-coordinated atoms we may have to worry about // the location of the implicit hydrogen - Issue 209 // Check if we have one of these situation // // 0 1 0 2 // * \*/ // 1 - C - 2 C // // here the hydrogen will be between 1 and 2 and we need to add an additional // swap if (neighborBondAngles.size() == 3) { // three coordinated DOUBLE_LIST::iterator angleIt = neighborBondAngles.begin(); ++angleIt; // the first is the 0 (or reference bond - we will ignoire that double angle1 = (*angleIt); ++angleIt; double angle2 = (*angleIt); if (angle2 - angle1 >= (M_PI - 1e-4)) { // we have the above situation nSwaps++; } } #ifdef VERBOSE_STEREOCHEM BOOST_LOG(rdDebugLog) << "--------- " << nSwaps << std::endl; std::copy(neighborBondIndices.begin(), neighborBondIndices.end(), std::ostream_iterator<int>(BOOST_LOG(rdDebugLog), " ")); BOOST_LOG(rdDebugLog) << std::endl; std::copy(neighborBondAngles.begin(), neighborBondAngles.end(), std::ostream_iterator<double>(BOOST_LOG(rdDebugLog), " ")); BOOST_LOG(rdDebugLog) << std::endl; #endif if (chiralType == Atom::CHI_TETRAHEDRAL_CCW) { if (nSwaps % 2 == 1) { // ^ reverse) { res = Bond::BEGINDASH; } else { res = Bond::BEGINWEDGE; } } else { if (nSwaps % 2 == 1) { // ^ reverse) { res = Bond::BEGINWEDGE; } else { res = Bond::BEGINDASH; } } return res; }
INT_MAP_INT pickBondsToWedge(const ROMol &mol) { // we need ring information; make sure findSSSR has been called before // if not call now if (!mol.getRingInfo()->isInitialized()) { MolOps::findSSSR(mol); } static int noNbrs = 100; INT_VECT nChiralNbrs(mol.getNumAtoms(), noNbrs); // start by looking for bonds that are already wedged for (ROMol::ConstBondIterator cbi = mol.beginBonds(); cbi != mol.endBonds(); ++cbi) { const Bond *bond = *cbi; if (bond->getBondDir() == Bond::BEGINWEDGE || bond->getBondDir() == Bond::BEGINDASH || bond->getBondDir() == Bond::UNKNOWN) { nChiralNbrs[bond->getBeginAtomIdx()] = noNbrs + 1; // std::cerr<<"skip: "<<bond->getBeginAtomIdx()<<std::endl; } } // now rank atoms by the number of chiral neighbors or Hs they have: bool chiNbrs = false; for (ROMol::ConstAtomIterator cai = mol.beginAtoms(); cai != mol.endAtoms(); ++cai) { const Atom *at = *cai; if (nChiralNbrs[at->getIdx()] > noNbrs) { // std::cerr<<" SKIPPING1: "<<at->getIdx()<<std::endl; continue; } Atom::ChiralType type = at->getChiralTag(); if (type != Atom::CHI_TETRAHEDRAL_CW && type != Atom::CHI_TETRAHEDRAL_CCW) continue; nChiralNbrs[at->getIdx()] = 0; chiNbrs = true; ROMol::ADJ_ITER nbrIdx, endNbrs; boost::tie(nbrIdx, endNbrs) = mol.getAtomNeighbors(at); while (nbrIdx != endNbrs) { const ATOM_SPTR nat = mol[*nbrIdx]; ++nbrIdx; if (nat->getAtomicNum() == 1) { // special case: it's an H... we weight these especially high: nChiralNbrs[at->getIdx()] -= 10; continue; } type = nat->getChiralTag(); if (type != Atom::CHI_TETRAHEDRAL_CW && type != Atom::CHI_TETRAHEDRAL_CCW) continue; nChiralNbrs[at->getIdx()] -= 1; } } std::vector<unsigned int> indices(mol.getNumAtoms()); for (unsigned int i = 0; i < mol.getNumAtoms(); ++i) indices[i] = i; if (chiNbrs) { std::sort(indices.begin(), indices.end(), Rankers::argless<INT_VECT>(nChiralNbrs)); } #if 0 std::cerr<<" nbrs: "; std::copy(nChiralNbrs.begin(),nChiralNbrs.end(),std::ostream_iterator<int>(std::cerr," ")); std::cerr<<std::endl; std::cerr<<" order: "; std::copy(indices.begin(),indices.end(),std::ostream_iterator<int>(std::cerr," ")); std::cerr<<std::endl; #endif // picks a bond for each atom that we will wedge when we write the mol file // here is what we are going to do // - at each chiral center look for a bond that is begins at the atom and // is not yet picked to be wedged for a different chiral center, preferring // bonds to Hs // - if we do not find a bond that begins at the chiral center - we will take // the first bond that is not yet picked by any other chiral centers // we use the orders calculated above to determine which order to do the // wedging INT_MAP_INT res; BOOST_FOREACH (unsigned int idx, indices) { if (nChiralNbrs[idx] > noNbrs) { // std::cerr<<" SKIPPING2: "<<idx<<std::endl; continue; // already have a wedged bond here } const Atom *atom = mol.getAtomWithIdx(idx); Atom::ChiralType type = atom->getChiralTag(); // the indices are ordered such that all chiral atoms come first. If // this has no chiral flag, we can stop the whole loop: if (type != Atom::CHI_TETRAHEDRAL_CW && type != Atom::CHI_TETRAHEDRAL_CCW) break; RDKit::ROMol::OBOND_ITER_PAIR atomBonds = mol.getAtomBonds(atom); std::vector<std::pair<int, int> > nbrScores; while (atomBonds.first != atomBonds.second) { const Bond *bond = mol[*atomBonds.first].get(); ++atomBonds.first; // can only wedge single bonds: if (bond->getBondType() != Bond::SINGLE) continue; int bid = bond->getIdx(); if (res.find(bid) == res.end()) { // very strong preference for Hs: if (bond->getOtherAtom(atom)->getAtomicNum() == 1) { nbrScores.push_back( std::make_pair(-1000, bid)); // lower than anything else can be continue; } int nbrScore = 0; // prefer neighbors that are nonchiral or have as few chiral neighbors // as possible: int oIdx = bond->getOtherAtomIdx(idx); if (nChiralNbrs[oIdx] < noNbrs) { // the counts are negative, so we have to subtract them off nbrScore -= 10 * nChiralNbrs[oIdx]; } // prefer non-ring bonds; nbrScore += mol.getRingInfo()->numBondRings(bid); nbrScores.push_back(std::make_pair(nbrScore, bid)); } } // There's still one situation where this whole thing can fail: an unlucky // situation where all neighbors of all neighbors of an atom are chiral and // that atom ends up being the last one picked for stereochem assignment. // // We'll catch that as an error here and hope that it's as unlikely to occur // as it seems like it is. (I'm going into this knowing that it's bound to // happen; I'll kick myself and do the hard solution at that point.) CHECK_INVARIANT(nbrScores.size(), "no eligible neighbors for chiral center"); std::sort(nbrScores.begin(), nbrScores.end(), Rankers::pairLess<int, int>()); res[nbrScores[0].second] = idx; } return res; }