Coverage Report

Created: 2026-03-12 07:11

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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