1 /**********************************************************************
2 Copyright (C) 2011 by Geoffrey Hutchison
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 
16 #include <openbabel/obmolecformat.h>
17 #include <openbabel/mol.h>
18 #include <openbabel/atom.h>
19 #include <openbabel/bond.h>
20 #include <openbabel/obiter.h>
21 #include <openbabel/elements.h>
22 #include <openbabel/generic.h>
23 
24 #include <cstdlib>
25 
26 using namespace std;
27 namespace OpenBabel
28 {
29 
30 #define BOHR_TO_ANGSTROM 0.5291772108
31 #define ANGSTROM_TO_BOHR 1.0 / BOHR_TO_ANGSTROM
32 
33 
34   class ABINITFormat : public OBMoleculeFormat
35   {
36   public:
37     //Register this format type ID
ABINITFormat()38     ABINITFormat()
39     {
40       OBConversion::RegisterFormat("abinit",this);
41     }
42 
Description()43     virtual const char* Description() //required
44     {
45       return
46         "ABINIT Output Format\n"
47         "Read Options e.g. -as\n"
48         "  s  Output single bonds only\n"
49         "  b  Disable bonding entirely\n\n";
50     };
51 
SpecificationURL()52     virtual const char* SpecificationURL()
53     { return "http://abinit.org/" ;}; //optional
54 
55     //Flags() can return be any the following combined by | or be omitted if none apply
56     // NOTREADABLE  READONEONLY  NOTWRITABLE  WRITEONEONLY
Flags()57     virtual unsigned int Flags()
58     {
59       return READONEONLY | NOTWRITABLE;
60     };
61 
62     //*** This section identical for most OBMol conversions ***
63     ////////////////////////////////////////////////////
64     /// The "API" interface functions
65     virtual bool ReadMolecule(OBBase* pOb, OBConversion* pConv);
66   };
67   //***
68 
69   //Make an instance of the format class
70   ABINITFormat theABINITFormat;
71 
72   /////////////////////////////////////////////////////////////////
ReadMolecule(OBBase * pOb,OBConversion * pConv)73   bool ABINITFormat::ReadMolecule(OBBase* pOb, OBConversion* pConv)
74   {
75 
76     OBMol* pmol = pOb->CastAndClear<OBMol>();
77     if (pmol == nullptr)
78       return false;
79 
80     //Define some references so we can use the old parameter names
81     istream &ifs = *pConv->GetInStream();
82     OBMol &mol = *pmol;
83     const char* title = pConv->GetTitle();
84 
85     char buffer[BUFF_SIZE];
86     string str;
87     vector<string> vs;
88 
89     OBAtom *atom;
90     int natom;
91     vector<int> atomicNumbers, atomTypes;
92     double x, y, z;
93     vector<vector3> atomPositions;
94     vector<double> energies;
95 
96     // Translation vectors (if present)
97     // aka rprim
98     vector3 translationVectors[3];
99     double acell[3]; // scale of lattice vectors
100     int numTranslationVectors = 0;
101     int symmetryCode = 0;
102     bool last = false;
103     mol.BeginModify();
104 
105     while (ifs.getline(buffer,BUFF_SIZE))
106       {
107 	if(strstr(buffer,"outvars")){
108 	     last = true;
109 	     continue;
110 	   }
111 	if(! last){
112 	  continue;
113 	}
114         // tokens are listed in alphabetical order for code clarity
115         if (strstr(buffer, "acell")) {
116           tokenize(vs, buffer);
117           if (vs.size() < 4)
118             continue; // invalid line
119 
120           // acell=  7.6967369631E+00  7.6967369631E+00  7.6967369631E+00
121           for (int i = 0; i < 3; ++i) {
122             acell[i] = atof(vs[i+1].c_str());
123           }
124         }
125         else if (strstr(buffer, " xcart ")) {
126           double unit = BOHR_TO_ANGSTROM;
127           if (strstr(buffer, "ngstrom"))
128             unit = 1.0; // no conversion needed
129 
130 	  //          ifs.getline(buffer,BUFF_SIZE);
131           tokenize(vs, buffer);
132 
133           // first line, rprim takes up a token
134           x = atof((char*)vs[1].c_str()) * unit;
135           y = atof((char*)vs[2].c_str()) * unit;
136 	  z = atof((char*)vs[3].c_str()) * unit;
137 	  atomPositions.push_back(vector3(x, y, z));
138 	  // get next line
139 	  ifs.getline(buffer,BUFF_SIZE);
140 	  tokenize(vs, buffer);
141 	  //rest of lines
142 	  while(vs.size() == 3) {
143 	    x = atof(vs[0].c_str()) * unit;
144 	    y = atof(vs[1].c_str()) * unit;
145 	    z = atof(vs[2].c_str()) * unit;
146             atomPositions.push_back(vector3(x, y, z));
147             // get next line
148             ifs.getline(buffer,BUFF_SIZE);
149             tokenize(vs, buffer);
150           }
151       }
152         else if (strstr(buffer, "natom")) {
153           tokenize(vs, buffer);
154           if (vs.size() != 2)
155             continue;
156           natom = atoi(vs[1].c_str());
157         }
158         else if (strstr(buffer, "rprim")) {
159           numTranslationVectors = 0;
160           int column;
161 	  ifs.getline(buffer,BUFF_SIZE);
162           for (int i = 1; i < 4; ++i) {
163             tokenize(vs, buffer);
164             if (vs.size() < 3)
165               break;
166 
167             // first line, rprim takes up a token
168 	    //            if (i == 0)
169 	    // column = 1;
170             //else
171               column = 0;
172 
173             x = atof((char*)vs[column].c_str()) * BOHR_TO_ANGSTROM;
174             y = atof((char*)vs[column+1].c_str()) * BOHR_TO_ANGSTROM;
175             z = atof((char*)vs[column+2].c_str()) * BOHR_TO_ANGSTROM;
176             translationVectors[numTranslationVectors++].Set(x, y,z);
177             ifs.getline(buffer,BUFF_SIZE);
178           }
179         }
180         else if (strstr(buffer, "Symmetries")) {
181           tokenize(vs, buffer, "()");
182           // Should be something like (#160)
183           symmetryCode = atoi(vs[1].substr(1).c_str());
184         }
185         else if (strstr(buffer, "typat")) {
186           atomTypes.clear();
187 	  int n=0;
188 	    while( n<=natom){
189 	      tokenize(vs, buffer);
190 	      for (unsigned int i = 1; i < vs.size(); ++i) {
191 		atomTypes.push_back(atoi(vs[i].c_str()));
192 	      }
193 	      n+=vs.size();
194 	      ifs.getline(buffer,BUFF_SIZE);
195 	    }
196         }
197         else if (strstr(buffer, "znucl")) {
198           tokenize(vs, buffer);
199           // make sure znucl is first token
200           if (vs[0] != "znucl")
201             continue;
202           // push back the remaining tokens into atomicNumbers
203           atomicNumbers.clear();
204           atomicNumbers.push_back(0); // abinit starts typat with 1
205           for (unsigned int i = 1; i < vs.size(); ++i)
206             atomicNumbers.push_back(int(atof(vs[i].c_str())));
207         }
208         // xangst
209         // forces
210       }
211 
212     for (int i = 0; i < natom; ++i) {
213       atom = mol.NewAtom();
214       //set atomic number
215       int idx = atom->GetIdx();
216       int type = atomTypes[idx - 1];
217       atom->SetAtomicNum(atomicNumbers[type]);
218       // we set the coordinates by conformers in another loop
219     }
220 
221     mol.EndModify();
222 
223     int numConformers = atomPositions.size() / natom;
224     for (int i = 0; i < numConformers; ++i) {
225       double *coordinates = new double[natom * 3];
226       for (int j = 0; j < natom; ++j) {
227         vector3 currentPosition = atomPositions[i*natom + j];
228         coordinates[j*3] = currentPosition.x();
229         coordinates[j*3 + 1] = currentPosition.y();
230         coordinates[j*3 + 2] = currentPosition.z();
231       }
232       mol.AddConformer(coordinates);
233     }
234     // Delete first conformer, created by EndModify, bunch of 0s
235     mol.DeleteConformer(0);
236     // Set geometry to last one
237     mol.SetConformer(mol.NumConformers() - 1);
238 
239     if (!pConv->IsOption("b",OBConversion::INOPTIONS))
240       mol.ConnectTheDots();
241     if (!pConv->IsOption("s",OBConversion::INOPTIONS) && !pConv->IsOption("b",OBConversion::INOPTIONS))
242       mol.PerceiveBondOrders();
243 
244     // Attach unit cell translation vectors if found
245     if (numTranslationVectors > 0) {
246       OBUnitCell* uc = new OBUnitCell;
247 	    //      uc->SetData(acell[0] * translationVectors[0], acell[1] * translationVectors[1], acell[2] * translationVectors[2]);
248       uc->SetData(translationVectors[0], translationVectors[1], translationVectors[2]);
249       uc->SetOrigin(fileformatInput);
250       if (symmetryCode)
251         uc->SetSpaceGroup(symmetryCode);
252       mol.SetData(uc);
253     }
254 
255     mol.SetTitle(title);
256     return(true);
257   }
258 
259 } //namespace OpenBabel
260