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