Files
rdkit/Code/GraphMol/ForceFieldHelpers/MMFF/Builder.cpp
Greg Landrum 6c373ef11f - added C++ and Python helpers to retrieve force-field parameters
(i.e., equilibrium distances/angles and force constants) from
  UFF and MMFF in response to two requests recently appeared
  on the RDKit-discuss mailing list:
  http://sourceforge.net/p/rdkit/mailman/message/32953737/
  http://sourceforge.net/p/rdkit/mailman/message/32880156/
- did some clean up on the MMFF code
- NB there are two ABI changes:
  1) StretchBendContrib(ForceField *owner,
       const unsigned int idx1, const unsigned int idx2, const unsigned int idx3,
       const MMFFStbn *mmffStbnParams, const MMFFAngle *mmffAngleParams,
       const MMFFBond *mmffBondParams1, const MMFFBond *mmffBondParams2);
     previously was:
     StretchBendContrib(ForceField *owner,
       const unsigned int idx1, const unsigned int idx2, const unsigned int idx3,
       const std::pair<bool, const MMFFStbn *> mmffStbnParams,
       const MMFFAngle *mmffAngleParams, const MMFFBond *mmffBondParams1,
       const MMFFBond *mmffBondParams2);
  2) std::pair<double, double> calcStbnForceConstants(const MMFFStbn *mmffStbnParams);
     previously was:
     std::pair<double, double> calcStbnForceConstants
       (const std::pair<bool, const MMFFStbn *> mmffStbnParams);
  The two changes are NOT mandatory - however, both the StretchBendContrib constructor
  and calcStbnForceConstants(), though public, are basically "internal" method that
  most likely no-one has ever invoked. Given that the current API is MUCH better
  and cleaner, I would really advise for the new version.
2014-11-17 05:51:20 +01:00

990 lines
46 KiB
C++

// $Id$
//
// Copyright (C) 2013 Paolo Tosco
//
// Copyright (C) 2004-2010 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 <iostream>
#include <cmath>
#include <RDGeneral/Invariant.h>
#include <GraphMol/RDKitBase.h>
#include <GraphMol/SmilesParse/SmilesParse.h>
#include <GraphMol/Substruct/SubstructMatch.h>
#include <ForceField/ForceField.h>
#include <ForceField/MMFF/Params.h>
#include <ForceField/MMFF/Contribs.h>
#include "AtomTyper.h"
#include "Builder.h"
#include <stdio.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;
}
field->initialize();
}
for (ROMol::ConstBondIterator bi = mol.beginBonds();
bi != mol.endBonds(); ++bi) {
unsigned int idx1 = (*bi)->getBeginAtomIdx();
unsigned int idx2 = (*bi)->getEndAtomIdx();
unsigned int bondType;
BondStretchContrib *contrib;
MMFFBond mmffBondParams;
if (mmffMolProperties->getMMFFBondStretchParams
(mol, idx1, idx2, bondType, mmffBondParams)) {
contrib = new BondStretchContrib(field, idx1, idx2, &mmffBondParams);
field->contribs().push_back(ForceFields::ContribPtr(contrib));
if (mmffMolProperties->getMMFFVerbosity()) {
unsigned int iAtomType = mmffMolProperties->getMMFFAtomType(idx1);
unsigned int jAtomType = mmffMolProperties->getMMFFAtomType(idx2);
const Atom *iAtom = (*bi)->getBeginAtom();
const Atom *jAtom = (*bi)->getEndAtom();
const double dist = field->distance(idx1, idx2);
const double bondStretchEnergy = 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;
}
}
void setTwoBitCell(boost::shared_array<boost::uint8_t> &res,
unsigned int pos, boost::uint8_t value)
{
unsigned int twoBitPos = pos / 4;
unsigned int shift = 2 * (pos % 4);
boost::uint8_t twoBitMask = 3 << shift;
res[twoBitPos] = ((res[twoBitPos] & (~twoBitMask)) | (value << shift));
}
boost::uint8_t getTwoBitCell
(boost::shared_array<boost::uint8_t> &res, unsigned int pos)
{
unsigned int twoBitPos = pos / 4;
unsigned int shift = 2 * (pos % 4);
boost::uint8_t twoBitMask = 3 << shift;
return ((res[twoBitPos] & twoBitMask) >> shift);
}
// ------------------------------------------------------------------------
//
// 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
//
// NOTE: the caller is responsible for calling delete []
// on the result
//
// ------------------------------------------------------------------------
boost::shared_array<boost::uint8_t> buildNeighborMatrix(const ROMol &mol)
{
unsigned int nAtoms = mol.getNumAtoms();
unsigned nTwoBitCells = (nAtoms * nAtoms - 1) / 4 + 1;
boost::shared_array<boost::uint8_t> res(new boost::uint8_t[nTwoBitCells]);
for (unsigned int i = 0; i < nTwoBitCells; ++i) {
res[i] = 0;
}
for (unsigned int i = 0; i < nAtoms; ++i) {
unsigned int iTab = i * nAtoms;
for (unsigned int j = i; j < nAtoms; ++j) {
setTwoBitCell(res, iTab + j, RELATION_1_X);
setTwoBitCell(res, i + j * nAtoms, RELATION_1_X);
}
}
for (unsigned int i = 0; i < mol.getNumBonds(); ++i) {
const Bond *bondi = mol.getBondWithIdx(i);
setTwoBitCell(res, bondi->getBeginAtomIdx() * nAtoms + bondi->getEndAtomIdx(), RELATION_1_2);
setTwoBitCell(res, bondi->getEndAtomIdx() * nAtoms + bondi->getBeginAtomIdx(), RELATION_1_2);
for (unsigned int j = i + 1; j < mol.getNumBonds(); ++j) {
const Bond *bondj = mol.getBondWithIdx(j);
int idx1 = -1;
int idx3 = -1;
if (bondi->getBeginAtomIdx() == bondj->getBeginAtomIdx()) {
idx1 = bondi->getEndAtomIdx();
idx3 = bondj->getEndAtomIdx();
}
else if (bondi->getBeginAtomIdx() == bondj->getEndAtomIdx()) {
idx1 = bondi->getEndAtomIdx();
idx3 = bondj->getBeginAtomIdx();
}
else if (bondi->getEndAtomIdx() == bondj->getBeginAtomIdx()) {
idx1 = bondi->getBeginAtomIdx();
idx3 = bondj->getEndAtomIdx();
}
else if (bondi->getEndAtomIdx() == bondj->getEndAtomIdx()) {
idx1 = bondi->getBeginAtomIdx();
idx3 = bondj->getBeginAtomIdx();
}
else {
// check if atoms i and j are in a 1,4-relationship
if ((mol.getBondBetweenAtoms
(bondi->getBeginAtomIdx(), bondj->getBeginAtomIdx()))
&& (getTwoBitCell(res, bondi->getEndAtomIdx() * nAtoms + bondj->getEndAtomIdx()) == RELATION_1_X)) {
setTwoBitCell(res, bondi->getEndAtomIdx() * nAtoms + bondj->getEndAtomIdx(), RELATION_1_4);
setTwoBitCell(res, bondj->getEndAtomIdx() * nAtoms + bondi->getEndAtomIdx(), RELATION_1_4);
}
else if ((mol.getBondBetweenAtoms
(bondi->getBeginAtomIdx(), bondj->getEndAtomIdx()))
&& (getTwoBitCell(res, bondi->getEndAtomIdx() * nAtoms + bondj->getBeginAtomIdx()) == RELATION_1_X)) {
setTwoBitCell(res, bondi->getEndAtomIdx() * nAtoms + bondj->getBeginAtomIdx(), RELATION_1_4);
setTwoBitCell(res, bondj->getBeginAtomIdx() * nAtoms + bondi->getEndAtomIdx(), RELATION_1_4);
}
else if ((mol.getBondBetweenAtoms
(bondi->getEndAtomIdx(), bondj->getBeginAtomIdx()))
&& (getTwoBitCell(res, bondi->getBeginAtomIdx() * nAtoms + bondj->getEndAtomIdx()) == RELATION_1_X)) {
setTwoBitCell(res, bondi->getBeginAtomIdx() * nAtoms + bondj->getEndAtomIdx(), RELATION_1_4);
setTwoBitCell(res, bondj->getEndAtomIdx() * nAtoms + bondi->getBeginAtomIdx(), RELATION_1_4);
}
else if ((mol.getBondBetweenAtoms
(bondi->getEndAtomIdx(), bondj->getEndAtomIdx()))
&& (getTwoBitCell(res, bondi->getBeginAtomIdx() * nAtoms + bondj->getBeginAtomIdx()) == RELATION_1_X)) {
setTwoBitCell(res, bondi->getBeginAtomIdx() * nAtoms + bondj->getBeginAtomIdx(), RELATION_1_4);
setTwoBitCell(res, bondj->getBeginAtomIdx() * nAtoms + bondi->getBeginAtomIdx(), RELATION_1_4);
}
}
if (idx1 > -1) {
setTwoBitCell(res, idx1 * nAtoms + idx3, RELATION_1_3);
setTwoBitCell(res, idx3 * nAtoms + idx1, RELATION_1_3);
}
}
}
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];
MMFFPropCollection *mmffProp = MMFFPropCollection::getMMFFProp();
AngleBendContrib *contrib;
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;
}
field->initialize();
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);
boost::tie(nbr1Idx, end1Nbrs) = mol.getAtomNeighbors(jAtom);
for (; nbr1Idx != end1Nbrs; ++nbr1Idx) {
const Atom *iAtom = mol[*nbr1Idx].get();
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].get();
idx[2] = kAtom->getIdx();
unsigned int angleType;
MMFFAngle mmffAngleParams;
if (mmffMolProperties->getMMFFAngleBendParams(mol,
idx[0], idx[1], idx[2], angleType, mmffAngleParams)) {
contrib = new AngleBendContrib(field, idx[0], idx[1], idx[2],
&mmffAngleParams, mmffPropParamsCentralAtom);
field->contribs().push_back(ForceFields::ContribPtr(contrib));
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 = 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 = 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;
}
}
// ------------------------------------------------------------------------
//
//
//
// ------------------------------------------------------------------------
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];
unsigned int bondType[2];
MMFFPropCollection *mmffProp = MMFFPropCollection::getMMFFProp();
std::pair<bool, const MMFFStbn *> mmffStbnParams;
const MMFFAngle *mmffAngleParams = NULL;
StretchBendContrib *contrib;
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;
}
field->initialize();
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)) {
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 = Utils::calcCosTheta(p1, p2, p3, dist1, dist2);
const double theta = RAD2DEG * acos(cosTheta);
const std::pair<double, double> forceConstants =
Utils::calcStbnForceConstants(&mmffStbnParams);
const std::pair<double, double> stretchBendEnergies
= 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 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;
OopBendContrib *contrib;
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;
}
field->initialize();
points = field->positions();
}
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].get();
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 = new OopBendContrib
(field, idx[n[0]], idx[n[1]], idx[n[2]], idx[n[3]], &mmffOopParams);
field->contribs().push_back(ForceFields::ContribPtr(contrib));
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 = Utils::calcOopChi(p1, p2, p3, p4);
const double oopBendEnergy = 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;
}
}
// ------------------------------------------------------------------------
//
//
//
// ------------------------------------------------------------------------
void addTorsions(const ROMol &mol,
MMFFMolProperties *mmffMolProperties, ForceFields::ForceField *field,
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;
}
field->initialize();
points = field->positions();
}
std::vector<MatchVectType> matchVect;
ROMol *query = SmartsToMol(torsionBondSmarts);
TEST_ASSERT(query);
unsigned int nHits = SubstructMatch(mol, *query, matchVect);
delete query;
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].get();
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].get();
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)) {
TorsionAngleContrib *contrib = new TorsionAngleContrib
(field, idx1, idx2, idx3, idx4, &mmffTorParams);
field->contribs().push_back(ForceFields::ContribPtr(contrib));
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 = Utils::calcTorsionCosPhi(p1, p2, p3, p4);
const double torsionEnergy = 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;
}
}
// ------------------------------------------------------------------------
//
//
//
// ------------------------------------------------------------------------
void addVdW(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 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);
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, i * nAtoms + j) >= RELATION_1_4) {
double dist = (conf.getAtomPos(i) - conf.getAtomPos(j)).length();
if (dist > nonBondedThresh) {
continue;
}
MMFFVdWRijstarEps mmffVdWParams;
if (mmffMolProperties->getMMFFVdWParams(i, j, mmffVdWParams)) {
VdWContrib *contrib = new VdWContrib(field, i, j, &mmffVdWParams);
field->contribs().push_back(ForceFields::ContribPtr(contrib));
if (mmffMolProperties->getMMFFVerbosity()) {
const Atom *iAtom = mol.getAtomWithIdx(i);
const Atom *jAtom = mol.getAtomWithIdx(j);
const double vdWEnergy = Utils::calcVdWEnergy(dist,
mmffVdWParams.R_ij_star, mmffVdWParams.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) << mmffVdWParams.R_ij_star
<< std::setw(9) << mmffVdWParams.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;
}
}
// ------------------------------------------------------------------------
//
//
//
// ------------------------------------------------------------------------
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;
}
if ((getTwoBitCell(neighborMatrix, i * nAtoms + j) >= RELATION_1_4)
&& (!isDoubleZero(mmffMolProperties->getMMFFPartialCharge(i)))
&& (!isDoubleZero(mmffMolProperties->getMMFFPartialCharge(j)))) {
double dist = (conf.getAtomPos(i) - conf.getAtomPos(j)).length();
if (dist > nonBondedThresh) {
continue;
}
EleContrib *contrib;
double chargeTerm = mmffMolProperties->getMMFFPartialCharge(i)
* mmffMolProperties->getMMFFPartialCharge(j) / dielConst;
contrib = new EleContrib(field, i, j, chargeTerm, dielModel,
getTwoBitCell(neighborMatrix, i * nAtoms + j) == RELATION_1_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 = Utils::calcEleEnergy
(i, j, dist, chargeTerm, dielModel,
getTwoBitCell(neighborMatrix, i * nAtoms + j) == RELATION_1_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;
}
}
} // 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");
ForceFields::ForceField *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)));
}
if (mmffMolProperties->getMMFFBondTerm()) {
Tools::addBonds(mol, mmffMolProperties, res);
}
if (mmffMolProperties->getMMFFAngleTerm()) {
Tools::addAngles(mol, mmffMolProperties, res);
}
if (mmffMolProperties->getMMFFStretchBendTerm()) {
Tools::addStretchBend(mol, mmffMolProperties, res);
}
if (mmffMolProperties->getMMFFOopTerm()) {
Tools::addOop(mol, mmffMolProperties, res);
}
if (mmffMolProperties->getMMFFTorsionTerm()) {
Tools::addTorsions(mol, mmffMolProperties, res);
}
if (mmffMolProperties->getMMFFVdWTerm()
|| mmffMolProperties->getMMFFEleTerm()) {
boost::shared_array<boost::uint8_t> neighborMat = Tools::buildNeighborMatrix(mol);
if (mmffMolProperties->getMMFFVdWTerm()) {
Tools::addVdW(mol, confId, mmffMolProperties, res,
neighborMat, nonBondedThresh, ignoreInterfragInteractions);
}
if (mmffMolProperties->getMMFFEleTerm()) {
Tools::addEle(mol, confId, mmffMolProperties, res,
neighborMat, nonBondedThresh, ignoreInterfragInteractions);
}
}
return res;
}
}
}