1 /**********************************************************************
2 forcefield.cpp - Handle OBForceField class.
3 
4 Copyright (C) 2006-2007 by Tim Vandermeersch <tim.vandermeersch@gmail.com>
5 Some portions Copyright (C) 2007-2008 by Geoffrey Hutchison
6 
7 This file is part of the Open Babel project.
8 For more information, see <http://openbabel.org/>
9 
10 This program is free software; you can redistribute it and/or modify
11 it under the terms of the GNU General Public License as published by
12 the Free Software Foundation version 2 of the License.
13 
14 This program is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17 GNU General Public License for more details.
18 
19 ***********************************************************************/
20 #include <openbabel/babelconfig.h>
21 
22 #include <set>
23 
24 #include <openbabel/forcefield.h>
25 
26 #include <openbabel/mol.h>
27 #include <openbabel/atom.h>
28 #include <openbabel/bond.h>
29 #include <openbabel/ring.h>
30 #include <openbabel/obiter.h>
31 #include <openbabel/math/matrix3x3.h>
32 #include <openbabel/rotamer.h>
33 #include <openbabel/rotor.h>
34 #include <openbabel/grid.h>
35 #include <openbabel/griddata.h>
36 #include <openbabel/elements.h>
37 #include "rand.h"
38 
39 using namespace std;
40 
41 namespace OpenBabel
42 {
43 #if defined(__CYGWIN__) || defined(__MINGW32__)
44   // macro to implement static OBPlugin::PluginMapType& Map()
PLUGIN_CPP_FILE(OBForceField)45   PLUGIN_CPP_FILE(OBForceField)
46 #endif
47 
48   /** \class OBForceField forcefield.h <openbabel/forcefield.h>
49       \brief Base class for molecular mechanics force fields
50 
51       The OBForceField class is the base class for molecular mechanics in Open Babel.
52       Classes derived from the OBForceField implement specific force fields (Ghemical,
53       MMFF94, UFF, ...).Other classes such as OBFFParameter, OBFFConstraint,
54       OBFFCalculation and its derived classes are only for internal use. As a user
55       interested in using the available force fields in Open Babel, you don't need
56       these classes. The rest of this short introduction is aimed at these users. For
57       information on how to implement additional force fields, see the wiki pages or
58       post your questions to the openbabel-devel mailing list.
59 
60       Before we can start using a force field, we must first select it and set it up.
61       This is illustrated in the first example below. The Setup procedure assigns atom
62       types, charges and parameters. There are several reasons why this may fail, a log
63       message will be written to the logfile before Setup() returns false.
64 
65       The force field classes use their own logging functions. You can set the logfile
66       using SetLogFile() and set the log level using SetLogLevel(). If needed you can
67       also write to the logfile using OBFFLog(). There are four log levels:
68       BFF_LOGLVL_NONE, OBFF_LOGLVL_LOW, OBFF_LOGLVL_MEDIUM, OBFF_LOGLVL_HIGH.
69       See the API documentation to know what kind of output each function writes to
70       the logfile for the different log levels.
71 
72       Below are two examples which explain the basics.
73 
74       This piece of code will output a list of available forcefields to cout:
75       \code
76       OBPlugin::List("forcefields");
77       \endcode
78 
79       Calculate the energy for the structure in mol using the Ghemical forcefield.
80       \code
81       #include <openbabel/forcefield.h>
82       #include <openbabel/mol.h>
83 
84       // See OBConversion class to fill the mol object.
85       OBMol mol;
86       // Select the forcefield, this returns a pointer that we
87       // will later use to access the forcefield functions.
88       OBForceField* pFF = OBForceField::FindForceField("MMFF94");
89 
90       // Make sure we have a valid pointer
91       if (!pFF)
92       // exit...
93 
94       // Set the logfile (can also be &cout or &cerr)
95       pFF->SetLogFile(&cerr);
96       // Set the log level. See indivual functions to know
97       // what kind of output each function produces for the
98       // different log levels.
99       pFF->SetLogLevel(OBFF_LOGLVL_HIGH);
100 
101       // We need to setup the forcefield before we can use it. Setup()
102       // returns false if it failes to find the atom types, parameters, ...
103       if (!pFF->Setup(mol)) {
104       cerr << "ERROR: could not setup force field." << endl;
105       }
106 
107       // Calculate the energy. The output will be written to the
108       // logfile specified by SetLogFile()
109       pFF->Energy();
110       \endcode
111 
112       Minimize the structure in mol using conjugate gradients.
113       \code
114       #include <openbabel/forcefield.h>
115       #include <openbabel/mol.h>
116 
117       OBMol mol;
118       OBForceField* pFF = OBForceField::FindForceField("MMFF94");
119 
120       // Make sure we have a valid pointer
121       if (!pFF)
122       // exit...
123 
124       pFF->SetLogFile(&cerr);
125       pFF->SetLogLevel(OBFF_LOGLVL_LOW);
126       if (!pFF->Setup(mol)) {
127       cerr << "ERROR: could not setup force field." << endl;
128       }
129 
130       // Perform the actual minimization, maximum 1000 steps
131       pFF->ConjugateGradients(1000);
132       \endcode
133 
134       Minimize the structure in mol using steepest descent and fix the position of atom with index 1.
135       \code
136       #include <openbabel/forcefield.h>
137       #include <openbabel/mol.h>
138 
139       OBMol mol;
140       OBForceField* pFF = OBForceField::FindForceField("MMFF94");
141 
142       // Make sure we have a valid pointer
143       if (!pFF)
144       // exit...
145 
146       pFF->SetLogFile(&cerr);
147       pFF->SetLogLevel(OBFF_LOGLVL_LOW);
148 
149       // Set the constraints
150       OBFFConstraints constraints;
151       constraints.AddAtomConstraint(1);
152 
153       // We pass the constraints as argument for Setup()
154       if (!pFF->Setup(mol, constraints)) {
155       cerr << "ERROR: could not setup force field." << endl;
156       }
157 
158       // Perform the actual minimization, maximum 1000 steps
159       pFF->ConjugateGradients(1000);
160       \endcode
161 
162       Minimize a ligand molecule in a binding pocket.
163       \code
164       #include <openbabel/forcefield.h>
165       #include <openbabel/mol.h>
166 
167       OBMol mol;
168 
169       //
170       // Read the pocket + ligand (initial guess for position) into mol...
171       //
172 
173       OBBitVec pocket; // set the bits with atoms indexes for the pocket to 1...
174       OBBitVec ligand; // set the bits with atoms indexes for the ligand to 1...
175 
176       OBForceField* pFF = OBForceField::FindForceField("MMFF94");
177 
178       // Make sure we have a valid pointer
179       if (!pFF)
180       // exit...
181 
182       pFF->SetLogFile(&cerr);
183       pFF->SetLogLevel(OBFF_LOGLVL_LOW);
184 
185       // Fix the binding pocket atoms
186       OBFFConstraints constraints;
187       FOR_ATOMS_OF_MOL (a, mol) {
188       if (pocket.BitIsSet(a->GetIdx())
189       constraints.AddAtomConstraint(a->GetIdx());
190       }
191 
192       // Specify the interacting groups. The pocket atoms are fixed, so there
193       // is no need to calculate intra- and inter-molecular interactions for
194       // the binding pocket.
195       pFF->AddIntraGroup(ligand); // bonded interactions in the ligand
196       pFF->AddInterGroup(ligand); // non-bonded between ligand-ligand atoms
197       pFF->AddInterGroups(ligand, pocket); // non-bonded between ligand and pocket atoms
198 
199       // We pass the constraints as argument for Setup()
200       if (!pFF->Setup(mol, constraints)) {
201       cerr << "ERROR: could not setup force field." << endl;
202       }
203 
204       // Perform the actual minimization, maximum 1000 steps
205       pFF->ConjugateGradients(1000);
206       \endcode
207 
208   **/
209 
210   int OBForceField::GetParameterIdx(int a, int b, int c, int d, vector<OBFFParameter> &parameter)
211   {
212     if (!b)
213       for (unsigned int idx=0; idx < parameter.size(); idx++)
214         if (a == parameter[idx].a)
215           return idx;
216 
217     if (!c)
218       for (unsigned int idx=0; idx < parameter.size(); idx++)
219         if (((a == parameter[idx].a) && (b == parameter[idx].b)) ||
220             ((a == parameter[idx].b) && (b == parameter[idx].a)))
221           return idx;
222 
223     if (!d)
224       for (unsigned int idx=0; idx < parameter.size(); idx++)
225         if (((a == parameter[idx].a) && (b == parameter[idx].b) && (c == parameter[idx].c)) ||
226             ((a == parameter[idx].c) && (b == parameter[idx].b) && (c == parameter[idx].a)))
227           return idx;
228 
229     for (unsigned int idx=0; idx < parameter.size(); idx++)
230       if (((a == parameter[idx].a) && (b == parameter[idx].b) &&
231            (c == parameter[idx].c) && (d == parameter[idx].d)) ||
232           ((a == parameter[idx].d) && (b == parameter[idx].c) &&
233            (c == parameter[idx].b) && (d == parameter[idx].a)))
234         return idx;
235 
236     return -1;
237   }
238 
GetParameter(int a,int b,int c,int d,vector<OBFFParameter> & parameter)239   OBFFParameter* OBForceField::GetParameter(int a, int b, int c, int d,
240                                             vector<OBFFParameter> &parameter)
241   {
242     OBFFParameter *par;
243 
244     if (!b)
245       for (unsigned int idx=0; idx < parameter.size(); idx++)
246         if (a == parameter[idx].a) {
247           par = &parameter[idx];
248           return par;
249         }
250 
251     if (!c)
252       for (unsigned int idx=0; idx < parameter.size(); idx++)
253         if (((a == parameter[idx].a) && (b == parameter[idx].b)) ||
254             ((a == parameter[idx].b) && (b == parameter[idx].a))) {
255           par = &parameter[idx];
256           return par;
257         }
258 
259     if (!d)
260       for (unsigned int idx=0; idx < parameter.size(); idx++)
261         if (((a == parameter[idx].a) && (b == parameter[idx].b) && (c == parameter[idx].c)) ||
262             ((a == parameter[idx].c) && (b == parameter[idx].b) && (c == parameter[idx].a))) {
263           par = &parameter[idx];
264           return par;
265         }
266 
267     for (unsigned int idx=0; idx < parameter.size(); idx++)
268       if (((a == parameter[idx].a) && (b == parameter[idx].b) &&
269            (c == parameter[idx].c) && (d == parameter[idx].d)) ||
270           ((a == parameter[idx].d) && (b == parameter[idx].c) &&
271            (c == parameter[idx].b) && (d == parameter[idx].a))) {
272         par = &parameter[idx];
273         return par;
274       }
275 
276     return nullptr;
277   }
278 
GetParameter(const char * a,const char * b,const char * c,const char * d,vector<OBFFParameter> & parameter)279   OBFFParameter* OBForceField::GetParameter(const char* a, const char* b, const char* c,
280                                             const char* d, vector<OBFFParameter> &parameter)
281   {
282     OBFFParameter *par;
283     if (a == nullptr)
284       return nullptr;
285 
286     if (b == nullptr) {
287       string _a(a);
288       for (unsigned int idx=0; idx < parameter.size(); idx++)
289         if (_a == parameter[idx]._a) {
290           par = &parameter[idx];
291           return par;
292         }
293       return nullptr;
294     }
295     if (c == nullptr) {
296       string _a(a);
297       string _b(b);
298       for (unsigned int idx=0; idx < parameter.size(); idx++) {
299         if (((_a == parameter[idx]._a) && (_b == parameter[idx]._b)) ||
300             ((_a == parameter[idx]._b) && (_b == parameter[idx]._a))) {
301           par = &parameter[idx];
302           return par;
303         }
304       }
305       return nullptr;
306     }
307     if (d == nullptr) {
308       string _a(a);
309       string _b(b);
310       string _c(c);
311       for (unsigned int idx=0; idx < parameter.size(); idx++) {
312         if (((_a == parameter[idx]._a) && (_b == parameter[idx]._b) && (_c == parameter[idx]._c)) ||
313             ((_a == parameter[idx]._c) && (_b == parameter[idx]._b) && (_c == parameter[idx]._a))) {
314           par = &parameter[idx];
315           return par;
316         }
317       }
318       return nullptr;
319     }
320     string _a(a);
321     string _b(b);
322     string _c(c);
323     string _d(d);
324     for (unsigned int idx=0; idx < parameter.size(); idx++)
325       if (((_a == parameter[idx]._a) && (_b == parameter[idx]._b) &&
326            (_c == parameter[idx]._c) && (_d == parameter[idx]._d)) ||
327           ((_a == parameter[idx]._d) && (_b == parameter[idx]._c) &&
328            (_c == parameter[idx]._b) && (_d == parameter[idx]._a))) {
329         par = &parameter[idx];
330         return par;
331       }
332 
333     return nullptr;
334   }
335 
336   //////////////////////////////////////////////////////////////////////////////////
337   //
338   // Constraints
339   //
340   //////////////////////////////////////////////////////////////////////////////////
341 
342   OBFFConstraints OBForceField::_constraints = OBFFConstraints(); // define static data variable
343   unsigned int OBForceField::_fixAtom = 0; // define static data variable
344   unsigned int OBForceField::_ignoreAtom = 0; // define static data variable
345 
GetConstraints()346   OBFFConstraints& OBForceField::GetConstraints()
347   {
348     return _constraints;
349   }
350 
SetConstraints(OBFFConstraints & constraints)351   void OBForceField::SetConstraints(OBFFConstraints& constraints)
352   {
353     if (!(_constraints.GetIgnoredBitVec() == constraints.GetIgnoredBitVec())) {
354       _constraints = constraints;
355       if (!SetupCalculations()) {
356         _validSetup = false;
357         return;
358       }
359     } else {
360       _constraints = constraints;
361     }
362 
363     _constraints.Setup(_mol);
364   }
365 
SetFixAtom(int index)366   void OBForceField::SetFixAtom(int index)
367   {
368     _fixAtom = index;
369   }
370 
UnsetFixAtom()371   void OBForceField::UnsetFixAtom()
372   {
373     _fixAtom = 0;
374   }
375 
SetIgnoreAtom(int index)376   void OBForceField::SetIgnoreAtom(int index)
377   {
378     _ignoreAtom = index; // remember the index
379   }
380 
UnsetIgnoreAtom()381   void OBForceField::UnsetIgnoreAtom()
382   {
383     _ignoreAtom = 0;
384   }
385 
IgnoreCalculation(int a,int b)386   bool OBForceField::IgnoreCalculation(int a, int b)
387   {
388     if (!_ignoreAtom)
389       return false;
390 
391     if (_ignoreAtom == a)
392       return true;
393     if (_ignoreAtom == b)
394       return true;
395 
396     return false;
397   }
398 
IgnoreCalculation(int a,int b,int c)399   bool OBForceField::IgnoreCalculation(int a, int b, int c)
400   {
401     if (OBForceField::IgnoreCalculation(a, c))
402       return true;
403     if (_ignoreAtom == b)
404       return true;
405 
406     return false;
407   }
408 
IgnoreCalculation(int a,int b,int c,int d)409   bool OBForceField::IgnoreCalculation(int a, int b, int c, int d)
410   {
411     if (OBForceField::IgnoreCalculation(a, b, c))
412       return true;
413     if (_ignoreAtom == d)
414       return true;
415 
416     return false;
417   }
418 
OBFFConstraints()419   OBFFConstraints::OBFFConstraints()
420   {
421     _factor = 50000.0;
422   }
423 
Setup(OBMol & mol)424   void OBFFConstraints::Setup(OBMol& mol)
425   {
426     vector<OBFFConstraint>::iterator i;
427 
428     for (i = _constraints.begin(); i != _constraints.end(); ++i) {
429       i->a = mol.GetAtom(i->ia);
430       i->b = mol.GetAtom(i->ib);
431       i->c = mol.GetAtom(i->ic);
432       i->d = mol.GetAtom(i->id);
433     }
434   }
435 
GetGradient(int a)436   vector3 OBFFConstraints::GetGradient(int a)
437   {
438     vector<OBFFConstraint>::iterator i;
439 
440     vector3 grad(0.0, 0.0, 0.0);
441 
442     for (i = _constraints.begin(); i != _constraints.end(); ++i)
443       grad += i->GetGradient(a);
444 
445     return grad;
446   }
447 
Clear()448   void OBFFConstraints::Clear()
449   {
450     _constraints.clear();
451     _ignored.Clear();
452     _fixed.Clear();
453     _Xfixed.Clear();
454     _Yfixed.Clear();
455     _Zfixed.Clear();
456   }
457 
Size() const458   int OBFFConstraints::Size() const
459   {
460     return _constraints.size();
461   }
462 
GetConstraintEnergy()463   double OBFFConstraints::GetConstraintEnergy()
464   {
465     vector<OBFFConstraint>::iterator i;
466     double constraint_energy = 0.0;
467 
468     for (i = _constraints.begin(); i != _constraints.end(); ++i)
469       if ( (i->type == OBFF_CONST_DISTANCE) || (i->type == OBFF_CONST_ANGLE) ||
470            (i->type == OBFF_CONST_TORSION) ) {
471         vector3 da, db, dc, dd;
472         double delta, delta2, rab, theta, dE;
473 
474         switch (i->type) {
475         case OBFF_CONST_DISTANCE:
476           if (i->a == nullptr || (i->b) == nullptr)
477             break;
478 
479           da = (i->a)->GetVector();
480           db = (i->b)->GetVector();
481 
482           rab = OBForceField::VectorLengthDerivative(da, db);
483           delta = rab - i->constraint_value;
484           delta2 = delta * delta;
485           constraint_energy += i->factor * delta2;
486           dE = 2.0 * i->factor * delta;
487 
488           i->grada = dE * da;
489           i->gradb = dE * db;
490           break;
491         case OBFF_CONST_ANGLE:
492           if (i->a == nullptr || i->b == nullptr || i->c == nullptr)
493             break;
494 
495           da = (i->a)->GetVector();
496           db = (i->b)->GetVector();
497           dc = (i->c)->GetVector();
498 
499           theta = OBForceField::VectorAngleDerivative(da, db, dc);
500           delta = theta - i->constraint_value;
501           delta2 = delta * delta;
502           constraint_energy += 0.0002 * i->factor * delta2;
503           dE = 0.0004 * i->factor * delta;
504 
505           i->grada = dE * da;
506           i->gradb = dE * db;
507           i->gradc = dE * dc;
508           break;
509         case OBFF_CONST_TORSION:
510           if (i->a == nullptr || i->b == nullptr || i->c == nullptr || i->d == nullptr)
511             break;
512 
513           da = (i->a)->GetVector();
514           db = (i->b)->GetVector();
515           dc = (i->c)->GetVector();
516           dd = (i->d)->GetVector();
517 
518           theta = OBForceField::VectorTorsionDerivative(da, db, dc, dd);
519           if (!isfinite(theta))
520             theta = 1.0e-7;
521 
522           theta = DEG_TO_RAD * (theta + 180.0 - i->constraint_value);
523           constraint_energy +=  0.001 * i->factor * (1.0 + cos(theta));
524           dE = 0.001 * i->factor * sin(theta);
525 
526           i->grada = dE * da;
527           i->gradb = dE * db;
528           i->gradc = dE * dc;
529           i->gradd = dE * dd;
530           break;
531         default:
532           break;
533         }
534       }
535     return constraint_energy;
536   }
537 
DeleteConstraint(int index)538   void OBFFConstraints::DeleteConstraint(int index)
539   {
540     vector<OBFFConstraint>::iterator i;
541     int n = 0;
542 
543     for (i = _constraints.begin(); i != _constraints.end(); ++n, ++i) {
544       if (n == index) {
545         if (i->type == OBFF_CONST_IGNORE)
546           _ignored.SetBitOff(i->ia);
547         if (i->type == OBFF_CONST_ATOM)
548           _fixed.SetBitOff(i->ia);
549         if (i->type == OBFF_CONST_ATOM_X)
550           _Xfixed.SetBitOff(i->ia);
551         if (i->type == OBFF_CONST_ATOM_Y)
552           _Yfixed.SetBitOff(i->ia);
553         if (i->type == OBFF_CONST_ATOM_Z)
554           _Zfixed.SetBitOff(i->ia);
555 
556 
557         _constraints.erase(i);
558         break;
559       }
560     }
561   }
562 
SetFactor(double factor)563   void OBFFConstraints::SetFactor(double factor)
564   {
565     _factor = factor;
566   }
567 
GetFactor()568   double OBFFConstraints::GetFactor()
569   {
570     return _factor;
571   }
572 
AddIgnore(int a)573   void OBFFConstraints::AddIgnore(int a)
574   {
575     _ignored.SetBitOn(a);
576 
577     OBFFConstraint constraint;
578     constraint.type = OBFF_CONST_IGNORE; // constraint type
579     constraint.ia   = a; // atom to fix
580     _constraints.push_back(constraint);
581   }
582 
AddAtomConstraint(int a)583   void OBFFConstraints::AddAtomConstraint(int a)
584   {
585     _fixed.SetBitOn(a);
586 
587     OBFFConstraint constraint;
588     constraint.type = OBFF_CONST_ATOM; // constraint type
589     constraint.ia   = a; // atom to fix
590     constraint.factor = _factor;
591     _constraints.push_back(constraint);
592   }
593 
AddAtomXConstraint(int a)594   void OBFFConstraints::AddAtomXConstraint(int a)
595   {
596     _Xfixed.SetBitOn(a);
597 
598     OBFFConstraint constraint;
599     constraint.type = OBFF_CONST_ATOM_X; // constraint type
600     constraint.ia   = a; // atom to fix
601     constraint.factor = _factor;
602     _constraints.push_back(constraint);
603   }
604 
AddAtomYConstraint(int a)605   void OBFFConstraints::AddAtomYConstraint(int a)
606   {
607     _Yfixed.SetBitOn(a);
608 
609     OBFFConstraint constraint;
610     constraint.type = OBFF_CONST_ATOM_Y; // constraint type
611     constraint.ia   = a; // atom to fix
612     constraint.factor = _factor;
613     _constraints.push_back(constraint);
614   }
615 
AddAtomZConstraint(int a)616   void OBFFConstraints::AddAtomZConstraint(int a)
617   {
618     _Zfixed.SetBitOn(a);
619 
620     OBFFConstraint constraint;
621     constraint.type = OBFF_CONST_ATOM_Z; // constraint type
622     constraint.ia   = a; // atom to fix
623     constraint.factor = _factor;
624     _constraints.push_back(constraint);
625   }
626 
AddDistanceConstraint(int a,int b,double length)627   void OBFFConstraints::AddDistanceConstraint(int a, int b, double length)
628   {
629     OBFFConstraint constraint;
630     constraint.type = OBFF_CONST_DISTANCE; // constraint type
631     constraint.ia   = a; // atom a
632     constraint.ib   = b; // atom b
633     constraint.constraint_value = length; // bond length
634     constraint.factor = _factor;
635     _constraints.push_back(constraint);
636   }
637 
AddAngleConstraint(int a,int b,int c,double angle)638   void OBFFConstraints::AddAngleConstraint(int a, int b, int c, double angle)
639   {
640     OBFFConstraint constraint;
641     constraint.type = OBFF_CONST_ANGLE; // constraint type
642     constraint.ia   = a; // atom a
643     constraint.ib   = b; // atom b
644     constraint.ic   = c; // atom c
645     constraint.constraint_value = angle; // angle
646     constraint.factor = _factor;
647     _constraints.push_back(constraint);
648   }
649 
AddTorsionConstraint(int a,int b,int c,int d,double torsion)650   void OBFFConstraints::AddTorsionConstraint(int a, int b, int c, int d, double torsion)
651   {
652     OBFFConstraint constraint;
653     constraint.type = OBFF_CONST_TORSION; // constraint type
654     constraint.ia   = a; // atom a
655     constraint.ib   = b; // atom b
656     constraint.ic   = c; // atom c
657     constraint.id   = d; // atom d
658     constraint.constraint_value = torsion; // torsion
659     constraint.factor = _factor;
660     _constraints.push_back(constraint);
661   }
662 
GetConstraintType(int index) const663   int OBFFConstraints::GetConstraintType(int index) const
664   {
665     if (index >= _constraints.size())
666       return 0;
667 
668     return _constraints[index].type;
669   }
670 
GetConstraintValue(int index) const671   double OBFFConstraints::GetConstraintValue(int index) const
672   {
673     if (index >= _constraints.size())
674       return 0;
675 
676     return _constraints[index].constraint_value;
677   }
678 
GetConstraintAtomA(int index) const679   int OBFFConstraints::GetConstraintAtomA(int index) const
680   {
681     if (index >= _constraints.size())
682       return 0;
683 
684     return _constraints[index].ia;
685   }
686 
GetConstraintAtomB(int index) const687   int OBFFConstraints::GetConstraintAtomB(int index) const
688   {
689     if (index >= _constraints.size())
690       return 0;
691 
692     return _constraints[index].ib;
693   }
694 
GetConstraintAtomC(int index) const695   int OBFFConstraints::GetConstraintAtomC(int index) const
696   {
697     if (index >= _constraints.size())
698       return 0;
699 
700     return _constraints[index].ic;
701   }
702 
GetConstraintAtomD(int index) const703   int OBFFConstraints::GetConstraintAtomD(int index) const
704   {
705     if (index >= _constraints.size())
706       return 0;
707 
708     return _constraints[index].id;
709   }
710 
IsIgnored(int index)711   bool OBFFConstraints::IsIgnored(int index)
712   {
713     return _ignored.BitIsSet(index);
714   }
715 
IsFixed(int index)716   bool OBFFConstraints::IsFixed(int index)
717   {
718     return _fixed.BitIsSet(index);
719   }
720 
IsXFixed(int index)721   bool OBFFConstraints::IsXFixed(int index)
722   {
723     return _Xfixed.BitIsSet(index);
724   }
725 
IsYFixed(int index)726   bool OBFFConstraints::IsYFixed(int index)
727   {
728     return _Yfixed.BitIsSet(index);
729   }
730 
IsZFixed(int index)731   bool OBFFConstraints::IsZFixed(int index)
732   {
733     return _Zfixed.BitIsSet(index);
734   }
735 
736   //////////////////////////////////////////////////////////////////////////////////
737   //
738   // Methods for logging
739   //
740   //////////////////////////////////////////////////////////////////////////////////
741 
PrintTypes()742   void OBForceField::PrintTypes()
743   {
744     IF_OBFF_LOGLVL_LOW {
745       OBFFLog("\nA T O M   T Y P E S\n\n");
746       OBFFLog("IDX\tTYPE\tRING\n");
747 
748       FOR_ATOMS_OF_MOL (a, _mol) {
749         snprintf(_logbuf, BUFF_SIZE, "%d\t%s\t%s\n", a->GetIdx(), a->GetType(),
750           (a->IsInRing() ? (a->IsAromatic() ? "AR" : "AL") : "NO"));
751         OBFFLog(_logbuf);
752       }
753     }
754   }
755 
PrintFormalCharges()756   void OBForceField::PrintFormalCharges()
757   {
758     IF_OBFF_LOGLVL_LOW {
759       OBFFLog("\nF O R M A L   C H A R G E S\n\n");
760       OBFFLog("IDX\tCHARGE\n");
761 
762       FOR_ATOMS_OF_MOL (a, _mol) {
763         snprintf(_logbuf, BUFF_SIZE, "%d\t%f\n", a->GetIdx(), a->GetPartialCharge());
764         OBFFLog(_logbuf);
765       }
766     }
767   }
768 
PrintPartialCharges()769   void OBForceField::PrintPartialCharges()
770   {
771     IF_OBFF_LOGLVL_LOW {
772       OBFFLog("\nP A R T I A L   C H A R G E S\n\n");
773       OBFFLog("IDX\tCHARGE\n");
774 
775       FOR_ATOMS_OF_MOL (a, _mol) {
776         snprintf(_logbuf, BUFF_SIZE, "%d\t%f\n", a->GetIdx(), a->GetPartialCharge());
777         OBFFLog(_logbuf);
778       }
779     }
780   }
781 
PrintVelocities()782   void OBForceField::PrintVelocities()
783   {
784     IF_OBFF_LOGLVL_LOW {
785       OBFFLog("\nA T O M   V E L O C I T I E S\n\n");
786       OBFFLog("IDX\tVELOCITY\n");
787 
788       FOR_ATOMS_OF_MOL (a, _mol) {
789         snprintf(_logbuf, BUFF_SIZE, "%d\t<%8.3f, %8.3f, %8.3f>\n", a->GetIdx(), _velocityPtr[a->GetIdx()],
790                  _velocityPtr[a->GetIdx()+1], _velocityPtr[a->GetIdx()+2]);
791         OBFFLog(_logbuf);
792       }
793     }
794   }
795 
SetLogFile(ostream * pos)796   bool OBForceField::SetLogFile(ostream* pos)
797   {
798     if(pos)
799       _logos = pos;
800     else
801       _logos = &cout;
802 
803     return true;
804   }
805 
806   //////////////////////////////////////////////////////////////////////////////////
807   //
808   // General
809   //
810   //////////////////////////////////////////////////////////////////////////////////
811 
Setup(OBMol & mol)812   bool OBForceField::Setup(OBMol &mol)
813   {
814     if (!_init) {
815       ParseParamFile();
816       _init = true;
817       _velocityPtr = nullptr;
818       _gradientPtr = nullptr;
819       _grad1 = nullptr;
820     }
821 
822     if (IsSetupNeeded(mol)) {
823       _mol = mol;
824       _ncoords = _mol.NumAtoms() * 3;
825 
826       if (_velocityPtr)
827         delete [] _velocityPtr;
828       _velocityPtr = nullptr;
829 
830       if (_gradientPtr)
831         delete [] _gradientPtr;
832       _gradientPtr = new double[_ncoords];
833 
834       if (_mol.NumAtoms() && _constraints.Size())
835         _constraints.Setup(_mol);
836 
837       _mol.SetSSSRPerceived(false);
838       _mol.DeleteData(OBGenericDataType::TorsionData); // bug #1954233
839 
840       if (!SetTypes()) {
841         _validSetup = false;
842         return false;
843       }
844 
845       SetFormalCharges();
846       SetPartialCharges();
847 
848       if (!SetupCalculations()) {
849         _validSetup = false;
850         return false;
851       }
852 
853     } else {
854       if (_validSetup) {
855         PrintTypes();
856         PrintFormalCharges();
857         PrintPartialCharges();
858         SetCoordinates(mol);
859         return true;
860       } else {
861         return false;
862       }
863     }
864 
865     _validSetup = true;
866     return true;
867   }
868 
Setup(OBMol & mol,OBFFConstraints & constraints)869   bool OBForceField::Setup(OBMol &mol, OBFFConstraints &constraints)
870   {
871     if (!_init) {
872       ParseParamFile();
873       _init = true;
874       _velocityPtr = nullptr;
875       _gradientPtr = nullptr;
876     }
877 
878     if (IsSetupNeeded(mol)) {
879       _mol = mol;
880       _ncoords = _mol.NumAtoms() * 3;
881 
882       if (_velocityPtr)
883         delete [] _velocityPtr;
884       _velocityPtr = nullptr;
885 
886       if (_gradientPtr)
887         delete [] _gradientPtr;
888       _gradientPtr = new double[_ncoords];
889 
890       _constraints = constraints;
891       if (_mol.NumAtoms() && _constraints.Size())
892         _constraints.Setup(_mol);
893 
894       _mol.SetSSSRPerceived(false);
895       _mol.DeleteData(OBGenericDataType::TorsionData); // bug #1954233
896 
897       if (!SetTypes()) {
898         _validSetup = false;
899         return false;
900       }
901 
902       SetFormalCharges();
903       SetPartialCharges();
904 
905       if (!SetupCalculations()) {
906         _validSetup = false;
907         return false;
908       }
909 
910     } else {
911       if (_validSetup) {
912         if (!(_constraints.GetIgnoredBitVec() == constraints.GetIgnoredBitVec())) {
913           _constraints = constraints;
914           if (!SetupCalculations()) {
915             _validSetup = false;
916             return false;
917           }
918         } else {
919           _constraints = constraints;
920         }
921 
922         _constraints.Setup(_mol);
923         SetCoordinates(mol);
924         return true;
925       } else {
926         return false;
927       }
928     }
929 
930     _validSetup = true;
931     return true;
932   }
933 
SetLogLevel(int level)934   bool OBForceField::SetLogLevel(int level)
935   {
936     _loglvl = level;
937 
938     return true;
939   }
940 
941   // This function might need expanding, bu could really increase performance for
942   // avogadro's AutoOpt tool
IsSetupNeeded(OBMol & mol)943   bool OBForceField::IsSetupNeeded(OBMol &mol)
944   {
945     if (_mol.NumAtoms() != mol.NumAtoms())
946       return true;
947 
948     if (_mol.NumBonds() != mol.NumBonds())
949       return true;
950 
951     FOR_ATOMS_OF_MOL (atom, _mol) {
952       // if we have Fe or Cu the atom type depends on the formal
953       // charge, so a new setup must be forced anyway
954       if (atom->GetAtomicNum() == 26 || atom->GetAtomicNum() == 29)
955         return true;
956       if (atom->GetAtomicNum() != (mol.GetAtom(atom->GetIdx()))->GetAtomicNum())
957         return true;
958       if (atom->GetExplicitDegree() != (mol.GetAtom(atom->GetIdx()))->GetExplicitDegree())
959         return true;
960     }
961     FOR_BONDS_OF_MOL (bond, _mol) {
962       if (bond->GetBondOrder() != (mol.GetBond(bond->GetIdx()))->GetBondOrder())
963         return true;
964       if (bond->GetBeginAtom()->GetAtomicNum()
965           != (mol.GetBond(bond->GetIdx()))->GetBeginAtom()->GetAtomicNum()
966           || bond->GetEndAtom()->GetAtomicNum()
967           != (mol.GetBond(bond->GetIdx()))->GetEndAtom()->GetAtomicNum())
968         return true;
969     }
970 
971     return false;
972   }
973 
GetAtomTypes(OBMol & mol)974   bool OBForceField::GetAtomTypes(OBMol &mol)
975   {
976     if (_mol.NumAtoms() != mol.NumAtoms())
977       return false;
978 
979     FOR_ATOMS_OF_MOL (intAtom, _mol) {
980       OBAtom *extAtom = mol.GetAtom(intAtom->GetIdx());
981 
982       if (extAtom->HasData("FFAtomType")) {
983         OBPairData *data = (OBPairData*) extAtom->GetData("FFAtomType");
984         data->SetValue(intAtom->GetType());
985       } else {
986         OBPairData *data = new OBPairData();
987        	data->SetAttribute("FFAtomType");
988         data->SetValue(intAtom->GetType());
989         extAtom->SetData(data);
990       }
991     }
992 
993     return true;
994   }
995 
GetPartialCharges(OBMol & mol)996   bool OBForceField::GetPartialCharges(OBMol &mol)
997   {
998     if (_mol.NumAtoms() != mol.NumAtoms())
999       return false;
1000 
1001     ostringstream out;
1002     FOR_ATOMS_OF_MOL (intAtom, _mol) {
1003       OBAtom *extAtom = mol.GetAtom(intAtom->GetIdx());
1004 
1005       out.str("");
1006       out << intAtom->GetPartialCharge();
1007       if (extAtom->HasData("FFPartialCharge")) {
1008         OBPairData *data = (OBPairData*) extAtom->GetData("FFPartialCharge");
1009         data->SetValue(out.str());
1010       } else {
1011         OBPairData *data = new OBPairData();
1012        	data->SetAttribute("FFPartialCharge");
1013         data->SetValue(out.str());
1014         extAtom->SetData(data);
1015       }
1016     }
1017 
1018     return true;
1019   }
1020 
1021 
GetCoordinates(OBMol & mol)1022   bool OBForceField::GetCoordinates(OBMol &mol)
1023   {
1024     OBAtom *atom;
1025 
1026     if (_mol.NumAtoms() != mol.NumAtoms())
1027       return false;
1028 
1029     // Copy coordinates for current conformer only
1030     FOR_ATOMS_OF_MOL (a, _mol) {
1031       atom = mol.GetAtom(a->GetIdx());
1032       atom->SetVector(a->GetVector());
1033     }
1034 
1035     if (!mol.HasData(OBGenericDataType::ConformerData))
1036       mol.SetData(new OBConformerData);
1037     OBConformerData *cd = (OBConformerData*) mol.GetData(OBGenericDataType::ConformerData);
1038     cd->SetEnergies(_energies);
1039 
1040     vector<vector3> forces;
1041     vector<vector<vector3> > confForces;
1042     for (unsigned int i = 0; i < _mol.NumAtoms(); ++i) {
1043       const int coordIdx = i * 3;
1044       forces.push_back(vector3(_gradientPtr[coordIdx],
1045                                _gradientPtr[coordIdx+1], _gradientPtr[coordIdx+2]));
1046     }
1047     confForces.push_back(forces);
1048     cd->SetForces(confForces);
1049 
1050     return true;
1051   }
1052 
GetConformers(OBMol & mol)1053   bool OBForceField::GetConformers(OBMol &mol)
1054   {
1055     //    OBAtom *atom;
1056 
1057     if (_mol.NumAtoms() != mol.NumAtoms())
1058       return false;
1059 
1060     /*
1061       FOR_ATOMS_OF_MOL (a, _mol) {
1062       atom = mol.GetAtom(a->GetIdx());
1063       atom->SetVector(a->GetVector());
1064       }
1065     */
1066 
1067     //Copy conformer information
1068     if (_mol.NumConformers() > 0) {
1069       int k,l;
1070       vector<double*> conf;
1071       double* xyz = nullptr;
1072       for (k=0 ; k<_mol.NumConformers() ; ++k) {
1073         xyz = new double [3*_mol.NumAtoms()];
1074         for (l=0 ; l<(int) (3*_mol.NumAtoms()) ; ++l)
1075           xyz[l] = _mol.GetConformer(k)[l];
1076         conf.push_back(xyz);
1077       }
1078       mol.SetConformers(conf);
1079       mol.SetConformer(_current_conformer);
1080 
1081       if (!mol.HasData(OBGenericDataType::ConformerData))
1082         mol.SetData(new OBConformerData);
1083       OBConformerData *cd = (OBConformerData*) mol.GetData(OBGenericDataType::ConformerData);
1084       cd->SetEnergies(_energies);
1085 
1086       //mol.SetEnergies(_energies);
1087     }
1088 
1089     return true;
1090   }
1091 
SetCoordinates(OBMol & mol)1092   bool OBForceField::SetCoordinates(OBMol &mol)
1093   {
1094     OBAtom *atom;
1095 
1096     if (_mol.NumAtoms() != mol.NumAtoms())
1097       return false;
1098 
1099     // Copy coordinates for current conformer only
1100     FOR_ATOMS_OF_MOL (a, mol) {
1101       atom = _mol.GetAtom(a->GetIdx());
1102       atom->SetVector(a->GetVector());
1103     }
1104 
1105     return true;
1106   }
1107 
SetConformers(OBMol & mol)1108   bool OBForceField::SetConformers(OBMol &mol)
1109   {
1110     OBAtom *atom;
1111 
1112     if (_mol.NumAtoms() != mol.NumAtoms())
1113       return false;
1114 
1115     FOR_ATOMS_OF_MOL (a, mol) {
1116       atom = _mol.GetAtom(a->GetIdx());
1117       atom->SetVector(a->GetVector());
1118     }
1119 
1120     //Copy conformer information
1121     if (mol.NumConformers() > 1) {
1122       int k,l;
1123       vector<double*> conf;
1124       double* xyz = nullptr;
1125       for (k=0 ; k<mol.NumConformers() ; ++k) {
1126         xyz = new double [3*mol.NumAtoms()];
1127         for (l=0 ; l<(int) (3*mol.NumAtoms()) ; ++l)
1128           xyz[l] = mol.GetConformer(k)[l];
1129         conf.push_back(xyz);
1130       }
1131       _mol.SetConformers(conf);
1132       _mol.SetConformer(_current_conformer);
1133       SetupPointers(); // update pointers to atom positions in the OBFFCalculation objects
1134     }
1135 
1136     return true;
1137   }
1138 
ValidateGradientError(vector3 & numgrad,vector3 & anagrad)1139   vector3 OBForceField::ValidateGradientError(vector3 &numgrad, vector3 &anagrad)
1140   {
1141     double errx, erry, errz;
1142 
1143     if (fabs(numgrad.x()) < 1.0)
1144       errx = numgrad.x() * fabs(numgrad.x() - anagrad.x()) * 100;
1145     else
1146       errx = fabs((numgrad.x() - anagrad.x()) / numgrad.x()) * 100;
1147 
1148     if (fabs(numgrad.y()) < 1.0)
1149       erry = numgrad.y() * fabs(numgrad.y() - anagrad.y()) * 100;
1150     else
1151       erry = fabs((numgrad.y() - anagrad.y()) / numgrad.y()) * 100;
1152 
1153     if (fabs(numgrad.z()) < 1.0)
1154       errz = numgrad.z() * fabs(numgrad.z() - anagrad.z()) * 100;
1155     else
1156       errz = fabs((numgrad.z() - anagrad.z()) / numgrad.z()) * 100;
1157 
1158     errx = fabs(errx);
1159     erry = fabs(erry);
1160     errz = fabs(errz);
1161 
1162     return vector3(errx, erry, errz);
1163   }
1164 
1165   //////////////////////////////////////////////////////////////////////////////////
1166   //
1167   // Structure generation
1168   //
1169   //////////////////////////////////////////////////////////////////////////////////
1170 
SystematicRotorSearchInitialize(unsigned int geomSteps,bool sampleRingBonds)1171   int OBForceField::SystematicRotorSearchInitialize(unsigned int geomSteps, bool sampleRingBonds)
1172   {
1173     if (!_validSetup)
1174       return 0;
1175 
1176     OBRotorList rl;
1177     OBRotamerList rotamers;
1178     OBRotorIterator ri;
1179     OBRotor *rotor;
1180 
1181     _origLogLevel = _loglvl;
1182 
1183     OBBitVec fixed = _constraints.GetFixedBitVec();
1184     rl.SetFixAtoms(fixed);
1185     rl.Setup(_mol, sampleRingBonds);
1186     rotamers.SetBaseCoordinateSets(_mol);
1187     rotamers.Setup(_mol, rl);
1188 
1189     IF_OBFF_LOGLVL_LOW {
1190       OBFFLog("\nS Y S T E M A T I C   R O T O R   S E A R C H\n\n");
1191       snprintf(_logbuf, BUFF_SIZE, "  NUMBER OF ROTATABLE BONDS: %lu\n", (unsigned long)rl.Size());
1192       OBFFLog(_logbuf);
1193 
1194       unsigned long int combinations = 1;
1195       for (rotor = rl.BeginRotor(ri); rotor;
1196            rotor = rl.NextRotor(ri)) {
1197         combinations *= rotor->GetResolution().size();
1198       }
1199       snprintf(_logbuf, BUFF_SIZE, "  NUMBER OF POSSIBLE ROTAMERS: %lu\n", combinations);
1200       OBFFLog(_logbuf);
1201     }
1202 
1203     _current_conformer = 0;
1204 
1205     if (!rl.Size()) { // only one conformer
1206       IF_OBFF_LOGLVL_LOW
1207         OBFFLog("  GENERATED ONLY ONE CONFORMER\n\n");
1208 
1209       ConjugateGradients(geomSteps); // final energy minimizatin for best conformation
1210 
1211       return 1; // there are no more conformers
1212     }
1213 
1214     OBRotorKeys rotorKeys;
1215     rotor = rl.BeginRotor(ri);
1216     for (unsigned int i = 1; i < rl.Size() + 1; ++i, rotor = rl.NextRotor(ri)) // foreach rotor
1217       rotorKeys.AddRotor(rotor->GetResolution().size());
1218 
1219     rotamers.AddRotamer(rotorKeys.GetKey());
1220     while (rotorKeys.Next())
1221       rotamers.AddRotamer(rotorKeys.GetKey());
1222 
1223     rotamers.ExpandConformerList(_mol, _mol.GetConformers());
1224 
1225     IF_OBFF_LOGLVL_LOW {
1226       snprintf(_logbuf, BUFF_SIZE, "  GENERATED %d CONFORMERS\n\n", _mol.NumConformers());
1227       OBFFLog(_logbuf);
1228       OBFFLog("CONFORMER     ENERGY\n");
1229       OBFFLog("--------------------\n");
1230     }
1231 
1232     _energies.clear();
1233 
1234     return _mol.NumConformers();
1235   }
1236 
SystematicRotorSearchNextConformer(unsigned int geomSteps)1237   bool OBForceField::SystematicRotorSearchNextConformer(unsigned int geomSteps)
1238   {
1239     if (!_validSetup)
1240       return 0;
1241 
1242     if (_current_conformer >=  _mol.NumConformers()) { // done
1243       // Select conformer with lowest energy
1244       int best_conformer = 0;
1245       for (int i = 0; i < _mol.NumConformers(); i++) {
1246         if (_energies[i] < _energies[best_conformer])
1247           best_conformer = i;
1248       }
1249 
1250       IF_OBFF_LOGLVL_LOW {
1251         snprintf(_logbuf, BUFF_SIZE, "\n  CONFORMER %d HAS THE LOWEST ENERGY\n\n",  best_conformer + 1);
1252         OBFFLog(_logbuf);
1253       }
1254 
1255       _mol.SetConformer(best_conformer);
1256       SetupPointers(); // update pointers to atom positions in the OBFFCalculation objects
1257       _current_conformer = best_conformer;
1258 
1259       return false;
1260     }
1261 
1262     _mol.SetConformer(_current_conformer); // select conformer
1263     SetupPointers(); // update pointers to atom positions in the OBFFCalculation objects
1264 
1265     _loglvl = OBFF_LOGLVL_NONE;
1266     ConjugateGradients(geomSteps); // energy minimization for conformer
1267     _loglvl = _origLogLevel;
1268 
1269     _energies.push_back(Energy(false)); // calculate and store energy
1270 
1271     IF_OBFF_LOGLVL_LOW {
1272       snprintf(_logbuf, BUFF_SIZE, "   %3d   %20.3f\n", (_current_conformer + 1), _energies[_current_conformer]);
1273       OBFFLog(_logbuf);
1274     }
1275 
1276     _current_conformer++;
1277     return true;
1278   }
1279 
SystematicRotorSearch(unsigned int geomSteps,bool sampleRingBonds)1280   void OBForceField::SystematicRotorSearch(unsigned int geomSteps, bool sampleRingBonds)
1281   {
1282     if (SystematicRotorSearchInitialize(geomSteps, sampleRingBonds))
1283       while (SystematicRotorSearchNextConformer(geomSteps)) {}
1284   }
1285 
FastRotorSearch(bool permute)1286   int OBForceField::FastRotorSearch(bool permute)
1287   {
1288     if (_mol.NumRotors() == 0)
1289       return 0;
1290 
1291     int origLogLevel = _loglvl;
1292 
1293     // Remove all conformers (e.g. from previous conformer generators) except for current conformer
1294     double *initialCoord = new double [_mol.NumAtoms() * 3]; // initial state
1295     double *store_initial = new double [_mol.NumAtoms() * 3]; // store the initial state
1296     memcpy((char*)initialCoord,(char*)_mol.GetCoordinates(),sizeof(double)*3*_mol.NumAtoms());
1297     memcpy((char*)store_initial,(char*)_mol.GetCoordinates(),sizeof(double)*3*_mol.NumAtoms());
1298     std::vector<double *> newConfs(1, initialCoord);
1299     _mol.SetConformers(newConfs);
1300 
1301     _energies.clear(); // Wipe any energies from previous conformer generators
1302 
1303     OBRotorList rl;
1304     OBBitVec fixed = _constraints.GetFixedBitVec();
1305     rl.SetFixAtoms(fixed);
1306     rl.SetQuiet();
1307     rl.Setup(_mol);
1308 
1309     OBRotorIterator ri;
1310     OBRotamerList rotamerlist;
1311     rotamerlist.SetBaseCoordinateSets(_mol);
1312     rotamerlist.Setup(_mol, rl);
1313 
1314     // Start with all of the rotors in their 0 position
1315     // (perhaps instead I should set them randomly?)
1316     std::vector<int> init_rotorKey(rl.Size() + 1, 0);
1317     std::vector<int> rotorKey(init_rotorKey);
1318 
1319     unsigned int j, minj;
1320     double currentE, minE, best_minE;
1321 
1322     double *verybestconf = new double [_mol.NumAtoms() * 3]; // store the best conformer to date
1323     double *bestconf = new double [_mol.NumAtoms() * 3]; // store the best conformer to date in the current permutation
1324     double *minconf = new double [_mol.NumAtoms() * 3];  // store the best conformer for the current rotor
1325     memcpy((char*)bestconf,(char*)_mol.GetCoordinates(),sizeof(double)*3*_mol.NumAtoms());
1326 
1327     double energy_offset;
1328     // Can take shortcut later, as 4 components of the energy will be constant
1329     rotamerlist.SetCurrentCoordinates(_mol, rotorKey);
1330     SetupPointers();
1331     energy_offset = E_Bond(false) + E_Angle(false) + E_StrBnd(false) + E_OOP(false);
1332 
1333     // This function relies on the fact that Rotors are ordered from the most
1334     // central to the most peripheral (due to CompareRotors in rotor.cpp)
1335     std::vector<OBRotor *> vrotors;
1336     OBRotor *rotor;
1337     for (rotor = rl.BeginRotor(ri); rotor; rotor = rl.NextRotor(ri))
1338       vrotors.push_back(rotor);
1339 
1340     // The permutations are ordered so that the first 2 permutations cover the
1341     // combinations of 2, and the first 6 permutations cover the combinations of 3
1342     const char permutations[24*4] = {0,1,2,3, 1,0,2,3, 0,2,1,3, 1,2,0,3, 2,0,1,3, 2,1,0,3,
1343                                      0,1,3,2, 0,2,3,1, 0,3,1,2, 0,3,2,1, 1,0,3,2, 1,2,3,0,
1344                                      1,3,0,2, 1,3,2,0, 2,0,3,1, 2,1,3,0, 2,3,0,1, 2,3,1,0,
1345                                      3,0,1,2, 3,0,2,1, 3,1,0,2, 3,1,2,0, 3,2,0,1, 3,2,1,0};
1346     const char factorial[5] = {0, 1, 2, 6, 24};
1347 
1348     char num_rotors_to_permute, num_permutations;
1349     if (permute)
1350       num_rotors_to_permute = (char)std::min<size_t> (4, vrotors.size());
1351     else
1352       num_rotors_to_permute = 1; // i.e. just use the original order
1353     num_permutations = factorial[num_rotors_to_permute];
1354 
1355     // Initialize reordered_rotors - the order in which to test rotors
1356     std::vector<unsigned int> reordered_rotors(vrotors.size());
1357     for (int i=0; i<vrotors.size(); ++i)
1358       reordered_rotors[i] = i;
1359 
1360     std::set<unsigned int> seen;
1361     best_minE = DBL_MAX;
1362     for (int N=0; N<num_permutations; ++N) {
1363       for (int i=0; i<num_rotors_to_permute; ++i)
1364         reordered_rotors.at(i) = *(permutations + N*4 + i);
1365 
1366       rotorKey = init_rotorKey;
1367       _mol.SetCoordinates(store_initial);
1368       bool quit = false;
1369 
1370       for (int i=0; i<reordered_rotors.size(); ++i) {
1371         unsigned int idx = reordered_rotors[i];
1372         rotor = vrotors.at(idx);
1373 
1374         minE = DBL_MAX;
1375 
1376         for (j = 0; j < rotor->GetResolution().size(); j++) { // For each rotor position
1377           // Note: we could do slightly better by skipping the rotor position we already
1378           //       tested in the last loop (position 0 at the moment). Note that this
1379           //       isn't as simple as just changing the loop starting point to j = 1.
1380           _mol.SetCoordinates(bestconf);
1381           rotorKey[idx + 1] = j;
1382           rotamerlist.SetCurrentCoordinates(_mol, rotorKey);
1383           SetupPointers();
1384 
1385           currentE = E_VDW(false) + E_Torsion(false) + E_Electrostatic(false);
1386 
1387           if (currentE < minE) {
1388             minE = currentE;
1389             minj = j;
1390             memcpy((char*)minconf,(char*)_mol.GetCoordinates(),sizeof(double)*3*_mol.NumAtoms());
1391           }
1392         } // Finished testing all positions of this rotor
1393         rotorKey[idx + 1] = minj;
1394 
1395         if (i==4) { // Check whether this rotorKey has already been chosen
1396           // Create a hash of the rotorKeys (given that the max value of any rotorKey is 11 from torlib.txt)
1397           unsigned int hash = rotorKey[1] + rotorKey[2]*12 + rotorKey[3]*12*12 + rotorKey[4]*12*12*12;
1398 
1399           if (seen.find(hash) == seen.end()) // Not seen before
1400             seen.insert(hash);
1401           else { // Already seen - no point continuing
1402             quit = true;
1403             break;
1404           }
1405         }
1406 
1407         memcpy((char*)bestconf,(char*)minconf,sizeof(double)*3*_mol.NumAtoms());
1408       } // end of this permutation
1409       if (!quit) {
1410           if (minE < best_minE) {
1411             best_minE = minE;
1412             memcpy((char*)verybestconf,(char*)bestconf,sizeof(double)*3*_mol.NumAtoms());
1413           }
1414       }
1415 
1416     } // end of final permutation
1417 
1418     _mol.SetCoordinates(verybestconf);
1419     SetupPointers();
1420 
1421     delete [] store_initial;
1422     delete [] bestconf;
1423     delete [] verybestconf;
1424     delete [] minconf;
1425 
1426     return true;
1427   }
1428 
RandomRotorSearchInitialize(unsigned int conformers,unsigned int geomSteps,bool sampleRingBonds)1429   void OBForceField::RandomRotorSearchInitialize(unsigned int conformers, unsigned int geomSteps,
1430                                                  bool sampleRingBonds)
1431   {
1432     if (!_validSetup)
1433       return;
1434 
1435     OBRotorList rl;
1436     OBRotamerList rotamers;
1437     OBRotorIterator ri;
1438     OBRotor *rotor;
1439 
1440     OBRandom generator;
1441     generator.TimeSeed();
1442     _origLogLevel = _loglvl;
1443 
1444     if (_mol.GetCoordinates() == nullptr)
1445       return;
1446 
1447     OBBitVec fixed = _constraints.GetFixedBitVec();
1448     rl.SetFixAtoms(fixed);
1449     rl.Setup(_mol, sampleRingBonds);
1450     rotamers.SetBaseCoordinateSets(_mol);
1451     rotamers.Setup(_mol, rl);
1452 
1453     IF_OBFF_LOGLVL_LOW {
1454       OBFFLog("\nR A N D O M   R O T O R   S E A R C H\n\n");
1455       snprintf(_logbuf, BUFF_SIZE, "  NUMBER OF ROTATABLE BONDS: %lu\n", (unsigned long)rl.Size());
1456       OBFFLog(_logbuf);
1457 
1458       unsigned long int combinations = 1;
1459       for (rotor = rl.BeginRotor(ri); rotor;
1460            rotor = rl.NextRotor(ri)) {
1461         combinations *= rotor->GetResolution().size();
1462       }
1463       snprintf(_logbuf, BUFF_SIZE, "  NUMBER OF POSSIBLE ROTAMERS: %lu\n", combinations);
1464       OBFFLog(_logbuf);
1465     }
1466 
1467     _current_conformer = 0;
1468 
1469     if (!rl.Size()) { // only one conformer
1470       IF_OBFF_LOGLVL_LOW
1471         OBFFLog("  GENERATED ONLY ONE CONFORMER\n\n");
1472 
1473       _loglvl = OBFF_LOGLVL_NONE;
1474       ConjugateGradients(geomSteps); // energy minimization for conformer
1475       _loglvl = _origLogLevel;
1476 
1477       return;
1478     }
1479 
1480     std::vector<int> rotorKey(rl.Size() + 1, 0); // indexed from 1
1481 
1482     for (unsigned int c = 0; c < conformers; ++c) {
1483       rotor = rl.BeginRotor(ri);
1484       for (unsigned int i = 1; i < rl.Size() + 1; ++i, rotor = rl.NextRotor(ri)) {
1485         // foreach rotor
1486         rotorKey[i] = generator.NextInt() % rotor->GetResolution().size();
1487       }
1488       rotamers.AddRotamer(rotorKey);
1489     }
1490 
1491     rotamers.ExpandConformerList(_mol, _mol.GetConformers());
1492 
1493     IF_OBFF_LOGLVL_LOW {
1494       snprintf(_logbuf, BUFF_SIZE, "  GENERATED %d CONFORMERS\n\n", _mol.NumConformers());
1495       OBFFLog(_logbuf);
1496       OBFFLog("CONFORMER     ENERGY\n");
1497       OBFFLog("--------------------\n");
1498     }
1499 
1500     _energies.clear();
1501   }
1502 
RandomRotorSearchNextConformer(unsigned int geomSteps)1503   bool OBForceField::RandomRotorSearchNextConformer(unsigned int geomSteps)
1504   {
1505     if (!_validSetup)
1506       return 0;
1507 
1508     if (_current_conformer >=  _mol.NumConformers()) { // done
1509       // Select conformer with lowest energy
1510       int best_conformer = 0;
1511       for (int i = 0; i < _mol.NumConformers(); i++) {
1512         if (_energies[i] < _energies[best_conformer])
1513           best_conformer = i;
1514       }
1515 
1516       IF_OBFF_LOGLVL_LOW {
1517         snprintf(_logbuf, BUFF_SIZE, "\n  CONFORMER %d HAS THE LOWEST ENERGY\n\n",  best_conformer + 1);
1518         OBFFLog(_logbuf);
1519       }
1520 
1521       _mol.SetConformer(best_conformer);
1522       SetupPointers(); // update pointers to atom positions in the OBFFCalculation objects
1523       _current_conformer = best_conformer;
1524 
1525       return false;
1526     }
1527 
1528     _mol.SetConformer(_current_conformer); // select conformer
1529     SetupPointers(); // update pointers to atom positions in the OBFFCalculation objects
1530 
1531     _loglvl = OBFF_LOGLVL_NONE;
1532     ConjugateGradients(geomSteps); // energy minimization for conformer
1533     _loglvl = _origLogLevel;
1534 
1535     _energies.push_back(Energy(false)); // calculate and store energy
1536 
1537     IF_OBFF_LOGLVL_LOW {
1538       snprintf(_logbuf, BUFF_SIZE, "   %3d      %8.3f\n", (_current_conformer + 1), _energies[_current_conformer]);
1539       OBFFLog(_logbuf);
1540     }
1541 
1542     _current_conformer++;
1543     return true;
1544   }
1545 
RandomRotorSearch(unsigned int conformers,unsigned int geomSteps,bool sampleRingBonds)1546   void OBForceField::RandomRotorSearch(unsigned int conformers, unsigned int geomSteps,
1547                                        bool sampleRingBonds)
1548   {
1549     RandomRotorSearchInitialize(conformers, geomSteps, sampleRingBonds);
1550     while (RandomRotorSearchNextConformer(geomSteps)) {}
1551   }
1552 
Reweight(std::vector<std::vector<double>> & rotorWeights,std::vector<int> rotorKey,double bonus)1553   void Reweight(std::vector< std::vector <double> > &rotorWeights,
1554                 std::vector<int> rotorKey, double bonus)
1555   {
1556     double fraction, minWeight, maxWeight;
1557     bool improve = (bonus > 0.0);
1558 
1559     for (unsigned int i = 1; i < rotorWeights.size() - 1; ++i) {
1560 			if (rotorKey[i] == -1)
1561 				continue; // don't rotate
1562 
1563       if (improve && rotorWeights[i][rotorKey[i]] > 0.999 - bonus)
1564         continue;
1565       if (!improve && rotorWeights[i][rotorKey[i]] < 0.001 - bonus) // bonus < 0
1566         continue;
1567 
1568       // Check to make sure we don't kill some poor weight
1569       minWeight = maxWeight = rotorWeights[i][0];
1570       for (unsigned int j = 1; j < rotorWeights[i].size(); ++j) {
1571         if (j == rotorKey[i])
1572           continue; // we already checked for problems with this entry
1573         if (rotorWeights[i][j] < minWeight)
1574           minWeight = rotorWeights[i][j];
1575         if (rotorWeights[i][j] > maxWeight)
1576           maxWeight = rotorWeights[i][j];
1577       }
1578 
1579       fraction = bonus / (rotorWeights[i].size() - 1);
1580       if (improve && fraction > minWeight) {
1581         bonus = minWeight / 2.0;
1582         fraction = bonus / (rotorWeights[i].size() - 1);
1583       }
1584       if (!improve && fraction > maxWeight) {
1585         bonus = (maxWeight - 1.0) / 2.0; // negative "bonus"
1586         fraction = bonus / (rotorWeights[i].size() - 1);
1587       }
1588 
1589       for (unsigned int j = 0; j < rotorWeights[i].size(); ++j) {
1590         if (j == rotorKey[i])
1591           rotorWeights[i][j] += bonus;
1592         else
1593           rotorWeights[i][j] -= fraction;
1594       }
1595     }
1596   }
1597 
1598 
WeightedRotorSearch(unsigned int conformers,unsigned int geomSteps,bool sampleRingBonds)1599   void OBForceField::WeightedRotorSearch(unsigned int conformers, unsigned int geomSteps,
1600                                          bool sampleRingBonds)
1601   {
1602     if (!_validSetup)
1603       return;
1604 
1605     OBRotorList rl;
1606     OBRotamerList rotamers;
1607     OBRotorIterator ri;
1608     OBRotor *rotor;
1609 
1610     OBRandom generator;
1611     generator.TimeSeed();
1612     int origLogLevel = _loglvl;
1613 
1614     if (_mol.GetCoordinates() == nullptr)
1615       return;
1616 
1617     double *initialCoord = new double [_mol.NumAtoms() * 3]; // initial state
1618     memcpy((double*)initialCoord,(double*)_mol.GetCoordinates(),sizeof(double)*3*_mol.NumAtoms());
1619     vector<double *> newConfs(1, initialCoord);
1620     _mol.SetConformers(newConfs);
1621     _current_conformer = 0;
1622     _mol.SetConformer(_current_conformer);
1623     SetupPointers();
1624 
1625     OBBitVec fixed = _constraints.GetFixedBitVec();
1626     rl.SetFixAtoms(fixed);
1627     rl.Setup(_mol, sampleRingBonds);
1628     rotamers.SetBaseCoordinateSets(_mol);
1629     rotamers.Setup(_mol, rl);
1630 
1631     IF_OBFF_LOGLVL_LOW {
1632       OBFFLog("\nW E I G H T E D   R O T O R   S E A R C H\n\n");
1633       snprintf(_logbuf, BUFF_SIZE, "  NUMBER OF ROTATABLE BONDS: %lu\n", (unsigned long)rl.Size());
1634       OBFFLog(_logbuf);
1635 
1636       unsigned long int combinations = 1;
1637       for (rotor = rl.BeginRotor(ri); rotor;
1638            rotor = rl.NextRotor(ri)) {
1639         combinations *= rotor->GetResolution().size();
1640       }
1641       snprintf(_logbuf, BUFF_SIZE, "  NUMBER OF POSSIBLE ROTAMERS: %lu\n", combinations);
1642       OBFFLog(_logbuf);
1643     }
1644 
1645 
1646     _energies.clear(); // Wipe any energies from previous conformer generators
1647 
1648     if (!rl.Size()) { // only one conformer
1649       IF_OBFF_LOGLVL_LOW
1650         OBFFLog("  GENERATED ONLY ONE CONFORMER\n\n");
1651 
1652       _loglvl = OBFF_LOGLVL_NONE;
1653       ConjugateGradients(geomSteps); // energy minimization for conformer
1654       _loglvl = origLogLevel;
1655       _energies.push_back(Energy(false));
1656 
1657       return;
1658     }
1659 
1660     _energies.push_back(Energy(false)); // Store the energy of the original conf
1661 
1662     // key for generating particular conformers
1663     std::vector<int> rotorKey(rl.Size() + 1, 0); // indexed from 1
1664 
1665     // initialize the weights
1666     std::vector< std::vector <double> > rotorWeights;
1667     std::vector< double > weightSet;
1668     rotorWeights.push_back(weightSet); // empty set for unused index 0
1669 
1670     double weight;
1671     double bestE, worstE, currentE;
1672     std::vector<double> energies;
1673     // First off, we test out each rotor position. How good (or bad) is it?
1674     // This lets us pre-weight the search to a useful level
1675     // So each rotor is considered in isolation
1676     IF_OBFF_LOGLVL_LOW
1677       OBFFLog("  INITIAL WEIGHTING OF ROTAMERS...\n\n");
1678 
1679     rotor = rl.BeginRotor(ri);
1680     for (unsigned int i = 1; i < rl.Size() + 1; ++i, rotor = rl.NextRotor(ri)) {
1681       rotorKey[i] = -1; // no rotation (new in 2.2)
1682     }
1683 
1684     rotor = rl.BeginRotor(ri);
1685 
1686     for (unsigned int i = 1; i < rl.Size() + 1; ++i, rotor = rl.NextRotor(ri)) {
1687       // foreach rotor
1688       energies.clear();
1689       for (unsigned int j = 0; j < rotor->GetResolution().size(); j++) {
1690         // foreach rotor position
1691         _mol.SetCoordinates(initialCoord);
1692         rotorKey[i] = j;
1693         rotamers.SetCurrentCoordinates(_mol, rotorKey);
1694         SetupPointers(); // update pointers to atom positions in the OBFFCalculation objects
1695 
1696         _loglvl = OBFF_LOGLVL_NONE;
1697         ConjugateGradients(geomSteps); // energy minimization for conformer
1698         _loglvl = origLogLevel;
1699         currentE = Energy(false);
1700 
1701         if (j == 0)
1702           bestE = worstE = currentE;
1703         else {
1704           if (currentE > worstE)
1705             worstE = currentE;
1706           else if (currentE < bestE)
1707             bestE = currentE;
1708         }
1709         energies.push_back(currentE);
1710       }
1711       rotorKey[i] = -1; // back to the previous setting before we go to another rotor
1712 
1713       weightSet.clear();
1714       // first loop through and calculate the relative populations from Boltzmann
1715       double totalPop = 0.0;
1716       for (unsigned int j = 0; j < rotor->GetResolution().size(); j++) {
1717         currentE = energies[j];
1718         // add back the Boltzmann population for these relative energies at 300K (assuming kJ/mol)
1719         energies[j] = exp(-1.0*fabs(currentE - bestE) / 2.5);
1720         totalPop += energies[j];
1721       }
1722       // now set the weights
1723       for (unsigned int j = 0; j < rotor->GetResolution().size(); j++) {
1724         if (IsNear(worstE, bestE, 1.0e-3))
1725           weight = 1 / rotor->GetResolution().size();
1726         else
1727           weight = energies[j]/totalPop;
1728         weightSet.push_back(weight);
1729       }
1730       rotorWeights.push_back(weightSet);
1731     }
1732 
1733     int best_conformer=-1;
1734     //    double penalty; // for poor performance
1735     double randFloat; // generated random number -- used to pick a rotor
1736     double total; // used to calculate the total probability
1737 
1738     // Start with the current coordinates
1739     bestE = worstE = Energy(false);
1740 
1741     // Now we actually test some weightings
1742     IF_OBFF_LOGLVL_LOW {
1743       snprintf(_logbuf, BUFF_SIZE, "  GENERATED %d CONFORMERS\n\n", conformers);
1744       OBFFLog(_logbuf);
1745       OBFFLog("CONFORMER     ENERGY\n");
1746       OBFFLog("--------------------\n");
1747     }
1748 
1749     double defaultRotor = 1.0/sqrt((double)rl.Size());
1750     unsigned c = 0;
1751     while (c < conformers) {
1752       _mol.SetCoordinates(initialCoord);
1753 
1754       // Choose the rotor key based on current weightings
1755       rotor = rl.BeginRotor(ri);
1756       for (unsigned int i = 1; i < rl.Size() + 1; ++i, rotor = rl.NextRotor(ri)) {
1757         // foreach rotor
1758         rotorKey[i] = -1; // default = don't change dihedral
1759         randFloat = generator.NextFloat();
1760         if (randFloat < defaultRotor) // should we just leave this rotor with default setting?
1761           continue;
1762 
1763         randFloat = generator.NextFloat();
1764         total = 0.0;
1765         for (unsigned int j = 0; j < rotor->GetResolution().size(); j++) {
1766           if (randFloat > total && randFloat < (total+ rotorWeights[i][j])) {
1767             rotorKey[i] = j;
1768             break;
1769           }
1770           else
1771             total += rotorWeights[i][j];
1772         }
1773       }
1774 
1775       //FIXME: for now, allow even invalid ring conformers
1776       rotamers.SetCurrentCoordinates(_mol, rotorKey);
1777       ++c;
1778 
1779       SetupPointers(); // update pointers to atom positions in the OBFFCalculation objects
1780 
1781       _loglvl = OBFF_LOGLVL_NONE;
1782       ConjugateGradients(geomSteps); // energy minimization for conformer
1783       _loglvl = origLogLevel;
1784       currentE = Energy(false);
1785       _energies.push_back(currentE);
1786       double *confCoord = new double [_mol.NumAtoms() * 3]; // initial state
1787       memcpy((char*)confCoord,(char*)_mol.GetCoordinates(),sizeof(double)*3*_mol.NumAtoms());
1788       _mol.AddConformer(confCoord);
1789 
1790       IF_OBFF_LOGLVL_LOW {
1791         snprintf(_logbuf, BUFF_SIZE, "   %3d      %8.3f\n", c + 1, currentE);
1792         OBFFLog(_logbuf);
1793       }
1794 
1795       if (!isfinite(currentE))
1796         continue;
1797 
1798       if (currentE < bestE) {
1799         bestE = currentE;
1800         best_conformer = c;
1801 
1802         // improve this rotorKey
1803         Reweight(rotorWeights, rotorKey, +0.11);
1804       } else if (currentE > worstE) { // horrible!
1805         worstE = currentE;
1806 
1807         // penalize this rotorKey
1808         Reweight(rotorWeights, rotorKey, -0.11);
1809       } else {
1810         double slope = -0.2 / (worstE - bestE);
1811         Reweight(rotorWeights, rotorKey, (currentE - bestE)*slope);
1812       }
1813     }
1814 
1815     IF_OBFF_LOGLVL_LOW {
1816       snprintf(_logbuf, BUFF_SIZE, "\n  LOWEST ENERGY: %8.3f\n\n",
1817                bestE);
1818       OBFFLog(_logbuf);
1819     }
1820 
1821     // debugging output to see final weightings of each rotor setting
1822     IF_OBFF_LOGLVL_HIGH {
1823       OBFFLog("Final Weights: \n");
1824       for (unsigned int i = 1; i < rotorWeights.size() - 1; ++i) {
1825         snprintf(_logbuf, BUFF_SIZE, " Weight: %d", i);
1826         OBFFLog(_logbuf);
1827         for (unsigned int j = 0; j < rotorWeights[i].size(); ++j) {
1828           snprintf(_logbuf, BUFF_SIZE, " %8.3f", rotorWeights[i][j]);
1829           OBFFLog(_logbuf);
1830         }
1831         OBFFLog("\n");
1832       }
1833     }
1834 
1835     _current_conformer = best_conformer; // Initial coords are stored in _vconf[0]
1836     _mol.SetConformer(_current_conformer);
1837     SetupPointers(); // update pointers to atom positions in the OBFFCalculation objects
1838   }
1839 
DistanceGeometry()1840   void OBForceField::DistanceGeometry()
1841   {
1842     if (!_validSetup)
1843       return;
1844 
1845     int N = _mol.NumAtoms();
1846     int i = 0;
1847     int j = 0;
1848     //double matrix[N][N], G[N][N];
1849     vector<vector<double> > matrix(N);
1850     for(int i=0; i<N; i++)
1851       matrix[i].reserve(N);
1852     vector<vector<double> > G(N);
1853     for(int i=0; i<N; i++)
1854       G[i].reserve(N);
1855 
1856     bool is15;
1857 
1858     IF_OBFF_LOGLVL_LOW
1859       OBFFLog("\nD I S T A N C E   G E O M E T R Y\n\n");
1860 
1861     // Calculate initial distance matrix
1862     //
1863     // - diagonal elements are 0.0
1864     // - when atoms i and j form a bond, the bond length is used for
1865     //   upper and lower limit
1866     // - when atoms i and j are in a 1-3 relationship, the distance
1867     //   is calculated using the cosine rule: (upper limit = lower limit)
1868     //
1869     //          b_         ab: bond length
1870     //         /  \_       bc: bond length
1871     //        /A    \_     ac = sqrt(ab^2 + bc^2 - 2*ab*bc*cos(A))
1872     //       a--------c
1873     //
1874     // - when atoms i anf j are in a 1-4 relationship, the lower distance
1875     //   limit is calculated using a torsional angle of 0.0. The upper limit
1876     //   is calculated using a torsion angle of 180.0.
1877     //
1878     //      a       d      ab, bc, cd: bond lengths
1879     //       \ B C /
1880     //        b---c        ad = bc + ab*cos(180-B) + cd*cos(180-C)
1881     //
1882     //      a
1883     //       \ B           delta_x = bc + ab*cos(180-B) + cd*cos(180-C)
1884     //        b---c        delta_y = ab*sin(180-B) + cd*sin(180-C)
1885     //           C \       .
1886     //              d      ad = sqrt(delta_x^2 + delta_y^2)
1887     //
1888     FOR_ATOMS_OF_MOL (a, _mol) {
1889       i = a->GetIdx() - 1;
1890       FOR_ATOMS_OF_MOL (b, _mol) {
1891         j = b->GetIdx() - 1;
1892 
1893         if (&*a == &*b) {
1894           matrix[i][j] = 0.0; // diagonal
1895           continue;
1896         }
1897         // Find relationship
1898         is15 = true;
1899         FOR_NBORS_OF_ATOM (nbr1, _mol.GetAtom(a->GetIdx())) { // 1-2
1900           if (&*nbr1 == &*b) {
1901             matrix[i][j] = 1.3;
1902             break;
1903           }
1904           FOR_NBORS_OF_ATOM (nbr2, _mol.GetAtom(nbr1->GetIdx())) { // 1-3
1905             if (&*nbr2 == &*b) {
1906               matrix[i][j] = sqrt(1.3*1.3 + 1.3*1.3 - 2.0 *
1907                                   cos(DEG_TO_RAD*120.0) * 1.3*1.3 );
1908               is15 = false;
1909               break;
1910             }
1911             FOR_NBORS_OF_ATOM (nbr3, &*nbr2) { // 1-4
1912               if (&*nbr3 == &*b) {
1913                 is15 = false;
1914                 if (i > j) // minimum distance (torsion angle = 0)
1915                   matrix[i][j] = 1.3 + 1.3*cos(DEG_TO_RAD*(180.0 - 120.0))
1916                     + 1.3*cos(DEG_TO_RAD*(180.0 - 120.0));
1917                 else {// maximum distance (torsion angle = 180)
1918                   double delta_x, delta_y;
1919                   delta_x = 1.3 + 1.3*cos(DEG_TO_RAD*(180.0-120.0))
1920                     + 1.3*cos(DEG_TO_RAD*(180.0-120.0));
1921                   delta_y = 1.3*sin(DEG_TO_RAD*(180.0-120.0)) + 1.3*sin(DEG_TO_RAD*(180.0-120.0));
1922                   matrix[i][j] = sqrt(delta_x*delta_x + delta_y*delta_y);
1923                 }
1924                 break;
1925               }
1926               if (i > j && is15) {// minimum distance (sum vdw radii)
1927                 matrix[i][j] = 1.4 + 1.4;
1928               } else if (is15) // maximum distance (torsion angle = 180)
1929                 matrix[i][j] = 99.0;
1930             }
1931           }
1932         }
1933       }
1934     }
1935 
1936     // output initial distance matrix
1937     IF_OBFF_LOGLVL_LOW {
1938 
1939       OBFFLog("INITIAL DISTANCE MATRIX\n\n");
1940       for (i=0; i<N; i++) {
1941         OBFFLog("[");
1942         for (j=0; j<N; j++) {
1943           snprintf(_logbuf, BUFF_SIZE, " %8.4f ", matrix[i][j]);
1944           OBFFLog(_logbuf);
1945         }
1946         OBFFLog("]\n");
1947       }
1948       OBFFLog("\n");
1949     }
1950 
1951     // Triangle smoothing
1952     //FOR_ANGLES_OF_MOL(angle, _mol) {
1953     int a, b, c;
1954     bool self_consistent = false;
1955     while (!self_consistent) {
1956       self_consistent = true;
1957 
1958       FOR_ATOMS_OF_MOL (_a, _mol) {
1959         a = _a->GetIdx() - 1;
1960         FOR_ATOMS_OF_MOL (_b, _mol) {
1961           if (&*_b == &*_a)
1962             continue;
1963           b = _b->GetIdx() - 1;
1964           FOR_ATOMS_OF_MOL (_c, _mol) {
1965             if ((&*_c == &*_b) || (&*_c == &*_a))
1966               continue;
1967             c = _c->GetIdx() - 1;
1968 
1969             double u_ab, u_bc, u_ac; // upper limits
1970             double l_ab, l_bc, l_ac; // lower limits
1971 
1972             // get the upper and lower limits for ab, bc and ac
1973             if (b > a) {
1974               u_ab = matrix[a][b];
1975               l_ab = matrix[b][a];
1976             } else {
1977               u_ab = matrix[b][a];
1978               l_ab = matrix[a][b];
1979             }
1980             if (c > b) {
1981               u_bc = matrix[b][c];
1982               l_bc = matrix[c][b];
1983             } else {
1984               u_bc = matrix[c][b];
1985               l_bc = matrix[b][c];
1986             }
1987             if (c > a) {
1988               u_ac = matrix[a][c];
1989               l_ac = matrix[c][a];
1990             } else {
1991               u_ac = matrix[c][a];
1992               l_ac = matrix[a][c];
1993             }
1994 
1995             if (u_ac > (u_ab + u_bc)) { // u_ac <= u_ab + u_bc
1996               u_ac = u_ab + u_bc;
1997               self_consistent = false;
1998             }
1999 
2000             if (l_ac < (l_ab - l_bc)) {// l_ac >= l_ab - l_bc
2001               l_ac = l_ab - l_bc;
2002       	      self_consistent = false;
2003             }
2004 
2005             // store smoothed l_ac and u_ac
2006             if (c > a) {
2007               matrix[a][c] = u_ac;
2008               matrix[c][a] = l_ac;
2009             } else {
2010               matrix[c][a] = u_ac;
2011               matrix[a][c] = l_ac;
2012             }
2013           }
2014         }
2015       }
2016     }
2017 
2018     // output result of triangle smoothing
2019     IF_OBFF_LOGLVL_LOW {
2020 
2021       OBFFLog("TRIANGLE SMOOTHING\n\n");
2022       for (i=0; i<N; i++) {
2023         OBFFLog("[");
2024         for (j=0; j<N; j++) {
2025           snprintf(_logbuf, BUFF_SIZE, " %8.4f ", matrix[i][j]);
2026           OBFFLog(_logbuf);
2027         }
2028         OBFFLog("]\n");
2029       }
2030       OBFFLog("\n");
2031     }
2032 
2033     // Generate random distance matrix between lower and upper limits
2034     FOR_ATOMS_OF_MOL (a, _mol) {
2035       i = a->GetIdx() - 1;
2036       FOR_ATOMS_OF_MOL (b, _mol) {
2037         j = b->GetIdx() - 1;
2038 
2039         if (&*a == &*b) {
2040           matrix[i][j] = 0.0; // diagonal
2041           continue;
2042         }
2043 
2044         srand(static_cast<unsigned int>(time(nullptr)));
2045         double rand_ab, u_ab, l_ab;
2046         if (j > i) {
2047           u_ab = matrix[i][j];
2048           l_ab = matrix[j][i];
2049           rand_ab = l_ab + (u_ab - l_ab) * rand()/RAND_MAX;
2050           matrix[i][j] = rand_ab;
2051           matrix[j][i] = rand_ab;
2052         } else {
2053           u_ab = matrix[j][i];
2054           l_ab = matrix[i][j];
2055           rand_ab = l_ab + (u_ab - l_ab) * rand()/RAND_MAX;
2056           matrix[i][j] = rand_ab;
2057           matrix[j][i] = rand_ab;
2058         }
2059       }
2060     }
2061 
2062     // output result of triangle smoothing
2063     IF_OBFF_LOGLVL_LOW {
2064       OBFFLog("RANDOM DISTANCE MATRIX BETWEEN LIMITS\n\n");
2065       for (i=0; i<N; i++) {
2066         OBFFLog("[");
2067         for (j=0; j<N; j++) {
2068           snprintf(_logbuf, BUFF_SIZE, " %8.4f ", matrix[i][j]);
2069           OBFFLog(_logbuf);
2070         }
2071         OBFFLog("]\n");
2072       }
2073       OBFFLog("\n");
2074     }
2075 
2076     // Generate metric matrix
2077     // (origin = first atom )
2078     for (i=0; i<N; i++) {
2079       for (j=0; j<N; j++) {
2080         G[i][j] = 0.5 * (matrix[0][i]*matrix[0][i] + matrix[0][j]*matrix[0][j] - matrix[i][j]*matrix[i][j]);
2081       }
2082     }
2083 
2084     // output metric matrix
2085     IF_OBFF_LOGLVL_LOW {
2086       OBFFLog("METRIC MATRIX\n\n");
2087       for (i=0; i<N; i++) {
2088         OBFFLog("[");
2089         for (j=0; j<N; j++) {
2090           snprintf(_logbuf, BUFF_SIZE, " %8.4f ", G[i][j]);
2091           OBFFLog(_logbuf);
2092         }
2093         OBFFLog("]\n");
2094       }
2095       OBFFLog("\n");
2096     }
2097 
2098     // Calculate eigenvalues and eigenvectors
2099     //double eigenvalues[N];
2100     vector<double> eigenvalues(N);
2101     //double eigenvectors[N][N];
2102     vector<vector<double> > eigenvectors(N);
2103     for(int i=0; i<N; i++)
2104       eigenvectors[i].reserve(N);
2105 
2106     matrix3x3::jacobi(N, (double *) &G, (double *) &eigenvalues, (double *) &eigenvectors);
2107 
2108     // output eigenvalues and eigenvectors
2109     IF_OBFF_LOGLVL_LOW {
2110 
2111       OBFFLog("EIGENVALUES OF METRIC MATRIX\n\n");
2112       for (i=0; i<N; i++) {
2113         snprintf(_logbuf, BUFF_SIZE, "%8.4f ", eigenvalues[i]);
2114         OBFFLog(_logbuf);
2115       }
2116       OBFFLog("\n");
2117 
2118       OBFFLog("EIGENVECTORS OF METRIC MATRIX\n\n");
2119       for (i=0; i<N; i++) {
2120         OBFFLog("[");
2121         for (j=0; j<N; j++) {
2122           snprintf(_logbuf, BUFF_SIZE, " %8.4f ", eigenvectors[i][j]);
2123           OBFFLog(_logbuf);
2124         }
2125         OBFFLog("]\n");
2126       }
2127       OBFFLog("\n");
2128     }
2129 
2130     // Assign coordinates
2131     double xa, ya, za;
2132     FOR_ATOMS_OF_MOL (a, _mol) {
2133       i = a->GetIdx() - 1;
2134 
2135       if (eigenvectors[i][N-1] > 0)
2136         xa = sqrt(eigenvalues[N-1] * eigenvectors[i][N-1]);
2137       else
2138         xa = -sqrt(eigenvalues[N-1] * -eigenvectors[i][N-1]);
2139 
2140       if (eigenvectors[i][N-2] > 0)
2141         ya = sqrt(eigenvalues[N-2] * eigenvectors[i][N-2]);
2142       else
2143         ya = -sqrt(eigenvalues[N-2] * -eigenvectors[i][N-2]);
2144 
2145       if (eigenvectors[i][N-3] > 0)
2146         za = sqrt(eigenvalues[N-3] * eigenvectors[i][N-3]);
2147       else
2148         za = -sqrt(eigenvalues[N-3] * -eigenvectors[i][N-3]);
2149 
2150       a->SetVector(xa, ya, za);
2151     }
2152 
2153   }
2154 
2155   //////////////////////////////////////////////////////////////////////////////////
2156   //
2157   // Cut-off
2158   //
2159   //////////////////////////////////////////////////////////////////////////////////
2160 
UpdatePairsSimple()2161   void OBForceField::UpdatePairsSimple()
2162   {
2163     _vdwpairs.Clear();
2164     _elepairs.Clear();
2165 
2166     const unsigned int numAtoms = _mol.NumAtoms();
2167     const unsigned int numPairs = numAtoms * (numAtoms - 1) / 2;
2168     _vdwpairs.Resize(numPairs);
2169     _elepairs.Resize(numPairs);
2170 
2171     //! \todo set the criteria as squared values
2172     //  from what the user supplies
2173     double rvdwSquared = SQUARE(_rvdw);
2174     double releSquared = SQUARE(_rele);
2175 
2176     unsigned int pairIndex = -1;
2177     FOR_PAIRS_OF_MOL(p, _mol) {
2178       ++pairIndex;
2179 
2180       OBAtom *a = _mol.GetAtom((*p)[0]);
2181       OBAtom *b = _mol.GetAtom((*p)[1]);
2182 
2183       // Check whether or not this interaction is included
2184       if (HasGroups()) {
2185         bool isIncludedPair = false;
2186         for (size_t i=0; i < _interGroup.size(); ++i) {
2187           if (_interGroup[i].BitIsSet(a->GetIdx()) &&
2188               _interGroup[i].BitIsSet(b->GetIdx())) {
2189             isIncludedPair = true;
2190             break;
2191           }
2192         }
2193         if (!isIncludedPair) {
2194           for (size_t i=0; i < _interGroups.size(); ++i) {
2195             if (_interGroups[i].first.BitIsSet(a->GetIdx()) &&
2196                 _interGroups[i].second.BitIsSet(b->GetIdx())) {
2197               isIncludedPair = true;
2198               break;
2199             }
2200             if (_interGroups[i].first.BitIsSet(b->GetIdx()) &&
2201                 _interGroups[i].second.BitIsSet(a->GetIdx())) {
2202               isIncludedPair = true;
2203               break;
2204             }
2205           }
2206         }
2207         if (!isIncludedPair) {
2208           continue;
2209         }
2210       }
2211 
2212       // Get the distance squared btwn a and b
2213       // Not currently a method and this can be vectorized
2214       double ab[3];
2215       VectorSubtract(a->GetCoordinate(), b->GetCoordinate(), ab);
2216       double rabSq = 0.0;
2217       for (int j = 0; j < 3; ++j) {
2218         rabSq += SQUARE(ab[j]);
2219       }
2220 
2221       // update vdw pairs
2222       if (rabSq < rvdwSquared) {
2223         _vdwpairs.SetBitOn(pairIndex);
2224       } else {
2225         _vdwpairs.SetBitOff(pairIndex);
2226       }
2227       // update electrostatic pairs
2228       if (rabSq < releSquared) {
2229         _elepairs.SetBitOn(pairIndex);
2230       } else {
2231         _elepairs.SetBitOff(pairIndex);
2232       }
2233     }
2234 
2235     /*
2236       IF_OBFF_LOGLVL_LOW {
2237       snprintf(_logbuf, BUFF_SIZE, "UPDATE VDW PAIRS: %d --> %d (VDW), %d (ELE) \n", i+1,
2238       _vdwpairs.CountBits(), _elepairs.CountBits());
2239       OBFFLog(_logbuf);
2240       }
2241     */
2242   }
2243 
GetNumPairs()2244   unsigned int OBForceField::GetNumPairs()
2245   {
2246     unsigned int i = 1;
2247     FOR_PAIRS_OF_MOL(p, _mol)
2248       i++;
2249 
2250     return i;
2251   }
2252 
GetNumElectrostaticPairs()2253   unsigned int OBForceField::GetNumElectrostaticPairs()
2254   {
2255     return _elepairs.CountBits();
2256   }
2257 
GetNumVDWPairs()2258   unsigned int OBForceField::GetNumVDWPairs()
2259   {
2260     return _vdwpairs.CountBits();
2261   }
2262 
2263   //////////////////////////////////////////////////////////////////////////////////
2264   //
2265   // Interaction groups
2266   //
2267   //////////////////////////////////////////////////////////////////////////////////
2268 
2269 
AddIntraGroup(OBBitVec & group)2270   void OBForceField::AddIntraGroup(OBBitVec &group)
2271   {
2272     _intraGroup.push_back(group);
2273   }
2274 
AddInterGroup(OBBitVec & group)2275   void OBForceField::AddInterGroup(OBBitVec &group)
2276   {
2277     _interGroup.push_back(group);
2278   }
2279 
AddInterGroups(OBBitVec & group1,OBBitVec & group2)2280   void OBForceField::AddInterGroups(OBBitVec &group1, OBBitVec &group2)
2281   {
2282     pair<OBBitVec,OBBitVec> groups;
2283     groups.first = group1;
2284     groups.second = group2;
2285     _interGroups.push_back(groups);
2286   }
2287 
ClearGroups()2288   void OBForceField::ClearGroups()
2289   {
2290     _intraGroup.clear();
2291     _interGroup.clear();
2292     _interGroups.clear();
2293   }
2294 
HasGroups()2295   bool OBForceField::HasGroups()
2296   {
2297     if (!_intraGroup.empty())
2298       return true;
2299 
2300     if (!_interGroup.empty())
2301       return true;
2302 
2303     if (!_interGroups.empty())
2304       return true;
2305 
2306     return false;
2307   }
2308 
2309   //////////////////////////////////////////////////////////////////////////////////
2310   //
2311   // Energy Minimization
2312   //
2313   //////////////////////////////////////////////////////////////////////////////////
2314 
2315   // LineSearch
2316   //
2317   // atom: coordinates of atom at iteration k (x_k)
2318   // direction: search direction ( d = -grad(x_0) )
2319   //
2320   // ALGORITHM:
2321   //
2322   // step = 1
2323   // for (i = 1 to 100) {                max steps = 100
2324   //   e_k = energy(x_k)                 energy of current iteration
2325   //   x_k = x_k + step * d              update coordinates
2326   //   e_k+1 = energy(x_k+1)             energy of next iteration
2327   //
2328   //   if (e_k+1 < e_k)
2329   //     step = step * 1.2               increase step size
2330   //   if (e_k+1 > e_k) {
2331   //     x_k = x_k - step * d            reset coordinates to previous iteration
2332   //     step = step * 0.5               reduce step size
2333   //   }
2334   //   if (e_k+1 == e_k)
2335   //     end                             convergence criteria reached, stop
2336   // }
LineSearch(OBAtom * atom,vector3 & direction)2337   vector3 OBForceField::LineSearch(OBAtom *atom, vector3 &direction)
2338   {
2339     double e_n1, e_n2, step;
2340     vector3 old_xyz, orig_xyz, xyz_k, dir(0.0, 0.0, 0.0);
2341 
2342     // This needs several enhancements
2343     // 1) Switch to smarter line search method (e.g., Newton's method in 1D)
2344     //  x(n+1) = x(n) - F(x) / F'(x)   -- can be done numerically
2345     // 2) Switch to line search for one step of the entire molecule!
2346     //   This dramatically cuts down on the number of Energy() calls.
2347     //  (and is more correct anyway)
2348 
2349     // See the following implementation
2350 
2351     step = 0.2;
2352     direction.normalize();
2353     orig_xyz = atom->GetVector();
2354 
2355     e_n1 = Energy(false); // calculate e_k
2356 
2357     unsigned int i;
2358     for (i=0; i<100; i++) {
2359       old_xyz = atom->GetVector();
2360 
2361       xyz_k = atom->GetVector() + direction*step;
2362       atom->SetVector(xyz_k);  // update coordinates
2363 
2364       e_n2 = Energy(false); // calculate e_k+1
2365 
2366       // convergence criteria: A higher precision here
2367       // only takes longer with the same result.
2368       if (IsNear(e_n2, e_n1, 1.0e-3))
2369         break;
2370 
2371       if (e_n2 > e_n1) { // decrease stepsize
2372         step *= 0.1;
2373         atom->SetVector(old_xyz);
2374       }
2375       if (e_n2 < e_n1) {  // increase stepsize
2376         e_n1 = e_n2;
2377         step *= 2.15;
2378         if (step > 1.0)
2379           step = 1.0;
2380       }
2381 
2382     }
2383 
2384     dir = atom->GetVector() - orig_xyz;
2385     atom->SetVector(orig_xyz);
2386 
2387     // cutoff accuracy
2388     if (dir.length() < 1.0e-8)
2389       return VZero;
2390 
2391     return dir;
2392   }
2393 
2394   //
2395   // Based on the ghemical code (conjgrad.cpp)
2396   //
2397   // Implements several enhancements:
2398   // 1) Switch to smarter line search method (e.g., Newton's method in 1D)
2399   //  x(n+1) = x(n) - F(x) / F'(x)   -- can be done numerically
2400   // 2) Switch to line search for one step of the entire molecule!
2401   //   This dramatically cuts down on the number of Energy() calls.
2402   //  (and is more correct anyway)
Newton2NumLineSearch(double * direction)2403   double OBForceField::Newton2NumLineSearch(double *direction)
2404   {
2405     double e_n1, e_n2, e_n3;
2406     double *origCoords = new double [_ncoords];
2407 
2408     double opt_step = 0.0;
2409     double opt_e = _e_n1; // get energy calculated by sd or cg
2410     const double def_step = 0.025; // default step
2411     const double max_step = 4.5; // don't go too far
2412 
2413     double sum = 0.0;
2414     for (unsigned int c = 0; c < _ncoords; ++c) {
2415       if (isfinite(direction[c])) {
2416         sum += direction[c] * direction[c];
2417       } else {
2418         // make sure we don't have NaN or infinity
2419         direction[c] = 0.0;
2420       }
2421     }
2422 
2423     double scale = sqrt(sum);
2424     if (IsNearZero(scale)) {
2425       //      cout << "WARNING: too small \"scale\" at Newton2NumLineSearch" << endl;
2426       scale = 1.0e-70; // try to avoid "division by zero" conditions
2427     }
2428 
2429     double step = def_step / scale;
2430     double max_scl = max_step / scale;
2431 
2432     // Save the current position, before we take a step
2433     memcpy((char*)origCoords,(char*)_mol.GetCoordinates(),sizeof(double)*_ncoords);
2434 
2435     int newton = 0;
2436     while (true) {
2437       // Take step X(n) + step
2438       LineSearchTakeStep(origCoords, direction, step);
2439       e_n1 = Energy(false) + _constraints.GetConstraintEnergy();
2440 
2441       if (e_n1 < opt_e) {
2442         opt_step = step;
2443         opt_e = e_n1;
2444       }
2445 
2446       if (newton++ > 3)
2447         break;
2448       double delta = step * 0.001;
2449 
2450       // Take step X(n) + step + delta
2451       LineSearchTakeStep(origCoords, direction, step+delta);
2452       e_n2 = Energy(false) + _constraints.GetConstraintEnergy();
2453 
2454       // Take step X(n) + step + delta * 2.0
2455       LineSearchTakeStep(origCoords, direction, step+delta*2.0);
2456       e_n3 = Energy(false) + _constraints.GetConstraintEnergy();
2457 
2458       double denom = e_n3 - 2.0 * e_n2 + e_n1; // f'(x)
2459       if (denom != 0.0) {
2460         step = fabs(step - delta * (e_n2 - e_n1) / denom);
2461         if (step > max_scl) {
2462           //          cout << "WARNING: damped steplength " << step << " to " << max_scl << endl;
2463           step = max_scl;
2464         }
2465       } else {
2466         break;
2467       }
2468     }
2469 
2470     if (opt_step == 0.0) { // if we still don't have any valid steplength, try a very small step
2471       step = 0.001 * def_step / scale;
2472 
2473       // Take step X(n) + step
2474       LineSearchTakeStep(origCoords, direction, step);
2475       e_n1 = Energy(false) + _constraints.GetConstraintEnergy();
2476 
2477       if (e_n1 < opt_e) {
2478         opt_step = step;
2479         opt_e = e_n1;
2480       }
2481 
2482     }
2483 
2484     // Take optimal step
2485     LineSearchTakeStep(origCoords, direction, opt_step);
2486 
2487     //    cout << " scale: " << scale << " step: " << opt_step*scale << " maxstep " << max_scl*scale << endl;
2488 
2489     delete [] origCoords;
2490 
2491     return opt_step * scale;
2492   }
2493 
LineSearchTakeStep(double * origCoords,double * direction,double step)2494   void OBForceField::LineSearchTakeStep(double* origCoords, double *direction, double step)
2495   {
2496     double *currentCoords = _mol.GetCoordinates();
2497 
2498     for (unsigned int c = 0; c < _ncoords; ++c) {
2499       if (isfinite(direction[c])) {
2500         currentCoords[c] = origCoords[c] + direction[c] * step;
2501       }
2502     }
2503   }
2504 
LineSearch(double * currentCoords,double * direction)2505   double OBForceField::LineSearch(double *currentCoords, double *direction)
2506   {
2507     unsigned int numCoords = _mol.NumAtoms() * 3;
2508     double e_n1, e_n2, step, alpha, tempStep;
2509     double *lastStep = new double [numCoords];
2510 
2511     alpha = 0.0; // Scale factor along direction vector
2512     step = 0.2;
2513     double trustRadius = 0.75; // don't move too far at once
2514 
2515     // The initial energy should be precomputed
2516     e_n1 = _e_n1; // Energy(false) + _constraints.GetConstraintEnergy();
2517 
2518     unsigned int i;
2519     for (i=0; i < 10; ++i) {
2520       // Save the current position, before we take a step
2521       memcpy((char*)lastStep,(char*)currentCoords,sizeof(double)*numCoords);
2522 
2523       // Vectorizing this would be a big benefit
2524       // Need to look up using BLAS or Eigen or whatever
2525       for (unsigned int c = 0; c < numCoords; ++c) {
2526         if (isfinite(direction[c])) {
2527           // make sure we don't have NaN or infinity
2528           tempStep = direction[c] * step;
2529 
2530           if (tempStep > trustRadius) // positive big step
2531             currentCoords[c] += trustRadius;
2532           else if (tempStep < -trustRadius) // negative big step
2533             currentCoords[c] -= trustRadius;
2534           else
2535             currentCoords[c] += tempStep;
2536         }
2537       }
2538 
2539       e_n2 = Energy(false) + _constraints.GetConstraintEnergy();
2540 
2541       // convergence criteria: A higher precision here
2542       // only takes longer with the same result.
2543       if (IsNear(e_n2, e_n1, 1.0e-3)) {
2544         alpha += step;
2545         break;
2546       }
2547 
2548       if (e_n2 > e_n1) { // decrease stepsize
2549         step *= 0.1;
2550         // move back to the last step
2551         memcpy((char*)currentCoords,(char*)lastStep,sizeof(double)*numCoords);
2552       } else if (e_n2 < e_n1) {  // increase stepsize
2553         e_n1 = e_n2;
2554         alpha += step; // we've moved some distance
2555         step *= 2.15;
2556         if (step > 1.0)
2557           step = 1.0;
2558       }
2559 
2560     }
2561 
2562     delete [] lastStep;
2563 
2564     return alpha;
2565   }
2566 
DetectExplosion()2567   bool OBForceField::DetectExplosion()
2568   {
2569     FOR_ATOMS_OF_MOL (atom, _mol) {
2570       if (!isfinite(atom->GetX()))
2571         return true;
2572       if (!isfinite(atom->GetY()))
2573         return true;
2574       if (!isfinite(atom->GetZ()))
2575         return true;
2576     }
2577 
2578     FOR_BONDS_OF_MOL (bond, _mol) {
2579       if (bond->GetLength() > 30.0)
2580         return true;
2581     }
2582 
2583     return false;
2584   }
2585 
ValidateLineSearch(OBAtom * atom,vector3 & direction)2586   vector3 OBForceField::ValidateLineSearch(OBAtom *atom, vector3 &direction)
2587   {
2588     double e_n1, e_n2, step;
2589     vector3 old_xyz, orig_xyz, xyz_k, dir(0.0, 0.0, 0.0);
2590 
2591     step = 0.2;
2592     direction.normalize();
2593     orig_xyz = atom->GetVector();
2594 
2595     e_n1 = atom->x() * atom->x() + 2 * (atom->y() * atom->y()); // e_k
2596 
2597     for (unsigned int i=0; i<100; i++) {
2598       old_xyz = atom->GetVector();
2599 
2600       xyz_k = atom->GetVector() + direction*step;
2601       atom->SetVector(xyz_k);  // update coordinates
2602 
2603       e_n2 = atom->x() * atom->x() + 2 * (atom->y() * atom->y()); // e_k+1
2604 
2605       if (e_n2 == e_n1) // convergence criteria
2606         break;
2607 
2608       if (e_n2 > e_n1) { // decrease stepsize
2609         step *= 0.5;
2610         atom->SetVector(old_xyz);
2611       }
2612       if (e_n2 < e_n1) {  // increase stepsize
2613         e_n1 = e_n2;
2614         step *= 1.2;
2615         if (step > 1.0)
2616           step = 1.0;
2617       }
2618 
2619     }
2620 
2621     dir = atom->GetVector() - orig_xyz;
2622     atom->SetVector(orig_xyz);
2623     return dir;
2624   }
2625 
2626   // used to validate the SteepestDescent implementation using a simple function.
2627   //
2628   // f(x,y) = x^2 + 2y^2
2629   // minimum: (0, 0)
2630   // df/dx = 2x
2631   // df/dy = 4y
2632   //
ValidateSteepestDescent(int steps)2633   void OBForceField::ValidateSteepestDescent(int steps)
2634   {
2635     OBAtom *atom = new OBAtom;
2636     vector3 grad;
2637     double e_n1, e_n2;
2638 
2639     atom->SetVector(9.0, 9.0, 0.0);
2640     e_n1 = atom->x() * atom->x() + 2 * (atom->y() * atom->y());
2641 
2642     IF_OBFF_LOGLVL_LOW {
2643       OBFFLog("\nV A L I D A T E   S T E E P E S T   D E S C E N T\n\n");
2644       snprintf(_logbuf, BUFF_SIZE, "STEPS = %d\n\n",  steps);
2645       OBFFLog(_logbuf);
2646       OBFFLog("STEP n     E(n)       E(n-1)    \n");
2647       OBFFLog("--------------------------------\n");
2648     }
2649 
2650     for (int i = 1; i <= steps; i++) {
2651       grad.Set(-2*atom->x(), -4*atom->y(), 0.0);
2652       grad = ValidateLineSearch(&*atom, grad);
2653       atom->SetVector(atom->x() + grad.x(), atom->y() + grad.y(), 0.0);
2654       e_n2 = atom->x() * atom->x() + 2 * (atom->y() * atom->y());
2655 
2656       IF_OBFF_LOGLVL_LOW {
2657         snprintf(_logbuf, BUFF_SIZE, " %4d    %8.3f    %8.3f\n", i, e_n2, e_n1);
2658         OBFFLog(_logbuf);
2659       }
2660 
2661       if (IsNear(e_n2, e_n1, 1.0e-7)) {
2662         IF_OBFF_LOGLVL_LOW
2663           OBFFLog("    STEEPEST DESCENT HAS CONVERGED (DELTA E < 1.0e-7)\n");
2664         break;
2665       }
2666 
2667       e_n1 = e_n2;
2668     }
2669 
2670     IF_OBFF_LOGLVL_LOW
2671       OBFFLog("\n");
2672 
2673     delete atom;
2674   }
2675 
ValidateConjugateGradients(int steps)2676   void OBForceField::ValidateConjugateGradients(int steps)
2677   {
2678     OBAtom *atom = new OBAtom;
2679     vector3 grad1, grad2, dir1, dir2;
2680     double e_n1, e_n2;
2681     double g2g2, g1g1, g2g1;
2682     bool firststep;
2683 
2684     firststep = true;
2685     atom->SetVector(9.0, 9.0, 0.0);
2686     e_n1 = atom->x() * atom->x() + 2 * (atom->y() * atom->y());
2687 
2688     IF_OBFF_LOGLVL_LOW {
2689       OBFFLog("\nV A L I D A T E   C O N J U G A T E   G R A D I E N T S\n\n");
2690       snprintf(_logbuf, BUFF_SIZE, "STEPS = %d\n\n",  steps);
2691       OBFFLog(_logbuf);
2692       OBFFLog("STEP n     E(n)       E(n-1)    \n");
2693       OBFFLog("--------------------------------\n");
2694     }
2695 
2696     for (int i = 1; i <= steps; i++) {
2697       if (firststep) {
2698         grad1.Set(-2*atom->x(), -4*atom->y(), 0.0);
2699         dir1 = grad1;
2700         dir1 = ValidateLineSearch(&*atom, dir1);
2701         atom->SetVector(atom->x() + dir1.x(), atom->y() + dir1.y(), atom->z() + dir1.z());
2702         e_n2 = atom->x() * atom->x() + 2 * (atom->y() * atom->y());
2703 
2704         IF_OBFF_LOGLVL_LOW {
2705           snprintf(_logbuf, BUFF_SIZE, " %4d    %8.3f    %8.3f\n", i, e_n2, e_n1);
2706           OBFFLog(_logbuf);
2707         }
2708 
2709         e_n1 = e_n2;
2710         dir1 = grad1;
2711         firststep = false;
2712       } else {
2713         grad2.Set(-2*atom->x(), -4*atom->y(), 0.0);
2714         g2g2 = dot(grad2, grad2);
2715         g1g1 = dot(grad1, grad1);
2716         g2g1 = g2g2 / g1g1;
2717         dir2 = grad2 + g2g1 * dir1;
2718         dir2 = ValidateLineSearch(&*atom, dir2);
2719         atom->SetVector(atom->x() + dir2.x(), atom->y() + dir2.y(), atom->z() + dir2.z());
2720         grad1 = grad2;
2721         dir1 = dir2;
2722         e_n2 = atom->x() * atom->x() + 2 * (atom->y() * atom->y());
2723 
2724         IF_OBFF_LOGLVL_LOW {
2725           snprintf(_logbuf, BUFF_SIZE, " %4d    %8.3f    %8.3f\n", i, e_n2, e_n1);
2726           OBFFLog(_logbuf);
2727         }
2728 
2729         if (IsNear(e_n2, e_n1, 1.0e-7)) {
2730           IF_OBFF_LOGLVL_LOW
2731             OBFFLog("    CONJUGATE GRADIENTS HAS CONVERGED (DELTA E < 1.0e-7)\n");
2732           break;
2733         }
2734 
2735         e_n1 = e_n2;
2736       }
2737     }
2738 
2739     delete atom;
2740   }
2741 
SteepestDescentInitialize(int steps,double econv,int method)2742   void OBForceField::SteepestDescentInitialize(int steps, double econv, int method)
2743   {
2744     if (!_validSetup)
2745       return;
2746 
2747     _nsteps = steps;
2748     _cstep = 0;
2749     _econv = econv;
2750     _gconv = 1.0e-2; // gradient convergence (0.1) squared
2751 
2752     if (_cutoff)
2753       UpdatePairsSimple(); // Update the non-bonded pairs (Cut-off)
2754 
2755     _e_n1 = Energy() + _constraints.GetConstraintEnergy();
2756 
2757     IF_OBFF_LOGLVL_LOW {
2758       OBFFLog("\nS T E E P E S T   D E S C E N T\n\n");
2759       snprintf(_logbuf, BUFF_SIZE, "STEPS = %d\n\n",  steps);
2760       OBFFLog(_logbuf);
2761       OBFFLog("STEP n       E(n)         E(n-1)    \n");
2762       OBFFLog("------------------------------------\n");
2763       snprintf(_logbuf, BUFF_SIZE, " %4d    %8.3f      ----\n", _cstep, _e_n1);
2764       OBFFLog(_logbuf);
2765     }
2766 
2767   }
2768 
SteepestDescentTakeNSteps(int n)2769   bool OBForceField::SteepestDescentTakeNSteps(int n)
2770   {
2771     if (!_validSetup)
2772       return 0;
2773 
2774     _ncoords = _mol.NumAtoms() * 3;
2775     double e_n2;
2776     vector3 dir;
2777     double maxgrad; // for convergence
2778 
2779     for (int i = 1; i <= n; i++) {
2780       _cstep++;
2781       maxgrad = 1.0e20;
2782 
2783       FOR_ATOMS_OF_MOL (a, _mol) {
2784         unsigned int idx = a->GetIdx();
2785         unsigned int coordIdx = (idx - 1) * 3;
2786 
2787         if (_constraints.IsFixed(idx) || (_fixAtom == idx) || (_ignoreAtom == idx)) {
2788           _gradientPtr[coordIdx] = 0.0;
2789           _gradientPtr[coordIdx+1] = 0.0;
2790           _gradientPtr[coordIdx+2] = 0.0;
2791         } else {
2792           if (!HasAnalyticalGradients()) {
2793             // use numerical gradients
2794             dir = NumericalDerivative(&*a) + _constraints.GetGradient(a->GetIdx());
2795           } else {
2796             // use analytical gradients
2797             dir = GetGradient(&*a) + _constraints.GetGradient(a->GetIdx());
2798           }
2799 
2800           // check to see how large the gradients are
2801           if (dir.length_2() < maxgrad)
2802             maxgrad = dir.length_2();
2803 
2804           if (!_constraints.IsXFixed(idx))
2805             _gradientPtr[coordIdx] = dir.x();
2806           else
2807             _gradientPtr[coordIdx] = 0.0;
2808 
2809           if (!_constraints.IsYFixed(idx))
2810             _gradientPtr[coordIdx+1] = dir.y();
2811           else
2812             _gradientPtr[coordIdx+1] = 0.0;
2813 
2814           if (!_constraints.IsZFixed(idx))
2815             _gradientPtr[coordIdx+2] = dir.z();
2816           else
2817             _gradientPtr[coordIdx+2] = 0.0;
2818         }
2819       }
2820       // perform a linesearch
2821       switch (_linesearch) {
2822       case LineSearchType::Newton2Num:
2823         Newton2NumLineSearch(_gradientPtr);
2824         break;
2825       default:
2826       case LineSearchType::Simple:
2827         LineSearch(_mol.GetCoordinates(), _gradientPtr);
2828         break;
2829       }
2830       e_n2 = Energy() + _constraints.GetConstraintEnergy();
2831 
2832       if ((_cstep % _pairfreq == 0) && _cutoff)
2833         UpdatePairsSimple(); // Update the non-bonded pairs (Cut-off)
2834 
2835       IF_OBFF_LOGLVL_LOW {
2836         if (_cstep % 10 == 0) {
2837           snprintf(_logbuf, BUFF_SIZE, " %4d    %8.5f    %8.5f\n", _cstep, e_n2, _e_n1);
2838           OBFFLog(_logbuf);
2839         }
2840       }
2841 
2842       if (IsNear(e_n2, _e_n1, _econv)
2843           && (maxgrad < _gconv)) { // gradient criteria (0.1) squared
2844         IF_OBFF_LOGLVL_LOW
2845           OBFFLog("    STEEPEST DESCENT HAS CONVERGED\n");
2846         return false;
2847       }
2848 
2849       if (_nsteps == _cstep) {
2850         return false;
2851       }
2852 
2853       _e_n1 = e_n2;
2854     }
2855 
2856     return true;  // no convergence reached
2857   }
2858 
SteepestDescent(int steps,double econv,int method)2859   void OBForceField::SteepestDescent(int steps, double econv, int method)
2860   {
2861     if (steps > 0) {
2862       SteepestDescentInitialize(steps, econv, method);
2863       SteepestDescentTakeNSteps(steps);
2864     }
2865   }
2866 
ConjugateGradientsInitialize(int steps,double econv,int method)2867   void OBForceField::ConjugateGradientsInitialize(int steps, double econv,
2868                                                   int method)
2869   {
2870     if (!_validSetup || steps==0)
2871       return;
2872 
2873     double e_n2;
2874     vector3 dir;
2875 
2876     _cstep = 0;
2877     _nsteps = steps;
2878     _econv = econv;
2879     _gconv = 1.0e-2; // gradient convergence (0.1) squared
2880     _ncoords = _mol.NumAtoms() * 3;
2881 
2882     if (_cutoff)
2883       UpdatePairsSimple(); // Update the non-bonded pairs (Cut-off)
2884 
2885     _e_n1 = Energy() + _constraints.GetConstraintEnergy();
2886 
2887     IF_OBFF_LOGLVL_LOW {
2888       OBFFLog("\nC O N J U G A T E   G R A D I E N T S\n\n");
2889       snprintf(_logbuf, BUFF_SIZE, "STEPS = %d\n\n",  steps);
2890       OBFFLog(_logbuf);
2891       OBFFLog("STEP n     E(n)       E(n-1)    \n");
2892       OBFFLog("--------------------------------\n");
2893     }
2894 
2895     if (_grad1 != nullptr)
2896       delete [] _grad1;
2897     _grad1 = new double[_ncoords];
2898     memset(_grad1, '\0', sizeof(double)*_ncoords);
2899 
2900     // Take the first step (same as steepest descent because there is no
2901     // gradient from the previous step.
2902     FOR_ATOMS_OF_MOL (a, _mol) {
2903       unsigned int idx = a->GetIdx();
2904       unsigned int coordIdx = (idx - 1) * 3;
2905 
2906       if (_constraints.IsFixed(idx) || (_fixAtom == idx) || (_ignoreAtom == idx)) {
2907         _gradientPtr[coordIdx] = 0.0;
2908         _gradientPtr[coordIdx+1] = 0.0;
2909         _gradientPtr[coordIdx+2] = 0.0;
2910       } else {
2911         if (!HasAnalyticalGradients()) {
2912           // use numerical gradients
2913           dir = NumericalDerivative(&*a) + _constraints.GetGradient(a->GetIdx());
2914         } else {
2915           // use analytical gradients
2916           dir = GetGradient(&*a) + _constraints.GetGradient(a->GetIdx());
2917         }
2918 
2919         if (!_constraints.IsXFixed(idx))
2920           _gradientPtr[coordIdx] = dir.x();
2921         else
2922           _gradientPtr[coordIdx] = 0.0;
2923 
2924         if (!_constraints.IsYFixed(idx))
2925           _gradientPtr[coordIdx+1] = dir.y();
2926         else
2927           _gradientPtr[coordIdx+1] = 0.0;
2928 
2929         if (!_constraints.IsZFixed(idx))
2930           _gradientPtr[coordIdx+2] = dir.z();
2931         else
2932           _gradientPtr[coordIdx+2] = 0.0;
2933       }
2934     }
2935     // perform a linesearch
2936     switch (_linesearch) {
2937     case LineSearchType::Newton2Num:
2938       Newton2NumLineSearch(_gradientPtr);
2939       break;
2940     default:
2941     case LineSearchType::Simple:
2942       LineSearch(_mol.GetCoordinates(), _gradientPtr);
2943       break;
2944     }
2945     e_n2 = Energy() + _constraints.GetConstraintEnergy();
2946 
2947     IF_OBFF_LOGLVL_LOW {
2948       snprintf(_logbuf, BUFF_SIZE, " %4d    %8.3f    %8.3f\n", 1, e_n2, _e_n1);
2949       OBFFLog(_logbuf);
2950     }
2951 
2952     // save the direction and energy
2953     memcpy(_grad1, _gradientPtr, sizeof(double)*_ncoords);
2954     _e_n1 = e_n2;
2955   }
2956 
ConjugateGradientsTakeNSteps(int n)2957   bool OBForceField::ConjugateGradientsTakeNSteps(int n)
2958   {
2959     if (!_validSetup)
2960       return 0;
2961 
2962     double e_n2;
2963     double g2g2, g1g1, beta;
2964     vector3 grad2, dir2;
2965     vector3 grad1, dir1; // temporaries to perform dot product, etc.
2966     double maxgrad; // for convergence
2967 
2968     if (_ncoords != _mol.NumAtoms() * 3)
2969       return false;
2970 
2971     e_n2 = 0.0;
2972 
2973     for (int i = 1; i <= n; i++) {
2974       _cstep++;
2975       maxgrad = 1.0e20;
2976 
2977       FOR_ATOMS_OF_MOL (a, _mol) {
2978         unsigned int idx = a->GetIdx();
2979         unsigned int coordIdx = (a->GetIdx() - 1) * 3;
2980 
2981         if (_constraints.IsFixed(idx) || (_fixAtom == idx) || (_ignoreAtom == idx)) {
2982           _grad1[coordIdx] = 0.0;
2983           _grad1[coordIdx+1] = 0.0;
2984           _grad1[coordIdx+2] = 0.0;
2985         } else {
2986           if (!HasAnalyticalGradients()) {
2987             // use numerical gradients
2988             grad2 = NumericalDerivative(&*a) + _constraints.GetGradient(a->GetIdx());
2989           } else {
2990             // use analytical gradients
2991             grad2 = GetGradient(&*a) + _constraints.GetGradient(a->GetIdx());
2992           }
2993 
2994           // Fletcher-Reeves formula for Beta
2995           // http://en.wikipedia.org/wiki/Nonlinear_conjugate_gradient_method
2996           // NOTE: We make sure to reset and use the steepest descent direction
2997           //   after NumAtoms steps
2998           if (_cstep % _mol.NumAtoms() != 0) {
2999             g2g2 = dot(grad2, grad2);
3000             grad1 = vector3(_grad1[coordIdx], _grad1[coordIdx+1], _grad1[coordIdx+2]);
3001             g1g1 = dot(grad1, grad1);
3002             beta = g2g2 / g1g1;
3003             grad2 += beta * grad1;
3004           }
3005 
3006           // check to see how large the gradients are
3007           if (grad2.length_2() < maxgrad)
3008             maxgrad = grad2.length_2();
3009 
3010           if (!_constraints.IsXFixed(idx))
3011             _grad1[coordIdx] = grad2.x();
3012           else
3013             _grad1[coordIdx] = 0.0;
3014 
3015           if (!_constraints.IsYFixed(idx))
3016             _grad1[coordIdx+1] = grad2.y();
3017           else
3018             _grad1[coordIdx+1] = 0.0;
3019 
3020           if (!_constraints.IsZFixed(idx))
3021             _grad1[coordIdx+2] = grad2.z();
3022           else
3023             _grad1[coordIdx+2] = 0.0;
3024         }
3025       }
3026       // perform a linesearch
3027       switch (_linesearch) {
3028       case LineSearchType::Newton2Num:
3029         Newton2NumLineSearch(_grad1);
3030         break;
3031       default:
3032       case LineSearchType::Simple:
3033         LineSearch(_mol.GetCoordinates(), _grad1);
3034         break;
3035       }
3036       // save the direction
3037       memcpy(_gradientPtr, _grad1, sizeof(double)*_ncoords);
3038 
3039       e_n2 = Energy() + _constraints.GetConstraintEnergy();
3040 
3041       if ((_cstep % _pairfreq == 0) && _cutoff)
3042         UpdatePairsSimple(); // Update the non-bonded pairs (Cut-off)
3043 
3044       if (IsNear(e_n2, _e_n1, _econv)
3045           && (maxgrad < _gconv)) { // gradient criteria (0.1) squared
3046         IF_OBFF_LOGLVL_LOW {
3047           snprintf(_logbuf, BUFF_SIZE, " %4d    %8.3f    %8.3f\n", _cstep, e_n2, _e_n1);
3048           OBFFLog(_logbuf);
3049           OBFFLog("    CONJUGATE GRADIENTS HAS CONVERGED\n");
3050         }
3051         return false;
3052       }
3053 
3054       IF_OBFF_LOGLVL_LOW {
3055         if (_cstep % 10 == 0) {
3056           snprintf(_logbuf, BUFF_SIZE, " %4d    %8.3f    %8.3f\n", _cstep, e_n2, _e_n1);
3057           OBFFLog(_logbuf);
3058         }
3059       }
3060 
3061       if (_nsteps == _cstep)
3062         return false;
3063 
3064       _e_n1 = e_n2;
3065     }
3066 
3067     return true; // no convergence reached
3068   }
3069 
ConjugateGradients(int steps,double econv,int method)3070   void OBForceField::ConjugateGradients(int steps, double econv, int method)
3071   {
3072     ConjugateGradientsInitialize(steps, econv, method);
3073     if (steps > 1) // ConjugateGradientsInitialize takes the first step
3074       ConjugateGradientsTakeNSteps(steps);
3075   }
3076 
3077   //
3078   //         f(1) - f(0)
3079   // f'(0) = -----------      f(1) = f(0+h)
3080   //              h
3081   //
NumericalDerivative(OBAtom * atom,int terms)3082   vector3 OBForceField::NumericalDerivative(OBAtom *atom, int terms)
3083   {
3084     vector3 va, grad;
3085     double e_orig, e_plus_delta, delta, dx, dy, dz;
3086 
3087     delta = 1.0e-5;
3088 
3089     va = atom->GetVector();
3090 
3091     if (terms & OBFF_ENERGY)
3092       e_orig = Energy(false);
3093     else {
3094       e_orig = 0.0;
3095       if (terms & OBFF_EBOND)
3096         e_orig += E_Bond(false);
3097       if (terms & OBFF_EANGLE)
3098         e_orig += E_Angle(false);
3099       if (terms & OBFF_ESTRBND)
3100         e_orig += E_StrBnd(false);
3101       if (terms & OBFF_ETORSION)
3102         e_orig += E_Torsion(false);
3103       if (terms & OBFF_EOOP)
3104         e_orig += E_OOP(false);
3105       if (terms & OBFF_EVDW)
3106         e_orig += E_VDW(false);
3107       if (terms & OBFF_EELECTROSTATIC)
3108         e_orig += E_Electrostatic(false);
3109     }
3110 
3111     // X direction
3112     atom->SetVector(va.x() + delta, va.y(), va.z());
3113 
3114     if (terms & OBFF_ENERGY)
3115       e_plus_delta = Energy(false);
3116     else {
3117       e_plus_delta = 0.0;
3118       if (terms & OBFF_EBOND)
3119         e_plus_delta += E_Bond(false);
3120       if (terms & OBFF_EANGLE)
3121         e_plus_delta += E_Angle(false);
3122       if (terms & OBFF_ESTRBND)
3123         e_plus_delta += E_StrBnd(false);
3124       if (terms & OBFF_ETORSION)
3125         e_plus_delta += E_Torsion(false);
3126       if (terms & OBFF_EOOP)
3127         e_plus_delta += E_OOP(false);
3128       if (terms & OBFF_EVDW)
3129         e_plus_delta += E_VDW(false);
3130       if (terms & OBFF_EELECTROSTATIC)
3131         e_plus_delta += E_Electrostatic(false);
3132     }
3133 
3134     dx = (e_plus_delta - e_orig) / delta;
3135 
3136     // Y direction
3137     atom->SetVector(va.x(), va.y() + delta, va.z());
3138 
3139     if (terms & OBFF_ENERGY)
3140       e_plus_delta = Energy(false);
3141     else {
3142       e_plus_delta = 0.0;
3143       if (terms & OBFF_EBOND)
3144         e_plus_delta += E_Bond(false);
3145       if (terms & OBFF_EANGLE)
3146         e_plus_delta += E_Angle(false);
3147       if (terms & OBFF_ESTRBND)
3148         e_plus_delta += E_StrBnd(false);
3149       if (terms & OBFF_ETORSION)
3150         e_plus_delta += E_Torsion(false);
3151       if (terms & OBFF_EOOP)
3152         e_plus_delta += E_OOP(false);
3153       if (terms & OBFF_EVDW)
3154         e_plus_delta += E_VDW(false);
3155       if (terms & OBFF_EELECTROSTATIC)
3156         e_plus_delta += E_Electrostatic(false);
3157     }
3158 
3159     dy = (e_plus_delta - e_orig) / delta;
3160 
3161     // Z direction
3162     atom->SetVector(va.x(), va.y(), va.z() + delta);
3163 
3164     if (terms & OBFF_ENERGY)
3165       e_plus_delta = Energy(false);
3166     else {
3167       e_plus_delta = 0.0;
3168       if (terms & OBFF_EBOND)
3169         e_plus_delta += E_Bond(false);
3170       if (terms & OBFF_EANGLE)
3171         e_plus_delta += E_Angle(false);
3172       if (terms & OBFF_ESTRBND)
3173         e_plus_delta += E_StrBnd(false);
3174       if (terms & OBFF_ETORSION)
3175         e_plus_delta += E_Torsion(false);
3176       if (terms & OBFF_EOOP)
3177         e_plus_delta += E_OOP(false);
3178       if (terms & OBFF_EVDW)
3179         e_plus_delta += E_VDW(false);
3180       if (terms & OBFF_EELECTROSTATIC)
3181         e_plus_delta += E_Electrostatic(false);
3182     }
3183 
3184     dz = (e_plus_delta - e_orig) / delta;
3185 
3186     // reset coordinates to original
3187     atom->SetVector(va.x(), va.y(), va.z());
3188 
3189     grad.Set(-dx, -dy, -dz);
3190     return (grad);
3191   }
3192 
3193   //
3194   //         f(2) - 2f(1) + f(0)
3195   // f'(0) = -------------------      f(1) = f(0+h)
3196   //                 h^2              f(1) = f(0+2h)
3197   //
NumericalSecondDerivative(OBAtom * atom,int terms)3198   vector3 OBForceField::NumericalSecondDerivative(OBAtom *atom, int terms)
3199   {
3200     vector3 va, grad;
3201     double e_0, e_1, e_2, delta, dx, dy, dz;
3202 
3203     delta = 1.0e-5;
3204 
3205     va = atom->GetVector();
3206 
3207     // calculate f(0)
3208     if (terms & OBFF_ENERGY)
3209       e_0 = Energy(false);
3210     else {
3211       e_0 = 0.0;
3212       if (terms & OBFF_EBOND)
3213         e_0 += E_Bond(false);
3214       if (terms & OBFF_EANGLE)
3215         e_0 += E_Angle(false);
3216       if (terms & OBFF_ESTRBND)
3217         e_0 += E_StrBnd(false);
3218       if (terms & OBFF_ETORSION)
3219         e_0 += E_Torsion(false);
3220       if (terms & OBFF_EOOP)
3221         e_0 += E_OOP(false);
3222       if (terms & OBFF_EVDW)
3223         e_0 += E_VDW(false);
3224       if (terms & OBFF_EELECTROSTATIC)
3225         e_0 += E_Electrostatic(false);
3226     }
3227 
3228     //
3229     // X direction
3230     //
3231 
3232     // calculate f(1)
3233     atom->SetVector(va.x() + delta, va.y(), va.z());
3234 
3235     if (terms & OBFF_ENERGY)
3236       e_1 = Energy(false);
3237     else {
3238       e_1 = 0.0;
3239       if (terms & OBFF_EBOND)
3240         e_1 += E_Bond(false);
3241       if (terms & OBFF_EANGLE)
3242         e_1 += E_Angle(false);
3243       if (terms & OBFF_ESTRBND)
3244         e_1 += E_StrBnd(false);
3245       if (terms & OBFF_ETORSION)
3246         e_1 += E_Torsion(false);
3247       if (terms & OBFF_EOOP)
3248         e_1 += E_OOP(false);
3249       if (terms & OBFF_EVDW)
3250         e_1 += E_VDW(false);
3251       if (terms & OBFF_EELECTROSTATIC)
3252         e_1 += E_Electrostatic(false);
3253     }
3254 
3255     // calculate f(2)
3256     atom->SetVector(va.x() + 2 * delta, va.y(), va.z());
3257 
3258     if (terms & OBFF_ENERGY)
3259       e_2 = Energy(false);
3260     else {
3261       e_2 = 0.0;
3262       if (terms & OBFF_EBOND)
3263         e_2 += E_Bond(false);
3264       if (terms & OBFF_EANGLE)
3265         e_2 += E_Angle(false);
3266       if (terms & OBFF_ESTRBND)
3267         e_2 += E_StrBnd(false);
3268       if (terms & OBFF_ETORSION)
3269         e_2 += E_Torsion(false);
3270       if (terms & OBFF_EOOP)
3271         e_2 += E_OOP(false);
3272       if (terms & OBFF_EVDW)
3273         e_2 += E_VDW(false);
3274       if (terms & OBFF_EELECTROSTATIC)
3275         e_2 += E_Electrostatic(false);
3276     }
3277 
3278     dx = (e_2 - 2 * e_1 + e_0) / (delta * delta);
3279 
3280     //
3281     // Y direction
3282     //
3283 
3284     // calculate f(1)
3285     atom->SetVector(va.x(), va.y() + delta, va.z());
3286 
3287     if (terms & OBFF_ENERGY)
3288       e_1 = Energy(false);
3289     else {
3290       e_1 = 0.0;
3291       if (terms & OBFF_EBOND)
3292         e_1 += E_Bond(false);
3293       if (terms & OBFF_EANGLE)
3294         e_1 += E_Angle(false);
3295       if (terms & OBFF_ESTRBND)
3296         e_1 += E_StrBnd(false);
3297       if (terms & OBFF_ETORSION)
3298         e_1 += E_Torsion(false);
3299       if (terms & OBFF_EOOP)
3300         e_1 += E_OOP(false);
3301       if (terms & OBFF_EVDW)
3302         e_1 += E_VDW(false);
3303       if (terms & OBFF_EELECTROSTATIC)
3304         e_1 += E_Electrostatic(false);
3305     }
3306 
3307     // calculate f(2)
3308     atom->SetVector(va.x(), va.y() + 2 * delta, va.z());
3309 
3310     if (terms & OBFF_ENERGY)
3311       e_2 = Energy(false);
3312     else {
3313       e_2 = 0.0;
3314       if (terms & OBFF_EBOND)
3315         e_2 += E_Bond(false);
3316       if (terms & OBFF_EANGLE)
3317         e_2 += E_Angle(false);
3318       if (terms & OBFF_ESTRBND)
3319         e_2 += E_StrBnd(false);
3320       if (terms & OBFF_ETORSION)
3321         e_2 += E_Torsion(false);
3322       if (terms & OBFF_EOOP)
3323         e_2 += E_OOP(false);
3324       if (terms & OBFF_EVDW)
3325         e_2 += E_VDW(false);
3326       if (terms & OBFF_EELECTROSTATIC)
3327         e_2 += E_Electrostatic(false);
3328     }
3329 
3330     dy = (e_2 - 2 * e_1 + e_0) / (delta * delta);
3331 
3332     //
3333     // Z direction
3334     //
3335 
3336     // calculate f(1)
3337     atom->SetVector(va.x(), va.y(), va.z() + delta);
3338 
3339     if (terms & OBFF_ENERGY)
3340       e_1 = Energy(false);
3341     else {
3342       e_1 = 0.0;
3343       if (terms & OBFF_EBOND)
3344         e_1 += E_Bond(false);
3345       if (terms & OBFF_EANGLE)
3346         e_1 += E_Angle(false);
3347       if (terms & OBFF_ESTRBND)
3348         e_1 += E_StrBnd(false);
3349       if (terms & OBFF_ETORSION)
3350         e_1 += E_Torsion(false);
3351       if (terms & OBFF_EOOP)
3352         e_1 += E_OOP(false);
3353       if (terms & OBFF_EVDW)
3354         e_1 += E_VDW(false);
3355       if (terms & OBFF_EELECTROSTATIC)
3356         e_1 += E_Electrostatic(false);
3357     }
3358 
3359     // calculate f(2)
3360     atom->SetVector(va.x(), va.y(), va.z() + 2 * delta);
3361 
3362     if (terms & OBFF_ENERGY)
3363       e_2 = Energy(false);
3364     else {
3365       e_2 = 0.0;
3366       if (terms & OBFF_EBOND)
3367         e_2 += E_Bond(false);
3368       if (terms & OBFF_EANGLE)
3369         e_2 += E_Angle(false);
3370       if (terms & OBFF_ESTRBND)
3371         e_2 += E_StrBnd(false);
3372       if (terms & OBFF_ETORSION)
3373         e_2 += E_Torsion(false);
3374       if (terms & OBFF_EOOP)
3375         e_2 += E_OOP(false);
3376       if (terms & OBFF_EVDW)
3377         e_2 += E_VDW(false);
3378       if (terms & OBFF_EELECTROSTATIC)
3379         e_2 += E_Electrostatic(false);
3380     }
3381 
3382     dz = (e_2 - 2 * e_1 + e_0) / (delta * delta);
3383 
3384     // reset coordinates to original
3385     atom->SetVector(va.x(), va.y(), va.z());
3386 
3387     grad.Set(-dx, -dy, -dz);
3388     return (grad);
3389   }
3390 
3391   //////////////////////////////////////////////////////////////////////////////////
3392   //
3393   // Molecular Dynamics
3394   //
3395   //////////////////////////////////////////////////////////////////////////////////
3396   // Most OpenBabel MD alogrithms are based on:
3397   // GROMACS, Groningen Machine for Chemical Simulations, USER MANUAL, version 3.3
3398   //
3399   // Quantity:		Symbol:		Unit:
3400   // length		r		A = 10e-10m
3401   // mass		m 		amu = 1.6605402 10e-27kg
3402   // time		t		ps = 10e-12s
3403   // temperature	T		K
3404   //
3405   // force		F		kcal mol^-1 A^-1
3406   // acceleration	a		kcal mol^-1 A^-1 1000x amu-1 = A ps^-2 (*)
3407   // velocity		v		A ps^-1
3408   //
3409   // The force we calculate comes from the force field. Currently this functions
3410   // only works for MMFF94 and so the unit for energy is kcal, but this does not
3411   // affect what is explained below. (note: A isn't a SI unit either)
3412   //
3413   //     [   kcal   ]          F    [     kcal    ]   [       kcal       ]   [      A      ]   [   A    ]
3414   // F = [ -------- ]     a = --- = [ ----------- ] = [ ---------------- ] = [ ----------- ] = [ ------ ]
3415   //     [  mol  A  ]          m    [  mol A amu  ]   [  mol A 10^-27kg  ]   [  10^-24s^2  ]   [  ps^2  ]
3416   //
3417   // This means that if we divide the force (coming from the FF) by 1000x its mass in amu,
3418   // we get the acceleration in A ps^-2.
3419 
3420   // gromacs user manual page 17
GenerateVelocities()3421   void OBForceField::GenerateVelocities()
3422   {
3423     cout << "OBForceField::GenerateVelocities()" << endl;
3424     OBRandom generator;
3425     generator.TimeSeed();
3426     _ncoords = _mol.NumAtoms() * 3;
3427     int velocityIdx;
3428     double velocity;
3429 
3430     _velocityPtr = new double[_ncoords];
3431     memset(_velocityPtr, '\0', sizeof(double)*_ncoords);
3432 
3433     FOR_ATOMS_OF_MOL (a, _mol) {
3434       if (!_constraints.IsFixed(a->GetIdx()) || (_fixAtom == a->GetIdx()) || (_ignoreAtom == a->GetIdx())) {
3435         velocityIdx = (a->GetIdx() - 1) * 3;
3436 
3437         // add twelve random numbers between 0.0 and 1.0,
3438         // subtract 6.0 from their sum, multiply with sqrt(kT/m)
3439         if (!_constraints.IsXFixed(a->GetIdx())) {
3440           velocity = 0.0;
3441           for (int i=0; i < 12; ++i)
3442             velocity += generator.NextFloat();
3443           velocity -= 6.0;
3444           velocity *= sqrt((GAS_CONSTANT * _temp)/ (1000 * a->GetAtomicMass()));
3445           _velocityPtr[velocityIdx] = velocity; // x10: gromacs uses nm instead of A
3446         }
3447 
3448         if (!_constraints.IsYFixed(a->GetIdx())) {
3449           velocity = 0.0;
3450           for (int i=0; i < 12; ++i)
3451             velocity += generator.NextFloat();
3452           velocity -= 6.0;
3453           velocity *= sqrt((GAS_CONSTANT * _temp)/ (1000 * a->GetAtomicMass()));
3454           _velocityPtr[velocityIdx+1] = velocity; // idem
3455         }
3456 
3457         if (!_constraints.IsZFixed(a->GetIdx())) {
3458           velocity = 0.0;
3459           for (int i=0; i < 12; ++i)
3460             velocity += generator.NextFloat();
3461           velocity -= 6.0;
3462           velocity *= sqrt((GAS_CONSTANT * _temp)/ (1000 * a->GetAtomicMass()));
3463           _velocityPtr[velocityIdx+2] = velocity; // idem
3464         }
3465       }
3466     }
3467 
3468     CorrectVelocities();
3469   }
3470 
CorrectVelocities()3471   void OBForceField::CorrectVelocities()
3472   {
3473     _ncoords = _mol.NumAtoms() * 3;
3474     int velocityIdx;
3475     double velocity, E_kin, E_kin2, factor;
3476 
3477     // E_kin = 0.5 * Ndf * R * T
3478     E_kin = _ncoords * GAS_CONSTANT * _temp;
3479     //cout << "E_{kin} = Ndf * R * T = " << E_kin << endl;
3480 
3481     // E_kin = 0.5 * sum( m_i * v_i^2 )
3482     E_kin2 = 0.0;
3483     FOR_ATOMS_OF_MOL (a, _mol) {
3484       velocityIdx = (a->GetIdx() - 1) * 3;
3485 
3486       velocity = sqrt( _velocityPtr[velocityIdx] * _velocityPtr[velocityIdx] +
3487                        _velocityPtr[velocityIdx+1] * _velocityPtr[velocityIdx+1] +
3488                        _velocityPtr[velocityIdx+2] * _velocityPtr[velocityIdx+2] );
3489 
3490       E_kin2 += 1000 * a->GetAtomicMass() * velocity * velocity;
3491     }
3492     //cout << "E_{kin} = sum( m_i * v_i^2 ) = " << E_kin2 << endl;
3493 
3494     // correct
3495     factor = sqrt(E_kin / E_kin2);
3496     FOR_ATOMS_OF_MOL (a, _mol) {
3497       velocityIdx = (a->GetIdx() - 1) * 3;
3498       _velocityPtr[velocityIdx] *= factor;
3499       _velocityPtr[velocityIdx+1] *= factor;
3500       _velocityPtr[velocityIdx+2] *= factor;
3501     }
3502 
3503     // E_kin = 0.5 * sum( m_i * v_i^2 )
3504     E_kin2 = 0.0;
3505     FOR_ATOMS_OF_MOL (a, _mol) {
3506       velocityIdx = (a->GetIdx() - 1) * 3;
3507 
3508       velocity = sqrt( _velocityPtr[velocityIdx] * _velocityPtr[velocityIdx] +
3509                        _velocityPtr[velocityIdx+1] * _velocityPtr[velocityIdx+1] +
3510                        _velocityPtr[velocityIdx+2] * _velocityPtr[velocityIdx+2] );
3511 
3512       E_kin2 += 1000 * a->GetAtomicMass() * velocity * velocity;
3513     }
3514     //cout << "E_{kin_corr} = sum( m_i * v_i^2 ) = " << E_kin2 << endl;
3515   }
3516 
MolecularDynamicsTakeNSteps(int n,double T,double timestep,int method)3517   void OBForceField::MolecularDynamicsTakeNSteps(int n, double T, double timestep, int method)
3518   {
3519     if (!_validSetup)
3520       return;
3521 
3522     int coordIdx;
3523     double timestep2;
3524     vector3 force, pos, accel;
3525     _timestep = timestep;
3526     _temp = T;
3527 
3528 
3529     timestep2 = 0.5 * _timestep * _timestep;
3530 
3531     if (!_velocityPtr)
3532       GenerateVelocities();
3533     Energy(true); // compute gradients
3534 
3535     for (int i = 1; i <= n; i++) {
3536       FOR_ATOMS_OF_MOL (a, _mol) {
3537         if (!_constraints.IsFixed(a->GetIdx()) || (_fixAtom == a->GetIdx()) || (_ignoreAtom == a->GetIdx())) {
3538           if (HasAnalyticalGradients())
3539             force = GetGradient(&*a) + _constraints.GetGradient(a->GetIdx());
3540           else
3541             force = NumericalDerivative(&*a) + _constraints.GetGradient(a->GetIdx());
3542 
3543           pos = a->GetVector();
3544           coordIdx = (a->GetIdx() - 1) * 3;
3545 
3546           // a(i) = F(i) / m
3547           accel = force / (1000 * a->GetAtomicMass());
3548 
3549           // x(i+1) = x(i) + v(i) /\t + 0.5 a(i) /\t^2
3550           pos.SetX(pos.x() + _velocityPtr[coordIdx]   * _timestep + accel.x() * timestep2);
3551           pos.SetY(pos.y() + _velocityPtr[coordIdx+1] * _timestep + accel.y() * timestep2);
3552           pos.SetZ(pos.z() + _velocityPtr[coordIdx+2] * _timestep + accel.z() * timestep2);
3553           a->SetVector(pos);
3554 
3555           // v(i+.5) = v(i) + 0.5 a(i) /\t
3556           _velocityPtr[coordIdx]   += 0.5 * accel.x() * _timestep;
3557           _velocityPtr[coordIdx+1] += 0.5 * accel.y() * _timestep;
3558           _velocityPtr[coordIdx+2] += 0.5 * accel.z() * _timestep;
3559 
3560           Energy(true); // compute gradients
3561 
3562           if (HasAnalyticalGradients())
3563             force = GetGradient(&*a) + _constraints.GetGradient(a->GetIdx());
3564           else
3565             force = NumericalDerivative(&*a) + _constraints.GetGradient(a->GetIdx());
3566 
3567           // a(i+1) = F(i+1) / m
3568           accel = force / (1000 * a->GetAtomicMass());
3569 
3570           _velocityPtr[coordIdx]   += 0.5 * accel.x() * _timestep;
3571           _velocityPtr[coordIdx+1] += 0.5 * accel.y() * _timestep;
3572           _velocityPtr[coordIdx+2] += 0.5 * accel.z() * _timestep;
3573 
3574         }
3575       }
3576       if (i % 10 == 0)
3577         CorrectVelocities();
3578     }
3579   }
3580 
3581   //////////////////////////////////////////////////////////////////////////////////
3582   //
3583   // Vector analyse
3584   //
3585   //////////////////////////////////////////////////////////////////////////////////
3586 
VectorLengthDerivative(vector3 & a,vector3 & b)3587   double OBForceField::VectorLengthDerivative(vector3 &a, vector3 &b)
3588   {
3589     vector3 vab, drab;
3590     double rab;
3591 
3592     vab = a - b;
3593     rab = vab.length();
3594     if (rab < 0.1) // atoms are too close to each other
3595       {
3596         vab.randomUnitVector();
3597         vab *= 0.1; // move the atoms a small, random distance apart
3598         rab = 0.1;
3599       }
3600     drab = vab / rab;
3601 
3602     a = -drab; // -drab/da
3603     b =  drab; // -drab/db
3604 
3605     return rab;
3606   }
3607 
VectorBondDerivative(double * pos_i,double * pos_j,double * force_i,double * force_j)3608   double OBForceField::VectorBondDerivative(double *pos_i, double *pos_j,
3609                                             double *force_i, double *force_j)
3610   {
3611     double ij[3];
3612     VectorSubtract(pos_i, pos_j, ij);
3613 
3614     double rij = VectorLength(ij);
3615     if (rij < 0.1) { // atoms are too close to each other
3616       vector3 vij;
3617       vij.randomUnitVector();
3618       vij *= 0.1; // move the atoms a small, random distance apart
3619       vij.Get(ij);
3620       rij = 0.1;
3621     }
3622     VectorDivide(ij, rij, force_j);
3623     VectorMultiply(force_j, -1.0, force_i);
3624 
3625     return rij;
3626   }
3627 
3628 
VectorDistanceDerivative(const double * const pos_i,const double * const pos_j,double * force_i,double * force_j)3629   double OBForceField::VectorDistanceDerivative(const double* const pos_i, const double* const pos_j,
3630                                                 double *force_i, double *force_j)
3631   {
3632     VectorSubtract(pos_i, pos_j, force_j);
3633     const double rij = VectorLength(force_j);
3634     const double inverse_rij = 1.0 / rij;
3635     VectorSelfMultiply(force_j, inverse_rij);
3636     VectorMultiply(force_j, -1.0, force_i);
3637     return rij;
3638   }
3639 
VectorAngleDerivative(vector3 & i,vector3 & j,vector3 & k)3640   double OBForceField::VectorAngleDerivative(vector3 &i, vector3 &j, vector3 &k)
3641   {
3642     // This is adapted from http://scidok.sulb.uni-saarland.de/volltexte/2007/1325/pdf/Dissertation_1544_Moll_Andr_2007.pdf
3643     // Many thanks to Andreas Moll and the BALLView developers for this
3644 
3645     vector3 v1, v2;
3646     vector3 n1, n2;
3647 
3648     // Calculate the vector between atom1 and atom2,
3649     // test if the vector has length larger than 0 and normalize it
3650     v1 = i - j;
3651     v2 = k - j;
3652 
3653     double length1 = v1.length();
3654     double length2 = v2.length();
3655 
3656     // test if the vector has length larger than 0 and normalize it
3657     if (IsNearZero(length1) || IsNearZero(length2)) {
3658       i = VZero;
3659       j = VZero;
3660       k = VZero;
3661       return 0.0;
3662     }
3663 
3664     // Calculate the normalized bond vectors
3665     double inverse_length_v1 = 1.0 / length1;
3666     double inverse_length_v2 = 1.0 / length2;
3667     v1 *= inverse_length_v1 ;
3668     v2 *= inverse_length_v2;
3669 
3670     // Calculate the cross product of v1 and v2, test if it has length unequal 0,
3671     // and normalize it.
3672     vector3 c1 = cross(v1, v2);
3673     double length = c1.length();
3674     if (IsNearZero(length)) {
3675       i = VZero;
3676       j = VZero;
3677       k = VZero;
3678       return 0.0;
3679     }
3680 
3681     c1 /= length;
3682 
3683     // Calculate the cos of theta and then theta
3684     double costheta = dot(v1, v2);
3685     double theta;
3686     if (costheta > 1.0) {
3687       theta = 0.0;
3688       costheta = 1.0;
3689     } else if (costheta < -1.0) {
3690       theta = 180.0;
3691       costheta = -1.0;
3692     } else {
3693       theta = RAD_TO_DEG * acos(costheta);
3694     }
3695 
3696     vector3 t1 = cross(v1, c1);
3697     t1.normalize();
3698     vector3 t2 = cross(v2, c1);
3699     t2.normalize();
3700 
3701     i = -t1 * inverse_length_v1;
3702     k =  t2 * inverse_length_v2;
3703     j = - (i + k);
3704 
3705     return theta;
3706   }
3707 
VectorAngleDerivative(double * pos_i,double * pos_j,double * pos_k,double * force_i,double * force_j,double * force_k)3708   double OBForceField::VectorAngleDerivative(double *pos_i, double *pos_j, double *pos_k,
3709                                              double *force_i, double *force_j, double *force_k)
3710   {
3711     // This is adapted from http://scidok.sulb.uni-saarland.de/volltexte/2007/1325/pdf/Dissertation_1544_Moll_Andr_2007.pdf
3712     // Many thanks to Andreas Moll and the BALLView developers for this
3713 
3714     // Bond vectors of the three atoms
3715     double ij[3], jk[3];
3716     VectorSubtract(pos_i, pos_j, ij);
3717     VectorSubtract(pos_k, pos_j, jk);
3718 
3719     // length of the two bonds
3720     double l_ij, l_jk;
3721     l_ij = VectorLength(ij);
3722     l_jk = VectorLength(jk);
3723 
3724     if (IsNearZero(l_ij) || IsNearZero(l_jk)) {
3725       VectorClear(force_i);
3726       VectorClear(force_j);
3727       VectorClear(force_k);
3728       return 0.0;
3729     }
3730 
3731     // normalize the bond vectors:
3732     VectorDivide (ij, l_ij, ij);
3733     VectorDivide (jk, l_jk, jk);
3734 
3735     // Calculate the cross product of v1 and v2, test if it has length unequal 0,
3736     // and normalize it.
3737     double c1[3];
3738     VectorCross(ij, jk, c1);
3739     double length = VectorLength(c1);
3740     if (IsNearZero(length)) {
3741       VectorClear(force_i);
3742       VectorClear(force_j);
3743       VectorClear(force_k);
3744       return 0.0;
3745     }
3746 
3747     VectorDivide(c1, length, c1);
3748 
3749     // Calculate the cos of theta and then theta
3750     double cos_ijk = VectorDot(ij, jk);
3751     double angle_ijk;
3752     if (cos_ijk > 1.0) {
3753       angle_ijk = 0.0;
3754       cos_ijk = 1.0;
3755     } else if (cos_ijk < -1.0) {
3756       angle_ijk = 180.0;
3757       cos_ijk = -1.0;
3758     } else {
3759       angle_ijk = RAD_TO_DEG * acos(cos_ijk);
3760     }
3761 
3762     double t1[3], t2[3];
3763 
3764     VectorCross(ij, c1, t1);
3765     VectorNormalize(t1);
3766 
3767     VectorCross(jk, c1, t2);
3768     VectorNormalize(t2);
3769 
3770     VectorDivide(t1, -l_ij, force_i);
3771     VectorDivide(t2,  l_jk, force_k);
3772 
3773     VectorAdd(force_i, force_k, force_j);
3774     VectorSelfMultiply(force_j, -1.0);
3775 
3776     return angle_ijk;
3777   }
3778 
VectorAngle(double * pos_i,double * pos_j,double * pos_k)3779   double OBForceField::VectorAngle(double *pos_i, double *pos_j, double *pos_k)
3780   {
3781     // This is adapted from http://scidok.sulb.uni-saarland.de/volltexte/2007/1325/pdf/Dissertation_1544_Moll_Andr_2007.pdf
3782     // Many thanks to Andreas Moll and the BALLView developers for this
3783 
3784     // Bond vectors of the three atoms
3785     double ij[3], jk[3];
3786     VectorSubtract(pos_i, pos_j, ij);
3787     VectorSubtract(pos_k, pos_j, jk);
3788 
3789     // length of the two bonds
3790     double l_ij, l_jk;
3791     l_ij = VectorLength(ij);
3792     l_jk = VectorLength(jk);
3793 
3794     if (IsNearZero(l_ij) || IsNearZero(l_jk)) {
3795       return 0.0;
3796     }
3797 
3798     // normalize the bond vectors:
3799     VectorDivide (ij, l_ij, ij);
3800     VectorDivide (jk, l_jk, jk);
3801 
3802     // Calculate the cross product of v1 and v2, test if it has length unequal 0,
3803     // and normalize it.
3804     double c1[3];
3805     VectorCross(ij, jk, c1);
3806     double length = VectorLength(c1);
3807     if (IsNearZero(length)) {
3808       return 0.0;
3809     }
3810 
3811     // Calculate the cos of theta and then theta
3812     double cos_ijk = VectorDot(ij, jk);
3813     double angle_ijk;
3814     if (cos_ijk > 1.0) {
3815       angle_ijk = 0.0;
3816       cos_ijk = 1.0;
3817     } else if (cos_ijk < -1.0) {
3818       angle_ijk = 180.0;
3819       cos_ijk = -1.0;
3820     } else {
3821       angle_ijk = RAD_TO_DEG * acos(cos_ijk);
3822     }
3823 
3824     return angle_ijk;
3825   }
3826 
VectorOOPDerivative(vector3 & i,vector3 & j,vector3 & k,vector3 & l)3827   double OBForceField::VectorOOPDerivative(vector3 &i, vector3 &j, vector3 &k, vector3 &l)
3828   {
3829     // This is adapted from http://scidok.sulb.uni-saarland.de/volltexte/2007/1325/pdf/Dissertation_1544_Moll_Andr_2007.pdf
3830     // Many thanks to Andreas Moll and the BALLView developers for this
3831 
3832     // temp variables:
3833     double length;
3834     vector3 delta;
3835 
3836     // normal vectors of the three planes:
3837     vector3 an, bn, cn;
3838 
3839     // calculate normalized bond vectors from central atom to outer atoms:
3840     delta = i - j;
3841     length = delta.length();
3842     if (IsNearZero(length)) {
3843       i = VZero;
3844       j = VZero;
3845       k = VZero;
3846       l = VZero;
3847       return 0.0;
3848     }
3849     // normalize the bond vector:
3850     delta /= length;
3851     // store the normalized bond vector from central atom to outer atoms:
3852     const vector3 ji = delta;
3853     // store length of this bond:
3854     const double length_ji = length;
3855 
3856     delta = k - j;
3857     length = delta.length();
3858     if (IsNearZero(length)) {
3859       i = VZero;
3860       j = VZero;
3861       k = VZero;
3862       l = VZero;
3863       return 0.0;
3864     }
3865     // normalize the bond vector:
3866     delta /= length;
3867     // store the normalized bond vector from central atom to outer atoms:
3868     const vector3 jk = delta;
3869     // store length of this bond:
3870     const double length_jk = length;
3871 
3872     delta = l - j;
3873     length = delta.length();
3874     if (IsNearZero(length)) {
3875       i = VZero;
3876       j = VZero;
3877       k = VZero;
3878       l = VZero;
3879       return 0.0;
3880     }
3881     // normalize the bond vector:
3882     delta /= length;
3883     // store the normalized bond vector from central atom to outer atoms:
3884     const vector3 jl = delta;
3885     // store length of this bond:
3886     const double length_jl = length;
3887 
3888     // the normal vectors of the three planes:
3889     an = cross(ji, jk);
3890     bn = cross(jk, jl);
3891     cn = cross(jl, ji);
3892 
3893     // Bond angle ji to jk
3894     const double cos_theta = dot(ji, jk);
3895     const double theta = acos(cos_theta);
3896     // If theta equals 180 degree or 0 degree
3897     if (IsNearZero(theta) || IsNearZero(fabs(theta - M_PI))) {
3898       i = VZero;
3899       j = VZero;
3900       k = VZero;
3901       l = VZero;
3902       return 0.0;
3903     }
3904 
3905     const double sin_theta = sin(theta);
3906     const double sin_dl = dot(an, jl) / sin_theta;
3907 
3908     // the wilson angle:
3909     const double dl = asin(sin_dl);
3910 
3911     // In case: wilson angle equals 0 or 180 degree: do nothing
3912     if (IsNearZero(dl) || IsNearZero(fabs(dl - M_PI))) {
3913       i = VZero;
3914       j = VZero;
3915       k = VZero;
3916       l = VZero;
3917       return RAD_TO_DEG * dl;
3918     }
3919 
3920     const double cos_dl = cos(dl);
3921 
3922     // if wilson angle equal 90 degree: abort
3923     if (cos_dl < 0.0001) {
3924       i = VZero;
3925       j = VZero;
3926       k = VZero;
3927       l = VZero;
3928       return RAD_TO_DEG * dl;
3929     }
3930 
3931     l = (an / sin_theta - jl * sin_dl) / length_jl;
3932     i = ((bn + (((-ji + jk * cos_theta) * sin_dl) / sin_theta)) / length_ji) / sin_theta;
3933     k = ((cn + (((-jk + ji * cos_theta) * sin_dl) / sin_theta)) / length_jk) / sin_theta;
3934     j = -(i + k + l);
3935 
3936     return RAD_TO_DEG * dl;
3937   }
3938 
VectorOOPDerivative(double * pos_i,double * pos_j,double * pos_k,double * pos_l,double * force_i,double * force_j,double * force_k,double * force_l)3939   double OBForceField::VectorOOPDerivative(double *pos_i, double *pos_j, double *pos_k, double *pos_l,
3940                                            double *force_i, double *force_j, double *force_k, double *force_l)
3941   {
3942     // This is adapted from http://scidok.sulb.uni-saarland.de/volltexte/2007/1325/pdf/Dissertation_1544_Moll_Andr_2007.pdf
3943     // Many thanks to Andreas Moll and the BALLView developers for this
3944 
3945     // vector lengths of the three bonds:
3946     double ji[3], jk[3], jl[3];
3947     // normal vectors of the three planes:
3948     double an[3], bn[3], cn[3];
3949 
3950     // calculate normalized bond vectors from central atom to outer atoms:
3951     VectorSubtract(pos_i, pos_j, ji);
3952     // store length of this bond:
3953     const double length_ji = VectorLength(ji);
3954     if (IsNearZero(length_ji)) {
3955       VectorClear(force_i);
3956       VectorClear(force_j);
3957       VectorClear(force_k);
3958       VectorClear(force_l);
3959       return 0.0;
3960     }
3961     // store the normalized bond vector from central atom to outer atoms:
3962     // normalize the bond vector:
3963     VectorDivide(ji, length_ji, ji);
3964 
3965     VectorSubtract(pos_k, pos_j, jk);
3966     const double length_jk = VectorLength(jk);
3967     if (IsNearZero(length_jk)) {
3968       VectorClear(force_i);
3969       VectorClear(force_j);
3970       VectorClear(force_k);
3971       VectorClear(force_l);
3972       return 0.0;
3973     }
3974     VectorDivide(jk, length_jk, jk);
3975 
3976     VectorSubtract(pos_l, pos_j, jl);
3977     const double length_jl = VectorLength(jl);
3978     if (IsNearZero(length_jl)) {
3979       VectorClear(force_i);
3980       VectorClear(force_j);
3981       VectorClear(force_k);
3982       VectorClear(force_l);
3983       return 0.0;
3984     }
3985     VectorDivide(jl, length_jl, jl);
3986 
3987     // the normal vectors of the three planes:
3988     VectorCross(ji, jk, an);
3989     VectorCross(jk, jl, bn);
3990     VectorCross(jl, ji, cn);
3991 
3992     // Bond angle ji to jk
3993     const double cos_theta = VectorDot(ji, jk);
3994     const double theta = acos(cos_theta);
3995     // If theta equals 180 degree or 0 degree
3996     if (IsNearZero(theta) || IsNearZero(fabs(theta - M_PI))) {
3997       VectorClear(force_i);
3998       VectorClear(force_j);
3999       VectorClear(force_k);
4000       VectorClear(force_l);
4001       return 0.0;
4002     }
4003 
4004     const double sin_theta = sin(theta);
4005     const double sin_dl = VectorDot(an, jl) / sin_theta;
4006 
4007     // the wilson angle:
4008     const double dl = asin(sin_dl);
4009 
4010     // In case: wilson angle equals 0 or 180 degree: do nothing
4011     if (IsNearZero(dl) || IsNearZero(fabs(dl - M_PI))) {
4012       VectorClear(force_i);
4013       VectorClear(force_j);
4014       VectorClear(force_k);
4015       VectorClear(force_l);
4016       return RAD_TO_DEG * dl;
4017     }
4018 
4019     const double cos_dl = cos(dl);
4020 
4021     // if wilson angle equal 90 degree: abort
4022     if (cos_dl < 0.0001) {
4023       VectorClear(force_i);
4024       VectorClear(force_j);
4025       VectorClear(force_k);
4026       VectorClear(force_l);
4027       return RAD_TO_DEG * dl;
4028     }
4029 
4030     double temp[3];
4031     /* l = (an / sin_theta - jl * sin_dl) / length_jl; */
4032     VectorDivide(an, sin_theta, an);
4033     VectorMultiply(jl, sin_dl, temp);
4034     VectorSubtract(an, temp, force_l);
4035     VectorDivide(force_l, length_jl, force_l);
4036     /* i = ((bn + (((-ji + jk * cos_theta) * sin_dl) / sin_theta)) / length_ji) / sin_theta; */
4037     VectorMultiply(jk, cos_theta, temp);
4038     VectorSubtract(temp, ji, temp);
4039     VectorSelfMultiply(temp, sin_dl);
4040     VectorDivide(temp, sin_theta, temp);
4041     VectorAdd(bn, temp, force_i);
4042     VectorSelfMultiply(force_i, (sin_theta/length_ji));
4043     /* k = ((cn + (((-jk + ji * cos_theta) * sin_dl) / sin_theta)) / length_jk) / sin_theta; */
4044     VectorMultiply(ji, cos_theta, temp);
4045     VectorSubtract(temp, jk, temp);
4046     VectorSelfMultiply(temp, sin_dl);
4047     VectorDivide(temp, sin_theta, temp);
4048     VectorAdd(cn, temp, force_k);
4049     VectorSelfMultiply(force_k, (sin_theta/length_jk));
4050     /* j = -(i + k + l); */
4051     VectorAdd(force_i, force_k, temp);
4052     VectorAdd(force_l, temp, temp);
4053     VectorMultiply(temp, -1.0, force_j);
4054 
4055     return RAD_TO_DEG * dl;
4056   }
4057 
VectorOOP(double * pos_i,double * pos_j,double * pos_k,double * pos_l)4058   double OBForceField::VectorOOP(double *pos_i, double *pos_j, double *pos_k, double *pos_l)
4059   {
4060     // This is adapted from http://scidok.sulb.uni-saarland.de/volltexte/2007/1325/pdf/Dissertation_1544_Moll_Andr_2007.pdf
4061     // Many thanks to Andreas Moll and the BALLView developers for this
4062 
4063     // vector lengths of the three bonds:
4064     double ji[3], jk[3], jl[3];
4065     // normal vectors of the three planes:
4066     double an[3], bn[3], cn[3];
4067 
4068     // calculate normalized bond vectors from central atom to outer atoms:
4069     VectorSubtract(pos_i, pos_j, ji);
4070     // store length of this bond:
4071     const double length_ji = VectorLength(ji);
4072     if (IsNearZero(length_ji)) {
4073       return 0.0;
4074     }
4075     // store the normalized bond vector from central atom to outer atoms:
4076     // normalize the bond vector:
4077     VectorDivide(ji, length_ji, ji);
4078 
4079     VectorSubtract(pos_k, pos_j, jk);
4080     const double length_jk = VectorLength(jk);
4081     if (IsNearZero(length_jk)) {
4082       return 0.0;
4083     }
4084     VectorDivide(jk, length_jk, jk);
4085 
4086     VectorSubtract(pos_l, pos_j, jl);
4087     const double length_jl = VectorLength(jl);
4088     if (IsNearZero(length_jl)) {
4089       return 0.0;
4090     }
4091     VectorDivide(jl, length_jl, jl);
4092 
4093     // the normal vectors of the three planes:
4094     VectorCross(ji, jk, an);
4095     VectorCross(jk, jl, bn);
4096     VectorCross(jl, ji, cn);
4097 
4098     // Bond angle ji to jk
4099     const double cos_theta = VectorDot(ji, jk);
4100     const double theta = acos(cos_theta);
4101     // If theta equals 180 degree or 0 degree
4102     if (IsNearZero(theta) || IsNearZero(fabs(theta - M_PI))) {
4103       return 0.0;
4104     }
4105 
4106     const double sin_theta = sin(theta);
4107     const double sin_dl = VectorDot(an, jl) / sin_theta;
4108 
4109     // the wilson angle:
4110     const double dl = asin(sin_dl);
4111 
4112     return RAD_TO_DEG * dl;
4113   }
4114 
VectorTorsionDerivative(vector3 & i,vector3 & j,vector3 & k,vector3 & l)4115   double OBForceField::VectorTorsionDerivative(vector3 &i, vector3 &j, vector3 &k, vector3 &l)
4116   {
4117     // This is adapted from http://scidok.sulb.uni-saarland.de/volltexte/2007/1325/pdf/Dissertation_1544_Moll_Andr_2007.pdf
4118     // Many thanks to Andreas Moll and the BALLView developers for this
4119 
4120     // Bond vectors of the three atoms
4121     vector3 ij, jk, kl;
4122     // length of the three bonds
4123     double l_ij, l_jk, l_kl;
4124     // angle between ijk and jkl:
4125     double angle_ijk, angle_jkl;
4126 
4127     ij = j - i;
4128     jk = k - j;
4129     kl = l - k;
4130 
4131     l_ij = ij.length();
4132     l_jk = jk.length();
4133     l_kl = kl.length();
4134 
4135     if (IsNearZero(l_ij) || IsNearZero(l_jk) || IsNearZero(l_kl) ) {
4136       i = VZero;
4137       j = VZero;
4138       k = VZero;
4139       l = VZero;
4140       return 0.0;
4141     }
4142 
4143     angle_ijk = DEG_TO_RAD * vectorAngle(ij, jk);
4144     angle_jkl = DEG_TO_RAD * vectorAngle(jk, kl);
4145 
4146     // normalize the bond vectors:
4147     ij /= l_ij;
4148     jk /= l_jk;
4149     kl /= l_kl;
4150 
4151     double sin_j = sin(angle_ijk);
4152     double sin_k = sin(angle_jkl);
4153 
4154     double rsj = l_ij * sin_j;
4155     double rsk = l_kl * sin_k;
4156 
4157     double rs2j = 1. / (rsj * sin_j);
4158     double rs2k = 1. / (rsk * sin_k);
4159 
4160     double rrj = l_ij / l_jk;
4161     double rrk = l_kl / l_jk;
4162 
4163     double rrcj = rrj * (-cos(angle_ijk));
4164     double rrck = rrk * (-cos(angle_jkl));
4165 
4166     vector3 a = cross(ij, jk);
4167     vector3 b = cross(jk, kl);
4168     vector3 c = cross(a, b);
4169     double d1 = dot(c, jk);
4170     double d2 = dot(a, b);
4171     double tor = RAD_TO_DEG * atan2(d1, d2);
4172 
4173     i = -a * rs2j;
4174     l = b * rs2k;
4175 
4176     j = i * (rrcj - 1.) - l * rrck;
4177     k = -(i + j + l);
4178 
4179     return tor;
4180   }
4181 
VectorTorsionDerivative(double * pos_i,double * pos_j,double * pos_k,double * pos_l,double * force_i,double * force_j,double * force_k,double * force_l)4182   double OBForceField::VectorTorsionDerivative(double *pos_i, double *pos_j, double *pos_k, double *pos_l,
4183                                                double *force_i, double *force_j, double *force_k, double *force_l)
4184   {
4185     // This is adapted from http://scidok.sulb.uni-saarland.de/volltexte/2007/1325/pdf/Dissertation_1544_Moll_Andr_2007.pdf
4186     // Many thanks to Andreas Moll and the BALLView developers for this
4187 
4188     // Bond vectors of the three atoms
4189     double ij[3], jk[3], kl[3];
4190     VectorSubtract(pos_j, pos_i, ij);
4191     VectorSubtract(pos_k, pos_j, jk);
4192     VectorSubtract(pos_l, pos_k, kl);
4193 
4194     // length of the three bonds
4195     double l_ij, l_jk, l_kl;
4196     l_ij = VectorLength(ij);
4197     l_jk = VectorLength(jk);
4198     l_kl = VectorLength(kl);
4199 
4200     if (IsNearZero(l_ij) || IsNearZero(l_jk) || IsNearZero(l_kl) ) {
4201       VectorClear(force_i);
4202       VectorClear(force_j);
4203       VectorClear(force_k);
4204       VectorClear(force_l);
4205       return 0.0;
4206     }
4207 
4208     // normalize the bond vectors:
4209     VectorDivide (ij, l_ij, ij);
4210     VectorDivide (jk, l_jk, jk);
4211     VectorDivide (kl, l_kl, kl);
4212 
4213     // angle between ijk and jkl:
4214     double angle_ijk, angle_jkl;
4215 
4216     double cos_ijk = VectorDot(ij, jk);
4217     if (cos_ijk > 1.0) {
4218       angle_ijk = 0.0;
4219       cos_ijk = 1.0;
4220     } else if (cos_ijk < -1.0) {
4221       angle_ijk = M_PI;
4222       cos_ijk = -1.0;
4223     } else {
4224       angle_ijk = acos(cos_ijk);
4225     }
4226 
4227     double cos_jkl = VectorDot(jk, kl);
4228     if (cos_jkl > 1.0) {
4229       angle_jkl = 0.0;
4230       cos_jkl = 1.0;
4231     } else if (cos_jkl < -1.0) {
4232       angle_jkl = M_PI;
4233       cos_jkl = -1.0;
4234     } else {
4235       angle_jkl = acos(cos_jkl);
4236     }
4237 
4238     double sin_j = sin(angle_ijk);
4239     double sin_k = sin(angle_jkl);
4240 
4241     double rsj = l_ij * sin_j;
4242     double rsk = l_kl * sin_k;
4243 
4244     double rs2j = 1. / (rsj * sin_j);
4245     double rs2k = 1. / (rsk * sin_k);
4246 
4247     double rrj = l_ij / l_jk;
4248     double rrk = l_kl / l_jk;
4249 
4250     double rrcj = rrj * (-cos(angle_ijk));
4251     double rrck = rrk * (-cos(angle_jkl));
4252 
4253     double a[3];
4254     VectorCross(ij, jk, a);
4255     double b[3];
4256     VectorCross(jk, kl, b);
4257     double c[3];
4258     VectorCross(a, b, c);
4259     double d1 = VectorDot(c, jk);
4260     double d2 = VectorDot(a, b);
4261     double tor = RAD_TO_DEG * atan2(d1, d2);
4262 
4263     VectorMultiply(a, -rs2j, force_i);
4264     VectorMultiply(b, rs2k, force_l);
4265 
4266     VectorMultiply(force_i, (rrcj - 1.0), a);
4267     VectorMultiply(force_l, rrck, b);
4268     VectorSubtract(a, b, force_j);
4269 
4270     VectorAdd(force_i, force_j, a);
4271     VectorAdd(a, force_l, b);
4272     VectorMultiply(b, -1.0, force_k);
4273 
4274     return tor;
4275   }
4276 
VectorTorsion(double * pos_i,double * pos_j,double * pos_k,double * pos_l)4277   double OBForceField::VectorTorsion(double *pos_i, double *pos_j, double *pos_k, double *pos_l)
4278   {
4279     // Bond vectors of the three atoms
4280     double ij[3], jk[3], kl[3];
4281     VectorSubtract(pos_j, pos_i, ij);
4282     VectorSubtract(pos_k, pos_j, jk);
4283     VectorSubtract(pos_l, pos_k, kl);
4284 
4285     // length of the three bonds
4286     const double l_ij = VectorLength(ij);
4287     const double l_jk = VectorLength(jk);
4288     const double l_kl = VectorLength(kl);
4289 
4290     if (IsNearZero(l_ij) || IsNearZero(l_jk) || IsNearZero(l_kl) ) {
4291       return 0.0;
4292     }
4293 
4294     // normalize the bond vectors:
4295     VectorDivide (ij, l_ij, ij);
4296     VectorDivide (jk, l_jk, jk);
4297     VectorDivide (kl, l_kl, kl);
4298 
4299     double a[3];
4300     VectorCross(ij, jk, a);
4301     double b[3];
4302     VectorCross(jk, kl, b);
4303     double c[3];
4304     VectorCross(a, b, c);
4305     double d1 = VectorDot(c, jk);
4306     double d2 = VectorDot(a, b);
4307     double tor = RAD_TO_DEG * atan2(d1, d2);
4308 
4309     return tor;
4310   }
4311 
IsInSameRing(OBAtom * a,OBAtom * b)4312   bool OBForceField::IsInSameRing(OBAtom* a, OBAtom* b)
4313   {
4314     bool a_in, b_in;
4315     vector<OBRing*> vr;
4316     vr = _mol.GetSSSR();
4317 
4318     vector<OBRing*>::iterator i;
4319     vector<int>::iterator j;
4320 
4321     for (i = vr.begin();i != vr.end();++i) {
4322       a_in = false;
4323       b_in = false;
4324       for(j = (*i)->_path.begin();j != (*i)->_path.end();++j) {
4325         if ((unsigned)(*j) == a->GetIdx())
4326           a_in = true;
4327         if ((unsigned)(*j) == b->GetIdx())
4328           b_in = true;
4329       }
4330 
4331       if (a_in && b_in)
4332         return true;
4333     }
4334 
4335     return false;
4336   }
4337 
GetGrid(double step,double padding,const char * type,double pchg)4338   OBGridData* OBForceField::GetGrid(double step, double padding, const char* type, double pchg)
4339   {
4340     cout << "OBForceFieldMMFF94::GetGrid(" << step << ", " << type << ")" << endl;
4341     OBFloatGrid fgrid;
4342     fgrid.Init(_mol, step, padding);
4343     vector3 min;
4344     unsigned int xDim, yDim, zDim, xyzDim;
4345 
4346     min = fgrid.GetMin();
4347 
4348     xDim = fgrid.GetXdim();
4349     yDim = fgrid.GetYdim();
4350     zDim = fgrid.GetZdim();
4351     xyzDim = xDim * yDim * zDim;
4352 
4353     cout << "xDim = " << xDim << ", yDim = " << yDim << ", zDim = " << zDim << endl;
4354 
4355     // Add the probe atom
4356     _mol.BeginModify();
4357     OBAtom *atom = _mol.NewAtom();
4358     int index = atom->GetIdx();
4359     _mol.EndModify();
4360     SetTypes();
4361     atom->SetType(type);
4362     atom->SetPartialCharge(pchg);
4363 
4364     SetupCalculations();
4365 
4366     atom = _mol.GetAtom(index);
4367     double *pos = atom->GetCoordinate();
4368 
4369     vector3 coord;
4370     double evdw, eele;
4371     double distance, minDistance;
4372 
4373     OBGridData *grid = new OBGridData;
4374     vector3 xAxis, yAxis, zAxis;
4375     xAxis = vector3(step, 0.0, 0.0);
4376     yAxis = vector3(0.0, step, 0.0);
4377     zAxis = vector3(0.0, 0.0, step);
4378 
4379     grid->SetNumberOfPoints(xDim, yDim, zDim);
4380     grid->SetLimits(min, xAxis, yAxis, zAxis);
4381 
4382     // VDW surface
4383     for (unsigned int i = 0; i < xDim; ++i) {
4384       coord.SetX(min[0] + i * step);
4385       for (unsigned int j = 0; j < yDim; ++j) {
4386         coord.SetY(min[1] + j * step);
4387         for (unsigned int k = 0; k < zDim; ++k)
4388           {
4389             coord.SetZ(min[2] + k * step);
4390             minDistance = 1.0E+10;
4391             FOR_ATOMS_OF_MOL (a, _mol) {
4392               if (a->GetIdx() == atom->GetIdx())
4393                 continue;
4394               if (a->GetAtomicNum() == OBElements::Hydrogen)
4395                 continue;
4396 
4397               distance = sqrt(coord.distSq(a->GetVector()));
4398 
4399               if (distance < minDistance)
4400                 minDistance = distance;
4401             } // end checking atoms
4402             // negative = away from molecule, 0 = vdw surface, positive = inside
4403             if (minDistance > 1.0) {
4404               grid->SetValue(i, j, k, 0.0); // outside the molecule
4405             } else {
4406               grid->SetValue(i, j, k, 10e99); // inside the molecule
4407             }
4408           } // z-axis
4409       } // y-axis
4410     } // x-axis
4411 
4412 
4413     unsigned int count = 0;
4414     for (unsigned int i = 0; i < xDim; ++i) {
4415       coord.SetX(min[0] + i * step);
4416       for (unsigned int j = 0; j < yDim; ++j) {
4417         coord.SetY(min[1] + j * step);
4418         for (unsigned int k = 0; k < zDim; ++k)
4419           {
4420             coord.SetZ(min[2] + k * step);
4421 
4422             count++;
4423             cout << "\r" << count << "/" << xyzDim;
4424 
4425             if (grid->GetValue(i, j, k) == 0.0) {
4426               pos[0] = coord.x();
4427               pos[1] = coord.y();
4428               pos[2] = coord.z();
4429               evdw = E_VDW(false);
4430               eele = E_Electrostatic(false);
4431               grid->SetValue(i, j, k, evdw + eele);
4432             }
4433           } // z-axis
4434       } // y-axis
4435     } // x-axis
4436 
4437     cout << endl;
4438 
4439     _mol.BeginModify();
4440     _mol.DeleteAtom(atom);
4441     _mol.EndModify();
4442 
4443     return grid;
4444   }
4445 
4446   /**
4447    * @example obforcefield_energy.cpp
4448    * Example showing how to compute the enrgy for a molecule.
4449    */
4450 
4451 } // end namespace OpenBabel
4452 
4453 
4454 //! \file forcefield.cpp
4455 //! \brief Handle OBForceField class
4456