1 /**********************************************************************
2 Copyright (C) 2012 GNM http://www.gnm.cl
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 
14 #include <openbabel/babelconfig.h>
15 #include <openbabel/obmolecformat.h>
16 #include <openbabel/mol.h>
17 #include <openbabel/atom.h>
18 #include <openbabel/elements.h>
19 #include <openbabel/generic.h>
20 
21 #include <cstdlib>
22 
23 using namespace std;
24 namespace OpenBabel
25 {
26 
27 class LpmdFormat : public OBMoleculeFormat
28 // Derive directly from OBFormat for objects which are not molecules.
29 {
30  public:
31   //Register this format type ID in the constructor
LpmdFormat()32   LpmdFormat()
33   {
34    file_line=0;
35    OBConversion::RegisterFormat("lpmd",this);
36   }
Description()37   virtual const char* Description() //required
38   {
39    return
40     "LPMD format\n"
41     "Read and write LPMD's atomic configuration file\n\n"
42     "Read Options e.g. -ab\n"
43     "  s  Output single bonds only\n"
44     "  b  Disable bonding entirely\n\n"
45     "Write Options e.g. -xf 1\n"
46     "  f# Indicate the level of the output file: 0 (default), 1 or 2.\n"
47     "  m# Indicate the mode for level 2 output files\n"
48     "        0 (default) is for accelerations and 1 for forces\n"
49     "  c <vectorcells> Set the cell vectors if not present\n"
50     "        Example: ``-xc 10.0,0,0,0.0,10.0,0.0,0.0,0.0,20.0``\n"
51     "  e Add the charge to the output file\n\n"
52     ;
53   };
54 
55   //Optional URL where the file format is specified
SpecificationURL()56   virtual const char* SpecificationURL()
57   {return "http://www.lpmd.cl/index.php/documentation/the-lpmd-format";};
58 
59   //Optional
GetMIMEType()60   virtual const char* GetMIMEType()  { return "chemical/lpmd"; };
61 
SkipObjects(int n,OBConversion * pConv)62   virtual int SkipObjects(int n, OBConversion* pConv)
63   {
64    return 0;
65   };
66 
67   ////////////////////////////////////////////////////
68   /// Declarations for the "API" interface functions. Definitions are below
69 
70   //Converts a string to a numerical type
71   //This purloined from: http://www.codeguru.com/forum/showthread.php?t=231054
72   template <class T>
from_string(T & t,const std::string & s,std::ios_base & (* f)(std::ios_base &))73   bool from_string(T& t, const std::string& s, std::ios_base& (*f)(std::ios_base&))
74   {
75    std::istringstream iss(s);
76    return !(iss >> f >> t).fail();
77   }
78 
79   bool ReadHeader(std::istream &ifs, OBMol &mol);
80   bool ReadAtom(std::istream &ifs, OBMol &mol);
81   virtual bool ReadMolecule(OBBase* pOb, OBConversion* pConv);
82   virtual bool WriteMolecule(OBBase* pOb, OBConversion* pConv);
83 
84  private:
85   char buffer[BUFF_SIZE];
86   std::vector<std::string> tokens;
87   std::vector<std::string> headers; //Header information.
88   int N; //the number of atoms
89   int file_line;
90   std::vector< vector3 > forces;
91   std::vector< vector3 > accele;
92   std::vector< vector3 > veloci;
93 };
94 
95 LpmdFormat theLpmdFormat;
96 
ReadHeader(std::istream & ifs,OBMol & mol)97 bool LpmdFormat::ReadHeader( std::istream &ifs, OBMol &mol )
98 {
99  //Header Line
100  if( ! ifs.getline(buffer,BUFF_SIZE) )
101  {
102   obErrorLog.ThrowError(__FUNCTION__,"Problem reading header line",obWarning);
103   return false;
104  }
105  tokenize(tokens, buffer," ");
106  if(tokens.size() == 0)
107  {
108   obErrorLog.ThrowError(__FUNCTION__,"The initial line it is empty!!! non LPMD format",obError);
109   return false;
110  }
111  if(tokens.at(0).compare("LPMD") != 0 || tokens.at(1).compare("2.0") != 0)
112  {
113   obErrorLog.ThrowError(__FUNCTION__,"The start line, doesn't identify this file like a lpmd 2.0 file",obError);
114   return false;
115  }
116  if(tokens.size()==3 && tokens.at(2).compare("Z")==0)
117  {
118   obErrorLog.ThrowError(__FUNCTION__,"There is not support for zipped files yet.",obError);
119   return false;
120  }
121  if ( ! ifs.getline(buffer,BUFF_SIZE) )
122  {
123   obErrorLog.ThrowError(__FUNCTION__,"Problem reading header line",obError);
124   return false;
125  }
126  tokenize(headers, buffer, " ");
127  if(headers.size()<=1 || headers.at(0).compare("HDR")!=0)
128  {
129   obErrorLog.ThrowError(__FUNCTION__,"Problem reading header, check the HDR line",obError);
130  }
131  file_line = 2;
132  return true;
133 } // End ReadHeader
134 /////////////////////////////////////////////////////////////////
ReadMolecule(OBBase * pOb,OBConversion * pConv)135 bool LpmdFormat::ReadMolecule(OBBase* pOb, OBConversion* pConv)
136 {
137  OBMol* pmol = pOb->CastAndClear<OBMol>();
138  if (pmol == nullptr) return false;
139 
140  N=0;
141  std::istream &ifs = *pConv->GetInStream();
142  OBMol &mol = *pmol;
143 
144  //Read the header only once.
145  if(file_line==0)
146  {
147   if ( ! ReadHeader( ifs, mol ) ) return false;
148  }
149  std::stringstream ErrMsg;
150  //Read the cell vectors.
151  double ax=0.0e0,ay=0.0e0,az=0.0e0;
152  double bx=0.0e0,by=0.0e0,bz=0.0e0;
153  double cx=0.0e0,cy=0.0e0,cz=0.0e0;
154  //Reading the number of atoms.
155  ifs.getline(buffer,BUFF_SIZE);
156  file_line++;
157  tokenize(tokens, buffer, " ");
158  if(tokens.size()!=1)
159  {
160   obErrorLog.ThrowError(__FUNCTION__,"The number of atoms line was not correctly readed",obError);
161  }
162  from_string<int>(N, tokens.at(0), std::dec);
163 
164  tokens.clear();
165  ifs.getline(buffer,BUFF_SIZE);
166  file_line++;
167  tokenize(tokens, buffer, " ");
168  from_string<double>(ax, tokens.at(0), std::dec);
169  from_string<double>(ay, tokens.at(1), std::dec);
170  from_string<double>(az, tokens.at(2), std::dec);
171  from_string<double>(bx, tokens.at(3), std::dec);
172  from_string<double>(by, tokens.at(4), std::dec);
173  from_string<double>(bz, tokens.at(5), std::dec);
174  from_string<double>(cx, tokens.at(6), std::dec);
175  from_string<double>(cy, tokens.at(7), std::dec);
176  from_string<double>(cz, tokens.at(8), std::dec);
177 
178  vector3 vx = vector3(ax,ay,az);
179  vector3 vy = vector3(bx,by,bz);
180  vector3 vz = vector3(cx,cy,cz);
181  OBUnitCell * unitcell = new OBUnitCell();
182  unitcell->SetData( vx, vy, vz );
183 
184  //////////////////////////////////////////////////////
185  //Start molecule modification.////////////////////////
186  //////////////////////////////////////////////////////
187  mol.BeginModify();
188  mol.SetData( unitcell );
189 
190  for(int i=0 ; i<N ; ++i)
191  {
192   OBAtom *atom  = mol.NewAtom();
193   ifs.getline(buffer,BUFF_SIZE);
194   file_line++;
195   tokenize(tokens, buffer, " ");
196   if(int(headers.size()-1)!=tokens.size())
197   {
198    ErrMsg << "There was a problem reading an atomic configuration,  "
199           << "the line # " << file_line << " doesn't have the number "
200 	  << "of columns indicated in the HDR (second line). ";
201    obErrorLog.ThrowError(__FUNCTION__, ErrMsg.str(), obError);
202   }
203   //Based in the OBAtom class, only X Y Z VX VY VZ FX FY FZ
204   //FCH PCH CHG are the first candidates to use from a LPMD file.
205   double X=0.0,Y=0.0,Z=0.0, VX=0.0, VY=0.0, VZ=0.0;
206   double AX=0.0, AY=0.0, AZ=0.0, FX=0.0, FY=0.0, FZ=0.0;
207   double CHG=0.0,FCH=0.0;
208   std::string symbol = "Xx";
209   int ma=0,mf=0; //mode for acceleration or forces.
210   for(int i=1 ; i < headers.size() ; ++i) //the first element is HDR
211   {
212    //Basic information position and atomic symbol.
213    if(headers.at(i).compare("X")==0) from_string<double>(X, tokens.at(i-1), std::dec);
214    if(headers.at(i).compare("Y")==0) from_string<double>(Y, tokens.at(i-1), std::dec);
215    if(headers.at(i).compare("Z")==0) from_string<double>(Z, tokens.at(i-1), std::dec);
216    if(headers.at(i).compare("SYM")==0) symbol = tokens.at(i-1);
217    //Velocities
218    if(headers.at(i).compare("VX")==0) from_string<double>(VX, tokens.at(i-1), std::dec);
219    if(headers.at(i).compare("VY")==0) from_string<double>(VY, tokens.at(i-1), std::dec);
220    if(headers.at(i).compare("VZ")==0) from_string<double>(VZ, tokens.at(i-1), std::dec);
221    //Accelerations
222    if(headers.at(i).compare("AX")==0) {from_string<double>(AX, tokens.at(i-1), std::dec);ma++;}
223    if(headers.at(i).compare("AY")==0) {from_string<double>(AY, tokens.at(i-1), std::dec);ma++;}
224    if(headers.at(i).compare("AZ")==0) {from_string<double>(AZ, tokens.at(i-1), std::dec);ma++;}
225    //Forces
226    if(headers.at(i).compare("FX")==0) {from_string<double>(FX, tokens.at(i-1), std::dec);mf++;}
227    if(headers.at(i).compare("FY")==0) {from_string<double>(FY, tokens.at(i-1), std::dec);mf++;}
228    if(headers.at(i).compare("FZ")==0) {from_string<double>(FZ, tokens.at(i-1), std::dec);mf++;}
229    //Charges
230    if(headers.at(i).compare("CHG")==0) from_string<double>(CHG, tokens.at(i-1), std::dec);
231    if(headers.at(i).compare("PHG")==0) from_string<double>(CHG, tokens.at(i-1), std::dec);
232    if(headers.at(i).compare("FHG")==0) from_string<double>(FCH, tokens.at(i-1), std::dec);
233   }
234   atom->SetVector(unitcell->FractionalToCartesian(vector3(X,Y,Z)));
235   int atomicNum = OBElements::GetAtomicNum(symbol.c_str());
236   atom->SetAtomicNum(atomicNum);
237   //Conditional or zero??
238   if( CHG!=0.0e0 ) atom->SetPartialCharge(CHG);
239   //Always fill it?
240   if(ma!=0 && mf ==0)
241   {
242    double mass = atom->GetAtomicMass();
243    double FX = AX/mass; double FY = AY/mass; double FZ = AZ/mass;
244   }
245   forces.push_back(vector3(FX,FY,FZ));
246   veloci.push_back(vector3(VX,VY,VZ));
247  }
248 
249  OBConformerData * conformer = new OBConformerData();
250  std::vector< std::vector< vector3 > > forceslist;
251  std::vector< std::vector< vector3 > > velocilist;
252  forceslist.push_back( forces );
253  velocilist.push_back( veloci );
254  conformer->SetForces( forceslist );
255  conformer->SetVelocities( velocilist );
256  mol.SetData(conformer);
257 
258  while(ifs.peek() != EOF && ifs.good() &&
259        (ifs.peek() == '\n' || ifs.peek() == '\r'))
260    ifs.getline(buffer,BUFF_SIZE);
261 
262  if (!pConv->IsOption("b",OBConversion::INOPTIONS))
263     mol.ConnectTheDots();
264  if (!pConv->IsOption("s",OBConversion::INOPTIONS) && !pConv->IsOption("b",OBConversion::INOPTIONS))
265     mol.PerceiveBondOrders();
266 
267  mol.EndModify();
268  return true;
269 }
270 
271 ////////////////////////////////////////////////////////////////
272 
WriteMolecule(OBBase * pOb,OBConversion * pConv)273 bool LpmdFormat::WriteMolecule(OBBase* pOb, OBConversion* pConv)
274 {
275  OBMol* pmol = dynamic_cast<OBMol*>(pOb);
276  if (pmol == nullptr) return false;
277 
278  ostream& ofs = *pConv->GetOutStream();
279  OBMol &mol = *pmol;
280  OBUnitCell myUC;
281  OBUnitCell *uc = nullptr;
282 
283  std::vector< std::vector< vector3 > > forceslist;
284  std::vector< std::vector< vector3 > > velocilist;
285 
286  std::stringstream ErrMsg;
287  std::vector < vector3 > v;
288 
289  //Vectors read from command line.
290  const char *c = pConv->IsOption("c");
291  if(c)
292  {
293   double ax=0.0,ay=0.0,az=0.0;
294   double bx=0.0,by=0.0,bz=0.0;
295   double cx=0.0,cy=0.0,cz=0.0;
296   tokenize(tokens, c, ",");
297   from_string<double>(ax, tokens.at(0), std::dec);
298   from_string<double>(ay, tokens.at(1), std::dec);
299   from_string<double>(az, tokens.at(2), std::dec);
300   from_string<double>(bx, tokens.at(3), std::dec);
301   from_string<double>(by, tokens.at(4), std::dec);
302   from_string<double>(bz, tokens.at(5), std::dec);
303   from_string<double>(cx, tokens.at(6), std::dec);
304   from_string<double>(cy, tokens.at(7), std::dec);
305   from_string<double>(cz, tokens.at(8), std::dec);
306   v.push_back(vector3(ax,ay,az));
307   v.push_back(vector3(bx,by,bz));
308   v.push_back(vector3(cx,cy,cz));
309   myUC.SetData(v[0],v[1],v[2]);
310   uc = &myUC;
311  }
312 
313  if(mol.HasData(OBGenericDataType::UnitCell))
314  {
315   v.clear();
316   uc = (OBUnitCell*)mol.GetData(OBGenericDataType::UnitCell);
317   v = uc -> GetCellVectors();
318  }
319  else if(v.size()!=3)
320  {
321   ErrMsg << "\n*******************************************************************\n"
322    << "Molecule -> " << pConv->GetOutputIndex()<< " : \n"
323    << "The original file doesn't have the information about the unitcell\n"
324    << "*******************************************************************\n";
325   obErrorLog.ThrowError(__FUNCTION__, ErrMsg.str(), obError);
326   return false;
327  }
328 
329  //Define level and mode [(f)orce or (a)cceleration] and extra [(c)harge].
330  int level=0, mode=0;
331  bool charge = false;
332  const char* p = pConv->IsOption("f");
333  const char* q = pConv->IsOption("m");
334  const char* r = pConv->IsOption("e");
335  if(p) level = atoi(p);
336  if(q) mode  = atoi(q);
337  if(r) charge = true;
338 
339  if(pConv->GetOutputIndex()==1)
340  {
341   ofs << "LPMD 2.0 L\n" ;
342   ofs << "HDR X Y Z ";
343   if(level>=1) ofs << "VX VY VZ ";
344   if(level>=2 && mode==0) ofs << "AX AY AZ ";
345   if(level>=2 && mode==1) ofs << "FX FY FZ ";
346   if(charge) ofs << "CHG ";
347   ofs << '\n';
348  }
349 
350  snprintf(buffer, BUFF_SIZE, "%d\n", mol.NumAtoms());
351  ofs << buffer;
352 
353  //Fill velocities and forces.
354  if(mol.HasData(OBGenericDataType::ConformerData))
355  {
356   OBConformerData * conformer = (OBConformerData*)mol.GetData(OBGenericDataType::ConformerData);
357   forceslist = conformer->GetForces();
358   velocilist = conformer->GetVelocities();
359  }
360  else
361  {
362   std::vector < vector3 > empty;
363   empty.push_back(vector3(0,0,0));
364   empty.push_back(vector3(0,0,0));
365   empty.push_back(vector3(0,0,0));
366   for(int i=0; i < mol.NumAtoms() ; ++i)
367   {
368    forceslist.push_back(empty);
369    velocilist.push_back(empty);
370   }
371  }
372 
373  //Cell vectors
374  snprintf(buffer, BUFF_SIZE, "%-10.3f%-10.3f%-10.3f", v[0].x(),v[0].y(),v[0].z());
375  ofs << buffer;
376  snprintf(buffer, BUFF_SIZE, "%-10.3f%-10.3f%-10.3f", v[1].x(),v[1].y(),v[1].z());
377  ofs << buffer;
378  snprintf(buffer, BUFF_SIZE, "%-10.3f%-10.3f%-10.3f\n", v[2].x(),v[2].y(),v[2].z());
379  ofs << buffer;
380 
381  //Iteration over atoms
382  for(int i=0;i<mol.NumAtoms();++i)
383  {
384   OBAtom *atom = mol.GetAtom(i + 1);
385   vector3 tmp=uc->CartesianToFractional(vector3(atom->GetX(),atom->GetY(),atom->GetZ()));
386   snprintf(buffer, BUFF_SIZE, "%-3s%15.5f%15.5f%15.5f",OBElements::GetSymbol(atom->GetAtomicNum()),
387    tmp.GetX(),
388    tmp.GetY(),
389    tmp.GetZ());
390   ofs << buffer;
391   if(level>=1)
392   {
393    vector3 v = vector3(velocilist[0][i].x(),velocilist[0][i].y(),velocilist[0][i].z());
394    snprintf(buffer, BUFF_SIZE, "%15.5f%15.5f%15.5f",v.x(),v.y(),v.z());
395    ofs << buffer;
396   }
397   if(level>=2 && mode==0)
398   {
399    double mass = atom -> GetAtomicMass();
400    vector3 a = vector3(forceslist[0][i].x()/mass,forceslist[0][i].y()/mass,forceslist[0][i].z()/mass);
401    snprintf(buffer, BUFF_SIZE, "%15.5f%15.5f%15.5f",a.x(),a.y(),a.z());
402    ofs << buffer;
403   }
404   if(level>=2 && mode==1)
405   {
406    vector3 force = vector3(forceslist[0][i].x(),forceslist[0][i].y(),forceslist[0][i].z());
407    snprintf(buffer, BUFF_SIZE, "%15.5f%15.5f%15.5f",force.x(),force.y(),force.z());
408    ofs << buffer;
409   }
410   if(charge)
411   {
412    snprintf(buffer, BUFF_SIZE, "%15.5f", atom -> GetPartialCharge());
413    ofs << buffer;
414   }
415   ofs << '\n';
416  }
417 
418  return(true);
419 }
420 
421 } //namespace OpenBabel
422