void TorsionAngleContrib::getGrad(double *pos, double *grad) const { PRECONDITION(dp_forceField, "no owner"); PRECONDITION(pos, "bad vector"); PRECONDITION(grad, "bad vector"); RDGeom::Point3D iPoint(pos[3 * d_at1Idx], pos[3 * d_at1Idx + 1], pos[3 * d_at1Idx + 2]); RDGeom::Point3D jPoint(pos[3 * d_at2Idx], pos[3 * d_at2Idx + 1], pos[3 * d_at2Idx + 2]); RDGeom::Point3D kPoint(pos[3 * d_at3Idx], pos[3 * d_at3Idx + 1], pos[3 * d_at3Idx + 2]); RDGeom::Point3D lPoint(pos[3 * d_at4Idx], pos[3 * d_at4Idx + 1], pos[3 * d_at4Idx + 2]); double *g[4] = {&(grad[3 * d_at1Idx]), &(grad[3 * d_at2Idx]), &(grad[3 * d_at3Idx]), &(grad[3 * d_at4Idx])}; RDGeom::Point3D r[4] = {iPoint - jPoint, kPoint - jPoint, jPoint - kPoint, lPoint - kPoint}; RDGeom::Point3D t[2] = {r[0].crossProduct(r[1]), r[2].crossProduct(r[3])}; double d[2] = {t[0].length(), t[1].length()}; if (isDoubleZero(d[0]) || isDoubleZero(d[1])) { return; } t[0] /= d[0]; t[1] /= d[1]; double cosPhi = t[0].dotProduct(t[1]); clipToOne(cosPhi); double sinPhiSq = 1.0 - cosPhi * cosPhi; double sinPhi = ((sinPhiSq > 0.0) ? sqrt(sinPhiSq) : 0.0); double sin2Phi = 2.0 * sinPhi * cosPhi; double sin3Phi = 3.0 * sinPhi - 4.0 * sinPhi * sinPhiSq; // dE/dPhi is independent of cartesians: double dE_dPhi = 0.5 * (-(d_V1)*sinPhi + 2.0 * d_V2 * sin2Phi - 3.0 * d_V3 * sin3Phi); #if 0 if(dE_dPhi!=dE_dPhi){ std::cout << "\tNaN in Torsion("<<d_at1Idx<<","<<d_at2Idx<<","<<d_at3Idx<<","<<d_at4Idx<<")"<< std::endl; std::cout << "sin: " << sinPhi << std::endl; std::cout << "cos: " << cosPhi << std::endl; } #endif // FIX: use a tolerance here // this is hacky, but it's per the // recommendation from Niketic and Rasmussen: double sinTerm = -dE_dPhi * (isDoubleZero(sinPhi) ? (1.0 / cosPhi) : (1.0 / sinPhi)); Utils::calcTorsionGrad(r, t, d, g, sinTerm, cosPhi); }
void InversionContrib::getGrad(double *pos, double *grad) const { PRECONDITION(dp_forceField, "no owner"); PRECONDITION(pos, "bad vector"); PRECONDITION(grad, "bad vector"); RDGeom::Point3D p1(pos[3 * d_at1Idx], pos[3 * d_at1Idx + 1], pos[3 * d_at1Idx + 2]); RDGeom::Point3D p2(pos[3 * d_at2Idx], pos[3 * d_at2Idx + 1], pos[3 * d_at2Idx + 2]); RDGeom::Point3D p3(pos[3 * d_at3Idx], pos[3 * d_at3Idx + 1], pos[3 * d_at3Idx + 2]); RDGeom::Point3D p4(pos[3 * d_at4Idx], pos[3 * d_at4Idx + 1], pos[3 * d_at4Idx + 2]); double *g1 = &(grad[3 * d_at1Idx]); double *g2 = &(grad[3 * d_at2Idx]); double *g3 = &(grad[3 * d_at3Idx]); double *g4 = &(grad[3 * d_at4Idx]); RDGeom::Point3D rJI = p1 - p2; RDGeom::Point3D rJK = p3 - p2; RDGeom::Point3D rJL = p4 - p2; double dJI = rJI.length(); double dJK = rJK.length(); double dJL = rJL.length(); if (isDoubleZero(dJI) || isDoubleZero(dJK) || isDoubleZero(dJL)) { return; } rJI /= dJI; rJK /= dJK; rJL /= dJL; RDGeom::Point3D n = (-rJI).crossProduct(rJK); n /= n.length(); double cosY = n.dotProduct(rJL); double sinYSq = 1.0 - cosY * cosY; double sinY = std::max(((sinYSq > 0.0) ? sqrt(sinYSq) : 0.0), 1.0e-8); double cosTheta = rJI.dotProduct(rJK); double sinThetaSq = std::max(1.0 - cosTheta * cosTheta, 1.0e-8); double sinTheta = std::max(((sinThetaSq > 0.0) ? sqrt(sinThetaSq) : 0.0), 1.0e-8); // sin(2 * W) = 2 * sin(W) * cos(W) = 2 * cos(Y) * sin(Y) double dE_dW = -d_forceConstant * (d_C1 * cosY - 4.0 * d_C2 * cosY * sinY); RDGeom::Point3D t1 = rJL.crossProduct(rJK); RDGeom::Point3D t2 = rJI.crossProduct(rJL); RDGeom::Point3D t3 = rJK.crossProduct(rJI); double term1 = sinY * sinTheta; double term2 = cosY / (sinY * sinThetaSq); double tg1[3] = { (t1.x / term1 - (rJI.x - rJK.x * cosTheta) * term2) / dJI, (t1.y / term1 - (rJI.y - rJK.y * cosTheta) * term2) / dJI, (t1.z / term1 - (rJI.z - rJK.z * cosTheta) * term2) / dJI }; double tg3[3] = { (t2.x / term1 - (rJK.x - rJI.x * cosTheta) * term2) / dJK, (t2.y / term1 - (rJK.y - rJI.y * cosTheta) * term2) / dJK, (t2.z / term1 - (rJK.z - rJI.z * cosTheta) * term2) / dJK }; double tg4[3] = { (t3.x / term1 - rJL.x * cosY / sinY) / dJL, (t3.y / term1 - rJL.y * cosY / sinY) / dJL, (t3.z / term1 - rJL.z * cosY / sinY) / dJL }; for (unsigned int i = 0; i < 3; ++i) { g1[i] += dE_dW * tg1[i]; g2[i] += -dE_dW * (tg1[i] + tg3[i] + tg4[i]); g3[i] += dE_dW * tg3[i]; g4[i] += dE_dW * tg4[i]; } }
void OopBendContrib::getGrad(double *pos, double *grad) const { PRECONDITION(dp_forceField, "no owner"); PRECONDITION(pos, "bad vector"); PRECONDITION(grad, "bad vector"); RDGeom::Point3D iPoint(pos[3 * d_at1Idx], pos[3 * d_at1Idx + 1], pos[3 * d_at1Idx + 2]); RDGeom::Point3D jPoint(pos[3 * d_at2Idx], pos[3 * d_at2Idx + 1], pos[3 * d_at2Idx + 2]); RDGeom::Point3D kPoint(pos[3 * d_at3Idx], pos[3 * d_at3Idx + 1], pos[3 * d_at3Idx + 2]); RDGeom::Point3D lPoint(pos[3 * d_at4Idx], pos[3 * d_at4Idx + 1], pos[3 * d_at4Idx + 2]); double *g1 = &(grad[3 * d_at1Idx]); double *g2 = &(grad[3 * d_at2Idx]); double *g3 = &(grad[3 * d_at3Idx]); double *g4 = &(grad[3 * d_at4Idx]); RDGeom::Point3D rJI = iPoint - jPoint; RDGeom::Point3D rJK = kPoint - jPoint; RDGeom::Point3D rJL = lPoint - jPoint; double dJI = rJI.length(); double dJK = rJK.length(); double dJL = rJL.length(); if (isDoubleZero(dJI) || isDoubleZero(dJK) || isDoubleZero(dJL)) { return; } rJI /= dJI; rJK /= dJK; rJL /= dJL; RDGeom::Point3D n = (-rJI).crossProduct(rJK); n /= n.length(); double const c2 = MDYNE_A_TO_KCAL_MOL * DEG2RAD * DEG2RAD; double sinChi = rJL.dotProduct(n); clipToOne(sinChi); double cosChiSq = 1.0 - sinChi * sinChi; double cosChi = std::max(((cosChiSq > 0.0) ? sqrt(cosChiSq) : 0.0), 1.0e-8); double chi = RAD2DEG * asin(sinChi); double cosTheta = rJI.dotProduct(rJK); clipToOne(cosTheta); double sinThetaSq = std::max(1.0 - cosTheta * cosTheta, 1.0e-8); double sinTheta = std::max(((sinThetaSq > 0.0) ? sqrt(sinThetaSq) : 0.0), 1.0e-8); double dE_dChi = RAD2DEG * c2 * d_koop * chi; RDGeom::Point3D t1 = rJL.crossProduct(rJK); RDGeom::Point3D t2 = rJI.crossProduct(rJL); RDGeom::Point3D t3 = rJK.crossProduct(rJI); double term1 = cosChi * sinTheta; double term2 = sinChi / (cosChi * sinThetaSq); double tg1[3] = {(t1.x / term1 - (rJI.x - rJK.x * cosTheta) * term2) / dJI, (t1.y / term1 - (rJI.y - rJK.y * cosTheta) * term2) / dJI, (t1.z / term1 - (rJI.z - rJK.z * cosTheta) * term2) / dJI}; double tg3[3] = {(t2.x / term1 - (rJK.x - rJI.x * cosTheta) * term2) / dJK, (t2.y / term1 - (rJK.y - rJI.y * cosTheta) * term2) / dJK, (t2.z / term1 - (rJK.z - rJI.z * cosTheta) * term2) / dJK}; double tg4[3] = {(t3.x / term1 - rJL.x * sinChi / cosChi) / dJL, (t3.y / term1 - rJL.y * sinChi / cosChi) / dJL, (t3.z / term1 - rJL.z * sinChi / cosChi) / dJL}; for (unsigned int i = 0; i < 3; ++i) { g1[i] += dE_dChi * tg1[i]; g2[i] += -dE_dChi * (tg1[i] + tg3[i] + tg4[i]); g3[i] += dE_dChi * tg3[i]; g4[i] += dE_dChi * tg4[i]; } }
// ------------------------------------------------------------------------ // // // // ------------------------------------------------------------------------ void addEle(const ROMol &mol, int confId, MMFFMolProperties *mmffMolProperties, ForceFields::ForceField *field, boost::shared_array<boost::uint8_t> neighborMatrix, double nonBondedThresh, bool ignoreInterfragInteractions) { PRECONDITION(field, "bad ForceField"); PRECONDITION(mmffMolProperties, "bad MMFFMolProperties"); PRECONDITION(mmffMolProperties->isValid(), "missing atom types - invalid force-field"); std::ostream &oStream = mmffMolProperties->getMMFFOStream(); INT_VECT fragMapping; if (ignoreInterfragInteractions) { std::vector<ROMOL_SPTR> molFrags = MolOps::getMolFrags(mol, true, &fragMapping); } unsigned int nAtoms = mol.getNumAtoms(); double totalEleEnergy = 0.0; if (mmffMolProperties->getMMFFVerbosity()) { if (mmffMolProperties->getMMFFVerbosity() == MMFF_VERBOSITY_HIGH) { oStream << "\n" "E L E C T R O S T A T I C\n\n" "------ATOMS------ ATOM TYPES\n" " I J I J DISTANCE ENERGY\n" "--------------------------------------------------" << std::endl; } } const Conformer &conf = mol.getConformer(confId); double dielConst = mmffMolProperties->getMMFFDielectricConstant(); boost::uint8_t dielModel = mmffMolProperties->getMMFFDielectricModel(); for (unsigned int i = 0; i < nAtoms; ++i) { for (unsigned int j = i + 1; j < nAtoms; ++j) { if (ignoreInterfragInteractions && (fragMapping[i] != fragMapping[j])) { continue; } boost::uint8_t cell = getTwoBitCell(neighborMatrix, twoBitCellPos(nAtoms, i, j)); bool is1_4 = (cell == RELATION_1_4); if (cell >= RELATION_1_4) { if (isDoubleZero(mmffMolProperties->getMMFFPartialCharge(i)) || isDoubleZero(mmffMolProperties->getMMFFPartialCharge(j))) continue; double dist = (conf.getAtomPos(i) - conf.getAtomPos(j)).length(); if (dist > nonBondedThresh) { continue; } double chargeTerm = mmffMolProperties->getMMFFPartialCharge(i) * mmffMolProperties->getMMFFPartialCharge(j) / dielConst; EleContrib *contrib = new EleContrib( field, i, j, chargeTerm, dielModel, is1_4); field->contribs().push_back(ForceFields::ContribPtr(contrib)); if (mmffMolProperties->getMMFFVerbosity()) { const unsigned int iAtomType = mmffMolProperties->getMMFFAtomType(i); const unsigned int jAtomType = mmffMolProperties->getMMFFAtomType(j); const Atom *iAtom = mol.getAtomWithIdx(i); const Atom *jAtom = mol.getAtomWithIdx(j); const double eleEnergy = MMFF::Utils::calcEleEnergy( i, j, dist, chargeTerm, dielModel, is1_4); if (mmffMolProperties->getMMFFVerbosity() == MMFF_VERBOSITY_HIGH) { oStream << std::left << std::setw(2) << iAtom->getSymbol() << " #" << std::setw(5) << i + 1 << std::setw(2) << jAtom->getSymbol() << " #" << std::setw(5) << j + 1 << std::right << std::setw(5) << iAtomType << std::setw(5) << jAtomType << " " << std::fixed << std::setprecision(3) << std::setw(9) << dist << std::setw(10) << eleEnergy << std::endl; } totalEleEnergy += eleEnergy; } } } } if (mmffMolProperties->getMMFFVerbosity()) { if (mmffMolProperties->getMMFFVerbosity() == MMFF_VERBOSITY_HIGH) { oStream << std::endl; } oStream << "TOTAL ELECTROSTATIC ENERGY =" << std::right << std::setw(16) << std::fixed << std::setprecision(4) << totalEleEnergy << std::endl; } }
// ------------------------------------------------------------------------ // // // // ------------------------------------------------------------------------ void addStretchBend(const ROMol &mol, MMFFMolProperties *mmffMolProperties, ForceFields::ForceField *field) { PRECONDITION(field, "bad ForceField"); PRECONDITION(mmffMolProperties, "bad MMFFMolProperties"); PRECONDITION(mmffMolProperties->isValid(), "missing atom types - invalid force-field"); std::ostream &oStream = mmffMolProperties->getMMFFOStream(); unsigned int idx[3]; MMFFPropCollection *mmffProp = MMFFPropCollection::getMMFFProp(); std::pair<bool, const MMFFStbn *> mmffStbnParams; ROMol::ADJ_ITER nbr1Idx; ROMol::ADJ_ITER end1Nbrs; ROMol::ADJ_ITER nbr2Idx; ROMol::ADJ_ITER end2Nbrs; unsigned int nAtoms = mol.getNumAtoms(); double totalStretchBendEnergy = 0.0; RDGeom::PointPtrVect points; if (mmffMolProperties->getMMFFVerbosity()) { if (mmffMolProperties->getMMFFVerbosity() == MMFF_VERBOSITY_HIGH) { oStream << "\n" "S T R E T C H B E N D I N G\n\n" "-----------ATOMS----------- ATOM TYPES FF VALENCE " " DELTA DELTA DELTA F CON\n" " I J K I J K CLASS ANGLE " " ANGLE R(I,J) R(J,K) ENERGY I-J (J-K)\n" "-------------------------------------------------------------" "-----------------------------------------------------" << std::endl; } points = field->positions(); } for (idx[1] = 0; idx[1] < nAtoms; ++idx[1]) { const Atom *jAtom = mol.getAtomWithIdx(idx[1]); if (jAtom->getDegree() == 1) { continue; } unsigned int jAtomType = mmffMolProperties->getMMFFAtomType(idx[1]); const MMFFProp *mmffPropParamsCentralAtom = (*mmffProp)(jAtomType); if (mmffPropParamsCentralAtom->linh) { continue; } boost::tie(nbr1Idx, end1Nbrs) = mol.getAtomNeighbors(jAtom); unsigned int i = 0; for (; nbr1Idx != end1Nbrs; ++nbr1Idx) { const Atom *iAtom = mol[*nbr1Idx].get(); boost::tie(nbr2Idx, end2Nbrs) = mol.getAtomNeighbors(jAtom); unsigned int j = 0; for (; nbr2Idx != end2Nbrs; ++nbr2Idx) { const Atom *kAtom = mol[*nbr2Idx].get(); if (j < (i + 1)) { ++j; continue; } idx[0] = iAtom->getIdx(); idx[2] = kAtom->getIdx(); unsigned int stretchBendType; MMFFStbn mmffStbnParams; MMFFBond mmffBondParams[2]; MMFFAngle mmffAngleParams; if (mmffMolProperties->getMMFFStretchBendParams( mol, idx[0], idx[1], idx[2], stretchBendType, mmffStbnParams, mmffBondParams, mmffAngleParams)) { StretchBendContrib *contrib = new StretchBendContrib( field, idx[0], idx[1], idx[2], &mmffStbnParams, &mmffAngleParams, &mmffBondParams[0], &mmffBondParams[1]); field->contribs().push_back(ForceFields::ContribPtr(contrib)); if (mmffMolProperties->getMMFFVerbosity()) { unsigned int iAtomType = mmffMolProperties->getMMFFAtomType(idx[0]); unsigned int jAtomType = mmffMolProperties->getMMFFAtomType(idx[1]); unsigned int kAtomType = mmffMolProperties->getMMFFAtomType(idx[2]); const double dist1 = field->distance(idx[0], idx[1]); const double dist2 = field->distance(idx[1], idx[2]); const RDGeom::Point3D p1((*(points[idx[0]]))[0], (*(points[idx[0]]))[1], (*(points[idx[0]]))[2]); const RDGeom::Point3D p2((*(points[idx[1]]))[0], (*(points[idx[1]]))[1], (*(points[idx[1]]))[2]); const RDGeom::Point3D p3((*(points[idx[2]]))[0], (*(points[idx[2]]))[1], (*(points[idx[2]]))[2]); const double cosTheta = MMFF::Utils::calcCosTheta(p1, p2, p3, dist1, dist2); const double theta = RAD2DEG * acos(cosTheta); const std::pair<double, double> forceConstants = MMFF::Utils::calcStbnForceConstants(&mmffStbnParams); const std::pair<double, double> stretchBendEnergies = MMFF::Utils::calcStretchBendEnergy( dist1 - mmffBondParams[0].r0, dist2 - mmffBondParams[1].r0, theta - mmffAngleParams.theta0, forceConstants); if (mmffMolProperties->getMMFFVerbosity() == MMFF_VERBOSITY_HIGH) { if (!isDoubleZero(forceConstants.first)) { oStream << std::left << std::setw(2) << iAtom->getSymbol() << " #" << std::setw(5) << idx[0] + 1 << std::setw(2) << jAtom->getSymbol() << " #" << std::setw(5) << idx[1] + 1 << std::setw(2) << kAtom->getSymbol() << " #" << std::setw(5) << idx[2] + 1 << std::right << std::setw(5) << iAtomType << std::setw(5) << jAtomType << std::setw(5) << kAtomType << std::setw(6) << stretchBendType << " " << std::fixed << std::setprecision(3) << std::setw(10) << theta << std::setw(10) << theta - mmffAngleParams.theta0 << std::setw(10) << dist1 - mmffBondParams[0].r0 << std::setw(10) << dist2 - mmffBondParams[1].r0 << std::setw(10) << stretchBendEnergies.first << std::setw(10) << mmffStbnParams.kbaIJK << std::endl; } if (!isDoubleZero(forceConstants.second)) { oStream << std::left << std::setw(2) << kAtom->getSymbol() << " #" << std::setw(5) << idx[2] + 1 << std::setw(2) << jAtom->getSymbol() << " #" << std::setw(5) << idx[1] + 1 << std::setw(2) << iAtom->getSymbol() << " #" << std::setw(5) << idx[0] + 1 << std::right << std::setw(5) << kAtomType << std::setw(5) << jAtomType << std::setw(5) << iAtomType << std::setw(6) << stretchBendType << " " << std::fixed << std::setprecision(3) << std::setw(10) << theta << std::setw(10) << theta - mmffAngleParams.theta0 << std::setw(10) << dist1 - mmffBondParams[0].r0 << std::setw(10) << dist2 - mmffBondParams[1].r0 << std::setw(10) << stretchBendEnergies.second << std::setw(10) << mmffStbnParams.kbaKJI << std::endl; } } totalStretchBendEnergy += (stretchBendEnergies.first + stretchBendEnergies.second); } } ++j; } ++i; } } if (mmffMolProperties->getMMFFVerbosity()) { if (mmffMolProperties->getMMFFVerbosity() == MMFF_VERBOSITY_HIGH) { oStream << std::endl; } oStream << "TOTAL STRETCH-BEND ENERGY =" << std::right << std::setw(16) << std::fixed << std::setprecision(4) << totalStretchBendEnergy << std::endl; } }
void TorsionConstraintContrib::getGrad(double *pos, double *grad) const { PRECONDITION(dp_forceField,"no owner"); PRECONDITION(pos,"bad vector"); PRECONDITION(grad,"bad vector"); RDGeom::Point3D p1(pos[3 * d_at1Idx], pos[3 * d_at1Idx + 1], pos[3 * d_at1Idx + 2]); RDGeom::Point3D p2(pos[3 * d_at2Idx], pos[3 * d_at2Idx + 1], pos[3 * d_at2Idx + 2]); RDGeom::Point3D p3(pos[3 * d_at3Idx], pos[3 * d_at3Idx + 1], pos[3 * d_at3Idx + 2]); RDGeom::Point3D p4(pos[3 * d_at4Idx], pos[3 * d_at4Idx + 1], pos[3 * d_at4Idx + 2]); double *g[4] = { &(grad[3 * d_at1Idx]), &(grad[3 * d_at2Idx]), &(grad[3 * d_at3Idx]), &(grad[3 * d_at4Idx]) }; RDGeom::Point3D r[4] = { p1 - p2, p3 - p2, p2 - p3, p4 - p3 }; RDGeom::Point3D t[2] = { r[0].crossProduct(r[1]), r[2].crossProduct(r[3]) }; double d[2] = { t[0].length(), t[1].length() }; if (isDoubleZero(d[0]) || isDoubleZero(d[1])) { return; } t[0] /= d[0]; t[1] /= d[1]; double cosPhi = t[0].dotProduct(t[1]); double sinPhiSq = 1.0 - cosPhi * cosPhi; double sinPhi = ((sinPhiSq > 0.0) ? sqrt(sinPhiSq) : 0.0); // dE/dPhi is independent of cartesians: RDGeom::Point3D n123 = (-r[0]).crossProduct(r[1]); double n123SqLength = n123.lengthSq(); RDGeom::Point3D n234 = r[1].crossProduct(r[3]); double n234SqLength = n234.lengthSq(); RDGeom::Point3D m = n123.crossProduct(r[1]); // we want a signed dihedral, that's why we use atan2 instead of acos double dihedral = RAD2DEG * (-atan2(m.dotProduct(n234) / sqrt(n234SqLength * m.lengthSq()), n123.dotProduct(n234) / sqrt(n123SqLength * n234SqLength))); //double dihedral = RAD2DEG * acos(cosPhi); double ave = 0.5 * (d_minDihedralDeg + d_maxDihedralDeg); dihedral += 360.0 * boost::math::round((ave - dihedral) / 360.0); double dihedralTerm = 0.0; if (dihedral < d_minDihedralDeg) { dihedralTerm = dihedral - d_minDihedralDeg; } else if (dihedral > d_maxDihedralDeg) { dihedralTerm = dihedral - d_maxDihedralDeg; } double dE_dPhi = DEG2RAD * d_forceConstant * dihedralTerm; // FIX: use a tolerance here // this is hacky, but it's per the // recommendation from Niketic and Rasmussen: double sinTerm = -dE_dPhi * (isDoubleZero(sinPhi) ? (1.0 / cosPhi) : (1.0 / sinPhi)); Utils::calcTorsionGrad(r, t, d, g, sinTerm, cosPhi); }
void TorsionAngleContrib::getGrad(double *pos, double *grad) const { PRECONDITION(dp_forceField,"no owner"); PRECONDITION(pos,"bad vector"); PRECONDITION(grad,"bad vector"); RDGeom::Point3D iPoint(pos[3 * this->d_at1Idx], pos[3 * this->d_at1Idx + 1], pos[3 * this->d_at1Idx + 2]); RDGeom::Point3D jPoint(pos[3 * this->d_at2Idx], pos[3 * this->d_at2Idx + 1], pos[3 * this->d_at2Idx + 2]); RDGeom::Point3D kPoint(pos[3 * this->d_at3Idx], pos[3 * this->d_at3Idx + 1], pos[3 * this->d_at3Idx + 2]); RDGeom::Point3D lPoint(pos[3 * this->d_at4Idx], pos[3 * this->d_at4Idx + 1], pos[3 * this->d_at4Idx + 2]); double *g1 = &(grad[3 * this->d_at1Idx]); double *g2 = &(grad[3 * this->d_at2Idx]); double *g3 = &(grad[3 * this->d_at3Idx]); double *g4 = &(grad[3 * this->d_at4Idx]); RDGeom::Point3D r1 = iPoint - jPoint; RDGeom::Point3D r2 = kPoint - jPoint; RDGeom::Point3D r3 = jPoint - kPoint; RDGeom::Point3D r4 = lPoint - kPoint; RDGeom::Point3D t1 = r1.crossProduct(r2); RDGeom::Point3D t2 = r3.crossProduct(r4); double d1 = t1.length(); t1 /= d1; double d2 = t2.length(); t2 /= d2; if (isDoubleZero(d1) || isDoubleZero(d2)) { return; } double cosPhi = t1.dotProduct(t2); double sinPhiSq = 1.0 - cosPhi * cosPhi; double sinPhi = ((sinPhiSq > 0.0) ? sqrt(sinPhiSq) : 0.0); double sin2Phi = 2.0 * sinPhi * cosPhi; double sin3Phi = 3.0 * sinPhi - 4.0 * sinPhi * sinPhiSq; // dE/dPhi is independent of cartesians: double dE_dPhi = 0.5 * (-(this->d_V1) * sinPhi + 2.0 * this->d_V2 * sin2Phi - 3.0 * this->d_V3 * sin3Phi); #if 0 if(dE_dPhi!=dE_dPhi){ std::cout << "\tNaN in Torsion("<<this->d_at1Idx<<","<<this->d_at2Idx<<","<<this->d_at3Idx<<","<<this->d_at4Idx<<")"<< std::endl; std::cout << "sin: " << sinPhi << std::endl; std::cout << "cos: " << cosPhi << std::endl; } #endif // ------- // dTheta/dx is trickier: double dCos_dT1 = 1.0 / d1 * (t2.x - cosPhi * t1.x); double dCos_dT2 = 1.0 / d1 * (t2.y - cosPhi * t1.y); double dCos_dT3 = 1.0 / d1 * (t2.z - cosPhi * t1.z); double dCos_dT4 = 1.0 / d2 * (t1.x - cosPhi * t2.x); double dCos_dT5 = 1.0 / d2 * (t1.y - cosPhi * t2.y); double dCos_dT6 = 1.0 / d2 * (t1.z - cosPhi * t2.z); // FIX: use a tolerance here // this is hacky, but it's per the // recommendation from Niketic and Rasmussen: double sinTerm = -dE_dPhi * (isDoubleZero(sinPhi) ? (1.0 / cosPhi) : (1.0 / sinPhi)); g1[0] += sinTerm * (dCos_dT3 * r2.y - dCos_dT2 * r2.z); g1[1] += sinTerm * (dCos_dT1 * r2.z - dCos_dT3 * r2.x); g1[2] += sinTerm * (dCos_dT2 * r2.x - dCos_dT1 * r2.y); g2[0] += sinTerm * (dCos_dT2 * (r2.z - r1.z) + dCos_dT3 * (r1.y - r2.y) + dCos_dT5 * (-1.0 * r4.z) + dCos_dT6 * (r4.y)); g2[1] += sinTerm * (dCos_dT1 * (r1.z - r2.z) + dCos_dT3 * (r2.x - r1.x) + dCos_dT4 * (r4.z) + dCos_dT6 * (-1.0 * r4.x)); g2[2] += sinTerm * (dCos_dT1 * (r2.y - r1.y) + dCos_dT2 * (r1.x - r2.x) + dCos_dT4 * (-1.0 * r4.y) + dCos_dT5 * (r4.x)); g3[0] += sinTerm * (dCos_dT2 * (r1.z) + dCos_dT3 * (-1.0 * r1.y) + dCos_dT5 * (r4.z - r3.z) + dCos_dT6 * (r3.y - r4.y)); g3[1] += sinTerm * (dCos_dT1 * (-1.0 * r1.z) + dCos_dT3 * (r1.x) + dCos_dT4 * (r3.z - r4.z) + dCos_dT6 * (r4.x - r3.x)); g3[2] += sinTerm * (dCos_dT1 * (r1.y) + dCos_dT2 * (-1.0 * r1.x) + dCos_dT4 * (r4.y - r3.y) + dCos_dT5 * (r3.x - r4.x)); g4[0] += sinTerm * (dCos_dT5 * r3.z - dCos_dT6 * r3.y); g4[1] += sinTerm * (dCos_dT6 * r3.x - dCos_dT4 * r3.z); g4[2] += sinTerm * (dCos_dT4 * r3.y - dCos_dT5 * r3.x); }