/src/openbabel/src/formats/vaspformat.cpp
Line | Count | Source |
1 | | /********************************************************************** |
2 | | Copyright (C) 2004 by Chris Morley for template |
3 | | Copyright (C) 2009 by David C. Lonie for VASP |
4 | | |
5 | | This program is free software; you can redistribute it and/or modify |
6 | | it under the terms of the GNU General Public License as published by |
7 | | the Free Software Foundation version 2 of the License. |
8 | | |
9 | | This program is distributed in the hope that it will be useful, |
10 | | but WITHOUT ANY WARRANTY; without even the implied warranty of |
11 | | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
12 | | GNU General Public License for more details. |
13 | | ***********************************************************************/ |
14 | | |
15 | | #include <openbabel/babelconfig.h> |
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 | | |
25 | | #include <limits> |
26 | | #include <locale> // For isalpha(int) |
27 | | #include <functional> |
28 | | #include <iostream> |
29 | | #include <algorithm> |
30 | | #include <cstdlib> |
31 | | |
32 | 0 | #define EV_TO_KCAL_PER_MOL 23.060538 |
33 | | |
34 | | using namespace std; |
35 | | namespace OpenBabel { |
36 | | class VASPFormat : public OBMoleculeFormat |
37 | | { |
38 | | protected: |
39 | | class compare_sort_items |
40 | | { |
41 | | std::vector<int> csm; |
42 | | bool num_sort; |
43 | | public: |
44 | | compare_sort_items(const std::vector<int> &_custom_sort_nums, bool _num_sort): |
45 | 0 | csm(_custom_sort_nums), num_sort(_num_sort) {}; |
46 | | bool operator()(const OBAtom *a, const OBAtom *b) |
47 | 0 | { |
48 | 0 | int a_num = a->GetAtomicNum(); |
49 | 0 | int b_num = b->GetAtomicNum(); |
50 | 0 | int dist = std::distance(std::find(csm.begin(), csm.end(), b_num), |
51 | 0 | std::find(csm.begin(), csm.end(), a_num)); |
52 | | |
53 | 0 | if ( dist != 0) |
54 | 0 | return dist < 0; |
55 | | |
56 | 0 | if( (num_sort) && ( a_num - b_num != 0 ) ) |
57 | 0 | return a_num < b_num; |
58 | | |
59 | 0 | return false; |
60 | 0 | } |
61 | | }; |
62 | | public: |
63 | | |
64 | | VASPFormat() |
65 | 6 | { |
66 | | // This will actually read the CONTCAR file: |
67 | 6 | OBConversion::RegisterFormat("CONTCAR",this); |
68 | 6 | OBConversion::RegisterFormat("POSCAR",this); |
69 | 6 | OBConversion::RegisterFormat("VASP",this); |
70 | 6 | OBConversion::RegisterOptionParam("s", this, 0, OBConversion::INOPTIONS); |
71 | 6 | OBConversion::RegisterOptionParam("b", this, 0, OBConversion::INOPTIONS); |
72 | 6 | OBConversion::RegisterOptionParam("w", this, 0, OBConversion::OUTOPTIONS); |
73 | 6 | OBConversion::RegisterOptionParam("z", this, 0, OBConversion::OUTOPTIONS); |
74 | 6 | OBConversion::RegisterOptionParam("4", this, 0, OBConversion::OUTOPTIONS); |
75 | 6 | } |
76 | | |
77 | | const char* Description() override |
78 | 0 | { |
79 | 0 | return |
80 | 0 | "VASP format\n" |
81 | 0 | "Reads in data from POSCAR and CONTCAR to obtain information from " |
82 | 0 | "VASP calculations.\n\n" |
83 | |
|
84 | 0 | "Due to limitations in Open Babel's file handling, reading in VASP\n" |
85 | 0 | "files can be a bit tricky; the client that is using Open Babel must\n" |
86 | 0 | "use OBConversion::ReadFile() to begin the conversion. This change is\n" |
87 | 0 | "usually trivial. Also, the complete path to the CONTCAR/POSCAR file\n" |
88 | 0 | "must be provided, otherwise the other files needed will not be\n" |
89 | 0 | "found.\n\n" |
90 | |
|
91 | 0 | "Both VASP 4.x and 5.x POSCAR formats are supported.\n\n" |
92 | |
|
93 | 0 | "By default, atoms are written out in the order they are present in the input\n" |
94 | 0 | "molecule. To sort by atomic number specify ``-xw``. To specify the sort\n" |
95 | 0 | "order, use the ``-xz`` option.\n\n" |
96 | |
|
97 | 0 | "Read Options e.g. -as\n" |
98 | 0 | " s Output single bonds only\n" |
99 | 0 | " b Disable bonding entirely\n\n" |
100 | |
|
101 | 0 | "Write Options e.g. -x4\n" |
102 | 0 | " w Sort atoms by atomic number\n" |
103 | 0 | " z <list of atoms> Specify the order to write out atoms\n" |
104 | 0 | " 'atom1 atom2 ...': atom1 first, atom2 second, etc. The remaining\n" |
105 | 0 | " atoms are written in the default order or (if ``-xw`` is specified)\n" |
106 | 0 | " in order of atomic number.\n" |
107 | 0 | " 4 Write a POSCAR using the VASP 4.x specification.\n" |
108 | 0 | " The default is to use the VASP 5.x specification.\n\n" |
109 | 0 | ; |
110 | |
|
111 | 0 | } |
112 | | |
113 | 0 | const char* SpecificationURL() override { |
114 | 0 | return "http://cms.mpi.univie.ac.at/vasp/vasp/vasp.html"; |
115 | 0 | } |
116 | | |
117 | | /* Flags() can return be any of the following combined by | |
118 | | or be omitted if none apply |
119 | | NOTREADABLE READONEONLY NOTWRITABLE WRITEONEONLY DEFAULTFORMAT |
120 | | READBINARY WRITEBINARY READXML ZEROATOMSOK */ |
121 | | unsigned int Flags() override |
122 | 18 | { |
123 | 18 | return READONEONLY | WRITEONEONLY; |
124 | 18 | } |
125 | | |
126 | | int SkipObjects(int n, OBConversion* pConv) override |
127 | 0 | { |
128 | 0 | return 0; |
129 | 0 | } |
130 | | |
131 | | //////////////////////////////////////////////////// |
132 | | /// Declarations for the "API" interface functions. Definitions are below |
133 | | bool ReadMolecule(OBBase* pOb, OBConversion* pConv) override; |
134 | | bool WriteMolecule(OBBase* pOb, OBConversion* pConv) override; |
135 | | |
136 | | private: |
137 | | /* Add declarations for any local function or member variables used. |
138 | | Generally only a single instance of a format class is used. Keep this in |
139 | | mind if you employ member variables. */ |
140 | | }; |
141 | | //////////////////////////////////////////////////// |
142 | | |
143 | | //Make an instance of the format class |
144 | | VASPFormat theVASPFormat; |
145 | | |
146 | | ///////////////////////////////////////////////////////////////// |
147 | | |
148 | | bool VASPFormat::ReadMolecule(OBBase* pOb, OBConversion* pConv) |
149 | 0 | { |
150 | 0 | OBMol* pmol = pOb->CastAndClear<OBMol>(); |
151 | 0 | if (pmol == nullptr) |
152 | 0 | return false; |
153 | | |
154 | | // Move stream to EOF, some apps check ifs position to check for multimolecule files. |
155 | | // VASP does not support this, and this parser makes its own streams. |
156 | 0 | istream &ifs = *pConv->GetInStream(); |
157 | 0 | ifs.seekg (0, ios::end); |
158 | |
|
159 | 0 | char buffer[BUFF_SIZE], tag[BUFF_SIZE]; |
160 | 0 | double x,y,z, scale; |
161 | 0 | unsigned int totalAtoms=0, atomIndex=0, atomCount=0; |
162 | 0 | OBAtom *atom; |
163 | 0 | bool cartesian; |
164 | 0 | string str, path; |
165 | 0 | vector<string> vs; |
166 | 0 | vector<unsigned int> numAtoms, atomTypes; |
167 | 0 | bool selective; // is selective dynamics used? |
168 | 0 | string key, value; // store the info about constraints |
169 | 0 | OBPairData *cp; // in this PairData |
170 | 0 | bool hasEnthalpy=false; |
171 | 0 | bool hasVibrations=false; |
172 | 0 | bool needSymbolsInGeometryFile = false; |
173 | 0 | double enthalpy_eV, pv_eV; |
174 | 0 | vector<vector <vector3> > Lx; |
175 | 0 | vector<double> Frequencies; |
176 | 0 | vector<matrix3x3> dipGrad; |
177 | | |
178 | | // Get path of CONTCAR/POSCAR: |
179 | | // ifs_path.getline(buffer,BUFF_SIZE); |
180 | | // path = buffer; |
181 | 0 | path = pConv->GetInFilename(); |
182 | 0 | if (path.empty()) return false; // Should be using ReadFile, not Read! |
183 | 0 | size_t found; |
184 | 0 | found = path.rfind("/"); |
185 | 0 | path = path.substr(0, found); |
186 | 0 | if (found == string::npos) path = "./"; // No "/" in path? |
187 | | |
188 | | // Open files |
189 | 0 | string potcar_filename = path + "/POTCAR"; |
190 | 0 | string outcar_filename = path + "/OUTCAR"; |
191 | 0 | string doscar_filename = path + "/DOSCAR"; |
192 | 0 | string contcar_filename = pConv->GetInFilename(); // POSCAR _OR_ CONTCAR |
193 | 0 | ifstream ifs_pot (potcar_filename.c_str()); |
194 | 0 | ifstream ifs_out (outcar_filename.c_str()); |
195 | 0 | ifstream ifs_dos (doscar_filename.c_str()); |
196 | 0 | ifstream ifs_cont (contcar_filename.c_str()); |
197 | 0 | if (!ifs_pot) { |
198 | 0 | needSymbolsInGeometryFile = true; |
199 | 0 | } |
200 | 0 | if (!ifs_cont) { |
201 | 0 | return false; // No geometry file? |
202 | 0 | } |
203 | | |
204 | 0 | pmol->BeginModify(); |
205 | | |
206 | | // Start working on CONTCAR: |
207 | 0 | ifs_cont.getline(buffer,BUFF_SIZE); // Comment line |
208 | 0 | pmol->SetTitle(buffer); |
209 | 0 | ifs_cont.getline(buffer,BUFF_SIZE); // Scale |
210 | 0 | scale = atof(buffer); |
211 | |
|
212 | 0 | ifs_cont.getline(buffer,BUFF_SIZE); // X_Vec vector |
213 | 0 | tokenize(vs, buffer); |
214 | 0 | x = atof(vs.at(0).c_str()) * scale; |
215 | 0 | y = atof(vs.at(1).c_str()) * scale; |
216 | 0 | z = atof(vs.at(2).c_str()) * scale; |
217 | 0 | vector3 x_vec (x,y,z); |
218 | |
|
219 | 0 | ifs_cont.getline(buffer,BUFF_SIZE); // Y_Vec vector |
220 | 0 | tokenize(vs, buffer); |
221 | 0 | x = atof(vs.at(0).c_str()) * scale; |
222 | 0 | y = atof(vs.at(1).c_str()) * scale; |
223 | 0 | z = atof(vs.at(2).c_str()) * scale; |
224 | 0 | vector3 y_vec (x,y,z); |
225 | |
|
226 | 0 | ifs_cont.getline(buffer,BUFF_SIZE); // Z_Vec vector |
227 | 0 | tokenize(vs, buffer); |
228 | 0 | x = atof(vs.at(0).c_str()) * scale; |
229 | 0 | y = atof(vs.at(1).c_str()) * scale; |
230 | 0 | z = atof(vs.at(2).c_str()) * scale; |
231 | 0 | vector3 z_vec (x,y,z); |
232 | | |
233 | | // Build unit cell |
234 | 0 | OBUnitCell *cell = new OBUnitCell; |
235 | 0 | cell->SetData(x_vec, y_vec, z_vec); |
236 | 0 | cell->SetSpaceGroup(1); |
237 | 0 | pmol->SetData(cell); |
238 | | |
239 | | // Next comes either a list of numbers that represent the stoichiometry of |
240 | | // the cell. The numbers are the atom counts for each type, in the order |
241 | | // listed in the POTCAR file. Since VASP 5.2, a line with a list of atomic |
242 | | // element symbols may precede the atom counts. This will be used if the |
243 | | // POTCAR file is not present. If both are present, the data in the POSCAR |
244 | | // or CONTCAR file will be used. |
245 | 0 | ifs_cont.getline(buffer,BUFF_SIZE); |
246 | 0 | tokenize(vs, buffer); |
247 | 0 | bool symbolsInGeometryFile = false; |
248 | 0 | if (vs.size() != 0) { |
249 | 0 | if (vs.at(0).size() != 0) { |
250 | 0 | if (isalpha(static_cast<int>(vs.at(0).at(0))) != 0) { |
251 | 0 | symbolsInGeometryFile = true; |
252 | 0 | } |
253 | 0 | } |
254 | 0 | } |
255 | | |
256 | | // If no element data is present anywhere |
257 | 0 | if (needSymbolsInGeometryFile && !symbolsInGeometryFile && |
258 | | // and there are atoms specified |
259 | 0 | vs.size() != 0) { |
260 | | // Abort read |
261 | 0 | pmol->EndModify(); |
262 | 0 | return false; |
263 | 0 | } |
264 | | |
265 | 0 | if (symbolsInGeometryFile) { |
266 | 0 | atomTypes.clear(); |
267 | 0 | for (size_t i = 0; i < vs.size(); ++i) { |
268 | 0 | atomTypes.push_back(OpenBabel::OBElements::GetAtomicNum(vs.at(i).c_str())); |
269 | 0 | } |
270 | | // Fetch next line to get stoichiometry |
271 | 0 | ifs_cont.getline(buffer,BUFF_SIZE); |
272 | 0 | tokenize(vs, buffer); |
273 | 0 | } |
274 | 0 | else if (ifs_pot) { |
275 | | // Get atom types from POTCAR |
276 | 0 | while (ifs_pot.getline(buffer,BUFF_SIZE)) { |
277 | 0 | if (strstr(buffer,"VRHFIN")) { |
278 | 0 | str = buffer; |
279 | 0 | size_t start = str.find("=") + 1; |
280 | 0 | size_t end = str.find(":"); |
281 | 0 | str = str.substr(start, end - start); |
282 | | // Clean up whitespace: |
283 | 0 | for (unsigned int i = 0; i < str.size(); i++) |
284 | 0 | if (str.at(i) == ' ') { |
285 | 0 | str.erase(i,1); |
286 | 0 | --i; |
287 | 0 | } |
288 | 0 | atomTypes.push_back(OpenBabel::OBElements::GetAtomicNum(str.c_str())); |
289 | 0 | } |
290 | 0 | } |
291 | 0 | ifs_pot.close(); |
292 | 0 | } |
293 | | |
294 | | // Extract and sum the atom counts. The sum is used to parse the atomic |
295 | | // coordinates |
296 | 0 | totalAtoms = 0; |
297 | 0 | for (unsigned int i = 0; i < vs.size(); i++) { |
298 | 0 | int currentCount = atoi(vs.at(i).c_str()); |
299 | 0 | numAtoms.push_back(currentCount); |
300 | 0 | totalAtoms += currentCount; |
301 | 0 | } |
302 | | |
303 | | // Do the number of atom types match the number of atom counts? |
304 | 0 | if (numAtoms.size() != atomTypes.size()) { |
305 | | // If not, abort read |
306 | 0 | pmol->EndModify(); |
307 | 0 | return false; |
308 | 0 | } |
309 | | |
310 | | // Cartesian or fractional? |
311 | 0 | ifs_cont.getline(buffer,BUFF_SIZE); |
312 | 0 | selective = false; |
313 | | // Set the variable selective accordingly |
314 | 0 | if (buffer[0] == 'S' || buffer[0] == 's') { |
315 | 0 | selective = true; |
316 | 0 | ifs_cont.getline(buffer,BUFF_SIZE); |
317 | 0 | } |
318 | | // [C|c|K|k] indicates cartesian coordinates, anything else (including |
319 | | // an empty string, buffer[0] == 0) indicates fractional coordinates |
320 | 0 | if ( buffer[0] == 'C' || buffer[0] == 'c' || |
321 | 0 | buffer[0] == 'K' || buffer[0] == 'k' ) { |
322 | 0 | cartesian = true; |
323 | 0 | } |
324 | 0 | else { |
325 | 0 | cartesian = false; |
326 | 0 | } |
327 | |
|
328 | 0 | atomCount = 0; |
329 | 0 | for (unsigned int i = 0; i < totalAtoms; i++) { |
330 | | // Things get a little nasty here. VASP just prints all the coordinates with no |
331 | | // identifying information one after another here. So in the above sections we've |
332 | | // parsed out which atom types and how many of each are present in atomTypes and |
333 | | // numAtoms, respectively. The counters atomIndex and atomCount have the following |
334 | | // roles: atomIndex keeps track of where we are in atomTypes so that we know the |
335 | | // atomic species we're inserting. atomCount tracks how many of the current |
336 | | // atomTypes.at(atomIndex) species have been inserted, so that when we reach |
337 | | // (atomCount >= numAtoms.at(atomIndex) ) we should stop. Phew. |
338 | 0 | ifs_cont.getline(buffer,BUFF_SIZE); // atom location |
339 | | |
340 | | // Let's first figure out the atomic number we are dealing with: |
341 | 0 | while (atomCount >= numAtoms.at(atomIndex)) { |
342 | 0 | atomIndex++; |
343 | 0 | atomCount = 0; |
344 | 0 | } |
345 | | |
346 | | // If we made it past that check, we have atomic number = atomTypes.at(atomIndex) |
347 | | // Parse the buffer now. |
348 | 0 | tokenize(vs, buffer); |
349 | 0 | atom = pmol->NewAtom(); |
350 | 0 | atom->SetAtomicNum(atomTypes.at(atomIndex)); |
351 | 0 | x = atof((char*)vs[0].c_str()); |
352 | 0 | y = atof((char*)vs[1].c_str()); |
353 | 0 | z = atof((char*)vs[2].c_str()); |
354 | 0 | vector3 coords (x,y,z); |
355 | 0 | if (!cartesian) |
356 | 0 | coords = cell->FractionalToCartesian( coords ); |
357 | | // If we have Cartesian coordinates, we need to apply the scaling factor |
358 | 0 | else coords *= scale; |
359 | 0 | atom->SetVector(coords); |
360 | | //if the selective dynamics info is present then read it into OBPairData |
361 | | //this needs to be kept somehow to be able to write out the same as input |
362 | | //it's string so it wastes memory :( |
363 | 0 | if (selective && vs.size() >= 6) { |
364 | 0 | key = "move"; |
365 | 0 | value = " "; value += vs[3].c_str(); |
366 | 0 | value += " "; value += vs[4].c_str(); |
367 | 0 | value += " "; value += vs[5].c_str(); |
368 | 0 | cp = new OBPairData; |
369 | 0 | cp->SetAttribute(key); |
370 | 0 | cp->SetValue(value); |
371 | 0 | cp->SetOrigin(fileformatInput); |
372 | 0 | atom->SetData(cp); |
373 | 0 | } |
374 | |
|
375 | 0 | atomCount++; |
376 | 0 | }; |
377 | | |
378 | | // There is some trailing garbage, but AFAIK it's not useful for anything. |
379 | 0 | ifs_cont.close(); |
380 | | |
381 | | // Read density of states info from DOSCAR, if available |
382 | 0 | if (ifs_dos) { |
383 | | // Create DOS object |
384 | 0 | OBDOSData *dos = new OBDOSData(); |
385 | | |
386 | | // skip header |
387 | 0 | ifs_dos.getline(buffer,BUFF_SIZE); // Junk |
388 | 0 | ifs_dos.getline(buffer,BUFF_SIZE); // Junk |
389 | 0 | ifs_dos.getline(buffer,BUFF_SIZE); // Junk |
390 | 0 | ifs_dos.getline(buffer,BUFF_SIZE); // Junk |
391 | 0 | ifs_dos.getline(buffer,BUFF_SIZE); // Junk |
392 | | |
393 | | // Get fermi level |
394 | 0 | double fermi; |
395 | 0 | if (ifs_dos.getline(buffer,BUFF_SIZE)) { // startE endE res fermi ??? |
396 | 0 | tokenize(vs, buffer); |
397 | 0 | fermi = atof(vs[3].c_str()); |
398 | 0 | } |
399 | | |
400 | | // Start pulling out energies and densities |
401 | 0 | std::vector<double> energies; |
402 | 0 | std::vector<double> densities; |
403 | 0 | std::vector<double> integration; |
404 | 0 | while (ifs_dos.getline(buffer,BUFF_SIZE)) { |
405 | 0 | tokenize(vs, buffer); |
406 | 0 | energies.push_back(atof(vs[0].c_str())); |
407 | 0 | densities.push_back(atof(vs[1].c_str())); |
408 | 0 | integration.push_back(atof(vs[2].c_str())); |
409 | 0 | } |
410 | |
|
411 | 0 | if (energies.size() != 0) { |
412 | 0 | dos->SetData(fermi, energies, densities, integration); |
413 | 0 | pmol->SetData(dos); |
414 | 0 | } |
415 | 0 | } |
416 | |
|
417 | 0 | ifs_dos.close(); |
418 | | |
419 | | // Vibration intensities |
420 | 0 | vector3 prevDm; |
421 | 0 | vector<vector3> prevXyz; |
422 | 0 | vector3 currDm; |
423 | 0 | vector<vector3> currXyz; |
424 | | |
425 | | // Read in optional information from outcar |
426 | 0 | if (ifs_out) { |
427 | 0 | while (ifs_out.getline(buffer,BUFF_SIZE)) { |
428 | | // Enthalphy |
429 | 0 | if (strstr(buffer, "enthalpy is")) { |
430 | 0 | hasEnthalpy = true; |
431 | 0 | tokenize(vs, buffer); |
432 | 0 | enthalpy_eV = atof(vs[4].c_str()); |
433 | 0 | pv_eV = atof(vs[8].c_str()); |
434 | 0 | } |
435 | | |
436 | | // Free energy |
437 | 0 | if (strstr(buffer, "free energy")) { |
438 | 0 | tokenize(vs, buffer); |
439 | 0 | pmol->SetEnergy(atof(vs[4].c_str()) * EV_TO_KCAL_PER_MOL); |
440 | 0 | } |
441 | | |
442 | | // Frequencies |
443 | 0 | if (strstr(buffer, "Eigenvectors") && Frequencies.size() == 0) { |
444 | 0 | hasVibrations = true; |
445 | 0 | double x, y, z; |
446 | 0 | ifs_out.getline(buffer,BUFF_SIZE); // dash line |
447 | 0 | ifs_out.getline(buffer,BUFF_SIZE); // blank line |
448 | 0 | ifs_out.getline(buffer,BUFF_SIZE); // blank line |
449 | 0 | ifs_out.getline(buffer,BUFF_SIZE); // first frequency line |
450 | 0 | while (!strstr(buffer, "Eigenvectors")) { |
451 | 0 | vector<vector3> vib; |
452 | 0 | tokenize(vs, buffer); |
453 | 0 | int freqnum = atoi(vs[0].c_str()); |
454 | 0 | if (vs[1].size() == 1 and vs[1].compare("f") == 0) { |
455 | | // Real frequency |
456 | 0 | Frequencies.push_back(atof(vs[7].c_str())); |
457 | 0 | } else if (strstr(vs[1].c_str(), "f/i=")) { |
458 | | // Imaginary frequency |
459 | 0 | Frequencies.push_back(-atof(vs[6].c_str())); |
460 | 0 | } else { |
461 | | // No more frequencies |
462 | 0 | break; |
463 | 0 | } |
464 | 0 | ifs_out.getline(buffer,BUFF_SIZE); // header line |
465 | 0 | ifs_out.getline(buffer,BUFF_SIZE); // first displacement line |
466 | 0 | tokenize(vs, buffer); |
467 | | // normal modes |
468 | 0 | while (vs.size() == 6) { |
469 | 0 | x = atof(vs[3].c_str()); |
470 | 0 | y = atof(vs[4].c_str()); |
471 | 0 | z = atof(vs[5].c_str()); |
472 | 0 | vib.push_back(vector3(x, y, z)); |
473 | 0 | ifs_out.getline(buffer,BUFF_SIZE); // next displacement line |
474 | 0 | tokenize(vs, buffer); |
475 | 0 | } |
476 | 0 | Lx.push_back(vib); |
477 | 0 | ifs_out.getline(buffer,BUFF_SIZE); // next frequency line |
478 | 0 | } |
479 | 0 | } |
480 | |
|
481 | 0 | if (strstr(buffer, "dipolmoment")) { |
482 | 0 | tokenize(vs, buffer); |
483 | 0 | x = atof(vs[1].c_str()); |
484 | 0 | y = atof(vs[2].c_str()); |
485 | 0 | z = atof(vs[3].c_str()); |
486 | 0 | currDm.Set(x, y, z); |
487 | 0 | } |
488 | 0 | if (strstr(buffer, "TOTAL-FORCE")) { |
489 | 0 | currXyz.clear(); |
490 | 0 | ifs_out.getline(buffer, BUFF_SIZE); // header line |
491 | 0 | ifs_out.getline(buffer, BUFF_SIZE); |
492 | 0 | tokenize(vs, buffer); |
493 | 0 | while (vs.size() == 6) { |
494 | 0 | x = atof(vs[0].c_str()); |
495 | 0 | y = atof(vs[1].c_str()); |
496 | 0 | z = atof(vs[2].c_str()); |
497 | 0 | currXyz.push_back(vector3(x, y, z)); |
498 | 0 | ifs_out.getline(buffer, BUFF_SIZE); // next line |
499 | 0 | tokenize(vs, buffer); |
500 | 0 | } |
501 | 0 | } |
502 | 0 | if (strstr(buffer, "BORN EFFECTIVE CHARGES")) { |
503 | | // IBRION = 7; IBRION = 8 |
504 | 0 | dipGrad.clear(); |
505 | 0 | ifs_out.getline(buffer, BUFF_SIZE); // header line |
506 | 0 | ifs_out.getline(buffer, BUFF_SIZE); // `ion #` |
507 | 0 | tokenize(vs, buffer); |
508 | 0 | while (vs.size() == 2) { |
509 | 0 | matrix3x3 dmudq; |
510 | 0 | for (int row = 0; row < 3; ++row) { |
511 | 0 | ifs_out.getline(buffer, BUFF_SIZE); |
512 | 0 | tokenize(vs, buffer); |
513 | 0 | x = atof(vs[1].c_str()); |
514 | 0 | y = atof(vs[2].c_str()); |
515 | 0 | z = atof(vs[3].c_str()); |
516 | 0 | dmudq.SetRow(row, vector3(x, y, z)); |
517 | 0 | } |
518 | 0 | dipGrad.push_back(dmudq); |
519 | 0 | ifs_out.getline(buffer, BUFF_SIZE); // next line |
520 | 0 | tokenize(vs, buffer); |
521 | 0 | } |
522 | 0 | } else if (strstr(buffer, "free energy")) { |
523 | | // IBRION = 5 |
524 | | // reached the end of an iteration, use the values |
525 | 0 | if (dipGrad.empty()) { |
526 | | // first iteration: nondisplaced ions |
527 | 0 | dipGrad.resize(pmol->NumAtoms()); |
528 | 0 | } else if (prevXyz.empty()) { |
529 | | // even iteration: store values |
530 | 0 | prevXyz = currXyz; |
531 | 0 | prevDm = currDm; |
532 | 0 | } else { |
533 | | // odd iteration: compute dipGrad = dmu / dxyz for moved ion |
534 | 0 | for (size_t natom = 0; natom < pmol->NumAtoms(); ++natom) { |
535 | 0 | const vector3 dxyz = currXyz[natom] - prevXyz[natom]; |
536 | 0 | vector3::const_iterator iter = std::find_if(dxyz.begin(), dxyz.end(), |
537 | 0 | [](double v) { return v != 0.0; }); |
538 | 0 | if (iter != dxyz.end()) dipGrad[natom].SetRow(iter - dxyz.begin(), |
539 | 0 | (currDm - prevDm) / *iter); |
540 | 0 | } |
541 | 0 | prevXyz.clear(); |
542 | 0 | } |
543 | 0 | } |
544 | 0 | } |
545 | 0 | } |
546 | 0 | ifs_out.close(); |
547 | | |
548 | | // Set enthalpy |
549 | 0 | if (hasEnthalpy) { |
550 | 0 | OBPairData *enthalpyPD = new OBPairData(); |
551 | 0 | OBPairData *enthalpyPD_pv = new OBPairData(); |
552 | 0 | OBPairData *enthalpyPD_eV = new OBPairData(); |
553 | 0 | OBPairData *enthalpyPD_pv_eV = new OBPairData(); |
554 | 0 | enthalpyPD->SetAttribute("Enthalpy (kcal/mol)"); |
555 | 0 | enthalpyPD_pv->SetAttribute("Enthalpy PV term (kcal/mol)"); |
556 | 0 | enthalpyPD_eV->SetAttribute("Enthalpy (eV)"); |
557 | 0 | enthalpyPD_pv_eV->SetAttribute("Enthalpy PV term (eV)"); |
558 | 0 | double en_kcal_per_mole = enthalpy_eV * EV_TO_KCAL_PER_MOL; |
559 | 0 | double pv_kcal_per_mole = pv_eV * EV_TO_KCAL_PER_MOL; |
560 | 0 | snprintf(tag, BUFF_SIZE, "%f", en_kcal_per_mole); |
561 | 0 | enthalpyPD->SetValue(tag); |
562 | 0 | snprintf(tag, BUFF_SIZE, "%f", pv_kcal_per_mole); |
563 | 0 | enthalpyPD_pv->SetValue(tag); |
564 | 0 | snprintf(tag, BUFF_SIZE, "%f", enthalpy_eV); |
565 | 0 | enthalpyPD_eV->SetValue(tag); |
566 | 0 | snprintf(tag, BUFF_SIZE, "%f", pv_eV); |
567 | 0 | enthalpyPD_pv_eV->SetValue(tag); |
568 | 0 | pmol->SetData(enthalpyPD); |
569 | 0 | pmol->SetData(enthalpyPD_pv); |
570 | 0 | pmol->SetData(enthalpyPD_eV); |
571 | 0 | pmol->SetData(enthalpyPD_pv_eV); |
572 | 0 | } |
573 | | |
574 | | // Set vibrations |
575 | 0 | if (hasVibrations) { |
576 | | // compute dDip/dQ |
577 | 0 | vector<double> Intensities; |
578 | 0 | for (vector<vector<vector3> >::const_iterator |
579 | 0 | lxIter = Lx.begin(); lxIter != Lx.end(); ++lxIter) { |
580 | 0 | vector3 intensity; |
581 | 0 | for (size_t natom = 0; natom < dipGrad.size(); ++natom) { |
582 | 0 | intensity += dipGrad[natom].transpose() * lxIter->at(natom) |
583 | 0 | / sqrt(pmol->GetAtomById(natom)->GetAtomicMass()); |
584 | 0 | } |
585 | 0 | Intensities.push_back(dot(intensity, intensity)); |
586 | 0 | } |
587 | 0 | const double max = *max_element(Intensities.begin(), Intensities.end()); |
588 | 0 | if (max != 0.0) { |
589 | | // Normalize |
590 | 0 | std::transform(Intensities.begin(), Intensities.end(), Intensities.begin(), |
591 | 0 | [=](double v) { return v / (max / 100.0); }); |
592 | 0 | } else { |
593 | 0 | Intensities.clear(); |
594 | 0 | } |
595 | 0 | OBVibrationData* vd = new OBVibrationData; |
596 | 0 | vd->SetData(Lx, Frequencies, Intensities); |
597 | 0 | pmol->SetData(vd); |
598 | 0 | } |
599 | |
|
600 | 0 | pmol->EndModify(); |
601 | |
|
602 | 0 | const char *noBonding = pConv->IsOption("b", OBConversion::INOPTIONS); |
603 | 0 | const char *singleOnly = pConv->IsOption("s", OBConversion::INOPTIONS); |
604 | |
|
605 | 0 | if (noBonding == nullptr) { |
606 | 0 | pmol->ConnectTheDots(); |
607 | 0 | if (singleOnly == nullptr) { |
608 | 0 | pmol->PerceiveBondOrders(); |
609 | 0 | } |
610 | 0 | } |
611 | |
|
612 | 0 | return true; |
613 | 0 | } |
614 | | |
615 | | bool VASPFormat::WriteMolecule(OBBase* pOb, OBConversion* pConv) |
616 | 0 | { |
617 | | //No surprises in this routine, cartesian coordinates are written out |
618 | | //and if at least a single atom has information about constraints, |
619 | | //then selective dynamics is used and the info is written out. |
620 | | //The atoms are ordered according to their atomic number so that the |
621 | | //output looks nice, this can be reversed by using command line flag "-xw". |
622 | | // |
623 | 0 | OBMol* pmol = dynamic_cast<OBMol*>(pOb); |
624 | 0 | if (pmol == nullptr) { |
625 | 0 | return false; |
626 | 0 | } |
627 | | |
628 | 0 | ostream& ofs = *pConv->GetOutStream(); |
629 | 0 | OBMol &mol = *pmol; |
630 | |
|
631 | 0 | char buffer[BUFF_SIZE]; |
632 | 0 | OBUnitCell *uc = nullptr; |
633 | 0 | vector<vector3> cell; |
634 | |
|
635 | 0 | const char * sortAtomsNum = pConv->IsOption("w", OBConversion::OUTOPTIONS); |
636 | 0 | const char * sortAtomsCustom = pConv->IsOption("z", OBConversion::OUTOPTIONS); |
637 | | |
638 | | // Create a list of ids. These may be sorted by atomic number depending |
639 | | // on the value of keepOrder. |
640 | 0 | std::vector<OBAtom *> atoms_sorted; |
641 | 0 | atoms_sorted.reserve(mol.NumAtoms()); |
642 | |
|
643 | 0 | FOR_ATOMS_OF_MOL(atom, mol) { |
644 | 0 | atoms_sorted.push_back(&(*atom)); |
645 | 0 | } |
646 | |
|
647 | 0 | std::vector<int> custom_sort_nums; |
648 | | |
649 | 0 | if (sortAtomsCustom != nullptr) |
650 | 0 | { |
651 | 0 | vector<string> vs; |
652 | 0 | tokenize(vs, sortAtomsCustom); |
653 | 0 | for(size_t i = 0; i < vs.size(); ++i) |
654 | 0 | custom_sort_nums.push_back(OBElements::GetAtomicNum(vs[i].c_str())); |
655 | 0 | } |
656 | |
|
657 | 0 | compare_sort_items csi(custom_sort_nums, sortAtomsNum != nullptr); |
658 | 0 | std::stable_sort(atoms_sorted.begin(), atoms_sorted.end(), csi); |
659 | | |
660 | | // Use the atomicNums vector to determine the composition line. |
661 | | // atomicNumsCondensed and atomCounts contain the same data as atomicNums: |
662 | | // if: |
663 | | // atoms_sorted[i]->GetAtomicNum() = [ 3 3 3 2 2 8 2 6 6 ] |
664 | | // then: |
665 | | // atomicNums = [(3 3) (2 2) (8 1) (2 1) (6 2)] |
666 | | |
667 | 0 | std::vector<std::pair<int, int> > atomicNums; |
668 | | |
669 | 0 | int prev_anum = -20; //not a periodic table number |
670 | 0 | for(int i = 0; i < atoms_sorted.size(); i++) |
671 | 0 | { |
672 | 0 | const int anum = atoms_sorted[i]->GetAtomicNum(); |
673 | | |
674 | 0 | if( prev_anum != anum ) |
675 | 0 | { |
676 | 0 | std::pair<int, int> x(anum, 1); |
677 | 0 | atomicNums.push_back(x); |
678 | 0 | } |
679 | 0 | else |
680 | 0 | { |
681 | 0 | if(atomicNums.size() > 0) |
682 | 0 | atomicNums.rbegin()->second++; |
683 | 0 | } |
684 | | |
685 | 0 | prev_anum = anum; |
686 | 0 | } |
687 | | |
688 | | // write title |
689 | 0 | ofs << mol.GetTitle() << endl; |
690 | | // write the multiplication factor, set this to one |
691 | | // and write the cell using the 3x3 cell matrix |
692 | 0 | ofs << "1.000 " << endl; |
693 | |
|
694 | 0 | if (!mol.HasData(OBGenericDataType::UnitCell)) { |
695 | | // the unit cell has not been defined. Leave as all zeros so the user |
696 | | // can fill it in themselves |
697 | 0 | for (int ii = 0; ii < 3; ii++) { |
698 | 0 | snprintf(buffer, BUFF_SIZE, "0.0 0.0 0.0"); |
699 | 0 | ofs << buffer << endl; |
700 | 0 | } |
701 | 0 | } |
702 | 0 | else |
703 | 0 | { |
704 | | // there is a unit cell, write it out |
705 | 0 | uc = static_cast<OBUnitCell*>(mol.GetData(OBGenericDataType::UnitCell)); |
706 | 0 | cell = uc->GetCellVectors(); |
707 | 0 | for (vector<vector3>::const_iterator i = cell.begin(); |
708 | 0 | i != cell.end(); ++i) { |
709 | 0 | snprintf(buffer, BUFF_SIZE, "%20.15f%20.15f%20.15f", |
710 | 0 | i->x(), i->y(), i->z()); |
711 | 0 | ofs << buffer << endl; |
712 | 0 | } |
713 | 0 | } |
714 | | |
715 | | // go through the atoms first to write out the element names if using |
716 | | // VASP 5 format |
717 | 0 | const char *vasp4Format = pConv->IsOption("4", OBConversion::OUTOPTIONS); |
718 | 0 | if (!vasp4Format) { |
719 | 0 | for (vector< std::pair<int, int> >::const_iterator |
720 | 0 | it = atomicNums.begin(), |
721 | 0 | it_end = atomicNums.end(); it != it_end; ++it) { |
722 | 0 | snprintf(buffer, BUFF_SIZE, "%-3s ", OBElements::GetSymbol(it->first)); |
723 | 0 | ofs << buffer ; |
724 | 0 | } |
725 | 0 | ofs << endl; |
726 | 0 | } |
727 | | |
728 | | // then do the same to write out the number of ions of each element |
729 | 0 | for (vector< std::pair<int, int> >::const_iterator |
730 | 0 | it = atomicNums.begin(), |
731 | 0 | it_end = atomicNums.end(); it != it_end; ++it) { |
732 | 0 | snprintf(buffer, BUFF_SIZE, "%-3u ", it->second); |
733 | 0 | ofs << buffer ; |
734 | 0 | } |
735 | 0 | ofs << endl; |
736 | | |
737 | | // assume that there are no constraints on the atoms |
738 | 0 | bool selective = false; |
739 | | // and test if any of the atoms has constraints |
740 | 0 | FOR_ATOMS_OF_MOL(atom, mol) { |
741 | 0 | if (atom->HasData("move")){ |
742 | 0 | selective = true; |
743 | 0 | break; |
744 | 0 | } |
745 | 0 | } |
746 | 0 | if (selective) { |
747 | 0 | ofs << "SelectiveDyn" << endl; |
748 | 0 | } |
749 | | |
750 | | // print the atomic coordinates in \AA |
751 | 0 | ofs << "Cartesian" << endl; |
752 | |
|
753 | 0 | for (std::vector<OBAtom *>::const_iterator it = atoms_sorted.begin(); |
754 | 0 | it != atoms_sorted.end(); ++it) |
755 | 0 | { |
756 | | // Print coordinates |
757 | 0 | snprintf(buffer,BUFF_SIZE, "%26.19f %26.19f %26.19f", |
758 | 0 | (*it)->GetX(), (*it)->GetY(), (*it)->GetZ()); |
759 | 0 | ofs << buffer; |
760 | | |
761 | | // if at least one atom has info about constraints |
762 | 0 | if (selective) { |
763 | | // if this guy has, write it out |
764 | 0 | if ((*it)->HasData("move")) { |
765 | 0 | OBPairData *cp = (OBPairData*)(*it)->GetData("move"); |
766 | | // seemingly ridiculous number of digits is written out |
767 | | // but sometimes you just don't want to change them |
768 | 0 | ofs << " " << cp->GetValue().c_str(); |
769 | 0 | } |
770 | 0 | else { |
771 | | // the atom has been created and the info has not been copied |
772 | 0 | ofs << " T T T"; |
773 | 0 | } |
774 | 0 | } |
775 | 0 | ofs << endl; |
776 | 0 | } |
777 | |
|
778 | 0 | return true; |
779 | 0 | } |
780 | | |
781 | | } //namespace OpenBabel |
782 | | |