/src/openbabel/src/formats/gausscubeformat.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | /********************************************************************** |
2 | | gausscubeformat.cpp - Read in Gaussian cube format files. |
3 | | |
4 | | // Molekel - Molecular Visualization Program |
5 | | // Copyright (C) 2006, 2007 Swiss National Supercomputing Centre (CSCS) |
6 | | |
7 | | Some Portions Copyright (c) 2007 by Geoffrey R. Hutchison |
8 | | Some Portions Copyright (C) 2008 by Marcus D. Hanwell |
9 | | Some Portions Copyright (C) 2008 by Tim Vandermeersch |
10 | | |
11 | | This file is part of the Open Babel project. |
12 | | For more information, see <http://openbabel.org/> |
13 | | |
14 | | This program is free software; you can redistribute it and/or modify |
15 | | it under the terms of the GNU General Public License as published by |
16 | | the Free Software Foundation: either version 2 of the License, or (at |
17 | | your option) any later version. |
18 | | |
19 | | This program is distributed in the hope that it will be useful, |
20 | | but WITHOUT ANY WARRANTY; without even the implied warranty of |
21 | | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
22 | | GNU General Public License for more details. |
23 | | ***********************************************************************/ |
24 | | |
25 | | #include <sstream> |
26 | | #include <cstdlib> |
27 | | |
28 | | #include <openbabel/babelconfig.h> |
29 | | #include <openbabel/obmolecformat.h> |
30 | | #include <openbabel/mol.h> |
31 | | #include <openbabel/atom.h> |
32 | | #include <openbabel/bond.h> |
33 | | #include <openbabel/obiter.h> |
34 | | #include <openbabel/elements.h> |
35 | | |
36 | | #include <openbabel/griddata.h> |
37 | | |
38 | | using namespace std; |
39 | | |
40 | | /// @warning seems that every position in cube files has always to be |
41 | | /// multiplied by this constant even if according to: |
42 | | /// reference: https://www.gaussian.com/cubegen/ |
43 | | /// <Num points along first axis> > 0 means that the values |
44 | | /// are already in Angstroms. |
45 | | static const double BOHR_TO_ANGSTROM = 0.529177249; |
46 | | static const double ANGSTROM_TO_BOHR = 1.0 / 0.529177249; |
47 | | |
48 | | namespace OpenBabel |
49 | | { |
50 | | |
51 | | class OBGaussianCubeFormat : public OpenBabel::OBMoleculeFormat |
52 | | { |
53 | | public: |
54 | | // Constructor: register 'cube' and "CUBE" format. |
55 | | OBGaussianCubeFormat() |
56 | 4 | { |
57 | 4 | OpenBabel::OBConversion::RegisterFormat( "cube", this ); |
58 | 4 | OpenBabel::OBConversion::RegisterFormat( "cub", this ); |
59 | 4 | } |
60 | | |
61 | | // Return description. |
62 | | const char* Description() override // required |
63 | 0 | { |
64 | 0 | return |
65 | 0 | "Gaussian cube format\n" |
66 | 0 | "A grid format for volume data used by Gaussian\n" |
67 | 0 | "Open Babel supports reading and writing Gaussian cubes, including multiple\n" |
68 | 0 | "grids in one file.\n\n" |
69 | |
|
70 | 0 | "Read Options e.g. -as\n" |
71 | 0 | " b no bonds\n" |
72 | 0 | " s no multiple bonds\n\n"; |
73 | 0 | } |
74 | | |
75 | | // Return a specification url, not really a specification since |
76 | | // I couldn't find it but close enough. |
77 | | const char* SpecificationURL() override |
78 | 0 | { |
79 | 0 | return "https://www.gaussian.com/cubegen/"; |
80 | 0 | } |
81 | | |
82 | | // Return MIME type, NULL in this case. |
83 | 0 | const char* GetMIMEType() override { return nullptr; } |
84 | | |
85 | | // Skip to object: used for multi-object file formats. |
86 | 0 | int SkipObjects(int n, OpenBabel::OBConversion* pConv) override { return 0; } |
87 | | |
88 | | unsigned int Flags() override |
89 | 8 | { |
90 | 8 | return 0; |
91 | 8 | } |
92 | | |
93 | | /// The "API" interface functions |
94 | | bool ReadMolecule(OpenBabel::OBBase* pOb, OpenBabel::OBConversion* pConv) override; |
95 | | /// Write: always returns false right now - read only |
96 | | bool WriteMolecule(OpenBabel::OBBase* pOb, OpenBabel::OBConversion* pConv) override; |
97 | | |
98 | | }; |
99 | | |
100 | | //------------------------------------------------------------------------------ |
101 | | |
102 | | // Global variable used to register Gaussian cube format. |
103 | | OBGaussianCubeFormat theGaussianCubeFormat; |
104 | | |
105 | | //------------------------------------------------------------------------------ |
106 | | bool OBGaussianCubeFormat::ReadMolecule( OBBase* pOb, OBConversion* pConv ) |
107 | 0 | { |
108 | 0 | OBMol* pmol = pOb->CastAndClear<OBMol>(); |
109 | 0 | if (pmol == nullptr) |
110 | 0 | return false; |
111 | | |
112 | 0 | istream& ifs = *pConv->GetInStream(); |
113 | |
|
114 | 0 | const char* title = pConv->GetTitle(); |
115 | 0 | char buffer[BUFF_SIZE]; |
116 | |
|
117 | 0 | stringstream errorMsg; |
118 | |
|
119 | 0 | int nAtoms = 0; |
120 | 0 | int negAtoms = false; |
121 | |
|
122 | 0 | int line = 0; // Line counter - help in debugging... |
123 | |
|
124 | 0 | if (!ifs) |
125 | 0 | return false; // We are attempting to read past the end of the file |
126 | | |
127 | | // The first two lines are comments - and so not needed. |
128 | 0 | ++line; |
129 | 0 | if (!ifs.getline(buffer, BUFF_SIZE)) |
130 | 0 | { |
131 | 0 | obErrorLog.ThrowError(__FUNCTION__, |
132 | 0 | "Problem reading the Gaussian cube file: cannot read the first line (title/comments).", obWarning); |
133 | 0 | return false; |
134 | 0 | } |
135 | 0 | string cubeTitle = buffer; // save for later use |
136 | |
|
137 | 0 | ++line; |
138 | 0 | if (!ifs.getline(buffer,BUFF_SIZE)) |
139 | 0 | { |
140 | 0 | obErrorLog.ThrowError(__FUNCTION__, |
141 | 0 | "Problem reading the Gaussian cube file: cannot read the second line (title/comments).", obWarning); |
142 | 0 | return false; |
143 | 0 | } |
144 | | |
145 | | // This line contains the number of atoms included in the file followed by |
146 | | // the position of the origin of the cube. |
147 | 0 | ++line; |
148 | 0 | if (!ifs.getline(buffer, BUFF_SIZE)) |
149 | 0 | { |
150 | 0 | obErrorLog.ThrowError(__FUNCTION__, |
151 | 0 | "Problem reading the Gaussian cube file: cannot read the third line. This should contain the number of atoms and the position of the origin of the cube.", obWarning); |
152 | 0 | return false; |
153 | 0 | } |
154 | 0 | vector<string> vs; |
155 | 0 | tokenize(vs, buffer); |
156 | 0 | if (vs.size() < 4) |
157 | 0 | { |
158 | 0 | obErrorLog.ThrowError(__FUNCTION__, |
159 | 0 | "Problem reading the Gassian cube file: The third line must contain the number of atoms and the origin of the cube.", obWarning); |
160 | 0 | return false; |
161 | 0 | } |
162 | 0 | char *endptr; |
163 | 0 | nAtoms = strtol(static_cast<const char*>(vs.at(0).c_str()), &endptr, 10); |
164 | 0 | if (endptr == static_cast<const char*>(vs.at(0).c_str())) |
165 | 0 | { |
166 | 0 | errorMsg << "Problems reading the Gaussian cube file: " |
167 | 0 | << "Could not read line #3.\n" |
168 | 0 | << "According to the specification this line should contain " |
169 | 0 | << "four entries separated by white space.\n" |
170 | 0 | << "OpenBabel could not interpret item #0 as an integer."; |
171 | 0 | obErrorLog.ThrowError(__FUNCTION__, errorMsg.str(), obWarning); |
172 | 0 | return false; |
173 | 0 | } |
174 | 0 | if (nAtoms < 0) |
175 | 0 | { |
176 | | // If the number of atoms is negative it means there is data between |
177 | | // the atom positions and grid data. |
178 | 0 | negAtoms = true; |
179 | 0 | nAtoms *= -1; |
180 | 0 | } |
181 | | // cerr << "Number of atoms: " << nAtoms << " Orig: " << vs.at(0) << endl; |
182 | 0 | double x = strtod(static_cast<const char*>(vs.at(1).c_str()), &endptr); |
183 | 0 | if (endptr == static_cast<const char*>(vs.at(1).c_str())) |
184 | 0 | { |
185 | 0 | errorMsg << "Problems reading the Gaussian cube file: " |
186 | 0 | << "Could not read line #3.\n" |
187 | 0 | << "According to the specification this line should contain " |
188 | 0 | << "four entries separated by white space.\n" |
189 | 0 | << "OpenBabel could not interpret item #1 as a double."; |
190 | 0 | obErrorLog.ThrowError(__FUNCTION__, errorMsg.str(), obWarning); |
191 | 0 | return false; |
192 | 0 | } |
193 | 0 | double y = strtod(static_cast<const char*>(vs.at(2).c_str()), &endptr); |
194 | 0 | if (endptr == static_cast<const char*>(vs.at(2).c_str())) |
195 | 0 | { |
196 | 0 | errorMsg << "Problems reading the Gaussian cube file: " |
197 | 0 | << "Could not read line #3.\n" |
198 | 0 | << "According to the specification this line should contain " |
199 | 0 | << "four entries separated by white space.\n" |
200 | 0 | << "OpenBabel could not interpret item #2 as a double."; |
201 | 0 | obErrorLog.ThrowError(__FUNCTION__, errorMsg.str(), obWarning); |
202 | 0 | return false; |
203 | 0 | } |
204 | 0 | double z = strtod(static_cast<const char*>(vs.at(3).c_str()), &endptr); |
205 | 0 | if (endptr == static_cast<const char*>(vs.at(3).c_str())) |
206 | 0 | { |
207 | 0 | errorMsg << "Problems reading the Gaussian cube file: " |
208 | 0 | << "Could not read line #3.\n" |
209 | 0 | << "According to the specification this line should contain " |
210 | 0 | << "four entries separated by white space.\n" |
211 | 0 | << "OpenBabel could not interpret item #3 as a double."; |
212 | 0 | obErrorLog.ThrowError(__FUNCTION__, errorMsg.str(), obWarning); |
213 | 0 | return false; |
214 | 0 | } |
215 | 0 | vector3 origin(x, y, z); |
216 | 0 | origin *= BOHR_TO_ANGSTROM; |
217 | | |
218 | | // The next three lines contain the number of voxels in x, y and z along |
219 | | // with the three cube cell axes. |
220 | 0 | vector<int> voxels(3); |
221 | 0 | vector<vector3> axes(3); |
222 | 0 | bool angstroms = true; |
223 | 0 | for (int i = 0; i < 3; i++) |
224 | 0 | { |
225 | 0 | ++line; |
226 | 0 | if (!ifs.getline(buffer, BUFF_SIZE)) |
227 | 0 | { |
228 | 0 | errorMsg << "Problem reading the Gaussian cube file: cannot read the " |
229 | 0 | << line << "line.\nAccording to the specification this line " |
230 | 0 | << "should contain header information on the size of the cube.\n"; |
231 | 0 | obErrorLog.ThrowError(__FUNCTION__, errorMsg.str(), obWarning); |
232 | 0 | return false; |
233 | 0 | } |
234 | 0 | vs.clear(); |
235 | 0 | tokenize(vs, buffer); |
236 | 0 | if (vs.size() < 4) |
237 | 0 | { |
238 | 0 | errorMsg << "Problem reading the Gaussian cube file: cannot read the " |
239 | 0 | << i+4 << "line.\nAccording to the specification this line " |
240 | 0 | << "should contain four elements (cube header).\n"; |
241 | 0 | obErrorLog.ThrowError(__FUNCTION__, errorMsg.str(), obWarning); |
242 | 0 | return false; |
243 | 0 | } |
244 | 0 | voxels[i] = strtol(static_cast<const char*>(vs.at(0).c_str()), &endptr, 10); |
245 | 0 | if (endptr == static_cast<const char*>(vs.at(0).c_str())) |
246 | 0 | { |
247 | 0 | errorMsg << "Problems reading the Gaussian cube file: " |
248 | 0 | << "Could not read line " << i+4 << ".\n" |
249 | 0 | << "According to the specification this line should contain " |
250 | 0 | << "four entries separated by white space.\n" |
251 | 0 | << "OpenBabel could not interpret item #0 as an integer."; |
252 | 0 | obErrorLog.ThrowError(__FUNCTION__, errorMsg.str(), obWarning); |
253 | 0 | return false; |
254 | 0 | } |
255 | 0 | if (voxels[i] < 0) |
256 | 0 | { |
257 | | // If the number of voxels is negative then the cube is in Bohr |
258 | 0 | angstroms = false; |
259 | 0 | voxels[i] *= -1; |
260 | 0 | } |
261 | 0 | x = strtod(static_cast<const char*>(vs.at(1).c_str()), &endptr); |
262 | 0 | if (endptr == static_cast<const char*>(vs.at(1).c_str())) |
263 | 0 | { |
264 | 0 | errorMsg << "Problems reading the Gaussian cube file: " |
265 | 0 | << "Could not read line " << i+4 << ".\n" |
266 | 0 | << "According to the specification this line should contain " |
267 | 0 | << "four entries separated by white space.\n" |
268 | 0 | << "OpenBabel could not interpret item #1 as a double."; |
269 | 0 | obErrorLog.ThrowError(__FUNCTION__, errorMsg.str(), obWarning); |
270 | 0 | return false; |
271 | 0 | } |
272 | 0 | y = strtod(static_cast<const char*>(vs.at(2).c_str()), &endptr); |
273 | 0 | if (endptr == static_cast<const char*>(vs.at(2).c_str())) |
274 | 0 | { |
275 | 0 | errorMsg << "Problems reading the Gaussian cube file: " |
276 | 0 | << "Could not read line " << i+4 << ".\n" |
277 | 0 | << "According to the specification this line should contain " |
278 | 0 | << "four entries separated by white space.\n" |
279 | 0 | << "OpenBabel could not interpret item #2 as a double."; |
280 | 0 | obErrorLog.ThrowError(__FUNCTION__, errorMsg.str(), obWarning); |
281 | 0 | return false; |
282 | 0 | } |
283 | 0 | z = strtod(static_cast<const char*>(vs.at(3).c_str()), &endptr); |
284 | 0 | if (endptr == static_cast<const char*>(vs.at(3).c_str())) |
285 | 0 | { |
286 | 0 | errorMsg << "Problems reading the Gaussian cube file: " |
287 | 0 | << "Could not read line " << i+4 << ".\n" |
288 | 0 | << "According to the specification this line should contain " |
289 | 0 | << "four entries separated by white space.\n" |
290 | 0 | << "OpenBabel could not interpret item #3 as a double."; |
291 | 0 | obErrorLog.ThrowError(__FUNCTION__, errorMsg.str(), obWarning); |
292 | 0 | return false; |
293 | 0 | } |
294 | 0 | axes[i] = vector3(x, y, z); |
295 | 0 | axes[i] *= BOHR_TO_ANGSTROM; |
296 | 0 | } |
297 | | |
298 | 0 | pmol->BeginModify(); |
299 | 0 | pmol->SetDimension(3); |
300 | 0 | pmol->ReserveAtoms(nAtoms); |
301 | | |
302 | | // Now to read in the atoms contained in the Gaussian cube |
303 | 0 | for (int i = 0; i < nAtoms; i++) |
304 | 0 | { |
305 | 0 | ++line; |
306 | 0 | if (!ifs.getline(buffer, BUFF_SIZE)) |
307 | 0 | { |
308 | 0 | errorMsg << "Problem reading the Gaussian cube file: cannot read the " |
309 | 0 | << line << "line.\nAccording to the specification this line " |
310 | 0 | << "should contain atomic number and coordinates.\n"; |
311 | 0 | obErrorLog.ThrowError(__FUNCTION__, errorMsg.str(), obWarning); |
312 | 0 | return false; |
313 | 0 | } |
314 | 0 | vs.clear(); |
315 | 0 | tokenize(vs, buffer); |
316 | 0 | if (vs.size() < 5) |
317 | 0 | { |
318 | 0 | errorMsg << "Problem reading the Gaussian cube file: cannot read the " |
319 | 0 | << line << "line.\nAccording to the specification this line " |
320 | 0 | << "should contain five elements (atom information).\n"; |
321 | 0 | obErrorLog.ThrowError(__FUNCTION__, errorMsg.str(), obWarning); |
322 | 0 | return false; |
323 | 0 | } |
324 | | |
325 | | // Only contains atoms - no bonds |
326 | 0 | OBAtom *atom = pmol->NewAtom(); |
327 | |
|
328 | 0 | int atomicNum = strtol(static_cast<const char*>(vs.at(0).c_str()), &endptr, 10); |
329 | 0 | if (endptr == static_cast<const char*>(vs.at(0).c_str())) |
330 | 0 | { |
331 | 0 | errorMsg << "Problems reading the Gaussian cube file: " |
332 | 0 | << "Could not read line " << line << ".\n" |
333 | 0 | << "According to the specification this line should contain " |
334 | 0 | << "five entries separated by white space.\n" |
335 | 0 | << "OpenBabel could not interpret item #0 as an integer."; |
336 | 0 | obErrorLog.ThrowError(__FUNCTION__, errorMsg.str(), obWarning); |
337 | 0 | return false; |
338 | 0 | } |
339 | | |
340 | 0 | atom->SetAtomicNum(atomicNum); |
341 | | |
342 | | // Read the atom coordinates |
343 | 0 | x = strtod(static_cast<const char*>(vs.at(2).c_str()), &endptr); |
344 | 0 | if (endptr == static_cast<const char*>(vs.at(2).c_str())) |
345 | 0 | { |
346 | 0 | errorMsg << "Problems reading the Gaussian cube file: " |
347 | 0 | << "Could not read line " << line << ".\n" |
348 | 0 | << "According to the specification this line should contain " |
349 | 0 | << "five entries separated by white space.\n" |
350 | 0 | << "OpenBabel could not interpret item #2 as a double."; |
351 | 0 | obErrorLog.ThrowError(__FUNCTION__, errorMsg.str(), obWarning); |
352 | 0 | return false; |
353 | 0 | } |
354 | 0 | y = strtod(static_cast<const char*>(vs.at(3).c_str()), &endptr); |
355 | 0 | if (endptr == static_cast<const char*>(vs.at(3).c_str())) |
356 | 0 | { |
357 | 0 | errorMsg << "Problems reading the Gaussian cube file: " |
358 | 0 | << "Could not read line " << line << ".\n" |
359 | 0 | << "According to the specification this line should contain " |
360 | 0 | << "five entries separated by white space.\n" |
361 | 0 | << "OpenBabel could not interpret item #3 as a double."; |
362 | 0 | obErrorLog.ThrowError(__FUNCTION__, errorMsg.str(), obWarning); |
363 | 0 | return false; |
364 | 0 | } |
365 | 0 | z = strtod(static_cast<const char*>(vs.at(4).c_str()), &endptr); |
366 | 0 | if (endptr == static_cast<const char*>(vs.at(4).c_str())) |
367 | 0 | { |
368 | 0 | errorMsg << "Problems reading the Gaussian cube file: " |
369 | 0 | << "Could not read line " << line << ".\n" |
370 | 0 | << "According to the specification this line should contain " |
371 | 0 | << "five entries separated by white space.\n" |
372 | 0 | << "OpenBabel could not interpret item #4 as a double."; |
373 | 0 | obErrorLog.ThrowError(__FUNCTION__, errorMsg.str(), obWarning); |
374 | 0 | return false; |
375 | 0 | } |
376 | | // Convert units... |
377 | 0 | x *= BOHR_TO_ANGSTROM; |
378 | 0 | y *= BOHR_TO_ANGSTROM; |
379 | 0 | z *= BOHR_TO_ANGSTROM; |
380 | 0 | atom->SetVector(x, y, z); |
381 | 0 | } |
382 | | |
383 | 0 | int nCubes = 1; |
384 | 0 | vector<OBGridData*> vgd; |
385 | | // If the number of atoms was negative then there is some data between the |
386 | | // atom data and the cube data, i.e. we have multiple cubes... |
387 | 0 | if (negAtoms) |
388 | 0 | { |
389 | 0 | ++line; |
390 | 0 | if (!ifs.getline(buffer, BUFF_SIZE)) |
391 | 0 | { |
392 | 0 | obErrorLog.ThrowError(__FUNCTION__, |
393 | 0 | "Problem reading the Gaussian cube file: cannot read the line between the atom data and cube data.", obError); |
394 | 0 | return false; |
395 | 0 | } |
396 | | |
397 | 0 | tokenize(vs, buffer); |
398 | 0 | if (vs.size() < 1) return false; // timvdm 18/06/2008 |
399 | 0 | nCubes = strtol(static_cast<const char*>(vs.at(0).c_str()), &endptr, 10); |
400 | 0 | if (endptr == static_cast<const char*>(vs.at(0).c_str())) |
401 | 0 | { |
402 | 0 | errorMsg << "Problems reading the Gaussian cube file: " |
403 | 0 | << "Could not read line " << line << ".\n" |
404 | 0 | << "According to the specification this line should contain " |
405 | 0 | << "the number of cubes and a number for each cube (orbital number).\n" |
406 | 0 | << "OpenBabel could not interpret item #0 as an integer."; |
407 | 0 | obErrorLog.ThrowError(__FUNCTION__, errorMsg.str(), obWarning); |
408 | 0 | return false; |
409 | 0 | } |
410 | | |
411 | 0 | vgd.reserve(nCubes); |
412 | 0 | for (unsigned int i = 1; i < vs.size(); ++i) |
413 | 0 | { |
414 | 0 | int cubeNumber = strtol(static_cast<const char*>(vs.at(i).c_str()), &endptr, 10); |
415 | 0 | if (endptr == static_cast<const char*>(vs.at(i).c_str())) |
416 | 0 | { |
417 | 0 | errorMsg << "Problems reading the Gaussian cube file: " |
418 | 0 | << "Could not read line " << line << ".\n" |
419 | 0 | << "According to the specification this line should contain " |
420 | 0 | << "the number of cubes and a number for each cube (orbital number).\n" |
421 | 0 | << "OpenBabel could not interpret item #" << vgd.size() |
422 | 0 | << " as an integer."; |
423 | 0 | obErrorLog.ThrowError(__FUNCTION__, errorMsg.str(), obWarning); |
424 | 0 | return false; |
425 | 0 | } |
426 | 0 | vgd.push_back(new OBGridData); |
427 | 0 | stringstream cubeTitle; |
428 | 0 | cubeTitle << "MO " << cubeNumber; |
429 | 0 | vgd.at(vgd.size()-1)->SetAttribute(cubeTitle.str().c_str()); |
430 | 0 | } |
431 | | // Now if we have lots of cubes we need to read in more line(s) |
432 | 0 | while (vgd.size() < nCubes) |
433 | 0 | { |
434 | 0 | ++line; |
435 | 0 | if (!ifs.getline(buffer, BUFF_SIZE)) |
436 | 0 | { |
437 | 0 | errorMsg << "Problem reading the Gaussian cube file: cannot" |
438 | 0 | << " read line " << line |
439 | 0 | << " of the file. More data was expected.\n" |
440 | 0 | << "Grid MO titles read in = " << vgd.size() |
441 | 0 | << " and expected number of values = " |
442 | 0 | << nCubes << endl; |
443 | 0 | obErrorLog.ThrowError(__FUNCTION__, errorMsg.str(), obError); |
444 | 0 | return false; |
445 | 0 | } |
446 | 0 | tokenize(vs, buffer); |
447 | 0 | for (unsigned int i = 0; i < vs.size(); ++i) |
448 | 0 | { |
449 | 0 | int cubeNumber = strtol(static_cast<const char*>(vs.at(i).c_str()), &endptr, 10); |
450 | 0 | if (endptr == static_cast<const char*>(vs.at(i).c_str())) |
451 | 0 | { |
452 | 0 | errorMsg << "Problems reading the Gaussian cube file: " |
453 | 0 | << "Could not read line " << line << ".\n" |
454 | 0 | << "According to the specification this line should contain " |
455 | 0 | << "the number of cubes and a number for each cube (orbital number).\n" |
456 | 0 | << "OpenBabel could not interpret item #" << vgd.size() |
457 | 0 | << " as an integer."; |
458 | 0 | obErrorLog.ThrowError(__FUNCTION__, errorMsg.str(), obWarning); |
459 | 0 | return false; |
460 | 0 | } |
461 | 0 | vgd.push_back(new OBGridData); |
462 | 0 | stringstream cubeTitle; |
463 | 0 | cubeTitle << "MO " << cubeNumber; |
464 | 0 | vgd.at(vgd.size()-1)->SetAttribute(cubeTitle.str().c_str()); |
465 | 0 | } |
466 | 0 | } |
467 | 0 | } |
468 | 0 | else |
469 | 0 | { |
470 | | // only one cube |
471 | 0 | vgd.push_back(new OBGridData); |
472 | 0 | vgd.at(0)->SetAttribute(cubeTitle.c_str()); |
473 | 0 | } |
474 | | |
475 | | // get all values as one vector<double> |
476 | 0 | vector<double> values; |
477 | 0 | int n = voxels[0]*voxels[1]*voxels[2]; |
478 | 0 | values.reserve(n*nCubes); |
479 | 0 | while (values.size() < n*nCubes) |
480 | 0 | { |
481 | | // Read in values until we have a complete row of data |
482 | 0 | ++line; |
483 | 0 | if (!ifs.getline(buffer, BUFF_SIZE)) |
484 | 0 | { |
485 | 0 | errorMsg << "Problem reading the Gaussian cube file: cannot" |
486 | 0 | << " read line " << line |
487 | 0 | << " of the file. More data was expected.\n" |
488 | 0 | << "Values read in = " << values.size() |
489 | 0 | << " and expected number of values = " |
490 | 0 | << n*nCubes << endl; |
491 | 0 | obErrorLog.ThrowError(__FUNCTION__, errorMsg.str(), obError); |
492 | 0 | return false; |
493 | 0 | } |
494 | 0 | tokenize(vs, buffer); |
495 | 0 | if (vs.size() == 0) |
496 | 0 | { |
497 | 0 | errorMsg << "Problem reading the Gaussian cube file: cannot" |
498 | 0 | << " read line " << line |
499 | 0 | << ", there does not appear to be any data in it.\n" |
500 | 0 | << buffer << "\n"; |
501 | 0 | obErrorLog.ThrowError(__FUNCTION__, errorMsg.str(), obError); |
502 | 0 | return false; |
503 | 0 | } |
504 | | |
505 | 0 | for (unsigned int l = 0; l < vs.size(); ++l) |
506 | 0 | { |
507 | 0 | values.push_back(strtod(static_cast<const char*>(vs.at(l).c_str()), &endptr)); |
508 | 0 | } |
509 | 0 | } |
510 | | |
511 | | // translate the vector<double> to vector<vector<double> > |
512 | 0 | vector<vector<double> > vvd; |
513 | 0 | vvd.resize(nCubes); |
514 | 0 | for (unsigned int i = 0; i < values.size(); ) |
515 | 0 | { |
516 | 0 | for (int j = 0; j < nCubes; ++j, ++i) // foreach cube |
517 | 0 | { |
518 | 0 | vvd[j].push_back(values[i]); |
519 | 0 | } |
520 | 0 | } |
521 | |
|
522 | 0 | for (int i = 0; i < nCubes; ++i) // foreach cube |
523 | 0 | { |
524 | 0 | vgd[i]->SetNumberOfPoints(voxels[0], voxels[1], voxels[2]); |
525 | 0 | vgd[i]->SetLimits(origin, axes[0], axes[1], axes[2]); |
526 | 0 | vgd[i]->SetUnit(angstroms ? OBGridData::ANGSTROM : OBGridData::BOHR); |
527 | 0 | vgd[i]->SetOrigin(fileformatInput); // i.e., is this data from a file or determined by Open Babel |
528 | 0 | vgd[i]->SetValues(vvd[i]); // set the values |
529 | 0 | pmol->SetData(vgd[i]); // store the grids in the OBMol |
530 | 0 | } |
531 | |
|
532 | 0 | pmol->EndModify(); |
533 | | |
534 | | // clean out any remaining blank lines |
535 | 0 | std::streampos ipos; |
536 | 0 | do |
537 | 0 | { |
538 | 0 | ipos = ifs.tellg(); |
539 | 0 | ifs.getline(buffer,BUFF_SIZE); |
540 | 0 | } |
541 | 0 | while(strlen(buffer) == 0 && !ifs.eof() ); |
542 | 0 | ifs.seekg(ipos); |
543 | | |
544 | | // Connect the dots and guess bond orders |
545 | 0 | if (!pConv->IsOption("b", OBConversion::INOPTIONS)) |
546 | 0 | pmol->ConnectTheDots(); |
547 | 0 | if (!pConv->IsOption("s", OBConversion::INOPTIONS) && !pConv->IsOption("b", OBConversion::INOPTIONS)) |
548 | 0 | pmol->PerceiveBondOrders(); |
549 | |
|
550 | 0 | pmol->SetDimension(3); // always a 3D structure |
551 | |
|
552 | 0 | return true; |
553 | 0 | } |
554 | | |
555 | | //------------------------------------------------------------------------------ |
556 | | bool OBGaussianCubeFormat::WriteMolecule(OBBase* pOb, OBConversion* pConv) |
557 | 0 | { |
558 | 0 | OBMol* pmol = dynamic_cast<OBMol*>(pOb); |
559 | 0 | if (pmol == nullptr) |
560 | 0 | return false; |
561 | | |
562 | 0 | ostream &ofs = *pConv->GetOutStream(); |
563 | 0 | OBMol &mol = *pmol; |
564 | |
|
565 | 0 | char buffer[BUFF_SIZE]; |
566 | 0 | string str; |
567 | 0 | stringstream errorMsg; |
568 | | |
569 | | // first two lines are comments |
570 | 0 | str = mol.GetTitle(); |
571 | 0 | if (str.empty()) |
572 | 0 | ofs << "*****" << endl; |
573 | 0 | else |
574 | 0 | ofs << str << endl; |
575 | |
|
576 | 0 | ofs << endl; // line 2 |
577 | |
|
578 | 0 | OBGridData *gd = (OBGridData*)mol.GetData(OBGenericDataType::GridData); |
579 | 0 | if (gd == nullptr) { |
580 | 0 | errorMsg << "The molecule has no grid."; |
581 | 0 | obErrorLog.ThrowError(__FUNCTION__, errorMsg.str(), obWarning); |
582 | 0 | return false; |
583 | 0 | } |
584 | | |
585 | 0 | int nx, ny, nz; |
586 | 0 | double origin[3], xAxis[3], yAxis[3], zAxis[3]; |
587 | 0 | gd->GetAxes(xAxis, yAxis, zAxis); |
588 | 0 | gd->GetNumberOfPoints(nx, ny, nz); |
589 | 0 | gd->GetOriginVector(origin); |
590 | | |
591 | | // line 3: number of atoms, origin x y z |
592 | 0 | snprintf(buffer, BUFF_SIZE,"%5d%12.6f%12.6f%12.6f", - static_cast<signed int> (mol.NumAtoms()), |
593 | 0 | origin[0]*ANGSTROM_TO_BOHR, origin[1]*ANGSTROM_TO_BOHR, origin[2]*ANGSTROM_TO_BOHR); |
594 | 0 | ofs << buffer << endl; |
595 | | |
596 | | // line 4: number of points x direction, axis x direction x y z |
597 | 0 | snprintf(buffer, BUFF_SIZE,"%5d%12.6f%12.6f%12.6f", nx, |
598 | 0 | xAxis[0]*ANGSTROM_TO_BOHR, xAxis[1]*ANGSTROM_TO_BOHR, xAxis[2]*ANGSTROM_TO_BOHR); |
599 | 0 | ofs << buffer << endl; |
600 | | |
601 | | // line 5: number of points y direction, axis y direction x y z |
602 | 0 | snprintf(buffer, BUFF_SIZE,"%5d%12.6f%12.6f%12.6f", ny, |
603 | 0 | yAxis[0]*ANGSTROM_TO_BOHR, yAxis[1]*ANGSTROM_TO_BOHR, yAxis[2]*ANGSTROM_TO_BOHR); |
604 | 0 | ofs << buffer << endl; |
605 | | |
606 | | // line 6: number of points z direction, axis z direction x y z |
607 | 0 | snprintf(buffer, BUFF_SIZE,"%5d%12.6f%12.6f%12.6f", nz, |
608 | 0 | zAxis[0]*ANGSTROM_TO_BOHR, zAxis[1]*ANGSTROM_TO_BOHR, zAxis[2]*ANGSTROM_TO_BOHR); |
609 | 0 | ofs << buffer << endl; |
610 | | |
611 | | // Atom lines: atomic number, ?, X, Y, Z |
612 | 0 | FOR_ATOMS_OF_MOL (atom, mol) { |
613 | 0 | double *coordPtr = atom->GetCoordinate(); |
614 | 0 | snprintf(buffer, BUFF_SIZE,"%5d%12.6f%12.6f%12.6f%12.6f", atom->GetAtomicNum(), |
615 | 0 | static_cast<double>(atom->GetAtomicNum()), |
616 | 0 | coordPtr[0]*ANGSTROM_TO_BOHR, coordPtr[1]*ANGSTROM_TO_BOHR, coordPtr[2]*ANGSTROM_TO_BOHR); |
617 | 0 | ofs << buffer << endl; |
618 | 0 | } |
619 | |
|
620 | 0 | vector<OBGenericData*> grids = pmol->GetAllData(OBGenericDataType::GridData); |
621 | 0 | snprintf(buffer, BUFF_SIZE," %5lu", (unsigned long)grids.size()); |
622 | 0 | ofs << buffer << flush; |
623 | 0 | for (unsigned int l = 1; l <= grids.size(); ++l) |
624 | 0 | { |
625 | 0 | snprintf(buffer, BUFF_SIZE," %3d", l); |
626 | 0 | ofs << buffer << flush; |
627 | 0 | } |
628 | 0 | ofs << endl; |
629 | |
|
630 | 0 | for (unsigned int l = 0; l < grids.size(); ++l) |
631 | 0 | { |
632 | 0 | gd = static_cast<OBGridData*>(grids[l]); |
633 | 0 | int mx, my, mz; |
634 | 0 | gd->GetNumberOfPoints(mx, my, mz); |
635 | |
|
636 | 0 | if ((nx != mx) || (ny != my) || (nz != mz)) |
637 | 0 | { |
638 | 0 | errorMsg << "Problem writing the Gaussian cube file: cube " << l |
639 | 0 | << " does not have the same dimentions as cube 0.\n" |
640 | 0 | << "This cube will be skipped.\n"; |
641 | 0 | obErrorLog.ThrowError(__FUNCTION__, errorMsg.str(), obError); |
642 | 0 | } |
643 | 0 | } |
644 | | |
645 | | // The cube(s) |
646 | 0 | double value; |
647 | 0 | unsigned int count = 1; |
648 | 0 | for (int i = 0; i < nx; ++i) |
649 | 0 | { |
650 | 0 | for (int j = 0; j < ny; ++j) |
651 | 0 | { |
652 | 0 | for (int k = 0; k < nz; ++k) |
653 | 0 | { |
654 | | // The cube files are stored in staggered z |
655 | 0 | for (unsigned int l = 0; l < grids.size(); ++l) |
656 | 0 | { |
657 | 0 | value = static_cast<OBGridData*>(grids[l])->GetValue(i, j, k); |
658 | 0 | snprintf(buffer, BUFF_SIZE," %12.5E", value); |
659 | 0 | if (count % 6 == 0) |
660 | 0 | ofs << buffer << endl; |
661 | 0 | else |
662 | 0 | ofs << buffer; |
663 | 0 | count++; |
664 | 0 | } |
665 | 0 | } // z-axis |
666 | 0 | } // y-axis |
667 | 0 | } // x-axis |
668 | |
|
669 | 0 | return true; |
670 | 0 | } |
671 | | } |