// Copyright (C) 2014-2025 Greg Landrum and other RDKit contributors // @@ 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 #include #include #include #include #include #include #include "AtomTyper.h" #include "Builder.h" namespace RDKit { namespace MMFF { using namespace ForceFields::MMFF; namespace Tools { // ------------------------------------------------------------------------ // // // // ------------------------------------------------------------------------ void addBonds(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(); double totalBondStretchEnergy = 0.0; if (mmffMolProperties->getMMFFVerbosity()) { if (mmffMolProperties->getMMFFVerbosity() == MMFF_VERBOSITY_HIGH) { oStream << "\n" "B O N D S T R E T C H I N G\n\n" "------ATOMS------ ATOM TYPES FF BOND IDEAL " " FORCE\n" " I J I J CLASS LENGTH LENGTH " "DIFF. ENERGY CONSTANT\n" "-------------------------------------------------------------" "------------------------" << std::endl; } } auto contrib = std::make_unique(field); bool hasContrib = false; for (const auto bond : mol.bonds()) { unsigned int idx1 = bond->getBeginAtomIdx(); unsigned int idx2 = bond->getEndAtomIdx(); unsigned int bondType; MMFFBond mmffBondParams; if (mmffMolProperties->getMMFFBondStretchParams(mol, idx1, idx2, bondType, mmffBondParams)) { contrib->addTerm(idx1, idx2, &mmffBondParams); hasContrib = true; if (mmffMolProperties->getMMFFVerbosity()) { unsigned int iAtomType = mmffMolProperties->getMMFFAtomType(idx1); unsigned int jAtomType = mmffMolProperties->getMMFFAtomType(idx2); const Atom *iAtom = bond->getBeginAtom(); const Atom *jAtom = bond->getEndAtom(); const double dist = field->distance(idx1, idx2); const double bondStretchEnergy = MMFF::Utils::calcBondStretchEnergy( mmffBondParams.r0, mmffBondParams.kb, dist); if (mmffMolProperties->getMMFFVerbosity() == MMFF_VERBOSITY_HIGH) { oStream << std::left << std::setw(2) << iAtom->getSymbol() << " #" << std::setw(5) << idx1 + 1 << std::setw(2) << jAtom->getSymbol() << " #" << std::setw(5) << idx2 + 1 << std::right << std::setw(5) << iAtomType << std::setw(5) << jAtomType << std::setw(6) << bondType << " " << std::fixed << std::setprecision(3) << std::setw(9) << dist << std::setw(9) << mmffBondParams.r0 << std::setw(9) << dist - mmffBondParams.r0 << std::setw(10) << bondStretchEnergy << std::setw(10) << mmffBondParams.kb << std::endl; } totalBondStretchEnergy += bondStretchEnergy; } } } if (mmffMolProperties->getMMFFVerbosity()) { if (mmffMolProperties->getMMFFVerbosity() == MMFF_VERBOSITY_HIGH) { oStream << std::endl; } oStream << "TOTAL BOND STRETCH ENERGY =" << std::right << std::setw(16) << std::fixed << std::setprecision(4) << totalBondStretchEnergy << std::endl; } if (hasContrib) { field->contribs().push_back(ForceFields::ContribPtr(contrib.release())); } } unsigned int twoBitCellPos(unsigned int N, int i, int j) { if (j < i) { std::swap(i, j); } return i * (N - 1) - (i - 1) * i / 2 + j; } void setTwoBitCell(boost::shared_array &res, unsigned int pos, std::uint8_t value) { res[pos] = value; } std::uint8_t getTwoBitCell(boost::shared_array &res, unsigned int pos) { return res[pos]; } // ------------------------------------------------------------------------ // // the two-bit matrix returned by this contains: // 0: if atoms i and j are directly connected // 1: if atoms i and j are connected via an atom // 2: if atoms i and j are in a 1,4 relationship // 3: otherwise // // ------------------------------------------------------------------------ boost::shared_array buildNeighborMatrix(const ROMol &mol) { const std::uint8_t RELATION_1_X_INIT = RELATION_1_X | (RELATION_1_X << 2) | (RELATION_1_X << 4) | (RELATION_1_X << 6); unsigned int nAtoms = mol.getNumAtoms(); unsigned nTwoBitCells = nAtoms * (nAtoms + 1) / 2; boost::shared_array res(new std::uint8_t[nTwoBitCells]); std::memset(res.get(), RELATION_1_X_INIT, nTwoBitCells); constexpr bool useBO = false; constexpr bool useAtomWts = false; auto dmat = MolOps::getDistanceMat(mol, useBO, useAtomWts); for (unsigned i = 0; i < nAtoms; ++i) { setTwoBitCell(res, twoBitCellPos(nAtoms, i, i), RELATION_1_X); for (unsigned j = i + 1; j < nAtoms; ++j) { if (dmat[i * nAtoms + j] == 1.0) { setTwoBitCell(res, twoBitCellPos(nAtoms, i, j), RELATION_1_2); } else if (dmat[i * nAtoms + j] == 2.0) { setTwoBitCell(res, twoBitCellPos(nAtoms, i, j), RELATION_1_3); } else if (dmat[i * nAtoms + j] == 3.0) { setTwoBitCell(res, twoBitCellPos(nAtoms, i, j), RELATION_1_4); } else if (dmat[i * nAtoms + j] < 1e7) { // the distance matrix sets the distance to 1e8 for atoms that have no // connecting path, so here we know that there's at least a connection setTwoBitCell(res, twoBitCellPos(nAtoms, i, j), RELATION_1_X); } } } return res; } // ------------------------------------------------------------------------ // // // // ------------------------------------------------------------------------ void addAngles(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]; const MMFFPropCollection *mmffProp = DefaultParameters::getMMFFProp(); ROMol::ADJ_ITER nbr1Idx; ROMol::ADJ_ITER end1Nbrs; ROMol::ADJ_ITER nbr2Idx; ROMol::ADJ_ITER end2Nbrs; unsigned int nAtoms = mol.getNumAtoms(); double totalAngleBendEnergy = 0.0; RDGeom::PointPtrVect points; if (mmffMolProperties->getMMFFVerbosity()) { if (mmffMolProperties->getMMFFVerbosity() == MMFF_VERBOSITY_HIGH) { oStream << "\n" "A N G L E B E N D I N G\n\n" "-----------ATOMS----------- ATOM TYPES FF VALENCE " " IDEAL FORCE\n" " I J K I J K CLASS ANGLE " " ANGLE DIFF. ENERGY CONSTANT\n" "-------------------------------------------------------------" "-----------------------------------------" << std::endl; } points = field->positions(); } auto contrib = std::make_unique(field); bool hasContrib = false; 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); boost::tie(nbr1Idx, end1Nbrs) = mol.getAtomNeighbors(jAtom); for (; nbr1Idx != end1Nbrs; ++nbr1Idx) { const Atom *iAtom = mol[*nbr1Idx]; idx[0] = iAtom->getIdx(); boost::tie(nbr2Idx, end2Nbrs) = mol.getAtomNeighbors(jAtom); for (; nbr2Idx != end2Nbrs; ++nbr2Idx) { if (nbr2Idx < (nbr1Idx + 1)) { continue; } const Atom *kAtom = mol[*nbr2Idx]; idx[2] = kAtom->getIdx(); unsigned int angleType; MMFFAngle mmffAngleParams; if (mmffMolProperties->getMMFFAngleBendParams( mol, idx[0], idx[1], idx[2], angleType, mmffAngleParams)) { hasContrib = true; contrib->addTerm(idx[0], idx[1], idx[2], &mmffAngleParams, mmffPropParamsCentralAtom); if (mmffMolProperties->getMMFFVerbosity()) { unsigned int iAtomType = mmffMolProperties->getMMFFAtomType(idx[0]); unsigned int kAtomType = mmffMolProperties->getMMFFAtomType(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, field->distance(idx[0], idx[1]), field->distance(idx[1], idx[2])); const double theta = RAD2DEG * acos(cosTheta); const double angleBendEnergy = MMFF::Utils::calcAngleBendEnergy( mmffAngleParams.theta0, mmffAngleParams.ka, mmffPropParamsCentralAtom->linh, cosTheta); if (mmffMolProperties->getMMFFVerbosity() == MMFF_VERBOSITY_HIGH) { 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) << angleType << " " << std::fixed << std::setprecision(3) << std::setw(10) << theta << std::setw(10) << mmffAngleParams.theta0 << std::setw(10) << theta - mmffAngleParams.theta0 << std::setw(10) << angleBendEnergy << std::setw(10) << mmffAngleParams.ka << std::endl; } totalAngleBendEnergy += angleBendEnergy; } } } } } if (mmffMolProperties->getMMFFVerbosity()) { if (mmffMolProperties->getMMFFVerbosity() == MMFF_VERBOSITY_HIGH) { oStream << std::endl; } oStream << "TOTAL ANGLE BEND ENERGY =" << std::right << std::setw(16) << std::fixed << std::setprecision(4) << totalAngleBendEnergy << std::endl; } if (hasContrib) { field->contribs().push_back(ForceFields::ContribPtr(contrib.release())); } } // ------------------------------------------------------------------------ // // // // ------------------------------------------------------------------------ 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]; const MMFFPropCollection *mmffProp = DefaultParameters::getMMFFProp(); 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(); } auto contrib = std::make_unique(field); bool contribAdded = false; 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]; boost::tie(nbr2Idx, end2Nbrs) = mol.getAtomNeighbors(jAtom); unsigned int j = 0; for (; nbr2Idx != end2Nbrs; ++nbr2Idx) { const Atom *kAtom = mol[*nbr2Idx]; 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)) { contribAdded = true; contrib->addTerm(idx[0], idx[1], idx[2], &mmffStbnParams, &mmffAngleParams, &mmffBondParams[0], &mmffBondParams[1]); 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 forceConstants = MMFF::Utils::calcStbnForceConstants(&mmffStbnParams); const std::pair 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; } if (contribAdded) { field->contribs().push_back(ForceFields::ContribPtr(contrib.release())); } } void addOop(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[4]; unsigned int atomType[4]; unsigned int n[4]; const Atom *atom[4]; ROMol::ADJ_ITER nbrIdx; ROMol::ADJ_ITER endNbrs; double totalOopBendEnergy = 0.0; RDGeom::PointPtrVect points; if (mmffMolProperties->getMMFFVerbosity()) { if (mmffMolProperties->getMMFFVerbosity() == MMFF_VERBOSITY_HIGH) { oStream << "\n" "O U T - O F - P L A N E B E N D I N G\n\n" "--------------ATOMS--------------- ATOM TYPES " " OOP FORCE\n" " I J K L I J K L " "ANGLE ENERGY CONSTANT\n" "-------------------------------------------------------------" "-----------------------------" << std::endl; } points = field->positions(); } bool hasContrib = false; auto contrib = std::make_unique(field); for (idx[1] = 0; idx[1] < mol.getNumAtoms(); ++idx[1]) { atom[1] = mol.getAtomWithIdx(idx[1]); if (atom[1]->getDegree() != 3) { continue; } atomType[1] = mmffMolProperties->getMMFFAtomType(idx[1]); boost::tie(nbrIdx, endNbrs) = mol.getAtomNeighbors(atom[1]); unsigned int i = 0; for (; nbrIdx != endNbrs; ++nbrIdx) { atom[i] = mol[*nbrIdx]; idx[i] = atom[i]->getIdx(); atomType[i] = mmffMolProperties->getMMFFAtomType(idx[i]); if (!i) { ++i; } ++i; } MMFFOop mmffOopParams; // if no parameters could be found, we exclude this term (SURDOX02) if (!(mmffMolProperties->getMMFFOopBendParams(mol, idx[0], idx[1], idx[2], idx[3], mmffOopParams))) { continue; } for (unsigned int i = 0; i < 3; ++i) { n[1] = 1; switch (i) { case 0: n[0] = 0; n[2] = 2; n[3] = 3; break; case 1: n[0] = 0; n[2] = 3; n[3] = 2; break; case 2: n[0] = 2; n[2] = 3; n[3] = 0; break; } contrib->addTerm(idx[n[0]], idx[n[1]], idx[n[2]], idx[n[3]], &mmffOopParams); hasContrib = true; if (mmffMolProperties->getMMFFVerbosity()) { const RDGeom::Point3D p1((*(points[idx[n[0]]]))[0], (*(points[idx[n[0]]]))[1], (*(points[idx[n[0]]]))[2]); const RDGeom::Point3D p2((*(points[idx[n[1]]]))[0], (*(points[idx[n[1]]]))[1], (*(points[idx[n[1]]]))[2]); const RDGeom::Point3D p3((*(points[idx[n[2]]]))[0], (*(points[idx[n[2]]]))[1], (*(points[idx[n[2]]]))[2]); const RDGeom::Point3D p4((*(points[idx[n[3]]]))[0], (*(points[idx[n[3]]]))[1], (*(points[idx[n[3]]]))[2]); const double chi = MMFF::Utils::calcOopChi(p1, p2, p3, p4); const double oopBendEnergy = MMFF::Utils::calcOopBendEnergy(chi, mmffOopParams.koop); if (mmffMolProperties->getMMFFVerbosity() == MMFF_VERBOSITY_HIGH) { oStream << std::left << std::setw(2) << atom[0]->getSymbol() << " #" << std::setw(5) << idx[n[0]] + 1 << std::setw(2) << atom[1]->getSymbol() << " #" << std::setw(5) << idx[n[1]] + 1 << std::setw(2) << atom[2]->getSymbol() << " #" << std::setw(5) << idx[n[2]] + 1 << std::setw(2) << atom[3]->getSymbol() << " #" << std::setw(5) << idx[n[3]] + 1 << std::right << std::setw(5) << atomType[n[0]] << std::setw(5) << atomType[n[1]] << std::setw(5) << atomType[n[2]] << std::setw(5) << atomType[n[3]] << std::fixed << std::setprecision(3) << std::setw(10) << chi << std::setw(10) << oopBendEnergy << std::setw(10) << mmffOopParams.koop << std::endl; } totalOopBendEnergy += oopBendEnergy; } } } if (mmffMolProperties->getMMFFVerbosity()) { if (mmffMolProperties->getMMFFVerbosity() == MMFF_VERBOSITY_HIGH) { oStream << std::endl; } oStream << "TOTAL OUT-OF-PLANE BEND ENERGY =" << std::right << std::setw(16) << std::fixed << std::setprecision(4) << totalOopBendEnergy << std::endl; } if (hasContrib) { field->contribs().push_back(ForceFields::ContribPtr(contrib.release())); } } // ------------------------------------------------------------------------ // // // // ------------------------------------------------------------------------ const std::string DefaultTorsionBondSmarts::ds_string = "[!$(*#*)&!D1]~[!$(*#*)&!D1]"; boost::scoped_ptr DefaultTorsionBondSmarts::ds_instance; void DefaultTorsionBondSmarts::create() { ds_instance.reset(SmartsToMol(ds_string)); } #ifdef RDK_BUILD_THREADSAFE_SSS std::once_flag DefaultTorsionBondSmarts::ds_flag; #endif const ROMol *DefaultTorsionBondSmarts::query() { #ifdef RDK_BUILD_THREADSAFE_SSS std::call_once(ds_flag, create); #else static bool created = false; if (!created) { created = true; create(); } #endif return ds_instance.get(); } void addTorsions(const ROMol &mol, MMFFMolProperties *mmffMolProperties, ForceFields::ForceField *field, const std::string &torsionBondSmarts) { PRECONDITION(field, "bad ForceField"); PRECONDITION(mmffMolProperties, "bad MMFFMolProperties"); PRECONDITION(mmffMolProperties->isValid(), "missing atom types - invalid force-field"); std::ostream &oStream = mmffMolProperties->getMMFFOStream(); ROMol::ADJ_ITER nbr1Idx; ROMol::ADJ_ITER end1Nbrs; ROMol::ADJ_ITER nbr2Idx; ROMol::ADJ_ITER end2Nbrs; double totalTorsionEnergy = 0.0; RDGeom::PointPtrVect points; if (mmffMolProperties->getMMFFVerbosity()) { if (mmffMolProperties->getMMFFVerbosity() == MMFF_VERBOSITY_HIGH) { oStream << "\n" "T O R S I O N A L\n\n" "--------------ATOMS--------------- ---ATOM TYPES--- " "FF TORSION -----FORCE Params-----\n" " I J K L I J K L " "CLASS ANGLE ENERGY V1 V2 V3\n" "-------------------------------------------------------------" "-----------------------------------------------------" << std::endl; } points = field->positions(); } std::vector matchVect; const ROMol *defaultQuery = DefaultTorsionBondSmarts::query(); const ROMol *query = (torsionBondSmarts == DefaultTorsionBondSmarts::string()) ? defaultQuery : SmartsToMol(torsionBondSmarts); TEST_ASSERT(query); unsigned int nHits = SubstructMatch(mol, *query, matchVect); if (query != defaultQuery) { delete query; } auto contrib = std::make_unique(field); bool contribAdded = false; for (unsigned int i = 0; i < nHits; ++i) { MatchVectType match = matchVect[i]; TEST_ASSERT(match.size() == 2); int idx2 = match[0].second; int idx3 = match[1].second; const Bond *bond = mol.getBondBetweenAtoms(idx2, idx3); TEST_ASSERT(bond); const Atom *jAtom = mol.getAtomWithIdx(idx2); const Atom *kAtom = mol.getAtomWithIdx(idx3); if (((jAtom->getHybridization() == Atom::SP2) || (jAtom->getHybridization() == Atom::SP3)) && ((kAtom->getHybridization() == Atom::SP2) || (kAtom->getHybridization() == Atom::SP3))) { ROMol::OEDGE_ITER beg1, end1; boost::tie(beg1, end1) = mol.getAtomBonds(jAtom); while (beg1 != end1) { const Bond *tBond1 = mol[*beg1]; if (tBond1 != bond) { int idx1 = tBond1->getOtherAtomIdx(idx2); ROMol::OEDGE_ITER beg2, end2; boost::tie(beg2, end2) = mol.getAtomBonds(kAtom); while (beg2 != end2) { const Bond *tBond2 = mol[*beg2]; if ((tBond2 != bond) && (tBond2 != tBond1)) { int idx4 = tBond2->getOtherAtomIdx(idx3); // make sure this isn't a three-membered ring: if (idx4 != idx1) { // we now have a torsion involving atoms (bonds): // bIdx - (tBond1) - idx1 - (bond) - idx2 - (tBond2) - eIdx unsigned int torType; MMFFTor mmffTorParams; if (mmffMolProperties->getMMFFTorsionParams( mol, idx1, idx2, idx3, idx4, torType, mmffTorParams)) { contrib->addTerm(idx1, idx2, idx3, idx4, &mmffTorParams); contribAdded = true; if (mmffMolProperties->getMMFFVerbosity()) { const Atom *iAtom = mol.getAtomWithIdx(idx1); const Atom *lAtom = mol.getAtomWithIdx(idx4); unsigned int iAtomType = mmffMolProperties->getMMFFAtomType(idx1); unsigned int jAtomType = mmffMolProperties->getMMFFAtomType(idx2); unsigned int kAtomType = mmffMolProperties->getMMFFAtomType(idx3); unsigned int lAtomType = mmffMolProperties->getMMFFAtomType(idx4); const RDGeom::Point3D p1((*(points[idx1]))[0], (*(points[idx1]))[1], (*(points[idx1]))[2]); const RDGeom::Point3D p2((*(points[idx2]))[0], (*(points[idx2]))[1], (*(points[idx2]))[2]); const RDGeom::Point3D p3((*(points[idx3]))[0], (*(points[idx3]))[1], (*(points[idx3]))[2]); const RDGeom::Point3D p4((*(points[idx4]))[0], (*(points[idx4]))[1], (*(points[idx4]))[2]); const double cosPhi = MMFF::Utils::calcTorsionCosPhi(p1, p2, p3, p4); const double torsionEnergy = MMFF::Utils::calcTorsionEnergy( mmffTorParams.V1, mmffTorParams.V2, mmffTorParams.V3, cosPhi); if (mmffMolProperties->getMMFFVerbosity() == MMFF_VERBOSITY_HIGH) { oStream << std::left << std::setw(2) << iAtom->getSymbol() << " #" << std::setw(5) << idx1 + 1 << std::setw(2) << jAtom->getSymbol() << " #" << std::setw(5) << idx2 + 1 << std::setw(2) << kAtom->getSymbol() << " #" << std::setw(5) << idx3 + 1 << std::setw(2) << lAtom->getSymbol() << " #" << std::setw(5) << idx4 + 1 << std::right << std::setw(5) << iAtomType << std::setw(5) << jAtomType << std::setw(5) << kAtomType << std::setw(5) << lAtomType << std::setw(6) << torType << " " << std::fixed << std::setprecision(3) << std::setw(10) << RAD2DEG * acos(cosPhi) << std::setw(10) << torsionEnergy << std::setw(10) << mmffTorParams.V1 << std::setw(10) << mmffTorParams.V2 << std::setw(10) << mmffTorParams.V3 << std::endl; } totalTorsionEnergy += torsionEnergy; } } } } beg2++; } } beg1++; } } } if (mmffMolProperties->getMMFFVerbosity()) { if (mmffMolProperties->getMMFFVerbosity() == MMFF_VERBOSITY_HIGH) { oStream << std::endl; } oStream << "TOTAL TORSIONAL ENERGY =" << std::right << std::setw(16) << std::fixed << std::setprecision(4) << totalTorsionEnergy << std::endl; } if (contribAdded) { field->contribs().push_back(ForceFields::ContribPtr(contrib.release())); } } // ------------------------------------------------------------------------ // // // // ------------------------------------------------------------------------ void addVdW(const ROMol &mol, int confId, MMFFMolProperties *mmffMolProperties, ForceFields::ForceField *field, boost::shared_array 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 molFrags = MolOps::getMolFrags(mol, true, &fragMapping); } unsigned int nAtoms = mol.getNumAtoms(); double totalVdWEnergy = 0.0; if (mmffMolProperties->getMMFFVerbosity()) { if (mmffMolProperties->getMMFFVerbosity() == MMFF_VERBOSITY_HIGH) { oStream << "\n" "V A N D E R W A A L S\n\n" "------ATOMS------ ATOM TYPES " " WELL\n" " I J I J DISTANCE ENERGY R* " " DEPTH\n" "-------------------------------------------------------------" "-------" << std::endl; } } const Conformer &conf = mol.getConformer(confId); auto contrib = std::make_unique(field); bool hasContrib = false; for (unsigned int i = 0; i < nAtoms; ++i) { for (unsigned int j = i + 1; j < nAtoms; ++j) { if (ignoreInterfragInteractions && (fragMapping[i] != fragMapping[j])) { continue; } if (getTwoBitCell(neighborMatrix, twoBitCellPos(nAtoms, i, j)) >= RELATION_1_4) { double dist = (conf.getAtomPos(i) - conf.getAtomPos(j)).length(); if (dist > nonBondedThresh) { continue; } MMFFVdWRijstarEps mmffVdWConstants; if (mmffMolProperties->getMMFFVdWParams(i, j, mmffVdWConstants)) { contrib->addTerm(i, j, &mmffVdWConstants); hasContrib = true; if (mmffMolProperties->getMMFFVerbosity()) { const Atom *iAtom = mol.getAtomWithIdx(i); const Atom *jAtom = mol.getAtomWithIdx(j); const double vdWEnergy = MMFF::Utils::calcVdWEnergy( dist, mmffVdWConstants.R_ij_star, mmffVdWConstants.epsilon); if (mmffMolProperties->getMMFFVerbosity() == MMFF_VERBOSITY_HIGH) { unsigned int iAtomType = mmffMolProperties->getMMFFAtomType(i); unsigned int jAtomType = mmffMolProperties->getMMFFAtomType(j); 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) << vdWEnergy << std::setw(9) << mmffVdWConstants.R_ij_star << std::setw(9) << mmffVdWConstants.epsilon << std::endl; } totalVdWEnergy += vdWEnergy; } } } } } if (mmffMolProperties->getMMFFVerbosity()) { if (mmffMolProperties->getMMFFVerbosity() == MMFF_VERBOSITY_HIGH) { oStream << std::endl; } oStream << "TOTAL VAN DER WAALS ENERGY =" << std::right << std::setw(16) << std::fixed << std::setprecision(4) << totalVdWEnergy << std::endl; } if (hasContrib) { field->contribs().push_back(ForceFields::ContribPtr(contrib.release())); } } // ------------------------------------------------------------------------ // // // // ------------------------------------------------------------------------ void addEle(const ROMol &mol, int confId, MMFFMolProperties *mmffMolProperties, ForceFields::ForceField *field, boost::shared_array 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 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(); std::uint8_t dielModel = mmffMolProperties->getMMFFDielectricModel(); auto contrib = std::make_unique(field); bool hasContrib = false; for (unsigned int i = 0; i < nAtoms; ++i) { for (unsigned int j = i + 1; j < nAtoms; ++j) { if (ignoreInterfragInteractions && (fragMapping[i] != fragMapping[j])) { continue; } std::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; contrib->addTerm(i, j, chargeTerm, dielModel, is1_4); hasContrib = true; 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; } if (hasContrib) { field->contribs().push_back(ForceFields::ContribPtr(contrib.release())); } } void addNonbonded(const ROMol &mol, int confId, MMFFMolProperties *mmffMolProperties, ForceFields::ForceField *field, boost::shared_array neighborMatrix, double nonBondedThresh, bool ignoreInterfragInteractions) { PRECONDITION(field, "bad ForceField"); PRECONDITION(mmffMolProperties, "bad MMFFMolProperties"); PRECONDITION(mmffMolProperties->isValid(), "missing atom types - invalid force-field"); INT_VECT fragMapping; if (ignoreInterfragInteractions) { std::vector molFrags = MolOps::getMolFrags(mol, true, &fragMapping); } unsigned int nAtoms = mol.getNumAtoms(); // FIX: need a solution for the verbosity here // std::ostream &oStream = mmffMolProperties->getMMFFOStream(); std::stringstream vdwStream; std::stringstream eleStream; double totalVdWEnergy = 0.0; double totalEleEnergy = 0.0; if (mmffMolProperties->getMMFFVerbosity()) { if (mmffMolProperties->getMMFFVerbosity() == MMFF_VERBOSITY_HIGH) { vdwStream << "\n" "V A N D E R W A A L S\n\n" "------ATOMS------ ATOM TYPES " " WELL\n" " I J I J DISTANCE ENERGY R* " " DEPTH\n" "-------------------------------------------------------------" "-------" << std::endl; } if (mmffMolProperties->getMMFFVerbosity() == MMFF_VERBOSITY_HIGH) { eleStream << "\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); auto contrib = std::make_unique(field); bool hasContrib = false; for (unsigned int i = 0; i < nAtoms; ++i) { for (unsigned int j = i + 1; j < nAtoms; ++j) { std::uint8_t cell = getTwoBitCell(neighborMatrix, twoBitCellPos(nAtoms, i, j)); #if 1 if (ignoreInterfragInteractions && (fragMapping[i] != fragMapping[j])) { continue; } #else if (ignoreInterfragInteractions && cell) { continue; } #endif if (cell >= RELATION_1_4) { bool is1_4 = (cell == RELATION_1_4); double dist = (conf.getAtomPos(i) - conf.getAtomPos(j)).length(); if (dist > nonBondedThresh) { continue; } MMFFVdWRijstarEps *vdwConstants = nullptr; MMFFVdWRijstarEps mmffVdWConstants; if (mmffMolProperties->getMMFFVdWTerm() && mmffMolProperties->getMMFFVdWParams(i, j, mmffVdWConstants)) { vdwConstants = &mmffVdWConstants; hasContrib = true; if (mmffMolProperties->getMMFFVerbosity()) { const Atom *iAtom = mol.getAtomWithIdx(i); const Atom *jAtom = mol.getAtomWithIdx(j); const double vdWEnergy = MMFF::Utils::calcVdWEnergy( dist, mmffVdWConstants.R_ij_star, mmffVdWConstants.epsilon); if (mmffMolProperties->getMMFFVerbosity() == MMFF_VERBOSITY_HIGH) { unsigned int iAtomType = mmffMolProperties->getMMFFAtomType(i); unsigned int jAtomType = mmffMolProperties->getMMFFAtomType(j); vdwStream << 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) << vdWEnergy << std::setw(9) << mmffVdWConstants.R_ij_star << std::setw(9) << mmffVdWConstants.epsilon << std::endl; } totalVdWEnergy += vdWEnergy; } } bool hasEle = false; double dielConst = 0; std::uint8_t dielModel = 0; double chargeTerm = 0.0; if (mmffMolProperties->getMMFFEleTerm() && !isDoubleZero(mmffMolProperties->getMMFFPartialCharge(i)) && !isDoubleZero(mmffMolProperties->getMMFFPartialCharge(j))) { dielConst = mmffMolProperties->getMMFFDielectricConstant(); dielModel = mmffMolProperties->getMMFFDielectricModel(); chargeTerm = mmffMolProperties->getMMFFPartialCharge(i) * mmffMolProperties->getMMFFPartialCharge(j) / dielConst; hasEle = true; hasContrib = true; 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) { eleStream << 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 (vdwConstants || hasEle) { contrib->addTerm(i, j, vdwConstants, hasEle, chargeTerm, dielModel, is1_4); } } } } if (hasContrib) { field->contribs().push_back(ForceFields::ContribPtr(contrib.release())); } if (mmffMolProperties->getMMFFVerbosity()) { std::ostream &oStream = mmffMolProperties->getMMFFOStream(); oStream << vdwStream.str(); if (mmffMolProperties->getMMFFVerbosity() == MMFF_VERBOSITY_HIGH) { oStream << std::endl; } oStream << "TOTAL VAN DER WAALS ENERGY =" << std::right << std::setw(16) << std::fixed << std::setprecision(4) << totalVdWEnergy << std::endl; oStream << eleStream.str(); 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; } } } // end of namespace Tools // ------------------------------------------------------------------------ // // // // ------------------------------------------------------------------------ ForceFields::ForceField *constructForceField(ROMol &mol, double nonBondedThresh, int confId, bool ignoreInterfragInteractions) { MMFFMolProperties mmffMolProperties(mol); PRECONDITION(mmffMolProperties.isValid(), "missing atom types - invalid force-field"); ForceFields::ForceField *res = constructForceField(mol, &mmffMolProperties, nonBondedThresh, confId, ignoreInterfragInteractions); return res; } // ------------------------------------------------------------------------ // // // // ------------------------------------------------------------------------ ForceFields::ForceField *constructForceField( ROMol &mol, MMFFMolProperties *mmffMolProperties, double nonBondedThresh, int confId, bool ignoreInterfragInteractions) { PRECONDITION(mmffMolProperties, "bad MMFFMolProperties"); PRECONDITION(mmffMolProperties->isValid(), "missing atom types - invalid force-field"); std::unique_ptr res(new ForceFields::ForceField()); // add the atomic positions: Conformer &conf = mol.getConformer(confId); for (unsigned int i = 0; i < mol.getNumAtoms(); ++i) { res->positions().push_back(&(conf.getAtomPos(i))); } res->initialize(); if (mmffMolProperties->getMMFFBondTerm()) { Tools::addBonds(mol, mmffMolProperties, res.get()); } if (mmffMolProperties->getMMFFAngleTerm()) { Tools::addAngles(mol, mmffMolProperties, res.get()); } if (mmffMolProperties->getMMFFStretchBendTerm()) { Tools::addStretchBend(mol, mmffMolProperties, res.get()); } if (mmffMolProperties->getMMFFOopTerm()) { Tools::addOop(mol, mmffMolProperties, res.get()); } if (mmffMolProperties->getMMFFTorsionTerm()) { Tools::addTorsions(mol, mmffMolProperties, res.get()); } if (mmffMolProperties->getMMFFVdWTerm() || mmffMolProperties->getMMFFEleTerm()) { boost::shared_array neighborMat = Tools::buildNeighborMatrix(mol); Tools::addNonbonded(mol, confId, mmffMolProperties, res.get(), neighborMat, nonBondedThresh, ignoreInterfragInteractions); } return res.release(); } } // namespace MMFF } // namespace RDKit