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