Coverage Report

Created: 2026-10-17 06:33

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