Coverage for cclib/parser/jaguarparser.py : 99%
Hot-keys on this page
r m x p toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
1# -*- coding: utf-8 -*-
2#
3# Copyright (c) 2020, the cclib development team
4#
5# This file is part of cclib (http://cclib.github.io) and is distributed under
6# the terms of the BSD 3-Clause License.
8"""Parser for Jaguar output files"""
11import re
13import numpy
15from cclib.parser import logfileparser
16from cclib.parser import utils
19class Jaguar(logfileparser.Logfile):
20 """A Jaguar output file"""
22 def __init__(self, *args, **kwargs):
24 # Call the __init__ method of the superclass
25 super(Jaguar, self).__init__(logname="Jaguar", *args, **kwargs)
27 def __str__(self):
28 """Return a string representation of the object."""
29 return "Jaguar output file %s" % (self.filename)
31 def __repr__(self):
32 """Return a representation of the object."""
33 return 'Jaguar("%s")' % (self.filename)
35 def normalisesym(self, label):
36 """Normalise the symmetries used by Jaguar.
38 To normalise, three rules need to be applied:
39 (1) To handle orbitals of E symmetry, retain everything before the /
40 (2) Replace two p's by "
41 (2) Replace any remaining single p's by '
42 """
43 ans = label.split("/")[0].replace("pp", '"').replace("p", "'")
44 return ans
46 def before_parsing(self):
48 # We need to track whether we are inside geometry optimization in order
49 # to parse SCF targets/values correctly.
50 self.geoopt = False
52 def after_parsing(self):
54 # This is to make sure we always have optdone after geometry optimizations,
55 # even if it is to be empty for unconverged runs. We have yet to test this
56 # with a regression for Jaguar, though.
57 if self.geoopt and not hasattr(self, 'optdone'):
58 self.optdone = []
60 def extract(self, inputfile, line):
61 """Extract information from the file object inputfile."""
63 # Extract the package version number.
64 if "Jaguar version" in line:
65 tokens = line.split()
66 base_version = tokens[3][:-1]
67 package_version = "{}+{}".format(base_version, tokens[5])
68 self.metadata["package_version"] = package_version
69 self.metadata["legacy_package_version"] = base_version
71 # Extract the basis set name
72 if line[2:12] == "basis set:":
73 self.metadata["basis_set"] = line.split()[2]
75 # Extract charge and multiplicity
76 if line[2:22] == "net molecular charge":
77 self.set_attribute('charge', int(line.split()[-1]))
78 self.set_attribute('mult', int(next(inputfile).split()[-1]))
80 # The Gaussian basis set information is printed before the geometry, and we need
81 # to do some indexing to get this into cclib format, because fn increments
82 # for each engular momentum, but cclib does not (we have just P instead of
83 # all three X/Y/Z with the same parameters. On the other hand, fn enumerates
84 # the atomic orbitals correctly, so use it to build atombasis.
85 #
86 # Gaussian basis set information
87 #
88 # renorm mfac*renorm
89 # atom fn prim L z coef coef coef
90 # -------- ----- ---- --- ------------- ----------- ----------- -----------
91 # C1 1 1 S 7.161684E+01 1.5433E-01 2.7078E+00 2.7078E+00
92 # C1 1 2 S 1.304510E+01 5.3533E-01 2.6189E+00 2.6189E+00
93 # ...
94 # C1 3 6 X 2.941249E+00 2.2135E-01 1.2153E+00 1.2153E+00
95 # 4 Y 1.2153E+00
96 # 5 Z 1.2153E+00
97 # C1 2 8 S 2.222899E-01 1.0000E+00 2.3073E-01 2.3073E-01
98 # C1 3 7 X 6.834831E-01 8.6271E-01 7.6421E-01 7.6421E-01
99 # ...
100 # C2 6 1 S 7.161684E+01 1.5433E-01 2.7078E+00 2.7078E+00
101 # ...
102 #
103 if line.strip() == "Gaussian basis set information":
105 self.skip_lines(inputfile, ['b', 'renorm', 'header', 'd'])
107 # This is probably the only place we can get this information from Jaguar.
108 self.gbasis = []
110 atombasis = []
111 line = next(inputfile)
112 fn_per_atom = []
113 while line.strip():
115 if len(line.split()) > 3:
117 aname = line.split()[0]
118 fn = int(line.split()[1])
119 prim = int(line.split()[2])
120 L = line.split()[3]
121 z = float(line.split()[4])
122 coef = float(line.split()[5])
124 # The primitive count is reset for each atom, so use that for adding
125 # new elements to atombasis and gbasis. We could also probably do this
126 # using the atom name, although that perhaps might not always be unique.
127 if prim == 1:
128 atombasis.append([])
129 fn_per_atom = []
130 self.gbasis.append([])
132 # Remember that fn is repeated when functions are contracted.
133 if not fn-1 in atombasis[-1]:
134 atombasis[-1].append(fn-1)
136 # Here we use fn only to know when a new contraction is encountered,
137 # so we don't need to decrement it, and we don't even use all values.
138 # What's more, since we only wish to save the parameters for each subshell
139 # once, we don't even need to consider lines for orbitals other than
140 # those for X*, making things a bit easier.
141 if not fn in fn_per_atom:
142 fn_per_atom.append(fn)
143 label = {'S': 'S', 'X': 'P', 'XX': 'D', 'XXX': 'F'}[L]
144 self.gbasis[-1].append((label, []))
145 igbasis = fn_per_atom.index(fn)
146 self.gbasis[-1][igbasis][1].append([z, coef])
148 else:
150 fn = int(line.split()[0])
151 L = line.split()[1]
153 # Some AO indices are only printed in these lines, for L > 0.
154 if not fn-1 in atombasis[-1]:
155 atombasis[-1].append(fn-1)
157 line = next(inputfile)
159 # The indices for atombasis can also be read later from the molecular orbital output.
160 self.set_attribute('atombasis', atombasis)
162 # This length of atombasis should always be the number of atoms.
163 self.set_attribute('natom', len(self.atombasis))
165 # Effective Core Potential
166 #
167 # Atom Electrons represented by ECP
168 # Mo 36
169 # Maximum angular term 3
170 # F Potential 1/r^n Exponent Coefficient
171 # ----- -------- -----------
172 # 0 140.4577691 -0.0469492
173 # 1 89.4739342 -24.9754989
174 # ...
175 # S-F Potential 1/r^n Exponent Coefficient
176 # ----- -------- -----------
177 # 0 33.7771969 2.9278406
178 # 1 10.0120020 34.3483716
179 # ...
180 # O 0
181 # Cl 10
182 # Maximum angular term 2
183 # D Potential 1/r^n Exponent Coefficient
184 # ----- -------- -----------
185 # 1 94.8130000 -10.0000000
186 # ...
187 if line.strip() == "Effective Core Potential":
189 self.skip_line(inputfile, 'blank')
190 line = next(inputfile)
191 assert line.split()[0] == "Atom"
192 assert " ".join(line.split()[1:]) == "Electrons represented by ECP"
194 self.coreelectrons = []
195 line = next(inputfile)
196 while line.strip():
197 if len(line.split()) == 2:
198 self.coreelectrons.append(int(line.split()[1]))
199 line = next(inputfile)
201 if line[2:14] == "new geometry" or line[1:21] == "Symmetrized geometry" or line.find("Input geometry") > 0:
202 # Get the atom coordinates
203 if not hasattr(self, "atomcoords") or line[1:21] == "Symmetrized geometry":
204 # Wipe the "Input geometry" if "Symmetrized geometry" present
205 self.atomcoords = []
206 p = re.compile(r"(\D+)\d+") # One/more letters followed by a number
207 atomcoords = []
208 atomnos = []
209 angstrom = next(inputfile)
210 title = next(inputfile)
211 line = next(inputfile)
212 while line.strip():
213 temp = line.split()
214 element = p.findall(temp[0])[0]
215 atomnos.append(self.table.number[element])
216 atomcoords.append(list(map(float, temp[1:])))
217 line = next(inputfile)
218 self.atomcoords.append(atomcoords)
219 self.atomnos = numpy.array(atomnos, "i")
220 self.set_attribute('natom', len(atomcoords))
222 # Hartree-Fock energy after SCF
223 if line[1:18] == "SCFE: SCF energy:":
224 self.metadata["methods"].append("HF")
225 if not hasattr(self, "scfenergies"):
226 self.scfenergies = []
227 temp = line.strip().split()
228 scfenergy = float(temp[temp.index("hartrees") - 1])
229 scfenergy = utils.convertor(scfenergy, "hartree", "eV")
230 self.scfenergies.append(scfenergy)
232 # Energy after LMP2 correction
233 if line[1:18] == "Total LMP2 Energy":
234 self.metadata["methods"].append("LMP2")
235 if not hasattr(self, "mpenergies"):
236 self.mpenergies = [[]]
237 lmp2energy = float(line.split()[-1])
238 lmp2energy = utils.convertor(lmp2energy, "hartree", "eV")
239 self.mpenergies[-1].append(lmp2energy)
241 if line[15:45] == "Geometry optimization complete":
242 if not hasattr(self, 'optdone'):
243 self.optdone = []
244 self.optdone.append(len(self.geovalues) - 1)
246 if line.find("number of occupied orbitals") > 0:
247 # Get number of MOs
248 occs = int(line.split()[-1])
249 line = next(inputfile)
250 virts = int(line.split()[-1])
251 self.nmo = occs + virts
252 self.homos = numpy.array([occs-1], "i")
254 self.unrestrictedflag = False
256 if line[1:28] == "number of occupied orbitals":
257 self.homos = numpy.array([float(line.strip().split()[-1])-1], "i")
259 if line[2:27] == "number of basis functions":
260 nbasis = int(line.strip().split()[-1])
261 self.set_attribute('nbasis', nbasis)
263 if line.find("number of alpha occupied orb") > 0:
264 # Get number of MOs for an unrestricted calc
266 aoccs = int(line.split()[-1])
267 line = next(inputfile)
268 avirts = int(line.split()[-1])
269 line = next(inputfile)
270 boccs = int(line.split()[-1])
271 line = next(inputfile)
272 bvirt = int(line.split()[-1])
274 self.nmo = aoccs + avirts
275 self.homos = numpy.array([aoccs-1, boccs-1], "i")
276 self.unrestrictedflag = True
278 if line[0:4] == "etot":
279 # Get SCF convergence information
280 if not hasattr(self, "scfvalues"):
281 self.scfvalues = []
282 self.scftargets = [[5E-5, 5E-6]]
283 values = []
284 while line[0:4] == "etot":
285 # Jaguar 4.2
286 # etot 1 N N 0 N -382.08751886450 2.3E-03 1.4E-01
287 # etot 2 Y Y 0 N -382.27486023153 1.9E-01 1.4E-03 5.7E-02
288 # Jaguar 6.5
289 # etot 1 N N 0 N -382.08751881733 2.3E-03 1.4E-01
290 # etot 2 Y Y 0 N -382.27486018708 1.9E-01 1.4E-03 5.7E-02
291 temp = line.split()[7:]
292 if len(temp) == 3:
293 denergy = float(temp[0])
294 else:
295 denergy = 0 # Should really be greater than target value
296 # or should we just ignore the values in this line
297 ddensity = float(temp[-2])
298 maxdiiserr = float(temp[-1])
299 if not self.geoopt:
300 values.append([denergy, ddensity])
301 else:
302 values.append([ddensity])
303 try:
304 line = next(inputfile)
305 except StopIteration:
306 self.logger.warning('File terminated before end of last SCF! Last error: {}'.format(maxdiiserr))
307 break
308 self.scfvalues.append(values)
310 # MO energies and symmetries.
311 # Jaguar 7.0: provides energies and symmetries for both
312 # restricted and unrestricted calculations, like this:
313 # Alpha Orbital energies/symmetry label:
314 # -10.25358 Bu -10.25353 Ag -10.21931 Bu -10.21927 Ag
315 # -10.21792 Bu -10.21782 Ag -10.21773 Bu -10.21772 Ag
316 # ...
317 # Jaguar 6.5: prints both only for restricted calculations,
318 # so for unrestricted calculations the output it looks like this:
319 # Alpha Orbital energies:
320 # -10.25358 -10.25353 -10.21931 -10.21927 -10.21792 -10.21782
321 # -10.21773 -10.21772 -10.21537 -10.21537 -1.02078 -0.96193
322 # ...
323 # Presence of 'Orbital energies' is enough to catch all versions.
324 if "Orbital energies" in line:
326 # Parsing results is identical for restricted/unrestricted
327 # calculations, just assert later that alpha/beta order is OK.
328 spin = int(line[2:6] == "Beta")
330 # Check if symmetries are printed also.
331 issyms = "symmetry label" in line
333 if not hasattr(self, "moenergies"):
334 self.moenergies = []
335 if issyms and not hasattr(self, "mosyms"):
336 self.mosyms = []
338 # Grow moeneriges/mosyms and make sure they are empty when
339 # parsed multiple times - currently cclib returns only
340 # the final output (ex. in a geomtry optimization).
341 if len(self.moenergies) < spin+1:
342 self.moenergies.append([])
343 self.moenergies[spin] = []
344 if issyms:
345 if len(self.mosyms) < spin+1:
346 self.mosyms.append([])
347 self.mosyms[spin] = []
349 line = next(inputfile).split()
350 while len(line) > 0:
351 if issyms:
352 energies = [float(line[2*i]) for i in range(len(line)//2)]
353 syms = [line[2*i+1] for i in range(len(line)//2)]
354 else:
355 energies = [float(e) for e in line]
356 energies = [utils.convertor(e, "hartree", "eV") for e in energies]
357 self.moenergies[spin].extend(energies)
358 if issyms:
359 syms = [self.normalisesym(s) for s in syms]
360 self.mosyms[spin].extend(syms)
361 line = next(inputfile).split()
363 line = next(inputfile)
365 # The second trigger string is in the version 8.3 unit test and the first one was
366 # encountered in version 6.x and is followed by a bit different format. In particular,
367 # the line with occupations is missing in each block. Here is a fragment of this block
368 # from version 8.3:
369 #
370 # *****************************************
371 #
372 # occupied + virtual orbitals: final wave function
373 #
374 # *****************************************
375 #
376 #
377 # 1 2 3 4 5
378 # eigenvalues- -11.04064 -11.04058 -11.03196 -11.03196 -11.02881
379 # occupations- 2.00000 2.00000 2.00000 2.00000 2.00000
380 # 1 C1 S 0.70148 0.70154 -0.00958 -0.00991 0.00401
381 # 2 C1 S 0.02527 0.02518 0.00380 0.00374 0.00371
382 # ...
383 #
384 if line.find("Occupied + virtual Orbitals- final wvfn") > 0 or \
385 line.find("occupied + virtual orbitals: final wave function") > 0:
387 self.skip_lines(inputfile, ['b', 's', 'b', 'b'])
389 if not hasattr(self, "mocoeffs"):
390 self.mocoeffs = []
392 aonames = []
393 lastatom = "X"
395 readatombasis = False
396 if not hasattr(self, "atombasis"):
397 self.atombasis = []
398 for i in range(self.natom):
399 self.atombasis.append([])
400 readatombasis = True
402 offset = 0
404 spin = 1 + int(self.unrestrictedflag)
405 for s in range(spin):
406 mocoeffs = numpy.zeros((len(self.moenergies[s]), self.nbasis), "d")
408 if s == 1: # beta case
409 self.skip_lines(inputfile, ['s', 'b', 'title', 'b', 's', 'b', 'b'])
411 for k in range(0, len(self.moenergies[s]), 5):
412 self.updateprogress(inputfile, "Coefficients")
414 # All known version have a line with indices followed by the eigenvalues.
415 self.skip_lines(inputfile, ['numbers', 'eigens'])
417 # Newer version also have a line with occupation numbers here.
418 line = next(inputfile)
419 if "occupations-" in line:
420 line = next(inputfile)
422 for i in range(self.nbasis):
424 info = line.split()
426 # Fill atombasis only first time around.
427 if readatombasis and k == 0:
428 orbno = int(info[0])
429 atom = info[1]
430 if atom[1].isalpha():
431 atomno = int(atom[2:])
432 else:
433 atomno = int(atom[1:])
434 self.atombasis[atomno-1].append(orbno-1)
436 if not hasattr(self, "aonames"):
437 if lastatom != info[1]:
438 scount = 1
439 pcount = 3
440 dcount = 6 # six d orbitals in Jaguar
442 if info[2] == 'S':
443 aonames.append("%s_%i%s" % (info[1], scount, info[2]))
444 scount += 1
446 if info[2] == 'X' or info[2] == 'Y' or info[2] == 'Z':
447 aonames.append("%s_%iP%s" % (info[1], pcount / 3, info[2]))
448 pcount += 1
450 if info[2] == 'XX' or info[2] == 'YY' or info[2] == 'ZZ' or \
451 info[2] == 'XY' or info[2] == 'XZ' or info[2] == 'YZ':
453 aonames.append("%s_%iD%s" % (info[1], dcount / 6, info[2]))
454 dcount += 1
456 lastatom = info[1]
458 for j in range(len(info[3:])):
459 mocoeffs[j+k, i] = float(info[3+j])
461 line = next(inputfile)
463 if not hasattr(self, "aonames"):
464 self.aonames = aonames
466 offset += 5
467 self.mocoeffs.append(mocoeffs)
469 # Atomic charges from Mulliken population analysis:
470 #
471 # Atom C1 C2 C3 C4 C5
472 # Charge 0.00177 -0.06075 -0.05956 0.00177 -0.06075
473 #
474 # Atom H6 H7 H8 C9 C10
475 # ...
476 if line.strip() == "Atomic charges from Mulliken population analysis:":
478 if not hasattr(self, 'atomcharges'):
479 self.atomcharges = {}
481 charges = []
482 self.skip_line(inputfile, "blank")
483 line = next(inputfile)
484 while "sum of atomic charges" not in line:
485 assert line.split()[0] == "Atom"
486 line = next(inputfile)
487 assert line.split()[0] == "Charge"
488 charges.extend([float(c) for c in line.split()[1:]])
489 self.skip_line(inputfile, "blank")
490 line = next(inputfile)
492 self.atomcharges['mulliken'] = charges
494 if (line[2:6] == "olap") or (line.strip() == "overlap matrix:"):
496 if line[6] == "-":
497 return
498 # This was continue (in loop) before parser refactoring.
499 # continue # avoid "olap-dev"
500 self.aooverlaps = numpy.zeros((self.nbasis, self.nbasis), "d")
502 for i in range(0, self.nbasis, 5):
503 self.updateprogress(inputfile, "Overlap")
505 self.skip_lines(inputfile, ['b', 'header'])
507 for j in range(i, self.nbasis):
508 temp = list(map(float, next(inputfile).split()[1:]))
509 self.aooverlaps[j, i:(i+len(temp))] = temp
510 self.aooverlaps[i:(i+len(temp)), j] = temp
512 if line[2:24] == "start of program geopt":
513 if not self.geoopt:
514 # Need to keep only the RMS density change info
515 # if this is a geooptz
516 self.scftargets = [[self.scftargets[0][0]]]
517 if hasattr(self, "scfvalues"):
518 self.scfvalues[0] = [[x[0]] for x in self.scfvalues[0]]
519 self.geoopt = True
520 else:
521 self.scftargets.append([5E-5])
523 # Get Geometry Opt convergence information
524 #
525 # geometry optimization step 7
526 # energy: -382.30219111487 hartrees
527 # [ turning on trust-radius adjustment ]
528 # ** restarting optimization from step 6 **
529 #
530 #
531 # Level shifts adjusted to satisfy step-size constraints
532 # Step size: 0.0360704
533 # Cos(theta): 0.8789215
534 # Final level shift: -8.6176299E-02
535 #
536 # energy change: 2.5819E-04 . ( 5.0000E-05 )
537 # gradient maximum: 5.0947E-03 . ( 4.5000E-04 )
538 # gradient rms: 1.2996E-03 . ( 3.0000E-04 )
539 # displacement maximum: 1.3954E-02 . ( 1.8000E-03 )
540 # displacement rms: 4.6567E-03 . ( 1.2000E-03 )
541 #
542 if line[2:28] == "geometry optimization step":
544 if not hasattr(self, "geovalues"):
545 self.geovalues = []
546 self.geotargets = numpy.zeros(5, "d")
548 gopt_step = int(line.split()[-1])
550 energy = next(inputfile)
551 blank = next(inputfile)
553 # A quick hack for messages that show up right after the energy
554 # at this point, which include:
555 # ** restarting optimization from step 2 **
556 # [ turning on trust-radius adjustment ]
557 # as found in regression file ptnh3_2_H2O_2_2plus.out and other logfiles.
558 restarting_from_1 = False
559 while blank.strip():
560 if blank.strip() == "** restarting optimization from step 1 **":
561 restarting_from_1 = True
562 blank = next(inputfile)
564 # One or more blank lines, depending on content.
565 line = next(inputfile)
566 while not line.strip():
567 line = next(inputfile)
569 # Note that the level shift message is followed by a blank, too.
570 if "Level shifts adjusted" in line:
571 while line.strip():
572 line = next(inputfile)
573 line = next(inputfile)
575 # The first optimization step does not produce an energy change, and
576 # ther is also no energy change when the optimization is restarted
577 # from step 1 (since step 1 had no change).
578 values = []
579 target_index = 0
580 if (gopt_step == 1) or restarting_from_1:
581 values.append(0.0)
582 target_index = 1
583 while line.strip():
584 if len(line) > 40 and line[41] == "(":
585 # A new geo convergence value
586 values.append(float(line[26:37]))
587 self.geotargets[target_index] = float(line[43:54])
588 target_index += 1
589 line = next(inputfile)
590 self.geovalues.append(values)
592 # IR output looks like this:
593 # frequencies 72.45 113.25 176.88 183.76 267.60 312.06
594 # symmetries Au Bg Au Bu Ag Bg
595 # intensities 0.07 0.00 0.28 0.52 0.00 0.00
596 # reduc. mass 1.90 0.74 1.06 1.42 1.19 0.85
597 # force const 0.01 0.01 0.02 0.03 0.05 0.05
598 # C1 X 0.00000 0.00000 0.00000 -0.05707 -0.06716 0.00000
599 # C1 Y 0.00000 0.00000 0.00000 0.00909 -0.02529 0.00000
600 # C1 Z 0.04792 -0.06032 -0.01192 0.00000 0.00000 0.11613
601 # C2 X 0.00000 0.00000 0.00000 -0.06094 -0.04635 0.00000
602 # ... etc. ...
603 # This is a complete ouput, some files will not have intensities,
604 # and older Jaguar versions sometimes skip the symmetries.
605 if line[2:23] == "start of program freq":
607 self.skip_line(inputfile, 'blank')
609 # Version 8.3 has two blank lines here, earlier versions just one.
610 line = next(inputfile)
611 if not line.strip():
612 line = next(inputfile)
614 self.vibfreqs = []
615 self.vibdisps = []
616 self.vibrmasses = []
617 forceconstants = False
618 intensities = False
619 while line.strip():
620 if "force const" in line:
621 forceconstants = True
622 if "intensities" in line:
623 intensities = True
624 line = next(inputfile)
626 # In older version, the last block had an extra blank line after it,
627 # which could be caught. This is not true in newer version (including 8.3),
628 # but in general it would be better to bound this loop more strictly.
629 freqs = next(inputfile)
630 while freqs.strip() and not "imaginary frequencies" in freqs:
632 # Number of modes (columns printed in this block).
633 nmodes = len(freqs.split())-1
635 # Append the frequencies.
636 self.vibfreqs.extend(list(map(float, freqs.split()[1:])))
637 line = next(inputfile).split()
639 # May skip symmetries (older Jaguar versions).
640 if line[0] == "symmetries":
641 if not hasattr(self, "vibsyms"):
642 self.vibsyms = []
643 self.vibsyms.extend(list(map(self.normalisesym, line[1:])))
644 line = next(inputfile).split()
645 if intensities:
646 if not hasattr(self, "vibirs"):
647 self.vibirs = []
648 self.vibirs.extend(list(map(float, line[1:])))
649 line = next(inputfile).split()
650 self.vibrmasses.extend(list(map(float, line[2:])))
651 if forceconstants:
652 line = next(inputfile).split()
653 if not hasattr(self, "vibfconsts"):
654 self.vibfconsts = []
655 self.vibfconsts.extend(list(map(float, line[2:])))
657 # Start parsing the displacements.
658 # Variable 'q' holds up to 7 lists of triplets.
659 q = [[] for i in range(7)]
660 for n in range(self.natom):
661 # Variable 'p' holds up to 7 triplets.
662 p = [[] for i in range(7)]
663 for i in range(3):
664 line = next(inputfile)
665 disps = [float(disp) for disp in line.split()[2:]]
666 for j in range(nmodes):
667 p[j].append(disps[j])
668 for i in range(nmodes):
669 q[i].append(p[i])
671 self.vibdisps.extend(q[:nmodes])
673 self.skip_line(inputfile, 'blank')
674 freqs = next(inputfile)
676 # Convert new data to arrays.
677 self.vibfreqs = numpy.array(self.vibfreqs, "d")
678 self.vibdisps = numpy.array(self.vibdisps, "d")
679 self.vibrmasses = numpy.array(self.vibrmasses, "d")
680 if hasattr(self, "vibirs"):
681 self.vibirs = numpy.array(self.vibirs, "d")
682 if hasattr(self, "vibfconsts"):
683 self.vibfconsts = numpy.array(self.vibfconsts, "d")
685 # Parse excited state output (for CIS calculations).
686 # Jaguar calculates only singlet states.
687 if line[2:15] == "Excited State":
688 if not hasattr(self, "etenergies"):
689 self.etenergies = []
690 if not hasattr(self, "etoscs"):
691 self.etoscs = []
692 if not hasattr(self, "etsecs"):
693 self.etsecs = []
694 self.etsyms = []
695 etenergy = float(line.split()[3])
696 etenergy = utils.convertor(etenergy, "eV", "wavenumber")
697 self.etenergies.append(etenergy)
699 self.skip_lines(inputfile, ['line', 'line', 'line', 'line'])
701 line = next(inputfile)
702 self.etsecs.append([])
703 # Jaguar calculates only singlet states.
704 self.etsyms.append('Singlet-A')
705 while line.strip() != "":
706 fromMO = int(line.split()[0])-1
707 toMO = int(line.split()[2])-1
708 coeff = float(line.split()[-1])
709 self.etsecs[-1].append([(fromMO, 0), (toMO, 0), coeff])
710 line = next(inputfile)
711 # Skip 3 lines
712 for i in range(4):
713 line = next(inputfile)
714 strength = float(line.split()[-1])
715 self.etoscs.append(strength)
717 if line[:20] == ' Total elapsed time:' \
718 or line[:18] == ' Total cpu seconds':
719 self.metadata['success'] = True