void setNewProductBond(const Bond &origB, RWMOL_SPTR product, unsigned bondBeginIdx, unsigned bondEndIdx) { unsigned bondIdx = product->addBond(bondBeginIdx, bondEndIdx, origB.getBondType()) - 1; Bond *newB = product->getBondWithIdx(bondIdx); newB->setBondDir(origB.getBondDir()); }
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); } } }
void WedgeMolBonds(ROMol &mol, const Conformer *conf) { PRECONDITION(conf, "no conformer"); INT_MAP_INT wedgeBonds = pickBondsToWedge(mol); for (ROMol::BondIterator bondIt = mol.beginBonds(); bondIt != mol.endBonds(); ++bondIt) { Bond *bond = *bondIt; if (bond->getBondType() == Bond::SINGLE) { Bond::BondDir dir = DetermineBondWedgeState(bond, wedgeBonds, conf); if (dir == Bond::BEGINWEDGE || dir == Bond::BEGINDASH) { bond->setBondDir(dir); } } } }
void updateDoubleBondNeighbors(ROMol &mol, Bond *dblBond, const Conformer *conf, boost::dynamic_bitset<> &needsDir, std::vector<unsigned int> &singleBondCounts) { // we want to deal only with double bonds: PRECONDITION(dblBond, "bad bond"); PRECONDITION(dblBond->getBondType() == Bond::DOUBLE, "not a double bond"); PRECONDITION(conf, "no conformer"); #if 0 std::cerr << "**********************\n"; std::cerr << "**********************\n"; std::cerr << "**********************\n"; std::cerr << "UDBN: "<<dblBond->getIdx()<<"\n"; #endif ROMol::OEDGE_ITER beg, end; Bond *bond1 = 0, *obond1 = 0; boost::tie(beg, end) = mol.getAtomBonds(dblBond->getBeginAtom()); while (beg != end) { Bond *tBond = mol[*beg].get(); if (tBond->getBondType() == Bond::SINGLE || tBond->getBondType() == Bond::AROMATIC) { // prefer bonds that already have their directionality set // or that are adjacent to more double bonds: if (!bond1) { bond1 = tBond; } else if (needsDir[tBond->getIdx()]) { if (singleBondCounts[tBond->getIdx()] > singleBondCounts[bond1->getIdx()]) { obond1 = bond1; bond1 = tBond; } else { obond1 = tBond; } } else { obond1 = bond1; bond1 = tBond; } } ++beg; } if (!bond1) { // no single bonds from the beginning atom, mark // the double bond as directionless and return: dblBond->setBondDir(Bond::EITHERDOUBLE); return; } Bond *bond2 = 0, *obond2 = 0; boost::tie(beg, end) = mol.getAtomBonds(dblBond->getEndAtom()); while (beg != end) { Bond *tBond = mol[*beg].get(); if (tBond->getBondType() == Bond::SINGLE || tBond->getBondType() == Bond::AROMATIC) { if (!bond2) { bond2 = tBond; } else if (needsDir[tBond->getIdx()]) { if (singleBondCounts[tBond->getIdx()] > singleBondCounts[bond2->getIdx()]) { obond2 = bond2; bond2 = tBond; } else { obond2 = tBond; } } else { // we already had a bond2 and we don't need to set the direction // on the new one, so swap. obond2 = bond2; bond2 = tBond; } } ++beg; } if (!bond2) { dblBond->setBondDir(Bond::EITHERDOUBLE); return; } CHECK_INVARIANT(bond1 && bond2, "no bonds found"); RDGeom::Point3D beginP = conf->getAtomPos(dblBond->getBeginAtomIdx()); RDGeom::Point3D endP = conf->getAtomPos(dblBond->getEndAtomIdx()); RDGeom::Point3D bond1P = conf->getAtomPos(bond1->getOtherAtomIdx(dblBond->getBeginAtomIdx())); RDGeom::Point3D bond2P = conf->getAtomPos(bond2->getOtherAtomIdx(dblBond->getEndAtomIdx())); // check for a linear arrangement of atoms on either end: bool linear = false; RDGeom::Point3D p1; RDGeom::Point3D p2; p1 = bond1P - beginP; p2 = endP - beginP; if (isLinearArrangement(p1, p2)) { if (!obond1) { linear = true; } else { // one of the bonds was linear; what about the other one? Bond *tBond = bond1; bond1 = obond1; obond1 = tBond; bond1P = conf->getAtomPos(bond1->getOtherAtomIdx(dblBond->getBeginAtomIdx())); p1 = bond1P - beginP; if (isLinearArrangement(p1, p2)) { linear = true; } } } if (!linear) { p1 = bond2P - endP; p2 = beginP - endP; if (isLinearArrangement(p1, p2)) { if (!obond2) { linear = true; } else { Bond *tBond = bond2; bond2 = obond2; obond2 = tBond; bond2P = conf->getAtomPos(bond2->getOtherAtomIdx(dblBond->getEndAtomIdx())); p1 = bond2P - beginP; if (isLinearArrangement(p1, p2)) { linear = true; } } } } if (linear) { dblBond->setBondDir(Bond::EITHERDOUBLE); return; } double ang = RDGeom::computeDihedralAngle(bond1P, beginP, endP, bond2P); bool sameTorsionDir; if (ang < M_PI / 2) { sameTorsionDir = false; } else { sameTorsionDir = true; } // std::cerr << " angle: "<<ang<<" sameTorsionDir: " <<sameTorsionDir<<"\n"; /* Time for some clarificatory text, because this gets really confusing really fast. The dihedral angle analysis above is based on viewing things with an atom order as follows: 1 \ 2 = 3 \ 4 so dihedrals > 90 correspond to sameDir=true however, the stereochemistry representation is based on something more like this: 2 \ 1 = 3 \ 4 (i.e. we consider the direction-setting single bonds to be starting at the double-bonded atom) */ bool reverseBondDir = sameTorsionDir; Atom *atom1 = dblBond->getBeginAtom(), *atom2 = dblBond->getEndAtom(); if (!needsDir[bond1->getIdx()]) { if (!needsDir[bond2->getIdx()]) { // check that we agree } else { if (bond1->getBeginAtom() != atom1) { reverseBondDir = !reverseBondDir; } setBondDirRelativeToAtom(bond2, atom2, bond1->getBondDir(), reverseBondDir, needsDir); } } else if (!needsDir[bond2->getIdx()]) { if (bond2->getBeginAtom() != atom2) { reverseBondDir = !reverseBondDir; } setBondDirRelativeToAtom(bond1, atom1, bond2->getBondDir(), reverseBondDir, needsDir); } else { setBondDirRelativeToAtom(bond1, atom1, Bond::ENDDOWNRIGHT, false, needsDir); setBondDirRelativeToAtom(bond2, atom2, Bond::ENDDOWNRIGHT, reverseBondDir, needsDir); } needsDir[bond1->getIdx()] = 0; needsDir[bond2->getIdx()] = 0; if (obond1 && needsDir[obond1->getIdx()]) { setBondDirRelativeToAtom(obond1, atom1, bond1->getBondDir(), bond1->getBeginAtom() == atom1, needsDir); needsDir[obond1->getIdx()] = 0; } if (obond2 && needsDir[obond2->getIdx()]) { setBondDirRelativeToAtom(obond2, atom2, bond2->getBondDir(), bond2->getBeginAtom() == atom2, needsDir); needsDir[obond2->getIdx()] = 0; } #if 0 std::cerr << " 1:"<<bond1->getIdx()<<" "; if(obond1) std::cerr<<obond1->getIdx()<<std::endl; else std::cerr<<"N/A"<<std::endl; std::cerr << " 2:"<<bond2->getIdx()<<" "; if(obond2) std::cerr<<obond2->getIdx()<<std::endl; else std::cerr<<"N/A"<<std::endl; std::cerr << "**********************\n"; std::cerr << "**********************\n"; std::cerr << "**********************\n"; #endif }
// finds cycles void dfsFindCycles(ROMol &mol, int atomIdx, int inBondIdx, std::vector<AtomColors> &colors, const UINT_VECT &ranks, INT_VECT &atomOrders, VECT_INT_VECT &atomRingClosures, const boost::dynamic_bitset<> *bondsInPlay, const std::vector<std::string> *bondSymbols, bool doRandom) { Atom *atom = mol.getAtomWithIdx(atomIdx); atomOrders.push_back(atomIdx); colors[atomIdx] = GREY_NODE; // --------------------- // // Build the list of possible destinations from here // // --------------------- std::vector<PossibleType> possibles; possibles.resize(0); ROMol::OBOND_ITER_PAIR bondsPair = mol.getAtomBonds(atom); possibles.reserve(bondsPair.second - bondsPair.first); while (bondsPair.first != bondsPair.second) { Bond *theBond = mol[*(bondsPair.first)]; bondsPair.first++; if (bondsInPlay && !(*bondsInPlay)[theBond->getIdx()]) continue; if (inBondIdx < 0 || theBond->getIdx() != static_cast<unsigned int>(inBondIdx)) { int otherIdx = theBond->getOtherAtomIdx(atomIdx); long rank = ranks[otherIdx]; // --------------------- // // things are a bit more complicated if we are sitting on a // ring atom. we would like to traverse first to the // ring-closure atoms, then to atoms outside the ring first, // then to atoms in the ring that haven't already been visited // (non-ring-closure atoms). // // Here's how the black magic works: // - non-ring atom neighbors have their original ranks // - ring atom neighbors have this added to their ranks: // (MAX_BONDTYPE - bondOrder)*MAX_NATOMS*MAX_NATOMS // - ring-closure neighbors lose a factor of: // (MAX_BONDTYPE+1)*MAX_NATOMS*MAX_NATOMS // // This tactic biases us to traverse to non-ring neighbors first, // original ordering if bond orders are all equal... crafty, neh? // // --------------------- if (!doRandom) { if (colors[otherIdx] == GREY_NODE) { rank -= static_cast<int>(MAX_BONDTYPE + 1) * MAX_NATOMS * MAX_NATOMS; if (!bondSymbols) { rank += static_cast<int>(MAX_BONDTYPE - theBond->getBondType()) * MAX_NATOMS; } else { const std::string &symb = (*bondSymbols)[theBond->getIdx()]; std::uint32_t hsh = gboost::hash_range(symb.begin(), symb.end()); rank += (hsh % MAX_NATOMS) * MAX_NATOMS; } } else if (theBond->getOwningMol().getRingInfo()->numBondRings( theBond->getIdx())) { if (!bondSymbols) { rank += static_cast<int>(MAX_BONDTYPE - theBond->getBondType()) * MAX_NATOMS * MAX_NATOMS; } else { const std::string &symb = (*bondSymbols)[theBond->getIdx()]; std::uint32_t hsh = gboost::hash_range(symb.begin(), symb.end()); rank += (hsh % MAX_NATOMS) * MAX_NATOMS * MAX_NATOMS; } } } else { // randomize the rank rank = std::rand(); } // std::cerr << " " << atomIdx << ": " << otherIdx << " " << // rank // << std::endl; // std::cerr<<"aIdx: "<< atomIdx <<" p: "<<otherIdx<<" Rank: // "<<ranks[otherIdx] <<" "<<colors[otherIdx]<<" // "<<theBond->getBondType()<<" "<<rank<<std::endl; possibles.push_back(PossibleType(rank, otherIdx, theBond)); } } // --------------------- // // Sort on ranks // // --------------------- std::sort(possibles.begin(), possibles.end(), _possibleCompare()); // if (possibles.size()) // std::cerr << " aIdx1: " << atomIdx // << " first: " << possibles.front().get<0>() << " " // << possibles.front().get<1>() << std::endl; // // --------------------- // // Now work the children // // --------------------- for (auto &possible : possibles) { int possibleIdx = possible.get<1>(); Bond *bond = possible.get<2>(); switch (colors[possibleIdx]) { case WHITE_NODE: // ----- // we haven't seen this node at all before, traverse // ----- dfsFindCycles(mol, possibleIdx, bond->getIdx(), colors, ranks, atomOrders, atomRingClosures, bondsInPlay, bondSymbols, doRandom); break; case GREY_NODE: // ----- // we've seen this, but haven't finished it (we're finishing a ring) // ----- atomRingClosures[possibleIdx].push_back(bond->getIdx()); atomRingClosures[atomIdx].push_back(bond->getIdx()); break; default: // ----- // this node has been finished. don't do anything. // ----- break; } } colors[atomIdx] = BLACK_NODE; } // namespace Canon