1 // $Id$
2 //
3 //  Copyright (C) 2004-2006 Rational Discovery LLC
4 //
5 //   @@ All Rights Reserved @@
6 //  This file is part of the RDKit.
7 //  The contents are covered by the terms of the BSD license
8 //  which is included in the file license.txt, found at the root
9 //  of the RDKit source tree.
10 //
11 
12 #include <RDBoost/python.h>
13 #include <RDBoost/Wrap.h>
14 #include <RDGeneral/Exceptions.h>
15 #include <GraphMol/GraphMol.h>
16 #include <ForceField/ForceField.h>
17 #include <ForceField/UFF/DistanceConstraint.h>
18 #include <ForceField/UFF/AngleConstraint.h>
19 #include <ForceField/UFF/TorsionConstraint.h>
20 #include <ForceField/UFF/PositionConstraint.h>
21 #include <ForceField/MMFF/DistanceConstraint.h>
22 #include <ForceField/MMFF/AngleConstraint.h>
23 #include <ForceField/MMFF/TorsionConstraint.h>
24 #include <ForceField/MMFF/PositionConstraint.h>
25 #include "PyForceField.h"
26 
27 using namespace ForceFields;
28 namespace python = boost::python;
29 
ForceFieldAddDistanceConstraint(PyForceField * self,unsigned int idx1,unsigned int idx2,double minLen,double maxLen,double forceConstant)30 void ForceFieldAddDistanceConstraint(PyForceField *self, unsigned int idx1,
31                                      unsigned int idx2, double minLen,
32                                      double maxLen, double forceConstant) {
33   UFF::DistanceConstraintContrib *constraint;
34   constraint = new UFF::DistanceConstraintContrib(
35       self->field.get(), idx1, idx2, minLen, maxLen, forceConstant);
36   self->field->contribs().push_back(ForceFields::ContribPtr(constraint));
37 }
38 
ForceFieldAddFixedPoint(PyForceField * self,unsigned int idx)39 void ForceFieldAddFixedPoint(PyForceField *self, unsigned int idx) {
40   self->field->fixedPoints().push_back(idx);
41 }
42 
UFFAddDistanceConstraint(PyForceField * self,unsigned int idx1,unsigned int idx2,bool relative,double minLen,double maxLen,double forceConstant)43 void UFFAddDistanceConstraint(PyForceField *self, unsigned int idx1,
44                               unsigned int idx2, bool relative, double minLen,
45                               double maxLen, double forceConstant) {
46   auto *constraint = new UFF::DistanceConstraintContrib(
47       self->field.get(), idx1, idx2, relative, minLen, maxLen, forceConstant);
48   self->field->contribs().push_back(ForceFields::ContribPtr(constraint));
49 }
50 
UFFAddAngleConstraint(PyForceField * self,unsigned int idx1,unsigned int idx2,unsigned int idx3,bool relative,double minAngleDeg,double maxAngleDeg,double forceConstant)51 void UFFAddAngleConstraint(PyForceField *self, unsigned int idx1,
52                            unsigned int idx2, unsigned int idx3, bool relative,
53                            double minAngleDeg, double maxAngleDeg,
54                            double forceConstant) {
55   auto *constraint = new UFF::AngleConstraintContrib(
56       self->field.get(), idx1, idx2, idx3, relative, minAngleDeg, maxAngleDeg,
57       forceConstant);
58   self->field->contribs().push_back(ForceFields::ContribPtr(constraint));
59 }
60 
UFFAddTorsionConstraint(PyForceField * self,unsigned int idx1,unsigned int idx2,unsigned int idx3,unsigned int idx4,bool relative,double minDihedralDeg,double maxDihedralDeg,double forceConstant)61 void UFFAddTorsionConstraint(PyForceField *self, unsigned int idx1,
62                              unsigned int idx2, unsigned int idx3,
63                              unsigned int idx4, bool relative,
64                              double minDihedralDeg, double maxDihedralDeg,
65                              double forceConstant) {
66   auto *constraint = new UFF::TorsionConstraintContrib(
67       self->field.get(), idx1, idx2, idx3, idx4, relative, minDihedralDeg,
68       maxDihedralDeg, forceConstant);
69   self->field->contribs().push_back(ForceFields::ContribPtr(constraint));
70 }
71 
UFFAddPositionConstraint(PyForceField * self,unsigned int idx,double maxDispl,double forceConstant)72 void UFFAddPositionConstraint(PyForceField *self, unsigned int idx,
73                               double maxDispl, double forceConstant) {
74   auto *constraint = new UFF::PositionConstraintContrib(
75       self->field.get(), idx, maxDispl, forceConstant);
76   self->field->contribs().push_back(ForceFields::ContribPtr(constraint));
77 }
78 
MMFFAddDistanceConstraint(PyForceField * self,unsigned int idx1,unsigned int idx2,bool relative,double minLen,double maxLen,double forceConstant)79 void MMFFAddDistanceConstraint(PyForceField *self, unsigned int idx1,
80                                unsigned int idx2, bool relative, double minLen,
81                                double maxLen, double forceConstant) {
82   auto *constraint = new MMFF::DistanceConstraintContrib(
83       self->field.get(), idx1, idx2, relative, minLen, maxLen, forceConstant);
84   self->field->contribs().push_back(ForceFields::ContribPtr(constraint));
85 }
86 
MMFFAddAngleConstraint(PyForceField * self,unsigned int idx1,unsigned int idx2,unsigned int idx3,bool relative,double minAngleDeg,double maxAngleDeg,double forceConstant)87 void MMFFAddAngleConstraint(PyForceField *self, unsigned int idx1,
88                             unsigned int idx2, unsigned int idx3, bool relative,
89                             double minAngleDeg, double maxAngleDeg,
90                             double forceConstant) {
91   auto *constraint = new MMFF::AngleConstraintContrib(
92       self->field.get(), idx1, idx2, idx3, relative, minAngleDeg, maxAngleDeg,
93       forceConstant);
94   self->field->contribs().push_back(ForceFields::ContribPtr(constraint));
95 }
96 
MMFFAddTorsionConstraint(PyForceField * self,unsigned int idx1,unsigned int idx2,unsigned int idx3,unsigned int idx4,bool relative,double minDihedralDeg,double maxDihedralDeg,double forceConstant)97 void MMFFAddTorsionConstraint(PyForceField *self, unsigned int idx1,
98                               unsigned int idx2, unsigned int idx3,
99                               unsigned int idx4, bool relative,
100                               double minDihedralDeg, double maxDihedralDeg,
101                               double forceConstant) {
102   auto *constraint = new MMFF::TorsionConstraintContrib(
103       self->field.get(), idx1, idx2, idx3, idx4, relative, minDihedralDeg,
104       maxDihedralDeg, forceConstant);
105   self->field->contribs().push_back(ForceFields::ContribPtr(constraint));
106 }
107 
MMFFAddPositionConstraint(PyForceField * self,unsigned int idx,double maxDispl,double forceConstant)108 void MMFFAddPositionConstraint(PyForceField *self, unsigned int idx,
109                                double maxDispl, double forceConstant) {
110   auto *constraint = new MMFF::PositionConstraintContrib(
111       self->field.get(), idx, maxDispl, forceConstant);
112   self->field->contribs().push_back(ForceFields::ContribPtr(constraint));
113 }
114 
ForceFieldGetExtraPointLoc(PyForceField * self,unsigned int idx)115 PyObject *ForceFieldGetExtraPointLoc(PyForceField *self, unsigned int idx) {
116   if (idx >= self->extraPoints.size()) {
117     throw IndexErrorException(idx);
118   }
119   PyObject *res = PyTuple_New(3);
120   PyTuple_SetItem(res, 0, PyFloat_FromDouble(self->extraPoints[idx]->x));
121   PyTuple_SetItem(res, 1, PyFloat_FromDouble(self->extraPoints[idx]->y));
122   PyTuple_SetItem(res, 2, PyFloat_FromDouble(self->extraPoints[idx]->z));
123   return res;
124 }
125 
calcEnergyWithPos(const python::object & pos)126 double PyForceField::calcEnergyWithPos(const python::object &pos) {
127   PRECONDITION(this->field, "no force field");
128   if (pos != python::object()) {
129     size_t s = this->field->dimension() * this->field->numPoints();
130     size_t numElements = python::extract<size_t>(pos.attr("__len__")());
131     if (s != numElements) {
132       throw ValueErrorException("The Python container must have length equal to Dimension() * NumPoints()");
133     }
134     std::vector<double> c(s);
135     for (size_t i = 0; i < s; ++i) {
136       c[i] = python::extract<double>(pos[i]);
137     }
138     return this->field->calcEnergy(c.data());
139   } else {
140     return this->field->calcEnergy();
141   }
142 }
143 
positions()144 PyObject *PyForceField::positions() {
145   PRECONDITION(this->field, "no force field");
146   size_t s = this->field->dimension() * this->field->numPoints();
147   PyObject *coordTuple = PyTuple_New(s);
148   const RDGeom::PointPtrVect &p = this->field->positions();
149   size_t i = 0;
150   PyObject *coordItem;
151   for (const auto pptr: p) {
152     for (size_t j = 0; j < 3; ++j) {
153       coordItem = PyFloat_FromDouble((*pptr)[j]);
154       PyTuple_SetItem(coordTuple, i++, coordItem);
155     }
156   }
157   return coordTuple;
158 }
159 
calcGradWithPos(const python::object & pos)160 PyObject *PyForceField::calcGradWithPos(const python::object &pos) {
161   PRECONDITION(this->field, "no force field");
162   size_t s = this->field->dimension() * this->field->numPoints();
163   std::vector<double> g(s, 0.0);
164   PyObject *gradTuple = PyTuple_New(s);
165   if (pos != python::object()) {
166     size_t numElements = python::extract<size_t>(pos.attr("__len__")());
167     if (s != numElements) {
168       throw ValueErrorException("The Python container must have length equal to Dimension() * NumPoints()");
169     }
170     std::vector<double> c(s);
171     for (size_t i = 0; i < s; ++i) {
172       c[i] = python::extract<double>(pos[i]);
173     }
174     this->field->calcGrad(c.data(), g.data());
175   } else {
176     this->field->calcGrad(g.data());
177   }
178   for (size_t i = 0; i < s; ++i) {
179     PyObject *coordItem = PyFloat_FromDouble(g[i]);
180     PyTuple_SetItem(gradTuple, i, coordItem);
181   }
182   return gradTuple;
183 }
184 
minimizeTrajectory(unsigned int snapshotFreq,int maxIts,double forceTol,double energyTol)185 python::tuple PyForceField::minimizeTrajectory(unsigned int snapshotFreq, int maxIts, double forceTol, double energyTol) {
186   PRECONDITION(this->field, "no force field");
187   RDKit::SnapshotVect snapshotVect;
188   int resInt = this->field->minimize(snapshotFreq, &snapshotVect,
189                                maxIts, forceTol, energyTol);
190   python::list l;
191   for (RDKit::SnapshotVect::const_iterator it = snapshotVect.begin();
192        it != snapshotVect.end(); ++it) {
193     l.append(new RDKit::Snapshot(*it));
194   }
195   return python::make_tuple(resInt, l);
196 
197 }
198 
getMMFFBondStretchParams(const RDKit::ROMol & mol,const unsigned int idx1,const unsigned int idx2)199 PyObject *PyMMFFMolProperties::getMMFFBondStretchParams(
200     const RDKit::ROMol &mol, const unsigned int idx1, const unsigned int idx2) {
201   PyObject *res = nullptr;
202   unsigned int bondType;
203   ForceFields::MMFF::MMFFBond mmffBondStretchParams;
204   if (mmffMolProperties->getMMFFBondStretchParams(mol, idx1, idx2, bondType,
205                                                   mmffBondStretchParams)) {
206     res = PyTuple_New(3);
207     PyTuple_SetItem(res, 0, PyInt_FromLong(bondType));
208     PyTuple_SetItem(res, 1, PyFloat_FromDouble(mmffBondStretchParams.kb));
209     PyTuple_SetItem(res, 2, PyFloat_FromDouble(mmffBondStretchParams.r0));
210   }
211   return res;
212 };
213 
getMMFFAngleBendParams(const RDKit::ROMol & mol,const unsigned int idx1,const unsigned int idx2,const unsigned int idx3)214 PyObject *PyMMFFMolProperties::getMMFFAngleBendParams(const RDKit::ROMol &mol,
215                                                       const unsigned int idx1,
216                                                       const unsigned int idx2,
217                                                       const unsigned int idx3) {
218   PyObject *res = nullptr;
219   unsigned int angleType;
220   ForceFields::MMFF::MMFFAngle mmffAngleBendParams;
221   if (mmffMolProperties->getMMFFAngleBendParams(
222           mol, idx1, idx2, idx3, angleType, mmffAngleBendParams)) {
223     res = PyTuple_New(3);
224     PyTuple_SetItem(res, 0, PyInt_FromLong(angleType));
225     PyTuple_SetItem(res, 1, PyFloat_FromDouble(mmffAngleBendParams.ka));
226     PyTuple_SetItem(res, 2, PyFloat_FromDouble(mmffAngleBendParams.theta0));
227   }
228   return res;
229 };
230 
getMMFFStretchBendParams(const RDKit::ROMol & mol,const unsigned int idx1,const unsigned int idx2,const unsigned int idx3)231 PyObject *PyMMFFMolProperties::getMMFFStretchBendParams(
232     const RDKit::ROMol &mol, const unsigned int idx1, const unsigned int idx2,
233     const unsigned int idx3) {
234   PyObject *res = nullptr;
235   unsigned int stretchBendType;
236   ForceFields::MMFF::MMFFStbn mmffStretchBendParams;
237   ForceFields::MMFF::MMFFBond mmffBondStretchParams[2];
238   ForceFields::MMFF::MMFFAngle mmffAngleBendParams;
239   if (mmffMolProperties->getMMFFStretchBendParams(
240           mol, idx1, idx2, idx3, stretchBendType, mmffStretchBendParams,
241           mmffBondStretchParams, mmffAngleBendParams)) {
242     res = PyTuple_New(3);
243     PyTuple_SetItem(res, 0, PyInt_FromLong(stretchBendType));
244     PyTuple_SetItem(res, 1, PyFloat_FromDouble(mmffStretchBendParams.kbaIJK));
245     PyTuple_SetItem(res, 2, PyFloat_FromDouble(mmffStretchBendParams.kbaKJI));
246   }
247   return res;
248 };
249 
getMMFFTorsionParams(const RDKit::ROMol & mol,const unsigned int idx1,const unsigned int idx2,const unsigned int idx3,const unsigned int idx4)250 PyObject *PyMMFFMolProperties::getMMFFTorsionParams(const RDKit::ROMol &mol,
251                                                     const unsigned int idx1,
252                                                     const unsigned int idx2,
253                                                     const unsigned int idx3,
254                                                     const unsigned int idx4) {
255   PyObject *res = nullptr;
256   unsigned int torType;
257   ForceFields::MMFF::MMFFTor mmffTorsionParams;
258   if (mmffMolProperties->getMMFFTorsionParams(mol, idx1, idx2, idx3, idx4,
259                                               torType, mmffTorsionParams)) {
260     res = PyTuple_New(4);
261     PyTuple_SetItem(res, 0, PyInt_FromLong(torType));
262     PyTuple_SetItem(res, 1, PyFloat_FromDouble(mmffTorsionParams.V1));
263     PyTuple_SetItem(res, 2, PyFloat_FromDouble(mmffTorsionParams.V2));
264     PyTuple_SetItem(res, 3, PyFloat_FromDouble(mmffTorsionParams.V3));
265   }
266   return res;
267 };
268 
getMMFFOopBendParams(const RDKit::ROMol & mol,const unsigned int idx1,const unsigned int idx2,const unsigned int idx3,const unsigned int idx4)269 PyObject *PyMMFFMolProperties::getMMFFOopBendParams(const RDKit::ROMol &mol,
270                                                     const unsigned int idx1,
271                                                     const unsigned int idx2,
272                                                     const unsigned int idx3,
273                                                     const unsigned int idx4) {
274   PyObject *res = nullptr;
275   ForceFields::MMFF::MMFFOop mmffOopBendParams;
276   if (mmffMolProperties->getMMFFOopBendParams(mol, idx1, idx2, idx3, idx4,
277                                               mmffOopBendParams)) {
278     res = PyFloat_FromDouble(mmffOopBendParams.koop);
279   }
280   return res;
281 };
282 
getMMFFVdWParams(const unsigned int idx1,const unsigned int idx2)283 PyObject *PyMMFFMolProperties::getMMFFVdWParams(const unsigned int idx1,
284                                                 const unsigned int idx2) {
285   PyObject *res = nullptr;
286   ForceFields::MMFF::MMFFVdWRijstarEps mmffVdWParams;
287   if (mmffMolProperties->getMMFFVdWParams(idx1, idx2, mmffVdWParams)) {
288     res = PyTuple_New(4);
289     PyTuple_SetItem(res, 0,
290                     PyFloat_FromDouble(mmffVdWParams.R_ij_starUnscaled));
291     PyTuple_SetItem(res, 1, PyFloat_FromDouble(mmffVdWParams.epsilonUnscaled));
292     PyTuple_SetItem(res, 2, PyFloat_FromDouble(mmffVdWParams.R_ij_star));
293     PyTuple_SetItem(res, 3, PyFloat_FromDouble(mmffVdWParams.epsilon));
294   }
295   return res;
296 };
297 
BOOST_PYTHON_MODULE(rdForceField)298 BOOST_PYTHON_MODULE(rdForceField) {
299   python::scope().attr("__doc__") = "Exposes the ForceField class";
300 
301   std::string docString;
302 
303   python::class_<PyForceField>("ForceField", "A force field", python::no_init)
304       .def("CalcEnergy",
305            (double (PyForceField::*)(const python::object &) const) &PyForceField::calcEnergyWithPos,
306            (python::arg("pos") = python::object()),
307            "Returns the energy (in kcal/mol) of the current arrangement\n"
308            "or of the supplied coordinate list (if non-empty)")
309       .def("CalcGrad", &PyForceField::calcGradWithPos,
310            (python::arg("pos") = python::object()),
311            "Returns a tuple filled with the per-coordinate gradients\n"
312            "of the current arrangement or of the supplied coordinate list (if non-empty)")
313       .def("Positions", &PyForceField::positions,
314            "Returns a tuple filled with the coordinates of the\n"
315            "points the ForceField is handling")
316       .def("Dimension",
317            (unsigned int (PyForceField::*)() const) &PyForceField::dimension,
318            "Returns the dimension of the ForceField")
319       .def("NumPoints",
320            (unsigned int (PyForceField::*)() const) &PyForceField::numPoints,
321            "Returns the number of points the ForceField is handling")
322       .def("Minimize", &PyForceField::minimize,
323            (python::arg("maxIts") = 200, python::arg("forceTol") = 1e-4,
324             python::arg("energyTol") = 1e-6),
325            "Runs some minimization iterations.\n\n  Returns 0 if the "
326            "minimization succeeded.")
327       .def("MinimizeTrajectory", &PyForceField::minimizeTrajectory,
328            (python::arg("snapshotFreq"), python::arg("maxIts") = 200,
329             python::arg("forceTol") = 1e-4, python::arg("energyTol") = 1e-6),
330            "Runs some minimization iterations, recording the minimization "
331            "trajectory every snapshotFreq steps.\n\n"
332            "Returns a (int, []) tuple; the int is 0 if the minimization succeeded, "
333            "while the list contains Snapshot objects.")
334       .def("AddDistanceConstraint", ForceFieldAddDistanceConstraint,
335            (python::arg("self"), python::arg("idx1"), python::arg("idx2"),
336             python::arg("minLen"), python::arg("maxLen"),
337             python::arg("forceConstant")),
338            "Adds a distance constraint to the UFF force field "
339            "(deprecated, use UFFAddDistanceConstraint instead).")
340       .def("AddFixedPoint", ForceFieldAddFixedPoint,
341            (python::arg("self"), python::arg("idx")),
342            "Adds a fixed point to the force field.")
343       .def("UFFAddDistanceConstraint", UFFAddDistanceConstraint,
344            (python::arg("self"), python::arg("idx1"), python::arg("idx2"),
345             python::arg("relative"), python::arg("minLen"),
346             python::arg("maxLen"), python::arg("forceConstant")),
347            "Adds a distance constraint to the UFF force field; if relative == "
348            "True, "
349            "then minLen and maxLen are intended as relative to the current "
350            "distance.")
351       .def("UFFAddAngleConstraint", UFFAddAngleConstraint,
352            (python::arg("self"), python::arg("idx1"), python::arg("idx2"),
353             python::arg("idx3"), python::arg("relative"),
354             python::arg("minAngleDeg"), python::arg("maxAngleDeg"),
355             python::arg("forceConstant")),
356            "Adds an angle constraint to the UFF force field; if relative == "
357            "True, "
358            "then minAngleDeg and maxAngleDeg are intended as relative to the "
359            "current angle.")
360       .def("UFFAddTorsionConstraint", UFFAddTorsionConstraint,
361            (python::arg("self"), python::arg("idx1"), python::arg("idx2"),
362             python::arg("idx3"), python::arg("idx4"), python::arg("relative"),
363             python::arg("minDihedralDeg"), python::arg("maxDihedralDeg"),
364             python::arg("forceConstant")),
365            "Adds a dihedral angle constraint to the UFF force field; if "
366            "relative == True, "
367            "then minDihedralDeg and maxDihedralDeg are intended as relative to "
368            "the current "
369            "dihedral angle.")
370       .def("UFFAddPositionConstraint", UFFAddPositionConstraint,
371            (python::arg("self"), python::arg("idx"), python::arg("maxDispl"),
372             python::arg("forceConstant")),
373            "Adds a position constraint to the UFF force field.")
374       .def("MMFFAddDistanceConstraint", MMFFAddDistanceConstraint,
375            (python::arg("self"), python::arg("idx1"), python::arg("idx2"),
376             python::arg("relative"), python::arg("minLen"),
377             python::arg("maxLen"), python::arg("forceConstant")),
378            "Adds a distance constraint to the MMFF force field; if relative == "
379            "True, "
380            "then minLen and maxLen are intended as relative to the current "
381            "distance.")
382       .def("MMFFAddAngleConstraint", MMFFAddAngleConstraint,
383            (python::arg("self"), python::arg("idx1"), python::arg("idx2"),
384             python::arg("idx3"), python::arg("relative"),
385             python::arg("minAngleDeg"), python::arg("maxAngleDeg"),
386             python::arg("forceConstant")),
387            "Adds an angle constraint to the MMFF force field; if relative == "
388            "True, "
389            "then minAngleDeg and maxAngleDeg are intended as relative to the "
390            "current angle.")
391       .def("MMFFAddTorsionConstraint", MMFFAddTorsionConstraint,
392            (python::arg("self"), python::arg("idx1"), python::arg("idx2"),
393             python::arg("idx3"), python::arg("idx4"), python::arg("relative"),
394             python::arg("minDihedralDeg"), python::arg("maxDihedralDeg"),
395             python::arg("forceConstant")),
396            "Adds a dihedral angle constraint to the MMFF force field; if "
397            "relative == True, "
398            "then minDihedralDeg and maxDihedralDeg are intended as relative to "
399            "the current "
400            "dihedral angle.")
401       .def("MMFFAddPositionConstraint", MMFFAddPositionConstraint,
402            (python::arg("self"), python::arg("idx"), python::arg("maxDispl"),
403             python::arg("forceConstant")),
404            "Adds a position constraint to the MMFF force field.")
405       .def("Initialize", &PyForceField::initialize,
406            "initializes the force field (call this before minimizing)")
407       .def("AddExtraPoint", &PyForceField::addExtraPoint,
408            (python::arg("self"), python::arg("x"), python::arg("y"),
409             python::arg("z"), python::arg("fixed") = true),
410            "Adds an extra point, this can be useful for adding constraints.")
411       .def("GetExtraPointPos", ForceFieldGetExtraPointLoc,
412            (python::arg("self"), python::arg("idx")),
413            "returns the location of an extra point as a tuple");
414   python::class_<PyMMFFMolProperties>(
415       "MMFFMolProperties", "MMFF molecular properties", python::no_init)
416       .def("GetMMFFAtomType", &PyMMFFMolProperties::getMMFFAtomType,
417            (python::arg("self"), python::arg("idx")),
418            "Retrieves MMFF atom type for atom with index idx")
419       .def("GetMMFFFormalCharge", &PyMMFFMolProperties::getMMFFFormalCharge,
420            (python::arg("self"), python::arg("idx")),
421            "Retrieves MMFF formal charge for atom with index idx")
422       .def("GetMMFFPartialCharge", &PyMMFFMolProperties::getMMFFPartialCharge,
423            (python::arg("self"), python::arg("idx")),
424            "Retrieves MMFF partial charge for atom with index idx")
425       .def("GetMMFFBondStretchParams",
426            &PyMMFFMolProperties::getMMFFBondStretchParams,
427            (python::arg("self"), python::arg("mol"), python::arg("idx1"),
428             python::arg("idx2")),
429            "Retrieves MMFF bond stretch parameters for atoms with indexes "
430            "idx1, idx2 "
431            "as a (bondType, kb, r0) tuple, or None if no parameters could be "
432            "found")
433       .def("GetMMFFAngleBendParams",
434            &PyMMFFMolProperties::getMMFFAngleBendParams,
435            (python::arg("self"), python::arg("mol"), python::arg("idx1"),
436             python::arg("idx2"), python::arg("idx3")),
437            "Retrieves MMFF angle bend parameters for atoms with indexes idx1, "
438            "idx2, idx3 "
439            "as a (angleType, ka, theta0) tuple, or None if no parameters could "
440            "be found")
441       .def("GetMMFFStretchBendParams",
442            &PyMMFFMolProperties::getMMFFStretchBendParams,
443            (python::arg("self"), python::arg("mol"), python::arg("idx1"),
444             python::arg("idx2"), python::arg("idx3")),
445            "Retrieves MMFF stretch-bend parameters for atoms with indexes "
446            "idx1, idx2, idx3 "
447            "as a (stretchBendType, kbaIJK, kbaKJI) tuple, or None if no "
448            "parameters could be found")
449       .def("GetMMFFTorsionParams", &PyMMFFMolProperties::getMMFFTorsionParams,
450            (python::arg("self"), python::arg("mol"), python::arg("idx1"),
451             python::arg("idx2"), python::arg("idx3"), python::arg("idx4")),
452            "Retrieves MMFF torsion parameters for atoms with indexes idx1, "
453            "idx2, idx3, idx4 "
454            "as a (torsionType, V1, V2, V3) tuple, or None if no parameters "
455            "could be found")
456       .def("GetMMFFOopBendParams", &PyMMFFMolProperties::getMMFFOopBendParams,
457            (python::arg("self"), python::arg("mol"), python::arg("idx1"),
458             python::arg("idx2"), python::arg("idx3"), python::arg("idx4")),
459            "Retrieves MMFF out-of-plane bending force constant for atoms with "
460            "indexes "
461            "idx1, idx2, idx3, idx4 as a koop float value")
462       .def("GetMMFFVdWParams", &PyMMFFMolProperties::getMMFFVdWParams,
463            (python::arg("self"), python::arg("idx1"), python::arg("idx2")),
464            "Retrieves MMFF van der Waals parameters for atoms with indexes "
465            "idx1, idx2 as a (R_ij_starUnscaled, epsilonUnscaled, R_ij_star, "
466            "epsilon) tuple, "
467            "or None if no parameters could be found")
468       .def("SetMMFFDielectricModel",
469            &PyMMFFMolProperties::setMMFFDielectricModel,
470            (python::arg("self"), python::arg("dielModel") = 1),
471            "Sets the DielModel MMFF property (1: constant; 2: "
472            "distance-dependent; "
473            "defaults to constant)")
474       .def("SetMMFFDielectricConstant",
475            &PyMMFFMolProperties::setMMFFDielectricConstant,
476            (python::arg("self"), python::arg("dielConst") = 1.0),
477            "Sets the DielConst MMFF property (defaults to 1.0)")
478       .def("SetMMFFBondTerm", &PyMMFFMolProperties::setMMFFBondTerm,
479            (python::arg("self"), python::arg("state") = true),
480            "Sets the bond term to be included in the MMFF equation (defaults "
481            "to True)")
482       .def("SetMMFFAngleTerm", &PyMMFFMolProperties::setMMFFAngleTerm,
483            (python::arg("self"), python::arg("state") = true),
484            "Sets the angle term to be included in the MMFF equation (defaults "
485            "to True)")
486       .def("SetMMFFStretchBendTerm",
487            &PyMMFFMolProperties::setMMFFStretchBendTerm,
488            (python::arg("self"), python::arg("state") = true),
489            "Sets the stretch-bend term to be included in the MMFF equation "
490            "(defaults to True)")
491       .def("SetMMFFOopTerm", &PyMMFFMolProperties::setMMFFOopTerm,
492            (python::arg("self"), python::arg("state") = true),
493            "Sets the out-of-plane bend term to be included in the MMFF "
494            "equation (defaults to True)")
495       .def("SetMMFFTorsionTerm", &PyMMFFMolProperties::setMMFFTorsionTerm,
496            (python::arg("self"), python::arg("state") = true),
497            "Sets the torsional term to be included in the MMFF equation "
498            "(defaults to True)")
499       .def("SetMMFFVdWTerm", &PyMMFFMolProperties::setMMFFVdWTerm,
500            (python::arg("self"), python::arg("state") = true),
501            "Sets the Van der Waals term to be included in the MMFF equation "
502            "(defaults to True)")
503       .def("SetMMFFEleTerm", &PyMMFFMolProperties::setMMFFEleTerm,
504            (python::arg("self"), python::arg("state") = true),
505            "Sets the electrostatic term to be included in the MMFF equation "
506            "(defaults to True)")
507       .def("SetMMFFVariant", &PyMMFFMolProperties::setMMFFVariant,
508            (python::arg("self"), python::arg("mmffVariant") = "MMFF94"),
509            "Sets the MMFF variant to be used (\"MMFF94\" or \"MMFF94s\"; "
510            "defaults to \"MMFF94\")")
511       .def("SetMMFFVerbosity", &PyMMFFMolProperties::setMMFFVerbosity,
512            (python::arg("self"), python::arg("verbosity") = 0),
513            "Sets the MMFF verbosity (0: none; 1: low; 2: high; defaults to 0)");
514 }
515 /*
516     (python::arg("self"), python::arg("mol"), python::arg("idx1"),
517    python::arg("idx2")),
518     "Retrieves MMFF bond stretch parameters for atoms with indexes idx1, idx2; "
519     "as a tuple (bondType, kb, r0)")
520 */
521