void setDihedralRad(Conformer &conf, unsigned int iAtomId, unsigned int jAtomId, unsigned int kAtomId, unsigned int lAtomId, double value) { RDGeom::POINT3D_VECT &pos = conf.getPositions(); URANGE_CHECK(iAtomId, pos.size()); URANGE_CHECK(jAtomId, pos.size()); URANGE_CHECK(kAtomId, pos.size()); URANGE_CHECK(lAtomId, pos.size()); ROMol &mol = conf.getOwningMol(); Bond *bondIJ = mol.getBondBetweenAtoms(iAtomId, jAtomId); if (!bondIJ) throw ValueErrorException("atoms i and j must be bonded"); Bond *bondJK = mol.getBondBetweenAtoms(jAtomId, kAtomId); if (!bondJK) throw ValueErrorException("atoms j and k must be bonded"); Bond *bondKL = mol.getBondBetweenAtoms(kAtomId, lAtomId); if (!bondKL) throw ValueErrorException("atoms k and l must be bonded"); if (queryIsBondInRing(bondJK)) throw ValueErrorException("bond (j,k) must not belong to a ring"); RDGeom::Point3D rIJ = pos[jAtomId] - pos[iAtomId]; double rIJSqLength = rIJ.lengthSq(); if (rIJSqLength <= 1.e-16) throw ValueErrorException("atoms i and j have identical 3D coordinates"); RDGeom::Point3D rJK = pos[kAtomId] - pos[jAtomId]; double rJKSqLength = rJK.lengthSq(); if (rJKSqLength <= 1.e-16) throw ValueErrorException("atoms j and k have identical 3D coordinates"); RDGeom::Point3D rKL = pos[lAtomId] - pos[kAtomId]; double rKLSqLength = rKL.lengthSq(); if (rKLSqLength <= 1.e-16) throw ValueErrorException("atoms k and l have identical 3D coordinates"); RDGeom::Point3D nIJK = rIJ.crossProduct(rJK); double nIJKSqLength = nIJK.lengthSq(); RDGeom::Point3D nJKL = rJK.crossProduct(rKL); double nJKLSqLength = nJKL.lengthSq(); RDGeom::Point3D m = nIJK.crossProduct(rJK); // we only need to rotate by delta with respect to the current dihedral value value -= -atan2(m.dotProduct(nJKL) / sqrt(nJKLSqLength * m.lengthSq()), nIJK.dotProduct(nJKL) / sqrt(nIJKSqLength * nJKLSqLength)); // our rotation axis is the (j,k) bond RDGeom::Point3D &rotAxisBegin = pos[jAtomId]; RDGeom::Point3D &rotAxisEnd = pos[kAtomId]; RDGeom::Point3D rotAxis = rotAxisEnd - rotAxisBegin; rotAxis.normalize(); // get all atoms bonded to k and loop through them std::list<unsigned int> alist; _toBeMovedIdxList(mol, jAtomId, kAtomId, alist); for (std::list<unsigned int>::iterator it = alist.begin(); it != alist.end(); ++it) { // translate atom so that it coincides with the origin of rotation pos[*it] -= rotAxisBegin; // rotate around our rotation axis RDGeom::Transform3D rotByAngle; rotByAngle.SetRotation(value, rotAxis); rotByAngle.TransformPoint(pos[*it]); // translate atom back pos[*it] += rotAxisBegin; } }
void transConformer(Conformer &conf, python::object trans) { PyObject *transObj = trans.ptr(); if (!PyArray_Check(transObj)) { throw_value_error("Expecting a numeric array for transformation"); } PyArrayObject *transMat = reinterpret_cast<PyArrayObject *>(transObj); unsigned int nrows = transMat->dimensions[0]; unsigned int dSize = nrows*nrows; double *inData = reinterpret_cast<double *>(transMat->data); RDGeom::Transform3D transform; double *tData = transform.getData(); memcpy(static_cast<void *>(tData), static_cast<void *>(inData), dSize*sizeof(double)); MolTransforms::transformConformer(conf, transform); }
PyObject *computeCanonTrans(const Conformer &conf, const RDGeom::Point3D *center=0, bool normalizeCovar=false, bool ignoreHs=true) { RDGeom::Transform3D *trans; trans = MolTransforms::computeCanonicalTransform(conf, center, normalizeCovar, ignoreHs); npy_intp dims[2]; dims[0] = 4; dims[1] = 4; PyArrayObject *res = (PyArrayObject *)PyArray_SimpleNew(2,dims,NPY_DOUBLE); double *resData=reinterpret_cast<double *>(res->data); const double *tdata = trans->getData(); memcpy(static_cast<void *>(resData), static_cast<const void *>(tdata), 4*4*sizeof(double)); delete trans; return PyArray_Return(res); }
void transformConformer(Conformer &conf, const RDGeom::Transform3D &trans) { RDGeom::POINT3D_VECT &positions = conf.getPositions(); RDGeom::POINT3D_VECT_I pi; for (pi = positions.begin(); pi != positions.end(); ++pi) { trans.TransformPoint(*pi); } }
void setAngleRad(Conformer &conf, unsigned int iAtomId, unsigned int jAtomId, unsigned int kAtomId, double value) { RDGeom::POINT3D_VECT &pos = conf.getPositions(); URANGE_CHECK(iAtomId, pos.size()); URANGE_CHECK(jAtomId, pos.size()); URANGE_CHECK(kAtomId, pos.size()); ROMol &mol = conf.getOwningMol(); Bond *bondJI = mol.getBondBetweenAtoms(jAtomId, iAtomId); if (!bondJI) throw ValueErrorException("atoms i and j must be bonded"); Bond *bondJK = mol.getBondBetweenAtoms(jAtomId, kAtomId); if (!bondJK) throw ValueErrorException("atoms j and k must be bonded"); if (queryIsBondInRing(bondJI) && queryIsBondInRing(bondJK)) throw ValueErrorException( "bonds (i,j) and (j,k) must not both belong to a ring"); RDGeom::Point3D rJI = pos[iAtomId] - pos[jAtomId]; double rJISqLength = rJI.lengthSq(); if (rJISqLength <= 1.e-16) throw ValueErrorException("atoms i and j have identical 3D coordinates"); RDGeom::Point3D rJK = pos[kAtomId] - pos[jAtomId]; double rJKSqLength = rJK.lengthSq(); if (rJKSqLength <= 1.e-16) throw ValueErrorException("atoms j and k have identical 3D coordinates"); // we only need to rotate by delta with respect to the current angle value value -= rJI.angleTo(rJK); RDGeom::Point3D &rotAxisBegin = pos[jAtomId]; // our rotation axis is the normal to the plane of atoms i, j, k RDGeom::Point3D rotAxisEnd = rJI.crossProduct(rJK) + pos[jAtomId]; RDGeom::Point3D rotAxis = rotAxisEnd - rotAxisBegin; rotAxis.normalize(); // get all atoms bonded to j and loop through them std::list<unsigned int> alist; _toBeMovedIdxList(mol, jAtomId, kAtomId, alist); for (std::list<unsigned int>::iterator it = alist.begin(); it != alist.end(); ++it) { // translate atom so that it coincides with the origin of rotation pos[*it] -= rotAxisBegin; // rotate around our rotation axis RDGeom::Transform3D rotByAngle; rotByAngle.SetRotation(value, rotAxis); rotByAngle.TransformPoint(pos[*it]); // translate atom back pos[*it] += rotAxisBegin; } }
void transformAtom(Atom *atom, RDGeom::Transform3D &tform) { PRECONDITION(atom, "no atom"); ROMol &mol = atom->getOwningMol(); for (ROMol::ConstConformerIterator ci = mol.beginConformers(); ci != mol.endConformers(); ci++) { RDGeom::Point3D &pos = (*ci)->getAtomPos(atom->getIdx()); tform.TransformPoint(pos); } // atom->setPos(pos); }
void _copyTransform(const PyArrayObject *transMat, RDGeom::Transform3D &trans) { unsigned int nrows = transMat->dimensions[0]; unsigned int ncols = transMat->dimensions[1]; if ((nrows != 4) || (ncols != 4)) { throw_value_error("The transform has to be square matrix, of size 4x4"); } if (transMat->descr->type_num != PyArray_DOUBLE) throw_value_error("Only double arrays allowed for transform object "); unsigned int dSize = nrows*nrows; const double *inData = reinterpret_cast<const double *>(transMat->data); double *tData = trans.getData(); memcpy(static_cast<void *>(tData), static_cast<const void *>(inData), dSize*sizeof(double)); }
RDGeom::Transform3D *computeCanonicalTransform(const Conformer &conf, const RDGeom::Point3D *center, bool normalizeCovar, bool ignoreHs) { RDGeom::Point3D origin; if (!center) { origin = computeCentroid(conf, ignoreHs); } else { origin = (*center); } RDNumeric::DoubleSymmMatrix *covMat = computeCovarianceMatrix(conf, origin, normalizeCovar, ignoreHs); // find the eigen values and eigen vectors for the covMat RDNumeric::DoubleMatrix eigVecs(3, 3); RDNumeric::DoubleVector eigVals(3); // if we have a single atom system we don't need to do anyhting other than // setting translation // translation unsigned int nAtms = conf.getNumAtoms(); RDGeom::Transform3D *trans = new RDGeom::Transform3D; // set the translation origin *= -1.0; // trans->SetTranslation(origin); // if we have a single atom system we don't need to do anyhting setting // translation is sufficient if (nAtms > 1) { RDNumeric::EigenSolvers::powerEigenSolver(3, *covMat, eigVals, eigVecs, conf.getNumAtoms()); // deal with zero eigen value systems unsigned int i, j, dim = 3; for (i = 0; i < 3; ++i) { if (fabs(eigVals.getVal(i)) < EIGEN_TOLERANCE) { dim--; } } CHECK_INVARIANT(dim >= 1, ""); if (dim < 3) { RDGeom::Point3D first(eigVecs.getVal(0, 0), eigVecs.getVal(0, 1), eigVecs.getVal(0, 2)); if (dim == 1) { // pick an arbitrary eigen vector perpendicular to the first vector RDGeom::Point3D second(first.getPerpendicular()); eigVecs.setVal(1, 0, second.x); eigVecs.setVal(1, 1, second.y); eigVecs.setVal(1, 2, second.z); if (eigVals.getVal(0) > 1.0) { eigVals.setVal(1, 1.0); } else { eigVals.setVal(1, eigVals.getVal(0) / 2.0); } } RDGeom::Point3D second(eigVecs.getVal(1, 0), eigVecs.getVal(1, 1), eigVecs.getVal(1, 2)); // pick the third eigen vector perpendicular to the first two RDGeom::Point3D third = first.crossProduct(second); eigVecs.setVal(2, 0, third.x); eigVecs.setVal(2, 1, third.y); eigVecs.setVal(2, 2, third.z); if (eigVals.getVal(1) > 1.0) { eigVals.setVal(2, 1.0); } else { eigVals.setVal(2, eigVals.getVal(1) / 2.0); } } // now set the transformation for (i = 0; i < 3; ++i) { for (j = 0; j < 3; ++j) { trans->setVal(i, j, eigVecs.getVal(i, j)); } } } // end of multiple atom system trans->TransformPoint(origin); trans->SetTranslation(origin); delete covMat; return trans; }
RDGeom::Point3D operator*(const RDGeom::Transform3D &t, const RDGeom::Point3D &pt) { RDGeom::Point3D res = pt; t.TransformPoint(res); return res; };
double AlignPoints(const RDGeom::Point3DConstPtrVect &refPoints, const RDGeom::Point3DConstPtrVect &probePoints, RDGeom::Transform3D &trans, const DoubleVector *weights, bool reflect, unsigned int maxIterations) { unsigned int npt = refPoints.size(); PRECONDITION(npt == probePoints.size(), "Mismatch in number of points"); trans.setToIdentity(); const DoubleVector *wts; double wtsSum; bool ownWts; if (weights) { PRECONDITION(npt == weights->size(), "Mismatch in number of points"); wts = weights; wtsSum = _sumOfWeights(*wts); ownWts = false; } else { wts = new DoubleVector(npt, 1.0); wtsSum = static_cast<double>(npt); ownWts = true; } RDGeom::Point3D rptSum = _weightedSumOfPoints(refPoints, *wts); RDGeom::Point3D pptSum = _weightedSumOfPoints(probePoints, *wts); double rptSumLenSq = _weightedSumOfLenSq(refPoints, *wts); double pptSumLenSq = _weightedSumOfLenSq(probePoints, *wts); double covMat[3][3]; // compute the co-variance matrix _computeCovarianceMat(refPoints, probePoints, *wts, covMat); if (ownWts) { delete wts; wts = 0; } if (reflect) { rptSum *= -1.0; reflectCovMat(covMat); } // convert the covariance matrix to a 4x4 matrix that needs to be diagonalized double quad[4][4]; _covertCovMatToQuad(covMat, rptSum, pptSum, wtsSum, quad); // get the eigenVecs and eigenVals for the matrix double eigenVecs[4][4], eigenVals[4]; jacobi(quad, eigenVals, eigenVecs, maxIterations); // get the quaternion double quater[4]; quater[0] = eigenVecs[0][0]; quater[1] = eigenVecs[1][0]; quater[2] = eigenVecs[2][0]; quater[3] = eigenVecs[3][0]; trans.SetRotationFromQuaternion(quater); if (reflect) { // put the flip in the rotation matrix trans.Reflect(); } // compute the SSR value double ssr = eigenVals[0] - (pptSum.lengthSq() + rptSum.lengthSq()) / wtsSum + rptSumLenSq + pptSumLenSq; if ((ssr < 0.0) && (fabs(ssr) < TOLERANCE)) { ssr = 0.0; } if (reflect) { rptSum *= -1.0; } // set the translation trans.TransformPoint(pptSum); RDGeom::Point3D move = rptSum; move -= pptSum; move /= wtsSum; trans.SetTranslation(move); return ssr; }