1 /********************************************************************** 2 Copyright (C) 2010 by Jens Thomas 3 4 This program is free software; you can redistribute it and/or modify 5 it under the terms of the GNU General Public License as published by 6 the Free Software Foundation version 2 of the License. 7 8 This program is distributed in the hope that it will be useful, 9 but WITHOUT ANY WARRANTY; without even the implied warranty of 10 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 GNU General Public License for more details. 12 ***********************************************************************/ 13 #include <openbabel/babelconfig.h> 14 #include <openbabel/obmolecformat.h> 15 #include <openbabel/mol.h> 16 #include <openbabel/atom.h> 17 #include <openbabel/elements.h> 18 #include <openbabel/generic.h> 19 #include <openbabel/obiter.h> 20 21 #include <iomanip> 22 #include <map> 23 24 namespace OpenBabel 25 { 26 27 class DlpolyInputReader 28 { 29 /* 30 * Base class for the CONFIG and HISTORY file parsers 31 */ 32 33 public: 34 35 // Parse the header; levcfg, imcon & unitcell 36 bool ParseHeader( std::istream &ifs, OBMol &mol ); 37 38 // Parse the unit cell info 39 bool ParseUnitCell( std::istream &ifs, OBMol &mol ); 40 41 // Read the data associated with a single atom 42 bool ReadAtom( std::istream &ifs, OBMol &mol ); 43 44 /** 45 * Converts a string to a numerical type 46 * This purloined from: http://www.codeguru.com/forum/showthread.php?t=231054 47 */ 48 template <class T> from_string(T & t,const std::string & s,std::ios_base & (* f)(std::ios_base &))49 bool from_string(T& t, const std::string& s, 50 std::ios_base& (*f)(std::ios_base&)) 51 { 52 std::istringstream iss(s); 53 return !(iss >> f >> t).fail(); 54 } 55 56 int LabelToAtomicNumber(std::string label); 57 58 std::stringstream errorMsg; 59 60 char buffer[BUFF_SIZE]; 61 std::string line; // For convenience so we can refer to lines from the iterator as 'line' 62 std::vector<std::string> tokens; // list of lines and list of tokens on a line 63 64 // These are data that are accessed by several subroutines, so we keep them as class variables 65 int levcfg,imcon; 66 std::string title; 67 std::vector< vector3 > forces; 68 typedef std::map<std::string, int> labelToZType; 69 labelToZType labelToZ; // For storing previously determined labels 70 71 }; // End DlpolyInputReader 72 LabelToAtomicNumber(std::string label)73 int DlpolyInputReader::LabelToAtomicNumber(std::string label) 74 { 75 /* 76 * Given a string with the label for an atom return the atomic number 77 * As we are using the GetAtomicNum function case is not important 78 */ 79 // Check we don't have it already 80 labelToZType::const_iterator it; 81 it = labelToZ.find( label ); 82 if ( it != labelToZ.end() ) return it-> second; 83 84 // See if the first 2 characters give us a valid atomic number 85 int Z=OBElements::GetAtomicNum(label.substr(0,2).c_str()); 86 87 // If not try the first one 88 if (Z==0) Z=OBElements::GetAtomicNum(label.substr(0,1).c_str()); 89 90 if (Z==0){ 91 // Houston... 92 errorMsg << "LabelToAtomicNumber got bad Label: " << label << std::endl; 93 obErrorLog.ThrowError(__FUNCTION__, errorMsg.str(), obWarning); 94 } 95 96 // Put it in the map 97 labelToZ.insert( std::pair<std::string,int>(label,Z) ); 98 return Z; 99 100 } //End LabelToAtomicNumber 101 ParseHeader(std::istream & ifs,OBMol & mol)102 bool DlpolyInputReader::ParseHeader( std::istream &ifs, OBMol &mol ) 103 { 104 105 // Title line 106 if ( ! ifs.getline(buffer,BUFF_SIZE) ) 107 { 108 obErrorLog.ThrowError(__FUNCTION__, "Problem reading title line", obWarning); 109 return false; 110 } 111 title=buffer; 112 Trim(title); // Remove leading & trailing space 113 mol.BeginModify(); 114 mol.SetTitle(title); 115 mol.EndModify(); 116 117 // levcfg, imcon & poss natms 118 if ( ! ifs.getline(buffer,BUFF_SIZE) ) 119 { 120 line=buffer; 121 line="Problem reading levcfg line: " + line; 122 obErrorLog.ThrowError(__FUNCTION__, line, obWarning); 123 return false; 124 } 125 126 tokenize(tokens, buffer, " \t\n"); 127 if ( tokens.size() < 2 || ! ( from_string<int>(levcfg, tokens.at(0), std::dec) 128 && from_string<int>(imcon, tokens.at(1), std::dec) ) ) 129 { 130 line=buffer; 131 line="Problem reading keytrj line: " + line; 132 obErrorLog.ThrowError(__FUNCTION__, line, obWarning); 133 return false; 134 } 135 136 return true; 137 138 } // End ParseHeader 139 ParseUnitCell(std::istream & ifs,OBMol & mol)140 bool DlpolyInputReader::ParseUnitCell( std::istream &ifs, OBMol &mol ) 141 { 142 /* 143 * Need to work out how to shift the origin of the cell, so for now 144 * we skip this 145 */ 146 147 //ifs.getline(buffer,BUFF_SIZE); 148 //ifs.getline(buffer,BUFF_SIZE); 149 //ifs.getline(buffer,BUFF_SIZE); 150 //return true; 151 152 bool ok; 153 double x,y,z; 154 ifs.getline(buffer,BUFF_SIZE); 155 tokenize(tokens, buffer, " \t\n"); 156 ok = from_string<double>(x, tokens.at(0), std::dec); 157 ok = from_string<double>(y, tokens.at(1), std::dec); 158 ok = from_string<double>(z, tokens.at(2), std::dec); 159 vector3 vx = vector3( x, y, z ); 160 161 ifs.getline(buffer,BUFF_SIZE); 162 tokenize(tokens, buffer, " \t\n"); 163 ok = from_string<double>(x, tokens.at(0), std::dec); 164 ok = from_string<double>(y, tokens.at(1), std::dec); 165 ok = from_string<double>(z, tokens.at(2), std::dec); 166 vector3 vy = vector3( x, y, z ); 167 168 ifs.getline(buffer,BUFF_SIZE); 169 tokenize(tokens, buffer, " \t\n"); 170 ok = from_string<double>(x, tokens.at(0), std::dec); 171 ok = from_string<double>(y, tokens.at(1), std::dec); 172 ok = from_string<double>(z, tokens.at(2), std::dec); 173 vector3 vz = vector3( x, y, z ); 174 175 // Add the Unit Cell to the molecule 176 OBUnitCell * unitcell = new OBUnitCell(); 177 unitcell->SetData( vx, vy, vz ); 178 unitcell->SetSpaceGroup(1); 179 //std::cout << "Set unit cell " << vx << vy << vz << std::endl; 180 mol.BeginModify(); 181 mol.SetData( unitcell ); 182 mol.EndModify(); 183 184 return true; 185 186 } // End ParseUnitCell 187 188 ReadAtom(std::istream & ifs,OBMol & mol)189 bool DlpolyInputReader::ReadAtom( std::istream &ifs, OBMol &mol ) 190 { 191 192 std::string AtomLabel; 193 int AtomIndex,AtomicNumber=-1; 194 double x,y,z; 195 OBAtom *atom; 196 bool ok; 197 198 // Line with AtomLabel, AtomIndex & AtomicNumber - only AtomLabel required 199 if ( ! ifs.getline(buffer,BUFF_SIZE) ) return false; 200 //std::cout << "Got Atom line " << buffer << std::endl; 201 202 tokenize(tokens, buffer, " \t\n"); 203 AtomLabel = tokens.at(0); 204 205 // Currently we ignore atom index as it is optional - assume atoms are in order 206 if ( tokens.size() >= 2 ) ok = from_string<int>(AtomIndex, tokens.at(1), std::dec); 207 208 if ( tokens.size() == 3 ) 209 { 210 ok = from_string<int>(AtomicNumber, tokens.at(2), std::dec); 211 if ( ! ok ) AtomicNumber=-1; 212 } 213 214 // Got data so read in coordinates on next line 215 if ( !ifs.getline(buffer,BUFF_SIZE) ) return false; 216 tokenize(tokens, buffer, " \t\n"); 217 ok = from_string<double>(x, tokens.at(0), std::dec); 218 ok = from_string<double>(y, tokens.at(1), std::dec); 219 ok = from_string<double>(z, tokens.at(2), std::dec); 220 221 // Check if we've read in the atomic charge or need to try and extract it from the label 222 if ( AtomicNumber == -1 ) { 223 AtomicNumber = LabelToAtomicNumber( AtomLabel ); 224 } 225 226 atom = mol.NewAtom(); 227 atom->SetAtomicNum(AtomicNumber); 228 atom->SetVector(x, y, z); //set coordinates 229 230 // Reset Atomic Number 231 AtomicNumber=-1; 232 233 // levcfg > 0, next line will be velocities - skip for now 234 if (levcfg > 0 ) 235 { 236 if ( !ifs.getline(buffer,BUFF_SIZE) ) return false; 237 } 238 239 // levcfg > 1, next line will be forces 240 if ( levcfg > 1 ) 241 { 242 if ( !ifs.getline(buffer,BUFF_SIZE) ) return false; 243 tokenize(tokens, buffer, " \t\n"); 244 ok = from_string<double>(x, tokens.at(0), std::dec); 245 ok = from_string<double>(y, tokens.at(1), std::dec); 246 ok = from_string<double>(z, tokens.at(2), std::dec); 247 forces.push_back( vector3( x,y,z ) ); 248 //std::cout << "ADDING FORCE " << x << ":" << y << ":" << z << std::endl; 249 } 250 251 return true; 252 253 } // End ReadAtom 254 255 256 class DlpolyConfigFormat : public OBMoleculeFormat, public DlpolyInputReader 257 { 258 public: 259 //Register this format type ID DlpolyConfigFormat()260 DlpolyConfigFormat() 261 { 262 OBConversion::RegisterFormat("CONFIG",this); 263 } 264 Description()265 virtual const char* Description() //required 266 { 267 return "DL-POLY CONFIG\n"; 268 }; 269 SpecificationURL()270 virtual const char* SpecificationURL() 271 { 272 return "http://www.cse.scitech.ac.uk/ccg/software/DL_POLY"; 273 }; 274 275 //Flags() can return be any the following combined by | or be omitted if none apply 276 // NOTREADABLE READONEONLY NOTWRITABLE WRITEONEONLY Flags()277 virtual unsigned int Flags() 278 { 279 return WRITEONEONLY; 280 }; 281 282 //////////////////////////////////////////////////// 283 /// The "API" interface functions 284 virtual bool ReadMolecule(OBBase* pOb, OBConversion* pConv); 285 virtual bool WriteMolecule(OBBase* pOb, OBConversion* pConv); 286 287 }; 288 289 //Make an instance of the format class 290 DlpolyConfigFormat theDlpolyConfigFormat; 291 ReadMolecule(OBBase * pOb,OBConversion * pConv)292 bool DlpolyConfigFormat::ReadMolecule(OBBase* pOb, OBConversion* pConv) 293 { 294 295 bool ok; 296 297 // Reset data 298 levcfg=0; 299 imcon=0; 300 forces.clear(); 301 302 OBMol* pmol = pOb->CastAndClear<OBMol>(); 303 if (pmol == nullptr) 304 return false; 305 306 //Define some references so we can use the old parameter names 307 std::istream &ifs = *pConv->GetInStream(); 308 OBMol &mol = *pmol; 309 310 if ( ! ParseHeader( ifs, mol ) ) return false; 311 312 // If imcon > 0 then there are 3 lines with the cell vectors 313 if ( imcon > 0 ) ParseUnitCell( ifs, mol ); 314 315 mol.BeginModify(); 316 ok = true; 317 while ( ok ) 318 { 319 ok = ReadAtom( ifs, mol ); 320 } 321 322 // Add forces as conformer data 323 if ( levcfg > 1 && forces.size() ) 324 { 325 OBConformerData * conformer = new OBConformerData(); 326 std::vector< std::vector< vector3 > > conflist; 327 conflist.push_back( forces ); 328 conformer->SetForces( conflist ); 329 mol.SetData( conformer ); 330 } 331 332 mol.EndModify(); 333 334 if ( mol.NumAtoms() == 0 ) 335 return(false); 336 else 337 return(true); 338 339 } // End ReadMolecule 340 341 WriteMolecule(OBBase * pOb,OBConversion * pConv)342 bool DlpolyConfigFormat::WriteMolecule(OBBase* pOb, OBConversion* pConv) 343 { 344 345 /** 346 * Write a DL-POLY CONFIG file. Ints are 10 chars wide, floats are formatted as 20.15f 347 */ 348 349 OBMol* pmol = dynamic_cast<OBMol*>(pOb); 350 if (pmol == nullptr) 351 return false; 352 353 //Define some references so we can use the old parameter names 354 std::ostream &ofs = *pConv->GetOutStream(); 355 OBMol &mol = *pmol; 356 357 // We only print the coordinates and labels 358 levcfg=0; 359 imcon=0; 360 361 int idx=0; // For counting molecule index 362 std::string title = mol.GetTitle(); 363 364 // 80 char title 365 ofs << title.substr(0,80) << std::endl; 366 367 // Set levcfg & imcon 368 ofs << std::setw(10) << levcfg << std::setw(10) << imcon << std::endl; 369 370 // Now loop through molecule 371 FOR_ATOMS_OF_MOL(atom, mol) 372 { 373 374 ofs << std::setw(8) << OBElements::GetSymbol(atom->GetAtomicNum()) << std::setw(10) << ++idx << std::setw(10) << atom->GetAtomicNum() << std::endl; 375 snprintf(buffer, BUFF_SIZE, "%20.15f %20.15f %20.15f\n", 376 atom->GetX(), 377 atom->GetY(), 378 atom->GetZ() 379 ); 380 ofs << buffer; 381 } 382 383 return(true); 384 385 } //End WriteMolecule 386 387 388 class DlpolyHISTORYFormat : public OBMoleculeFormat, public DlpolyInputReader 389 { 390 public: 391 //Register this format type ID DlpolyHISTORYFormat()392 DlpolyHISTORYFormat() 393 { 394 OBConversion::RegisterFormat("HISTORY",this); 395 } 396 Description()397 virtual const char* Description() //required 398 { 399 return "DL-POLY HISTORY\n"; 400 }; 401 SpecificationURL()402 virtual const char* SpecificationURL() 403 { 404 return "http://www.cse.scitech.ac.uk/ccg/software/DL_POLY"; 405 }; 406 407 //Flags() can return be any the following combined by | or be omitted if none apply 408 // NOTREADABLE READONEONLY NOTWRITABLE WRITEONEONLY Flags()409 virtual unsigned int Flags() 410 { 411 return NOTWRITABLE; 412 }; 413 414 //////////////////////////////////////////////////// 415 /// The "API" interface functions 416 virtual bool ReadMolecule(OBBase* pOb, OBConversion* pConv); 417 418 }; 419 420 //Make an instance of the format class 421 DlpolyHISTORYFormat theDlpolyHISTORYFormat; 422 ReadMolecule(OBBase * pOb,OBConversion * pConv)423 bool DlpolyHISTORYFormat::ReadMolecule(OBBase* pOb, OBConversion* pConv) 424 { 425 std::string tstitle; 426 bool ok; 427 int nstep,natms=0; 428 429 levcfg=0; 430 imcon=0; 431 forces.clear(); 432 433 OBMol* pmol = pOb->CastAndClear<OBMol>(); 434 if (pmol == nullptr) 435 return false; 436 437 //Define some references so we can use the old parameter names 438 std::istream &ifs = *pConv->GetInStream(); 439 OBMol &mol = *pmol; 440 441 // Only parse the header if we're at the start of the file 442 if ( ! ifs.tellg() ) 443 { 444 if ( ! ParseHeader( ifs, mol ) ) return false; 445 } 446 447 /* 448 * Read the trajectory line - this tells us how many atoms we read in and 449 * also the timestep which we use to set the title 450 */ 451 if ( ! ifs.getline(buffer,BUFF_SIZE) ) return false; 452 tokenize(tokens, buffer, " \t\n"); 453 if ( tokens.size() < 6 ) 454 { 455 line=buffer; 456 line="Problem reading trajectory line: " + line; 457 obErrorLog.ThrowError(__FUNCTION__, line, obWarning); 458 return false; 459 } 460 461 ok = from_string<int>(nstep, tokens.at(1), std::dec); 462 ok = from_string<int>(natms, tokens.at(2), std::dec); 463 // It ain't gonna work if we don't have natms 464 if ( ! ok ) 465 { 466 line=buffer; 467 line="Problem reading natoms on trajectory line: " + line; 468 obErrorLog.ThrowError(__FUNCTION__, line, obWarning); 469 return false; 470 } 471 // Get imcon as it could change? 472 ok = from_string<int>(levcfg, tokens.at(3), std::dec); 473 ok = from_string<int>(imcon, tokens.at(4), std::dec); 474 475 // Set the title 476 tstitle=title + ": timestep=" + tokens.at(1); 477 mol.SetTitle( tstitle ); 478 479 // If imcon > 0 then there are 3 lines with the cell vectors 480 if ( imcon > 0 ) ParseUnitCell( ifs, mol ); 481 482 // Start of coordinates - just loop through reading in data 483 int atomsRead=0; 484 mol.BeginModify(); 485 while ( true ) 486 { 487 488 if ( ! ReadAtom( ifs, mol ) ) break; 489 atomsRead++; 490 if ( atomsRead >= natms) break; 491 492 } // End while reading loop 493 494 // Add forces as conformer data 495 if ( levcfg > 1 && forces.size() ) 496 { 497 OBConformerData * conformer = new OBConformerData(); 498 std::vector< std::vector< vector3 > > conflist; 499 conflist.push_back( forces ); 500 conformer->SetForces( conflist ); 501 mol.SetData( conformer ); 502 } 503 504 mol.EndModify(); 505 506 if ( mol.NumAtoms() == 0 ) 507 return(false); 508 else 509 return(true); 510 511 } // End ReadMolecule 512 513 } //namespace OpenBabel 514