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