1 /********************************************************************** 2 Copyright (C) 1998-2001 by OpenEye Scientific Software, Inc. 3 Some portions Copyright (C) 2003-2006 Geoffrey R. Hutchison 4 Some portions Copyright (C) 2004 by Chris Morley 5 6 Original Copyright refers to the pdbformat.cpp file, for reading and 7 writing pdb format files. 8 Extensively modified 2010 Stuart Armstrong (Source Science/InhibOx) 9 for the purpose of reading and writing pdbqt format files. 10 Some portions Copyright (C) 2010 by Stuart Armstrong of Source Science/ 11 InhibOx 12 13 This program is free software; you can redistribute it and/or modify 14 it under the terms of the GNU General Public License as published by 15 the Free Software Foundation version 2 of the License. 16 17 This program is distributed in the hope that it will be useful, 18 but WITHOUT ANY WARRANTY; without even the implied warranty of 19 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 20 GNU General Public License for more details. 21 ***********************************************************************/ 22 23 #include <openbabel/babelconfig.h> 24 #include <openbabel/obmolecformat.h> 25 #include <openbabel/mol.h> 26 #include <openbabel/atom.h> 27 #include <openbabel/elements.h> 28 #include <openbabel/generic.h> 29 #include <openbabel/bond.h> 30 #include <openbabel/data.h> 31 #include <openbabel/obiter.h> 32 #include <openbabel/typer.h> 33 34 #include <algorithm> 35 #include <cstdlib> 36 #include <vector> 37 #include <map> 38 #include <set> 39 40 #include <sstream> 41 42 using namespace std; 43 namespace OpenBabel 44 { 45 class branch 46 { 47 public: 48 vector <int> atoms; 49 bool done; 50 unsigned int index; 51 set <unsigned int> children; 52 vector <unsigned int> parents; 53 unsigned int depth; 54 unsigned int connecting_atom_parent; 55 unsigned int connecting_atom_branch; 56 unsigned int how_many_atoms_moved; 57 58 set <unsigned int> rigid_with; //the other branches that move rigidly with this one 59 clear()60 void clear() {done=false; index=0; depth=0; connecting_atom_parent=0; connecting_atom_branch=0; 61 how_many_atoms_moved=0; children.clear(); parents.clear(); atoms.clear(); rigid_with.clear(); parents.push_back(0);} UpOne()62 unsigned int UpOne() {if (parents.size()>=2) {return parents.at(parents.size()-2);} return 0;} branch()63 branch() {clear();} all_atoms(OBMol & mol)64 void all_atoms(OBMol& mol) {clear(); rigid_with.insert(0); for (unsigned int i=1; i <= mol.NumAtoms(); i++) {atoms.push_back(i);}} 65 }; 66 67 class PDBQTFormat : public OBMoleculeFormat 68 { 69 public: 70 //Register this format type ID PDBQTFormat()71 PDBQTFormat() 72 { 73 OBConversion::RegisterFormat("pdbqt",this, "chemical/x-pdbqt"); 74 } 75 Description()76 virtual const char* Description() //required 77 { 78 return 79 80 "AutoDock PDBQT format\n" 81 "Reads and writes AutoDock PDBQT (Protein Data Bank, Partial Charge (Q), & Atom Type (T)) format\n" 82 "Note that the torsion tree is by default. Use the ``r`` write option\n" 83 "to prevent this.\n\n" 84 85 "Read Options, e.g. -ab\n" 86 " b Disable automatic bonding\n" 87 " d Input file is in dlg (AutoDock docking log) format\n\n" 88 89 "Write Options, e.g. -xr\n" 90 " b Enable automatic bonding\n" 91 " r Output as a rigid molecule (i.e. no branches or torsion tree)\n" 92 " c Combine separate molecular pieces of input into a single rigid molecule (requires \"r\" option or will have no effect)\n" 93 " s Output as a flexible residue\n" 94 " p Preserve atom indices from input file (default is to renumber atoms sequentially)\n" 95 " h Preserve hydrogens\n" 96 " n Preserve atom names\n\n"; 97 }; 98 SpecificationURL()99 virtual const char* SpecificationURL() 100 {return "http://autodock.scripps.edu/faqs-help/faq/what-is-the-format-of-a-pdbqt-file";}; 101 GetMIMEType()102 virtual const char* GetMIMEType() 103 {return "chemical/x-pdbqt";}; 104 105 //*** This section identical for most OBMol conversions *** 106 //////////////////////////////////////////////////// 107 /// The "API" interface functions 108 virtual int SkipObjects(int n, OBConversion* pConv); 109 virtual bool ReadMolecule(OBBase* pOb, OBConversion* pConv); 110 virtual bool WriteMolecule(OBBase* pOb, OBConversion* pConv); 111 112 }; 113 //*** 114 //Make an instance of the format class 115 PDBQTFormat thePDBQTFormat; 116 117 //////////////////////////////////////////////////// 118 /// Utility functions 119 static bool parseAtomRecord(char *buffer, OBMol & mol, int chainNum); 120 static bool IsRotBond_PDBQT(OBBond * the_bond); 121 static bool IsIn(const vector<int>& vec, const int num); 122 static void OutputAtom(OBAtom* atom, ostream& ofs, unsigned int index); 123 static void OutputGroup(OBMol& mol, ostream& ofs, const vector <int>& group, map <unsigned int, unsigned int> new_indexes, bool use_new_indexes); 124 static unsigned int AtomsSoFar(const map <unsigned int, branch >& tree, unsigned int depth); 125 static bool FindBondedPiece(const vector<int>& root, const vector<int>& branch, unsigned int& root_atom, unsigned int& branch_atom, 126 unsigned int& root_atom_rank, unsigned int& branch_atom_rank, const OBMol& mol, unsigned int & atoms_moved); 127 static bool OutputTree(OBConversion *pConv, OBMol& mol, ostream& ofs, map <unsigned int, branch >& tree, unsigned int depth, bool moves_many, bool preserve_original_index); 128 static void ConstructTree (map <unsigned int, branch >& tree, vector <vector <int> > rigid_fragments, unsigned int root_piece, const OBMol& mol, bool flexible); 129 static bool DeleteHydrogens(OBMol & mol); 130 static bool Separate_preserve_charges(OBMol & mol, vector<OBMol> & result); 131 static unsigned int FindFragments(OBMol mol, vector <vector <int> >& rigid_fragments); 132 static unsigned int RotBond_count(OBMol & mol); 133 134 ///////////////////////////////////////////////////////////////// SkipObjects(int n,OBConversion * pConv)135 int PDBQTFormat::SkipObjects(int n, OBConversion* pConv) 136 { 137 if (n == 0) 138 ++ n; 139 istream &ifs = *pConv->GetInStream(); 140 char buffer[BUFF_SIZE]; 141 while (n && ifs.getline(buffer,BUFF_SIZE)) 142 { 143 if (EQn(buffer,"ENDMDL",6)) {-- n;} 144 } 145 146 return ifs.good() ? 1 : -1; 147 } 148 ///////////////////////////////////////////////////////////////// ReadMolecule(OBBase * pOb,OBConversion * pConv)149 bool PDBQTFormat::ReadMolecule(OBBase* pOb, OBConversion* pConv) 150 { 151 OBMol* pmol = pOb->CastAndClear<OBMol>(); 152 if (pmol == nullptr) 153 return false; 154 155 156 //Define some references so we can use the old parameter names 157 istream &ifs = *pConv->GetInStream(); 158 OBMol &mol = *pmol; 159 const char* title = pConv->GetTitle(); 160 161 bool dlg=false; 162 if (pConv->IsOption("d",OBConversion::INOPTIONS)) {dlg=true;} //check whether we have a file in dlg format 163 164 int chainNum = 1; 165 char buffer[BUFF_SIZE]; 166 string line, key, value; 167 OBPairData *dp; 168 169 mol.SetTitle(title); 170 mol.SetChainsPerceived(); // It's a PDBQT file, we read all chain/res info. 171 172 mol.BeginModify(); 173 while (ifs.good() && ifs.getline(buffer,BUFF_SIZE)) 174 { 175 line = buffer; 176 if (dlg) //if we have a dlg file, only care about the lines starting with "DOCKED: " 177 { 178 if (!EQn(buffer,"DOCKED: ",8)) {continue;} 179 else 180 { 181 for (unsigned int i=0; i<BUFF_SIZE-8; i++) 182 { 183 buffer[i]=buffer[i+8]; 184 if (buffer[i]=='\0') {break;} 185 } 186 } 187 } 188 if (line.length() == 0) 189 { 190 stringstream errorMsg; 191 errorMsg << "Warning: empty line, ignoring." << endl; 192 obErrorLog.ThrowError(__FUNCTION__, errorMsg.str() , obWarning); 193 continue; 194 } 195 if (line.length() < 3) 196 { 197 stringstream errorMsg; 198 errorMsg << "ERROR: not a valid PDBQT file" << endl; 199 obErrorLog.ThrowError(__FUNCTION__, errorMsg.str() , obError); 200 return false; 201 } 202 if (EQn(buffer,"ROOT",4)) {continue;} 203 if (EQn(buffer,"ENDROOT",7)) {continue;} 204 if (EQn(buffer,"BRANCH",6)) {continue;} 205 if (EQn(buffer,"ENDBRANCH",9)) {continue;} 206 207 if (EQn(buffer,"ENDMDL",6)) {break;} 208 if (EQn(buffer,"END_RES",7)) {break;} 209 210 if (EQn(buffer,"END",3)) 211 { 212 // eat anything until the next ENDMDL 213 while (ifs.getline(buffer,BUFF_SIZE) && !EQn(buffer,"ENDMDL",6)); 214 break; 215 } 216 217 if (EQn(buffer,"ATOM",4) || EQn(buffer,"HETATM",6)) 218 { 219 if( ! parseAtomRecord(buffer,mol,chainNum)) 220 { 221 stringstream errorMsg; 222 errorMsg << "WARNING: Problems reading a PDBQT file\n" 223 << " Problems reading a ATOM/HETATM record.\n" 224 << endl << buffer << endl; 225 obErrorLog.ThrowError(__FUNCTION__, errorMsg.str() , obError); 226 } 227 continue; 228 } 229 if ((EQn(buffer,"REMARK",6)) || (EQn(buffer,"USER",4))) 230 { 231 stringstream sst; 232 string buffstring=buffer; 233 sst.str(buffstring); 234 sst >> buffstring; 235 sst >> buffstring; 236 if (buffstring == "Name") 237 { 238 sst >> buffstring; 239 if (sst.good()) 240 { 241 sst >> buffstring; 242 mol.SetTitle(buffstring); 243 } 244 } 245 else if (buffstring == "NEWDPF") 246 { 247 sst >> buffstring; 248 if (buffstring == "move") 249 { 250 if (sst.good()) 251 { 252 sst >> buffstring; 253 mol.SetTitle(buffstring); 254 } 255 } 256 } 257 } 258 259 if (line.length() <= 6) 260 { 261 continue; 262 } 263 264 265 key = line.substr(0,6); // the first 6 characters are the record name 266 Trim(key); 267 value = line.substr(6); 268 269 // We haven't found this record yet 270 if (!mol.HasData(key)) 271 { 272 dp = new OBPairData; 273 dp->SetAttribute(key); 274 dp->SetValue(value); 275 dp->SetOrigin(fileformatInput); 276 mol.SetData(dp); 277 } 278 // Add on additional lines 279 else 280 { 281 dp = static_cast<OBPairData*>(mol.GetData(key)); 282 line = dp->GetValue(); 283 line += '\n'; 284 line += value; 285 dp->SetValue(line); 286 } 287 } 288 289 if (!mol.NumAtoms()) 290 { // skip the rest of this processing 291 mol.EndModify(); 292 return(false); 293 } 294 295 resdat.AssignBonds(mol); 296 /*assign hetatm bonds based on distance*/ 297 298 mol.EndModify(); 299 // Clear all virtual bond data 300 vector<OBGenericData*> vbonds = mol.GetAllData(OBGenericDataType::VirtualBondData); 301 mol.DeleteData(vbonds); 302 303 if (!pConv->IsOption("b",OBConversion::INOPTIONS)) {mol.ConnectTheDots(); mol.PerceiveBondOrders();} 304 305 mol.SetChainsPerceived(); 306 307 // clean out remaining blank lines 308 std::streampos ipos; 309 do 310 { 311 ipos = ifs.tellg(); 312 ifs.getline(buffer,BUFF_SIZE); 313 } 314 while(strlen(buffer) == 0 && !ifs.eof() ); 315 ifs.seekg(ipos); 316 317 mol.SetPartialChargesPerceived(); 318 return(true); 319 } 320 321 ///////////////////////////////////////////////////////////////////////// OutputAtom(OBAtom * atom,ostream & ofs,const unsigned int index)322 void OutputAtom(OBAtom* atom, ostream& ofs, const unsigned int index) 323 { 324 char buffer[BUFF_SIZE]; 325 char type_name[10], padded_name[10]; 326 char the_res[10]; 327 char the_chain = ' '; 328 char the_icode = ' '; 329 const char *element_name; 330 string element_name_string; 331 int res_num; 332 bool het=false; 333 334 OBResidue *res; 335 strncpy(type_name, OBElements::GetSymbol(atom->GetAtomicNum()), sizeof(type_name)); 336 type_name[sizeof(type_name) - 1] = '\0'; 337 //two char. elements are on position 13 and 14 one char. start at 14 338 339 if (strlen(type_name) > 1) 340 type_name[1] = toupper(type_name[1]); 341 else 342 { 343 char tmp[10]; 344 strncpy(tmp, type_name, 9); // Make sure to null-terminate 345 snprintf(type_name, sizeof(type_name), " %-3s", tmp); 346 } 347 348 if ((res = atom->GetResidue()) != nullptr) 349 { 350 snprintf(the_res,4,"%s",(char*)res->GetName().c_str()); 351 snprintf(type_name,5,"%s",(char*)res->GetAtomID(atom).c_str()); 352 the_chain = res->GetChain(); 353 the_icode = res->GetInsertionCode(); 354 if(the_icode == 0) the_icode = ' '; 355 356 //two char. elements are on position 13 and 14 one char. start at 14 357 if (strlen(OBElements::GetSymbol(atom->GetAtomicNum())) == 1) 358 { 359 if (strlen(type_name) < 4) 360 { 361 char tmp[10]; 362 strncpy(tmp, type_name, 9); // make sure to null-terminate 363 snprintf(padded_name, sizeof(padded_name), " %-3s", tmp); 364 strncpy(type_name,padded_name,4); 365 type_name[4] = '\0'; 366 } 367 else 368 { 369 type_name[4] = '\0'; 370 } 371 } 372 res_num = res->GetNum(); 373 } 374 else 375 { 376 strcpy(the_res,"UNK"); 377 snprintf(padded_name,sizeof(padded_name), "%s",type_name); 378 strncpy(type_name,padded_name,4); 379 type_name[4] = '\0'; 380 res_num = 1; 381 } 382 383 element_name = OBElements::GetSymbol(atom->GetAtomicNum()); 384 char element_name_final[3]; 385 element_name_final[2] = '\0'; 386 387 if (atom->GetAtomicNum() == OBElements::Hydrogen) {element_name_final[0]='H'; element_name_final[1]='D';} 388 else if ((atom->GetAtomicNum() == OBElements::Carbon) && (atom->IsAromatic())) {element_name_final[0]='A'; element_name_final[1]=' ';} 389 else if (atom->GetAtomicNum() == OBElements::Oxygen) {element_name_final[0]='O'; element_name_final[1]='A';} 390 else if ((atom->GetAtomicNum() == OBElements::Nitrogen) && (atom->IsHbondAcceptor())) {element_name_final[0]='N'; element_name_final[1]='A';} 391 else if ((atom->GetAtomicNum() == OBElements::Sulfur) && (atom->IsHbondAcceptor())) {element_name_final[0]='S'; element_name_final[1]='A';} 392 else 393 { 394 if (!isalnum(element_name[0])) {element_name_final[0]=' ';} 395 else element_name_final[0]=element_name[0]; 396 if (!isalnum(element_name[1])) {element_name_final[1]=' ';} 397 else element_name_final[1]=element_name[1]; 398 } 399 400 double charge = atom->GetPartialCharge(); 401 snprintf(buffer, BUFF_SIZE, "%s%5d %-4s %-3s %c%4d%c %8.3f%8.3f%8.3f 0.00 0.00 %+5.3f %.2s", 402 het?"HETATM":"ATOM ", 403 index, 404 type_name, 405 the_res, 406 the_chain, 407 res_num, 408 the_icode, 409 atom->GetX(), 410 atom->GetY(), 411 atom->GetZ(), 412 charge, 413 element_name_final); 414 ofs << buffer; 415 ofs << endl; 416 } 417 OutputGroup(OBMol & mol,ostream & ofs,const vector<int> & group,map<unsigned int,unsigned int> new_indexes,bool use_new_indexes)418 void OutputGroup(OBMol& mol, ostream& ofs, const vector <int>& group, map <unsigned int, unsigned int> new_indexes, bool use_new_indexes) 419 { 420 for (vector <int>::const_iterator it = group.begin(); it != group.end(); ++it) 421 { 422 if (use_new_indexes) {OutputAtom(mol.GetAtom((*it)), ofs, new_indexes.find(*it)->second);} 423 else {OutputAtom(mol.GetAtom((*it)), ofs, (*it));} 424 } 425 } 426 427 FindFragments(OBMol mol,vector<vector<int>> & rigid_fragments)428 unsigned int FindFragments(OBMol mol, vector <vector <int> >& rigid_fragments) 429 { 430 unsigned int best_root_atom=1; 431 unsigned int shortest_maximal_remaining_subgraph=mol.NumAtoms(); 432 for (unsigned int i=1; i <= mol.NumAtoms(); i++) 433 //finds the root atom by copying the molecule, deleting each atom in turn, and finding the sizes of the resulting pieces 434 { 435 OBMol mol_pieces = mol; 436 OBAtom * atom_to_del = mol_pieces.GetAtom(i); 437 vector <vector <int> > frag_list; 438 439 mol_pieces.DeleteAtom(atom_to_del, true); 440 mol_pieces.ContigFragList(frag_list); 441 unsigned int smrsi=0; 442 for (unsigned int j = 0; j < frag_list.size(); j++) 443 { 444 smrsi= smrsi > frag_list.at(j).size() ? smrsi : frag_list.at(j).size(); 445 } 446 if (smrsi < shortest_maximal_remaining_subgraph) 447 { 448 shortest_maximal_remaining_subgraph=smrsi; 449 best_root_atom=i; 450 } 451 } 452 453 vector <OBBond*> bonds_to_delete; 454 OBMol mol_pieces = mol; 455 for (OBBondIterator it=mol_pieces.BeginBonds(); it != mol_pieces.EndBonds(); it++) 456 { 457 if (IsRotBond_PDBQT((*it))) 458 { 459 bonds_to_delete.push_back(*it); 460 } 461 } 462 for (vector<OBBond*>::iterator bit = bonds_to_delete.begin(); bit != bonds_to_delete.end(); ++bit) 463 { 464 mol_pieces.DeleteBond(*bit, true); 465 } 466 mol_pieces.ContigFragList(rigid_fragments); 467 468 return best_root_atom; 469 } 470 DeleteHydrogens(OBMol & mol)471 bool DeleteHydrogens(OBMol & mol) 472 { 473 for (OBAtomIterator it=mol.BeginAtoms(); it != mol.EndAtoms(); it++) 474 { 475 if ( (*it)->IsNonPolarHydrogen() ) 476 { 477 OBBondIterator voider; 478 double charger = (*it)->GetPartialCharge(); 479 charger += ((*it)->BeginNbrAtom(voider))->GetPartialCharge(); 480 ((*it)->BeginNbrAtom(voider))->SetPartialCharge(charger); 481 } 482 } 483 return mol.DeleteNonPolarHydrogens(); 484 } 485 OutputTree(OBConversion * pConv,OBMol & mol,ostream & ofs,map<unsigned int,branch> & tree,unsigned int depth,bool moves_many,bool preserve_original_index)486 bool OutputTree(OBConversion* pConv, OBMol& mol, ostream& ofs, map <unsigned int, branch> & tree, unsigned int depth, bool moves_many, bool preserve_original_index) 487 { 488 if (tree.empty()) {return false;} 489 if (depth>= tree.size()-1) {depth=tree.size()-1;} 490 491 set <unsigned int> free_bonds; //this section is to allow the code to be generalised when using obabel rather than babel, which accepts numerical arguments as to how many bonds to fix. As laid out, it will prioritise those bonds that move the fewest atoms, unless moves_many is true, where it will prioritise those that move the most. This is moot for the moment, as either all rotatable bonds are free, or they are all rigid. 492 493 free_bonds.insert(0); 494 multimap <unsigned int, unsigned int> how_many_atoms_move; 495 for (unsigned int i=1; i<tree.size(); i++) 496 { 497 how_many_atoms_move.insert(pair<unsigned int, unsigned int>( (*tree.find(i)).second.how_many_atoms_moved,i)); 498 } 499 500 multimap <unsigned int, unsigned int>::iterator it=how_many_atoms_move.begin(); 501 if ((!moves_many) && !how_many_atoms_move.empty()) { 502 it=how_many_atoms_move.end(); 503 if (it!=how_many_atoms_move.begin()) // don't move past begin 504 --it; 505 } 506 for (unsigned int i = 1; i <= depth; i++) 507 { 508 free_bonds.insert((*it).second); 509 if (!moves_many) { 510 if (it!=how_many_atoms_move.begin()) 511 --it; 512 } 513 else{ 514 ++it; 515 } 516 } 517 518 519 for (unsigned int i=tree.size()-1; i > 0; i--) 520 { 521 if (!free_bonds.count(i)) //adds the index of any branch with rigid rotations to its parent 522 { 523 unsigned int parent=(*tree.find(i)).second.UpOne(); 524 (*tree.find(parent)).second.rigid_with.insert( 525 (*tree.find(i)).second.rigid_with.begin(),(*tree.find(i)).second.rigid_with.end() ); 526 } 527 } 528 529 map <unsigned int, unsigned int> new_order; //gives the new ordering of the indexes of atoms, so that they are in increasing order from 1 in the output 530 531 532 if (!preserve_original_index) //generates the new ordering 533 { 534 unsigned int current_atom_index=1; //the index of the current atom 535 for (unsigned int i=0; i < tree.size(); i++) 536 { 537 if (free_bonds.count(i)) 538 { 539 for (set <unsigned int>::iterator it= (*tree.find(i)).second.rigid_with.begin() ; it != (*tree.find(i)).second.rigid_with.end(); ++it) 540 { 541 vector <int> atoms=(*tree.find(*it)).second.atoms; 542 for (unsigned int j=0; j < atoms.size(); j++) 543 { 544 new_order.insert(pair<unsigned int, unsigned int> (atoms.at(j), current_atom_index)); 545 current_atom_index++; 546 } 547 } 548 } 549 } 550 } 551 552 if (!(pConv->IsOption("r",OBConversion::OUTOPTIONS))) 553 ofs << "ROOT" << endl; 554 for (set <unsigned int>::iterator it= (*tree.find(0)).second.rigid_with.begin() ; it != (*tree.find(0)).second.rigid_with.end(); ++it) 555 { 556 OutputGroup(mol, ofs, (*tree.find(*it)).second.atoms, new_order, !preserve_original_index); 557 } 558 559 if (!(pConv->IsOption("r",OBConversion::OUTOPTIONS))) 560 ofs << "ENDROOT" << endl; 561 562 for (unsigned int i=1; i < tree.size(); i++) 563 { 564 if (free_bonds.count(i)) 565 { 566 ofs << "BRANCH"; 567 ofs.width(4); 568 unsigned int parent_atom=(*tree.find(i)).second.connecting_atom_parent; 569 unsigned int child_atom=(*tree.find(i)).second.connecting_atom_branch; 570 if (!preserve_original_index) { ofs << (new_order.find(parent_atom))-> second;} 571 else {ofs << parent_atom;} 572 ofs.width(4); 573 if (!preserve_original_index) {ofs << (new_order.find(child_atom))-> second;} 574 else {ofs << child_atom;} 575 ofs << endl; 576 for (set <unsigned int>::iterator it= (*tree.find(i)).second.rigid_with.begin() ; it != (*tree.find(i)).second.rigid_with.end(); ++it) 577 { 578 OutputGroup(mol, ofs, (*tree.find(*it)).second.atoms, new_order, !preserve_original_index); 579 } 580 } 581 unsigned int child=i; 582 for (vector <unsigned int>::iterator it=(*tree.find(i)).second.parents.end(); it != (*tree.find(i)).second.parents.begin(); ) 583 { 584 --it; 585 if ((*it)==0) {break;} //do not close the main root; that is closed separately 586 vector <unsigned int>::iterator it_parent=it; 587 --it_parent; 588 if ((*tree.find(*it)).second.children.size() == 0) 589 { 590 if (free_bonds.count(*it)) 591 { 592 ofs << "ENDBRANCH"; 593 ofs.width(4); 594 unsigned int parent_atom=(*tree.find(*it)).second.connecting_atom_parent; 595 unsigned int child_atom=(*tree.find(*it)).second.connecting_atom_branch; 596 if (!preserve_original_index) { ofs << (new_order.find(parent_atom))-> second;} 597 else {ofs << parent_atom;} 598 ofs.width(4); 599 if (!preserve_original_index) {ofs << (new_order.find(child_atom))-> second;} 600 else {ofs << child_atom;} 601 ofs << endl; 602 } 603 (*tree.find(*it_parent)).second.children.erase(*it); 604 } 605 } 606 } 607 return true; 608 } 609 ConstructTree(map<unsigned int,branch> & tree,vector<vector<int>> rigid_fragments,unsigned int root_piece,const OBMol & mol,bool flexible)610 void ConstructTree (map <unsigned int, branch>& tree, vector <vector <int> > rigid_fragments, unsigned int root_piece, const OBMol& mol, bool flexible) 611 { 612 unsigned int first_atom = 0; 613 unsigned int second_atom = 0; 614 unsigned int first_atom_rank = 0; 615 unsigned int second_atom_rank = 0; 616 617 618 branch sprog; 619 620 sprog.atoms=rigid_fragments.at(root_piece); 621 sprog.rigid_with.insert(0); 622 623 tree.insert(pair<unsigned int, branch> (0, sprog)); 624 625 rigid_fragments.erase(rigid_fragments.begin() + root_piece); 626 627 unsigned int position=0; 628 unsigned int atoms_moved=0; 629 bool fecund; 630 while (!((*tree.find(0)).second.done)) 631 { 632 fecund=!((*tree.find(position)).second.done); 633 if (fecund) 634 { 635 bool sterile=true; 636 for (unsigned int i = 0; i < rigid_fragments.size(); i++) 637 { 638 if (FindBondedPiece( (*tree.find(position)).second.atoms, rigid_fragments.at(i), 639 first_atom, second_atom, first_atom_rank, second_atom_rank, mol, atoms_moved)) 640 { 641 sprog.connecting_atom_parent = first_atom; 642 sprog.connecting_atom_branch = second_atom; 643 sprog.how_many_atoms_moved = atoms_moved; 644 sprog.atoms = rigid_fragments.at(i); 645 646 sprog.depth=(*tree.find(position)).second.depth+1; 647 sprog.parents=(*tree.find(position)).second.parents; //all parents of the parent are parents too 648 sprog.parents.push_back(tree.size()); //a branch is its own parent 649 sprog.index=tree.size(); //the index is simply the number of precursors 650 sprog.rigid_with.clear(); 651 sprog.rigid_with.insert(sprog.index); 652 653 (*tree.find(position)).second.children.insert(tree.size()); //tells the current parent it has an extra child 654 tree.insert(pair<unsigned int, branch> (tree.size(), sprog)); //adds the current branch to the tree 655 656 rigid_fragments.erase(rigid_fragments.begin() + i); 657 sterile=false; 658 position = tree.size()-1; 659 break; 660 } 661 } 662 if (sterile) 663 { 664 (*tree.find(position)).second.done=true; 665 } 666 } 667 else {position--;} 668 } 669 } RotBond_count(OBMol & mol)670 unsigned int RotBond_count(OBMol & mol) 671 { 672 unsigned int count=0; 673 for (OBBondIterator it=mol.BeginBonds(); it!=mol.EndBonds(); it++) 674 { 675 if (IsRotBond_PDBQT((*it))) {count++;} 676 } 677 return count; 678 } 679 IsImide(OBBond * querybond)680 static bool IsImide(OBBond* querybond) 681 { 682 if (querybond->GetBondOrder() != 2) 683 return(false); 684 685 OBAtom* bgn = querybond->GetBeginAtom(); 686 OBAtom* end = querybond->GetEndAtom(); 687 if ((bgn->GetAtomicNum() == 6 && end->GetAtomicNum() == 7) || 688 (bgn->GetAtomicNum() == 7 && end->GetAtomicNum() == 6)) 689 return(true); 690 691 return(false); 692 } 693 IsAmidine(OBBond * querybond)694 static bool IsAmidine(OBBond* querybond) 695 { 696 OBAtom *c, *n; 697 c = n = nullptr; 698 699 // Look for C-N bond 700 OBAtom* bgn = querybond->GetBeginAtom(); 701 OBAtom* end = querybond->GetEndAtom(); 702 if (bgn->GetAtomicNum() == 6 && end->GetAtomicNum() == 7) 703 { 704 c = bgn; 705 n = end; 706 } 707 if (bgn->GetAtomicNum() == 7 && end->GetAtomicNum() == 6) 708 { 709 c = end; 710 n =bgn; 711 } 712 if (!c || !n) return(false); 713 if (querybond->GetBondOrder() != 1) return(false); 714 if (n->GetTotalDegree() != 3) return false; // must be a degree 3 nitrogen 715 716 // Make sure C is attached to =N 717 OBBond *bond; 718 vector<OBBond*>::iterator i; 719 for (bond = c->BeginBond(i); bond; bond = c->NextBond(i)) 720 { 721 if (IsImide(bond)) return(true); 722 } 723 724 // Return 725 return(false); 726 } 727 728 729 ///////////////////////////////////////////////////////////////////////// IsRotBond_PDBQT(OBBond * the_bond)730 bool IsRotBond_PDBQT(OBBond * the_bond) 731 //identifies a bond as rotatable if it is a single bond, not amide, not in a ring, 732 //and if both atoms it connects have at least one other atom bounded to them 733 { 734 if ( the_bond->GetBondOrder() != 1 || the_bond->IsAromatic() || 735 the_bond->IsAmide() || IsAmidine(the_bond) || the_bond->IsInRing() ) 736 return false; 737 if ( ((the_bond->GetBeginAtom())->GetExplicitDegree() == 1) || ((the_bond->GetEndAtom())->GetExplicitDegree() == 1) ) {return false;} 738 return true; 739 } 740 IsIn(const vector<int> & vec,const int num)741 bool IsIn(const vector<int>& vec, const int num) //checks whether a vector of int contains a specific int 742 { 743 for (vector<int>::const_iterator itv=vec.begin(); itv != vec.end(); ++itv) 744 { 745 if ((*itv) == num ) {return true;} 746 } 747 return false; 748 } 749 AtomsSoFar(const map<unsigned int,branch> & tree,unsigned int depth)750 unsigned int AtomsSoFar(const map <unsigned int, branch> & tree, unsigned int depth) 751 { 752 if (depth > tree.size()) {return 0;} 753 unsigned int numberAtoms=0; 754 // for (unsigned int i = 0; i < depth; i++) {numberAtoms+= tree.at(i).second.first.size();} 755 return numberAtoms; 756 } 757 FindBondedPiece(const vector<int> & root,const vector<int> & branched,unsigned int & root_atom,unsigned int & branch_atom,unsigned int & root_atom_rank,unsigned int & branch_atom_rank,const OBMol & mol,unsigned int & atoms_moved)758 bool FindBondedPiece(const vector<int>& root, const vector<int>& branched, unsigned int& root_atom, unsigned int& branch_atom, 759 unsigned int& root_atom_rank, unsigned int& branch_atom_rank, const OBMol& mol, unsigned int & atoms_moved) 760 { 761 OBBond* the_bond; 762 for (unsigned int i=0; i < root.size(); i++) 763 { 764 for (unsigned int j=0; j < branched.size(); j++) 765 { 766 the_bond=mol.GetBond(mol.GetAtom(root.at(i)), mol.GetAtom(branched.at(j))); 767 if (the_bond != nullptr) 768 { 769 root_atom=root.at(i); 770 branch_atom=branched.at(j); 771 root_atom_rank=i; 772 branch_atom_rank=j; 773 OBMol mol_copy=mol; 774 the_bond=mol_copy.GetBond(mol_copy.GetAtom(root.at(i)), mol_copy.GetAtom(branched.at(j))); 775 mol_copy.DeleteBond(the_bond, true); 776 777 vector <vector <int> > two_pieces; 778 mol_copy.ContigFragList(two_pieces); 779 atoms_moved = two_pieces.at(1).size(); 780 return true; 781 } 782 } 783 } 784 return false; 785 } 786 ///////////////////////////////////////////////////////////////////////// 787 Separate_preserve_charges(OBMol & mol,vector<OBMol> & result)788 bool Separate_preserve_charges(OBMol & mol, vector <OBMol> & result) 789 { 790 // vector<OBMol> result; 791 if( mol.NumAtoms() == 0 ) 792 return false; // nothing to do, but let's prevent a crash 793 794 OBMolAtomDFSIter iter( mol, 1); 795 OBMol newMol; 796 newMol.SetAutomaticPartialCharge(false); 797 int fragments = 0; 798 while( mol.GetNextFragment( iter, newMol ) ) 799 { 800 result.push_back( newMol ); 801 newMol.Clear(); 802 newMol.SetAutomaticPartialCharge(false); 803 } 804 805 return true; 806 } 807 ///////////////////////////////////////////////////////////////////////// CompareBondAtoms(const void * a,const void * b)808 int CompareBondAtoms(const void *a, const void *b) 809 { 810 const OBAtom **da = (const OBAtom **)a; 811 const OBAtom **db = (const OBAtom **)b; 812 unsigned int aIdx = (*da)->GetIdx(); 813 unsigned int bIdx = (*db)->GetIdx(); 814 815 return ((aIdx > bIdx) - (aIdx < bIdx)); 816 } 817 ///////////////////////////////////////////////////////////////////////// CompareBonds(const void * a,const void * b)818 int CompareBonds(const void *a, const void *b) 819 { 820 const OBAtom ***da = (const OBAtom ***)a; 821 const OBAtom ***db = (const OBAtom ***)b; 822 unsigned int aIdx[2] = { (*da)[0]->GetIdx(), (*da)[1]->GetIdx() }; 823 unsigned int bIdx[2] = { (*db)[0]->GetIdx(), (*db)[1]->GetIdx() }; 824 int cmp1; 825 826 827 cmp1 = ((aIdx[0] > bIdx[0]) - (aIdx[0] < bIdx[0])); 828 return (cmp1 ? cmp1 : ((aIdx[1] > bIdx[1]) - (aIdx[1] < bIdx[1]))); 829 } 830 ///////////////////////////////////////////////////////////////////////// WriteMolecule(OBBase * pOb,OBConversion * pConv)831 bool PDBQTFormat::WriteMolecule(OBBase* pOb, OBConversion* pConv) 832 { 833 OBMol* pmol = dynamic_cast<OBMol*>(pOb); 834 if (pmol == nullptr) 835 return false; 836 837 //Define some references so we can use the old parameter names 838 ostream &ofs = *pConv->GetOutStream(); 839 OBMol & mol = *pmol; 840 841 if(!mol.HasAromaticPerceived()) { //need aromaticity for correct atom typing 842 aromtyper.AssignAromaticFlags(mol); 843 } 844 845 if (pConv->IsOption("b",OBConversion::OUTOPTIONS)) {mol.ConnectTheDots(); mol.PerceiveBondOrders();} 846 vector <OBMol> all_pieces; 847 if ((pConv->IsOption("c", OBConversion::OUTOPTIONS) != nullptr && pConv->IsOption("r", OBConversion::OUTOPTIONS) != nullptr) 848 || (pConv->IsOption("n",OBConversion::OUTOPTIONS)) 849 ) 850 { 851 mol.SetAutomaticPartialCharge(false); 852 all_pieces.push_back(mol); 853 } 854 else 855 { 856 Separate_preserve_charges(mol, all_pieces); 857 } 858 859 for (unsigned int i = 0; i < all_pieces.size(); i++) 860 { 861 bool residue=false; 862 string res_name=""; 863 string res_chain=""; 864 int res_num=1; 865 if (pConv->IsOption("s",OBConversion::OUTOPTIONS)) 866 { 867 residue=true; 868 OBResidue* res=mol.GetResidue(0); 869 res_name=res->GetName(); 870 res_name.resize(3); 871 res_chain=res->GetChain(); 872 res_num=res->GetNum(); 873 } 874 875 all_pieces.at(i).SetAutomaticPartialCharge(false); 876 all_pieces.at(i).SetAromaticPerceived(); //retain aromatic flags in fragments 877 if (!(pConv->IsOption("h",OBConversion::OUTOPTIONS))) { 878 DeleteHydrogens(all_pieces.at(i)); 879 } 880 881 int model_num = 0; 882 char buffer[BUFF_SIZE]; 883 if (!residue) 884 { 885 if (!pConv->IsLast() || pConv->GetOutputIndex() > 1) 886 { // More than one molecule record 887 model_num = pConv->GetOutputIndex(); // MODEL 1-based index 888 snprintf(buffer, BUFF_SIZE, "MODEL %8d", model_num); 889 ofs << buffer << endl; 890 } 891 ofs << "REMARK Name = " << mol.GetTitle(true) << endl; 892 // ofs << "USER Name = " << mol.GetTitle(true) << endl; 893 if (!(pConv->IsOption("r",OBConversion::OUTOPTIONS))) 894 { 895 char type_name[10]; 896 int nRotBond=RotBond_count(mol); 897 OBAtom ***rotBondTable = new OBAtom **[nRotBond]; 898 int rotBondId=0; 899 int bondAtomNum; 900 unsigned int end; 901 OBResidue *res; 902 for (OBBondIterator it=mol.BeginBonds(); it != mol.EndBonds(); it++) 903 { 904 if (IsRotBond_PDBQT((*it))) 905 { 906 rotBondTable[rotBondId] = new OBAtom *[2]; 907 rotBondTable[rotBondId][0] = (*it)->GetBeginAtom(); 908 rotBondTable[rotBondId][1] = (*it)->GetEndAtom(); 909 qsort(rotBondTable[rotBondId], 2, sizeof(OBAtom *), CompareBondAtoms); 910 rotBondId++; 911 } 912 } 913 qsort(rotBondTable, nRotBond, sizeof(OBAtom **), CompareBonds); 914 ofs << "REMARK " << nRotBond << " active torsions:" << endl; 915 ofs << "REMARK status: ('A' for Active; 'I' for Inactive)" << endl; 916 for (rotBondId=0; rotBondId < nRotBond; rotBondId++) 917 { 918 snprintf(buffer, BUFF_SIZE, "REMARK %3d A between atoms: ", rotBondId + 1); 919 ofs << buffer; 920 for (bondAtomNum=0; bondAtomNum < 2; bondAtomNum++) 921 { 922 memset(type_name, 0, sizeof(type_name)); 923 strncpy(type_name, OBElements::GetSymbol(rotBondTable[rotBondId][bondAtomNum]->GetAtomicNum()), sizeof(type_name)); 924 if (strlen(type_name) > 1) 925 type_name[1] = toupper(type_name[1]); 926 if ((res = rotBondTable[rotBondId][bondAtomNum]->GetResidue()) != nullptr) 927 { 928 snprintf(type_name,5,"%s",(char*)res->GetAtomID(rotBondTable[rotBondId][bondAtomNum]).c_str()); 929 // AtomIDs may start with space if read from a PDB file (rather than perceived) 930 end = isspace(type_name[0]) ? 1 : 0; 931 // Use sizeof() - 1 to ensure there's room for the NULL termination! 932 while (end < sizeof(type_name) - 1 && type_name[end] && !isspace(type_name[end])) 933 end++; 934 type_name[end] = '\0'; 935 } 936 snprintf(buffer, BUFF_SIZE, "%s_%d", type_name + (isspace(type_name[0]) ? 1 : 0), 937 rotBondTable[rotBondId][bondAtomNum]->GetIdx()); 938 ofs << buffer; 939 if (bondAtomNum == 0) 940 ofs << " and "; 941 } 942 delete [] rotBondTable[rotBondId]; 943 ofs << endl; 944 } 945 delete [] rotBondTable; 946 } 947 ofs << "REMARK x y z vdW Elec q Type" << endl; 948 // ofs << "USER x y z vdW Elec q Type" << endl; 949 ofs << "REMARK _______ _______ _______ _____ _____ ______ ____" << endl; 950 // ofs << "USER _______ _______ _______ _____ _____ ______ ____" << endl; 951 } 952 else 953 { 954 ofs << "BEGIN_RES" << " " << res_name << " " << res_chain << " "; 955 ofs.width(3); 956 ofs << right << res_num << endl; 957 } 958 959 // before we write any records, we should check to see if any coord < -1000 960 // which will cause errors in the formatting 961 double minX, minY, minZ; 962 minX = minY = minZ = -999.0f; 963 FOR_ATOMS_OF_MOL(a, all_pieces.at(i)) 964 { 965 if (a->GetX() < minX) 966 minX = a->GetX(); 967 if (a->GetY() < minY) 968 minY = a->GetY(); 969 if (a->GetZ() < minZ) 970 minZ = a->GetZ(); 971 } 972 vector3 transV = VZero; 973 if (minX < -999.0) 974 transV.SetX(-1.0*minX - 900.0); 975 if (minY < -999.0) 976 transV.SetY(-1.0*minY - 900.0); 977 if (minZ < -999.0) 978 transV.SetZ(-1.0*minZ - 900.0); 979 980 // if minX, minY, or minZ was never changed, shift will be 0.0f 981 // otherwise, move enough so that smallest coord is > -999.0f 982 all_pieces.at(i).Translate(transV); 983 984 bool flexible=!pConv->IsOption("r",OBConversion::OUTOPTIONS); 985 986 vector <vector <int> > rigid_fragments; //the vector of all the rigid molecule fragments, using atom indexes 987 unsigned int best_root_atom=1; 988 map <unsigned int, branch> tree; 989 unsigned int torsdof=0; 990 unsigned int root_piece=0; 991 unsigned int rotatable_bonds=0; 992 993 if (flexible) 994 { 995 996 best_root_atom=FindFragments(all_pieces.at(i), rigid_fragments); //the return value is the root atom index 997 998 if (residue) {best_root_atom=1;} //if this is a residue, uses the first atom as the root 999 1000 torsdof=rigid_fragments.size()-1; 1001 1002 for (unsigned int j = 0; j < rigid_fragments.size(); j++) 1003 { 1004 if (IsIn((rigid_fragments.at(j)), best_root_atom)) {root_piece=j; break;} //this is the root rigid molecule fragment 1005 } 1006 ConstructTree(tree, rigid_fragments, root_piece, all_pieces.at(i), true); 1007 rotatable_bonds=torsdof; 1008 } 1009 else //if no rotatable bonds are selected, then won't construct a tree, instead get a whole branch directly from the OBMol 1010 { 1011 branch all_molecule_branch; 1012 all_molecule_branch.all_atoms(all_pieces.at(i)); 1013 tree.insert(pair<unsigned int, branch> (0, all_molecule_branch)); 1014 torsdof=RotBond_count(all_pieces.at(i)); 1015 } 1016 1017 bool preserve_original_index = (pConv->IsOption("p",OBConversion::OUTOPTIONS)); 1018 if (!flexible) {preserve_original_index=false;} //no need to relabel if we are preserving the original order anyway 1019 1020 if (!OutputTree(pConv, all_pieces.at(i), ofs, tree, rotatable_bonds, false, preserve_original_index) ) 1021 { 1022 stringstream errorMsg; 1023 errorMsg << "WARNING: Problems writing a PDBQT file\n" 1024 << " The torsion tree is wrong.\n"; 1025 obErrorLog.ThrowError(__FUNCTION__, errorMsg.str(), obError); 1026 return false; 1027 } 1028 1029 1030 if (!residue) 1031 { 1032 if (!(pConv->IsOption("r",OBConversion::OUTOPTIONS))) 1033 ofs << "TORSDOF " << torsdof << endl; 1034 else 1035 ofs << "TER " << endl; 1036 // ofs << "TER" << endl; 1037 if (model_num) 1038 { 1039 ofs << "ENDMDL" << endl; 1040 } 1041 } 1042 else 1043 { 1044 ofs << "END_RES" << " " << res_name << " " << res_chain << " "; 1045 ofs.width(3); 1046 ofs << right << res_num << endl; 1047 } 1048 } 1049 return true; 1050 } 1051 1052 ///////////////////////////////////////////////////////////////////////// 1053 parseAtomRecord(char * buffer,OBMol & mol,int chainNum)1054 static bool parseAtomRecord(char *buffer, OBMol &mol,int chainNum) 1055 /* ATOMFORMAT "(i5,1x,a4,a1,a3,1x,a1,i4,a1,3x,3f8.3,2f6.2,a2,a2)" */ 1056 { 1057 string sbuf = &buffer[6]; 1058 if (sbuf.size() < 48) 1059 return(false); 1060 1061 bool hetatm = (EQn(buffer,"HETATM",6)) ? true : false; 1062 bool elementFound = false; // true if correct element found in col 77-78 1063 1064 /* serial number */ 1065 string serno = sbuf.substr(0,5); 1066 1067 /* atom name */ 1068 string atmid = sbuf.substr(6,4); 1069 1070 /* chain */ 1071 char chain = sbuf.substr(15,1)[0]; 1072 1073 /* element */ 1074 string element = " "; 1075 string pdbqt_element = " "; //the literal pdbqt element 1076 if (sbuf.size() == 72) {sbuf+=" ";} 1077 if (sbuf.size() > 72) 1078 { 1079 pdbqt_element = sbuf.substr(71,2); 1080 if ( (pdbqt_element == "A ") || (pdbqt_element == " A") || (pdbqt_element == "Z ") || (pdbqt_element == " Z") || 1081 (pdbqt_element == "G ") || (pdbqt_element == " G") || (pdbqt_element == "GA") || (pdbqt_element == "J ") || 1082 (pdbqt_element == " J") || (pdbqt_element == "Q ") || (pdbqt_element == " Q") ) 1083 {element = "C"; element += " ";} //all these are carbons 1084 else if (pdbqt_element == "HD") {element = "H"; element += " ";} //HD are hydrogens 1085 else if (pdbqt_element == "HS") {element = "H"; element += " ";} //HS are hydrogens 1086 else if (pdbqt_element == "NA") {element = "N"; element += " ";} //NA are nitrogens 1087 else if (pdbqt_element == "NS") {element = "N"; element += " ";} //NS are nitrogens 1088 else if (pdbqt_element == "OA") {element = "O"; element += " ";} //OA are oxygens 1089 else if (pdbqt_element == "OS") {element = "O"; element += " ";} //OS are oxygens 1090 else if (pdbqt_element == "SA") {element = "S"; element += " ";} //SA are sulphurs 1091 else {element = pdbqt_element;} 1092 1093 if (isalpha(element[0])) //trims the name if needed 1094 { 1095 if (element[1] == ' ') 1096 { 1097 element.erase(1, 1); 1098 elementFound = true; 1099 } 1100 else if (isalpha(element[1])) 1101 { 1102 elementFound = true; 1103 } 1104 } 1105 else if (isalpha(element[1])) 1106 { 1107 element.erase(0,1); 1108 elementFound = true; 1109 } 1110 } 1111 1112 if (!elementFound) 1113 { 1114 stringstream errorMsg; 1115 errorMsg << "WARNING: Problems reading a PDBQT file\n" 1116 << " Problems reading a HETATM or ATOM record.\n" 1117 << " According to the PDBQT specification,\n" 1118 << " columns 78-79 should contain the element symbol of an atom.\n" 1119 << " but OpenBabel found '" << element << "' (atom " << mol.NumAtoms()+1 << ")"; 1120 obErrorLog.ThrowError(__FUNCTION__, errorMsg.str(), obError); 1121 return false; 1122 } 1123 1124 // charge - optional 1125 string scharge; 1126 if (sbuf.size() > 69) 1127 { 1128 scharge = sbuf.substr(64,6); 1129 } 1130 1131 //trim spaces on the right and left sides 1132 while (!atmid.empty() && atmid[0] == ' ') 1133 atmid = atmid.erase(0, 1); 1134 1135 while (!atmid.empty() && atmid[atmid.size()-1] == ' ') 1136 atmid = atmid.substr(0,atmid.size()-1); 1137 1138 /* residue name */ 1139 string resname = sbuf.substr(11,3); 1140 if (resname == " ") resname = "UNK"; 1141 else 1142 { 1143 while (!resname.empty() && resname[0] == ' ') 1144 resname = resname.substr(1,resname.size()-1); 1145 while (!resname.empty() && resname[resname.size()-1] == ' ') 1146 resname = resname.substr(0,resname.size()-1); 1147 } 1148 1149 OBAtom atom; 1150 /* X, Y, Z */ 1151 string xstr = sbuf.substr(24,8); 1152 string ystr = sbuf.substr(32,8); 1153 string zstr = sbuf.substr(40,8); 1154 vector3 v(atof(xstr.c_str()),atof(ystr.c_str()),atof(zstr.c_str())); 1155 atom.SetVector(v); 1156 1157 // useful for debugging unknown atom types (e.g., PR#1577238) 1158 // cout << mol.NumAtoms() + 1 << " : '" << element << "'" << " " << OBElements::GetAtomicNum(element.c_str()) << endl; 1159 1160 1161 atom.SetAtomicNum(OBElements::GetAtomicNum(element.c_str())); 1162 1163 if ( (! scharge.empty()) && " " != scharge ) 1164 { 1165 stringstream sst; 1166 sst.str(scharge); 1167 double charge; 1168 sst >> charge; 1169 if ( !sst.fail() ) 1170 { 1171 atom.SetPartialCharge(charge); 1172 } 1173 else 1174 { 1175 stringstream errorMsg; 1176 errorMsg << "WARNING: Problems reading a PDBQT file\n" 1177 << " Problems reading a HETATM or ATOM record.\n" 1178 << " According to the PDBQT specification,\n" 1179 << " columns 72-76 should contain charge of the atom\n" 1180 << " but OpenBabel found '" << scharge << "' (atom " << mol.NumAtoms()+1 << ")."; 1181 obErrorLog.ThrowError(__FUNCTION__, errorMsg.str(), obWarning); 1182 } 1183 } 1184 else 1185 { 1186 atom.SetPartialCharge(0.); 1187 } 1188 1189 /* residue sequence number */ 1190 string resnum = sbuf.substr(16,4); 1191 char icode = sbuf[20]; 1192 if(icode == ' ') icode = 0; 1193 1194 OBResidue *res = mol.NumResidues() > 0 ? mol.GetResidue(mol.NumResidues()-1) : nullptr; 1195 if (res == nullptr || res->GetName() != resname 1196 || res->GetNumString() != resnum || res->GetInsertionCode() != icode) 1197 { 1198 vector<OBResidue*>::iterator ri; 1199 for (res = mol.BeginResidue(ri) ; res ; res = mol.NextResidue(ri)) 1200 if (res->GetName() == resname 1201 && res->GetNumString() == resnum 1202 && res->GetInsertionCode() == icode 1203 && static_cast<int>(res->GetChain()) == chain) 1204 break; 1205 1206 if (res == nullptr) 1207 { 1208 res = mol.NewResidue(); 1209 res->SetChain(chain); 1210 res->SetName(resname); 1211 res->SetNum(resnum); 1212 res->SetInsertionCode(icode); 1213 } 1214 } 1215 1216 if (!mol.AddAtom(atom)) 1217 return(false); 1218 else 1219 { 1220 OBAtom *atom = mol.GetAtom(mol.NumAtoms()); 1221 1222 res->AddAtom(atom); 1223 res->SetSerialNum(atom, atoi(serno.c_str())); 1224 res->SetAtomID(atom, sbuf.substr(6,4)); 1225 res->SetHetAtom(atom, hetatm); 1226 return(true); 1227 } 1228 } // end reading atom records 1229 1230 } //namespace OpenBabel 1231