// $Id$ // // Copyright (C) 2004-2012 Greg Landrum and Rational Discovery LLC // // @@ All Rights Reserved @@ // This file is part of the RDKit. // The contents are covered by the terms of the BSD license // which is included in the file license.txt, found at the root // of the RDKit source tree. // #include "Embedder.h" #include #include #include #include "BoundsMatrixBuilder.h" #include #include #include #include #include #include #include #include #include #include #include #include #define ERROR_TOL 0.00001 namespace RDKit { namespace DGeomHelpers { typedef std::pair INT_PAIR; typedef std::vector INT_PAIR_VECT; bool _embedPoints(RDGeom::PointPtrVect &positions, const DistGeom::BoundsMatPtr mmat, bool useRandomCoords,double boxSizeMult, bool randNegEig, unsigned int numZeroFail, double optimizerForceTol, double basinThresh, int seed, unsigned int maxIterations, const DistGeom::VECT_CHIRALSET &chiralCenters){ unsigned int nat = positions.size(); if(maxIterations==0){ maxIterations=10*nat; } RDNumeric::DoubleSymmMatrix distMat(nat, 0.0); // The basin threshold just gets us into trouble when we're using // random coordinates since it ends up ignoring 1-4 (and higher) // interactions. This causes us to get folded-up (and self-penetrating) // conformations for large flexible molecules if(useRandomCoords) basinThresh=1e8; bool gotCoords = false; unsigned int iter = 0; double largestDistance=-1.0; while ((gotCoords == false) && (iter < maxIterations)) { ++iter; if(!useRandomCoords){ if (seed > 0) { largestDistance=DistGeom::pickRandomDistMat(*mmat, distMat, iter*seed); } else { largestDistance=DistGeom::pickRandomDistMat(*mmat, distMat); } gotCoords = DistGeom::computeInitialCoords(distMat, positions, randNegEig, numZeroFail); } else { double boxSize; if(boxSizeMult>0){ boxSize=5.*boxSizeMult; } else { boxSize=-1*boxSizeMult; } if (seed > 0) { RDKit::rng_type &generator = RDKit::getRandomGenerator(); generator.seed(seed*iter); } gotCoords = DistGeom::computeRandomCoords(positions,boxSize); } } if (gotCoords) { ForceFields::ForceField *field = DistGeom::constructForceField(*mmat, positions, chiralCenters, 1.0, 0.1, 0,basinThresh); unsigned int nPasses=0; field->initialize(); //std::cerr<<"FIELD E: "<calcEnergy()<calcEnergy() > ERROR_TOL){ int needMore = 1; while(needMore){ needMore = field->minimize(200,optimizerForceTol); //needMore = field->minimize(200,1e-6); ++nPasses; } } //std::cerr<<" "<calcEnergy()<<" after npasses: "<0 || useRandomCoords) { ForceFields::ForceField *field2 = DistGeom::constructForceField(*mmat, positions, chiralCenters, 0.1, 1.0, 0, basinThresh); field2->initialize(); //std::cerr<<"FIELD2 E: "<calcEnergy()<calcEnergy() > ERROR_TOL){ int needMore = 1; int nPasses2=0; while(needMore){ needMore = field2->minimize(200,optimizerForceTol); ++nPasses2; } //std::cerr<<" "<calcEnergy()<<" after npasses: "<getAtomicNum() != 1) { //skip hydrogens if ((*ati)->hasProp("_CIPCode")) { // make a chiral set from the neighbors nbrs.clear(); nbrs.reserve(4); // find the neighbors of this atom and enter them into the // nbr list along with their CIPRanks boost::tie(beg,end) = mol.getAtomBonds(*ati); while (beg != end) { oatom = mol[*beg]->getOtherAtom(*ati); int rank; oatom->getProp("_CIPRank", rank); INT_PAIR rAid(rank, oatom->getIdx()); nbrs.push_back(rAid); ++beg; } // if we have less than 4 heavy atoms as neighbors, // we need to include the chiral center into the mix // we should at least have 3 though bool includeSelf = false; CHECK_INVARIANT(nbrs.size() >= 3, "Cannot be a chiral center"); std::sort(nbrs.begin(), nbrs.end()); if (nbrs.size() < 4) { int rank; (*ati)->getProp("_CIPRank", rank); INT_PAIR rAid(rank, (*ati)->getIdx()); nbrs.insert(nbrs.begin(), rAid); includeSelf = true; } // now create a chiral set and set the upper and lower bound on the volume std::string cipCode; (*ati)->getProp("_CIPCode", cipCode); if (cipCode == "S") { // postive chiral volume DistGeom::ChiralSet *cset = new DistGeom::ChiralSet(nbrs[0].second, nbrs[1].second, nbrs[2].second, nbrs[3].second, 5.0, 100.0); DistGeom::ChiralSetPtr cptr(cset); chiralCenters.push_back(cptr); } else { DistGeom::ChiralSet *cset = new DistGeom::ChiralSet(nbrs[0].second, nbrs[1].second, nbrs[2].second, nbrs[3].second, -100.0, -5.0); DistGeom::ChiralSetPtr cptr(cset); chiralCenters.push_back(cptr); } } // if block -chirality check } // if block - heavy atom check } // for loop over atoms } // end of _findChiralSets void _fillAtomPositions(RDGeom::Point3DConstPtrVect &pts, const Conformer &conf) { unsigned int na = conf.getNumAtoms(); pts.clear(); unsigned int ai; pts.reserve(na); for (ai = 0; ai < na; ++ai) { pts.push_back(&conf.getAtomPos(ai)); } } bool _isConfFarFromRest(const ROMol &mol, const Conformer &conf, double threshold) { // NOTE: it is tempting to use some triangle inequality to prune // conformations here but some basic testing has shown very // little advantage and given that the time for pruning fades in // comparison to embedding - we will use a simple for loop below // over all conformation until we find a match ROMol::ConstConformerIterator confi; RDGeom::Point3DConstPtrVect refPoints, prbPoints; _fillAtomPositions(refPoints, conf); bool res = true; unsigned int na = conf.getNumAtoms(); double ssrThres = na*threshold*threshold; RDGeom::Transform3D trans; double ssr; for (confi = mol.beginConformers(); confi != mol.endConformers(); confi++) { _fillAtomPositions(prbPoints, *(*confi)); ssr = RDNumeric::Alignments::AlignPoints(refPoints, prbPoints, trans); if (ssr < ssrThres) { res = false; break; } } return res; } int EmbedMolecule(ROMol &mol, unsigned int maxIterations, int seed, bool clearConfs, bool useRandomCoords,double boxSizeMult, bool randNegEig, unsigned int numZeroFail, const std::map *coordMap, double optimizerForceTol, bool ignoreSmoothingFailures, double basinThresh){ INT_VECT confIds; confIds=EmbedMultipleConfs(mol,1,maxIterations,seed,clearConfs, useRandomCoords,boxSizeMult,randNegEig, numZeroFail,-1.0,coordMap,optimizerForceTol, ignoreSmoothingFailures,basinThresh); int res; if(confIds.size()){ res=confIds[0]; } else { res=-1; } return res; } void adjustBoundsMatFromCoordMap(DistGeom::BoundsMatPtr mmat,unsigned int nAtoms, const std::map *coordMap){ for(std::map::const_iterator iIt=coordMap->begin(); iIt!=coordMap->end();++iIt){ int iIdx=iIt->first; const RDGeom::Point3D &iPoint=iIt->second; std::map::const_iterator jIt=iIt; while(++jIt != coordMap->end()){ int jIdx=jIt->first; const RDGeom::Point3D &jPoint=jIt->second; double dist=(iPoint-jPoint).length(); mmat->setUpperBound(iIdx,jIdx,dist); mmat->setLowerBound(iIdx,jIdx,dist); } } } INT_VECT EmbedMultipleConfs(ROMol &mol, unsigned int numConfs, unsigned int maxIterations, int seed, bool clearConfs, bool useRandomCoords,double boxSizeMult, bool randNegEig, unsigned int numZeroFail, double pruneRmsThresh, const std::map *coordMap, double optimizerForceTol, bool ignoreSmoothingFailures, double basinThresh){ INT_VECT fragMapping; std::vector molFrags=MolOps::getMolFrags(mol,true,&fragMapping); if(molFrags.size()>1 && coordMap){ BOOST_LOG(rdWarningLog)<<"Constrained conformer generation (via the coordMap argument) does not work with molecules that have multiple fragments."< confs; confs.reserve(numConfs); for(unsigned int i=0;i confsOk(numConfs); confsOk.set(); if (clearConfs) { mol.clearConformers(); } INT_VECT res; for(unsigned int fragIdx=0;fragIdxgetNumAtoms(); DistGeom::BoundsMatrix *mat = new DistGeom::BoundsMatrix(nAtoms); DistGeom::BoundsMatPtr mmat(mat); initBoundsMat(mmat); setTopolBounds(*piece, mmat, true, false); if(coordMap){ adjustBoundsMatFromCoordMap(mmat,nAtoms,coordMap); } if (!DistGeom::triangleSmoothBounds(mmat)) { // ok this bound matrix failed to triangle smooth - re-compute the bounds matrix // without 15 bounds and with VDW scaling initBoundsMat(mmat); setTopolBounds(*piece, mmat, false, true); if(coordMap){ adjustBoundsMatFromCoordMap(mmat,nAtoms,coordMap); } // try triangle smoothing again if (!DistGeom::triangleSmoothBounds(mmat)) { // ok, we're not going to be able to smooth this, if(ignoreSmoothingFailures){ // proceed anyway with the more relaxed bounds matrix initBoundsMat(mmat); setTopolBounds(*piece, mmat, false, true); if(coordMap){ adjustBoundsMatFromCoordMap(mmat,nAtoms,coordMap); } } else { BOOST_LOG(rdWarningLog)<<"Could not triangle bounds smooth molecule."<getNumAtoms();++li){ for(unsigned int lj=li+1;ljgetNumAtoms();++lj){ std::cerr<<" ("<getLowerBound(li,lj)<<" -> "<getUpperBound(li,lj)< 0) { fourD = true; } RDGeom::PointPtrVect positions; for (unsigned int i = 0; i < nAtoms; ++i) { if(fourD){ positions.push_back(new RDGeom::PointND(4)); } else { positions.push_back(new RDGeom::Point3D()); } } for (unsigned int ci=0; ci(fragIdx) ){ conf->setAtomPos(i, RDGeom::Point3D((*positions[fragAtomIdx])[0], (*positions[fragAtomIdx])[1], (*positions[fragAtomIdx])[2])); ++fragAtomIdx; } } } else { confsOk[ci]=0; } } for (unsigned int i = 0; i < nAtoms; ++i) { delete positions[i]; } } for(unsigned int ci=0;ci 0.0 && !_isConfFarFromRest(mol, *conf, pruneRmsThresh)) { delete conf; } else { int confId = (int)mol.addConformer(conf, true); res.push_back(confId); } } else { delete conf; } } return res; } } }