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