Coverage Report

Created: 2025-11-11 06:41

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/openbabel/src/rotor.cpp
Line
Count
Source
1
/**********************************************************************
2
rotor.cpp - Rotate dihedral angles according to rotor rules.
3
4
Copyright (C) 1998-2000 by OpenEye Scientific Software, Inc.
5
Some portions Copyright (C) 2001-2006 by Geoffrey R. 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 <openbabel/mol.h>
23
#include <openbabel/bond.h>
24
#include <openbabel/ring.h>
25
#include <openbabel/obiter.h>
26
#include <openbabel/oberror.h>
27
#include <openbabel/rotor.h>
28
#include <openbabel/graphsym.h>
29
#include <openbabel/elements.h>
30
31
#include <set>
32
#include <cassert>
33
34
// private data headers with default parameters
35
#include "torlib.h"
36
37
#ifndef M_PI
38
#define M_PI 3.14159265358979323846
39
#endif
40
41
42
using namespace std;
43
namespace OpenBabel
44
{
45
46
  //! Default step resolution for a dihedral angle (in degrees)
47
0
#define OB_DEFAULT_DELTA 15.0
48
  static bool GetDFFVector(OBMol&,vector<int>&,OBBitVec&);
49
  static bool CompareRotor(const pair<OBBond*,int>&,const pair<OBBond*,int>&);
50
51
52
  //**************************************
53
  //**** OBRotorList Member Functions ****
54
  //**************************************
55
56
  bool OBRotorList::Setup(OBMol &mol, bool sampleRingBonds)
57
0
  {
58
0
    Clear();
59
    // find the rotatable bonds
60
0
    FindRotors(mol, sampleRingBonds);
61
0
    if (!Size())
62
0
      return(false);
63
64
    // set the atoms that should be evaluated when this rotor changes
65
0
    SetEvalAtoms(mol);
66
0
    AssignTorVals(mol);
67
68
0
    OBRotor *rotor;
69
0
    vector<OBRotor*>::iterator i;
70
0
    for (rotor = BeginRotor(i);rotor;rotor = NextRotor(i))
71
0
      if (!rotor->Size())
72
0
        {
73
0
          int ref[4];
74
0
          rotor->GetDihedralAtoms(ref);
75
0
          char buffer[BUFF_SIZE];
76
0
          snprintf(buffer, BUFF_SIZE, "The rotor has no associated torsion values -> %d %d %d %d",
77
0
      ref[0],ref[1],ref[2],ref[3]);
78
0
          obErrorLog.ThrowError(__FUNCTION__, buffer, obDebug);
79
0
        }
80
81
    // Reduce the number of torsions to be checked through symmetry considerations
82
0
    if (_removesym)
83
0
      RemoveSymVals(mol);
84
85
0
    return(true);
86
0
  }
87
88
  bool OBRotorList::FindRotors(OBMol &mol, bool sampleRingBonds)
89
0
  {
90
    // Find ring atoms & bonds
91
    // This function will set OBBond::IsRotor().
92
0
    mol.FindRingAtomsAndBonds();
93
94
0
    obErrorLog.ThrowError(__FUNCTION__,
95
0
                          "Ran OpenBabel::FindRotors", obAuditMsg);
96
97
    //
98
    // Score the bonds using the graph theoretical distance (GTD).
99
    // The GTD is the distance from atom i to every other atom j.
100
    // Atoms on the "inside" of the molecule will have a lower GTD
101
    // value than atoms on the "outside"
102
    //
103
    // The scoring will rank "inside" bonds first.
104
    //
105
0
    vector<int> gtd;
106
0
    mol.GetGTDVector(gtd);
107
    // compute the scores
108
0
    vector<OBBond*>::iterator i;
109
0
    vector<pair<OBBond*,int> > vtmp;
110
0
    for (OBBond *bond = mol.BeginBond(i);bond;bond = mol.NextBond(i)) {
111
      // check if the bond is "rotatable"
112
0
      if (bond->IsRotor(sampleRingBonds)) {
113
        // check if the bond is fixed (using deprecated fixed atoms or new fixed bonds)
114
0
        if ((HasFixedAtoms() || HasFixedBonds()) && IsFixedBond(bond))
115
0
          continue;
116
117
0
        if (bond->IsInRing()) {
118
          //otherwise mark that we have them and add it to the pile
119
0
          _ringRotors = true;
120
0
        }
121
122
0
        int score = gtd[bond->GetBeginAtomIdx()-1] + gtd[bond->GetEndAtomIdx()-1];
123
        // compute the GTD bond score as sum of atom GTD scores
124
0
        vtmp.push_back(pair<OBBond*,int> (bond,score));
125
0
      }
126
0
    }
127
128
    // sort the rotatable bonds by GTD score
129
0
    sort(vtmp.begin(),vtmp.end(),CompareRotor);
130
131
    // create rotors for the bonds
132
0
    int count = 0;
133
0
    vector<pair<OBBond*,int> >::iterator j;
134
0
    for (j = vtmp.begin(); j != vtmp.end(); ++j, ++count) {
135
0
      OBRotor *rotor = new OBRotor;
136
0
      rotor->SetBond((*j).first);
137
0
      rotor->SetIdx(count);
138
0
      rotor->SetNumCoords(mol.NumAtoms()*3);
139
0
      _rotor.push_back(rotor);
140
0
    }
141
142
0
    return true;
143
0
  }
144
145
  bool OBRotorList::IsFixedBond(OBBond *bond)
146
0
  {
147
0
    if (_fixedatoms.IsEmpty() && _fixedbonds.IsEmpty())
148
0
      return false;
149
150
    // new fixed bonds
151
0
    if (!_fixedbonds.IsEmpty()) {
152
0
      return _fixedbonds.BitIsSet(bond->GetIdx());
153
0
    }
154
155
0
    if (_fixedatoms.IsEmpty())
156
0
      return false;
157
158
    // deprecated fixed atoms
159
0
    OBAtom *a1,*a2,*a3;
160
0
    vector<OBBond*>::iterator i;
161
162
0
    a1 = bond->GetBeginAtom();
163
0
    a2 = bond->GetEndAtom();
164
0
    if (!_fixedatoms[a1->GetIdx()] || !_fixedatoms[a2->GetIdx()])
165
0
      return(false);
166
167
0
    bool isfixed=false;
168
0
    for (a3 = a1->BeginNbrAtom(i);a3;a3 = a1->NextNbrAtom(i))
169
0
      if (a3 != a2 && _fixedatoms[a3->GetIdx()])
170
0
        {
171
0
          isfixed = true;
172
0
          break;
173
0
        }
174
175
0
    if (!isfixed)
176
0
      return(false);
177
178
0
    isfixed = false;
179
0
    for (a3 = a2->BeginNbrAtom(i);a3;a3 = a2->NextNbrAtom(i))
180
0
      if (a3 != a1 && _fixedatoms[a3->GetIdx()])
181
0
        {
182
0
          isfixed = true;
183
0
          break;
184
0
        }
185
186
0
    return(isfixed);
187
0
  }
188
189
  bool GetDFFVector(OBMol &mol,vector<int> &dffv,OBBitVec &bv)
190
0
  {
191
0
    dffv.clear();
192
0
    dffv.resize(mol.NumAtoms());
193
194
0
    int dffcount,natom;
195
0
    OBBitVec used,curr,next;
196
0
    OBAtom *atom,*atom1;
197
0
    OBBond *bond;
198
0
    vector<OBAtom*>::iterator i;
199
0
    vector<OBBond*>::iterator j;
200
201
0
    next.Clear();
202
203
0
    for (atom = mol.BeginAtom(i);atom;atom = mol.NextAtom(i))
204
0
      {
205
0
        if (bv[atom->GetIdx()])
206
0
          {
207
0
            dffv[atom->GetIdx()-1] = 0;
208
0
            continue;
209
0
          }
210
211
0
        dffcount = 0;
212
0
        used.Clear();
213
0
        curr.Clear();
214
0
        used.SetBitOn(atom->GetIdx());
215
0
        curr.SetBitOn(atom->GetIdx());
216
217
0
        while (!curr.IsEmpty() && (bv&curr).IsEmpty())
218
0
          {
219
0
            next.Clear();
220
0
            for (natom = curr.NextBit(-1);natom != curr.EndBit();natom = curr.NextBit(natom))
221
0
              {
222
0
                atom1 = mol.GetAtom(natom);
223
0
                for (bond = atom1->BeginBond(j);bond;bond = atom1->NextBond(j))
224
0
                  if (!used.BitIsSet(bond->GetNbrAtomIdx(atom1)) &&
225
0
                      !curr.BitIsSet(bond->GetNbrAtomIdx(atom1)))
226
0
                      if (bond->GetNbrAtom(atom1)->GetAtomicNum() != OBElements::Hydrogen)
227
0
                      next.SetBitOn(bond->GetNbrAtomIdx(atom1));
228
0
              }
229
230
0
            used |= next;
231
0
            curr = next;
232
0
            dffcount++;
233
0
          }
234
235
0
        dffv[atom->GetIdx()-1] = dffcount;
236
0
      }
237
238
0
    return(true);
239
0
  }
240
241
  void OBRotorList::RemoveSymVals(OBMol &mol)
242
0
  {
243
0
    OBGraphSym gs(&mol);
244
0
    vector<unsigned int> sym_classes;
245
0
    gs.GetSymmetry(sym_classes);
246
247
0
    OBRotor *rotor;
248
0
    vector<OBRotor*>::iterator i;
249
0
    std::set<unsigned int> syms;
250
0
    for (rotor = BeginRotor(i);rotor;rotor = NextRotor(i)) {
251
0
      OBBond* bond = rotor->GetBond();
252
0
      OBAtom* end = bond->GetEndAtom();
253
0
      OBAtom* begin = bond->GetBeginAtom();
254
0
      int N_fold_symmetry = 1;
255
0
      for (int here=0; here <= 1; ++here) { // Try each side of the bond in turn
256
257
0
        OBAtom *this_side, *other_side;
258
0
        if (here == 0) {
259
0
          this_side = begin; other_side = end;
260
0
        }
261
0
        else {
262
0
          this_side = end; other_side = begin;
263
0
        }
264
265
0
        for (unsigned int hyb=2; hyb<=3; ++hyb) { // sp2 and sp3 carbons, with explicit Hs
266
0
          if (this_side->GetAtomicNum() == 6 && this_side->GetHyb() == hyb && this_side->GetExplicitDegree() == (hyb + 1) ) {
267
0
            syms.clear();
268
0
            FOR_NBORS_OF_ATOM(nbr, this_side) {
269
0
              if ( &(*nbr) == other_side ) continue;
270
0
              syms.insert(sym_classes[nbr->GetIdx() - 1]);
271
0
            }
272
0
            if (syms.size() == 1) // All of the rotated atoms have the same symmetry class
273
0
              N_fold_symmetry *= hyb;
274
0
                      }
275
0
                  }
276
0
              }
277
278
0
      if (N_fold_symmetry  > 1) {
279
0
        size_t old_size = rotor->Size();
280
0
        rotor->RemoveSymTorsionValues(N_fold_symmetry);
281
0
        if (!_quiet) {
282
0
          cout << "...." << N_fold_symmetry << "-fold symmetry at rotor between " <<
283
0
                 begin->GetIdx() << " and " << end->GetIdx();
284
0
          cout << " - reduced from " << old_size << " to " << rotor->Size() << endl;
285
0
                  }
286
0
              }
287
0
      }
288
0
  }
289
290
  bool OBRotorList::SetEvalAtoms(OBMol &mol)
291
0
  {
292
0
    int j;
293
0
    OBBond *bond;
294
0
    OBAtom *a1,*a2;
295
0
    OBRotor *rotor;
296
0
    vector<OBRotor*>::iterator i;
297
0
    OBBitVec eval,curr,next;
298
0
    vector<OBBond*>::iterator k;
299
300
0
    for (rotor = BeginRotor(i);rotor;rotor = NextRotor(i))
301
0
      {
302
0
        bond = rotor->GetBond();
303
0
        curr.Clear();
304
0
        eval.Clear();
305
0
        curr.SetBitOn(bond->GetBeginAtomIdx());
306
0
        curr.SetBitOn(bond->GetEndAtomIdx());
307
0
        eval |= curr;
308
309
        //follow all non-rotor bonds and add atoms to eval list
310
0
        for (;!curr.IsEmpty();)
311
0
          {
312
0
            next.Clear();
313
0
            for (j = curr.NextBit(0);j != curr.EndBit();j = curr.NextBit(j))
314
0
              {
315
0
                a1 = mol.GetAtom(j);
316
0
                for (a2 = a1->BeginNbrAtom(k);a2;a2 = a1->NextNbrAtom(k))
317
0
                  if (!eval[a2->GetIdx()])
318
0
                    if (!((OBBond*)*k)->IsRotor(_ringRotors)||((HasFixedAtoms()||HasFixedBonds())&&IsFixedBond((OBBond*)*k)))
319
0
                      {
320
0
                        next.SetBitOn(a2->GetIdx());
321
0
                        eval.SetBitOn(a2->GetIdx());
322
0
                      }
323
0
              }
324
0
            curr = next;
325
0
          }
326
327
        //add atoms alpha to eval list
328
0
        next.Clear();
329
0
        for (j = eval.NextBit(0);j != eval.EndBit();j = eval.NextBit(j))
330
0
          {
331
0
            a1 = mol.GetAtom(j);
332
0
            for (a2 = a1->BeginNbrAtom(k);a2;a2 = a1->NextNbrAtom(k))
333
0
              next.SetBitOn(a2->GetIdx());
334
0
          }
335
0
        eval |= next;
336
0
        rotor->SetEvalAtoms(eval);
337
0
      }
338
339
0
    return(true);
340
0
  }
341
342
  bool OBRotorList::AssignTorVals(OBMol &mol)
343
0
  {
344
0
    vector<OBRotor*>::iterator i;
345
0
    for (i = _rotor.begin(); i != _rotor.end(); ++i) {
346
0
      OBRotor *rotor = *i;
347
0
      OBBond *bond = rotor->GetBond();
348
349
      // query the rotor database
350
0
      int ref[4];
351
0
      vector<double> angles;
352
0
      double delta;
353
0
      _rr.GetRotorIncrements(mol, bond, ref, angles, delta);
354
0
      rotor->SetTorsionValues(angles);
355
0
      rotor->SetDelta(delta);
356
357
      // Find the smallest set of atoms to rotate. There are two candidate sets,
358
      // one on either side of the bond. If the first tried set size plus one is
359
      // larger than half of the number of atoms, the other set is smaller and the
360
      // rotor atom indexes are inverted (.i.e. a-b-c-d -> d-c-b-a).
361
0
      vector<int> atoms;
362
0
      vector<int>::iterator j;
363
      // find all atoms for which there is a path to ref[2] without goinf through ref[1]
364
0
      mol.FindChildren(atoms, ref[1], ref[2]);
365
0
      if (atoms.size() + 1 > mol.NumAtoms() / 2) {
366
0
        atoms.clear();
367
        // select the other smaller set
368
0
        mol.FindChildren(atoms, ref[2], ref[1]);
369
        // invert the rotor
370
0
        swap(ref[0],ref[3]);
371
0
        swap(ref[1],ref[2]);
372
0
      }
373
374
      // translate the rotate atom indexes to coordinate indexes (i.e. from 0, multiplied by 3)
375
0
      for (j = atoms.begin(); j != atoms.end(); ++j)
376
0
        *j = ((*j) - 1) * 3;
377
      // set the rotate atoms and dihedral atom indexes
378
0
      rotor->SetRotAtoms(atoms);
379
0
      rotor->SetDihedralAtoms(ref);
380
0
    }
381
382
0
    return true;
383
0
  }
384
385
  bool OBRotorList::SetRotAtoms(OBMol &mol)
386
0
  {
387
0
    OBRotor *rotor;
388
0
    vector<int> rotatoms;
389
0
    vector<OBRotor*>::iterator i;
390
391
0
    int ref[4];
392
0
    vector<int>::iterator j;
393
0
    for (rotor = BeginRotor(i);rotor;rotor = NextRotor(i))
394
0
      {
395
0
        rotor->GetDihedralAtoms(ref);
396
397
0
        mol.FindChildren(rotatoms,ref[1],ref[2]);
398
0
        if (rotatoms.size()+1 > mol.NumAtoms()/2)
399
0
          {
400
0
            rotatoms.clear();
401
0
            mol.FindChildren(rotatoms,ref[2],ref[1]);
402
0
            swap(ref[0],ref[3]);
403
0
            swap(ref[1],ref[2]);
404
0
          }
405
406
0
        for (j = rotatoms.begin();j != rotatoms.end();++j)
407
0
          *j = ((*j)-1)*3;
408
0
        rotor->SetRotAtoms(rotatoms);
409
0
        rotor->SetDihedralAtoms(ref);
410
0
      }
411
412
0
    return(true);
413
0
  }
414
415
  void OBRotorList::SetRotAtomsByFix(OBMol &mol)
416
0
  {
417
0
    int ref[4];
418
0
    OBRotor *rotor;
419
0
    vector<int> rotatoms;
420
0
    vector<int>::iterator j;
421
0
    vector<OBRotor*>::iterator i;
422
423
0
    GetDFFVector(mol,_dffv,_fixedatoms);
424
425
0
    for (rotor = BeginRotor(i);rotor;rotor = NextRotor(i))
426
0
      {
427
0
        rotatoms.clear();
428
0
        rotor->GetDihedralAtoms(ref);
429
430
0
        if (_fixedatoms[ref[1]] && _fixedatoms[ref[2]])
431
0
          {
432
0
            if (!_fixedatoms[ref[0]])
433
0
              {
434
0
                swap(ref[0],ref[3]);
435
0
                swap(ref[1],ref[2]);
436
0
                mol.FindChildren(rotatoms,ref[1],ref[2]);
437
0
                for (j = rotatoms.begin();j != rotatoms.end();++j)
438
0
                  *j = ((*j)-1)*3;
439
0
                rotor->SetRotAtoms(rotatoms);
440
0
                rotor->SetDihedralAtoms(ref);
441
0
              }
442
0
          }
443
0
        else
444
0
          if (_dffv[ref[1]-1] > _dffv[ref[2]-1])
445
0
            {
446
0
              swap(ref[0],ref[3]);
447
0
              swap(ref[1],ref[2]);
448
0
              mol.FindChildren(rotatoms,ref[1],ref[2]);
449
0
              for (j = rotatoms.begin();j != rotatoms.end();++j)
450
0
                *j = ((*j)-1)*3;
451
0
              rotor->SetRotAtoms(rotatoms);
452
0
              rotor->SetDihedralAtoms(ref);
453
0
            }
454
0
      }
455
0
  }
456
457
  OBRotorList::OBRotorList()
458
0
  {
459
0
    _rotor.clear();
460
0
    _quiet = true;
461
0
    _removesym = true;
462
0
    _ringRotors = false;
463
0
  }
464
465
  OBRotorList::~OBRotorList()
466
0
  {
467
0
    vector<OBRotor*>::iterator i;
468
0
    for (i = _rotor.begin();i != _rotor.end();++i)
469
0
      delete *i;
470
0
  }
471
472
  void OBRotorList::Clear()
473
0
  {
474
0
    vector<OBRotor*>::iterator i;
475
0
    for (i = _rotor.begin();i != _rotor.end();++i)
476
0
      delete *i;
477
0
    _rotor.clear();
478
0
    _ringRotors = false;
479
    //_fix.Clear();
480
0
  }
481
482
  bool CompareRotor(const pair<OBBond*,int> &a,const pair<OBBond*,int> &b)
483
0
  {
484
    //return(a.second > b.second); //outside->in
485
0
    return(a.second < b.second);   //inside->out
486
0
  }
487
488
  //**********************************
489
  //**** OBRotor Member Functions ****
490
  //**********************************
491
492
  OBRotor::OBRotor()
493
0
  {
494
0
  }
495
496
  void OBRotor::SetRings()
497
0
  {
498
0
    _rings.clear();
499
0
    if (_bond == nullptr)
500
0
      return; // nothing to do
501
502
0
    vector<OBRing*> rlist;
503
0
    vector<OBRing*>::iterator i;
504
505
0
    OBMol *mol = _bond->GetParent();
506
507
0
    if (mol == nullptr)
508
0
      return; // nothing to do
509
510
0
    rlist = mol->GetSSSR();
511
0
    for (i = rlist.begin();i != rlist.end();++i) {
512
0
      if ((*i)->IsMember(_bond))
513
0
        _rings.push_back(*i);
514
0
    }
515
0
  }
516
517
  double OBRotor::CalcTorsion(double *c)
518
0
  {
519
0
    double v1x,v1y,v1z,v2x,v2y,v2z,v3x,v3y,v3z;
520
0
    double c1x,c1y,c1z,c2x,c2y,c2z,c3x,c3y,c3z;
521
0
    double c1mag,c2mag,ang,costheta;
522
523
    //
524
    //calculate the torsion angle
525
    //
526
0
    v1x = c[_torsion[0]] -  c[_torsion[1]];
527
0
    v1y = c[_torsion[0]+1] - c[_torsion[1]+1];
528
0
    v1z = c[_torsion[0]+2] - c[_torsion[1]+2];
529
0
    v2x = c[_torsion[1]] - c[_torsion[2]];
530
0
    v2y = c[_torsion[1]+1] - c[_torsion[2]+1];
531
0
    v2z = c[_torsion[1]+2] - c[_torsion[2]+2];
532
0
    v3x = c[_torsion[2]]   - c[_torsion[3]];
533
0
    v3y = c[_torsion[2]+1] - c[_torsion[3]+1];
534
0
    v3z = c[_torsion[2]+2] - c[_torsion[3]+2];
535
536
0
    c1x = v1y*v2z - v1z*v2y;
537
0
    c2x = v2y*v3z - v2z*v3y;
538
0
    c1y = -v1x*v2z + v1z*v2x;
539
0
    c2y = -v2x*v3z + v2z*v3x;
540
0
    c1z = v1x*v2y - v1y*v2x;
541
0
    c2z = v2x*v3y - v2y*v3x;
542
0
    c3x = c1y*c2z - c1z*c2y;
543
0
    c3y = -c1x*c2z + c1z*c2x;
544
0
    c3z = c1x*c2y - c1y*c2x;
545
546
0
    c1mag = SQUARE(c1x)+SQUARE(c1y)+SQUARE(c1z);
547
0
    c2mag = SQUARE(c2x)+SQUARE(c2y)+SQUARE(c2z);
548
0
    if (c1mag*c2mag < 0.01)
549
0
      costheta = 1.0; //avoid div by zero error
550
0
    else
551
0
      costheta = (c1x*c2x + c1y*c2y + c1z*c2z)/(sqrt(c1mag*c2mag));
552
553
0
    if (costheta < -0.9999999)
554
0
      costheta = -0.9999999;
555
0
    if (costheta >  0.9999999)
556
0
      costheta =  0.9999999;
557
558
0
    if ((v2x*c3x + v2y*c3y + v2z*c3z) > 0.0)
559
0
      ang = -acos(costheta);
560
0
    else
561
0
      ang = acos(costheta);
562
563
0
    return(ang);
564
0
  }
565
566
  double OBRotor::CalcBondLength(double *c)
567
0
  {
568
    // compute the difference
569
0
    double dx, dy, dz;
570
0
    dx = c[_torsion[1]  ] - c[_torsion[2]  ];
571
0
    dy = c[_torsion[1]+1] - c[_torsion[2]+1];
572
0
    dz = c[_torsion[1]+2] - c[_torsion[2]+2];
573
    // compute the length
574
0
    return sqrt(SQUARE(dx) + SQUARE(dy) + SQUARE(dz));
575
0
  }
576
577
578
  void OBRotor::SetRotor(double *c,int idx,int prev)
579
0
  {
580
0
    double ang,sn,cs,t,dx,dy,dz,mag;
581
582
0
    if (prev == -1)
583
0
      ang = _torsionAngles[idx] - CalcTorsion(c);
584
0
    else
585
0
      ang = _torsionAngles[idx] - _torsionAngles[prev];
586
587
0
    sn = sin(ang);
588
0
    cs = cos(ang);
589
0
    t = 1 - cs;
590
591
0
    dx = c[_torsion[1]]   - c[_torsion[2]];
592
0
    dy = c[_torsion[1]+1] - c[_torsion[2]+1];
593
0
    dz = c[_torsion[1]+2] - c[_torsion[2]+2];
594
0
    mag = sqrt(SQUARE(dx) + SQUARE(dy) + SQUARE(dz));
595
596
0
    Set(c,sn,cs,t,1.0/mag);
597
0
  }
598
599
  // used in combination with Set(double *coordinates, int idx)
600
  void OBRotor::Precompute(double *coordinates)
601
0
  {
602
0
    _imag = 1.0 / CalcBondLength(coordinates);
603
0
    _refang = CalcTorsion(coordinates);
604
0
  }
605
606
  // used in combination with Precompute(double *coordinates)
607
  void OBRotor::Set(double *coordinates, int idx)
608
0
  {
609
0
    double ang,sn,cs,t;
610
611
    // compute the rotation angle
612
0
    ang = _torsionAngles[idx] - _refang;
613
    // compute some values for the rotation matrix
614
0
    sn = sin(ang);
615
0
    cs = cos(ang);
616
0
    t = 1 - cs;
617
618
0
    double x,y,z,tx,ty,tz,m[9];
619
620
    // compute the bond vector
621
0
    x = coordinates[_torsion[1]]   - coordinates[_torsion[2]];
622
0
    y = coordinates[_torsion[1]+1] - coordinates[_torsion[2]+1];
623
0
    z = coordinates[_torsion[1]+2] - coordinates[_torsion[2]+2];
624
625
    // normalize the bond vector
626
0
    x *= _imag;
627
0
    y *= _imag;
628
0
    z *= _imag;
629
630
    // set up the rotation matrix
631
0
    tx = t*x;
632
0
    ty = t*y;
633
0
    tz = t*z;
634
0
    m[0]= tx*x + cs;
635
0
    m[1] = tx*y + sn*z;
636
0
    m[2] = tx*z - sn*y;
637
0
    m[3] = tx*y - sn*z;
638
0
    m[4] = ty*y + cs;
639
0
    m[5] = ty*z + sn*x;
640
0
    m[6] = tx*z + sn*y;
641
0
    m[7] = ty*z - sn*x;
642
0
    m[8] = tz*z + cs;
643
644
    //
645
    //now the matrix is set - time to rotate the atoms
646
    //
647
0
    tx = coordinates[_torsion[1]];
648
0
    ty = coordinates[_torsion[1]+1];
649
0
    tz = coordinates[_torsion[1]+2];
650
0
    unsigned int i, j;
651
0
    for (i = 0; i < _rotatoms.size(); ++i)
652
0
      {
653
0
        j = _rotatoms[i];
654
0
        coordinates[j] -= tx;
655
0
        coordinates[j+1] -= ty;
656
0
        coordinates[j+2]-= tz;
657
0
        x = coordinates[j]*m[0] + coordinates[j+1]*m[1] + coordinates[j+2]*m[2];
658
0
        y = coordinates[j]*m[3] + coordinates[j+1]*m[4] + coordinates[j+2]*m[5];
659
0
        z = coordinates[j]*m[6] + coordinates[j+1]*m[7] + coordinates[j+2]*m[8];
660
0
        coordinates[j] = x+tx;
661
0
        coordinates[j+1] = y+ty;
662
0
        coordinates[j+2] = z+tz;
663
0
      }
664
0
  }
665
666
  // used in combination with Set
667
  void OBRotor::Precalc(vector<double*> &cv)
668
0
  {
669
0
    double *c,ang;
670
0
    vector<double*>::iterator i;
671
0
    vector<double>::iterator j;
672
0
    vector<double> cs,sn,t;
673
0
    for (i = cv.begin();i != cv.end();++i)
674
0
      {
675
0
        c = *i;
676
0
        cs.clear();
677
0
        sn.clear();
678
0
        t.clear();
679
0
        ang = CalcTorsion(c);
680
681
0
        for (j = _torsionAngles.begin();j != _torsionAngles.end();++j)
682
0
          {
683
0
            cs.push_back(cos(*j-ang));
684
0
            sn.push_back(sin(*j-ang));
685
0
            t.push_back(1 - cos(*j-ang));
686
0
          }
687
688
0
        _cs.push_back(cs);
689
0
        _sn.push_back(sn);
690
0
        _t.push_back(t);
691
0
        _invmag.push_back(1.0/CalcBondLength(c));
692
0
      }
693
0
  }
694
695
696
  void OBRotor::Set(double *c,double sn,double cs,double t,double invmag)
697
0
  {
698
0
    double x,y,z,tx,ty,tz,m[9];
699
700
0
    x = c[_torsion[1]]   - c[_torsion[2]];
701
0
    y = c[_torsion[1]+1] - c[_torsion[2]+1];
702
0
    z = c[_torsion[1]+2] - c[_torsion[2]+2];
703
704
    //normalize the rotation vector
705
706
0
    x *= invmag;
707
0
    y *= invmag;
708
0
    z *= invmag;
709
710
    //set up the rotation matrix
711
0
    tx = t*x;
712
0
    ty = t*y;
713
0
    tz = t*z;
714
0
    m[0]= tx*x + cs;
715
0
    m[1] = tx*y + sn*z;
716
0
    m[2] = tx*z - sn*y;
717
0
    m[3] = tx*y - sn*z;
718
0
    m[4] = ty*y + cs;
719
0
    m[5] = ty*z + sn*x;
720
0
    m[6] = tx*z + sn*y;
721
0
    m[7] = ty*z - sn*x;
722
0
    m[8] = tz*z + cs;
723
724
    //
725
    //now the matrix is set - time to rotate the atoms
726
    //
727
0
    tx = c[_torsion[1]];
728
0
    ty = c[_torsion[1]+1];
729
0
    tz = c[_torsion[1]+2];
730
0
    unsigned int i, j;
731
0
    for (i = 0; i < _rotatoms.size(); ++i)
732
0
      {
733
0
        j = _rotatoms[i];
734
0
        c[j] -= tx;
735
0
        c[j+1] -= ty;
736
0
        c[j+2]-= tz;
737
0
        x = c[j]*m[0] + c[j+1]*m[1] + c[j+2]*m[2];
738
0
        y = c[j]*m[3] + c[j+1]*m[4] + c[j+2]*m[5];
739
0
        z = c[j]*m[6] + c[j+1]*m[7] + c[j+2]*m[8];
740
0
        c[j] = x+tx;
741
0
        c[j+1] = y+ty;
742
0
        c[j+2] = z+tz;
743
0
      }
744
0
  }
745
746
  //! Remove all torsions angles between 0 and 360/fold
747
  void OBRotor::RemoveSymTorsionValues(int fold)
748
0
  {
749
0
    vector<double>::iterator i;
750
0
    vector<double> tv;
751
0
    if (_torsionAngles.size() == 1)
752
0
      return;
753
754
0
    for (i = _torsionAngles.begin();i != _torsionAngles.end();++i)
755
0
      if (*i >= 0.0 && *i < 2.0*M_PI / fold)
756
0
            tv.push_back(*i);
757
758
0
    if (tv.empty())
759
0
      return;
760
0
    _torsionAngles = tv;
761
0
  }
762
763
  void OBRotor::SetDihedralAtoms(std::vector<int> &ref)
764
0
  {
765
0
    if (ref.size() != 4)
766
0
      return;
767
    // copy indexes starting from 1
768
0
    _ref.resize(4);
769
0
    for (int i = 0;i < 4;++i)
770
0
      _ref[i] = ref[i];
771
0
    _torsion.resize(4);
772
    // convert the indexes (start from 0, multiplied by 3) for easy access to coordinates
773
0
    _torsion[0] = (ref[0]-1)*3;
774
0
    _torsion[1] = (ref[1]-1)*3;
775
0
    _torsion[2] = (ref[2]-1)*3;
776
0
    _torsion[3] = (ref[3]-1)*3;
777
0
  }
778
779
  void OBRotor::SetDihedralAtoms(int ref[4])
780
0
  {
781
    // copy indexes starting from 1
782
0
    _ref.resize(4);
783
0
    for (int i = 0;i < 4;++i)
784
0
      _ref[i] = ref[i];
785
0
    _torsion.resize(4);
786
    // convert the indexes (start from 0, multiplied by 3) for easy access to coordinates
787
0
    _torsion[0] = (ref[0]-1)*3;
788
0
    _torsion[1] = (ref[1]-1)*3;
789
0
    _torsion[2] = (ref[2]-1)*3;
790
0
    _torsion[3] = (ref[3]-1)*3;
791
0
  }
792
793
  void OBRotor::SetRotAtoms(vector<int> &vi)
794
0
  {
795
0
    _rotatoms = vi;
796
0
  }
797
798
  //***************************************
799
  //**** OBRotorRules Member functions ****
800
  //***************************************
801
  OBRotorRules::OBRotorRules()
802
0
  {
803
0
    _quiet=false;
804
0
    _init = false;
805
0
    _dir = BABEL_DATADIR;
806
0
    _envvar = "BABEL_DATADIR";
807
0
    _filename = "torlib.txt";
808
0
    _subdir = "data";
809
0
    _dataptr = TorsionDefaults;
810
0
  }
811
812
  void OBRotorRules::ParseLine(const char *buffer)
813
0
  {
814
0
    int i;
815
0
    int ref[4];
816
0
    double delta;
817
0
    vector<double> vals;
818
0
    vector<string> vs;
819
0
    vector<string>::iterator j;
820
0
    char temp_buffer[BUFF_SIZE];
821
822
0
    if (buffer[0] == '#')
823
0
      return;
824
0
    tokenize(vs,buffer);
825
0
    if (vs.empty())
826
0
      return;
827
828
0
    if (EQn(buffer,"SP3-SP3",7))
829
0
      {
830
0
        _sp3sp3.clear();
831
        //        assert (vs.size() > 1);
832
0
        for (j = vs.begin(),++j;j != vs.end();++j)
833
0
          _sp3sp3.push_back(DEG_TO_RAD*atof(j->c_str()));
834
0
        return;
835
0
      }
836
837
0
    if (EQn(buffer,"SP3-SP2",7))
838
0
      {
839
0
        _sp3sp2.clear();
840
        //        assert(vs.size() > 1);
841
0
        for (j = vs.begin(),++j;j != vs.end();++j)
842
0
          _sp3sp2.push_back(DEG_TO_RAD*atof(j->c_str()));
843
0
        return;
844
0
      }
845
846
0
    if (EQn(buffer,"SP2-SP2",7))
847
0
      {
848
0
        _sp2sp2.clear();
849
        //        assert(vs.size() > 1);
850
0
        for (j = vs.begin(),++j;j != vs.end();++j)
851
0
          _sp2sp2.push_back(DEG_TO_RAD*atof(j->c_str()));
852
0
        return;
853
0
      }
854
855
0
    if (vs.size() > 5)
856
0
      {
857
0
        strncpy(temp_buffer,vs[0].c_str(), sizeof(temp_buffer) - 1);
858
0
        temp_buffer[sizeof(temp_buffer) - 1] = '\0';
859
        //reference atoms
860
0
        for (i = 0;i < 4;++i)
861
0
          ref[i] = atoi(vs[i+1].c_str())-1;
862
        //possible torsions
863
0
        vals.clear();
864
0
        delta = OB_DEFAULT_DELTA;
865
0
        for (i = 5;(unsigned)i < vs.size();++i)
866
0
          {
867
0
            if (i == (signed)(vs.size()-2) && vs[i] == "Delta")
868
0
              {
869
0
                delta = atof(vs[i+1].c_str());
870
0
                i += 2;
871
0
              }
872
0
            else
873
0
              vals.push_back(DEG_TO_RAD*atof(vs[i].c_str()));
874
0
          }
875
876
0
        if (vals.empty())
877
0
          {
878
0
            string err = "The following rule has no associated torsions: ";
879
0
            err += vs[0];
880
0
            obErrorLog.ThrowError(__FUNCTION__, err, obDebug);
881
0
          }
882
0
        OBRotorRule *rr = new OBRotorRule (temp_buffer,ref,vals,delta);
883
0
        if (rr->IsValid())
884
0
          _vr.push_back(rr);
885
0
        else
886
0
          delete rr;
887
0
      }
888
889
0
  }
890
891
  void OBRotorRules::GetRotorIncrements(OBMol &mol,OBBond *bond,
892
                                        int ref[4],vector<double> &vals,double &delta)
893
0
  {
894
0
    if (!_init)
895
0
      Init();
896
897
0
    vals.clear();
898
0
    vector<pair<int,int> > vpr;
899
0
    vpr.push_back(pair<int,int> (0,bond->GetBeginAtomIdx()));
900
0
    vpr.push_back(pair<int,int> (0,bond->GetEndAtomIdx()));
901
902
0
    delta = OB_DEFAULT_DELTA;
903
904
0
    int j;
905
0
    OBSmartsPattern *sp;
906
0
    vector<vector<int> > map;
907
0
    vector<OBRotorRule*>::iterator i;
908
0
    for (i = _vr.begin();i != _vr.end();++i)
909
0
      {
910
0
        sp = (*i)->GetSmartsPattern();
911
0
        (*i)->GetReferenceAtoms(ref);
912
0
        vpr[0].first = ref[1];
913
0
        vpr[1].first = ref[2];
914
915
0
        if (!sp->RestrictedMatch(mol,vpr,true))
916
0
          {
917
0
            swap(vpr[0].first,vpr[1].first);
918
0
            if (!sp->RestrictedMatch(mol,vpr,true))
919
0
              continue;
920
0
          }
921
922
0
        map = sp->GetMapList();
923
0
        for (j = 0;j < 4;++j)
924
0
          ref[j] = map[0][ref[j]];
925
0
        vals = (*i)->GetTorsionVals();
926
0
        delta = (*i)->GetDelta();
927
928
0
        OBAtom *a1,*a2,*a3,*a4,*r;
929
0
        a1 = mol.GetAtom(ref[0]);
930
0
        a4 = mol.GetAtom(ref[3]);
931
0
        if (a1->GetAtomicNum() == OBElements::Hydrogen && a4->GetAtomicNum() == OBElements::Hydrogen)
932
0
          continue; //don't allow hydrogens at both ends
933
0
        if (a1->GetAtomicNum() == OBElements::Hydrogen || a4->GetAtomicNum() == OBElements::Hydrogen) //need a heavy atom reference - can use hydrogen
934
0
          {
935
0
            bool swapped = false;
936
0
            a2 = mol.GetAtom(ref[1]);
937
0
            a3 = mol.GetAtom(ref[2]);
938
0
            if (a4->GetAtomicNum() == OBElements::Hydrogen)
939
0
              {
940
0
                swap(a1,a4);
941
0
                swap(a2,a3);
942
0
                swapped = true;
943
0
              }
944
945
0
            vector<OBBond*>::iterator k;
946
0
            for (r = a2->BeginNbrAtom(k);r;r = a2->NextNbrAtom(k))
947
0
              if (r->GetAtomicNum() != OBElements::Hydrogen && r != a3)
948
0
                break;
949
950
0
            if (!r)
951
0
              continue; //unable to find reference heavy atom
952
            //      cerr << "r = " << r->GetIdx() << endl;
953
954
0
            double t1 = mol.GetTorsion(a1,a2,a3,a4);
955
0
            double t2 = mol.GetTorsion(r,a2,a3,a4);
956
0
            double diff = t2 - t1;
957
0
            if (diff > 180.0)
958
0
              diff -= 360.0;
959
0
            if (diff < -180.0)
960
0
              diff += 360.0;
961
0
            diff *= DEG_TO_RAD;
962
963
0
            vector<double>::iterator m;
964
0
            for (m = vals.begin();m != vals.end();++m)
965
0
              {
966
0
                *m += diff;
967
0
                if (*m < M_PI)
968
0
                  *m += 2.0*M_PI;
969
0
                if (*m > M_PI)
970
0
                  *m -= 2.0*M_PI;
971
0
              }
972
973
0
            if (swapped)
974
0
              ref[3] = r->GetIdx();
975
0
            else
976
0
              ref[0] = r->GetIdx();
977
978
            /*
979
              mol.SetTorsion(r,a2,a3,a4,vals[0]);
980
              cerr << "test = " << (vals[0]-diff)*RAD_TO_DEG << ' ';
981
              cerr << mol.GetTorsion(a1,a2,a3,a4) <<  ' ';
982
              cerr << mol.GetTorsion(r,a2,a3,a4) << endl;
983
            */
984
0
          }
985
986
0
        char buffer[BUFF_SIZE];
987
0
        if (!_quiet)
988
0
          {
989
0
            snprintf(buffer,BUFF_SIZE,"%3d%3d%3d%3d %s",
990
0
                     ref[0],ref[1],ref[2],ref[3],
991
0
                     ((*i)->GetSmartsString()).c_str());
992
0
            obErrorLog.ThrowError(__FUNCTION__, buffer, obDebug);
993
0
          }
994
0
        return;
995
0
      }
996
997
    //***didn't match any rules - assign based on hybridization***
998
0
    OBAtom *a1,*a2,*a3,*a4;
999
0
    a2 = bond->GetBeginAtom();
1000
0
    a3 = bond->GetEndAtom();
1001
0
    vector<OBBond*>::iterator k;
1002
1003
0
    for (a1 = a2->BeginNbrAtom(k);a1;a1 = a2->NextNbrAtom(k))
1004
0
      if (a1->GetAtomicNum() != OBElements::Hydrogen && a1 != a3)
1005
0
        break;
1006
0
    for (a4 = a3->BeginNbrAtom(k);a4;a4 = a3->NextNbrAtom(k))
1007
0
      if (a4->GetAtomicNum() != OBElements::Hydrogen && a4 != a2)
1008
0
        break;
1009
1010
0
    ref[0] = a1->GetIdx();
1011
0
    ref[1] = a2->GetIdx();
1012
0
    ref[2] = a3->GetIdx();
1013
0
    ref[3] = a4->GetIdx();
1014
1015
0
    if (a2->GetHyb() == 3 && a3->GetHyb() == 3) //sp3-sp3
1016
0
      {
1017
0
        vals = _sp3sp3;
1018
1019
0
        if (!_quiet)
1020
0
          {
1021
0
            char buffer[BUFF_SIZE];
1022
0
            snprintf(buffer,BUFF_SIZE,"%3d%3d%3d%3d %s",
1023
0
                     ref[0],ref[1],ref[2],ref[3],"sp3-sp3");
1024
0
            obErrorLog.ThrowError(__FUNCTION__, buffer, obDebug);
1025
0
          }
1026
0
      }
1027
0
    else
1028
0
      if (a2->GetHyb() == 2 && a3->GetHyb() == 2) //sp2-sp2
1029
0
        {
1030
0
          vals = _sp2sp2;
1031
1032
0
          if (!_quiet)
1033
0
            {
1034
0
              char buffer[BUFF_SIZE];
1035
0
              snprintf(buffer,BUFF_SIZE,"%3d%3d%3d%3d %s",
1036
0
                       ref[0],ref[1],ref[2],ref[3],"sp2-sp2");
1037
0
              obErrorLog.ThrowError(__FUNCTION__, buffer, obDebug);
1038
0
            }
1039
0
        }
1040
0
      else //must be sp2-sp3
1041
0
        {
1042
0
          vals = _sp3sp2;
1043
1044
0
          if (!_quiet)
1045
0
            {
1046
0
              char buffer[BUFF_SIZE];
1047
0
              snprintf(buffer,BUFF_SIZE,"%3d%3d%3d%3d %s",
1048
0
                       ref[0],ref[1],ref[2],ref[3],"sp2-sp3");
1049
0
              obErrorLog.ThrowError(__FUNCTION__, buffer, obDebug);
1050
0
            }
1051
0
        }
1052
0
  }
1053
1054
  OBRotorRules::~OBRotorRules()
1055
0
  {
1056
0
    vector<OBRotorRule*>::iterator i;
1057
0
    for (i = _vr.begin();i != _vr.end();++i)
1058
0
      delete (*i);
1059
0
  }
1060
1061
#undef OB_DEFAULT_DELTA
1062
}
1063
1064
//! \file rotor.cpp
1065
//! \brief Rotate dihedral angles according to rotor rules.