1 /******************************************************************************
2
3 This source file is part of the Avogadro project.
4
5 Copyright 2010 Geoffrey R. Hutchison
6
7 This source code is released under the New BSD License, (the "License").
8
9 Unless required by applicable law or agreed to in writing, software
10 distributed under the License is distributed on an "AS IS" BASIS,
11 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 See the License for the specific language governing permissions and
13 limitations under the License.
14
15 ******************************************************************************/
16
17 #include "gamessus.h"
18
19 #include <avogadro/core/molecule.h>
20 #include <avogadro/core/utilities.h>
21
22 #include <iostream>
23
24 using std::vector;
25 using std::string;
26 using std::cout;
27 using std::endl;
28
29 namespace Avogadro {
30 namespace QuantumIO {
31
32 using Core::Atom;
33 using Core::BasisSet;
34 using Core::GaussianSet;
35 using Core::Rhf;
36 using Core::Uhf;
37 using Core::Rohf;
38 using Core::Unknown;
39
GAMESSUSOutput()40 GAMESSUSOutput::GAMESSUSOutput() : m_coordFactor(1.0), m_scftype(Rhf)
41 {
42 }
43
~GAMESSUSOutput()44 GAMESSUSOutput::~GAMESSUSOutput()
45 {
46 }
47
fileExtensions() const48 std::vector<std::string> GAMESSUSOutput::fileExtensions() const
49 {
50 std::vector<std::string> extensions;
51 extensions.push_back("gamout");
52 extensions.push_back("gamess");
53 extensions.push_back("log");
54 extensions.push_back("out");
55 return extensions;
56 }
57
mimeTypes() const58 std::vector<std::string> GAMESSUSOutput::mimeTypes() const
59 {
60 return std::vector<std::string>();
61 }
62
read(std::istream & in,Core::Molecule & molecule)63 bool GAMESSUSOutput::read(std::istream& in, Core::Molecule& molecule)
64 {
65 // Read the log file line by line, most sections are terminated by an empty
66 // line, so they should be retained.
67 bool atomsRead(false);
68 string buffer;
69 while (getline(in, buffer)) {
70 if (Core::contains(buffer, "COORDINATES (BOHR)")) {
71 if (atomsRead)
72 continue;
73 atomsRead = true;
74 readAtomBlock(in, molecule, false);
75 } else if (Core::contains(buffer, "COORDINATES OF ALL ATOMS ARE (ANGS)")) {
76 if (atomsRead)
77 continue;
78 atomsRead = true;
79 readAtomBlock(in, molecule, true);
80 } else if (Core::contains(buffer, "ATOMIC BASIS SET")) {
81 readBasisSet(in);
82 } else if (Core::contains(buffer, "NUMBER OF ELECTRONS")) {
83 vector<string> parts = Core::split(buffer, '=');
84 if (parts.size() == 2)
85 m_electrons = Core::lexicalCast<int>(parts[1]);
86 else
87 cout << "error" << buffer << endl;
88 }
89 /*else if (Core::contains(buffer, "NUMBER OF OCCUPIED ORBITALS (ALPHA)")) {
90 cout << "Found alpha orbitals\n";
91 }
92 else if (Core::contains(buffer, "NUMBER OF OCCUPIED ORBITALS (BETA )")) {
93 cout << "Found alpha orbitals\n";
94 }
95 else if (Core::contains(buffer, "SCFTYP=")) {
96 cout << "Found SCF type\n";
97 }*/
98 else if (Core::contains(buffer, "EIGENVECTORS")) {
99 readEigenvectors(in);
100 }
101 }
102
103 // f functions and beyond need to be reordered
104 reorderMOs();
105
106 molecule.perceiveBondsSimple();
107 GaussianSet* basis = new GaussianSet;
108 load(basis);
109 molecule.setBasisSet(basis);
110 basis->setMolecule(&molecule);
111
112 // outputAll();
113
114 return true;
115 }
116
readAtomBlock(std::istream & in,Core::Molecule & molecule,bool angs)117 void GAMESSUSOutput::readAtomBlock(std::istream& in, Core::Molecule& molecule,
118 bool angs)
119 {
120 // We read the atom block in until it terminates with a blank line.
121 double coordFactor = angs ? 1.0 : BOHR_TO_ANGSTROM_D;
122 string buffer;
123 while (getline(in, buffer)) {
124 if (Core::contains(buffer, "CHARGE") || Core::contains(buffer, "------"))
125 continue;
126 else if (buffer == "\n") // Our work here is done.
127 return;
128 vector<string> parts = Core::split(buffer, ' ');
129 if (parts.size() != 5) {
130 appendError("Poorly formed atom line: " + buffer);
131 return;
132 }
133 bool ok(false);
134 Vector3 pos;
135 unsigned char atomicNumber(
136 static_cast<unsigned char>(Core::lexicalCast<int>(parts[1], ok)));
137 if (!ok)
138 appendError("Failed to cast to int for atomic number: " + parts[1]);
139 pos.x() = Core::lexicalCast<Real>(parts[2], ok) * coordFactor;
140 if (!ok)
141 appendError("Failed to cast to double for position: " + parts[2]);
142 pos.y() = Core::lexicalCast<Real>(parts[3], ok) * coordFactor;
143 if (!ok)
144 appendError("Failed to cast to double for position: " + parts[3]);
145 pos.z() = Core::lexicalCast<Real>(parts[4], ok) * coordFactor;
146 if (!ok)
147 appendError("Failed to cast to double for position: " + parts[4]);
148 Atom atom = molecule.addAtom(atomicNumber);
149 atom.setPosition3d(pos);
150 }
151 }
152
readBasisSet(std::istream & in)153 void GAMESSUSOutput::readBasisSet(std::istream& in)
154 {
155 // Basic strategy is to use the number of parts in a line to determine the
156 // type, where atom has 1 part, and a GTO has 5 (or 6 for SP/L). Termination
157 // of the block when we hit the summary information at the end.
158 string buffer;
159 int currentAtom(0);
160 bool header(true);
161 while (getline(in, buffer)) {
162 if (header) { // Skip the header lines until we hit the last header line.
163 if (Core::contains(buffer, "SHELL"))
164 header = false;
165 continue;
166 }
167 vector<string> parts = Core::split(buffer, ' ');
168 if (Core::contains(buffer, "TOTAL NUMBER OF BASIS SET SHELLS")) {
169 // End of the basis set block.
170 return;
171 } else if (parts.size() == 1) {
172 // Currently just incrememt the current atom, we should probably at least
173 // verify the element matches in the future too.
174 ++currentAtom;
175 } else if (parts.size() == 5 || parts.size() == 6) {
176 if (parts[1].size() != 1) {
177 appendError("Error parsing basis set line, unrecognized type" +
178 parts[1]);
179 continue;
180 }
181 // Determine the shell type.
182 GaussianSet::orbital shellType(GaussianSet::UU);
183 switch (parts[1][0]) {
184 case 'S':
185 shellType = GaussianSet::S;
186 break;
187 case 'L':
188 shellType = GaussianSet::SP;
189 break;
190 case 'P':
191 shellType = GaussianSet::P;
192 break;
193 case 'D':
194 shellType = GaussianSet::D;
195 break;
196 case 'F':
197 shellType = GaussianSet::F;
198 break;
199 default:
200 shellType = GaussianSet::UU;
201 appendError("Unrecognized shell type: " + parts[1]);
202 }
203 // Read in the rest of the shell, terminate when the number of tokens
204 // is not 5 or 6 in a line.
205 int numGTOs(0);
206 while (parts.size() == 5 || parts.size() == 6) {
207 ++numGTOs;
208 m_a.push_back(Core::lexicalCast<double>(parts[3]));
209 m_c.push_back(Core::lexicalCast<double>(parts[4]));
210 if (shellType == GaussianSet::SP && parts.size() == 6)
211 m_csp.push_back(Core::lexicalCast<double>(parts[5]));
212 if (!getline(in, buffer))
213 break;
214 parts = Core::split(buffer, ' ');
215 }
216 // Now add this to our data structure.
217 m_shellNums.push_back(numGTOs);
218 m_shellTypes.push_back(shellType);
219 m_shelltoAtom.push_back(currentAtom);
220 }
221 }
222 }
223
readEigenvectors(std::istream & in)224 void GAMESSUSOutput::readEigenvectors(std::istream& in)
225 {
226 string buffer;
227 getline(in, buffer);
228 getline(in, buffer);
229 getline(in, buffer);
230 vector<string> parts = Core::split(buffer, ' ');
231 vector<vector<double>> eigenvectors;
232 bool ok(false);
233 size_t numberOfMos(0);
234 bool newBlock(true);
235 while (!Core::contains(buffer, "END OF") ||
236 Core::contains(buffer, "--------")) {
237 // Any line with actual information in it will contain >= 5 parts.
238 if (parts.size() > 5 && buffer.substr(0, 16) != " ") {
239 if (newBlock) {
240 // Reorder the columns/rows, add them and then prepare
241 for (size_t i = 0; i < eigenvectors.size(); ++i)
242 for (size_t j = 0; j < eigenvectors[i].size(); ++j)
243 m_MOcoeffs.push_back(eigenvectors[i][j]);
244 eigenvectors.clear();
245 eigenvectors.resize(parts.size() - 4);
246 numberOfMos += eigenvectors.size();
247 newBlock = false;
248 }
249 for (size_t i = 0; i < parts.size() - 4; ++i) {
250 eigenvectors[i].push_back(Core::lexicalCast<double>(parts[i + 4], ok));
251 if (!ok)
252 appendError("Failed to cast to double for eigenvector: " + parts[i]);
253 }
254 } else {
255 // Note that we are either ending or entering a new block of orbitals.
256 newBlock = true;
257 }
258 if (!getline(in, buffer))
259 break;
260 parts = Core::split(buffer, ' ');
261 }
262 m_nMOs = numberOfMos;
263 for (size_t i = 0; i < eigenvectors.size(); ++i)
264 for (size_t j = 0; j < eigenvectors[i].size(); ++j)
265 m_MOcoeffs.push_back(eigenvectors[i][j]);
266
267 // Now we just need to transpose the matrix, as GAMESS uses a different order.
268 // We know the number of columns (MOs), and the number of rows (primitives).
269 if (eigenvectors.size() != numberOfMos * m_a.size()) {
270 appendError("Incorrect number of eigenvectors loaded.");
271 return;
272 }
273 }
274
load(GaussianSet * basis)275 void GAMESSUSOutput::load(GaussianSet* basis)
276 {
277 // Now load up our basis set
278 basis->setElectronCount(m_electrons);
279
280 // Set up the GTO primitive counter, go through the shells and add them
281 int nGTO = 0;
282 int nSP = 0; // number of SP shells
283 for (unsigned int i = 0; i < m_shellTypes.size(); ++i) {
284 // Handle the SP case separately - this should possibly be a distinct type
285 if (m_shellTypes.at(i) == GaussianSet::SP) {
286 // SP orbital type - currently have to unroll into two shells
287 int tmpGTO = nGTO;
288 int s = basis->addBasis(m_shelltoAtom.at(i) - 1, GaussianSet::S);
289 for (int j = 0; j < m_shellNums.at(i); ++j) {
290 basis->addGto(s, m_c.at(nGTO), m_a.at(nGTO));
291 ++nGTO;
292 }
293 int p = basis->addBasis(m_shelltoAtom.at(i) - 1, GaussianSet::P);
294 for (int j = 0; j < m_shellNums.at(i); ++j) {
295 basis->addGto(p, m_csp.at(nSP), m_a.at(tmpGTO));
296 ++tmpGTO;
297 ++nSP;
298 }
299 } else {
300 int b = basis->addBasis(m_shelltoAtom.at(i) - 1, m_shellTypes.at(i));
301 for (int j = 0; j < m_shellNums.at(i); ++j) {
302 basis->addGto(b, m_c.at(nGTO), m_a.at(nGTO));
303 ++nGTO;
304 }
305 }
306 }
307 // qDebug() << " loading MOs " << m_MOcoeffs.size();
308
309 // Now to load in the MO coefficients
310 if (m_MOcoeffs.size())
311 basis->setMolecularOrbitals(m_MOcoeffs);
312 if (m_alphaMOcoeffs.size())
313 basis->setMolecularOrbitals(m_alphaMOcoeffs, BasisSet::Alpha);
314 if (m_betaMOcoeffs.size())
315 basis->setMolecularOrbitals(m_betaMOcoeffs, BasisSet::Beta);
316
317 // generateDensity();
318 // if (m_density.rows())
319 // basis->setDensityMatrix(m_density);
320
321 basis->setScfType(m_scftype);
322 }
323
reorderMOs()324 void GAMESSUSOutput::reorderMOs()
325 {
326 unsigned int GTOcounter = 0;
327 for (int iMO = 0; iMO < m_nMOs; iMO++) {
328 // loop over the basis set shells
329 for (unsigned int i = 0; i < m_shellTypes.size(); i++) {
330 // The angular momentum of the shell
331 // determines the number of primitive GTOs.
332 // GAMESS always prints the full cartesian set.
333 double yyy, zzz, xxy, xxz, yyx, yyz, zzx, zzy, xyz;
334 unsigned int nPrimGTOs = 0;
335 switch (m_shellTypes.at(i)) {
336 case GaussianSet::S:
337 nPrimGTOs = 1;
338 GTOcounter += nPrimGTOs;
339 break;
340 case GaussianSet::P:
341 nPrimGTOs = 3;
342 GTOcounter += nPrimGTOs;
343 break;
344 // L?
345 case GaussianSet::D:
346 nPrimGTOs = 6;
347 GTOcounter += nPrimGTOs;
348 break;
349 case GaussianSet::F:
350 nPrimGTOs = 10;
351 // f functions are the first set to be reordered.
352 // double xxx = m_MOcoeffs.at(GTOcounter);
353 yyy = m_MOcoeffs.at(GTOcounter + 1);
354 zzz = m_MOcoeffs.at(GTOcounter + 2);
355 xxy = m_MOcoeffs.at(GTOcounter + 3);
356 xxz = m_MOcoeffs.at(GTOcounter + 4);
357 yyx = m_MOcoeffs.at(GTOcounter + 5);
358 yyz = m_MOcoeffs.at(GTOcounter + 6);
359 zzx = m_MOcoeffs.at(GTOcounter + 7);
360 zzy = m_MOcoeffs.at(GTOcounter + 8);
361 xyz = m_MOcoeffs.at(GTOcounter + 9);
362 // xxx is unchanged
363 m_MOcoeffs.at(GTOcounter + 1) = xxy; // xxy
364 m_MOcoeffs.at(GTOcounter + 2) = xxz; // xxz
365 m_MOcoeffs.at(GTOcounter + 3) = yyx; // xyy
366 m_MOcoeffs.at(GTOcounter + 4) = xyz; // xyz
367 m_MOcoeffs.at(GTOcounter + 5) = zzx; // xzz
368 m_MOcoeffs.at(GTOcounter + 6) = yyy; // yyy
369 m_MOcoeffs.at(GTOcounter + 7) = yyz; // yyz
370 m_MOcoeffs.at(GTOcounter + 8) = zzy; // yzz
371 m_MOcoeffs.at(GTOcounter + 9) = zzz; // zzz
372
373 GTOcounter += nPrimGTOs;
374 break;
375 case GaussianSet::G:
376 nPrimGTOs = 15;
377 GTOcounter += nPrimGTOs;
378 break;
379 case GaussianSet::H:
380 nPrimGTOs = 21;
381 GTOcounter += nPrimGTOs;
382 break;
383 case GaussianSet::I:
384 nPrimGTOs = 28;
385 GTOcounter += nPrimGTOs;
386 break;
387 default:
388 cout << "Basis set not handled - results may be incorrect.\n";
389 }
390 }
391 }
392 }
393
outputAll()394 void GAMESSUSOutput::outputAll()
395 {
396 switch (m_scftype) {
397 case Rhf:
398 cout << "SCF type = RHF" << endl;
399 break;
400 case Uhf:
401 cout << "SCF type = UHF" << endl;
402 break;
403 case Rohf:
404 cout << "SCF type = ROHF" << endl;
405 break;
406 default:
407 cout << "SCF typ = Unknown" << endl;
408 }
409 cout << "Shell mappings\n";
410 for (unsigned int i = 0; i < m_shellTypes.size(); ++i) {
411 cout << i << ": type = " << m_shellTypes.at(i)
412 << ", number = " << m_shellNums.at(i)
413 << ", atom = " << m_shelltoAtom.at(i) << endl;
414 }
415 int nGTOs = 0;
416 if (m_MOcoeffs.size()) {
417 nGTOs = m_MOcoeffs.size() / m_nMOs;
418 cout << m_nMOs << " MOs, " << nGTOs << " GTOs" << endl;
419 }
420
421 for (unsigned int iMO = 0; iMO < 10; iMO++) {
422 for (unsigned int i = iMO * nGTOs; i < nGTOs * iMO + 10;
423 ++i) // m_MOcoeffs.size(); ++i)
424 cout << m_MOcoeffs.at(i) << "\t";
425 cout << "\n";
426 }
427
428 if (m_alphaMOcoeffs.size())
429 cout << "Alpha MO coefficients.\n";
430 for (unsigned int i = 0; i < m_alphaMOcoeffs.size(); ++i)
431 cout << m_alphaMOcoeffs.at(i);
432 if (m_betaMOcoeffs.size())
433 cout << "Beta MO coefficients.\n";
434 for (unsigned int i = 0; i < m_betaMOcoeffs.size(); ++i)
435 cout << m_betaMOcoeffs.at(i);
436 cout << std::flush;
437 }
438 }
439 }
440