Example #1
0
    def V(C_rad=None, calculated=[], get_calculated=False):
        if get_calculated:
            return calculated
        elif C_rad is not None:
            zmolecule = get_zm_from_C(C_rad)

            result = calculate(molecule=zmolecule, forces=True,
                               el_calc_input=el_calc_input, **kwargs)
            energy = convertor(result.scfenergies[0], 'eV', 'hartree')
            grad_energy_X = result.grads[0] / convertor(1, 'bohr', 'Angstrom')

            grad_X = zmolecule.get_grad_cartesian(
                as_function=False, drop_auto_dummies=True)
            grad_energy_C = np.sum(
                grad_energy_X.T[:, :, None, None] * grad_X, axis=(0, 1))

            for i in range(min(3, grad_energy_C.shape[0])):
                grad_energy_C[i, i:] = 0

            zmolecule.metadata['energy'] = energy
            zmolecule.metadata['grad_energy'] = grad_energy_C
            calculated.append({'energy': energy, 'grad_energy': grad_energy_C,
                               'zmolecule': zmolecule})
            with open(md_out, 'a') as f:
                f.write(_get_table_row(calculated, grad_energy_X))

            return energy, grad_energy_C.flatten()
        else:
            raise ValueError
Example #2
0
    def _calculate_dipole(self, charges, coords, origin):
        """Calculate the dipole moment from the given atomic charges
        and their coordinates with respect to the origin.
        """
        transl_coords_au = convertor(coords - origin, 'Angstrom', 'bohr')
        dipole = numpy.dot(charges, transl_coords_au)

        return convertor(dipole, 'ebohr', 'Debye')
Example #3
0
    def _calculate_dipole(self, charges, coords, origin):
        """Calculate the dipole moment from the given atomic charges
        and their coordinates with respect to the origin.
        """
        transl_coords_au = convertor(coords - origin, 'Angstrom', 'bohr')
        dipole = numpy.dot(charges, transl_coords_au)

        return convertor(dipole, 'ebohr', 'Debye')
Example #4
0
def test_converged_structures():
    """Tests that non converged coordinates in Gaussian log files are discarded"""
    structure = get_fun('MPR.psf')
    scan = qmdb.parse_gauss(get_fun('MPR.scan1.pos.log'), structure)
    log = Gaussian(get_fun('MPR.scan1.pos.log'))
    data = log.parse()
    assert_array_equal(data.atomcoords.shape, scan.xyz.shape)
    converted = convertor(data.scfenergies, "eV", "kJmol-1") - \
                min(convertor(data.scfenergies[:len(data.atomcoords)], "eV", "kJmol-1"))
    assert_array_equal(converted[:47], scan.qm_energy)
Example #5
0
def test_converged_structures():
    """Tests that non converged coordinates in Gaussian log files are discarded"""
    structure = get_fn('MPR.psf')
    scan = qmdb.parse_gauss(get_fn('MPR.scan1.pos.log'), structure)
    log = Gaussian(get_fn('MPR.scan1.pos.log'))
    data = log.parse()
    assert_array_equal(data.atomcoords.shape, scan.xyz.shape)
    converted = convertor(data.scfenergies, "eV", "kJmol-1") - \
                min(convertor(data.scfenergies[:len(data.atomcoords)], "eV", "kJmol-1"))
    assert_array_equal(converted[:47], scan.qm_energy)
Example #6
0
def makepyscf_mos(ccdata, mol):
    """
    Returns pyscf formatted MO properties from a cclib object.
    Parameters
    ---
    ccdata: cclib object
        cclib object from parsed output
    mol: pyscf Molecule object
       molecule object that must contain the mol.basis attribute

    Returns
    ----
    mo_coeff : n_spin x nmo x nao ndarray
        molecular coeffcients, unnormalized according to pyscf standards
    mo_occ : array
        molecular orbital occupation
    mo_syms : array
       molecular orbital symmetry labels
    mo_energies: array
        molecular orbital energies in units of Hartree
    """
    inputattrs = ccdata.__dict__
    if "mocoeffs" in inputattrs:
        mol.build()
        s = mol.intor('int1e_ovlp')
        if np.shape(ccdata.mocoeffs)[0] == 1:
            mo_coeffs = np.einsum('i,ij->ij', np.sqrt(1 / s.diagonal()),
                                  ccdata.mocoeffs[0].T)
            mo_occ = np.zeros(ccdata.nmo)
            mo_occ[:ccdata.homos[0] + 1] = 2
            mo_energies = convertor(np.array(ccdata.moenergies), "eV",
                                    "hartree")
            if hasattr(ccdata, 'mosyms'):
                mo_syms = ccdata.mosyms
            else:
                mo_syms = np.full_like(ccdata.moenergies, 'A', dtype=str)

        elif np.shape(ccdata.mocoeffs)[0] == 2:
            mo_coeff_a = np.einsum('i,ij->ij', np.sqrt(1 / s.diagonal()),
                                   ccdata.mocoeffs[0].T)
            mo_coeff_b = np.einsum('i,ij->ij', np.sqrt(1 / s.diagonal()),
                                   ccdata.mocoeffs[1].T)
            mo_occ = np.zeros((2, ccdata.nmo))
            mo_occ[0, :ccdata.homos[0] + 1] = 1
            mo_occ[1, :ccdata.homos[1] + 1] = 1
            mo_coeffs = np.array([mo_coeff_a, mo_coeff_b])
            mo_energies = convertor(np.array(ccdata.moenergies), "eV",
                                    "hartree")
            if hasattr(ccdata, 'mosyms'):
                mo_syms = ccdata.mosyms
            else:
                mo_syms = np.full_like(ccdata.moenergies, 'A', dtype=str)
    return mo_coeffs, mo_occ, mo_syms, mo_energies
Example #7
0
 def _mo_energies(self):
     """Section: Molecular Orbital Energies."""
     mo_energies = []
     alpha_elctrons = self._no_alpha_electrons()
     beta_electrons = self._no_beta_electrons()
     for mo_energy in self.ccdata.moenergies[0][:alpha_elctrons]:
         mo_energies.append(WFX_FIELD_FMT % (
             utils.convertor(mo_energy, 'eV', 'hartree')))
     if self.ccdata.mult > 1:
         for mo_energy in self.ccdata.moenergies[1][:beta_electrons]:
             mo_energies.append(WFX_FIELD_FMT % (
                 utils.convertor(mo_energy, 'eV', 'hartree')))
     return mo_energies
Example #8
0
def wavefunction(coords, mocoeffs, gbasis, volume):
    """Calculate the magnitude of the wavefunction at every point in a volume.
    
    Attributes:
        coords -- the coordinates of the atoms
        mocoeffs -- mocoeffs for one eigenvalue
        gbasis -- gbasis from a parser object
        volume -- a template Volume object (will not be altered)
    """
    bfs = getbfs(coords, gbasis)
    
    wavefn = copy.copy(volume)
    wavefn.data = numpy.zeros( wavefn.data.shape, "d")

    conversion = convertor(1,"bohr","Angstrom")
    x = numpy.arange(wavefn.origin[0], wavefn.topcorner[0]+wavefn.spacing[0], wavefn.spacing[0]) / conversion
    y = numpy.arange(wavefn.origin[1], wavefn.topcorner[1]+wavefn.spacing[1], wavefn.spacing[1]) / conversion
    z = numpy.arange(wavefn.origin[2], wavefn.topcorner[2]+wavefn.spacing[2], wavefn.spacing[2]) / conversion

    for bs in range(len(bfs)):
        data = numpy.zeros( wavefn.data.shape, "d")
        for i,xval in enumerate(x):
            for j,yval in enumerate(y):
                for k,zval in enumerate(z):
                    data[i, j, k] = bfs[bs].amp(xval,yval,zval)
        numpy.multiply(data, mocoeffs[bs], data)
        numpy.add(wavefn.data, data, wavefn.data)
    
    return wavefn
Example #9
0
def test_ECCD():
    filename = os.path.join("exampleCircularDichroism", "OPT_td.out")
    clearoutput(filename)
    data = parse(filename)

    Sigma = 2.402185  # Because it corresponds to a FWHM of 2.0
    ET(None, sys.stdout, data, filename, 100, 300, 10000, Sigma, False, False)

    spectrum = [
        list(map(float,
                 x.split()[:3]))
        for x in open(os.path.join(gaussdir(filename), "CDSpectrum.txt"))
        if not x[0] == "E"
    ]

    # Assert Peak Max
    maxval = max([x[2] for x in spectrum])
    assert abs(maxval - 0.143) < 0.001

    # Assert Sigma...(full width at 1/e height)
    one_over_e_max = maxval / math.e
    for x, y, z in spectrum:
        if abs(z - one_over_e_max) < 0.00002:
            one_over_e_x = x
        if z == maxval:
            max_x = x
    fullwidth = (one_over_e_x - max_x) * 2.
    width_in_eV = fullwidth / convertor(1., "eV", "cm-1")

    assert abs(width_in_eV - Sigma) < 0.01
Example #10
0
    def getbfs(ccdata):
        bfs = []
        # `atom` is instance of pyquante2.geo.atom.atom class.
        for i, atom in enumerate(ccdata.atomcoords[-1]):
            # `basis` is basis coefficients stored in ccData.
            basis = ccdata.gbasis[i]
            for sym, primitives in basis:
                # `sym` is S, P, D, F and is used as key here.
                for power in sym2powerlist[sym]:
                    exponentlist = []
                    coefficientlist = []

                    for exponents, coefficients in primitives:
                        exponentlist.append(exponents)
                        coefficientlist.append(coefficients)

                    basisfunction = cgbf(
                        convertor(atom, "Angstrom", "bohr"),
                        powers=power,
                        exps=exponentlist,
                        coefs=coefficientlist,
                    )
                    basisfunction.normalize()
                    bfs.append(basisfunction)

        return bfs
Example #11
0
 def writeascube(self, filename):
     # Remember that the units are bohr, not Angstroms
     convert = lambda x : convertor(x, "Angstrom", "bohr")
     ans = []
     ans.append("Cube file generated by cclib")
     ans.append("")
     format = "%4d%12.6f%12.6f%12.6f"
     origin = [convert(x) for x in self.origin]
     ans.append(format % (0, origin[0], origin[1], origin[2]))
     ans.append(format % (self.data.shape[0], convert(self.spacing[0]), 0.0, 0.0))
     ans.append(format % (self.data.shape[1], 0.0, convert(self.spacing[1]), 0.0))
     ans.append(format % (self.data.shape[2], 0.0, 0.0, convert(self.spacing[2])))
     line = []
     for i in range(self.data.shape[0]):
         for j in range(self.data.shape[1]):
             for k in range(self.data.shape[2]):
                 line.append(scinotation(self.data[i][j][k]))
                 if len(line)==6:
                     ans.append(" ".join(line))
                     line = []
             if line:
                 ans.append(" ".join(line))
                 line = []
     outputfile = open(filename, "w")
     outputfile.write("\n".join(ans))
     outputfile.close()
Example #12
0
    def __init__(self,
                 output_path,
                 cluster_label,
                 partition_label,
                 md_temp,
                 grad_e_unit,
                 grad_r_unit,
                 md_iter=0,
                 e_unit='kcal/mol',
                 r_unit='Angstrom',
                 theory='unknown'):

        self.output_path = output_path
        self.output_name = self.output_path.split('/')[-1].split('.')[0]
        self.cluster_label = cluster_label
        self.partition_label = partition_label
        self.partition_size = int(len(self.partition_label))
        self.md_temp = md_temp
        self.md_iter = md_iter
        self.e_unit = e_unit
        self.r_unit = r_unit
        self.theory = theory

        self.cclib_data = ccread(self.output_path)
        self._get_gdml_data()
        self.E = convertor(self.E, 'eV', self.e_unit)
        self.G = convert_forces(self.G, grad_e_unit, grad_r_unit, self.e_unit,
                                self.r_unit)
        self.F = np.negative(self.G)
Example #13
0
    def calculate_H(self):
        """ Calculate H_A(r_A)
            
            This is a quantity introduced in DDEC6 as a constraint preventing the tails from being
            too contracted.
            [STEP 4-7]
        """
        self._h = []
        for atomi in range(len(self._g)):
            # First set H_est as G_A
            self._h.append(self._g[atomi])

            # Determine eta_upper using equation 86 in doi: 10.1039/c6ra04656h
            # and apply upper limit using equation 91.
            temp = (
                1 - (self.tau[atomi])**2 + self.convergence_level
            )  # convergence_level is added to avoid divide-by-zero in next line for highly polar molecules.
            eta = 2.5 * convertor(1, "Angstrom", "bohr") / temp
            exp_applied = self._h[atomi][:-1] * numpy.exp(
                -1 * eta[1:] * numpy.diff(self.radial_grid_r[atomi]))
            for radiusi in range(1, len(self._h[atomi])):
                self._h[atomi][radiusi] = max(self._h[atomi][radiusi],
                                              exp_applied[radiusi - 1])

            # Normalize using equation 92 in doi: 10.1039/c6ra04656h.
            self._h[atomi] = (
                self._h[atomi] *
                self._integrate_from_radial([self._g[atomi]], [atomi]) /
                self._integrate_from_radial([self._h[atomi]], [atomi]))
Example #14
0
    def get_optimisation_E(self, units='eV'):

        energies = self._opt_data.read('scfenergies')

        if not units == 'eV':
            energies = convertor(energies, 'eV', units)
        return energies[-1]
Example #15
0
 def test_makepyscf_mos(self):
     pyscfmol = cclib2pyscf.makepyscf(self.data)
     mo_coeff, mo_occ, mo_syms, mo_energies = cclib2pyscf.makepyscf_mos(self.data,pyscfmol)
     assert np.allclose(mo_energies,convertor(np.array(self.data.moenergies),"eV","hartree"))
     # check first MO coefficient
     assert np.allclose(mo_coeff[0][0], self.data.mocoeffs[0][0][0])
     # check a random middle MO coefficient
     assert np.allclose(mo_coeff[0][10],self.data.mocoeffs[0][10][0])
     # test unrestricted code.
     pyscfmol = cclib2pyscf.makepyscf(self.udata)
     mo_coeff, mo_occ, mo_syms, mo_energies = cclib2pyscf.makepyscf_mos(self.udata,pyscfmol)
     assert np.allclose(mo_energies,convertor(np.array(self.udata.moenergies),"eV","hartree"))
     # check first MO coefficient
     assert np.allclose(mo_coeff[0][0][0], self.udata.mocoeffs[0][0][0])
     # check a random middle MO coefficient
     assert np.allclose(mo_coeff[0][0][10],self.udata.mocoeffs[0][10][0])
Example #16
0
def wavefunction(coords, mocoeffs, gbasis, volume):
    """Calculate the magnitude of the wavefunction at every point in a volume.
    
    Attributes:
        coords -- the coordinates of the atoms
        mocoeffs -- mocoeffs for one eigenvalue
        gbasis -- gbasis from a parser object
        volume -- a template Volume object (will not be altered)
    """
    bfs = getbfs(coords, gbasis)
    
    wavefn = copy.copy(volume)
    wavefn.data = numpy.zeros( wavefn.data.shape, "d")

    conversion = convertor(1,"bohr","Angstrom")
    x = numpy.arange(wavefn.origin[0], wavefn.topcorner[0]+wavefn.spacing[0], wavefn.spacing[0]) / conversion
    y = numpy.arange(wavefn.origin[1], wavefn.topcorner[1]+wavefn.spacing[1], wavefn.spacing[1]) / conversion
    z = numpy.arange(wavefn.origin[2], wavefn.topcorner[2]+wavefn.spacing[2], wavefn.spacing[2]) / conversion

    for bs in range(len(bfs)):
        data = numpy.zeros( wavefn.data.shape, "d")
        for i,xval in enumerate(x):
            for j,yval in enumerate(y):
                for k,zval in enumerate(z):
                    data[i, j, k] = bfs[bs].amp(xval,yval,zval)
        numpy.multiply(data, mocoeffs[bs], data)
        numpy.add(wavefn.data, data, wavefn.data)
    
    return wavefn
Example #17
0
 def writeascube(self, filename):
     # Remember that the units are bohr, not Angstroms
     convert = lambda x : convertor(x, "Angstrom", "bohr")
     ans = []
     ans.append("Cube file generated by cclib")
     ans.append("")
     format = "%4d%12.6f%12.6f%12.6f"
     origin = [convert(x) for x in self.origin]
     ans.append(format % (0, origin[0], origin[1], origin[2]))
     ans.append(format % (self.data.shape[0], convert(self.spacing[0]), 0.0, 0.0))
     ans.append(format % (self.data.shape[1], 0.0, convert(self.spacing[1]), 0.0))
     ans.append(format % (self.data.shape[2], 0.0, 0.0, convert(self.spacing[2])))
     line = []
     for i in range(self.data.shape[0]):
         for j in range(self.data.shape[1]):
             for k in range(self.data.shape[2]):
                 line.append(scinotation(self.data[i][j][k]))
                 if len(line)==6:
                     ans.append(" ".join(line))
                     line = []
             if line:
                 ans.append(" ".join(line))
                 line = []
     outputfile = open(filename, "w")
     outputfile.write("\n".join(ans))
     outputfile.close()
Example #18
0
 def integrate_square(self):
     boxvol = (
         self.spacing[0]
         * self.spacing[1]
         * self.spacing[2]
         * convertor(1, "Angstrom", "bohr") ** 3
     )
     return sum(self.data.ravel() ** 2) * boxvol
Example #19
0
 def _nuclear_coords(self):
     """Section: Nuclear Cartesian Coordinates.
     Nuclear coordinates in Bohr."""
     coord_template = WFX_FIELD_FMT * 3
     to_bohr = lambda x: utils.convertor(x, 'Angstrom', 'bohr')
     nuc_coords = [coord_template % tuple(to_bohr(coord))
                   for coord in self.ccdata.atomcoords[-1]]
     return nuc_coords
Example #20
0
    def integrate(self, weights=None):
        weights = numpy.ones_like(self.data) if weights is None else weights
        assert (weights.shape == self.data.shape
                ), "Shape of weights do not match with shape of Volume data."

        boxvol = (self.spacing[0] * self.spacing[1] * self.spacing[2] *
                  convertor(1, "Angstrom", "bohr")**3)

        return numpy.sum(self.data * weights) * boxvol
Example #21
0
    def _calculate_quadrupole(self, charges, coords, origin):
        """Calculate the traceless quadrupole moment from the given
        atomic charges and their coordinates with respect to the origin.
        """
        transl_coords_au = convertor(coords - origin, 'Angstrom', 'bohr')

        delta = numpy.eye(3)
        Q = numpy.zeros([3, 3])
        for i in range(3):
            for j in range(3):
                for q, r in zip(charges, transl_coords_au):
                    Q[i,j] += 1/2 * q * (3 * r[i] * r[j] - \
                              numpy.linalg.norm(r)**2 * delta[i,j])

        triu_idxs = numpy.triu_indices_from(Q)
        raveled_idxs = numpy.ravel_multi_index(triu_idxs, Q.shape)
        quadrupole = numpy.take(Q.flatten(), raveled_idxs)

        return convertor(quadrupole, 'ebohr2', 'Buckingham')
Example #22
0
    def _calculate_quadrupole(self, charges, coords, origin):
        """Calculate the traceless quadrupole moment from the given
        atomic charges and their coordinates with respect to the origin.
        """
        transl_coords_au = convertor(coords - origin, 'Angstrom', 'bohr')

        delta = numpy.eye(3)
        Q = numpy.zeros([3, 3])
        for i in range(3):
            for j in range(3):
                for q, r in zip(charges, transl_coords_au):
                    Q[i,j] += 1/2 * q * (3 * r[i] * r[j] - \
                              numpy.linalg.norm(r)**2 * delta[i,j])

        triu_idxs = numpy.triu_indices_from(Q)
        raveled_idxs = numpy.ravel_multi_index(triu_idxs, Q.shape)
        quadrupole = numpy.take(Q.flatten(), raveled_idxs)

        return convertor(quadrupole, 'ebohr2', 'Buckingham')
Example #23
0
    def integrate_square(self, weights=None):
        weights = numpy.ones_like(self.data) if weights is None else weights

        boxvol = (
            self.spacing[0]
            * self.spacing[1]
            * self.spacing[2]
            * convertor(1, "Angstrom", "bohr") ** 3
        )
        return numpy.sum((self.data * weights) ** 2) * boxvol
def main():
    """The main routine!"""

    parser = argparse.ArgumentParser()

    parser.add_argument('compchemfilename', nargs='+')
    parser.add_argument('--scaling-energy-change', type=float, default=10.0)

    args = parser.parse_args()
    compchemfilenames = args.compchemfilename

    for compchemfilename in compchemfilenames:

        stub = os.path.splitext(compchemfilename)[0]

        job = ccopen(compchemfilename)
        data = job.parse()

        fig, ax = plt.subplots()

        if type(job) == cclib.parser.qchemparser.QChem:

            scfenergies = [
                utils.convertor(scfenergy, 'eV', 'hartree')
                for scfenergy in data.scfenergies
            ]
            gradients = [geovalue[0] for geovalue in data.geovalues]
            displacements = [geovalue[1] for geovalue in data.geovalues]
            energy_changes = [(geovalue[2] * args.scaling_energy_change)
                              for geovalue in data.geovalues]

            # If this isn't true, something funny happened during the
            # parsing, so fail out.
            assert len(scfenergies) == len(gradients)

            steps = range(1, len(scfenergies) + 1)

            # ax.plot(steps, scfenergies, label='SCF energy')
            ax.plot(steps, gradients, label='max gradient')
            ax.plot(steps, displacements, label='max displacement')
            ax.plot(steps, energy_changes, label='energy change')

            ax.set_title(stub)
            ax.set_xlabel('optimization step #')

        elif type(job) == cclib.parser.orcaparser.ORCA:

            pass

        else:
            pass

        ax.legend(loc='best', fancybox=True)

        fig.savefig(stub + '.pdf', bbox_inches='tight')
Example #25
0
    def convertR(self, R_units):
        """Convert coordinates and updates ``r_unit``.

        Parameters
        ----------
        R_units : :obj:`str`
            Desired units of coordinates. Options are ``'Angstrom'`` or
            ``'bohr'``.
        """
        self._R = convertor(self.R, self.r_unit, R_units)
        self.r_unit = R_units
Example #26
0
def qchem_get_cisd_energies(inputfile, energy_gs):
    state_energies = []
    line = ''
    while 'CIS(D) excitation energy' not in line:
        line = next(inputfile)
    while list(set(line.strip())) != ['-']:
        if 'CIS(D) excitation energy' in line:
            # Stupid Q-Chem!
            energy_es = energy_gs + convertor(float(line.split()[-2]), 'eV', 'hartree')
            state_energies.append(energy_es)
        line = next(inputfile)
    return state_energies
Example #27
0
 def _parse_mosyms_moenergies(self, inputfile, spinidx):
     """Parse molecular orbital symmetries and energies from the
     'Post-Iterations' section.
     """
     line = next(inputfile)
     while line.strip():
         for i in range(len(line.split()) // 2):
             self.mosyms[spinidx].append(line.split()[i*2][-2:])
             moenergy = utils.convertor(float(line.split()[i*2+1]), "hartree", "eV")
             self.moenergies[spinidx].append(moenergy)
         line = next(inputfile)
     return
Example #28
0
 def _parse_mosyms_moenergies(self, inputfile, spinidx):
     """Parse molecular orbital symmetries and energies from the
     'Post-Iterations' section.
     """
     line = next(inputfile)
     while line.strip():
         for i in range(len(line.split()) // 2):
             self.mosyms[spinidx].append(line.split()[i*2][-2:])
             moenergy = utils.convertor(float(line.split()[i*2+1]), "hartree", "eV")
             self.moenergies[spinidx].append(moenergy)
         line = next(inputfile)
     return
Example #29
0
    def test_nre(self):
        """Testing nuclear repulsion energy for one logfile where it is printed."""

        data, logfile = getdatafile(QChem, "basicQChem4.2", "water_mp4sdq.out")
        nuclear = Nuclear(data)
        nuclear.logger.setLevel(logging.ERROR)

        with open(logfile.filename) as f:
            output = f.read()
        line = re.search('Nuclear Repulsion Energy = .* hartrees', output).group()
        nre = float(line.split()[4])
        nre = utils.convertor(nre, 'Angstrom', 'bohr')
        self.assertAlmostEqual(nuclear.repulsion_energy(), nre, places=7)
Example #30
0
    def test_repulsion_energy(self):
        """Testing nuclear repulsion energy for one logfile where it is printed."""

        data, logfile = getdatafile(QChem, "basicQChem5.4", ["water_mp4sdq.out"])
        nuclear = Nuclear(data)
        nuclear.logger.setLevel(logging.ERROR)

        with open(logfile.filename) as f:
            output = f.read()
        line = re.search('Nuclear Repulsion Energy = .* hartrees', output).group()
        nre = float(line.split()[4])
        nre = utils.convertor(nre, 'Angstrom', 'bohr')
        self.assertAlmostEqual(nuclear.repulsion_energy(), nre, places=7)
Example #31
0
    def reshape_G(self):
        """ Calculate G_A(r_A) and reshape densities
        
            This is a quantity introduced in DDEC6 as a constraint preventing the tails from being
            too diffuse.
            [STEP 4-7]
        """
        self._candidates_bigPhi = []
        self._candidates_phi = []

        # Initial conditions are detailed in Figure S3 in doi: 10.1039/c6ra04656h
        phiAII = numpy.zeros_like(self.data.atomnos, dtype=float)
        bigphiAII = numpy.zeros_like(self.data.atomnos, dtype=float)
        self._g = []
        self._eta = []

        for atomi in range(self.data.natom):
            # G_A -- equation S102 in doi: 10.1039/c6ra04656h
            self._g.append(
                numpy.zeros_like(self.proatom_density[atomi], dtype=float))
            self._g[atomi] = self.rho_wavg[
                atomi] + bigphiAII[atomi] * numpy.sqrt(self.rho_wavg[atomi])
            # Exponential constraint (as expressed in equation S105)
            self._eta.append((1 - (self.tau[atomi])**2) * 1.75 *
                             convertor(1, "Angstrom", "bohr"))
            exp_applied = self._g[atomi][:-1] * numpy.exp(
                -1 * self._eta[atomi][1:] *
                numpy.diff(self.radial_grid_r[atomi]))
            for radiusi in range(1, len(self._g[atomi])):
                self._g[atomi][radiusi] = min(self._g[atomi][radiusi],
                                              exp_applied[radiusi - 1])
            # phi_A^II -- Equation S106 in doi: 10.1039/c6ra04656h
            phiAII[atomi] = self._integrate_from_radial(
                [self._g[atomi] - self.rho_wavg[atomi]], [atomi])

            self._candidates_bigPhi.append([bigphiAII[atomi]])
            self._candidates_phi.append([phiAII[atomi]])

            # Attempt to find the point where phiAI is zero iteratively
            # Refer to S101 in doi: 10.1039/c6ra04656h
            self._candidates_phi[atomi], self._candidates_bigPhi[
                atomi] = self._converge_phi(phiAII[atomi], 1, atomi)

            # Perform parabolic fit to find optimized phiAI
            # Refer to Figure S1 in doi: 10.1039/c6ra04656h
            bigphiAII[atomi] = self._parabolic_fit(self.rho_wavg[atomi], 2,
                                                   atomi)

            # Set final G_A value using chosen Phi
            self._g[atomi] = self._update_phiaii(self.rho_wavg[atomi],
                                                 bigphiAII[atomi], atomi)[1]
def main():
    """The main routine!"""

    parser = argparse.ArgumentParser()

    parser.add_argument('compchemfilename', nargs='+')
    parser.add_argument('--scaling-energy-change', type=float, default=10.0)

    args = parser.parse_args()
    compchemfilenames = args.compchemfilename

    for compchemfilename in compchemfilenames:

        stub = os.path.splitext(compchemfilename)[0]

        job = ccopen(compchemfilename)
        data = job.parse()

        fig, ax = plt.subplots()

        if type(job) == cclib.parser.qchemparser.QChem:

            scfenergies = [utils.convertor(scfenergy, 'eV', 'hartree') for scfenergy in data.scfenergies]
            gradients = [geovalue[0] for geovalue in data.geovalues]
            displacements = [geovalue[1] for geovalue in data.geovalues]
            energy_changes = [(geovalue[2] * args.scaling_energy_change) for geovalue in data.geovalues]

            # If this isn't true, something funny happened during the
            # parsing, so fail out.
            assert len(scfenergies) == len(gradients)

            steps = range(1, len(scfenergies) + 1)

            # ax.plot(steps, scfenergies, label='SCF energy')
            ax.plot(steps, gradients, label='max gradient')
            ax.plot(steps, displacements, label='max displacement')
            ax.plot(steps, energy_changes, label='energy change')

            ax.set_title(stub)
            ax.set_xlabel('optimization step #')

        elif type(job) == cclib.parser.orcaparser.ORCA:

            pass

        else:
            pass

        ax.legend(loc='best', fancybox=True)

        fig.savefig(stub + '.pdf', bbox_inches='tight')
Example #33
0
def getGrid(vol):
    """Helper function that returns (x, y, z), each of which are numpy array of the values that
       correspond to grid points.
       
    Input:
       vol -- Volume object (will not be altered)
       """
    conversion = convertor(1, "bohr", "Angstrom")
    gridendpt = vol.topcorner + 0.5 * vol.spacing
    x = numpy.arange(vol.origin[0], gridendpt[0], vol.spacing[0]) / conversion
    y = numpy.arange(vol.origin[1], gridendpt[1], vol.spacing[1]) / conversion
    z = numpy.arange(vol.origin[2], gridendpt[2], vol.spacing[2]) / conversion

    return (x, y, z)
Example #34
0
    def plot_optimisation_E(self, units='eV'):

        energies = self._opt_data.read('scfenergies')
        for data in reversed(self._prev_opt_data):
            energies = np.concatenate([data.read('scfenergies'), energies])

        if not units == 'eV':
            energies = convertor(energies, 'eV', units)

        f, ax = plt.subplots()
        ax.plot(energies)
        ax.set_ylabel('Energy ({0})'.format(units))
        ax.set_xlabel('Optimisation Step')
        ax.grid(True)

        return ax
Example #35
0
def read_energy_levels(input_file, units='eV'):
    """
    Determines the energy levels and homos from and output file
    :param input_file: input file to read
    :param units: units to return energies in
    """
    try:
        data = ccopen(input_file).parse()
        levels = np.array(data.moenergies)
        if units != 'eV':
            try:
                levels = convertor(levels, 'eV', units)
            except KeyError as e:
                raise KeyError(f'Cannot convert energy levels to {units}')
    except AttributeError as e:
        raise Exception('Cannot find appropriate data, has the SCF finished yet?')
    return levels, data.homos
Example #36
0
def main():
    """The main routine!"""

    parser = argparse.ArgumentParser()

    parser.add_argument('compchemfilename', nargs='+')

    args = parser.parse_args()
    compchemfilenames = args.compchemfilename

    for compchemfilename in compchemfilenames:

        stub = os.path.splitext(compchemfilename)[0]

        job = ccopen(compchemfilename)
        data = job.parse()

        fig, ax = plt.subplots()

        if type(job) == cclib.parser.qchemparser.QChem:

            scfenergies = [
                utils.convertor(scfenergy, 'eV', 'hartree')
                for scfenergy in data.scfenergies
            ]
            print(scfenergies)
            # scfenergies = [scfenergy for scfenergy in data.scfenergies]

            steps = range(1, len(scfenergies) + 1)

            ax.plot(steps, scfenergies, label='SCF energy')

            ax.set_title(stub)
            ax.set_xlabel('SCF step #')
            ax.set_xticks(steps)

        elif type(job) == cclib.parser.orcaparser.ORCA:

            pass

        else:
            pass

        ax.legend(loc='best', fancybox=True)

        fig.savefig(stub + '.pdf', bbox_inches='tight')
Example #37
0
    def _mo_from_ccdata(self):
        """Create [MO] section.

        Sym= symmetry_label_1
        Ene= mo_energy_1
        Spin= (Alpha|Beta)
        Occup= mo_occupation_number_1
        ao_number_1 mo_coefficient_1
        ...
        ao_number_n mo_coefficient_n
        ...
        """

        moenergies = self.ccdata.moenergies
        mocoeffs = self.ccdata.mocoeffs
        homos = self.ccdata.homos
        mult = self.ccdata.mult

        has_syms = False
        lines = []

        # Sym attribute is optional in [MO] section.
        if hasattr(self.ccdata, 'mosyms'):
            has_syms = True
            syms = self.ccdata.mosyms

        spin = 'Alpha'
        for i in range(mult):
            for j in range(len(moenergies[i])):
                if has_syms:
                    lines.append(' Sym= %s' % syms[i][j])
                moenergy = utils.convertor(moenergies[i][j], 'eV', 'hartree')
                lines.append(' Ene= {:10.4f}'.format(moenergy))
                lines.append(' Spin= %s' % spin)
                if j <= homos[i]:
                    lines.append(' Occup= {:10.6f}'.format(2.0 / mult))
                else:
                    lines.append(' Occup= {:10.6f}'.format(0.0))
                # Rearrange mocoeffs according to Molden's lexicographical order.
                mocoeffs[i][j] = self._rearrange_mocoeffs(mocoeffs[i][j])
                for k, mocoeff in enumerate(mocoeffs[i][j]):
                    lines.append('{:4d}  {:10.6f}'.format(k + 1, mocoeff))

            spin = 'Beta'

        return lines
Example #38
0
    def _mo_from_ccdata(self):
        """Create [MO] section.

        Sym= symmetry_label_1
        Ene= mo_energy_1
        Spin= (Alpha|Beta)
        Occup= mo_occupation_number_1
        ao_number_1 mo_coefficient_1
        ...
        ao_number_n mo_coefficient_n
        ...
        """

        moenergies = self.ccdata.moenergies
        mocoeffs = self.ccdata.mocoeffs
        homos = self.ccdata.homos
        mult = self.ccdata.mult

        has_syms = False
        lines = []

        # Sym attribute is optional in [MO] section.
        if hasattr(self.ccdata, 'mosyms'):
            has_syms = True
            syms = self.ccdata.mosyms

        spin = 'Alpha'
        for i in range(mult):
            for j in range(len(moenergies[i])):
                if has_syms:
                    lines.append(' Sym= %s' % syms[i][j])
                moenergy = utils.convertor(moenergies[i][j], 'eV', 'hartree')
                lines.append(' Ene= {:10.4f}'.format(moenergy))
                lines.append(' Spin= %s' % spin)
                if j <= homos[i]:
                    lines.append(' Occup= {:10.6f}'.format(2.0 / mult))
                else:
                    lines.append(' Occup= {:10.6f}'.format(0.0))
                # Rearrange mocoeffs according to Molden's lexicographical order.
                mocoeffs[i][j] = self._rearrange_mocoeffs(mocoeffs[i][j])
                for k, mocoeff in enumerate(mocoeffs[i][j]):
                    lines.append('{:4d}  {:10.6f}'.format(k + 1, mocoeff))

            spin = 'Beta'

        return lines
Example #39
0
def electrondensity(coords, mocoeffslist, gbasis, volume):
    """Calculate the magnitude of the electron density at every point in a volume.

    Attributes:
        coords -- the coordinates of the atoms
        mocoeffs -- mocoeffs for all of the occupied eigenvalues
        gbasis -- gbasis from a parser object
        volume -- a template Volume object (will not be altered)

    Note: mocoeffs is a list of NumPy arrays. The list will be of length 1
          for restricted calculations, and length 2 for unrestricted.
    """
    bfs = getbfs(coords, gbasis)

    density = copy.copy(volume)
    density.data = numpy.zeros(density.data.shape, "d")

    conversion = convertor(1, "bohr", "Angstrom")
    x = numpy.arange(density.origin[0], density.topcorner[0] +
                     density.spacing[0], density.spacing[0]) / conversion
    y = numpy.arange(density.origin[1], density.topcorner[1] +
                     density.spacing[1], density.spacing[1]) / conversion
    z = numpy.arange(density.origin[2], density.topcorner[2] +
                     density.spacing[2], density.spacing[2]) / conversion

    for mocoeffs in mocoeffslist:
        for mocoeff in mocoeffs:
            wavefn = numpy.zeros(density.data.shape, "d")
            for bs in range(len(bfs)):
                data = numpy.zeros(density.data.shape, "d")
                for i, xval in enumerate(x):
                    for j, yval in enumerate(y):
                        tmp = []
                        for zval in z:
                            tmp.append(bfs[bs].amp(xval, yval, zval))
                        data[i, j, :] = tmp
                data *= mocoeff[bs]
                wavefn += data
            density.data += wavefn**2

    # TODO ROHF
    if len(mocoeffslist) == 1:
        density.data *= 2.0

    return density
Example #40
0
 def _energy(self):
     """Section: Energy = T + Vne + Vee + Vnn.
     The total energy of the molecule.
     HF and KSDFT: SCF energy        (scfenergies),
     MP2         : MP2 total energy  (mpenergies),
     CCSD        : CCSD total energy (ccenergies).
     """
     energy = 0
     if hasattr(self.ccdata, 'ccenergies'):
         energy = self.ccdata.ccenergies[-1]
     elif hasattr(self.ccdata, 'mpenergies'):
         energy = self.ccdata.mpenergies[-1][-1]
     elif hasattr(self.ccdata, 'scfenergies'):
         energy = self.ccdata.scfenergies[-1]
     else:
         raise filewriter.MissingAttributeError(
             'scfenergies/mpenergies/ccenergies')
     return WFX_FIELD_FMT % (utils.convertor(energy, 'eV', 'hartree'))
Example #41
0
def electrondensity(coords, mocoeffslist, gbasis, volume):
    """Calculate the magnitude of the electron density at every point in a volume.

    Attributes:
        coords -- the coordinates of the atoms
        mocoeffs -- mocoeffs for all of the occupied eigenvalues
        gbasis -- gbasis from a parser object
        volume -- a template Volume object (will not be altered)

    Note: mocoeffs is a list of NumPy arrays. The list will be of length 1
          for restricted calculations, and length 2 for unrestricted.
    """
    bfs = getbfs(coords, gbasis)

    density = copy.copy(volume)
    density.data = numpy.zeros(density.data.shape, "d")

    conversion = convertor(1, "bohr", "Angstrom")
    x = numpy.arange(density.origin[0], density.topcorner[0] + density.spacing[0], density.spacing[0]) / conversion
    y = numpy.arange(density.origin[1], density.topcorner[1] + density.spacing[1], density.spacing[1]) / conversion
    z = numpy.arange(density.origin[2], density.topcorner[2] + density.spacing[2], density.spacing[2]) / conversion

    for mocoeffs in mocoeffslist:
        for mocoeff in mocoeffs:
            wavefn = numpy.zeros(density.data.shape, "d")
            for bs in range(len(bfs)):
                data = numpy.zeros(density.data.shape, "d")
                for i, xval in enumerate(x):
                    for j, yval in enumerate(y):
                        tmp = []
                        for zval in z:
                            tmp.append(bfs[bs].amp(xval, yval, zval))
                        data[i, j, :] = tmp
                data *= mocoeff[bs]
                wavefn += data
            density.data += wavefn ** 2

    # TODO ROHF
    if len(mocoeffslist) == 1:
        density.data *= 2.0

    return density
Example #42
0
def gen_spectra(file_name, name, units='eV', thresh=9):
    data = ccread(file_name)
    energies, intensities = convertor(data.etenergies, 'cm-1', units), data.etoscs
    #intensities = abs(intensities)

    #print(len(energies))
    #energies, intensities = energies[intensities > 10**-thresh], intensities[intensities > 10**-thresh]
    #print(len(energies))
    #energies, intensities = energies[intensities > 10**-8], intensities[intensities > 10**-8]
    #print(len(energies))
    #energies, intensities = energies[intensities > 10**-7], intensities[intensities > 10**-7]
    #print(len(energies))
    #energies, intensities = energies[intensities > 10**-6], intensities[intensities > 10**-6]
    #print(len(energies))
    #energies, intensities = energies[intensities > 10**-5], intensities[intensities > 10**-5]
    #print(len(energies))
    #energies, intensities = energies[energies < 3*10**4], intensities[energies < 3*10**4]
    #print(len(energies))

    s = Spectra(energies, intensities, name)
    return s
Example #43
0
    def extract(self, inputfile, line):
        """Extract information from the file object inputfile."""

        # extract the version number first
        if "Version" in line:
            self.metadata["package_version"] = line.split()[1]

        if line[1:19] == "ATOMIC COORDINATES":

            if not hasattr(self, "atomcoords"):
                self.atomcoords = []

            atomcoords = []
            atomnos = []

            self.skip_lines(inputfile, ['line', 'line', 'line'])

            line = next(inputfile)
            while line.strip():
                temp = line.strip().split()
                atomcoords.append([utils.convertor(float(x), "bohr", "Angstrom") for x in temp[3:6]])  # bohrs to angs
                atomnos.append(int(round(float(temp[2]))))
                line = next(inputfile)

            self.atomcoords.append(atomcoords)

            self.set_attribute('atomnos', atomnos)
            self.set_attribute('natom', len(self.atomnos))

        # Use BASIS DATA to parse input for gbasis, aonames and atombasis. If symmetry is used,
        # the function number starts from 1 for each irrep (the irrep index comes after the dot).
        #
        # BASIS DATA
        #
        #   Nr Sym  Nuc  Type         Exponents   Contraction coefficients
        #
        #   1.1 A     1  1s           71.616837     0.154329
        #                             13.045096     0.535328
        #                              3.530512     0.444635
        #   2.1 A     1  1s            2.941249    -0.099967
        #                              0.683483     0.399513
        # ...
        #
        if line[1:11] == "BASIS DATA":

            # We can do a sanity check with the header.
            self.skip_line(inputfile, 'blank')
            header = next(inputfile)
            assert header.split() == ["Nr", "Sym", "Nuc", "Type", "Exponents", "Contraction", "coefficients"]
            self.skip_line(inputfile, 'blank')

            aonames = []
            atombasis = [[] for i in range(self.natom)]
            gbasis = [[] for i in range(self.natom)]
            while line.strip():

                # We need to read the line at the start of the loop here, because the last function
                # will be added when a blank line signalling the end of the block is encountered.
                line = next(inputfile)

                # The formatting here can exhibit subtle differences, including the number of spaces
                # or indentation size. However, we will rely on explicit slices since not all components
                # are always available. In fact, components not being there has some meaning (see below).
                line_nr = line[1:6].strip()
                line_sym = line[7:9].strip()
                line_nuc = line[11:15].strip()
                line_type = line[16:22].strip()
                line_exp = line[25:38].strip()
                line_coeffs = line[38:].strip()

                # If a new function type is printed or the BASIS DATA block ends with a blank line,
                # then add the previous function to gbasis, except for the first function since
                # there was no preceeding one. When translating the Molpro function name to gbasis,
                # note that Molpro prints all components, but we want it only once, with the proper
                # shell type (S,P,D,F,G). Molpro names also differ between Cartesian/spherical representations.
                if (line_type and aonames) or line.strip() == "":

                    # All the possible AO names are created with the class. The function should always
                    # find a match in that dictionary, so we can check for that here and will need to
                    # update the dict if something unexpected comes up.
                    funcbasis = None
                    for fb, names in self.atomic_orbital_names.items():
                        if functype in names:
                            funcbasis = fb
                    assert funcbasis

                    # There is a separate basis function for each column of contraction coefficients. Since all
                    # atomic orbitals for a subshell will have the same parameters, we can simply check if
                    # the function tuple is already in gbasis[i] before adding it.
                    for i in range(len(coefficients[0])):

                        func = (funcbasis, [])
                        for j in range(len(exponents)):
                            func[1].append((exponents[j], coefficients[j][i]))
                        if func not in gbasis[funcatom-1]:
                            gbasis[funcatom-1].append(func)

                # If it is a new type, set up the variables for the next shell(s). An exception is symmetry functions,
                # which we want to copy from the previous function and don't have a new number on the line. For them,
                # we just want to update the nuclear index.
                if line_type:
                    if line_nr:
                        exponents = []
                        coefficients = []
                        functype = line_type
                    funcatom = int(line_nuc)

                # Add any exponents and coefficients to lists.
                if line_exp and line_coeffs:
                    funcexp = float(line_exp)
                    funccoeffs = [float(s) for s in line_coeffs.split()]
                    exponents.append(funcexp)
                    coefficients.append(funccoeffs)

                # If the function number is present then add to atombasis and aonames, which is different from
                # adding to gbasis since it enumerates AOs rather than basis functions. The number counts functions
                # in each irrep from 1 and we could add up the functions for each irrep to get the global count,
                # but it is simpler to just see how many aonames we have already parsed. Any symmetry functions
                # are also printed, but they don't get numbers so they are nor parsed.
                if line_nr:
                    element = self.table.element[self.atomnos[funcatom-1]]
                    aoname = "%s%i_%s" % (element, funcatom, functype)
                    aonames.append(aoname)
                    funcnr = len(aonames)
                    atombasis[funcatom-1].append(funcnr-1)

            self.set_attribute('aonames', aonames)
            self.set_attribute('atombasis', atombasis)
            self.set_attribute('gbasis', gbasis)

        if line[1:23] == "NUMBER OF CONTRACTIONS":
            nbasis = int(line.split()[3])
            self.set_attribute('nbasis', nbasis)

        # Basis set name
        if line[1:8] == "Library":
            self.metadata["basis_set"] = line.split()[4]

        # This is used to signalize whether we are inside an SCF calculation.
        if line[1:8] == "PROGRAM" and line[14:18] == "-SCF":

            self.insidescf = True
            self.metadata["methods"].append("HF")

        # Use this information instead of 'SETTING ...', in case the defaults are standard.
        # Note that this is sometimes printed in each geometry optimization step.
        if line[1:20] == "NUMBER OF ELECTRONS":

            spinup = int(line.split()[3][:-1])
            spindown = int(line.split()[4][:-1])
            # Nuclear charges (atomnos) should be parsed by now.
            nuclear = numpy.sum(self.atomnos)

            charge = nuclear - spinup - spindown
            self.set_attribute('charge', charge)

            mult = spinup - spindown + 1
            self.set_attribute('mult', mult)

        # Convergenve thresholds for SCF cycle, should be contained in a line such as:
        #   CONVERGENCE THRESHOLDS:    1.00E-05 (Density)    1.40E-07 (Energy)
        if self.insidescf and line[1:24] == "CONVERGENCE THRESHOLDS:":

            if not hasattr(self, "scftargets"):
                self.scftargets = []

            scftargets = list(map(float, line.split()[2::2]))
            self.scftargets.append(scftargets)
            # Usually two criteria, but save the names this just in case.
            self.scftargetnames = line.split()[3::2]

        # Read in the print out of the SCF cycle - for scfvalues. For RHF looks like:
        # ITERATION    DDIFF          GRAD             ENERGY        2-EL.EN.            DIPOLE MOMENTS         DIIS
        #     1      0.000D+00      0.000D+00      -379.71523700   1159.621171   0.000000   0.000000   0.000000    0
        #     2      0.000D+00      0.898D-02      -379.74469736   1162.389787   0.000000   0.000000   0.000000    1
        #     3      0.817D-02      0.144D-02      -379.74635529   1162.041033   0.000000   0.000000   0.000000    2
        #     4      0.213D-02      0.571D-03      -379.74658063   1162.159929   0.000000   0.000000   0.000000    3
        #     5      0.799D-03      0.166D-03      -379.74660889   1162.144256   0.000000   0.000000   0.000000    4
        if self.insidescf and line[1:10] == "ITERATION":

            if not hasattr(self, "scfvalues"):
                self.scfvalues = []

            line = next(inputfile)
            energy = 0.0
            scfvalues = []
            while line.strip() != "":
                chomp = line.split()
                if chomp[0].isdigit():

                    ddiff = float(chomp[1].replace('D', 'E'))
                    grad = float(chomp[2].replace('D', 'E'))
                    newenergy = float(chomp[3])
                    ediff = newenergy - energy
                    energy = newenergy

                    # The convergence thresholds must have been read above.
                    # Presently, we recognize MAX DENSITY and MAX ENERGY thresholds.
                    numtargets = len(self.scftargetnames)
                    values = [numpy.nan]*numtargets
                    for n, name in zip(list(range(numtargets)), self.scftargetnames):
                        if "ENERGY" in name.upper():
                            values[n] = ediff
                        elif "DENSITY" in name.upper():
                            values[n] = ddiff
                    scfvalues.append(values)

                try:
                    line = next(inputfile)
                except StopIteration:
                    self.logger.warning('File terminated before end of last SCF! Last gradient: {}'.format(grad))
                    break
            self.scfvalues.append(numpy.array(scfvalues))

        # SCF result - RHF/UHF and DFT (RKS) energies.
        if (line[1:5] in ["!RHF", "!UHF", "!RKS"] and line[16:22].lower() == "energy"):

            if not hasattr(self, "scfenergies"):
                self.scfenergies = []
            scfenergy = float(line.split()[4])
            self.scfenergies.append(utils.convertor(scfenergy, "hartree", "eV"))

            # We are now done with SCF cycle (after a few lines).
            self.insidescf = False

        # MP2 energies.
        if line[1:5] == "!MP2":

            self.metadata["methods"].append("MP2")

            if not hasattr(self, 'mpenergies'):
                self.mpenergies = []
            mp2energy = float(line.split()[-1])
            mp2energy = utils.convertor(mp2energy, "hartree", "eV")
            self.mpenergies.append([mp2energy])

        # MP2 energies if MP3 or MP4 is also calculated.
        if line[1:5] == "MP2:":

            self.metadata["methods"].append("MP2")
            if not hasattr(self, 'mpenergies'):
                self.mpenergies = []
            mp2energy = float(line.split()[2])
            mp2energy = utils.convertor(mp2energy, "hartree", "eV")
            self.mpenergies.append([mp2energy])

        # MP3 (D) and MP4 (DQ or SDQ) energies.
        if line[1:8] == "MP3(D):":

            self.metadata["methods"].append("MP3")
            mp3energy = float(line.split()[2])
            mp2energy = utils.convertor(mp3energy, "hartree", "eV")
            line = next(inputfile)
            self.mpenergies[-1].append(mp2energy)
            if line[1:9] == "MP4(DQ):":
                self.metadata["methods"].append("MP4")
                mp4energy = float(line.split()[2])
                line = next(inputfile)
                if line[1:10] == "MP4(SDQ):":
                    self.metadata["methods"].append("MP4")
                    mp4energy = float(line.split()[2])
                mp4energy = utils.convertor(mp4energy, "hartree", "eV")
                self.mpenergies[-1].append(mp4energy)

        # The CCSD program operates all closed-shel coupled cluster runs.
        if line[1:15] == "PROGRAM * CCSD":

            self.metadata["methods"].append("CCSD")
            if not hasattr(self, "ccenergies"):
                self.ccenergies = []
            while line[1:20] != "Program statistics:":
                # The last energy (most exact) will be read last and thus saved.
                if line[1:5] == "!CCD" or line[1:6] == "!CCSD" or line[1:9] == "!CCSD(T)":
                    ccenergy = float(line.split()[-1])
                    ccenergy = utils.convertor(ccenergy, "hartree", "eV")
                line = next(inputfile)
            self.ccenergies.append(ccenergy)

        # Read the occupancy (index of H**O s).
        # For restricted calculations, there is one line here. For unrestricted, two:
        #   Final alpha occupancy:  ...
        #   Final beta  occupancy:  ...
        if line[1:17] == "Final occupancy:":
            self.homos = [int(line.split()[-1])-1]
        if line[1:23] == "Final alpha occupancy:":
            self.homos = [int(line.split()[-1])-1]
            line = next(inputfile)
            self.homos.append(int(line.split()[-1])-1)

        # Dipole is always printed on one line after the final RHF energy, and by default
        # it seems Molpro uses the origin as the reference point.
        if line.strip()[:13] == "Dipole moment":

            assert line.split()[2] == "/Debye"

            reference = [0.0, 0.0, 0.0]
            dipole = [float(d) for d in line.split()[-3:]]

            if not hasattr(self, 'moments'):
                self.moments = [reference, dipole]
            else:
                self.moments[1] == dipole

        # Static dipole polarizability.
        if line.strip() == "SCF dipole polarizabilities":
            if not hasattr(self, "polarizabilities"):
                self.polarizabilities = []
            polarizability = []
            self.skip_lines(inputfile, ['b', 'directions'])
            for _ in range(3):
                line = next(inputfile)
                polarizability.append(line.split()[1:])
            self.polarizabilities.append(numpy.array(polarizability))

        # Check for ELECTRON ORBITALS (canonical molecular orbitals).
        if line[1:18] == "ELECTRON ORBITALS" or self.electronorbitals:
            self._parse_orbitals(inputfile, line)


        # If the MATROP program was called appropriately,
        #   the atomic obital overlap matrix S is printed.
        # The matrix is printed straight-out, ten elements in each row, both halves.
        # Note that is the entire matrix is not printed, then aooverlaps
        #   will not have dimensions nbasis x nbasis.
        if line[1:9] == "MATRIX S":

            if not hasattr(self, "aooverlaps"):
                self.aooverlaps = [[]]

            self.skip_lines(inputfile, ['b', 'symblocklabel'])

            line = next(inputfile)
            while line.strip() != "":
                elements = [float(s) for s in line.split()]
                if len(self.aooverlaps[-1]) + len(elements) <= self.nbasis:
                    self.aooverlaps[-1] += elements
                else:
                    n = len(self.aooverlaps[-1]) + len(elements) - self.nbasis
                    self.aooverlaps[-1] += elements[:-n]
                    self.aooverlaps.append([])
                    self.aooverlaps[-1] += elements[-n:]
                line = next(inputfile)

        # Check for MCSCF natural orbitals.
        if line[1:17] == "NATURAL ORBITALS":
            self._parse_orbitals(inputfile, line)

        # Thresholds are printed only if the defaults are changed with GTHRESH.
        # In that case, we can fill geotargets with non-default values.
        # The block should look like this as of Molpro 2006.1:
        #   THRESHOLDS:

        #   ZERO    =  1.00D-12  ONEINT  =  1.00D-12  TWOINT  =  1.00D-11  PREFAC  =  1.00D-14  LOCALI  =  1.00D-09  EORDER  =  1.00D-04
        #   ENERGY  =  0.00D+00  ETEST   =  0.00D+00  EDENS   =  0.00D+00  THRDEDEF=  1.00D-06  GRADIENT=  1.00D-02  STEP    =  1.00D-03
        #   ORBITAL =  1.00D-05  CIVEC   =  1.00D-05  COEFF   =  1.00D-04  PRINTCI =  5.00D-02  PUNCHCI =  9.90D+01  OPTGRAD =  3.00D-04
        #   OPTENERG=  1.00D-06  OPTSTEP =  3.00D-04  THRGRAD =  2.00D-04  COMPRESS=  1.00D-11  VARMIN  =  1.00D-07  VARMAX  =  1.00D-03
        #   THRDOUB =  0.00D+00  THRDIV  =  1.00D-05  THRRED  =  1.00D-07  THRPSP  =  1.00D+00  THRDC   =  1.00D-10  THRCS   =  1.00D-10
        #   THRNRM  =  1.00D-08  THREQ   =  0.00D+00  THRDE   =  1.00D+00  THRREF  =  1.00D-05  SPARFAC =  1.00D+00  THRDLP  =  1.00D-07
        #   THRDIA  =  1.00D-10  THRDLS  =  1.00D-07  THRGPS  =  0.00D+00  THRKEX  =  0.00D+00  THRDIS  =  2.00D-01  THRVAR  =  1.00D-10
        #   THRLOC  =  1.00D-06  THRGAP  =  1.00D-06  THRLOCT = -1.00D+00  THRGAPT = -1.00D+00  THRORB  =  1.00D-06  THRMLTP =  0.00D+00
        #   THRCPQCI=  1.00D-10  KEXTA   =  0.00D+00  THRCOARS=  0.00D+00  SYMTOL  =  1.00D-06  GRADTOL =  1.00D-06  THROVL  =  1.00D-08
        #   THRORTH =  1.00D-08  GRID    =  1.00D-06  GRIDMAX =  1.00D-03  DTMAX   =  0.00D+00
        if line[1:12] == "THRESHOLDS":

            self.skip_line(input, 'blank')

            line = next(inputfile)
            while line.strip():

                if "OPTENERG" in line:
                    start = line.find("OPTENERG")
                    optenerg = line[start+10:start+20]
                if "OPTGRAD" in line:
                    start = line.find("OPTGRAD")
                    optgrad = line[start+10:start+20]
                if "OPTSTEP" in line:
                    start = line.find("OPTSTEP")
                    optstep = line[start+10:start+20]
                line = next(inputfile)

            self.geotargets = [optenerg, optgrad, optstep]

        # The optimization history is the source for geovlues:
        #
        #   END OF GEOMETRY OPTIMIZATION.    TOTAL CPU:       246.9 SEC
        #
        #     ITER.   ENERGY(OLD)    ENERGY(NEW)      DE          GRADMAX     GRADNORM    GRADRMS     STEPMAX     STEPLEN     STEPRMS
        #      1  -382.02936898  -382.04914450    -0.01977552  0.11354875  0.20127947  0.01183997  0.12972761  0.20171740  0.01186573
        #      2  -382.04914450  -382.05059234    -0.00144784  0.03299860  0.03963339  0.00233138  0.05577169  0.06687650  0.00393391
        #      3  -382.05059234  -382.05069136    -0.00009902  0.00694359  0.01069889  0.00062935  0.01654549  0.02016307  0.00118606
        # ...
        #
        # The above is an exerpt from Molpro 2006, but it is a little bit different
        # for Molpro 2012, namely the 'END OF GEOMETRY OPTIMIZATION occurs after the
        # actual history list. It seems there is a another consistent line before the
        # history, but this might not be always true -- so this is a potential weak link.
        if line[1:30] == "END OF GEOMETRY OPTIMIZATION." or line.strip() == "Quadratic Steepest Descent - Minimum Search":

            # I think this is the trigger for convergence, and it shows up at the top in Molpro 2006.
            geometry_converged = line[1:30] == "END OF GEOMETRY OPTIMIZATION."

            self.skip_line(inputfile, 'blank')

            # Newer version of Molpro (at least for 2012) print and additional column
            # with the timing information for each step. Otherwise, the history looks the same.
            headers = next(inputfile).split()
            if not len(headers) in (10, 11):
                return

            # Although criteria can be changed, the printed format should not change.
            # In case it does, retrieve the columns for each parameter.
            index_ITER = headers.index('ITER.')
            index_THRENERG = headers.index('DE')
            index_THRGRAD = headers.index('GRADMAX')
            index_THRSTEP = headers.index('STEPMAX')

            line = next(inputfile)
            self.geovalues = []
            while line.strip():

                line = line.split()
                istep = int(line[index_ITER])

                geovalues = []
                geovalues.append(float(line[index_THRENERG]))
                geovalues.append(float(line[index_THRGRAD]))
                geovalues.append(float(line[index_THRSTEP]))
                self.geovalues.append(geovalues)
                line = next(inputfile)
                if line.strip() == "Freezing grid":
                    line = next(inputfile)

            # The convergence trigger shows up somewhere at the bottom in Molpro 2012,
            # before the final stars. If convergence is not reached, there is an additional
            # line that can be checked for. This is a little tricky, though, since it is
            # not the last line... so bail out of the loop if convergence failure is detected.
            while "*****" not in line:
                line = next(inputfile)
                if line.strip() == "END OF GEOMETRY OPTIMIZATION.":
                    geometry_converged = True
                if "No convergence" in line:
                    geometry_converged = False
                    break

            # Finally, deal with optdone, append the last step to it only if we had convergence.
            if not hasattr(self, 'optdone'):
                self.optdone = []
            if geometry_converged:
                self.optdone.append(istep-1)

        # This block should look like this:
        #   Normal Modes
        #
        #                                1 Au        2 Bu        3 Ag        4 Bg        5 Ag
        #   Wavenumbers [cm-1]          151.81      190.88      271.17      299.59      407.86
        #   Intensities [km/mol]          0.33        0.28        0.00        0.00        0.00
        #   Intensities [relative]        0.34        0.28        0.00        0.00        0.00
        #             CX1              0.00000    -0.01009     0.02577     0.00000     0.06008
        #             CY1              0.00000    -0.05723    -0.06696     0.00000     0.06349
        #             CZ1             -0.02021     0.00000     0.00000     0.11848     0.00000
        #             CX2              0.00000    -0.01344     0.05582     0.00000    -0.02513
        #             CY2              0.00000    -0.06288    -0.03618     0.00000     0.00349
        #             CZ2             -0.05565     0.00000     0.00000     0.07815     0.00000
        #             ...
        # Molpro prints low frequency modes in a subsequent section with the same format,
        #   which also contains zero frequency modes, with the title:
        #   Normal Modes of low/zero frequencies
        if line[1:13] == "Normal Modes":

            islow = (line[1:37] == "Normal Modes of low/zero frequencies")

            self.skip_line(inputfile, 'blank')

            # Each portion of five modes is followed by a single blank line.
            # The whole block is followed by an additional blank line.
            line = next(inputfile)
            while line.strip():

                if line[1:25].isspace():
                    if not islow:  # vibsyms not printed for low freq modes
                        numbers = list(map(int, line.split()[::2]))
                        vibsyms = line.split()[1::2]
                    else:
                        # give low freq modes an empty str as vibsym
                        # note there could be other possibilities..
                        numbers = list(map(int, line.split()))
                        vibsyms = ['']*len(numbers)

                if line[1:12] == "Wavenumbers":
                    vibfreqs = list(map(float, line.strip().split()[2:]))

                if line[1:21] == "Intensities [km/mol]":
                    vibirs = list(map(float, line.strip().split()[2:]))

                # There should always by 3xnatom displacement rows.
                if line[1:11].isspace() and line[13:25].strip().isdigit():

                    # There are a maximum of 5 modes per line.
                    nmodes = len(line.split())-1

                    vibdisps = []
                    for i in range(nmodes):
                        vibdisps.append([])
                        for n in range(self.natom):
                            vibdisps[i].append([])
                    for i in range(nmodes):
                        disp = float(line.split()[i+1])
                        vibdisps[i][0].append(disp)
                    for i in range(self.natom*3 - 1):
                        line = next(inputfile)
                        iatom = (i+1)//3
                        for i in range(nmodes):
                            disp = float(line.split()[i+1])
                            vibdisps[i][iatom].append(disp)

                line = next(inputfile)
                if not line.strip():

                    if not hasattr(self, "vibfreqs"):
                        self.vibfreqs = []
                    if not hasattr(self, "vibsyms"):
                        self.vibsyms = []
                    if not hasattr(self, "vibirs") and "vibirs" in dir():
                        self.vibirs = []
                    if not hasattr(self, "vibdisps") and "vibdisps" in dir():
                        self.vibdisps = []

                    if not islow:
                        self.vibfreqs.extend(vibfreqs)
                        self.vibsyms.extend(vibsyms)
                        if "vibirs" in dir():
                            self.vibirs.extend(vibirs)
                        if "vibdisps" in dir():
                            self.vibdisps.extend(vibdisps)
                    else:
                        nonzero = [f > 0 for f in vibfreqs]
                        vibfreqs = [f for f in vibfreqs if f > 0]
                        self.vibfreqs = vibfreqs + self.vibfreqs
                        vibsyms = [vibsyms[i] for i in range(len(vibsyms)) if nonzero[i]]
                        self.vibsyms = vibsyms + self.vibsyms
                        if "vibirs" in dir():
                            vibirs = [vibirs[i] for i in range(len(vibirs)) if nonzero[i]]
                            self.vibirs = vibirs + self.vibirs
                        if "vibdisps" in dir():
                            vibdisps = [vibdisps[i] for i in range(len(vibdisps)) if nonzero[i]]
                            self.vibdisps = vibdisps + self.vibdisps

                    line = next(inputfile)

        if line[1:16] == "Force Constants":

            self.logger.info("Creating attribute hessian")
            self.hessian = []
            line = next(inputfile)
            hess = []
            tmp = []

            while line.strip():
                try:
                    list(map(float, line.strip().split()[2:]))
                except:
                    line = next(inputfile)
                line.strip().split()[1:]
                hess.extend([list(map(float, line.strip().split()[1:]))])
                line = next(inputfile)
            lig = 0

            while (lig == 0) or (len(hess[0]) > 1):
                tmp.append(hess.pop(0))
                lig += 1
            k = 5

            while len(hess) != 0:
                tmp[k] += hess.pop(0)
                k += 1
                if (len(tmp[k-1]) == lig):
                    break
                if k >= lig:
                    k = len(tmp[-1])
            for l in tmp:
                self.hessian += l

        if line[1:14] == "Atomic Masses" and hasattr(self, "hessian"):

            line = next(inputfile)
            self.amass = list(map(float, line.strip().split()[2:]))

            while line.strip():
                line = next(inputfile)
                self.amass += list(map(float, line.strip().split()[2:]))

        #1PROGRAM * POP (Mulliken population analysis)
        #
        #
        # Density matrix read from record         2100.2  Type=RHF/CHARGE (state 1.1)
        #
        # Population analysis by basis function type
        #
        # Unique atom        s        p        d        f        g    Total    Charge
        #   2  C       3.11797  2.88497  0.00000  0.00000  0.00000  6.00294  - 0.00294
        #   3  C       3.14091  2.91892  0.00000  0.00000  0.00000  6.05984  - 0.05984
        # ...
        if line.strip() == "1PROGRAM * POP (Mulliken population analysis)":

            self.skip_lines(inputfile, ['b', 'b', 'density_source', 'b', 'func_type', 'b'])

            header = next(inputfile)
            icharge = header.split().index('Charge')

            charges = []
            line = next(inputfile)
            while line.strip():
                cols = line.split()
                charges.append(float(cols[icharge]+cols[icharge+1]))
                line = next(inputfile)

            if not hasattr(self, "atomcharges"):
                self.atomcharges = {}
            self.atomcharges['mulliken'] = charges

        if 'GRADIENT FOR STATE' in line:
            for _ in range(3):
                next(inputfile)
            grad = []
            lines_read = 0
            while lines_read < self.natom:
                line = next(inputfile)
                # Because molpro inserts an empty line every 50th atom.
                if line:
                    grad.append([float(x) for x in line.split()[1:]])
                    lines_read += 1
            if not hasattr(self, 'grads'):
                self.grads = []
            self.grads.append(grad)

        if line[:25] == ' Variable memory released':
            self.metadata['success'] = True
Example #44
0
    def extract(self, inputfile, line):
        """Extract information from the file object inputfile."""

        # Extract the version number and optionally the revision number.
        if "version" in line:
            search = re.search(r"\sversion\s*(\d\.\d)", line)
            if search:
                self.metadata["package_version"] = search.groups()[0]
        if "Revision" in line:
            revision = line.split()[1]
            # Don't add revision information to the main package version for now.
            # if "package_version" in self.metadata:
            #     package_version = "{}.r{}".format(self.metadata["package_version"],
            #                                       revision)
            #     self.metadata["package_version"] = package_version

        if line[1:22] == "total number of atoms":
            natom = int(line.split()[-1])
            self.set_attribute('natom', natom)

        if line[3:44] == "convergence threshold in optimization run":
            # Assuming that this is only found in the case of OPTXYZ
            # (i.e. an optimization in Cartesian coordinates)
            self.geotargets = [float(line.split()[-2])]

        if line[32:61] == "largest component of gradient":
            # This is the geotarget in the case of OPTXYZ
            if not hasattr(self, "geovalues"):
                self.geovalues = []
            self.geovalues.append([float(line.split()[4])])

        if line[37:49] == "convergence?":
            # Get the geovalues and geotargets for OPTIMIZE
            if not hasattr(self, "geovalues"):
                self.geovalues = []
                self.geotargets = []
            geotargets = []
            geovalues = []
            for i in range(4):
                temp = line.split()
                geovalues.append(float(temp[2]))
                if not self.geotargets:
                    geotargets.append(float(temp[-2]))
                line = next(inputfile)
            self.geovalues.append(geovalues)
            if not self.geotargets:
                self.geotargets = geotargets

        # This is the only place coordinates are printed in single point calculations. Note that
        # in the following fragment, the basis set selection is not always printed:
        #
        #                                        ******************
        #                                        molecular geometry
        #                                        ******************
        #
        # ****************************************
        # * basis selected is sto     sto3g      *
        # ****************************************
        #
        #         *******************************************************************************
        #         *                                                                             *
        #         *     atom   atomic                coordinates                 number of      *
        #         *            charge       x             y              z       shells         *
        #         *                                                                             *
        #         *******************************************************************************
        #         *                                                                             *
        #         *                                                                             *
        #         *    c         6.0   0.0000000     -2.6361501      0.0000000       2          *
        #         *                                                                1s  2sp      *
        #         *                                                                             *
        #         *                                                                             *
        #         *    c         6.0   0.0000000      2.6361501      0.0000000       2          *
        #         *                                                                1s  2sp      *
        #         *                                                                             *
        # ...
        #
        if line.strip() == "molecular geometry":

            self.updateprogress(inputfile, "Coordinates")

            self.skip_lines(inputfile, ['s', 'b', 's'])
            line = next(inputfile)
            if "basis selected is" in line:
                self.skip_lines(inputfile, ['s', 'b', 's', 's'])

            self.skip_lines(inputfile, ['header1', 'header2', 's', 's'])

            atomnos = []
            atomcoords = []
            line = next(inputfile)
            while line.strip():
                line = next(inputfile)
                if line.strip()[1:10].strip() and list(set(line.strip())) != ['*']:
                    atomcoords.append([utils.convertor(float(x), "bohr", "Angstrom") for x in line.split()[3:6]])
                    atomnos.append(int(round(float(line.split()[2]))))

            if not hasattr(self, "atomcoords"):
                self.atomcoords = []
            self.atomcoords.append(atomcoords)
            self.set_attribute('atomnos', atomnos)

        # Each step of a geometry optimization will also print the coordinates:
        #
        # search  0
        #                                        *******************
        # point   0                              nuclear coordinates
        #                                        *******************
        #
        #         x              y              z            chg  tag
        #  ============================================================
        #        0.0000000     -2.6361501      0.0000000    6.00  c
        #        0.0000000      2.6361501      0.0000000    6.00  c
        # ..
        #
        if line[40:59] == "nuclear coordinates":

            self.updateprogress(inputfile, "Coordinates")

            # We need not remember the first geometry in geometry optimizations, as this will
            # be already parsed from the "molecular geometry" section (see above).
            if not hasattr(self, 'firstnuccoords') or self.firstnuccoords:
                self.firstnuccoords = False
                return

            self.skip_lines(inputfile, ['s', 'b', 'colname', 'e'])

            atomcoords = []
            atomnos = []
            line = next(inputfile)
            while list(set(line.strip())) != ['=']:

                cols = line.split()
                atomcoords.append([utils.convertor(float(x), "bohr", "Angstrom") for x in cols[0:3]])
                atomnos.append(int(float(cols[3])))

                line = next(inputfile)

            if not hasattr(self, "atomcoords"):
                self.atomcoords = []
            self.atomcoords.append(atomcoords)
            self.set_attribute('atomnos', atomnos)

        # This is printed when a geometry optimization succeeds, after the last gradient of the energy.
        if line[40:62] == "optimization converged":
            self.skip_line(inputfile, 's')
            if not hasattr(self, 'optdone'):
                self.optdone = []
            self.optdone.append(len(self.geovalues)-1)

        # This is apparently printed when a geometry optimization is not converged but the job ends.
        if "minimisation not converging" in line:
            self.skip_line(inputfile, 's')
            self.optdone = []

        if line[1:32] == "total number of basis functions":

            nbasis = int(line.split()[-1])
            self.set_attribute('nbasis', nbasis)

            while line.find("charge of molecule") < 0:
                line = next(inputfile)

            charge = int(line.split()[-1])
            self.set_attribute('charge', charge)

            mult = int(next(inputfile).split()[-1])
            self.set_attribute('mult', mult)

            alpha = int(next(inputfile).split()[-1])-1
            beta = int(next(inputfile).split()[-1])-1
            if self.mult == 1:
                self.homos = numpy.array([alpha], "i")
            else:
                self.homos = numpy.array([alpha, beta], "i")

        if line[37:69] == "s-matrix over gaussian basis set":
            self.aooverlaps = numpy.zeros((self.nbasis, self.nbasis), "d")

            self.skip_lines(inputfile, ['d', 'b'])

            i = 0
            while i < self.nbasis:
                self.updateprogress(inputfile, "Overlap")

                self.skip_lines(inputfile, ['b', 'b', 'header', 'b', 'b'])

                for j in range(self.nbasis):
                    temp = list(map(float, next(inputfile).split()[1:]))
                    self.aooverlaps[j, (0+i):(len(temp)+i)] = temp

                i += len(temp)

        if line[18:43] == 'EFFECTIVE CORE POTENTIALS':

            self.skip_line(inputfile, 'stars')

            self.coreelectrons = numpy.zeros(self.natom, 'i')
            line = next(inputfile)
            while line[15:46] != "*"*31:
                if line.find("for atoms ...") >= 0:
                    atomindex = []
                    line = next(inputfile)
                    while line.find("core charge") < 0:
                        broken = line.split()
                        atomindex.extend([int(x.split("-")[0]) for x in broken])
                        line = next(inputfile)
                    charge = float(line.split()[4])
                    for idx in atomindex:
                        self.coreelectrons[idx-1] = self.atomnos[idx-1] - charge
                line = next(inputfile)

        if line[3:27] == "Wavefunction convergence":
            self.scftarget = float(line.split()[-2])
            self.scftargets = []

        if line[11:22] == "normal mode":
            if not hasattr(self, "vibfreqs"):
                self.vibfreqs = []
                self.vibirs = []

            units = next(inputfile)
            xyz = next(inputfile)
            equals = next(inputfile)
            line = next(inputfile)
            while line != equals:
                temp = line.split()
                self.vibfreqs.append(float(temp[1]))
                self.vibirs.append(float(temp[-2]))
                line = next(inputfile)
            # Use the length of the vibdisps to figure out
            # how many rotations and translations to remove
            self.vibfreqs = self.vibfreqs[-len(self.vibdisps):]
            self.vibirs = self.vibirs[-len(self.vibdisps):]

        if line[44:73] == "normalised normal coordinates":

            self.skip_lines(inputfile, ['e', 'b', 'b'])

            self.vibdisps = []
            freqnum = next(inputfile)
            while freqnum.find("=") < 0:

                self.skip_lines(inputfile, ['b', 'e', 'freqs', 'e', 'b', 'header', 'e'])

                p = [[] for x in range(9)]
                for i in range(len(self.atomnos)):
                    brokenx = list(map(float, next(inputfile)[25:].split()))
                    brokeny = list(map(float, next(inputfile)[25:].split()))
                    brokenz = list(map(float, next(inputfile)[25:].split()))
                    for j, x in enumerate(list(zip(brokenx, brokeny, brokenz))):
                        p[j].append(x)
                self.vibdisps.extend(p)

                self.skip_lines(inputfile, ['b', 'b'])

                freqnum = next(inputfile)

        if line[26:36] == "raman data":
            self.vibramans = []

            self.skip_lines(inputfile, ['s', 'b', 'header', 'b'])

            line = next(inputfile)
            while line[1] != "*":
                self.vibramans.append(float(line.split()[3]))
                self.skip_line(inputfile, 'blank')
                line = next(inputfile)
            # Use the length of the vibdisps to figure out
            # how many rotations and translations to remove
            self.vibramans = self.vibramans[-len(self.vibdisps):]

        if line[3:11] == "SCF TYPE":
            self.scftype = line.split()[-2]
            assert self.scftype in ['rhf', 'uhf', 'gvb'], "%s not one of 'rhf', 'uhf' or 'gvb'" % self.scftype

        if line[15:31] == "convergence data":
            if not hasattr(self, "scfvalues"):
                self.scfvalues = []
            self.scftargets.append([self.scftarget])  # Assuming it does not change over time
            while line[1:10] != "="*9:
                line = next(inputfile)
            line = next(inputfile)
            tester = line.find("tester")  # Can be in a different place depending
            assert tester >= 0
            while line[1:10] != "="*9:  # May be two or three lines (unres)
                line = next(inputfile)

            scfvalues = []
            line = next(inputfile)
            while line.strip():
                # e.g. **** recalulation of fock matrix on iteration  4 (examples/chap12/pyridine.out)
                if line[2:6] != "****":
                    scfvalues.append([float(line[tester-5:tester+6])])
                try:
                    line = next(inputfile)
                except StopIteration:
                    self.logger.warning('File terminated before end of last SCF! Last tester: {}'.format(line.split()[5]))
                    break
            self.scfvalues.append(scfvalues)

        if line[10:22] == "total energy" and len(line.split()) == 3:
            if not hasattr(self, "scfenergies"):
                self.scfenergies = []
            scfenergy = utils.convertor(float(line.split()[-1]), "hartree", "eV")
            self.scfenergies.append(scfenergy)

        # Total energies after Moller-Plesset corrections
        # Second order correction is always first, so its first occurance
        #   triggers creation of mpenergies (list of lists of energies)
        # Further corrections are appended as found
        # Note: GAMESS-UK sometimes prints only the corrections,
        #   so they must be added to the last value of scfenergies
        if line[10:32] == "mp2 correlation energy" or \
           line[10:42] == "second order perturbation energy":
            if not hasattr(self, "mpenergies"):
                self.mpenergies = []
            self.mpenergies.append([])
            self.mp2correction = self.float(line.split()[-1])
            self.mp2energy = self.scfenergies[-1] + self.mp2correction
            self.mpenergies[-1].append(utils.convertor(self.mp2energy, "hartree", "eV"))
        if line[10:41] == "third order perturbation energy":
            self.mp3correction = self.float(line.split()[-1])
            self.mp3energy = self.mp2energy + self.mp3correction
            self.mpenergies[-1].append(utils.convertor(self.mp3energy, "hartree", "eV"))

        if line[40:59] == "molecular basis set":
            self.gbasis = []
            line = next(inputfile)
            while line.find("contraction coefficients") < 0:
                line = next(inputfile)
            equals = next(inputfile)
            blank = next(inputfile)
            atomname = next(inputfile)
            basisregexp = re.compile("\d*(\D+)")  # Get everything after any digits
            shellcounter = 1
            while line != equals:
                gbasis = []  # Stores basis sets on one atom
                blank = next(inputfile)
                blank = next(inputfile)
                line = next(inputfile)
                shellno = int(line.split()[0])
                shellgap = shellno - shellcounter
                shellsize = 0
                while len(line.split()) != 1 and line != equals:
                    if line.split():
                        shellsize += 1
                    coeff = {}
                    # coefficients and symmetries for a block of rows
                    while line.strip() and line != equals:
                        temp = line.strip().split()
                    # temp[1] may be either like (a) "1s" and "1sp", or (b) "s" and "sp"
                    # See GAMESS-UK 7.0 distribution/examples/chap12/pyridine2_21m10r.out
                    # for an example of the latter
                        sym = basisregexp.match(temp[1]).groups()[0]
                        assert sym in ['s', 'p', 'd', 'f', 'sp'], "'%s' not a recognized symmetry" % sym
                        if sym == "sp":
                            coeff.setdefault("S", []).append((float(temp[3]), float(temp[6])))
                            coeff.setdefault("P", []).append((float(temp[3]), float(temp[10])))
                        else:
                            coeff.setdefault(sym.upper(), []).append((float(temp[3]), float(temp[6])))
                        line = next(inputfile)
                    # either a blank or a continuation of the block
                    if coeff:
                        if sym == "sp":
                            gbasis.append(('S', coeff['S']))
                            gbasis.append(('P', coeff['P']))
                        else:
                            gbasis.append((sym.upper(), coeff[sym.upper()]))
                    if line == equals:
                        continue
                    line = next(inputfile)
                    # either the start of the next block or the start of a new atom or
                    # the end of the basis function section (signified by a line of equals)
                numtoadd = 1 + (shellgap // shellsize)
                shellcounter = shellno + shellsize
                for x in range(numtoadd):
                    self.gbasis.append(gbasis)

        if line[50:70] == "----- beta set -----":
            self.betamosyms = True
            self.betamoenergies = True
            self.betamocoeffs = True
            # betamosyms will be turned off in the next
            # SYMMETRY ASSIGNMENT section

        if line[31:50] == "SYMMETRY ASSIGNMENT":
            if not hasattr(self, "mosyms"):
                self.mosyms = []

            multiple = {'a': 1, 'b': 1, 'e': 2, 't': 3, 'g': 4, 'h': 5}

            equals = next(inputfile)
            line = next(inputfile)
            while line != equals:  # There may be one or two lines of title (compare mg10.out and duhf_1.out)
                line = next(inputfile)

            mosyms = []
            line = next(inputfile)
            while line != equals:
                temp = line[25:30].strip()
                if temp[-1] == '?':
                    # e.g. e? or t? or g? (see example/chap12/na7mg_uhf.out)
                    # for two As, an A and an E, and two Es of the same energy respectively.
                    t = line[91:].strip().split()
                    for i in range(1, len(t), 2):
                        for j in range(multiple[t[i][0]]):  # add twice for 'e', etc.
                            mosyms.append(self.normalisesym(t[i]))
                else:
                    for j in range(multiple[temp[0]]):
                        mosyms.append(self.normalisesym(temp))  # add twice for 'e', etc.
                line = next(inputfile)
            assert len(mosyms) == self.nmo, "mosyms: %d but nmo: %d" % (len(mosyms), self.nmo)
            if self.betamosyms:
                # Only append if beta (otherwise with IPRINT SCF
                # it will add mosyms for every step of a geo opt)
                self.mosyms.append(mosyms)
                self.betamosyms = False
            elif self.scftype == 'gvb':
                # gvb has alpha and beta orbitals but they are identical
                self.mosysms = [mosyms, mosyms]
            else:
                self.mosyms = [mosyms]

        if line[50:62] == "eigenvectors":
        # Mocoeffs...can get evalues from here too
        # (only if using FORMAT HIGH though will they all be present)
            if not hasattr(self, "mocoeffs"):
                self.aonames = []
                aonames = []
            minus = next(inputfile)

            mocoeffs = numpy.zeros((self.nmo, self.nbasis), "d")
            readatombasis = False
            if not hasattr(self, "atombasis"):
                self.atombasis = []
                for i in range(self.natom):
                    self.atombasis.append([])
                readatombasis = True

            self.skip_lines(inputfile, ['b', 'b', 'evalues'])

            p = re.compile(r"\d+\s+(\d+)\s*(\w+) (\w+)")
            oldatomname = "DUMMY VALUE"

            mo = 0
            while mo < self.nmo:
                self.updateprogress(inputfile, "Coefficients")

                self.skip_lines(inputfile, ['b', 'b', 'nums', 'b', 'b'])

                for basis in range(self.nbasis):
                    line = next(inputfile)
                    # Fill atombasis only first time around.
                    if readatombasis:
                        orbno = int(line[1:5])-1
                        atomno = int(line[6:9])-1
                        self.atombasis[atomno].append(orbno)
                    if not self.aonames:
                        pg = p.match(line[:18].strip()).groups()
                        atomname = "%s%s%s" % (pg[1][0].upper(), pg[1][1:], pg[0])
                        if atomname != oldatomname:
                            aonum = 1
                        oldatomname = atomname
                        name = "%s_%d%s" % (atomname, aonum, pg[2].upper())
                        if name in aonames:
                            aonum += 1
                        name = "%s_%d%s" % (atomname, aonum, pg[2].upper())
                        aonames.append(name)
                    temp = list(map(float, line[19:].split()))
                    mocoeffs[mo:(mo+len(temp)), basis] = temp
                # Fill atombasis only first time around.
                readatombasis = False
                if not self.aonames:
                    self.aonames = aonames

                line = next(inputfile)  # blank line
                while not line.strip():
                    line = next(inputfile)
                evalues = line
                if evalues[:17].strip():  # i.e. if these aren't evalues
                    break  # Not all the MOs are present
                mo += len(temp)
            mocoeffs = mocoeffs[0:(mo+len(temp)), :]  # In case some aren't present
            if self.betamocoeffs:
                self.mocoeffs.append(mocoeffs)
            else:
                self.mocoeffs = [mocoeffs]

        if line[7:12] == "irrep":
            ########## eigenvalues ###########
            # This section appears once at the start of a geo-opt and once at the end
            # unless IPRINT SCF is used (when it appears at every step in addition)
            if not hasattr(self, "moenergies"):
                self.moenergies = []

            equals = next(inputfile)
            while equals[1:5] != "====":  # May be one or two lines of title (compare duhf_1.out and mg10.out)
                equals = next(inputfile)

            moenergies = []
            line = next(inputfile)
            if not line.strip():  # May be a blank line here (compare duhf_1.out and mg10.out)
                line = next(inputfile)

            while line.strip() and line != equals:  # May end with a blank or equals
                temp = line.strip().split()
                moenergies.append(utils.convertor(float(temp[2]), "hartree", "eV"))
                line = next(inputfile)
            self.nmo = len(moenergies)
            if self.betamoenergies:
                self.moenergies.append(moenergies)
                self.betamoenergies = False
            elif self.scftype == 'gvb':
                self.moenergies = [moenergies, moenergies]
            else:
                self.moenergies = [moenergies]

        # The dipole moment is printed by default at the beginning of the wavefunction analysis,
        # but the value is in atomic units, so we need to convert to Debye. It seems pretty
        # evident that the reference point is the origin (0,0,0) which is also the center
        # of mass after reorientation at the beginning of the job, although this is not
        # stated anywhere (would be good to check).
        #
        #                                        *********************
        #                                        wavefunction analysis
        #                                        *********************
        #
        # commence analysis at     24.61 seconds
        #
        #                 dipole moments
        #
        #
        #           nuclear      electronic           total
        #
        # x       0.0000000       0.0000000       0.0000000
        # y       0.0000000       0.0000000       0.0000000
        # z       0.0000000       0.0000000       0.0000000
        #
        if line.strip() == "dipole moments":

            # In older version there is only one blank line before the header,
            # and newer version there are two.
            self.skip_line(inputfile, 'blank')
            line = next(inputfile)
            if not line.strip():
                line = next(inputfile)
            self.skip_line(inputfile, 'blank')

            dipole = []
            for i in range(3):
                line = next(inputfile)
                dipole.append(float(line.split()[-1]))

            reference = [0.0, 0.0, 0.0]
            dipole = utils.convertor(numpy.array(dipole), "ebohr", "Debye")

            if not hasattr(self, 'moments'):
                self.moments = [reference, dipole]
            else:
                assert self.moments[1] == dipole

        # Net atomic charges are not printed at all, it seems,
        # but you can get at them from nuclear charges and
        # electron populations, which are printed like so:
        #
        #  ---------------------------------------
        #  mulliken and lowdin population analyses
        #  ---------------------------------------
        #
        # ----- total gross population in aos ------
        #
        # 1  1  c s         1.99066     1.98479
        # 2  1  c s         1.14685     1.04816
        # ...
        #
        #  ----- total gross population on atoms ----
        #
        # 1  c            6.0     6.00446     5.99625
        # 2  c            6.0     6.00446     5.99625
        # 3  c            6.0     6.07671     6.04399
        # ...
        if line[10:49] == "mulliken and lowdin population analyses":

            if not hasattr(self, "atomcharges"):
                self.atomcharges = {}

            while not "total gross population on atoms" in line:
                line = next(inputfile)

            self.skip_line(inputfile, 'blank')

            line = next(inputfile)
            mulliken, lowdin = [], []
            while line.strip():
                nuclear = float(line.split()[2])
                mulliken.append(nuclear - float(line.split()[3]))
                lowdin.append(nuclear - float(line.split()[4]))
                line = next(inputfile)

            self.atomcharges["mulliken"] = mulliken
            self.atomcharges["lowdin"] = lowdin

        #          ----- spinfree UHF natural orbital occupations -----
        #
        #               2.0000000     2.0000000     2.0000000     2.0000000     2.0000000     2.0000000     2.0000000
        #
        #               2.0000000     2.0000000     2.0000000     2.0000000     2.0000000     1.9999997     1.9999997
        # ...
        if "natural orbital occupations" in line:

            occupations = []

            self.skip_line(inputfile, "blank")
            line = inputfile.next()

            while line.strip():
                occupations += map(float, line.split())

                self.skip_line(inputfile, "blank")
                line = inputfile.next()

            self.set_attribute('nooccnos', occupations)

        if line[:33] == ' end of  G A M E S S   program at':
            self.metadata['success'] = True
def main(args):
    import os
    import sys
    from utils import make_file_iterator
    import cclib
    import numpy as np
    if args.actually_plot:
        import matplotlib as mpl
        mpl.use('Agg')
        import matplotlib.pyplot as plt


    matches_ccman1 = (
        'DOING EOM-CCSD CALCULATIONS',
        'GENUINE CIS CODE',
        'GENUINE CISD CODE',
        'GENUINE CISDT CODE'
    )

    matches_correlated_gs_energies_cdman = (
        # Shows up in RI-CIS(D) calculations.
        'RIMP2         total energy',
        # Shows up in SOS-CIS(D) and SOS-CIS(D0) calculations.
        'Total SOS-MP2 energy',
        # Shows up in CIS(D) calculations (without RI).
        'Total ground state energy'
    )

    matches_orca_cis = (
        'CIS-EXCITED STATES',
        'CIS EXCITED STATES'
    )

    if args.actually_plot:
        fig, ax = plt.subplots()
        cmap = plt.cm.get_cmap('nipy_spectral')
        njobs = len(args.inputfile)
        ax.set_color_cycle([cmap(i) for i in np.linspace(0, 1, njobs)])

    for inputfilename in args.inputfile:

        # We aren't parsing the files, since they might fail; just
        # trying to determine which program they came from for now.
        job = cclib.io.ccopen(inputfilename)
        stub = os.path.splitext(inputfilename)[0]
        inputfile = make_file_iterator(inputfilename)
        unrestricted = True
        do_quartet = False

        if type(job) == cclib.parser.qchemparser.QChem:
            print('Q-Chem:', stub)
            for line in inputfile:
                # Are we using a ROHF reference?
                if ' restricted ' in line:
                    unrestricted = False
                # This is the RHF/ROHF/UHF ground state energy.
                if 'Total energy in the final basis set' in line:
                    energy_gs = float(line.split()[-1])
                # Do we want a correlated ground state energy instead?
                # (RI-MP2, MP2, CCSD, ...)
                if args.correlated_gs_energy:
                    # Runs that call cdman will match here.
                    if any(match in line for match in matches_correlated_gs_energies_cdman):
                        energy_gs = float(line.split()[-2])
                    # Runs that call ccman/ccman2 will match here.
                    if 'ccsd total energy' in line.lower():
                        energy_gs = float(line.split()[-1])
                if 'Doublet and Quartet excitation energies requested' in line:
                    do_quartet = True
                if 'CIS Excitation Energies' in line:
                    energies_es = qchem_get_cis_energies(inputfile, do_quartet)
                if 'TDDFT/TDA Excitation Energies' in line:
                    energies_es = qchem_get_cis_energies(inputfile)
                if line.strip() == 'CIS(D) Excitation Energies':
                    energies_es = qchem_get_cisd_energies(inputfile, energy_gs)
                if line.strip() == 'RI-CIS(D) Excitation Energies':
                    energies_es = qchem_get_ricisd_energies(inputfile)
                if line.strip() == 'SOS-CIS(D) Excitation Energies':
                    energies_es = qchem_get_ricisd_energies(inputfile)
                if line.strip() == 'SOS-CIS(D0) Excitation Energies':
                    energies_es = qchem_get_cis_energies(inputfile, unrestricted)
                if 'Solving for EOM-CCSD' in line:
                    energies_es = qchem_get_eom_energies_ccman2(inputfile)
                if any(line.strip() == match for match in matches_ccman1):
                    energies_es = qchem_get_eom_energies_ccman1(inputfile)

        elif type(job) == cclib.parser.orcaparser.ORCA:
            print('ORCA:', stub)
            for line in inputfile:
                if 'Total Energy' in line:
                    energy_gs = float(line.split()[3])
                if 'Generation of triplets' in line:
                    do_triplet = on_off_bool(line.split()[-1])
                if any(match in line for match in matches_orca_cis):
                    energies_es = orca_get_cis_ex_energies(inputfile)
                if 'TD-DFT/TDA EXCITED STATES' in line:
                    energies_es = orca_get_cis_ex_energies(inputfile)

        elif type(job) == cclib.parser.psiparser.Psi:
            print('Psi:', stub)
            pass

        else:
            sys.exit()

        print('Ground state energy (hartree):')
        print(energy_gs)

        try:

            print('Excited state energies:')
            print(energies_es)

            if type(job) == cclib.parser.orcaparser.ORCA:
                excitation_energies = energies_es
            else:
                excitation_energies = [convertor(energy_es - energy_gs, 'hartree', 'eV')
                                       for energy_es in energies_es]

            # print('Excitation energies:')
            # print(excitation_energies)

            nstates = len(excitation_energies)
            states = range(1, nstates + 1)

            if args.actually_plot:
                ax.plot(states, excitation_energies[:nstates], label=stub, marker='o')

        except:
            print("Something's wrong!")

    if args.actually_plot:
        ax.set_xlabel('excited state #')
        if args.correlated_gs_energy:
            ax.set_ylabel(r'$E_{\mathrm{state}} - E_{\mathrm{GS}}$ (eV)')
        else:
            ax.set_ylabel(r'$E_{\mathrm{state}} - E_{\mathrm{GS}}^{\mathrm{HF}}$ (eV)')
        ax.set_xticks(states)
        # Which one do I choose? Nobody knows! I can never remember.
        ax.set_xlim(1, nstates)
        # ax.set_xbound(1, nstates)
        ax.legend(loc='best', fancybox=True, framealpha=0.50, fontsize='x-small')
        if args.correlated_gs_energy:
            fig.savefig('plot_corr.pdf', bbox_inches='tight')
        else:
            fig.savefig('plot.pdf', bbox_inches='tight')
Example #46
0
    def extract(self, inputfile, line):
        """Extract information from the file object inputfile."""

        if "Start Module: gateway" in line:
            self.gateway_module_count += 1

        if self.gateway_module_count > 1:
            return

        # Extract the version number and optionally the Git tag and hash.
        if "version" in line:
            match = re.search(r"\s{2,}version\s(\d*\.\d*)", line)
            if match:
                package_version = match.groups()[0]
                self.metadata["package_version"] = package_version
        # Don't add revision information to the main package version for now.
        if "tag" in line:
            tag = line.split()[-1]
        if "build" in line:
            match = re.search(r"\*\s*build\s(\S*)\s*\*", line)
            if match:
                revision = match.groups()[0]

        ## This section is present when executing &GATEWAY.
        # ++    Molecular structure info:
        #       -------------------------

        #                     ************************************************
        #                     **** Cartesian Coordinates / Bohr, Angstrom ****
        #                     ************************************************

        #      Center  Label                x              y              z                     x              y              z
        #         1      C1               0.526628      -2.582937       0.000000              0.278679      -1.366832       0.000000
        #         2      C2               2.500165      -0.834760       0.000000              1.323030      -0.441736       0.000000
        if line[25:63] == 'Cartesian Coordinates / Bohr, Angstrom':
            if not hasattr(self, 'atomnos'):
                self.atomnos = []

            self.skip_lines(inputfile, ['stars', 'blank', 'header'])

            line = next(inputfile)

            atomelements = []
            atomcoords = []

            while line.strip() not in ('', '--'):
                sline = line.split()
                atomelement = sline[1].rstrip(string.digits).title()
                atomelements.append(atomelement)
                atomcoords.append(list(map(float, sline[5:])))
                line = next(inputfile)

            self.append_attribute('atomcoords', atomcoords)

            if self.atomnos == []:
                self.atomnos = [self.table.number[ae.title()] for ae in atomelements]

            if not hasattr(self, 'natom'):
                self.set_attribute('natom', len(self.atomnos))

        ## This section is present when executing &SCF.
        #  ++    Orbital specifications:
        #  -----------------------

        #  Symmetry species               1

        #  Frozen orbitals                0
        #  Occupied orbitals              3
        #  Secondary orbitals            77
        #  Deleted orbitals               0
        #  Total number of orbitals      80
        #  Number of basis functions     80
        #  --
        if line[:29] == '++    Orbital specifications:':

            self.skip_lines(inputfile, ['dashes', 'blank'])
            line = next(inputfile)

            symmetry_count = 1
            while not line.startswith('--'):
                if line.strip().startswith('Symmetry species'):
                    symmetry_count = int(line.split()[-1])
                if line.strip().startswith('Total number of orbitals'):
                    nmos = line.split()[-symmetry_count:]
                    self.set_attribute('nmo', sum(map(int, nmos)))
                if line.strip().startswith('Number of basis functions'):
                    nbasis = line.split()[-symmetry_count:]
                    self.set_attribute('nbasis', sum(map(int, nbasis)))

                line = next(inputfile)

        if line.strip().startswith(('Molecular charge', 'Total molecular charge')):
            self.set_attribute('charge', int(float(line.split()[-1])))

        #  ++    Molecular charges:
        #  ------------------

        #  Mulliken charges per centre and basis function type
        #  ---------------------------------------------------

        #         C1
        #  1s     2.0005
        #  2s     2.0207
        #  2px    0.0253
        #  2pz    0.1147
        #  2py    1.8198
        #  *s    -0.0215
        #  *px    0.0005
        #  *pz    0.0023
        #  *py    0.0368
        #  *d2+   0.0002
        #  *d1+   0.0000
        #  *d0    0.0000
        #  *d1-   0.0000
        #  *d2-   0.0000
        #  *f3+   0.0000
        #  *f2+   0.0001
        #  *f1+   0.0000
        #  *f0    0.0001
        #  *f1-   0.0001
        #  *f2-   0.0000
        #  *f3-   0.0003
        #  *g4+   0.0000
        #  *g3+   0.0000
        #  *g2+   0.0000
        #  *g1+   0.0000
        #  *g0    0.0000
        #  *g1-   0.0000
        #  *g2-   0.0000
        #  *g3-   0.0000
        #  *g4-   0.0000
        #  Total  6.0000

        #  N-E    0.0000

        #  Total electronic charge=    6.000000

        #  Total            charge=    0.000000
        #--
        if line[:24] == '++    Molecular charges:':

            atomcharges = []

            while line[6:29] != 'Total electronic charge':
                line = next(inputfile)
                if line[6:9] == 'N-E':
                    atomcharges.extend(map(float, line.split()[1:]))

            # Molcas only performs Mulliken population analysis.
            self.set_attribute('atomcharges', {'mulliken': atomcharges})

            # Ensure the charge printed here is identical to the
            # charge printed before entering the SCF.
            self.skip_line(inputfile, 'blank')
            line = next(inputfile)
            assert line[6:30] == 'Total            charge='
            if hasattr(self, 'charge'):
                assert int(float(line.split()[2])) == self.charge

        # This section is present when executing &SCF
        # This section parses the total SCF Energy.
        # *****************************************************************************************************************************
        # *                                                                                                                           *
        # *                                             SCF/KS-DFT Program, Final results                                             *
        # *                                                                                                                           *
        # *                                                                                                                           *
        # *                                                                                                                           *
        # *                                                       Final Results                                                       *
        # *                                                                                                                           *
        # *****************************************************************************************************************************

        # ::    Total SCF energy                                -37.6045426484
        if line[:22] == '::    Total SCF energy' or line[:25] == '::    Total KS-DFT energy':
            if not hasattr(self, 'scfenergies'):
                self.scfenergies = []
            scfenergy = float(line.split()[-1])
            self.scfenergies.append(utils.convertor(scfenergy, 'hartree', 'eV'))

        ## Parsing the scftargets in this section
        #  ++    Optimization specifications:
        #  ----------------------------

        #  SCF Algorithm: Conventional
        #  Minimized density differences are used
        #  Number of density matrices in core                9
        #  Maximum number of NDDO SCF iterations           400
        #  Maximum number of HF  SCF iterations            400
        #  Threshold for SCF energy change            0.10E-08
        #  Threshold for density matrix               0.10E-03
        #  Threshold for Fock matrix                  0.15E-03
        #  Threshold for linear dependence            0.10E-08
        #  Threshold at which DIIS is turned on       0.15E+00
        #  Threshold at which QNR/C2DIIS is turned on 0.75E-01
        #  Threshold for Norm(delta) (QNR/C2DIIS)     0.20E-04
        if line[:34] == '++    Optimization specifications:':
            self.skip_lines(inputfile, ['d', 'b'])
            line = next(inputfile)
            if line.strip().startswith('SCF'):
                scftargets = []
                self.skip_lines(inputfile,
                                ['Minimized', 'Number', 'Maximum', 'Maximum'])
                lines = [next(inputfile) for i in range(7)]
                targets = [
                    'Threshold for SCF energy change',
                    'Threshold for density matrix',
                    'Threshold for Fock matrix',
                    'Threshold for Norm(delta)',
                ]
                for y in targets:
                    scftargets.extend([float(x.split()[-1]) for x in lines if y in x])

                self.append_attribute('scftargets', scftargets)

        #  ++ Convergence information
        #                                     SCF        iterations: Energy and convergence statistics
        #
        #  Iter     Tot. SCF       One-electron     Two-electron   Energy   Max Dij or  Max Fij    DNorm      TNorm     AccCon    Time
        #             Energy          Energy          Energy       Change   Delta Norm                                          in Sec.
        #     1    -36.83817703    -50.43096166     13.59278464  0.00E+00   0.16E+00*  0.27E+01*   0.30E+01   0.33E+02   NoneDa    0.
        #     2    -36.03405202    -45.74525152      9.71119950  0.80E+00*  0.14E+00*  0.93E-02*   0.26E+01   0.43E+01   Damp      0.
        #     3    -37.08936118    -48.41536598     11.32600480 -0.11E+01*  0.12E+00*  0.91E-01*   0.97E+00   0.16E+01   Damp      0.
        #     4    -37.31610460    -50.54103969     13.22493509 -0.23E+00*  0.11E+00*  0.96E-01*   0.72E+00   0.27E+01   Damp      0.
        #     5    -37.33596239    -49.47021484     12.13425245 -0.20E-01*  0.59E-01*  0.59E-01*   0.37E+00   0.16E+01   Damp      0.
        # ...
        #           Convergence after 26 Macro Iterations
        # --
        if line[46:91] == 'iterations: Energy and convergence statistics':

            self.skip_line(inputfile, 'blank')

            while line.split() != ['Energy', 'Energy', 'Energy', 'Change', 'Delta', 'Norm', 'in', 'Sec.']:
                line = next(inputfile)

            iteration_regex = ("^([0-9]+)"                                  # Iter
                               "( [ \-0-9]*\.[0-9]{6,9})"                   # Tot. SCF Energy
                               "( [ \-0-9]*\.[0-9]{6,9})"                   # One-electron Energy
                               "( [ \-0-9]*\.[0-9]{6,9})"                   # Two-electron Energy
                               "( [ \-0-9]*\.[0-9]{2}E[\-\+][0-9]{2}\*?)"   # Energy Change
                               "( [ \-0-9]*\.[0-9]{2}E[\-\+][0-9]{2}\*?)"   # Max Dij or Delta Norm
                               "( [ \-0-9]*\.[0-9]{2}E[\-\+][0-9]{2}\*?)"   # Max Fij
                               "( [ \-0-9]*\.[0-9]{2}E[\-\+][0-9]{2}\*?)"   # DNorm
                               "( [ \-0-9]*\.[0-9]{2}E[\-\+][0-9]{2}\*?)"   # TNorm
                               "( [ A-Za-z0-9]*)"                           # AccCon
                               "( [ \.0-9]*)$")                             # Time in Sec.

            scfvalues = []
            line = next(inputfile)
            while not line.strip().startswith("Convergence"):

                match = re.match(iteration_regex, line.strip())
                if match:
                    groups = match.groups()
                    cols = [g.strip() for g in match.groups()]
                    cols = [c.replace('*', '') for c in cols]

                    energy = float(cols[4])
                    density = float(cols[5])
                    fock = float(cols[6])
                    dnorm = float(cols[7])
                    scfvalues.append([energy, density, fock, dnorm])

                if line.strip() == "--":
                    self.logger.warning('File terminated before end of last SCF!')
                    break

                line = next(inputfile)

            self.append_attribute('scfvalues', scfvalues)

        #  Harmonic frequencies in cm-1
        #
        #  IR Intensities in km/mol
        #
        #                         1         2         3         4         5         6
        #
        #      Frequency:       i60.14    i57.39    128.18    210.06    298.24    309.65
        #
        #      Intensity:    3.177E-03 2.129E-06 4.767E-01 2.056E-01 6.983E-07 1.753E-07
        #      Red. mass:      2.42030   2.34024   2.68044   3.66414   2.61721   3.34904
        #
        #      C1         x   -0.00000   0.00000   0.00000  -0.05921   0.00000  -0.06807
        #      C1         y    0.00001  -0.00001  -0.00001   0.00889   0.00001  -0.02479
        #      C1         z   -0.03190   0.04096  -0.03872   0.00001  -0.12398  -0.00002
        #      C2         x   -0.00000   0.00001   0.00000  -0.06504   0.00000  -0.03487
        #      C2         y    0.00000  -0.00000  -0.00000   0.01045   0.00001  -0.05659
        #      C2         z   -0.03703  -0.03449  -0.07269   0.00000  -0.07416  -0.00001
        #      C3         x   -0.00000   0.00001   0.00000  -0.06409  -0.00001   0.05110
        #      C3         y   -0.00000   0.00001   0.00000   0.00152   0.00000  -0.03263
        #      C3         z   -0.03808  -0.08037  -0.07267  -0.00001   0.07305   0.00000
        # ...
        #      H20        y    0.00245  -0.00394   0.03215   0.03444  -0.10424  -0.10517
        #      H20        z    0.00002  -0.00001   0.00000  -0.00000  -0.00000   0.00000
        #
        #
        #
        # ++ Thermochemistry
        if line[1:29] == 'Harmonic frequencies in cm-1':

            self.skip_line(inputfile, 'blank')
            line = next(inputfile)

            while 'Thermochemistry' not in line:

                if 'Frequency:' in line:
                    if not hasattr(self, 'vibfreqs'):
                        self.vibfreqs = []
                    vibfreqs = [float(i.replace('i', '-')) for i in line.split()[1:]]
                    self.vibfreqs.extend(vibfreqs)

                if 'Intensity:' in line:
                    if not hasattr(self, 'vibirs'):
                        self.vibirs = []
                    vibirs = map(float, line.split()[1:])
                    self.vibirs.extend(vibirs)

                if 'Red.' in line:
                    self.skip_line(inputfile, 'blank')
                    line = next(inputfile)
                    if not hasattr(self, 'vibdisps'):
                        self.vibdisps = []
                    disps = []
                    for n in range(3*self.natom):
                        numbers = [float(s) for s in line[17:].split()]
                        # The atomindex should start at 0 instead of 1.
                        atomindex = int(re.search(r'\d+$', line.split()[0]).group()) - 1
                        numbermodes = len(numbers)
                        if len(disps) == 0:
                            # Appends empty array of the following
                            # dimensions (numbermodes, natom, 0) to disps.
                            for mode in range(numbermodes):
                                disps.append([[] for x in range(0, self.natom)])
                        for mode in range(numbermodes):
                            disps[mode][atomindex].append(numbers[mode])
                        line = next(inputfile)
                    self.vibdisps.extend(disps)

                line = next(inputfile)

        ## Parsing thermochemistry attributes here
        #  ++ Thermochemistry
        #
        #   *********************
        #   *                   *
        #   *  THERMOCHEMISTRY  *
        #   *                   *
        #   *********************
        #
        #   Mass-centered Coordinates (Angstrom):
        #   ***********************************************************
        # ...
        #   *****************************************************
        #   Temperature =     0.00 Kelvin, Pressure =   1.00 atm
        #   -----------------------------------------------------
        #   Molecular Partition Function and Molar Entropy:
        #                          q/V (M**-3)    S(kcal/mol*K)
        #   Electronic            0.100000D+01        0.000
        #   Translational         0.100000D+01        0.000
        #   Rotational            0.100000D+01        2.981
        #   Vibrational           0.100000D+01        0.000
        #   TOTAL                 0.100000D+01        2.981
        #
        #   Thermal contributions to INTERNAL ENERGY:
        #   Electronic           0.000 kcal/mol      0.000000 au.
        #   Translational        0.000 kcal/mol      0.000000 au.
        #   Rotational           0.000 kcal/mol      0.000000 au.
        #   Vibrational        111.885 kcal/mol      0.178300 au.
        #   TOTAL              111.885 kcal/mol      0.178300 au.
        #
        #   Thermal contributions to
        #   ENTHALPY           111.885 kcal/mol      0.178300 au.
        #   GIBBS FREE ENERGY  111.885 kcal/mol      0.178300 au.
        #
        #   Sum of energy and thermal contributions
        #   INTERNAL ENERGY                       -382.121931 au.
        #   ENTHALPY                              -382.121931 au.
        #   GIBBS FREE ENERGY                     -382.121931 au.
        #   -----------------------------------------------------
        # ...
        #   ENTHALPY                              -382.102619 au.
        #   GIBBS FREE ENERGY                     -382.179819 au.
        #   -----------------------------------------------------
        #  --
        #
        #  ++    Isotopic shifts:
        if line[4:19] == 'THERMOCHEMISTRY':

            temperature_values = []
            pressure_values = []
            entropy_values = []
            internal_energy_values = []
            enthalpy_values = []
            free_energy_values = []

            while 'Isotopic' not in line:

                if line[1:12] == 'Temperature':
                    temperature_values.append(float(line.split()[2]))
                    pressure_values.append(float(line.split()[6]))

                if line[1:48] == 'Molecular Partition Function and Molar Entropy:':
                    while 'TOTAL' not in line:
                        line = next(inputfile)
                    entropy_values.append(utils.convertor(float(line.split()[2]), 'kcal/mol', 'hartree'))

                if line[1:40] == 'Sum of energy and thermal contributions':
                    internal_energy_values.append(float(next(inputfile).split()[2]))
                    enthalpy_values.append(float(next(inputfile).split()[1]))
                    free_energy_values.append(float(next(inputfile).split()[3]))

                line = next(inputfile)
            # When calculations for more than one temperature value are
            # performed, the values corresponding to room temperature (298.15 K)
            # are returned and if no calculations are performed for 298.15 K, then
            # the values corresponding last temperature value are returned.
            index = -1
            if 298.15 in temperature_values:
                index = temperature_values.index(298.15)

            self.set_attribute('temperature', temperature_values[index])
            if len(temperature_values) > 1:
                self.logger.warning('More than 1 values of temperature found')

            self.set_attribute('pressure', pressure_values[index])
            if len(pressure_values) > 1:
                self.logger.warning('More than 1 values of pressure found')

            self.set_attribute('entropy', entropy_values[index])
            if len(entropy_values) > 1:
                self.logger.warning('More than 1 values of entropy found')

            self.set_attribute('enthalpy', enthalpy_values[index])
            if len(enthalpy_values) > 1:
                self.logger.warning('More than 1 values of enthalpy found')

            self.set_attribute('freeenergy', free_energy_values[index])
            if len(free_energy_values) > 1:
                self.logger.warning('More than 1 values of freeenergy found')

        ## Parsing Geometrical Optimization attributes in this section.
        #  ++       Slapaf input parameters:
        #  ------------------------
        #
        # Max iterations:                            2000
        # Convergence test a la Schlegel.
        # Convergence criterion on gradient/para.<=: 0.3E-03
        # Convergence criterion on step/parameter<=: 0.3E-03
        # Convergence criterion on energy change <=: 0.0E+00
        # Max change of an internal coordinate:     0.30E+00
        # ...
        # ...
        #  **********************************************************************************************************************
        #  *                                    Energy Statistics for Geometry Optimization                                     *
        #  **********************************************************************************************************************
        #                          Energy     Grad      Grad              Step                 Estimated   Geom       Hessian
        #  Iter      Energy       Change     Norm      Max    Element    Max     Element     Final Energy Update Update   Index
        #    1   -382.30023222  0.00000000 0.107221  0.039531 nrc047   0.085726  nrc047     -382.30533799 RS-RFO  None      0
        #    2   -382.30702964 -0.00679742 0.043573  0.014908 nrc001   0.068195  nrc001     -382.30871333 RS-RFO  BFGS      0
        #    3   -382.30805348 -0.00102384 0.014883  0.005458 nrc010  -0.020973  nrc001     -382.30822089 RS-RFO  BFGS      0
        # ...
        # ...
        #   18   -382.30823419 -0.00000136 0.001032  0.000100 nrc053   0.012319  nrc053     -382.30823452 RS-RFO  BFGS      0
        #   19   -382.30823198  0.00000221 0.001051 -0.000092 nrc054   0.066565  nrc053     -382.30823822 RS-RFO  BFGS      0
        #   20   -382.30820252  0.00002946 0.001132 -0.000167 nrc021  -0.064003  nrc053     -382.30823244 RS-RFO  BFGS      0
        #
        #         +----------------------------------+----------------------------------+
        #         +    Cartesian Displacements       +    Gradient in internals         +
        #         +  Value      Threshold Converged? +  Value      Threshold Converged? +
        #   +-----+----------------------------------+----------------------------------+
        #   + RMS + 5.7330E-02  1.2000E-03     No    + 1.6508E-04  3.0000E-04     Yes   +
        #   +-----+----------------------------------+----------------------------------+
        #   + Max + 1.2039E-01  1.8000E-03     No    + 1.6711E-04  4.5000E-04     Yes   +
        #   +-----+----------------------------------+----------------------------------+
        if 'Convergence criterion on energy change' in line:
            self.energy_threshold = float(line.split()[6])
            # If energy change threshold equals zero,
            # then energy change is not a criteria for convergence.
            if self.energy_threshold == 0:
                self.energy_threshold = numpy.inf

        if 'Energy Statistics for Geometry Optimization' in line:
            if not hasattr(self, 'geovalues'):
                self.geovalues = []

            self.skip_lines(inputfile, ['stars', 'header'])
            line = next(inputfile)
            assert 'Iter      Energy       Change     Norm' in line
            # A variable keeping track of ongoing iteration.
            iter_number = len(self.geovalues) + 1
            # Iterate till blank line.
            while line.split() != []:
                for i in range(iter_number):
                    line = next(inputfile)
                self.geovalues.append([float(line.split()[2])])
                line = next(inputfile)
            # Along with energy change, RMS and Max values of change in
            # Cartesian Diaplacement and Gradients are used as optimization
            # criteria.
            self.skip_lines(inputfile, ['border', 'header', 'header', 'border'])
            line = next(inputfile)
            assert '+ RMS +' in line
            line_rms = line.split()
            line = next(inputfile)
            line_max = next(inputfile).split()
            if not hasattr(self, 'geotargets'):
                # The attribute geotargets is an array consisting of the following
                # values: [Energy threshold, Max Gradient threshold, RMS Gradient threshold, \
                #          Max Displacements threshold, RMS Displacements threshold].
                max_gradient_threshold = float(line_max[8])
                rms_gradient_threshold = float(line_rms[8])
                max_displacement_threshold = float(line_max[4])
                rms_displacement_threshold = float(line_rms[4])
                self.geotargets = [self.energy_threshold, max_gradient_threshold, rms_gradient_threshold, max_displacement_threshold, rms_displacement_threshold]

            max_gradient_change = float(line_max[7])
            rms_gradient_change = float(line_rms[7])
            max_displacement_change = float(line_max[3])
            rms_displacement_change = float(line_rms[3])
            self.geovalues[iter_number - 1].extend([max_gradient_change, rms_gradient_change, max_displacement_change, rms_displacement_change])

        #   *********************************************************
        #   * Nuclear coordinates for the next iteration / Angstrom *
        #   *********************************************************
        #    ATOM              X               Y               Z
        #    C1               0.235560       -1.415847        0.012012
        #    C2               1.313797       -0.488199        0.015149
        #    C3               1.087050        0.895510        0.014200
        # ...
        # ...
        #    H19             -0.021327       -4.934915       -0.029355
        #    H20             -1.432030       -3.721047       -0.039835
        #
        #  --
        if 'Nuclear coordinates for the next iteration / Angstrom' in line:
            self.skip_lines(inputfile, ['s', 'header'])
            line = next(inputfile)

            atomcoords = []
            while line.split() != []:
                atomcoords.append([float(c) for c in line.split()[1:]])
                line = next(inputfile)

            if len(atomcoords) == self.natom:
                self.atomcoords.append(atomcoords)
            else:
                self.logger.warning(
                        "Parsed coordinates not consistent with previous, skipping. "
                        "This could be due to symmetry being turned on during the job. "
                        "Length was %i, now found %i. New coordinates: %s"
                        % (len(self.atomcoords[-1]), len(atomcoords), str(atomcoords)))

        #  **********************************************************************************************************************
        #  *                                    Energy Statistics for Geometry Optimization                                     *
        #  **********************************************************************************************************************
        #                         Energy     Grad      Grad              Step                 Estimated   Geom       Hessian
        #  Iter      Energy       Change     Norm      Max    Element    Max     Element     Final Energy Update Update   Index
        #    1   -382.30023222  0.00000000 0.107221  0.039531 nrc047   0.085726  nrc047     -382.30533799 RS-RFO  None      0
        # ...
        # ...
        #   23   -382.30823115 -0.00000089 0.001030  0.000088 nrc053   0.000955  nrc053     -382.30823118 RS-RFO  BFGS      0
        #
        #         +----------------------------------+----------------------------------+
        #         +    Cartesian Displacements       +    Gradient in internals         +
        #         +  Value      Threshold Converged? +  Value      Threshold Converged? +
        #   +-----+----------------------------------+----------------------------------+
        #   + RMS + 7.2395E-04  1.2000E-03     Yes   + 2.7516E-04  3.0000E-04     Yes   +
        #   +-----+----------------------------------+----------------------------------+
        #   + Max + 1.6918E-03  1.8000E-03     Yes   + 8.7768E-05  4.5000E-04     Yes   +
        #   +-----+----------------------------------+----------------------------------+
        #
        #   Geometry is converged in  23 iterations to a Minimum Structure
        if 'Geometry is converged' in line:
            if not hasattr(self, 'optdone'):
                self.optdone = []
            self.optdone.append(len(self.atomcoords))

        #   *********************************************************
        #   * Nuclear coordinates of the final structure / Angstrom *
        #   *********************************************************
        #    ATOM              X               Y               Z
        #    C1               0.235547       -1.415838        0.012193
        #    C2               1.313784       -0.488201        0.015297
        #    C3               1.087036        0.895508        0.014333
        # ...
        # ...
        #    H19             -0.021315       -4.934913       -0.029666
        #    H20             -1.431994       -3.721026       -0.041078
        if 'Nuclear coordinates of the final structure / Angstrom' in line:
            self.skip_lines(inputfile, ['s', 'header'])
            line = next(inputfile)

            atomcoords = []

            while line.split() != []:
                atomcoords.append([float(c) for c in line.split()[1:]])
                line = next(inputfile)

            if len(atomcoords) == self.natom:
                self.atomcoords.append(atomcoords)
            else:
                self.logger.error(
                        'Number of atoms (%d) in parsed atom coordinates '
                        'is smaller than previously (%d), possibly due to '
                        'symmetry. Ignoring these coordinates.'
                        % (len(atomcoords), self.natom))

        #        All orbitals with orbital energies smaller than  E(LUMO)+0.5 are printed
        #
        #  ++    Molecular orbitals:
        #        -------------------
        #
        #        Title: RKS-DFT orbitals
        #
        #        Molecular orbitals for symmetry species 1: a
        #
        #            Orbital        1         2         3         4         5         6         7         8         9        10
        #            Energy      -10.0179  -10.0179  -10.0075  -10.0075  -10.0066  -10.0066  -10.0056  -10.0055   -9.9919   -9.9919
        #            Occ. No.      2.0000    2.0000    2.0000    2.0000    2.0000    2.0000    2.0000    2.0000    2.0000    2.0000
        #
        #          1 C1    1s     -0.6990    0.6989    0.0342    0.0346    0.0264   -0.0145   -0.0124   -0.0275   -0.0004   -0.0004
        #          2 C1    2s     -0.0319    0.0317   -0.0034   -0.0033   -0.0078    0.0034    0.0041    0.0073   -0.0002   -0.0002
        # ...
        # ...
        #         58 H18   1s      0.2678
        #         59 H19   1s     -0.2473
        #         60 H20   1s      0.1835
        #  --
        if '++    Molecular orbitals:' in line:

            self.skip_lines(inputfile, ['d', 'b'])
            line = next(inputfile)

            # We don't currently support parsing natural orbitals or active space orbitals.
            if 'Natural orbitals' not in line and "Pseudonatural" not in line:
                self.skip_line(inputfile, 'b')

                # Symmetry is not currently supported, so this line can have one form.
                while 'Molecular orbitals for symmetry species 1: a' not in line.strip():
                    line = next(inputfile)

                # Symmetry is not currently supported, so this line can have one form.
                if line.strip() != 'Molecular orbitals for symmetry species 1: a':
                    return
                
                line = next(inputfile)
                moenergies = []
                homos = 0
                mocoeffs = []
                while line[:2] != '--':
                    line = next(inputfile)
                    if line.strip().startswith('Orbital'):
                        orbital_index = line.split()[1:]
                        for i in orbital_index:
                            mocoeffs.append([])

                    if 'Energy' in line:
                        energies = [utils.convertor(float(x), 'hartree', 'eV') for x in line.split()[1:]]
                        moenergies.extend(energies)

                    if 'Occ. No.' in line:
                        for i in line.split()[2:]:
                            if float(i) != 0:
                                homos += 1

                    aonames = []
                    tokens = line.split()
                    if tokens and tokens[0] == '1':
                        while tokens and tokens[0] != '--':
                            aonames.append("{atom}_{orbital}".format(atom=tokens[1], orbital=tokens[2]))
                            info = tokens[3:]
                            j = 0
                            for i in orbital_index:
                                mocoeffs[int(i)-1].append(float(info[j]))
                                j += 1
                            line = next(inputfile)
                            tokens = line.split()
                        self.set_attribute('aonames', aonames)

                if len(moenergies) != self.nmo:
                    moenergies.extend([numpy.nan for x in range(self.nmo - len(moenergies))])

                self.append_attribute('moenergies', moenergies)

                if not hasattr(self, 'homos'):
                    self.homos = []
                self.homos.extend([homos-1])

                while len(mocoeffs) < self.nmo:
                    nan_array = [numpy.nan for i in range(self.nbasis)]
                    mocoeffs.append(nan_array)

                self.append_attribute('mocoeffs', mocoeffs)

        ## Parsing MP energy from the &MBPT2 module.
        #  Conventional algorithm used...
        #
        #         SCF energy                           =      -74.9644564043 a.u.
        #         Second-order correlation energy      =       -0.0364237923 a.u.
        #
        #         Total energy                         =      -75.0008801966 a.u.
        #         Reference weight ( Cref**2 )         =        0.98652
        #
        #  ::    Total MBPT2 energy                              -75.0008801966
        #
        #
        #         Zeroth-order energy (E0)             =      -36.8202538520 a.u.
        #
        #         Shanks-type energy S1(E)             =      -75.0009150108 a.u.
        if 'Total MBPT2 energy' in line:
            mpenergies = []
            mpenergies.append(utils.convertor(self.float(line.split()[4]), 'hartree', 'eV'))
            if not hasattr(self, 'mpenergies'):
                self.mpenergies = []
            self.mpenergies.append(mpenergies)

        # Parsing data ccenergies from &CCSDT module.
        #  --- Start Module: ccsdt at Thu Jul 26 14:03:23 2018 ---
        #
        #  ()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()
        #
        #                                                 &CCSDT
        # ...
        # ...
        #          14          -75.01515915      -0.05070274      -0.00000029
        #          15          -75.01515929      -0.05070289      -0.00000014
        #          16          -75.01515936      -0.05070296      -0.00000007
        #       Convergence after                    17  Iterations
        #
        #
        #      Total energy (diff) :     -75.01515936      -0.00000007
        #      Correlation energy  :        -0.0507029554992
        if 'Start Module: ccsdt' in line:
            self.skip_lines(inputfile, ['b', '()', 'b'])
            line = next(inputfile)
            if '&CCSDT' in line:
                while not line.strip().startswith('Total energy (diff)'):
                    line = next(inputfile)

                ccenergies = utils.convertor(self.float(line.split()[4]), 'hartree', 'eV')
                if not hasattr(self, 'ccenergies'):
                    self.ccenergies= []
                self.ccenergies.append(ccenergies)

        #  ++    Primitive basis info:
        #        ---------------------
        #
        #
        #                      *****************************************************
        #                      ******** Primitive Basis Functions (Valence) ********
        #                      *****************************************************
        #
        #
        #   Basis set:C.AUG-CC-PVQZ.........                                                          
        #
        #                    Type         
        #                     s
        #             No.      Exponent    Contraction Coefficients
        #             1  0.339800000D+05   0.000091  -0.000019   0.000000   0.000000   0.000000   0.000000
        #             2  0.508900000D+04   0.000704  -0.000151   0.000000   0.000000   0.000000   0.000000
        # ...
        # ...
        #             29  0.424000000D+00   0.000000   1.000000
        #
        #   Number of primitives                                   93
        #   Number of basis functions                              80
        #
        #  --
        if line.startswith('++    Primitive basis info:'):
            self.skip_lines(inputfile, ['d', 'b', 'b', 's', 'header', 's', 'b'])
            line = next(inputfile)
            gbasis_array = []
            while '--' not in line and '****' not in line:
                if 'Basis set:' in line:
                    basis_element_patterns = re.findall('Basis set:([A-Za-z]{1,2})\.', line)
                    assert len(basis_element_patterns) == 1
                    basis_element = basis_element_patterns[0].title()
                    gbasis_array.append((basis_element, []))

                if 'Type' in line:
                    line = next(inputfile)
                    shell_type = line.split()[0].upper()

                    self.skip_line(inputfile, 'headers')
                    line = next(inputfile)

                    exponents = []
                    coefficients = []
                    func_array = []
                    while line.split():
                        exponents.append(self.float(line.split()[1]))
                        coefficients.append([self.float(i) for i in line.split()[2:]])
                        line = next(inputfile)

                    for i in range(len(coefficients[0])):
                        func_tuple = (shell_type, [])
                        for iexp, exp in enumerate(exponents):
                            coeff = coefficients[iexp][i]
                            if coeff != 0:
                                func_tuple[1].append((exp, coeff))
                        gbasis_array[-1][1].append(func_tuple)

                line = next(inputfile)

            atomsymbols = [self.table.element[atomno] for atomno in self.atomnos]
            self.gbasis = [[] for i in range(self.natom)]
            for element, gbasis in gbasis_array:
                mask = [element == possible_element for possible_element in atomsymbols]
                indices = [i for (i, x) in enumerate(mask) if x]
                for index in indices:
                    self.gbasis[index] = gbasis

        #  ++    Basis set information:
        #        ----------------------
        # ...
        #        Basis set label: MO.ECP.HAY-WADT.5S6P4D.3S3P2D.14E-LANL2DZ.....
        #
        #        Electronic valence basis set:
        #        ------------------
        #        Associated Effective Charge  14.000000 au
        #        Associated Actual Charge     42.000000 au
        #        Nuclear Model: Point charge
        # ...
        #
        #        Effective Core Potential specification:
        #        =======================================
        #
        #         Label   Cartesian Coordinates / Bohr
        #
        #   MO                 0.0006141610       -0.0006141610        0.0979067106
        #  --
        if '++    Basis set information:' in line:
            self.core_array = []
            basis_element = None
            ncore = 0

            while line[:2] != '--':
                if 'Basis set label' in line:
                    try:
                        basis_element = line.split()[3].split('.')[0]
                        basis_element = basis_element[0] + basis_element[1:].lower()
                    except:
                        self.logger.warning('Basis set label is missing!')
                        basis_element = ''
                if 'valence basis set:' in line.lower():
                    self.skip_line(inputfile, 'd')
                    line = next(inputfile)
                    if 'Associated Effective Charge' in line:
                        effective_charge = float(line.split()[3])
                        actual_charge = float(next(inputfile).split()[3])
                        element = self.table.element[int(actual_charge)]
                        ncore = int(actual_charge - effective_charge)
                        if basis_element:
                            assert basis_element == element
                        else:
                            basis_element = element

                if basis_element and ncore:
                    self.core_array.append((basis_element, ncore))
                    basis_element = ''
                    ncore = 0

                line = next(inputfile)
Example #47
0
 def integrate_square(self):
     boxvol = (self.spacing[0] * self.spacing[1] * self.spacing[2] *
               convertor(1, "Angstrom", "bohr")**3)
     return sum(self.data.ravel()**2) * boxvol
    parser = argparse.ArgumentParser()

    parser.add_argument('outputfilename', nargs='+')

    args = parser.parse_args()

    return args


if __name__ == '__main__':

    args = getargs()

    scfenergies = []

    for outputfilename in args.outputfilename:
        if 'cfour' in outputfilename.lower():
            program = 'CFOUR'
            scfenergy = get_energy_nocclib(outputfilename, 'E(SCF)=', 1)
        else:
            job = ccopen(outputfilename)
            program = program_names[type(job)]
            data = job.parse()
            scfenergy = convertor(data.scfenergies[0], 'eV', 'hartree')
        scfenergies.append((program, outputfilename, scfenergy))

    scfenergies = sorted(scfenergies, key=lambda x: x[2])

    for (program, outputfilename, scfenergy) in scfenergies:
        print(scfenergy, program, outputfilename)
Example #49
0
    def extract(self, inputfile, line):
        """Extract information from the file object inputfile."""

        # If a file contains multiple calculations, currently we want to print a warning
        # and skip to the end of the file, since cclib parses only the main system, which
        # is usually the largest. Here we test this by checking if scftargets has already
        # been parsed when another INPUT FILE segment is found, although this might
        # not always be the best indicator.
        if line.strip() == "(INPUT FILE)" and hasattr(self, "scftargets"):
            self.logger.warning("Skipping remaining calculations")
            inputfile.seek(0, 2)
            return

        # We also want to check to make sure we aren't parsing "Create" jobs,
        # which normally come before the calculation we actually want to parse.
        if line.strip() == "(INPUT FILE)":
            while True:
                self.updateprogress(inputfile, "Unsupported Information", self.fupdate)
                line = next(inputfile) if line.strip() == "(INPUT FILE)" else None
                if line and not line[:6] in ("Create", "create"):
                    break
                line = next(inputfile)

        version_searchstr = "Amsterdam Density Functional  (ADF)"
        if version_searchstr in line:
            startidx = line.index(version_searchstr) + len(version_searchstr)
            trimmed_line = line[startidx:].strip()[:-1]
            # The package version is normally a year with revision
            # number (such as 2013.01), but it may also be a random
            # string (such as a version control branch name).
            match = re.search(r"([\d\.]{4,7})", trimmed_line)
            if match:
                package_version = match.groups()[0]
                self.metadata["package_version"] = package_version
            else:
                # This isn't as well-defined, but the field shouldn't
                # be left empty.
                self.metadata["package_version"] = trimmed_line.strip()

        # In ADF 2014.01, there are (INPUT FILE) messages, so we need to use just
        # the lines that start with 'Create' and run until the title or something
        # else we are sure is is the calculation proper. It would be good to combine
        # this with the previous block, if possible.
        if line[:6] == "Create":
            while line[:5] != "title" and "NO TITLE" not in line:
                line = inputfile.next()

        if line[1:10] == "Symmetry:":
            info = line.split()
            if info[1] == "NOSYM":
                self.nosymflag = True

        # Use this to read the subspecies of irreducible representations.
        # It will be a list, with each element representing one irrep.
        if line.strip() == "Irreducible Representations, including subspecies":

            self.skip_line(inputfile, 'dashes')

            self.irreps = []
            line = next(inputfile)
            while line.strip() != "":
                self.irreps.append(line.split())
                line = next(inputfile)

        if line[4:13] == 'Molecule:':
            info = line.split()
            if info[1] == 'UNrestricted':
                self.unrestrictedflag = True

        if line[1:6] == "ATOMS":
        # Find the number of atoms and their atomic numbers
        # Also extract the starting coordinates (for a GeoOpt anyway)
        # and the atommasses (previously called vibmasses)
            self.updateprogress(inputfile, "Attributes", self.cupdate)

            self.atomcoords = []

            self.skip_lines(inputfile, ['header1', 'header2', 'header3'])

            atomnos = []
            atommasses = []
            atomcoords = []
            coreelectrons = []
            line = next(inputfile)
            while len(line) > 2:  # ensure that we are reading no blank lines
                info = line.split()
                element = info[1].split('.')[0]
                atomnos.append(self.table.number[element])
                atomcoords.append(list(map(float, info[2:5])))
                coreelectrons.append(int(float(info[5]) - float(info[6])))
                atommasses.append(float(info[7]))
                line = next(inputfile)
            self.atomcoords.append(atomcoords)

            self.set_attribute('natom', len(atomnos))
            self.set_attribute('atomnos', atomnos)
            self.set_attribute('atommasses', atommasses)
            self.set_attribute('coreelectrons', coreelectrons)

        if line[1:10] == "FRAGMENTS":
            header = next(inputfile)

            self.frags = []
            self.fragnames = []

            line = next(inputfile)
            while len(line) > 2:  # ensure that we are reading no blank lines
                info = line.split()

                if len(info) == 7:  # fragment name is listed here
                    self.fragnames.append("%s_%s" % (info[1], info[0]))
                    self.frags.append([])
                    self.frags[-1].append(int(info[2]) - 1)

                elif len(info) == 5:  # add atoms into last fragment
                    self.frags[-1].append(int(info[0]) - 1)

                line = next(inputfile)

        # Extract charge
        if line[1:11] == "Net Charge":

            charge = int(line.split()[2])
            self.set_attribute('charge', charge)

            line = next(inputfile)
            if len(line.strip()):
                #  Spin polar: 1 (Spin_A minus Spin_B electrons)
                # (Not sure about this for higher multiplicities)
                mult = int(line.split()[2]) + 1
            else:
                mult = 1
            self.set_attribute('mult', mult)

        if line[1:22] == "S C F   U P D A T E S":
        # find targets for SCF convergence

            if not hasattr(self, "scftargets"):
                self.scftargets = []

            self.skip_lines(inputfile, ['e', 'b', 'numbers'])

            line = next(inputfile)
            self.SCFconv = float(line.split()[-1])
            line = next(inputfile)
            self.sconv2 = float(line.split()[-1])

        # In ADF 2013, the default numerical integration method is fuzzy cells,
        # although it used to be Voronoi polyhedra. Both methods apparently set
        # the accint parameter, although the latter does so indirectly, based on
        # a 'grid quality' setting. This is translated into accint using a
        # dictionary with values taken from the documentation.
        if "Numerical Integration : Voronoi Polyhedra (Te Velde)" in line:
            self.integration_method = "voronoi_polyhedra"
        if line[1:27] == 'General Accuracy Parameter':
            # Need to know the accuracy of the integration grid to
            # calculate the scftarget...note that it changes with time
            self.accint = float(line.split()[-1])
        if "Numerical Integration : Fuzzy Cells (Becke)" in line:
            self.integration_method = 'fuzzy_cells'
        if line[1:19] == "Becke grid quality":
            self.grid_quality = line.split()[-1]
            quality2accint = {
                'BASIC': 2.0,
                'NORMAL': 4.0,
                'GOOD': 6.0,
                'VERYGOOD': 8.0,
                'EXCELLENT': 10.0,
            }
            self.accint = quality2accint[self.grid_quality]

        # Half of the atomic orbital overlap matrix is printed since it is symmetric,
        # but this requires "PRINT Smat" to be in the input. There are extra blank lines
        # at the end of the block, which are used to terminate the parsing.
        #
        # ======  smat
        #
        # column           1                     2                     3                     4
        # row
        #    1    1.00000000000000E+00
        #    2    2.43370854175315E-01  1.00000000000000E+00
        #    3    0.00000000000000E+00  0.00000000000000E+00  1.00000000000000E+00
        # ...
        #
        if "======  smat" in line:

            # Initialize the matrix with Nones so we can easily check all has been parsed.
            overlaps = [[None] * self.nbasis for i in range(self.nbasis)]

            self.skip_line(inputfile, 'blank')

            line = inputfile.next()
            while line.strip():

                colline = line
                assert colline.split()[0] == "column"
                columns = [int(i) for i in colline.split()[1:]]

                rowline = inputfile.next()
                assert rowline.strip() == "row"

                line = inputfile.next()
                while line.strip():

                    i = int(line.split()[0])
                    vals = [float(col) for col in line.split()[1:]]
                    for j, o in enumerate(vals):
                        k = columns[j]
                        overlaps[k-1][i-1] = o
                        overlaps[i-1][k-1] = o

                    line = inputfile.next()

                line = inputfile.next()

            # Now all values should be parsed, and so no Nones remaining.
            assert all([all([x is not None for x in ao]) for ao in overlaps])

            self.set_attribute('aooverlaps', overlaps)

        if line[1:11] == "CYCLE    1":

            self.updateprogress(inputfile, "QM convergence", self.fupdate)

            newlist = []
            line = next(inputfile)

            if not hasattr(self, "geovalues"):
                # This is the first SCF cycle
                self.scftargets.append([self.sconv2*10, self.sconv2])
            elif self.finalgeometry in [self.GETLAST, self.NOMORE]:
                # This is the final SCF cycle
                self.scftargets.append([self.SCFconv*10, self.SCFconv])
            else:
                # This is an intermediate SCF cycle in a geometry optimization,
                # in which case the SCF convergence target needs to be derived
                # from the accint parameter. For Voronoi polyhedra integration,
                # accint is printed and parsed. For fuzzy cells, it can be inferred
                # from the grid quality setting, as is done somewhere above.
                if self.accint:
                    oldscftst = self.scftargets[-1][1]
                    grdmax = self.geovalues[-1][1]
                    scftst = max(self.SCFconv, min(oldscftst, grdmax/30, 10**(-self.accint)))
                    self.scftargets.append([scftst*10, scftst])

            while line.find("SCF CONVERGED") == -1 and line.find("SCF not fully converged, result acceptable") == -1 and line.find("SCF NOT CONVERGED") == -1:
                if line[4:12] == "SCF test":
                    if not hasattr(self, "scfvalues"):
                        self.scfvalues = []

                    info = line.split()
                    newlist.append([float(info[4]), abs(float(info[6]))])
                try:
                    line = next(inputfile)
                except StopIteration:  # EOF reached?
                    self.logger.warning("SCF did not converge, so attributes may be missing")
                    break

            if line.find("SCF not fully converged, result acceptable") > 0:
                self.logger.warning("SCF not fully converged, results acceptable")

            if line.find("SCF NOT CONVERGED") > 0:
                self.logger.warning("SCF did not converge! moenergies and mocoeffs are unreliable")

            if hasattr(self, "scfvalues"):
                self.scfvalues.append(newlist)

        # Parse SCF energy for SP calcs from bonding energy decomposition section.
        # It seems ADF does not print it earlier for SP calculations.
        # Geometry optimization runs also print this, and we want to parse it
        # for them, too, even if it repeats the last "Geometry Convergence Tests"
        # section (but it's usually a bit different).
        if line[:21] == "Total Bonding Energy:":

            if not hasattr(self, "scfenergies"):
                self.scfenergies = []

            energy = utils.convertor(float(line.split()[3]), "hartree", "eV")
            self.scfenergies.append(energy)

        if line[51:65] == "Final Geometry":
            self.finalgeometry = self.GETLAST

        # Get the coordinates from each step of the GeoOpt.
        if line[1:24] == "Coordinates (Cartesian)" and self.finalgeometry in [self.NOTFOUND, self.GETLAST]:

            self.skip_lines(inputfile, ['e', 'b', 'title', 'title', 'd'])

            atomcoords = []
            line = next(inputfile)
            while list(set(line.strip())) != ['-']:
                atomcoords.append(list(map(float, line.split()[5:8])))
                line = next(inputfile)

            if not hasattr(self, "atomcoords"):
                self.atomcoords = []
            self.atomcoords.append(atomcoords)

            # Don't get any more coordinates in this case.
            # KML: I think we could combine this with optdone (see below).
            if self.finalgeometry == self.GETLAST:
                self.finalgeometry = self.NOMORE

        # There have been some changes in the format of the geometry convergence information,
        # and this is how it is printed in older versions (2007.01 unit tests).
        #
        # ==========================
        # Geometry Convergence Tests
        # ==========================
        #
        # Energy  old :         -5.14170647
        #         new :         -5.15951374
        #
        # Convergence tests:
        # (Energies in hartree, Gradients in hartree/angstr or radian, Lengths in angstrom, Angles in degrees)
        #
        #       Item               Value         Criterion    Conv.        Ratio
        # -------------------------------------------------------------------------
        # change in energy      -0.01780727     0.00100000    NO         0.00346330
        # gradient max           0.03219530     0.01000000    NO         0.30402650
        # gradient rms           0.00858685     0.00666667    NO         0.27221261
        # cart. step max         0.07674971     0.01000000    NO         0.75559435
        # cart. step rms         0.02132310     0.00666667    NO         0.55335378
        #
        if line[1:27] == 'Geometry Convergence Tests':

            if not hasattr(self, "geotargets"):
                self.geovalues = []
                self.geotargets = numpy.array([0.0, 0.0, 0.0, 0.0, 0.0], "d")

            if not hasattr(self, "scfenergies"):
                self.scfenergies = []

            self.skip_lines(inputfile, ['e', 'b'])

            energies_old = next(inputfile)
            energies_new = next(inputfile)
            self.scfenergies.append(utils.convertor(float(energies_new.split()[-1]), "hartree", "eV"))

            self.skip_lines(inputfile, ['b', 'convergence', 'units', 'b', 'header', 'd'])

            values = []
            for i in range(5):
                temp = next(inputfile).split()
                self.geotargets[i] = float(temp[-3])
                values.append(float(temp[-4]))

            self.geovalues.append(values)

            # This is to make geometry optimization always have the optdone attribute,
            # even if it is to be empty for unconverged runs.
            if not hasattr(self, 'optdone'):
                self.optdone = []

        # After the test, there is a message if the search is converged:
        #
        # ***************************************************************************************************
        #                             Geometry CONVERGED
        # ***************************************************************************************************
        #
        if line.strip() == "Geometry CONVERGED":
            self.skip_line(inputfile, 'stars')
            self.optdone.append(len(self.geovalues) - 1)

        # Here is the corresponding geometry convergence info from the 2013.01 unit test.
        # Note that the step number is given, which it will be prudent to use in an assertion.
        #
        #----------------------------------------------------------------------
        #Geometry Convergence after Step   3       (Hartree/Angstrom,Angstrom)
        #----------------------------------------------------------------------
        #current energy                               -5.16274478 Hartree
        #energy change                      -0.00237544     0.00100000    F
        #constrained gradient max            0.00884999     0.00100000    F
        #constrained gradient rms            0.00249569     0.00066667    F
        #gradient max                        0.00884999
        #gradient rms                        0.00249569
        #cart. step max                      0.03331296     0.01000000    F
        #cart. step rms                      0.00844037     0.00666667    F
        if line[:31] == "Geometry Convergence after Step":

            stepno = int(line.split()[4])

            # This is to make geometry optimization always have the optdone attribute,
            # even if it is to be empty for unconverged runs.
            if not hasattr(self, 'optdone'):
                self.optdone = []

            # The convergence message is inline in this block, not later as it was before.
            if "** CONVERGED **" in line:
                if not hasattr(self, 'optdone'):
                    self.optdone = []
                self.optdone.append(len(self.geovalues) - 1)

            self.skip_line(inputfile, 'dashes')

            current_energy = next(inputfile)
            energy_change = next(inputfile)
            constrained_gradient_max = next(inputfile)
            constrained_gradient_rms = next(inputfile)
            gradient_max = next(inputfile)
            gradient_rms = next(inputfile)
            cart_step_max = next(inputfile)
            cart_step_rms = next(inputfile)

            if not hasattr(self, "scfenergies"):
                self.scfenergies = []

            energy = utils.convertor(float(current_energy.split()[-2]), "hartree", "eV")
            self.scfenergies.append(energy)

            if not hasattr(self, "geotargets"):
                self.geotargets = numpy.array([0.0, 0.0, 0.0, 0.0, 0.0], "d")

            self.geotargets[0] = float(energy_change.split()[-2])
            self.geotargets[1] = float(constrained_gradient_max.split()[-2])
            self.geotargets[2] = float(constrained_gradient_rms.split()[-2])
            self.geotargets[3] = float(cart_step_max.split()[-2])
            self.geotargets[4] = float(cart_step_rms.split()[-2])

            if not hasattr(self, "geovalues"):
                self.geovalues = []

            self.geovalues.append([])
            self.geovalues[-1].append(float(energy_change.split()[-3]))
            self.geovalues[-1].append(float(constrained_gradient_max.split()[-3]))
            self.geovalues[-1].append(float(constrained_gradient_rms.split()[-3]))
            self.geovalues[-1].append(float(cart_step_max.split()[-3]))
            self.geovalues[-1].append(float(cart_step_rms.split()[-3]))

        if line.find('Orbital Energies, per Irrep and Spin') > 0 and not hasattr(self, "mosyms") and self.nosymflag and not self.unrestrictedflag:
        #Extracting orbital symmetries and energies, homos for nosym case
        #Should only be for restricted case because there is a better text block for unrestricted and nosym

            self.mosyms = [[]]

            self.moenergies = [[]]

            self.skip_lines(inputfile, ['e', 'header', 'd', 'label'])

            line = next(inputfile)
            info = line.split()

            if not info[0] == '1':
                self.logger.warning("MO info up to #%s is missing" % info[0])

            #handle case where MO information up to a certain orbital are missing
            while int(info[0]) - 1 != len(self.moenergies[0]):
                self.moenergies[0].append(99999)
                self.mosyms[0].append('A')

            homoA = None

            while len(line) > 10:
                info = line.split()
                self.mosyms[0].append('A')
                self.moenergies[0].append(utils.convertor(float(info[2]), 'hartree', 'eV'))
                if info[1] == '0.000' and not hasattr(self, 'homos'):
                    self.set_attribute('homos', [len(self.moenergies[0]) - 2])
                line = next(inputfile)

            self.moenergies = [numpy.array(self.moenergies[0], "d")]

        if line[1:29] == 'Orbital Energies, both Spins' and not hasattr(self, "mosyms") and self.nosymflag and self.unrestrictedflag:
        #Extracting orbital symmetries and energies, homos for nosym case
        #should only be here if unrestricted and nosym

            self.mosyms = [[], []]
            moenergies = [[], []]

            self.skip_lines(inputfile, ['d', 'b', 'header', 'd'])

            homoa = 0
            homob = None

            line = next(inputfile)
            while len(line) > 5:
                info = line.split()
                if info[2] == 'A':
                    self.mosyms[0].append('A')
                    moenergies[0].append(utils.convertor(float(info[4]), 'hartree', 'eV'))
                    if info[3] != '0.00':
                        homoa = len(moenergies[0]) - 1
                elif info[2] == 'B':
                    self.mosyms[1].append('A')
                    moenergies[1].append(utils.convertor(float(info[4]), 'hartree', 'eV'))
                    if info[3] != '0.00':
                        homob = len(moenergies[1]) - 1
                else:
                    print(("Error reading line: %s" % line))

                line = next(inputfile)

            self.moenergies = [numpy.array(x, "d") for x in moenergies]

            self.set_attribute('homos', [homoa, homob])

        # Extracting orbital symmetries and energies, homos.
        if line[1:29] == 'Orbital Energies, all Irreps' and not hasattr(self, "mosyms"):

            self.symlist = {}
            self.mosyms = [[]]
            self.moenergies = [[]]

            self.skip_lines(inputfile, ['e', 'b', 'header', 'd'])

            homoa = None
            homob = None

            #multiple = {'E':2, 'T':3, 'P':3, 'D':5}
            # The above is set if there are no special irreps
            names = [irrep[0].split(':')[0] for irrep in self.irreps]
            counts = [len(irrep) for irrep in self.irreps]
            multiple = dict(list(zip(names, counts)))
            irrepspecies = {}
            for n in range(len(names)):
                indices = list(range(counts[n]))
                subspecies = self.irreps[n]
                irrepspecies[names[n]] = dict(list(zip(indices, subspecies)))

            line = next(inputfile)
            while line.strip():
                info = line.split()
                if len(info) == 5:  # this is restricted
                    #count = multiple.get(info[0][0],1)
                    count = multiple.get(info[0], 1)
                    for repeat in range(count):  # i.e. add E's twice, T's thrice
                        self.mosyms[0].append(self.normalisesym(info[0]))
                        self.moenergies[0].append(utils.convertor(float(info[3]), 'hartree', 'eV'))

                        sym = info[0]
                        if count > 1:   # add additional sym label
                            sym = self.normalisedegenerates(info[0], repeat, ndict=irrepspecies)

                        try:
                            self.symlist[sym][0].append(len(self.moenergies[0])-1)
                        except KeyError:
                            self.symlist[sym] = [[]]
                            self.symlist[sym][0].append(len(self.moenergies[0])-1)

                    if info[2] == '0.00' and not hasattr(self, 'homos'):
                        self.homos = [len(self.moenergies[0]) - (count + 1)]  # count, because need to handle degenerate cases
                    line = next(inputfile)
                elif len(info) == 6:  # this is unrestricted
                    if len(self.moenergies) < 2:  # if we don't have space, create it
                        self.moenergies.append([])
                        self.mosyms.append([])
#                    count = multiple.get(info[0][0], 1)
                    count = multiple.get(info[0], 1)
                    if info[2] == 'A':
                        for repeat in range(count):  # i.e. add E's twice, T's thrice
                            self.mosyms[0].append(self.normalisesym(info[0]))
                            self.moenergies[0].append(utils.convertor(float(info[4]), 'hartree', 'eV'))

                            sym = info[0]
                            if count > 1:  # add additional sym label
                                sym = self.normalisedegenerates(info[0], repeat)

                            try:
                                self.symlist[sym][0].append(len(self.moenergies[0])-1)
                            except KeyError:
                                self.symlist[sym] = [[], []]
                                self.symlist[sym][0].append(len(self.moenergies[0])-1)

                        if info[3] == '0.00' and homoa is None:
                            homoa = len(self.moenergies[0]) - (count + 1)  # count because degenerate cases need to be handled

                    if info[2] == 'B':
                        for repeat in range(count):  # i.e. add E's twice, T's thrice
                            self.mosyms[1].append(self.normalisesym(info[0]))
                            self.moenergies[1].append(utils.convertor(float(info[4]), 'hartree', 'eV'))

                            sym = info[0]
                            if count > 1:  # add additional sym label
                                sym = self.normalisedegenerates(info[0], repeat)

                            try:
                                self.symlist[sym][1].append(len(self.moenergies[1])-1)
                            except KeyError:
                                self.symlist[sym] = [[], []]
                                self.symlist[sym][1].append(len(self.moenergies[1])-1)

                        if info[3] == '0.00' and homob is None:
                            homob = len(self.moenergies[1]) - (count + 1)

                    line = next(inputfile)

                else:  # different number of lines
                    print(("Error", info))

            if len(info) == 6:  # still unrestricted, despite being out of loop
                self.set_attribute('homos', [homoa, homob])

            self.moenergies = [numpy.array(x, "d") for x in self.moenergies]

        # Section on extracting vibdisps
        # Also contains vibfreqs, but these are extracted in the
        # following section (see below)
        if line[1:28] == "Vibrations and Normal Modes":

            self.vibdisps = []

            self.skip_lines(inputfile, ['e', 'b', 'header', 'header', 'b', 'b'])

            freqs = next(inputfile)
            while freqs.strip() != "":
                minus = next(inputfile)
                p = [[], [], []]
                for i in range(len(self.atomnos)):
                    broken = list(map(float, next(inputfile).split()[1:]))
                    for j in range(0, len(broken), 3):
                        p[j//3].append(broken[j:j+3])
                self.vibdisps.extend(p[:(len(broken)//3)])
                self.skip_lines(inputfile, ['b', 'b'])
                freqs = next(inputfile)
            self.vibdisps = numpy.array(self.vibdisps, "d")

        if line[1:24] == "List of All Frequencies":
        # Start of the IR/Raman frequency section
            self.updateprogress(inputfile, "Frequency information", self.fupdate)

        #                 self.vibsyms = []  # Need to look into this a bit more
            self.vibirs = []
            self.vibfreqs = []
            for i in range(8):
                line = next(inputfile)
            line = next(inputfile).strip()
            while line:
                temp = line.split()
                self.vibfreqs.append(float(temp[0]))
                self.vibirs.append(float(temp[2]))  # or is it temp[1]?
                line = next(inputfile).strip()
            self.vibfreqs = numpy.array(self.vibfreqs, "d")
            self.vibirs = numpy.array(self.vibirs, "d")
            if hasattr(self, "vibramans"):
                self.vibramans = numpy.array(self.vibramans, "d")

        #******************************************************************************************************************8
        #delete this after new implementation using smat, eigvec print,eprint?
        # Extract the number of basis sets
        if line[1:49] == "Total nr. of (C)SFOs (summation over all irreps)":
            nbasis = int(line.split(":")[1].split()[0])
            self.set_attribute('nbasis', nbasis)

        # now that we're here, let's extract aonames

            self.fonames = []
            self.start_indeces = {}
            self.atombasis = [[] for frag in self.frags] # parse atombasis in the case of trivial SFOs

            self.skip_line(inputfile, 'blank')

            note = next(inputfile)
            symoffset = 0

            self.skip_line(inputfile, 'blank')
            line = next(inputfile)
            if len(line) > 2:  # fix for ADF2006.01 as it has another note
                self.skip_line(inputfile, 'blank')
                line = next(inputfile)
            self.skip_line(inputfile, 'blank')

            self.nosymreps = []
            while len(self.fonames) < self.nbasis:

                symline = next(inputfile)
                sym = symline.split()[1]
                line = next(inputfile)
                num = int(line.split(':')[1].split()[0])
                self.nosymreps.append(num)

                #read until line "--------..." is found
                while line.find('-----') < 0:
                    line = next(inputfile)

                line = next(inputfile)  # the start of the first SFO

                while len(self.fonames) < symoffset + num:
                    info = line.split()

                    #index0 index1 occ2 energy3/4 fragname5 coeff6 orbnum7 orbname8 fragname9
                    if not sym in list(self.start_indeces.keys()):
                    #have we already set the start index for this symmetry?
                        self.start_indeces[sym] = int(info[1])

                    orbname = info[8]
                    orbital = info[7] + orbname.replace(":", "")

                    fragname = info[5]
                    frag = fragname + info[9]

                    coeff = float(info[6])

                    # parse atombasis only in the case that all coefficients are 1
                    #    and delete it otherwise
                    if hasattr(self, 'atombasis'):
                        if coeff == 1.:
                            ibas  = int(info[0]) - 1
                            ifrag = int(info[9]) - 1
                            iat = self.frags[ifrag][0]
                            self.atombasis[iat].append(ibas)
                        else:
                            del self.atombasis

                    line = next(inputfile)
                    while line.strip() and not line[:7].strip():  # while it's the same SFO
                        # i.e. while not completely blank, but blank at the start
                        info = line[43:].split()
                        if len(info) > 0:  # len(info)==0 for the second line of dvb_ir.adfout
                            frag += "+" + fragname + info[-1]
                            coeff = float(info[-4])
                            if coeff < 0:
                                orbital += '-' + info[-3] + info[-2].replace(":", "")
                            else:
                                orbital += '+' + info[-3] + info[-2].replace(":", "")
                        line = next(inputfile)
                    # At this point, we are either at the start of the next SFO or at
                    # a blank line...the end

                    self.fonames.append("%s_%s" % (frag, orbital))
                symoffset += num

                # blankline blankline
                next(inputfile)
                next(inputfile)

        if line[1:32] == "S F O   P O P U L A T I O N S ,":
        #Extract overlap matrix

#            self.fooverlaps = numpy.zeros((self.nbasis, self.nbasis), "d")

            symoffset = 0

            for nosymrep in self.nosymreps:

                line = next(inputfile)
                while line.find('===') < 10:  # look for the symmetry labels
                    line = next(inputfile)

                self.skip_lines(inputfile, ['b', 'b'])

                text = next(inputfile)
                if text[13:20] != "Overlap":  # verify this has overlap info
                    break

                self.skip_lines(inputfile, ['b', 'col', 'row'])

                if not hasattr(self, "fooverlaps"):  # make sure there is a matrix to store this
                    self.fooverlaps = numpy.zeros((self.nbasis, self.nbasis), "d")

                base = 0
                while base < nosymrep:  # have we read all the columns?

                    for i in range(nosymrep - base):

                        self.updateprogress(inputfile, "Overlap", self.fupdate)
                        line = next(inputfile)
                        parts = line.split()[1:]
                        for j in range(len(parts)):
                            k = float(parts[j])
                            self.fooverlaps[base + symoffset + j, base + symoffset + i] = k
                            self.fooverlaps[base + symoffset + i, base + symoffset + j] = k

                    #blank, blank, column
                    for i in range(3):
                        next(inputfile)

                    base += 4

                symoffset += nosymrep
                base = 0

# The commented code below makes the atombasis attribute based on the BAS function in ADF,
#   but this is probably not so useful, since SFOs are used to build MOs in ADF.
#        if line[1:54] == "BAS: List of all Elementary Cartesian Basis Functions":
#
#            self.atombasis = []
#
#            # There will be some text, followed by a line:
#            #       (power of) X  Y  Z  R     Alpha  on Atom
#            while not line[1:11] == "(power of)":
#                line = inputfile.next()
#            dashes = inputfile.next()
#            blank = inputfile.next()
#            line = inputfile.next()
#            # There will be two blank lines when there are no more atom types.
#            while line.strip() != "":
#                atoms = [int(i)-1 for i in line.split()[1:]]
#                for n in range(len(atoms)):
#                    self.atombasis.append([])
#                dashes = inputfile.next()
#                line = inputfile.next()
#                while line.strip() != "":
#                    indices = [int(i)-1 for i in line.split()[5:]]
#                    for i in range(len(indices)):
#                        self.atombasis[atoms[i]].append(indices[i])
#                    line = inputfile.next()
#                line = inputfile.next()

        if line[48:67] == "SFO MO coefficients":

            self.mocoeffs = [numpy.zeros((self.nbasis, self.nbasis), "d")]
            spin = 0
            symoffset = 0
            lastrow = 0

            # Section ends with "1" at beggining of a line.
            while line[0] != "1":
                line = next(inputfile)

                # If spin is specified, then there will be two coefficient matrices.
                if line.strip() == "***** SPIN 1 *****":
                    self.mocoeffs = [numpy.zeros((self.nbasis, self.nbasis), "d"),
                                     numpy.zeros((self.nbasis, self.nbasis), "d")]

                # Bump up the spin.
                if line.strip() == "***** SPIN 2 *****":
                    spin = 1
                    symoffset = 0
                    lastrow = 0

                # Next symmetry.
                if line.strip()[:4] == "=== ":
                    sym = line.split()[1]
                    if self.nosymflag:
                        aolist = list(range(self.nbasis))
                    else:
                        aolist = self.symlist[sym][spin]
                    # Add to the symmetry offset of AO ordering.
                    symoffset += lastrow

                # Blocks with coefficient always start with "MOs :".
                if line[1:6] == "MOs :":
                    # Next line has the MO index contributed to.
                    monumbers = [int(n) for n in line[6:].split()]

                    self.skip_lines(inputfile, ['occup', 'label'])

                    # The table can end with a blank line or "1".
                    row = 0
                    line = next(inputfile)
                    while not line.strip() in ["", "1"]:
                        info = line.split()

                        if int(info[0]) < self.start_indeces[sym]:
                        #check to make sure we aren't parsing CFs
                            line = next(inputfile)
                            continue

                        self.updateprogress(inputfile, "Coefficients", self.fupdate)
                        row += 1
                        coeffs = [float(x) for x in info[1:]]
                        moindices = [aolist[n-1] for n in monumbers]
                        # The AO index is 1 less than the row.
                        aoindex = symoffset + row - 1
                        for i in range(len(monumbers)):
                            self.mocoeffs[spin][moindices[i], aoindex] = coeffs[i]
                        line = next(inputfile)
                    lastrow = row

        # **************************************************************************
        # *                                                                        *
        # *   Final excitation energies from Davidson algorithm                    *
        # *                                                                        *
        # **************************************************************************
        #
        #     Number of loops in Davidson routine     =   20
        #     Number of matrix-vector multiplications =   24
        #     Type of excitations = SINGLET-SINGLET
        #
        # Symmetry B.u
        #
        # ... several blocks ...
        #
        # Normal termination of EXCITATION program part
        if line[4:53] == "Final excitation energies from Davidson algorithm":

            while line[1:9] != "Symmetry" and "Normal termination" not in line:
                line = next(inputfile)
            symm = self.normalisesym(line.split()[1])

            # Excitation energies E in a.u. and eV, dE wrt prev. cycle,
            # oscillator strengths f in a.u.
            #
            # no.  E/a.u.        E/eV      f           dE/a.u.
            # -----------------------------------------------------
            #   1 0.17084      4.6488     0.16526E-01  0.28E-08
            # ...
            while line.split() != ['no.', 'E/a.u.', 'E/eV', 'f', 'dE/a.u.'] and "Normal termination" not in line:
                line = next(inputfile)

            self.skip_line(inputfile, 'dashes')

            etenergies = []
            etoscs = []
            etsyms = []
            line = next(inputfile)
            while len(line) > 2:
                info = line.split()
                etenergies.append(utils.convertor(float(info[2]), "eV", "wavenumber"))
                etoscs.append(float(info[3]))
                etsyms.append(symm)
                line = next(inputfile)

            # There is another section before this, with transition dipole moments,
            # but this should just skip past it.
            while line[1:53] != "Major MO -> MO transitions for the above excitations":
                line = next(inputfile)

            # Note that here, and later, the number of blank lines can vary between
            # version of ADF (extra lines are seen in 2013.01 unit tests, for example).
            self.skip_line(inputfile, 'blank')
            excitation_occupied = next(inputfile)
            header = next(inputfile)
            while not header.strip():
                header = next(inputfile)
            header2 = next(inputfile)
            x_y_z = next(inputfile)
            line = next(inputfile)
            while not line.strip():
                line = next(inputfile)

            # Before we start handeling transitions, we need to create mosyms
            # with indices; only restricted calcs are possible in ADF.
            counts = {}
            syms = []
            for mosym in self.mosyms[0]:
                if list(counts.keys()).count(mosym) == 0:
                    counts[mosym] = 1
                else:
                    counts[mosym] += 1
                syms.append(str(counts[mosym]) + mosym)

            etsecs = []
            printed_warning = False
            for i in range(len(etenergies)):

                etsec = []
                info = line.split()
                while len(info) > 0:

                    match = re.search('[^0-9]', info[1])
                    index1 = int(info[1][:match.start(0)])
                    text = info[1][match.start(0):]
                    symtext = text[0].upper() + text[1:]
                    sym1 = str(index1) + self.normalisesym(symtext)

                    match = re.search('[^0-9]', info[3])
                    index2 = int(info[3][:match.start(0)])
                    text = info[3][match.start(0):]
                    symtext = text[0].upper() + text[1:]
                    sym2 = str(index2) + self.normalisesym(symtext)

                    try:
                        index1 = syms.index(sym1)
                    except ValueError:
                        if not printed_warning:
                            self.logger.warning("Etsecs are not accurate!")
                            printed_warning = True

                    try:
                        index2 = syms.index(sym2)
                    except ValueError:
                        if not printed_warning:
                            self.logger.warning("Etsecs are not accurate!")
                            printed_warning = True

                    etsec.append([(index1, 0), (index2, 0), float(info[4])])

                    line = next(inputfile)
                    info = line.split()

                etsecs.append(etsec)

                # Again, the number of blank lines between transition can vary.
                line = next(inputfile)
                while not line.strip():
                    line = next(inputfile)

            if not hasattr(self, "etenergies"):
                self.etenergies = etenergies
            else:
                self.etenergies += etenergies

            if not hasattr(self, "etoscs"):
                self.etoscs = etoscs
            else:
                self.etoscs += etoscs

            if not hasattr(self, "etsyms"):
                self.etsyms = etsyms
            else:
                self.etsyms += etsyms

            if not hasattr(self, "etsecs"):
                self.etsecs = etsecs
            else:
                self.etsecs += etsecs

        if "M U L L I K E N   P O P U L A T I O N S" in line:
            if not hasattr(self, "atomcharges"):
                self.atomcharges = {}
            while line[1:5] != "Atom":
                line = next(inputfile)
            self.skip_line(inputfile, 'dashes')
            mulliken = []
            line = next(inputfile)
            while line.strip():
                mulliken.append(float(line.split()[2]))
                line = next(inputfile)
            self.atomcharges["mulliken"] = mulliken

        # Dipole moment is always printed after a point calculation,
        # and the reference point for this is always the origin (0,0,0)
        # and not necessarily the center of mass, as explained on the
        # ADF user mailing list (see cclib/cclib#113 for details).
        #
        # =============
        # Dipole Moment  ***  (Debye)  ***
        # =============
        #
        # Vector   :         0.00000000      0.00000000      0.00000000
        # Magnitude:         0.00000000
        #
        if line.strip()[:13] == "Dipole Moment":

            self.skip_line(inputfile, 'equals')

            # There is not always a blank line here, for example when the dipole and quadrupole
            # moments are printed after the multipole derived atomic charges. Still, to the best
            # of my knowledge (KML) the values are still in Debye.
            line = next(inputfile)
            if not line.strip():
                line = next(inputfile)

            assert line.split()[0] == "Vector"
            dipole = [float(d) for d in line.split()[-3:]]

            reference = [0.0, 0.0, 0.0]
            if not hasattr(self, 'moments'):
                self.moments = [reference, dipole]
            else:
                try:
                    assert self.moments[1] == dipole
                except AssertionError:
                    self.logger.warning('Overwriting previous multipole moments with new values')
                    self.moments = [reference, dipole]

        # Molecular response properties.
        if line.strip()[1:-1].strip() == "RESPONSE program part":

            while line.strip() != "Normal termination of RESPONSE program part":

                if "THE DIPOLE-DIPOLE POLARIZABILITY TENSOR:" in line:
                    if not hasattr(self, 'polarizabilities'):
                        self.polarizabilities = []
                    polarizability = numpy.empty(shape=(3, 3))
                    self.skip_lines(inputfile, ['b', 'FREQUENCY', 'coordinates'])
                    # Ordering of rows/columns is Y, Z, X.
                    ordering = [1, 2, 0]
                    indices = list(itertools.product(ordering, ordering))
                    for i in range(3):
                        tokens = next(inputfile).split()
                        for j in range(3):
                            polarizability[indices[(i*3)+j]] = tokens[j]
                    self.polarizabilities.append(polarizability)

                line = next(inputfile)

        if line[:24] == ' Buffered I/O statistics':
            self.metadata['success'] = True
Example #50
0
    def extract(self, inputfile, line):
        """Extract information from the file object inputfile."""

        ## This information is in the control file.
        #   $rundimensions
        #   dim(fock,dens)=1860
        #   natoms=20
        #   nshell=40
        #   nbf(CAO)=60
        #   nbf(AO)=60
        #   dim(trafo[SAO<-->AO/CAO])=60
        #   rhfshells=1
        if line[3:10]=="natoms=":
            self.natom=int(line[10:])

        if line[3:11] == "nbf(AO)=":
            nmo = int(line.split('=')[1])
            self.set_attribute('nbasis', nmo)
            self.set_attribute('nmo', nmo)

        # Extract the version number and optionally the build number.
        searchstr = ": TURBOMOLE"
        index = line.find(searchstr)
        if index > -1:
            line = line[index + len(searchstr):]
            tokens = line.split()
            self.metadata["package_version"] = tokens[0][1:].replace("-", ".")
            # Don't add revision information to the main package version for now.
            if tokens[1] == "(":
                revision = tokens[2]

        ## Atomic coordinates in job.last:
        #              +--------------------------------------------------+
        #              | Atomic coordinate, charge and isotop information |
        #              +--------------------------------------------------+
        #
        #
        #              atomic coordinates              atom shells charge pseudo isotop
        #    -2.69176330   -0.00007129   -0.44712612    c      3    6.000    0     0
        #    -1.69851645   -0.00007332    2.06488947    c      3    6.000    0     0
        #     0.92683848   -0.00007460    2.49592179    c      3    6.000    0     0
        #     2.69176331   -0.00007127    0.44712612    c      3    6.000    0     0
        #     1.69851645   -0.00007331   -2.06488947    c      3    6.000    0     0
        #...
        #    -7.04373606    0.00092244    2.74543891    h      1    1.000    0     0
        #    -9.36352819    0.00017229    0.07445322    h      1    1.000    0     0
        #    -0.92683849   -0.00007461   -2.49592179    c      3    6.000    0     0
        #    -1.65164853   -0.00009927   -4.45456858    h      1    1.000    0     0
        if 'Atomic coordinate, charge and isotop information' in line:
            while 'atomic coordinates' not in line:
                line = next(inputfile)

            atomcoords = []
            atomnos = []
            line = next(inputfile)
            while len(line) > 2:
                atomnos.append(self.periodic_table.number[line.split()[3].upper()])
                atomcoords.append([utils.convertor(float(x), "bohr", "Angstrom") 
                                   for x in line.split()[:3]])
                line = next(inputfile)

            self.append_attribute('atomcoords', atomcoords)
            self.set_attribute('atomnos', atomnos)
            self.set_attribute('natom', len(atomcoords))

        # Frequency values in aoforce.out
        #        mode               7        8        9       10       11       12
        #
        #      frequency          53.33    88.32   146.85   171.70   251.75   289.44
        #
        #      symmetry            a        a        a        a        a        a
        #
        #         IR               YES      YES      YES      YES      YES      YES
        # |dDIP/dQ|   (a.u.)     0.0002   0.0000   0.0005   0.0004   0.0000   0.0000
        # intensity (km/mol)       0.05     0.00     0.39     0.28     0.00     0.00
        # intensity (  %   )       0.05     0.00     0.40     0.28     0.00     0.00
        #
        #        RAMAN             YES      YES      YES      YES      YES      YES
        #
        #   1   c           x   0.00000  0.00001  0.00000 -0.01968 -0.04257  0.00001
        #                   y  -0.08246 -0.08792  0.02675 -0.00010  0.00000  0.17930
        #                   z   0.00001  0.00003  0.00004 -0.10350  0.11992 -0.00003
        if 'NORMAL MODES and VIBRATIONAL FREQUENCIES (cm**(-1))' in line:
            vibfreqs, vibsyms, vibirs, vibdisps = [], [], [], []
            while '****  force : all done  ****' not in line:
                if line.strip().startswith('frequency'):
                    freqs = [float(i.replace('i', '-')) for i in line.split()[1:]]
                    vibfreqs.extend(freqs)
                    self.skip_line(inputfile, ['b'])
                    line = next(inputfile)
                    if line.strip().startswith('symmetry'):
                        syms = line.split()[1:]
                        vibsyms.extend(syms)

                    self.skip_lines(inputfile, ['b', 'IR', 'dQIP'])
                    line = next(inputfile)
                    if line.strip().startswith('intensity (km/mol)'):
                        irs = [self.float(f) for f in line.split()[2:]]
                        vibirs.extend(irs)

                    self.skip_lines(inputfile, ['intensity', 'b', 'raman', 'b'])
                    line = next(inputfile)
                    x, y, z = [], [], []
                    while line.split():
                        x.append([float(i) for i in line.split()[3:]])
                        line = next(inputfile)
                        y.append([float(i) for i in line.split()[1:]])
                        line = next(inputfile)
                        z.append([float(i) for i in line.split()[1:]])
                        line = next(inputfile)

                    for j in range(len(x[0])):
                        disps = []
                        for i in range(len(x)):
                            disps.append([x[i][j], y[i][j], z[i][j]])
                        vibdisps.append(disps)

                line = next(inputfile)

            self.set_attribute('vibfreqs', vibfreqs)
            self.set_attribute('vibsyms', vibsyms)
            self.set_attribute('vibirs', vibirs)
            self.set_attribute('vibdisps', vibdisps)

        # In this section we are parsing mocoeffs and moenergies from
        # the files like: mos, alpha and beta.
        # $scfmo    scfconv=6   format(4d20.14)
        # # SCF total energy is     -382.3457535740 a.u.
        # #
        #      1  a      eigenvalue=-.97461484059799D+01   nsaos=60
        # 0.69876828353937D+000.32405121159405D-010.87670894913921D-03-.85232349313288D-07
        # 0.19361534257922D-04-.23841194890166D-01-.81711001390807D-020.13626356942047D-02
        # ...
        # ...
        # $end
        if (line.startswith('$scfmo') or line.startswith('$uhfmo')) and line.find('scfconv') > 0:
            if line.strip().startswith('$uhfmo_alpha'):
                self.unrestricted = True

            # Need to skip the first line to start with lines starting with '#'.
            line = next(inputfile)
            while line.strip().startswith('#') and not line.find('eigenvalue') > 0:
                line = next(inputfile)

            moenergies = []
            mocoeffs = []

            while not line.strip().startswith('$'):
                info = re.match(".*eigenvalue=(?P<moenergy>[0-9D\.+-]{20})\s+nsaos=(?P<count>\d+).*", line)
                eigenvalue = self.float(info.group('moenergy'))
                orbital_energy = utils.convertor(eigenvalue, 'hartree', 'eV')
                moenergies.append(orbital_energy)
                single_coeffs = []
                nsaos = int(info.group('count'))

                while(len(single_coeffs) < nsaos):
                    line = next(inputfile)
                    single_coeffs.extend(Turbomole.split_molines(line))

                mocoeffs.append(single_coeffs)
                line = next(inputfile)

            max_nsaos = max([len(i) for i in mocoeffs])
            for i in mocoeffs:
                while len(i) < max_nsaos:
                    i.append(numpy.nan)

            if not hasattr(self, 'mocoeffs'):
                self.mocoeffs = []

            if not hasattr(self, 'moenergies'):
                self.moenergies = []

            self.mocoeffs.append(mocoeffs)
            self.moenergies.append(moenergies)

        # Parsing the scfenergies, scfvalues and scftargets from job.last file.
        # scf convergence criterion : increment of total energy < .1000000D-05
        #                  and increment of one-electron energy < .1000000D-02
        #
        # ...
        # ...
        #                                              current damping :  0.700
        # ITERATION  ENERGY          1e-ENERGY        2e-ENERGY     NORM[dD(SAO)]  TOL
        #   1  -382.34543727790    -1396.8009423     570.56292464    0.000D+00 0.556D-09
        #                            Exc =   -57.835278090846     N = 69.997494722
        #          max. resid. norm for Fia-block=  2.782D-05 for orbital     33a
        # ...
        # ...
        #                                              current damping :  0.750
        # ITERATION  ENERGY          1e-ENERGY        2e-ENERGY     NORM[dD(SAO)]  TOL
        #   3  -382.34575357399    -1396.8009739     570.56263988    0.117D-03 0.319D-09
        #                            Exc =   -57.835593208072     N = 69.999813370
        #          max. resid. norm for Fia-block=  7.932D-06 for orbital     33a
        #          max. resid. fock norm         =  8.105D-06 for orbital     33a
        #
        # convergence criteria satisfied after  3 iterations
        #
        #
        #                  ------------------------------------------
        #                 |  total energy      =   -382.34575357399  |
        #                  ------------------------------------------
        #                 :  kinetic energy    =    375.67398458525  :
        #                 :  potential energy  =   -758.01973815924  :
        #                 :  virial theorem    =      1.98255043001  :
        #                 :  wavefunction norm =      1.00000000000  :
        #                  ..........................................
        if 'scf convergence criterion' in line:
            total_energy_threshold = self.float(line.split()[-1])
            one_electron_energy_threshold = self.float(next(inputfile).split()[-1])
            scftargets = [total_energy_threshold, one_electron_energy_threshold]
            self.append_attribute('scftargets', scftargets)
            iter_energy = []
            iter_one_elec_energy = []
            while 'convergence criteria satisfied' not in line:
                if 'ITERATION  ENERGY' in line:
                    line = next(inputfile)
                    info = line.split()
                    iter_energy.append(self.float(info[1]))
                    iter_one_elec_energy.append(self.float(info[2]))
                line = next(inputfile)

            assert len(iter_energy) == len(iter_one_elec_energy), \
                'Different number of values found for total energy and one electron energy.'
            scfvalues = [[x - y, a - b] for x, y, a, b in 
                         zip(iter_energy[1:], iter_energy[:-1], iter_one_elec_energy[1:], iter_one_elec_energy[:-1])]
            self.append_attribute('scfvalues', scfvalues)
            while 'total energy' not in line:
                line = next(inputfile)

            scfenergy = utils.convertor(self.float(line.split()[4]), 'hartree', 'eV')
            self.append_attribute('scfenergies', scfenergy)

        #  **********************************************************************
        #  *                                                                    *
        #  *   RHF  energy                             :    -74.9644564256      *
        #  *   MP2 correlation energy (doubles)        :     -0.0365225363      *
        #  *                                                                    *
        #  *   Final MP2 energy                        :    -75.0009789619      *
        # ...
        #  *   Norm of MP1 T2 amplitudes               :      0.0673494687      *
        #  *                                                                    *
        #  **********************************************************************
        # OR
        #  **********************************************************************
        #  *                                                                    *
        #  *   RHF  energy                             :    -74.9644564256      *
        #  *   correlation energy                      :     -0.0507799360      *
        #  *                                                                    *
        #  *   Final CCSD energy                       :    -75.0152363616      *
        #  *                                                                    *
        #  *   D1 diagnostic                           :      0.0132            *
        #  *                                                                    *
        #  **********************************************************************
        if 'C C S D F 1 2   P R O G R A M' in line:
            while 'ccsdf12 : all done' not in line:
                if 'Final MP2 energy' in line:
                    mp2energy = [utils.convertor(self.float(line.split()[5]), 'hartree', 'eV')]
                    self.append_attribute('mpenergies', mp2energy)

                if 'Final CCSD energy' in line:
                    ccenergy = [utils.convertor(self.float(line.split()[5]), 'hartree', 'eV')]
                    self.append_attribute('ccenergies', ccenergy)

                line = next(inputfile)

        #  *****************************************************
        #  *                                                   *
        #  *      SCF-energy   :     -74.49827196840999        *
        #  *      MP2-energy   :      -0.19254365976227        *
        #  *      total        :     -74.69081562817226        *
        #  *                                                   *
        #  *     (MP2-energy evaluated from T2 amplitudes)     *
        #  *                                                   *
        #  *****************************************************
        if 'm p g r a d - program' in line:
            while 'ccsdf12 : all done' not in line:
                if 'MP2-energy' in line:
                    line = next(inputfile)
                    if 'total' in line:
                        mp2energy = [utils.convertor(self.float(line.split()[3]), 'hartree', 'eV')]
                        self.append_attribute('mpenergies', mp2energy)
                line = next(inputfile)
Example #51
0
    def extract(self, inputfile, line):
        """Extract information from the file object inputfile."""

        if line[3:11]=="nbf(AO)=":
            nmo=int(line[11:])
            self.nbasis=nmo
            self.nmo=nmo
        if line[3:9]=="nshell":
            temp=line.split('=')
            homos=int(temp[1])

        if line[0:6] == "$basis":
            print("Found basis")
            self.basis_lib=[]
            line = inputfile.next()
            line = inputfile.next()

            while line[0] != '*' and line[0] != '$':
                temp=line.split()
                line = inputfile.next()
                while line[0]=="#":
                    line = inputfile.next()
                self.basis_lib.append(AtomBasis(temp[0], temp[1], inputfile))
                line = inputfile.next()
        if line == "$ecp\n":
            self.ecp_lib=[]
            
            line = inputfile.next()
            line = inputfile.next()
            
            while line[0] != '*' and line[0] != '$':
                fields=line.split()
                atname=fields[0]
                ecpname=fields[1]
                line = inputfile.next()
                line = inputfile.next()
                fields=line.split()
                ncore = int(fields[2])

                while line[0] != '*':
                    line = inputfile.next()
                self.ecp_lib.append([atname, ecpname, ncore])
        
        if line[0:6] == "$coord":
            if line[0:11] == "$coordinate":
#                print "Breaking"
                return

#            print "Found coords"
            self.atomcoords = []
            self.atomnos = []
            atomcoords = []
            atomnos = []

            line = inputfile.next()
            if line[0:5] == "$user":
#                print "Breaking"
                return

            while line[0] != "$":
                temp = line.split()
                atsym=temp[3].capitalize()
                atomnos.append(self.table.number[atsym])
                atomcoords.append([utils.convertor(float(x), "bohr", "Angstrom")
                                   for x in temp[0:3]])
                line = inputfile.next()
            self.atomcoords.append(atomcoords)
            self.atomnos = numpy.array(atomnos, "i")

        if line[14:32] == "atomic coordinates":
            atomcoords = []
            atomnos = []

            line = inputfile.next()
           
            while len(line) > 2:
                temp = line.split()
                atsym = temp[3].capitalize()
                atomnos.append(self.table.number[atsym])
                atomcoords.append([utils.convertor(float(x), "bohr", "Angstrom")
                                    for x in temp[0:3]])
                line = inputfile.next()

            if not hasattr(self,"atomcoords"):
                self.atomcoords = []

            self.atomcoords.append(atomcoords)
            self.atomnos = numpy.array(atomnos, "i")

        if line[0:6] == "$atoms":
            print("parsing atoms")
            line = inputfile.next()
            self.atomlist=[]
            while line[0]!="$":
                temp=line.split()
                at=temp[0]
                atnosstr=temp[1]
                while atnosstr[-1] == ",":
                    line = inputfile.next()
                    temp=line.split()
                    atnosstr=atnosstr+temp[0]
#                print "Debug:", atnosstr
                atlist=self.atlist(atnosstr)

                line = inputfile.next()

                temp=line.split()
#                print "Debug basisname (temp):",temp
                basisname=temp[2]
                ecpname=''
                line = inputfile.next()
                while(line.find('jbas')!=-1 or line.find('ecp')!=-1 or
                      line.find('jkbas')!=-1):
                    if line.find('ecp')!=-1:
                        temp=line.split()
                        ecpname=temp[2]
                    line = inputfile.next()

                self.atomlist.append( (at, basisname, ecpname, atlist))

# I have no idea what this does, so "comment" out
        if line[3:10]=="natoms=":
#        if 0:

            self.natom=int(line[10:])

            basistable=[]

            for i in range(0, self.natom, 1):
                for j in range(0, len(self.atomlist), 1):
                    for k in range(0, len(self.atomlist[j][3]), 1):
                        if self.atomlist[j][3][k]==i:
                            basistable.append((self.atomlist[j][0],
                                                   self.atomlist[j][1],
                                               self.atomlist[j][2]))
            self.aonames=[]
            counter=1
            for a, b, c in basistable:
                ncore=0
                if len(c) > 0:
                    for i in range(0, len(self.ecp_lib), 1):
                        if self.ecp_lib[i][0]==a and \
                           self.ecp_lib[i][1]==c:
                            ncore=self.ecp_lib[i][2]
                           
                for i in range(0, len(self.basis_lib), 1):
                    if self.basis_lib[i].atname==a and self.basis_lib[i].basis_name==b:
                        pa=a.capitalize()
                        basis=self.basis_lib[i]

                        s_counter=1
                        p_counter=2
                        d_counter=3
                        f_counter=4
                        g_counter=5
# this is a really ugly piece of code to assign the right labels to
# basis functions on atoms with an ecp
                        if ncore == 2:
                            s_counter=2
                        elif ncore == 10:
                            s_counter=3
                            p_counter=3
                        elif ncore == 18:
                            s_counter=4
                            p_counter=4
                        elif ncore == 28:
                            s_counter=4
                            p_counter=4
                            d_counter=4
                        elif ncore == 36:
                            s_counter=5
                            p_counter=5
                            d_counter=5
                        elif ncore == 46:
                            s_counter=5
                            p_counter=5
                            d_counter=6
                            
                        for j in range(0, len(basis.symmetries), 1):
                            if basis.symmetries[j]=='s':
                                self.aonames.append("%s%d_%d%s" % \
                                              (pa, counter, s_counter, "S"))
                                s_counter=s_counter+1
                            elif basis.symmetries[j]=='p':
                                self.aonames.append("%s%d_%d%s" % \
                                              (pa, counter, p_counter, "PX"))
                                self.aonames.append("%s%d_%d%s" % \
                                              (pa, counter, p_counter, "PY"))
                                self.aonames.append("%s%d_%d%s" % \
                                              (pa, counter, p_counter, "PZ"))
                                p_counter=p_counter+1
                            elif basis.symmetries[j]=='d':
                                self.aonames.append("%s%d_%d%s" % \
                                         (pa, counter, d_counter, "D 0"))
                                self.aonames.append("%s%d_%d%s" % \
                                         (pa, counter, d_counter, "D+1"))
                                self.aonames.append("%s%d_%d%s" % \
                                         (pa, counter, d_counter, "D-1"))
                                self.aonames.append("%s%d_%d%s" % \
                                         (pa, counter, d_counter, "D+2"))
                                self.aonames.append("%s%d_%d%s" % \
                                         (pa, counter, d_counter, "D-2"))
                                d_counter=d_counter+1
                            elif basis.symmetries[j]=='f':
                                 self.aonames.append("%s%d_%d%s" % \
                                       (pa, counter, f_counter, "F 0"))
                                 self.aonames.append("%s%d_%d%s" % \
                                       (pa, counter, f_counter, "F+1"))
                                 self.aonames.append("%s%d_%d%s" % \
                                       (pa, counter, f_counter, "F-1"))
                                 self.aonames.append("%s%d_%d%s" % \
                                       (pa, counter, f_counter, "F+2"))
                                 self.aonames.append("%s%d_%d%s" % \
                                       (pa, counter, f_counter, "F-2"))
                                 self.aonames.append("%s%d_%d%s" % \
                                       (pa, counter, f_counter, "F+3"))
                                 self.aonames.append("%s%d_%d%s" % \
                                        (pa, counter, f_counter, "F-3"))
                            elif basis.symmetries[j]=='g':
                                self.aonames.append("%s%d_%d%s" % \
                                       (pa, counter, f_counter, "G 0"))
                                self.aonames.append("%s%d_%d%s" % \
                                       (pa, counter, f_counter, "G+1"))
                                self.aonames.append("%s%d_%d%s" % \
                                       (pa, counter, f_counter, "G-1"))
                                self.aonames.append("%s%d_%d%s" % \
                                        (pa, counter, g_counter, "G+2"))
                                self.aonames.append("%s%d_%d%s" % \
                                         (pa, counter, g_counter, "G-2"))
                                self.aonames.append("%s%d_%d%s" % \
                                         (pa, counter, g_counter, "G+3"))
                                self.aonames.append("%s%d_%d%s" % \
                                          (pa, counter, g_counter, "G-3"))
                                self.aonames.append("%s%d_%d%s" % \
                                          (pa, counter, g_counter, "G+4"))
                                self.aonames.append("%s%d_%d%s" % \
                                          (pa, counter, g_counter, "G-4"))
                        break
                counter=counter+1
                
        if line=="$closed shells\n":
            line = inputfile.next()
            temp = line.split()
            occs = int(temp[1][2:])
            self.homos = numpy.array([occs-1], "i")

        if line == "$alpha shells\n":
            line = inputfile.next()
            temp = line.split()
            occ_a = int(temp[1][2:])
            line = inputfile.next() # should be $beta shells
            line = inputfile.next() # the beta occs
            temp = line.split()
            occ_b = int(temp[1][2:])
            self.homos = numpy.array([occ_a-1,occ_b-1], "i")

        if line[12:24]=="OVERLAP(CAO)":
            line = inputfile.next()
            line = inputfile.next()
            overlaparray=[]
            self.aooverlaps=numpy.zeros( (self.nbasis, self.nbasis), "d")
            while line != "       ----------------------\n":
                temp=line.split()
                overlaparray.extend(map(float, temp))
                line = inputfile.next()
            counter=0

            for i in range(0, self.nbasis, 1):
                for j in range(0, i+1, 1):
                    self.aooverlaps[i][j]=overlaparray[counter]
                    self.aooverlaps[j][i]=overlaparray[counter]
                    counter=counter+1

        if ( line[0:6] == "$scfmo" or line[0:12] == "$uhfmo_alpha" ) and line.find("scf") > 0:
            temp = line.split()

            if temp[1][0:7] == "scfdump":
#                self.logger.warning("SCF not converged?")
                print("SCF not converged?!")

            if line[0:12] == "$uhfmo_alpha": # if unrestricted, create flag saying so
                unrestricted = 1
            else:
                unrestricted = 0

            self.moenergies=[]
            self.mocoeffs=[]

            for spin in range(unrestricted + 1): # make sure we cover all instances
                title = inputfile.next()
                while(title[0] == "#"):
                    title = inputfile.next()

#                mocoeffs = numpy.zeros((self.nbasis, self.nbasis), "d")
                moenergies = []
                moarray=[]

                if spin == 1 and title[0:11] == "$uhfmo_beta":
                    title = inputfile.next()
                    while title[0] == "#":
                        title = inputfile.next()

                while(title[0] != '$'):
                    temp=title.split()

                    orb_symm=temp[1]

                    try:
                        energy = float(temp[2][11:].replace("D", "E"))
                    except ValueError:
                        print(spin, ": ", title)

                    orb_en = utils.convertor(energy,"hartree","eV")

                    moenergies.append(orb_en)
                    single_mo = []
                    
                    while(len(single_mo)<self.nbasis):
                        self.updateprogress(inputfile, "Coefficients", self.cupdate)
                        title = inputfile.next()
                        lines_coeffs=self.split_molines(title)
                        single_mo.extend(lines_coeffs)
                        
                    moarray.append(single_mo)
                    title = inputfile.next()

#                for i in range(0, len(moarray), 1):
#                    for j in range(0, self.nbasis, 1):
#                        try:
#                            mocoeffs[i][j]=moarray[i][j]
#                        except IndexError:
#                            print "Index Error in mocoeffs.", spin, i, j
#                            break

                mocoeffs = numpy.array(moarray,"d")
                self.mocoeffs.append(mocoeffs)
                self.moenergies.append(moenergies)

        if line[26:49] == "a o f o r c e - program":
            self.vibirs = []
            self.vibfreqs = []
            self.vibsyms = []
            self.vibdisps = []

#            while line[3:31] != "****  force : all done  ****":

        if line[12:26] == "ATOMIC WEIGHTS":
#begin parsing atomic weights
           self.vibmasses=[]
           line=inputfile.next() # lines =======
           line=inputfile.next() # notes
           line=inputfile.next() # start reading
           temp=line.split()
           while(len(temp) > 0):
                self.vibmasses.append(float(temp[2]))
                line=inputfile.next()
                temp=line.split()

        if line[5:14] == "frequency":
            if not hasattr(self,"vibfreqs"):
                self.vibfreqs = []
                self.vibfreqs = []
                self.vibsyms = []
                self.vibdisps = []
                self.vibirs = []

            temp=line.replace("i","-").split()

            freqs = [self.float(f) for f in temp[1:]]
            self.vibfreqs.extend(freqs)
                    
            line=inputfile.next()
            line=inputfile.next()

            syms=line.split()
            self.vibsyms.extend(syms[1:])

            line=inputfile.next()
            line=inputfile.next()
            line=inputfile.next()
            line=inputfile.next()

            temp=line.split()
            irs = [self.float(f) for f in temp[2:]]
            self.vibirs.extend(irs)

            line=inputfile.next()
            line=inputfile.next()
            line=inputfile.next()
            line=inputfile.next()

            x=[]
            y=[]
            z=[]

            line=inputfile.next()
            while len(line) > 1:
                temp=line.split()
                x.append(map(float, temp[3:]))

                line=inputfile.next()
                temp=line.split()
                y.append(map(float, temp[1:]))

                line=inputfile.next()
                temp=line.split()
                z.append(map(float, temp[1:]))
                line=inputfile.next()

# build xyz vectors for each mode

            for i in range(0, len(x[0]), 1):
                disp=[]
                for j in range(0, len(x), 1):
                    disp.append( [x[j][i], y[j][i], z[j][i]])
                self.vibdisps.append(disp)
Example #52
0
 def test_convertor(self):
     self.assertEqual("%.3f" % utils.convertor(8.0, "eV", "cm-1"),
                      "64524.354")
Example #53
0
def read_scan_logfile(logfiles, structure):
    """ parses Guassian09 torsion-scan log file

    parameters
    ----------
    logfiles: str of list of str
                Name of Guassian 09 torsion scan log file
    structure: charmm psf file

    returns
    -------
    TorsionScanSet
    """
    topology = md.load_psf(structure)
    structure = CharmmPsfFile(structure)
    positions = np.ndarray((0, topology.n_atoms, 3))
    qm_energies = np.ndarray(0)
    torsions = np.ndarray((0, 4), dtype=int)
    directions = np.ndarray(0, dtype=int)
    steps = np.ndarray((0, 3), dtype=int)

    if type(logfiles) != list:
        logfiles = [logfiles]

    for file in logfiles:
        print("loading %s" % file)
        direction = np.ndarray(1)
        torsion = np.ndarray((1, 4), dtype=int)
        step = []
        index = (2, 12, -1)
        f = file.split("/")[-1].split(".")
        if f[2] == "pos":
            direction[0] = 1
        else:
            direction[0] = 0

        fi = open(file, "r")
        for line in fi:

            if re.search("   Scan   ", line):
                t = line.split()[2].split(",")
                t[0] = t[0][-1]
                t[-1] = t[-1][0]
                for i in range(len(t)):
                    torsion[0][i] = int(t[i]) - 1
            if re.search("Step", line):
                try:
                    step = np.array(([int(line.rsplit()[j]) for j in index]))
                    step = step[np.newaxis, :]
                    steps = np.append(steps, step, axis=0)
                except:
                    pass
        fi.close()

        log = Gaussian(file)
        data = log.parse()
        # convert angstroms to nanometers
        positions = np.append(positions, data.atomcoords * 0.1, axis=0)
        qm_energies = np.append(
            qm_energies,
            (convertor(data.scfenergies, "eV", "kJmol-1") - min(convertor(data.scfenergies, "eV", "kJmol-1"))),
            axis=0,
        )
        for i in range(len(data.scfenergies)):
            torsions = np.append(torsions, torsion, axis=0)
            directions = np.append(directions, direction, axis=0)

    return TorsionScanSet(positions, topology, structure, torsions, directions, steps, qm_energies)
Example #54
0
    def _parse_orbitals(self, inputfile, line):
        # From this block aonames, atombasis, moenergies and mocoeffs can be parsed. The data is
        # flipped compared to most programs (GAMESS, Gaussian), since the MOs are in rows. Also, Molpro
        # does not cut the table into parts, rather each MO row has as many lines as it takes ro print
        # all of the MO coefficients. Each row normally has 10 coefficients, although this can be less
        # for the last row and when symmetry is used (each irrep has its own block).
        #
        # ELECTRON ORBITALS
        # =================
        #
        #
        #   Orb  Occ    Energy  Couls-En    Coefficients
        #
        #                                   1 1s      1 1s      1 2px     1 2py     1 2pz     2 1s   (...)
        #                                   3 1s      3 1s      3 2px     3 2py     3 2pz     4 1s   (...)
        # (...)
        #
        #   1.1   2   -11.0351  -43.4915  0.701460  0.025696 -0.000365 -0.000006  0.000000  0.006922 (...)
        #                                -0.006450  0.004742 -0.001028 -0.002955  0.000000 -0.701460 (...)
        # (...)
        #
        # If an MCSCF calculation was performed, the natural orbitals
        # (coefficients and occupation numbers) are printed in a
        # format nearly identical to the ELECTRON ORBITALS section.
        #
        # NATURAL ORBITALS (state averaged)
        # =================================
        #
        #   Orb     Occ        Energy       Coefficients
        #
        #                                   1 s       1 s       1 s       1 z       1 z       1 xx      1 yy      1 zz      2 s       2 s
        #                                   2 s       2 z       2 z       2 xx      2 yy      2 zz      3 s       3 s       3 z       3 y
        #
        #   1.1  2.00000   -20.678730     0.000141 -0.000057  0.001631 -0.001377  0.001117  0.000029  0.000293 -0.000852  1.000748  0.001746
        #                                -0.002552 -0.002005  0.001658 -0.001266 -0.001274 -0.001001  0.000215 -0.000131 -0.000242 -0.000126
        #
        #   2.1  2.00000   -11.322823     1.000682  0.004626 -0.000485  0.006634 -0.002096 -0.003072 -0.003282 -0.001724 -0.000181  0.006734
        #                                -0.002398 -0.000527  0.001335  0.000091  0.000058  0.000396 -0.003219  0.000981  0.000250 -0.000191
        # (...)

        # The assigment of final cclib attributes is different for
        # canonical/natural orbitals.
        self.naturalorbitals = (line[1:17] == "NATURAL ORBITALS")
        # Make sure we didn't get here by mistake.
        assert line[1:18] == "ELECTRON ORBITALS" or self.electronorbitals or self.naturalorbitals

        # For unrestricted calculations, ELECTRON ORBITALS is followed on the same line
        # by FOR POSITIVE SPIN or FOR NEGATIVE SPIN as appropriate.
        spin = (line[19:36] == "FOR NEGATIVE SPIN") or (self.electronorbitals[19:36] == "FOR NEGATIVE SPIN")

        if self.naturalorbitals:
            self.skip_lines(inputfile, ['equals', 'b', 'headers', 'b'])
        else:
            if not self.electronorbitals:
                self.skip_line(inputfile, 'equals')
            self.skip_lines(inputfile, ['b', 'b', 'headers', 'b'])

        aonames = []
        atombasis = [[] for i in range(self.natom)]
        moenergies = []
        # Use for both canonical and natural orbital coefficients.
        mocoeffs = []
        occnos = []
        line = next(inputfile)

        # Besides a double blank line, stop when the next orbitals are encountered for unrestricted jobs
        # or if there are stars on the line which always signifies the end of the block.
        while line.strip() and (not "ORBITALS" in line) and (not set(line.strip()) == {'*'}):

            # The function names are normally printed just once, but if symmetry is used then each irrep
            # has its own mocoeff block with a preceding list of names.
            is_aonames = line[:25].strip() == ""
            if is_aonames:

                # We need to save this offset for parsing the coefficients later.
                offset = len(aonames)

                aonum = len(aonames)
                while line.strip():
                    for s in line.split():
                        if s.isdigit():
                            atomno = int(s)
                            atombasis[atomno-1].append(aonum)
                            aonum += 1
                        else:
                            functype = s
                            element = self.table.element[self.atomnos[atomno-1]]
                            aoname = "%s%i_%s" % (element, atomno, functype)
                            aonames.append(aoname)
                    line = next(inputfile)

                # Now there can be one or two blank lines.
                while not line.strip():
                    line = next(inputfile)

            # Newer versions of Molpro (for example, 2012 test files) will print some
            # more things here, such as H**O and LUMO, but these have less than 10 columns.
            if "H**O" in line or "LUMO" in line:
                break

            # End of the NATURAL ORBITALS section.
            if "Natural orbital dump" in line:
                break

            # Now parse the MO coefficients, padding the list with an appropriate amount of zeros.
            coeffs = [0.0 for i in range(offset)]
            while line.strip() != "":
                if line[:31].rstrip():
                    tokens = line.split()
                    moenergy = float(tokens[2])
                    moenergy = utils.convertor(moenergy, "hartree", "eV")
                    moenergies.append(moenergy)
                    if self.naturalorbitals:
                        occno = float(tokens[1])
                        occnos.append(occno)

                # Coefficients are in 10.6f format and splitting does not work since there are not
                # always spaces between them. If the numbers are very large, there will be stars.
                str_coeffs = line[31:]
                ncoeffs = len(str_coeffs) // 10
                coeff = []
                for ic in range(ncoeffs):
                    p = str_coeffs[ic*10:(ic+1)*10]
                    try:
                        c = float(p)
                    except ValueError as detail:
                        self.logger.warn("setting coeff element to zero: %s" % detail)
                        c = 0.0
                    coeff.append(c)
                coeffs.extend(coeff)
                line = next(inputfile)
            mocoeffs.append(coeffs)

            # The loop should keep going until there is a double blank line, and there is
            # a single line between each coefficient block.
            line = next(inputfile)
            if not line.strip():
                line = next(inputfile)

        # If symmetry was used (offset was needed) then we will need to pad all MO vectors
        # up to nbasis for all irreps before the last one.
        if offset > 0:
            for im, m in enumerate(mocoeffs):
                if len(m) < self.nbasis:
                    mocoeffs[im] = m + [0.0 for i in range(self.nbasis - len(m))]

        self.set_attribute('atombasis', atombasis)
        self.set_attribute('aonames', aonames)

        if self.naturalorbitals:
            # Consistent with current cclib conventions, keep only the
            # last possible set of natural orbital coefficients and
            # occupation numbers.
            self.nocoeffs = mocoeffs
            self.nooccnos = occnos
        else:
            # Consistent with current cclib conventions, reset moenergies/mocoeffs if they have been
            # previously parsed, since we want to produce only the final values.
            if not hasattr(self, "moenergies") or spin == 0:
                self.mocoeffs = []
                self.moenergies = []
            self.moenergies.append(moenergies)
            self.mocoeffs.append(mocoeffs)

        # Check if last line begins the next ELECTRON ORBITALS section, because we already used
        # this line and need to know when this method is called next time.
        if line[1:18] == "ELECTRON ORBITALS":
            self.electronorbitals = line
        else:
            self.electronorbitals = ""

        return
Example #55
0
    def extract(self, inputfile, line):
        """Extract information from the file object inputfile."""

        # Extract the package version number.
        if "Jaguar version" in line:
            tokens = line.split()
            # Don't add revision information to the main package
            # version for now.
            # package_version = "{}.r{}".format(tokens[3][:-1], tokens[5])
            package_version = tokens[3][:-1]
            self.metadata["package_version"] = package_version

        # Extract the basis set name
        if line[2:12] == "basis set:":
            self.metadata["basis_set"] = line.split()[2]

        # Extract charge and multiplicity
        if line[2:22] == "net molecular charge":
            self.set_attribute('charge', int(line.split()[-1]))
            self.set_attribute('mult', int(next(inputfile).split()[-1]))

        # The Gaussian basis set information is printed before the geometry, and we need
        # to do some indexing to get this into cclib format, because fn increments
        # for each engular momentum, but cclib does not (we have just P instead of
        # all three X/Y/Z with the same parameters. On the other hand, fn enumerates
        # the atomic orbitals correctly, so use it to build atombasis.
        #
        #  Gaussian basis set information
        #
        #                                                        renorm    mfac*renorm
        #   atom    fn   prim  L        z            coef         coef         coef
        # -------- ----- ---- --- -------------  -----------  -----------  -----------
        # C1           1    1   S  7.161684E+01   1.5433E-01   2.7078E+00   2.7078E+00
        # C1           1    2   S  1.304510E+01   5.3533E-01   2.6189E+00   2.6189E+00
        # ...
        # C1           3    6   X  2.941249E+00   2.2135E-01   1.2153E+00   1.2153E+00
        #              4        Y                                           1.2153E+00
        #              5        Z                                           1.2153E+00
        # C1           2    8   S  2.222899E-01   1.0000E+00   2.3073E-01   2.3073E-01
        # C1           3    7   X  6.834831E-01   8.6271E-01   7.6421E-01   7.6421E-01
        # ...
        # C2           6    1   S  7.161684E+01   1.5433E-01   2.7078E+00   2.7078E+00
        # ...
        #
        if line.strip() == "Gaussian basis set information":

            self.skip_lines(inputfile, ['b', 'renorm', 'header', 'd'])

            # This is probably the only place we can get this information from Jaguar.
            self.gbasis = []

            atombasis = []
            line = next(inputfile)
            fn_per_atom = []
            while line.strip():

                if len(line.split()) > 3:

                    aname = line.split()[0]
                    fn = int(line.split()[1])
                    prim = int(line.split()[2])
                    L = line.split()[3]
                    z = float(line.split()[4])
                    coef = float(line.split()[5])

                    # The primitive count is reset for each atom, so use that for adding
                    # new elements to atombasis and gbasis. We could also probably do this
                    # using the atom name, although that perhaps might not always be unique.
                    if prim == 1:
                        atombasis.append([])
                        fn_per_atom = []
                        self.gbasis.append([])

                    # Remember that fn is repeated when functions are contracted.
                    if not fn-1 in atombasis[-1]:
                        atombasis[-1].append(fn-1)

                    # Here we use fn only to know when a new contraction is encountered,
                    # so we don't need to decrement it, and we don't even use all values.
                    # What's more, since we only wish to save the parameters for each subshell
                    # once, we don't even need to consider lines for orbitals other than
                    # those for X*, making things a bit easier.
                    if not fn in fn_per_atom:
                        fn_per_atom.append(fn)
                        label = {'S': 'S', 'X': 'P', 'XX': 'D', 'XXX': 'F'}[L]
                        self.gbasis[-1].append((label, []))
                    igbasis = fn_per_atom.index(fn)
                    self.gbasis[-1][igbasis][1].append([z, coef])

                else:

                    fn = int(line.split()[0])
                    L = line.split()[1]

                    # Some AO indices are only printed in these lines, for L > 0.
                    if not fn-1 in atombasis[-1]:
                        atombasis[-1].append(fn-1)

                line = next(inputfile)

            # The indices for atombasis can also be read later from the molecular orbital output.
            self.set_attribute('atombasis', atombasis)

            # This length of atombasis should always be the number of atoms.
            self.set_attribute('natom', len(self.atombasis))

        #  Effective Core Potential
        #
        #  Atom      Electrons represented by ECP
        # Mo                    36
        #              Maximum angular term         3
        # F Potential      1/r^n   Exponent  Coefficient
        #                  -----   --------  -----------
        #                    0  140.4577691   -0.0469492
        #                    1   89.4739342  -24.9754989
        # ...
        # S-F Potential    1/r^n   Exponent  Coefficient
        #                  -----   --------  -----------
        #                    0   33.7771969    2.9278406
        #                    1   10.0120020   34.3483716
        # ...
        # O                      0
        # Cl                    10
        #              Maximum angular term         2
        # D Potential      1/r^n   Exponent  Coefficient
        #                  -----   --------  -----------
        #                    1   94.8130000  -10.0000000
        # ...
        if line.strip() == "Effective Core Potential":

            self.skip_line(inputfile, 'blank')
            line = next(inputfile)
            assert line.split()[0] == "Atom"
            assert " ".join(line.split()[1:]) == "Electrons represented by ECP"

            self.coreelectrons = []
            line = next(inputfile)
            while line.strip():
                if len(line.split()) == 2:
                    self.coreelectrons.append(int(line.split()[1]))
                line = next(inputfile)

        if line[2:14] == "new geometry" or line[1:21] == "Symmetrized geometry" or line.find("Input geometry") > 0:
        # Get the atom coordinates
            if not hasattr(self, "atomcoords") or line[1:21] == "Symmetrized geometry":
                # Wipe the "Input geometry" if "Symmetrized geometry" present
                self.atomcoords = []
            p = re.compile("(\D+)\d+")  # One/more letters followed by a number
            atomcoords = []
            atomnos = []
            angstrom = next(inputfile)
            title = next(inputfile)
            line = next(inputfile)
            while line.strip():
                temp = line.split()
                element = p.findall(temp[0])[0]
                atomnos.append(self.table.number[element])
                atomcoords.append(list(map(float, temp[1:])))
                line = next(inputfile)
            self.atomcoords.append(atomcoords)
            self.atomnos = numpy.array(atomnos, "i")
            self.set_attribute('natom', len(atomcoords))

        # Hartree-Fock energy after SCF
        if line[1:18] == "SCFE: SCF energy:":
            self.metadata["methods"].append("HF")
            if not hasattr(self, "scfenergies"):
                self.scfenergies = []
            temp = line.strip().split()
            scfenergy = float(temp[temp.index("hartrees") - 1])
            scfenergy = utils.convertor(scfenergy, "hartree", "eV")
            self.scfenergies.append(scfenergy)

        # Energy after LMP2 correction
        if line[1:18] == "Total LMP2 Energy":
            self.metadata["methods"].append("LMP2")
            if not hasattr(self, "mpenergies"):
                self.mpenergies = [[]]
            lmp2energy = float(line.split()[-1])
            lmp2energy = utils.convertor(lmp2energy, "hartree", "eV")
            self.mpenergies[-1].append(lmp2energy)

        if line[15:45] == "Geometry optimization complete":
            if not hasattr(self, 'optdone'):
                self.optdone = []
            self.optdone.append(len(self.geovalues) - 1)

        if line.find("number of occupied orbitals") > 0:
        # Get number of MOs
            occs = int(line.split()[-1])
            line = next(inputfile)
            virts = int(line.split()[-1])
            self.nmo = occs + virts
            self.homos = numpy.array([occs-1], "i")

            self.unrestrictedflag = False

        if line[1:28] == "number of occupied orbitals":
            self.homos = numpy.array([float(line.strip().split()[-1])-1], "i")

        if line[2:27] == "number of basis functions":
            nbasis = int(line.strip().split()[-1])
            self.set_attribute('nbasis', nbasis)

        if line.find("number of alpha occupied orb") > 0:
        # Get number of MOs for an unrestricted calc

            aoccs = int(line.split()[-1])
            line = next(inputfile)
            avirts = int(line.split()[-1])
            line = next(inputfile)
            boccs = int(line.split()[-1])
            line = next(inputfile)
            bvirt = int(line.split()[-1])

            self.nmo = aoccs + avirts
            self.homos = numpy.array([aoccs-1, boccs-1], "i")
            self.unrestrictedflag = True

        if line[0:4] == "etot":
        # Get SCF convergence information
            if not hasattr(self, "scfvalues"):
                self.scfvalues = []
                self.scftargets = [[5E-5, 5E-6]]
            values = []
            while line[0:4] == "etot":
        # Jaguar 4.2
        # etot   1  N  N  0  N  -382.08751886450           2.3E-03  1.4E-01
        # etot   2  Y  Y  0  N  -382.27486023153  1.9E-01  1.4E-03  5.7E-02
        # Jaguar 6.5
        # etot   1  N  N  0  N    -382.08751881733           2.3E-03  1.4E-01
        # etot   2  Y  Y  0  N    -382.27486018708  1.9E-01  1.4E-03  5.7E-02
                temp = line.split()[7:]
                if len(temp) == 3:
                    denergy = float(temp[0])
                else:
                    denergy = 0  # Should really be greater than target value
                                 # or should we just ignore the values in this line
                ddensity = float(temp[-2])
                maxdiiserr = float(temp[-1])
                if not self.geoopt:
                    values.append([denergy, ddensity])
                else:
                    values.append([ddensity])
                try:
                    line = next(inputfile)
                except StopIteration:
                    self.logger.warning('File terminated before end of last SCF! Last error: {}'.format(maxdiiserr))
                    break
            self.scfvalues.append(values)

        # MO energies and symmetries.
        # Jaguar 7.0: provides energies and symmetries for both
        #   restricted and unrestricted calculations, like this:
        #     Alpha Orbital energies/symmetry label:
        #     -10.25358 Bu  -10.25353 Ag  -10.21931 Bu  -10.21927 Ag
        #     -10.21792 Bu  -10.21782 Ag  -10.21773 Bu  -10.21772 Ag
        #     ...
        # Jaguar 6.5: prints both only for restricted calculations,
        #   so for unrestricted calculations the output it looks like this:
        #     Alpha Orbital energies:
        #     -10.25358  -10.25353  -10.21931  -10.21927  -10.21792  -10.21782
        #     -10.21773  -10.21772  -10.21537  -10.21537   -1.02078   -0.96193
        #     ...
        # Presence of 'Orbital energies' is enough to catch all versions.
        if "Orbital energies" in line:

            # Parsing results is identical for restricted/unrestricted
            #   calculations, just assert later that alpha/beta order is OK.
            spin = int(line[2:6] == "Beta")

            # Check if symmetries are printed also.
            issyms = "symmetry label" in line

            if not hasattr(self, "moenergies"):
                self.moenergies = []
            if issyms and not hasattr(self, "mosyms"):
                    self.mosyms = []

            # Grow moeneriges/mosyms and make sure they are empty when
            #   parsed multiple times - currently cclib returns only
            #   the final output (ex. in a geomtry optimization).
            if len(self.moenergies) < spin+1:
                self.moenergies.append([])
            self.moenergies[spin] = []
            if issyms:
                if len(self.mosyms) < spin+1:
                    self.mosyms.append([])
                self.mosyms[spin] = []

            line = next(inputfile).split()
            while len(line) > 0:
                if issyms:
                    energies = [float(line[2*i]) for i in range(len(line)//2)]
                    syms = [line[2*i+1] for i in range(len(line)//2)]
                else:
                    energies = [float(e) for e in line]
                energies = [utils.convertor(e, "hartree", "eV") for e in energies]
                self.moenergies[spin].extend(energies)
                if issyms:
                    syms = [self.normalisesym(s) for s in syms]
                    self.mosyms[spin].extend(syms)
                line = next(inputfile).split()

            line = next(inputfile)

        # The second trigger string is in the version 8.3 unit test and the first one was
        # encountered in version 6.x and is followed by a bit different format. In particular,
        # the line with occupations is missing in each block. Here is a fragment of this block
        # from version 8.3:
        #
        # *****************************************
        #
        # occupied + virtual orbitals: final wave function
        #
        # *****************************************
        #
        #
        #                              1         2         3         4         5
        #  eigenvalues-            -11.04064 -11.04058 -11.03196 -11.03196 -11.02881
        #  occupations-              2.00000   2.00000   2.00000   2.00000   2.00000
        #    1 C1               S    0.70148   0.70154  -0.00958  -0.00991   0.00401
        #    2 C1               S    0.02527   0.02518   0.00380   0.00374   0.00371
        # ...
        #
        if line.find("Occupied + virtual Orbitals- final wvfn") > 0 or \
           line.find("occupied + virtual orbitals: final wave function") > 0:

            self.skip_lines(inputfile, ['b', 's', 'b', 'b'])

            if not hasattr(self, "mocoeffs"):
                self.mocoeffs = []

            aonames = []
            lastatom = "X"

            readatombasis = False
            if not hasattr(self, "atombasis"):
                self.atombasis = []
                for i in range(self.natom):
                    self.atombasis.append([])
                readatombasis = True

            offset = 0

            spin = 1 + int(self.unrestrictedflag)
            for s in range(spin):
                mocoeffs = numpy.zeros((len(self.moenergies[s]), self.nbasis), "d")

                if s == 1:  # beta case
                    self.skip_lines(inputfile, ['s', 'b', 'title', 'b', 's', 'b', 'b'])

                for k in range(0, len(self.moenergies[s]), 5):
                    self.updateprogress(inputfile, "Coefficients")

                    # All known version have a line with indices followed by the eigenvalues.
                    self.skip_lines(inputfile, ['numbers', 'eigens'])

                    # Newer version also have a line with occupation numbers here.
                    line = next(inputfile)
                    if "occupations-" in line:
                        line = next(inputfile)

                    for i in range(self.nbasis):

                        info = line.split()

                        # Fill atombasis only first time around.
                        if readatombasis and k == 0:
                            orbno = int(info[0])
                            atom = info[1]
                            if atom[1].isalpha():
                                atomno = int(atom[2:])
                            else:
                                atomno = int(atom[1:])
                            self.atombasis[atomno-1].append(orbno-1)

                        if not hasattr(self, "aonames"):
                            if lastatom != info[1]:
                                scount = 1
                                pcount = 3
                                dcount = 6  # six d orbitals in Jaguar

                            if info[2] == 'S':
                                aonames.append("%s_%i%s" % (info[1], scount, info[2]))
                                scount += 1

                            if info[2] == 'X' or info[2] == 'Y' or info[2] == 'Z':
                                aonames.append("%s_%iP%s" % (info[1], pcount / 3, info[2]))
                                pcount += 1

                            if info[2] == 'XX' or info[2] == 'YY' or info[2] == 'ZZ' or \
                               info[2] == 'XY' or info[2] == 'XZ' or info[2] == 'YZ':

                                aonames.append("%s_%iD%s" % (info[1], dcount / 6, info[2]))
                                dcount += 1

                            lastatom = info[1]

                        for j in range(len(info[3:])):
                            mocoeffs[j+k, i] = float(info[3+j])

                        line = next(inputfile)

                    if not hasattr(self, "aonames"):
                        self.aonames = aonames

                    offset += 5
                self.mocoeffs.append(mocoeffs)

        #  Atomic charges from Mulliken population analysis:
        #
        # Atom       C1           C2           C3           C4           C5
        # Charge    0.00177     -0.06075     -0.05956      0.00177     -0.06075
        #
        # Atom       H6           H7           H8           C9           C10
        # ...
        if line.strip() == "Atomic charges from Mulliken population analysis:":

            if not hasattr(self, 'atomcharges'):
                self.atomcharges = {}

            charges = []
            self.skip_line(inputfile, "blank")
            line = next(inputfile)
            while "sum of atomic charges" not in line:
                assert line.split()[0] == "Atom"
                line = next(inputfile)
                assert line.split()[0] == "Charge"
                charges.extend([float(c) for c in line.split()[1:]])
                self.skip_line(inputfile, "blank")
                line = next(inputfile)

            self.atomcharges['mulliken'] = charges

        if (line[2:6] == "olap") or (line.strip() == "overlap matrix:"):

            if line[6] == "-":
                return
                # This was continue (in loop) before parser refactoring.
                # continue # avoid "olap-dev"
            self.aooverlaps = numpy.zeros((self.nbasis, self.nbasis), "d")

            for i in range(0, self.nbasis, 5):
                self.updateprogress(inputfile, "Overlap")

                self.skip_lines(inputfile, ['b', 'header'])

                for j in range(i, self.nbasis):
                    temp = list(map(float, next(inputfile).split()[1:]))
                    self.aooverlaps[j, i:(i+len(temp))] = temp
                    self.aooverlaps[i:(i+len(temp)), j] = temp

        if line[2:24] == "start of program geopt":
            if not self.geoopt:
                # Need to keep only the RMS density change info
                # if this is a geooptz
                self.scftargets = [[self.scftargets[0][0]]]
                if hasattr(self, "scfvalues"):
                    self.scfvalues[0] = [[x[0]] for x in self.scfvalues[0]]
                self.geoopt = True
            else:
                self.scftargets.append([5E-5])

        # Get Geometry Opt convergence information
        #
        #  geometry optimization step  7
        #  energy:            -382.30219111487 hartrees
        #  [ turning on trust-radius adjustment ]
        #  ** restarting optimization from step    6 **
        #
        #
        #  Level shifts adjusted to satisfy step-size constraints
        #   Step size:    0.0360704
        #   Cos(theta):   0.8789215
        #   Final level shift:  -8.6176299E-02
        #
        #  energy change:           2.5819E-04 .  (  5.0000E-05 )
        #  gradient maximum:        5.0947E-03 .  (  4.5000E-04 )
        #  gradient rms:            1.2996E-03 .  (  3.0000E-04 )
        #  displacement maximum:    1.3954E-02 .  (  1.8000E-03 )
        #  displacement rms:        4.6567E-03 .  (  1.2000E-03 )
        #
        if line[2:28] == "geometry optimization step":

            if not hasattr(self, "geovalues"):
                self.geovalues = []
                self.geotargets = numpy.zeros(5, "d")

            gopt_step = int(line.split()[-1])

            energy = next(inputfile)
            blank = next(inputfile)

            # A quick hack for messages that show up right after the energy
            # at this point, which include:
            #   ** restarting optimization from step    2 **
            #   [ turning on trust-radius adjustment ]
            # as found in regression file ptnh3_2_H2O_2_2plus.out and other logfiles.
            restarting_from_1 = False
            while blank.strip():
                if blank.strip() == "** restarting optimization from step    1 **":
                    restarting_from_1 = True
                blank = next(inputfile)

            # One or more blank lines, depending on content.
            line = next(inputfile)
            while not line.strip():
                line = next(inputfile)

            # Note that the level shift message is followed by a blank, too.
            if "Level shifts adjusted" in line:
                while line.strip():
                    line = next(inputfile)
                line = next(inputfile)

            # The first optimization step does not produce an energy change, and
            # ther is also no energy change when the optimization is restarted
            # from step 1 (since step 1 had no change).
            values = []
            target_index = 0
            if (gopt_step == 1) or restarting_from_1:
                values.append(0.0)
                target_index = 1
            while line.strip():
                if len(line) > 40 and line[41] == "(":
                    # A new geo convergence value
                    values.append(float(line[26:37]))
                    self.geotargets[target_index] = float(line[43:54])
                    target_index += 1
                line = next(inputfile)
            self.geovalues.append(values)

        # IR output looks like this:
        #   frequencies        72.45   113.25   176.88   183.76   267.60   312.06
        #   symmetries       Au       Bg       Au       Bu       Ag       Bg
        #   intensities         0.07     0.00     0.28     0.52     0.00     0.00
        #   reduc. mass         1.90     0.74     1.06     1.42     1.19     0.85
        #   force const         0.01     0.01     0.02     0.03     0.05     0.05
        #   C1       X     0.00000  0.00000  0.00000 -0.05707 -0.06716  0.00000
        #   C1       Y     0.00000  0.00000  0.00000  0.00909 -0.02529  0.00000
        #   C1       Z     0.04792 -0.06032 -0.01192  0.00000  0.00000  0.11613
        #   C2       X     0.00000  0.00000  0.00000 -0.06094 -0.04635  0.00000
        #   ... etc. ...
        # This is a complete ouput, some files will not have intensities,
        #   and older Jaguar versions sometimes skip the symmetries.
        if line[2:23] == "start of program freq":

            self.skip_line(inputfile, 'blank')

            # Version 8.3 has two blank lines here, earlier versions just one.
            line = next(inputfile)
            if not line.strip():
                line = next(inputfile)

            self.vibfreqs = []
            self.vibdisps = []
            forceconstants = False
            intensities = False
            while line.strip():
                if "force const" in line:
                    forceconstants = True
                if "intensities" in line:
                    intensities = True
                line = next(inputfile)

            # In older version, the last block had an extra blank line after it,
            # which could be caught. This is not true in newer version (including 8.3),
            # but in general it would be better to bound this loop more strictly.
            freqs = next(inputfile)
            while freqs.strip() and not "imaginary frequencies" in freqs:

                # Number of modes (columns printed in this block).
                nmodes = len(freqs.split())-1

                # Append the frequencies.
                self.vibfreqs.extend(list(map(float, freqs.split()[1:])))
                line = next(inputfile).split()

                # May skip symmetries (older Jaguar versions).
                if line[0] == "symmetries":
                    if not hasattr(self, "vibsyms"):
                        self.vibsyms = []
                    self.vibsyms.extend(list(map(self.normalisesym, line[1:])))
                    line = next(inputfile).split()
                if intensities:
                    if not hasattr(self, "vibirs"):
                        self.vibirs = []
                    self.vibirs.extend(list(map(float, line[1:])))
                    line = next(inputfile).split()
                if forceconstants:
                    line = next(inputfile)

                # Start parsing the displacements.
                # Variable 'q' holds up to 7 lists of triplets.
                q = [[] for i in range(7)]
                for n in range(self.natom):
                    # Variable 'p' holds up to 7 triplets.
                    p = [[] for i in range(7)]
                    for i in range(3):
                        line = next(inputfile)
                        disps = [float(disp) for disp in line.split()[2:]]
                        for j in range(nmodes):
                            p[j].append(disps[j])
                    for i in range(nmodes):
                        q[i].append(p[i])

                self.vibdisps.extend(q[:nmodes])

                self.skip_line(inputfile, 'blank')
                freqs = next(inputfile)

            # Convert new data to arrays.
            self.vibfreqs = numpy.array(self.vibfreqs, "d")
            self.vibdisps = numpy.array(self.vibdisps, "d")
            if hasattr(self, "vibirs"):
                self.vibirs = numpy.array(self.vibirs, "d")

        # Parse excited state output (for CIS calculations).
        # Jaguar calculates only singlet states.
        if line[2:15] == "Excited State":
            if not hasattr(self, "etenergies"):
                self.etenergies = []
            if not hasattr(self, "etoscs"):
                self.etoscs = []
            if not hasattr(self, "etsecs"):
                self.etsecs = []
                self.etsyms = []
            etenergy = float(line.split()[3])
            etenergy = utils.convertor(etenergy, "eV", "wavenumber")
            self.etenergies.append(etenergy)

            self.skip_lines(inputfile, ['line', 'line', 'line', 'line'])

            line = next(inputfile)
            self.etsecs.append([])
            # Jaguar calculates only singlet states.
            self.etsyms.append('Singlet-A')
            while line.strip() != "":
                fromMO = int(line.split()[0])-1
                toMO = int(line.split()[2])-1
                coeff = float(line.split()[-1])
                self.etsecs[-1].append([(fromMO, 0), (toMO, 0), coeff])
                line = next(inputfile)
            # Skip 3 lines
            for i in range(4):
                line = next(inputfile)
            strength = float(line.split()[-1])
            self.etoscs.append(strength)

        if line[:20] == ' Total elapsed time:' \
                or line[:18] == ' Total cpu seconds':
            self.metadata['success'] = True
    for qmoutputfile in args.qmoutputfile:

        qmoutputfile = os.path.abspath(qmoutputfile)
        print(qmoutputfile)

        job = ccopen(qmoutputfile)

        if job:
            data = job.parse()

            idx_homo = data.homos[-1]
            idx_lumo = idx_homo + 1

            steps = len(data.scfenergies)
            total_e = convertor(data.scfenergies[-1], 'eV', 'hartree')
            h**o = convertor(data.moenergies[-1][idx_homo], 'eV', 'hartree')
            lumo = convertor(data.moenergies[-1][idx_lumo], 'eV', 'hartree')

            job_type = determine_job_type(job)
            total_time = extract_total_time(qmoutputfile, job_type)
            time_per_step = total_time / steps
        else:
            data = parse_non_cclib_output(qmoutputfile)

        print('Time (min): {}'.format(total_time))
        print('Steps     : {}'.format(steps))
        print('per step  : {}'.format(time_per_step))
        print('Total E   : {}'.format(total_e))
        print('H**O      : {}'.format(h**o))
        print('LUMO      : {}'.format(lumo))