Coverage Report

Created: 2026-04-12 06:51

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/openbabel/src/formats/xsfformat.cpp
Line
Count
Source
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
#include <cstdlib>
24
25
26
using namespace std;
27
namespace OpenBabel
28
{
29
30
  class XSFFormat : public OBMoleculeFormat
31
  {
32
  public:
33
    //Register this format type ID
34
    XSFFormat()
35
6
    {
36
6
      OBConversion::RegisterFormat("xsf",this);
37
      // animation variant
38
6
      OBConversion::RegisterFormat("axsf",this);
39
6
    }
40
41
    const char* Description() override  // required
42
0
    {
43
0
      return
44
0
        "XCrySDen Structure Format\n"
45
0
        "Read Options e.g. -as\n"
46
0
        "  s  Output single bonds only\n"
47
0
        "  b  Disable bonding entirely\n\n";
48
0
    }
49
50
    const char* SpecificationURL() override
51
0
    { return "http://www.xcrysden.org/doc/XSF.html/"; }  // optional
52
53
    //Flags() can return be any the following combined by | or be omitted if none apply
54
    // NOTREADABLE  READONEONLY  NOTWRITABLE  WRITEONEONLY
55
    unsigned int Flags() override
56
12
    {
57
12
      return READONEONLY | NOTWRITABLE;
58
12
    }
59
60
    //*** This section identical for most OBMol conversions ***
61
    ////////////////////////////////////////////////////
62
    /// The "API" interface functions
63
    bool ReadMolecule(OBBase* pOb, OBConversion* pConv) override;
64
  };
65
  //***
66
67
  //Make an instance of the format class
68
  XSFFormat theXSFFormat;
69
70
  /////////////////////////////////////////////////////////////////
71
  bool XSFFormat::ReadMolecule(OBBase* pOb, OBConversion* pConv)
72
0
  {
73
74
0
    OBMol* pmol = pOb->CastAndClear<OBMol>();
75
0
    if (pmol == nullptr)
76
0
      return false;
77
78
    //Define some references so we can use the old parameter names
79
0
    istream &ifs = *pConv->GetInStream();
80
0
    OBMol &mol = *pmol;
81
0
    const char* title = pConv->GetTitle();
82
83
0
    char buffer[BUFF_SIZE];
84
0
    string str;
85
0
    double x,y,z;
86
0
    OBAtom *atom;
87
0
    vector3 translationVectors[3];
88
0
    int numTranslationVectors = 0;
89
0
    vector<string> vs;
90
0
    vector<vector3> atomPositions;
91
0
    bool createdAtoms = false;
92
0
    int atomicNum;
93
94
0
    mol.BeginModify();
95
96
0
    while (ifs.getline(buffer, BUFF_SIZE))
97
0
      {
98
0
        if (buffer[0] == '#')
99
0
          continue; // comment
100
0
        if (strstr(buffer, "ATOMS") != nullptr) {
101
          // Minimum of 4 columns -- AtNum, x, y, z (forces)
102
          // where AtNum stands for atomic number (or symbol), while X Y Z are
103
0
          ifs.getline(buffer, BUFF_SIZE);
104
0
          tokenize(vs, buffer);
105
0
          while (vs.size() >= 4) {
106
0
            if (!createdAtoms) {
107
0
              atom = mol.NewAtom();
108
              //set atomic number
109
0
              atomicNum = OBElements::GetAtomicNum(vs[0].c_str());
110
0
              if (atomicNum == 0) {
111
0
                atomicNum = atoi(vs[0].c_str());
112
0
              }
113
0
              atom->SetAtomicNum(atomicNum);
114
0
            }
115
0
            x = atof((char*)vs[1].c_str());
116
0
            y = atof((char*)vs[2].c_str());
117
0
            z = atof((char*)vs[3].c_str());
118
0
            atomPositions.push_back(vector3(x, y, z)); // we may have a movie or animation
119
120
0
            ifs.getline(buffer, BUFF_SIZE);
121
0
            tokenize(vs, buffer);
122
0
          }
123
0
          createdAtoms = true; // don't run NewAtom() anymore
124
0
        }
125
0
        else if ( strstr(buffer, "PRIMVEC")
126
0
                 || strstr(buffer, "CONVVEC") ) {
127
          // translation vectors
128
0
          numTranslationVectors = 0; // if we have an animation
129
0
          while (numTranslationVectors < 3 && ifs.getline(buffer,BUFF_SIZE)) {
130
0
            tokenize(vs,buffer); // we really need to check that it's 3 entries only
131
0
            if (vs.size() < 3) return false; // timvdm 18/06/2008
132
0
            x = atof((char*)vs[0].c_str());
133
0
            y = atof((char*)vs[1].c_str());
134
0
            z = atof((char*)vs[2].c_str());
135
0
            translationVectors[numTranslationVectors++].Set(x, y, z);
136
0
          }
137
0
        }
138
0
        else if (strstr(buffer, "PRIMCOORD") != nullptr) {
139
          // read the coordinates
140
0
          ifs.getline(buffer, BUFF_SIZE);
141
0
          tokenize(vs, buffer);
142
0
          if (vs.size() < 2) return false;
143
0
          int numAtoms = atoi(vs[0].c_str());
144
0
          for (int a = 0; a < numAtoms; ++a) {
145
0
            if (!ifs.getline(buffer,BUFF_SIZE))
146
0
              break;
147
0
            tokenize(vs,buffer);
148
0
            if (vs.size() < 4)
149
0
              break;
150
151
0
            if (!createdAtoms) {
152
0
              atom = mol.NewAtom();
153
              //set atomic number
154
0
              atomicNum = OBElements::GetAtomicNum(vs[0].c_str());
155
0
              if (atomicNum == 0) {
156
0
                atomicNum = atoi(vs[0].c_str());
157
0
              }
158
0
              atom->SetAtomicNum(atomicNum);
159
0
            }
160
0
            x = atof((char*)vs[1].c_str());
161
0
            y = atof((char*)vs[2].c_str());
162
0
            z = atof((char*)vs[3].c_str());
163
0
            atomPositions.push_back(vector3(x, y, z));
164
0
          }
165
0
        }
166
0
      }
167
168
0
    mol.EndModify();
169
170
0
    int natom = mol.NumAtoms();
171
0
    if (natom == 0)
172
0
      return false;
173
174
0
    int numConformers = atomPositions.size() / natom;
175
0
    for (int i = 0; i < numConformers; ++i) {
176
0
      double *coordinates = new double[natom * 3];
177
0
      for (int j = 0; j < natom; ++j) {
178
0
        vector3 currentPosition = atomPositions[i*natom + j];
179
0
        coordinates[j*3] = currentPosition.x();
180
0
        coordinates[j*3 + 1] = currentPosition.y();
181
0
        coordinates[j*3 + 2] = currentPosition.z();
182
0
      }
183
0
      mol.AddConformer(coordinates);
184
0
    }
185
    // Delete first conformer, created by EndModify, bunch of 0s
186
0
    mol.DeleteConformer(0);
187
    // Set geometry to last one
188
0
    mol.SetConformer(mol.NumConformers() - 1);
189
190
0
    if (!pConv->IsOption("b",OBConversion::INOPTIONS))
191
0
      mol.ConnectTheDots();
192
0
    if (!pConv->IsOption("s",OBConversion::INOPTIONS) && !pConv->IsOption("b",OBConversion::INOPTIONS))
193
0
      mol.PerceiveBondOrders();
194
195
    // Add final properties
196
0
    mol.SetTitle(title);
197
0
    if (numTranslationVectors == 3) {
198
0
      OBUnitCell *uc = new OBUnitCell;
199
0
      uc->SetOrigin(fileformatInput);
200
0
      uc->SetData(translationVectors[0],
201
0
                  translationVectors[1],
202
0
                  translationVectors[2]);
203
0
      mol.SetData(uc);
204
0
    }
205
206
0
    return(true);
207
0
  }
208
209
} //namespace OpenBabel