OpenMD 3.1
Molecular Dynamics in the Open
Loading...
Searching...
No Matches
XYZFormat.cpp
1/**********************************************************************
2
3This basic Periodic Table class was originally taken from the data.cpp
4file in OpenBabel. The code has been modified to match the OpenMD coding style.
5
6We have retained the OpenBabel copyright and GPL license on this class:
7
8Copyright (C) 1998-2001 by OpenEye Scientific Software, Inc.
9Some portions Copyright (C) 2001-2005 by Geoffrey R. Hutchison
10
11This file is part of the Open Babel project.
12For more information, see <http://openbabel.sourceforge.net/>
13
14This program is free software; you can redistribute it and/or modify
15it under the terms of the GNU General Public License as published by
16the Free Software Foundation version 2 of the License.
17
18This program is distributed in the hope that it will be useful,
19but WITHOUT ANY WARRANTY; without even the implied warranty of
20MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21GNU General Public License for more details.
22***********************************************************************/
23
24#include "io/XYZFormat.hpp"
25
26#include <iostream>
27#include <sstream>
28#include <string>
29#include <vector>
30
31#include "math/Vector3.hpp"
34#include "utils/Trim.hpp"
35#include "utils/simError.h"
36
37namespace OpenMD {
38
39 bool XYZFormat::ReadMolecule(std::istream& ifs) {
40 char buffer[BUFF_SIZE];
41 unsigned int natoms;
42 std::stringstream errorMsg;
43
44 if (!ifs)
45 return false; // we're attempting to read past the end of the file
46
47 if (!ifs.getline(buffer, BUFF_SIZE)) {
48 strcpy(painCave.errMsg,
49 "Problems reading an XYZ file: Cannot read the first line.\n");
50 painCave.isFatal = 1;
51 simError();
52 }
53
54 if (sscanf(buffer, "%d", &natoms) == 0 || !natoms) {
55 strcpy(painCave.errMsg, "Problems reading an XYZ file: The first line "
56 "must contain the number of atoms.\n");
57 painCave.isFatal = 1;
58 simError();
59 }
60
61 mol_.reserve(natoms);
62
63 // The next line contains a title string for the molecule. Use this
64 // as the title for the molecule if the line is not
65 // empty. Otherwise, use the title given by the calling function.
66 if (!ifs.getline(buffer, BUFF_SIZE)) {
67 strcpy(painCave.errMsg, "Problems reading an XYZ file: Could not read "
68 "the second line (title/comments).\n");
69 painCave.isFatal = 1;
70 simError();
71 }
72 std::string readTitle(buffer);
73 std::string::size_type location = readTitle.find("Energy");
74 if (location != std::string::npos) readTitle.erase(location);
75 Utils::trim(readTitle);
76
77 location = readTitle.find_first_not_of(" \t\n\r");
78 if (location != std::string::npos)
79 title_ = readTitle;
80 else
81 title_ = "";
82
83 // The next lines contain four items each, separated by white
84 // spaces: the atom type, and the coordinates of the atom
85 for (unsigned int i = 1; i <= natoms; i++) {
86 if (!ifs.getline(buffer, BUFF_SIZE)) {
87 errorMsg << "Problems reading an XYZ file: "
88 << "Could not read line #" << i + 2 << ", file error.\n"
89 << " According to line one, there should be " << natoms
90 << " atoms, and therefore " << natoms + 2
91 << " lines in the file.";
92
93 strcpy(painCave.errMsg, errorMsg.str().c_str());
94 painCave.isFatal = 0;
95 painCave.severity = OPENMD_WARNING;
96 simError();
97 return (false);
98 }
99 StringTokenizer tokenizer(buffer, " ;,\t\n\r");
100 std::vector<std::string> vs = tokenizer.getAllTokens();
101 if (vs.size() < 4) // ignore extra columns which some applications add
102 {
103 errorMsg << "Problems reading an XYZ file: "
104 << "Could not read line #" << i + 2 << ".\n"
105 << "OpenBabel found the line '" << buffer << "'\n"
106 << "According to the specifications, this line should contain "
107 "exactly 4 entries, separated by white space.\n"
108 << "However, OpenBabel found " << vs.size() << " items.";
109
110 strcpy(painCave.errMsg, errorMsg.str().c_str());
111 painCave.isFatal = 0;
112 painCave.severity = OPENMD_WARNING;
113 simError();
114 return (false);
115 }
116
117 // Atom Type: get the atomic number from the element table, using
118 // the first entry in the currently read line. If the entry makes
119 // sense, set the atomic number and leave the atomic type open
120 // (the type is then later faulted in when atom->GetType() is
121 // called). If the entry does not make sense to use, set the atom
122 // type manually, assuming that the author of the xyz-file had
123 // something "special" in mind.
124
125 XYZAtom* atom = new XYZAtom();
126 mol_.push_back(atom);
127
128 int atomicNum = etab.GetAtomicNum(vs[0].c_str());
129 // set atomic number, or '0' if the atom type is not recognized
130 if (atomicNum == 0) {
131 // Sometimes people call this an XYZ file, but it's actually Unichem
132 // i.e., the first column is the atomic number, not a symbol
133 // so we'll first check if we can convert this to an element number
134 atomicNum = atoi(vs[0].c_str());
135 }
136
137 atom->atomicNum = atomicNum;
138 if (atomicNum == 0) // still strange, try using an atom type
139 atom->type = vs[0];
140
141 // Read the atom coordinates
142 char* endptr;
143 double x = strtod((char*)vs[1].c_str(), &endptr);
144 if (endptr == (char*)vs[1].c_str()) {
145 errorMsg << "Problems reading an XYZ file: "
146 << "Could not read line #" << i + 2 << ".\n"
147 << "OpenBabel found the line '" << buffer << "'\n"
148 << "According to the specifications, this line should contain "
149 "exactly 4 entries, separated by white space.\n"
150 << "OpenBabel could not interpret item #1 as a number.";
151
152 strcpy(painCave.errMsg, errorMsg.str().c_str());
153 painCave.isFatal = 1;
154 simError();
155 }
156 double y = strtod((char*)vs[2].c_str(), &endptr);
157 if (endptr == (char*)vs[2].c_str()) {
158 errorMsg << "Problems reading an XYZ file: "
159 << "Could not read line #" << i + 2 << ".\n"
160 << "OpenBabel found the line '" << buffer << "'\n"
161 << "According to the specifications, this line should contain "
162 "exactly 4 entries, separated by white space.\n"
163 << "OpenBabel could not interpret item #2 as a number.";
164
165 strcpy(painCave.errMsg, errorMsg.str().c_str());
166 painCave.isFatal = 1;
167 simError();
168 }
169 double z = strtod((char*)vs[3].c_str(), &endptr);
170 if (endptr == (char*)vs[3].c_str()) {
171 errorMsg << "Problems reading an XYZ file: "
172 << "Could not read line #" << i + 2 << ".\n"
173 << "OpenBabel found the line '" << buffer << "'\n"
174 << "According to the specifications, this line should contain "
175 "exactly 4 entries, separated by white space.\n"
176 << "OpenBabel could not interpret item #3 as a number.";
177
178 strcpy(painCave.errMsg, errorMsg.str().c_str());
179 painCave.isFatal = 0;
180 painCave.severity = OPENMD_WARNING;
181 simError();
182 return (false);
183 }
184 atom->pos = Vector3d(x, y, z); // set coordinates
185
186 // OK, sometimes there's sym x y z charge -- accepted by Jmol
187 if (vs.size() > 5) {
188 std::string::size_type decimal = vs[4].find('.');
189 if (decimal != std::string::npos) { // period found
190 double charge = strtod((char*)vs[4].c_str(), &endptr);
191 if (endptr != (char*)vs[4].c_str()) atom->charge = charge;
192 }
193 } // attempt to parse charges
194 }
195
196 // clean out any remaining blank lines
197 std::streampos ipos;
198 do {
199 ipos = ifs.tellg();
200 ifs.getline(buffer, BUFF_SIZE);
201 } while (strlen(buffer) == 0 && !ifs.eof());
202 ifs.seekg(ipos);
203 return (true);
204 }
205} // namespace OpenMD
This basic Periodic Table class was originally taken from the data.h file in OpenBabel.
int GetAtomicNum(const char *str)
This basic Periodic Table class was originally taken from the data.cpp file in OpenBabel.