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