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