Esempio n. 1
0
 def construct_qc(self, all_mo=True):
   '''Converts all global variables to a list of `QCinfo` classes.
   '''
   self.QC = []
   ilumo = None
   for rr in range(len(self.geo_spec_all)):
     qc = QCinfo()
     qc.geo_spec = self.geo_spec_all[rr]
     qc.geo_info = self.geo_info
     qc.ao_spec = self.ao_spec
     qc.mo_spec = []
     for s,ii_s in self.sym.items():
       for i,coeffs in enumerate(self.mo_coeff_all[ii_s][rr]):
         qc.mo_spec.append({'coeffs': coeffs,
                            'energy' : self.mo_energy_all[ii_s][rr,i],
                            'occ_num' : self.mo_occ_all[ii_s][rr,i],
                            'sym': '%d.%s' % (i+1,s)})
     qc.ao_spec.update()
     qc.mo_spec = MOClass(qc.mo_spec)
     qc.mo_spec.update()
     if not all_mo:
       ilumo = max(ilumo or 0, qc.mo_spec.get_lumo())
     
     self.QC.append(qc)
   
   if not all_mo:
     for i in range(len(self.QC)):
       self.QC[i].mo_spec = self.QC[i].mo_spec[slice(None,ilumo)]
   
   return self.QC
Esempio n. 2
0
def read_gaussian_log(fname,
                      all_mo=False,
                      spin=None,
                      orientation='standard',
                      i_link=-1,
                      i_geo=-1,
                      i_ao=-1,
                      i_mo=-1,
                      interactive=True,
                      **kwargs):
    '''Reads all information desired from a Gaussian .log file.

  **Parameters:**
  
    fname: str, file descriptor
      Specifies the filename for the input file.
      fname can also be used with a file descriptor instad of a filename.
    all_mo :  bool, optional
      If True, all molecular orbitals are returned.
    spin : {None, 'alpha', or 'beta'}, optional
      If not None, returns exclusively 'alpha' or 'beta' molecular orbitals.
    orientation : string, choices={'input', 'standard'}, optional
      Specifies orientation of the molecule in Gaussian nomenclature. [#first]_ 
    i_link : int, default=-1
      Selects the file for linked Gaussian jobs.
    i_geo : int, default=-1
      Selects the geometry section of the output file.
    i_ao : int, default=-1
      Selects the atomic orbital section of the output file.
    i_mo : int, default=-1
      Selects the molecular orbital section of the output file.
    interactive : bool
      If True, the user is asked to select the different sets.
  
  **Returns:**
  
    qc (class QCinfo) with attributes geo_spec, geo_info, ao_spec, ao_spherical, mo_spec, etot :
        See :ref:`Central Variables` for details.

.. [#first] Attention: The MOs in the output are only valid for the standard orientation!

  '''

    if isinstance(fname, str):
        filename = fname
        fname = descriptor_from_file(filename, index=0)
    else:
        filename = fname.name

    flines = fname.readlines()  # Read the WHOLE file into RAM
    if isinstance(fname, str):
        fname.close()  # Leave existing file descriptors alive

    # Search the file the specific sections
    count = {
        'link': 0,
        'geometry': 0,
        'geometry_input': 0,
        'atomic orbitals': 0,
        'molecular orbitals': [],
        'state': []
    }

    def check_sel(count, i, interactive=False, default=-1):
        if count == 0:
            raise IndexError
        elif count == 1:
            return 0
        message = '\tPlease give an integer from 0 to {0} (default: {0}): '.format(
            count - 1)

        try:
            if interactive:
                i = raw_input(message)
                i = default if i == '' else int(i)
            i = range(count)[i]
        except (IndexError, ValueError):
            raise IOError(message.replace(':', '!'))
        else:
            display('\tSelecting the %s' %
                    ('last element.' if
                     (i == count - 1) else 'element %d.' % i))
        return i

    # Go through the file line by line
    for il in range(len(flines)):
        line = flines[il]  # The current line as string
        # Check the file for keywords
        if ' Entering Link 1' in line:
            count['link'] += 1

    try:
        display('\tFound %d linked GAUSSIAN files.' % count['link'])
        i_link = check_sel(count['link'], i_link, interactive=interactive)
    except IndexError:
        raise IOError('Found no `Entering Link 1` keyword!')

    cartesian_basis = True
    c_link = 0
    # Go through the file line by line
    for il in range(len(flines)):
        line = flines[il]  # The current line as string
        thisline = line.split()  # The current line split into segments

        # Check the file for keywords
        if ' Entering Link 1' in line:
            c_link += 1
        if i_link == (c_link - 1):
            if ' orientation:' in line:
                if '%s orientation:' % orientation in line.lower():
                    count['geometry'] += 1
                if 'input orientation:' in line.lower():
                    count['geometry_input'] += 1
            elif 'Standard basis:' in line or 'General basis read from cards:' in line:
                # Check if a cartesian basis has been applied
                if '(5D, 7F)' in line:
                    cartesian_basis = False
                elif '(6D, 10F)' not in line:
                    raise IOError(
                        'Please apply a Spherical Harmonics (5D, 7F) or ' +
                        'a Cartesian Gaussian Basis Set (6D, 10F)!')
            elif 'AO basis set' in line:
                count['atomic orbitals'] += 1
            elif 'The electronic state is ' in line:
                count['state'].append(thisline[-1][:-1])
            elif 'Orbital Coefficients:' in line:
                mo_type = thisline[0]
                if mo_type != 'Beta':
                    count['molecular orbitals'].append(mo_type)
                else:
                    count['molecular orbitals'][-1] = 'Alpha&Beta'

    display('\nContent of the GAUSSIAN .log file:')
    display('\tFound %d geometry section(s). (%s orientation)' %
            (count['geometry'], orientation))
    try:
        i_geo = check_sel(count['geometry'], i_geo, interactive=interactive)
    except IndexError:
        count['geometry'] = count['geometry_input']
        orientation = 'input'
        display('\Looking for "Input orientation": \n' +
                '\tFound %d geometry section(s). (%s orientation)' %
                (count['geometry'], orientation))
        try:
            i_geo = check_sel(count['geometry'],
                              i_geo,
                              interactive=interactive)
        except IndexError:
            raise IOError('Found no geometry section!' +
                          ' Are you sure this is a GAUSSIAN .log file?')

    try:
        display('\tFound %d atomic orbitals section(s) %s.' %
                (count['atomic orbitals'],
                 '(6D, 10F)' if cartesian_basis else '(5D, 7F)'))
        i_ao = check_sel(count['atomic orbitals'],
                         i_ao,
                         interactive=interactive)
    except IndexError:
        raise IOError('Write GFINPUT in your GAUSSIAN route section to print' +
                      ' the basis set information!')

    try:
        display('\tFound the following %d molecular orbitals section(s):' %
                len(count['molecular orbitals']))
    except IndexError:
        raise IOError(
            'Write IOP(6/7=3) in your GAUSSIAN route section to print\n' +
            ' all molecular orbitals!')
    for i, j in enumerate(count['molecular orbitals']):
        string = '\t\tSection %d: %s Orbitals' % (i, j)
        try:
            string += ' (electronic state: %s)' % count['state'][i]
        except IndexError:
            pass
        display(string)
    i_mo = check_sel(len(count['molecular orbitals']),
                     i_mo,
                     interactive=interactive)

    if spin is not None:
        if spin != 'alpha' and spin != 'beta':
            raise IOError('`spin=%s` is not a valid option' % spin)
        else:
            display('Reading only molecular orbitals of spin %s.' % spin)

    # Set a counter for the AOs
    basis_count = 0

    # Initialize some variables
    sec_flag = None
    skip = 0
    c_link = 0
    c_geo = 0
    c_ao = 0
    c_mo = 0
    c_sao = 0
    old_ao = -1
    orb_sym = []
    qc = QCinfo()
    index = []

    # Go through the file line by line
    for il in range(len(flines)):
        line = flines[il]  # The current line as string
        thisline = line.split()  # The current line split into segments

        # Check the file for keywords
        if ' Entering Link 1' in line:
            c_link += 1
        if i_link == (c_link - 1):
            if '%s orientation:' % orientation in line.lower():
                # The section containing information about
                # the molecular geometry begins
                if i_geo == c_geo:
                    qc.geo_info = []
                    qc.geo_spec = []
                    sec_flag = 'geo_info'
                c_geo += 1
                skip = 4
            elif 'Standard basis:' in line or 'General basis read from cards:' in line:
                # Check if a cartesian basis has been applied
                if '(5D, 7F)' in line:
                    cartesian_basis = False
                elif '(6D, 10F)' not in line:
                    raise IOError(
                        'Please apply a Spherical Harmonics (5D, 7F) or ' +
                        'a Cartesian Gaussian Basis Sets (6D, 10F)!')
            elif 'AO basis set' in line:
                # The section containing information about
                # the atomic orbitals begins
                if i_ao == c_ao:
                    qc.ao_spec = []
                    if not cartesian_basis:
                        qc.ao_spherical = []
                    sec_flag = 'ao_info'
                c_ao += 1
                basis_count = 0
                bNew = True  # Indication for start of new AO section
            elif 'Orbital symmetries:' in line:
                sec_flag = 'mo_sym'
                add = ''
                orb_sym = []
            elif 'Orbital Coefficients:' in line:
                # The section containing information about
                # the molecular orbitals begins
                if (i_mo == c_mo):
                    sec_flag = 'mo_info'
                    mo_type = count['molecular orbitals'][i_mo]
                    qc.mo_spec = []
                    offset = 0
                    add = ''
                    orb_spin = []
                    if orb_sym == []:
                        if 'Alpha' in mo_type:
                            add = '_a'
                            orb_spin = ['alpha'] * basis_count
                        orb_sym = ['A1' + add] * basis_count
                        if 'Beta' in mo_type:
                            add = '_b'
                            orb_spin += ['beta'] * basis_count
                            orb_sym += ['A1' + add] * basis_count
                    for i in range(len(orb_sym)):
                        # for numpy version < 1.6
                        c = ((numpy.array(orb_sym[:i + 1]) == orb_sym[i]) !=
                             0).sum()
                        # for numpy version >= 1.6 this could be used:
                        #c = numpy.count_nonzero(numpy.array(orb_sym[:i+1]) == orb_sym[i])
                        qc.mo_spec.append({
                            'coeffs': numpy.zeros(basis_count),
                            'energy': 0.,
                            'sym': '%d.%s' % (c, orb_sym[i])
                        })
                        if orb_spin != []:
                            qc.mo_spec[-1]['spin'] = orb_spin[i]
                if mo_type != 'Beta':
                    c_mo += 1
                bNew = True  # Indication for start of new MO section
            elif 'E(' in line:
                qc.etot = float(line.split('=')[1].split()[0])
            else:
                # Check if we are in a specific section
                if sec_flag == 'geo_info':
                    if not skip:
                        qc.geo_info.append(
                            [thisline[1], thisline[0], thisline[1]])
                        qc.geo_spec.append([float(ij) for ij in thisline[3:]])
                        if '-----------' in flines[il + 1]:
                            sec_flag = None
                    else:
                        skip -= 1
                if sec_flag == 'ao_info':
                    # Atomic orbital section
                    if ' ****' in line:
                        # There is a line with stars after every AO
                        bNew = True
                        # If there is an additional blank line, the AO section is complete
                        if flines[il + 1].split() == []:
                            sec_flag = None
                    elif bNew:
                        # The following AOs are for which atom?
                        bNew = False
                        at_num = int(thisline[0]) - 1
                        ao_num = 0
                    elif len(thisline) == 4:
                        # AO information section
                        # Initialize a new dict for this AO
                        ao_num = 0  # Initialize number of atomic orbiatls
                        ao_type = thisline[0].lower()  # Type of atomic orbital
                        pnum = int(thisline[1])  # Number of primatives
                        for i_ao in ao_type:
                            # Calculate the degeneracy of this AO and increase basis_count
                            basis_count += l_deg(
                                lquant[i_ao], cartesian_basis=cartesian_basis)
                            qc.ao_spec.append({
                                'atom': at_num,
                                'type': i_ao,
                                'pnum': pnum,
                                'coeffs': numpy.zeros((pnum, 2))
                            })
                    else:
                        # Append the AO coefficients
                        coeffs = numpy.array(line.replace('D', 'e').split(),
                                             dtype=numpy.float64)
                        for i_ao in range(len(ao_type)):
                            qc.ao_spec[-len(ao_type) +
                                       i_ao]['coeffs'][ao_num, :] = [
                                           coeffs[0], coeffs[1 + i_ao]
                                       ]
                        ao_num += 1
                if sec_flag == 'mo_sym':
                    if 'electronic state' in line:
                        sec_flag = None
                    else:
                        info = line[18:].replace('(', '').replace(')',
                                                                  '').split()
                        if 'Alpha' in line:
                            add = '_a'
                        elif 'Beta' in line:
                            add = '_b'
                        for i in info:
                            orb_sym.append(i + add)
                if sec_flag == 'mo_info':
                    # Molecular orbital section
                    info = line[:21].split()
                    if info == []:
                        coeffs = line[21:].split()
                        if bNew:
                            index = [offset + i for i in range(len(coeffs))]
                            bNew = False
                        else:
                            for i, j in enumerate(index):
                                qc.mo_spec[j]['occ_num'] = int(
                                    'O' in coeffs[i])
                                if mo_type not in 'Alpha&Beta':
                                    qc.mo_spec[j]['occ_num'] *= 2
                    elif 'Eigenvalues' in info:
                        coeffs = line[21:].replace('-', ' -').split()
                        if mo_type == 'Natural':
                            key = 'occ_num'
                        else:
                            key = 'energy'
                        for i, j in enumerate(index):
                            qc.mo_spec[j][key] = float(coeffs[i])
                    else:
                        coeffs = line[21:].replace('-', ' -').split()
                        if not cartesian_basis and offset == 0:
                            if old_ao != line[:14].split()[-1] or len(
                                    line[:14].split()) == 4:
                                old_ao = line[:14].split()[-1]
                                c_sao += 1
                            i = c_sao - 1
                            l = lquant[line[13].lower()]
                            m = line[14:21].replace(' ', '').lower()
                            p = 'yzx'.find(m) if len(m) == 1 else -1
                            if p != -1:
                                m = p - 1
                            elif m == '':
                                m = 0
                            else:
                                m = int(m)
                            qc.ao_spherical.append([i, (l, m)])
                        for i, j in enumerate(index):
                            qc.mo_spec[j]['coeffs'][int(info[0]) - 1] = float(
                                coeffs[i])
                        if int(info[0]) == basis_count:
                            bNew = True
                            offset = index[-1] + 1
                            if index[-1] + 1 == len(orb_sym):
                                sec_flag = None
                                orb_sym = []

    # Are all MOs requested for the calculation?
    if not all_mo:
        for i in range(len(qc.mo_spec))[::-1]:
            if qc.mo_spec[i]['occ_num'] < 0.0000001:
                del qc.mo_spec[i]

    if spin is not None:
        if orb_spin == []:
            raise IOError(
                'You requested `%s` orbitals, but None of them are present.' %
                spin)
        else:
            for i in range(len(qc.mo_spec))[::-1]:
                if qc.mo_spec[i]['spin'] != spin:
                    del qc.mo_spec[i]

    # Convert geo_info and geo_spec to numpy.ndarrays
    qc.format_geo(is_angstrom=True)
    return qc
Esempio n. 3
0
def read_wfx(fname, all_mo=False, spin=None, **kwargs):
    '''Reads all information desired from a wfn file.
  
  **Parameters:**
  
    fname: str, file descriptor
      Specifies the filename for the input file.
      fname can also be used with a file descriptor instad of a filename.
    all_mo : bool, optional
      If True, all molecular orbitals are returned.
    spin : {None, 'alpha', or 'beta'}, optional
      If not None, returns exclusively 'alpha' or 'beta' molecular orbitals.
  
  **Returns:**
  
    qc (class QCinfo) with attributes geo_spec, geo_info, ao_spec, mo_spec, etot :
        See :ref:`Central Variables` for details.
  '''

    # Initialize the variables
    qc = QCinfo()
    qc.ao_spec = AOClass([])
    qc.mo_spec = MOClass([])
    lxlylz = []
    for j in exp_wfn:
        lxlylz.extend(j)
    lxlylz = numpy.array(lxlylz, dtype=numpy.int64)

    if isinstance(fname, str):
        filename = fname
        fname = descriptor_from_file(filename, index=0)
    else:
        filename = fname.name

    from io import TextIOWrapper
    if isinstance(fname, TextIOWrapper):
        flines = fname.readlines()  # Read the WHOLE file into RAM
    else:
        magic = 'This is an Orbkit magic string'
        text = fname.read().decode("iso-8859-1").replace(
            '\n', '\n{}'.format(magic))
        flines = text.split(magic)
        flines.pop()

    is_valid = False
    for il in range(len(flines)):
        if '<Keywords>' in flines[il] and 'GTO' in flines[il + 1]:
            is_valid = True

    if not is_valid:
        raise IOError('No valid .wfx file!\nMissing:\n' +
                      '<Keywords>\n  GTO\n</Keywords>')

    sec_flag = None  # A Flag specifying the current section
    at_num = None
    mo_num = None
    ao_num = None
    restricted = True
    count = 0

    # Go through the file line by line
    for il in range(len(flines)):
        line = flines[il]  # The current line as string

        if '<Number of Nuclei>' in line:
            at_num = int(flines[il + 1])
            qc.geo_info = [[None, i + 1, None] for i in range(at_num)]
            qc.geo_spec = []
        elif '<Nuclear Names>' in line:
            if not at_num:
                raise IOError('`<Number of Nuclei>` has to be found ' +
                              'before `<Nuclear Names>`.')
            for i in range(at_num):
                qc.geo_info[i][0] = flines[il + i + 1].replace(' ',
                                                               '').replace(
                                                                   '\n', '')
        elif '<Atomic Numbers>' in line:
            if not at_num:
                raise IOError('`<Number of Nuclei>` has to be found ' +
                              'before `<Atomic Numbers>`.')
            for i in range(at_num):
                qc.geo_info[i][2] = flines[il + i + 1].replace(' ',
                                                               '').replace(
                                                                   '\n', '')
        elif '<Nuclear Cartesian Coordinates>' in line:
            if not at_num:
                raise IOError('`<Number of Nuclei>` has to be found ' +
                              'before `<Nuclear Cartesian Coordinates>`.')
            for i in range(at_num):
                qc.geo_spec.append(flines[il + i + 1].split())
        elif '<Number of Primitives>' in line:
            ao_num = int(flines[il + 1])
            qc.ao_spec = AOClass([
                {
                    'atom': None,
                    'pnum': -1,
                    'coeffs': None,
                    'lxlylz': None,
                    #'lm': None
                } for i in range(ao_num)
            ])
        elif '<Primitive Centers>' in line:
            sec_flag = 'ao_center'
            count = 0
        elif '<Primitive Types>' in line:
            sec_flag = 'ao_type'
            count = 0
        elif '<Primitive Exponents>' in line:
            sec_flag = 'ao_exp'
            count = 0
        elif '<Number of Occupied Molecular Orbitals>' in line:
            mo_num = int(flines[il + 1])
            qc.mo_spec = MOClass([{
                'coeffs': numpy.zeros(ao_num),
                'energy': None,
                'occ_num': None,
                'spin': None,
                'sym': '%s.1' % (i + 1)
            } for i in range(mo_num)])
        elif '<Molecular Orbital Occupation Numbers>' in line:
            for i in range(mo_num):
                qc.mo_spec[i]['occ_num'] = float(flines[il + 1 + i])
        elif '<Molecular Orbital Energies>' in line:
            for i in range(mo_num):
                qc.mo_spec[i]['energy'] = float(flines[il + 1 + i])
        elif '<Molecular Orbital Spin Types>' in line:
            for i in range(mo_num):
                qc.mo_spec[i]['spin'] = (flines[il + 1 + i].replace(
                    ' ', '').replace('\n', '')).replace('and', '_').lower()
                restricted = restricted and ('_' in qc.mo_spec[i]['spin'])
        elif '<MO Number>' in line:
            index = int(flines[il + 1]) - 1
            for i in range(ao_num):
                qc.mo_spec[index]['coeffs'][i] = float(flines[il + 3 + i])
        elif '</' in line:
            sec_flag = None
        elif sec_flag is not None:
            if sec_flag == 'ao_center':
                for i in line.split():
                    qc.ao_spec[count]['atom'] = int(i) - 1
                    count += 1
            if sec_flag == 'ao_type':
                for i in line.split():
                    qc.ao_spec[count]['lxlylz'] = lxlylz[int(i) -
                                                         1][numpy.newaxis]
                    qc.ao_spec[count]['type'] = orbit[sum(lxlylz[int(i) - 1])]
                    count += 1
            if sec_flag == 'ao_exp':
                for i in line.split():
                    qc.ao_spec[count]['coeffs'] = numpy.array([[float(i),
                                                                1.0]])
                    count += 1

    has_alpha = any([i['spin'] == 'alpha' for i in qc.mo_spec])
    has_beta = any([i['spin'] == 'beta' for i in qc.mo_spec])

    spin_check(spin, restricted, has_alpha, has_beta)
    qc.select_spin(restricted, spin=spin)

    # Remove numbers from atom names
    for i in qc.geo_info:
        i[0] = ''.join([k for k in i[0] if not k.isdigit()])
    # Convert geo_info and geo_spec to numpy.ndarrays
    qc.format_geo()

    qc.mo_spec.update()
    qc.ao_spec.update()
    return qc
Esempio n. 4
0
def convert_json(jData, all_mo=False, spin=None):
  '''Converts a scanlog JSON data instance to an instance of
  orbkit's QCinfo class.

  **Parameters:**
  
    jData : class
      Contains the input JSON data.
    all_mo : bool, optional
      If True, all molecular orbitals are returned.
    spin : {None, 'alpha', or 'beta'}, optional
      If not None, returns exclusively 'alpha' or 'beta' molecular orbitals.


  **Returns:**

    qc (class QCinfo) with attributes geo_spec, geo_info, ao_spec, mo_spec, etot :
          See :ref:`Central Variables` for details.
  '''
  aa_to_au = 1/0.52917720859
  # Initialize the variables 
  qc = QCinfo()
  
  # Converting all information concerning atoms and geometry
  qc.geo_spec = numpy.array(jData['results']['geometry']['elements_3D_coords_converged']).reshape((-1, 3)) * aa_to_au
  for ii in range(jData["molecule"]['nb_atoms']):
    symbol = get_atom_symbol(atom=jData["molecule"]['atoms_Z'][ii])
    qc.geo_info.append([symbol,str(ii+1),str(jData["molecule"]['atoms_Z'][ii])])
  
  # Convert geo_info and geo_spec to numpy.ndarrays
  qc.format_geo()
  
  # Converting all information about atomic basis set
  from pickle import loads
  gbasis = loads(bytes(jData['comp_details']['general']['basis_set'], 'utf-8'))
  for ii in range(jData["molecule"]['nb_atoms']):
    for jj in range(len(gbasis[ii])):
      pnum = len(gbasis[ii][jj][1])
      qc.ao_spec.append({'atom': ii,
                  'type': str(gbasis[ii][jj][0]).lower(),
                  'pnum':  pnum,
                  'coeffs': numpy.zeros((pnum, 2))
                  })
      for kk in range(pnum):
        qc.ao_spec[-1]['coeffs'][kk][0] = gbasis[ii][jj][1][kk][0]
        qc.ao_spec[-1]['coeffs'][kk][1] = gbasis[ii][jj][1][kk][1]

  if "ao_names" in jData['comp_details']['general']:
    # Reconstruct exponents list for ao_spec
    aonames = jData['comp_details']['general']['ao_names']
    cartesian_basis = True
    for i in aonames:
      if '+' in i or '-' in i:
        cartesian_basis = False
# There is a problem here with the 6D 7F basis sets, that are a mixture of cartesian and spherical basis sets.
    if not cartesian_basis:
        qc.ao_spherical = []
    
    count = 0
    for i,ao in enumerate(qc.ao_spec):
      l = l_deg(lquant[ao['type']],cartesian_basis=cartesian_basis)
      if cartesian_basis:
        ao['exp_list'] = []
        
      for ll in range(l):
        if cartesian_basis:
          ao['exp_list'].append((aonames[count].lower().count('x'),
                                 aonames[count].lower().count('y'),
                                 aonames[count].lower().count('z')))
        else:
          m = aonames[count].lower().split('_')[-1]
          m = m.replace('+',' +').replace('-',' -').replace('s','s 0').split(' ') 
          p = 'yzx'.find(m[0][-1])
          if p != -1:
            m = p - 1
          else:
            m = int(m[-1])
          qc.ao_spherical.append([i,(lquant[ao['type']],m)])
        count += 1
  
  # Converting all information about molecular orbitals
  ele_num = numpy.sum(jData["molecule"]['atoms_Z']) - numpy.sum(jData['comp_details']['general']['core_electrons_per_atoms']) - jData['molecule']['charge']
  ue = (jData['molecule']['multiplicity']-1)
  
  # Check for natural orbitals and occupation numbers
  is_natorb = False
  #if hasattr(ccData,'nocoeffs'):
  #  if not hasattr(ccData,'nooccnos'):
  #    raise IOError('There are natural orbital coefficients (`nocoeffs`) in the cclib' + 
  #                  ' ccData, but no natural occupation numbers (`nooccnos`)!')
  #  is_natorb = True
  
  restricted = (len(jData['results']['wavefunction']['MO_energies']) == 1)
  if spin is not None:
    if spin != 'alpha' and spin != 'beta':
      raise IOError('`spin=%s` is not a valid option' % spin)
    elif restricted:
      raise IOError('The keyword `spin` is only supported for unrestricted calculations.')
    else:
      display('Converting only molecular orbitals of spin %s.' % spin)
  
  import scipy.sparse
  sym = {}
  shape = (jData['results']['wavefunction']['MO_number_kept'], jData['comp_details']['general']['basis_set_size'])
  pre_mocoeffs = jData['results']["wavefunction"]["MO_coefs"]
  if restricted:
    add = ['']
    orb_sym = [None]
    mocoeffs = [numpy.asarray(scipy.sparse.csr_matrix(tuple([numpy.asarray(d) for d in pre_mocoeffs[0]]), shape=shape).todense())]
  else:
    add = ['_a','_b']      
    orb_sym = ['alpha','beta']
    mocoeffs = [numpy.asarray(scipy.sparse.csr_matrix(tuple([numpy.asarray(d) for d in pre_mocoeffs[0]]), shape=shape).todense()),
                numpy.asarray(scipy.sparse.csr_matrix(tuple([numpy.asarray(d) for d in pre_mocoeffs[1]]), shape=shape).todense())]

  nmo = jData['results']['wavefunction']['MO_number'] if "nmo" in jData['results']['wavefunction'] else len(mocoeffs[0])
  for ii in range(nmo):
    for i,j in enumerate(add):
      a = '%s%s' % (jData['results']['wavefunction']['MO_sym'][i][ii],j)
      if a not in sym.keys(): sym[a] = 1
      else: sym[a] += 1
      #if is_natorb:
      #  occ_num = ccData.nooccnos[ii]
      if not restricted:
        occ_num = 1.0 if ii <= jData['results']['wavefunction']['homo_indexes'][i] else 0.0
      elif ele_num > ue:
        occ_num = 2.0
        ele_num -= 2.0
      elif ele_num > 0.0 and ele_num <= ue: 
        occ_num = 1.0
        ele_num -= 1.0
        ue -= 1.0
      else:
        occ_num = 0.0
      qc.mo_spec.append({'coeffs': mocoeffs[i][ii],
              'energy': jData['results']['wavefunction']['MO_energies'][i][ii],
              'occ_num': occ_num,
              'sym': '%d.%s' %(sym[a],a)
              })
      if orb_sym[i] is not None:
        qc.mo_spec[-1]['spin'] = orb_sym[i]
        if spin is not None and spin != orb_sym[i]:
          del qc.mo_spec[-1]
  
  # Use default order for atomic basis functions if aonames is not present
  if 'ao_names' not in jData['comp_details']['general']:
    display('The attribute `aonames` is not present in the parsed data.')
    display('Using the default order of basis functions.')
    
    # Check which basis functions have been used
    c_cart = sum([l_deg(l=ao['type'], cartesian_basis=True) for ao in qc.ao_spec])
    c_sph = sum([l_deg(l=ao['type'], cartesian_basis=False) for ao in qc.ao_spec])
    
    c = create_mo_coeff(qc.mo_spec,'').shape[-1]
    if c != c_cart and c == c_sph: # Spherical basis
      qc.ao_spherical = get_ao_spherical(qc.ao_spec,p=[0,1])
    elif c != c_cart:
      display('Warning: The basis set type does not match with pure spherical ' +
              'or pure Cartesian basis!') 
      display('Please specify qc.mo_spec["exp_list"] and/or qc.ao_spherical by your self.')
  
  # Are all MOs requested for the calculation? 
  if not all_mo:
    for i in range(len(qc.mo_spec))[::-1]:
      if qc.mo_spec[i]['occ_num'] < 0.0000001:
        del qc.mo_spec[i]

  return qc
Esempio n. 5
0
def read_gaussian_fchk(fname, all_mo=False, spin=None, **kwargs):
  '''Reads all information desired from a Gaussian FChk file. 

  **Parameters:**
  
  fname: str, file descriptor
    Specifies the filename for the input file.
    fname can also be used with a file descriptor instad of a filename.
    all_mo : bool, optional
      If True, all molecular orbitals are returned.
  
  **Returns:**
  
    qc (class QCinfo) with attributes geo_spec, geo_info, ao_spec, mo_spec, etot :
        See :ref:`Central Variables` for details.
  '''
  
  if isinstance(fname, str):
    filename = fname
    fname = descriptor_from_file(filename, index=0)
  else:
    filename = fname.name

  flines = fname.readlines()       # Read the WHOLE file into RAM
  if isinstance(fname, str):
    fname.close()                    # Leave existing file descriptors alive
  
  # Is this an unrestricted calculation?
  has_beta = False
  is_6D = False
  is_10F = False
  for line in flines:
    if 'beta mo coefficients' in line.lower():
      has_beta = True
    if 'Pure/Cartesian d shells' in line:
      is_6D = int(line.split()[-1]) == 1
    if 'Pure/Cartesian f shells' in line:
      is_10F = int(line.split()[-1]) == 1
  
  cartesian_basis = (is_6D and is_10F)
  if ((not is_6D) and is_10F) or (is_6D and (not is_10F)):
    raise IOError('Please apply a Spherical Harmonics (5D, 7F) or '+
                          'a Cartesian Gaussian Basis Set (6D, 10F)!')
  
  if spin is not None:
    if spin != 'alpha' and spin != 'beta':
      raise IOError('`spin=%s` is not a valid option' % spin)
    elif has_beta:
      display('Reading only molecular orbitals of spin %s.' % spin)
    else:
      raise IOError('The keyword `spin` is only supported for unrestricted calculations.')
  restricted = (not has_beta)
  
  sec_flag = None
  
  el_num = [0,0]
  mo_i0 = {'alpha': 0, 'beta': 0}
  what = 'alpha'
  index = 0
  at_num = 0
  
  ao_num = 0 
  ao_sp_coeffs = {}
  switch = 0
  qc = QCinfo()
  qc.geo_info = [[],[],[]]
  if not cartesian_basis:
    qc.ao_spherical = []
  
  # Go through the file line by line 
  for il in range(len(flines)):
    line = flines[il]         # The current line as string
    thisline = line.split()   # The current line split into segments
    
    # Check the file for keywords 
    if 'Number of alpha electrons' in line:
      el_num[0] = int(thisline[5]) 
    elif 'Number of beta electrons' in line:
      el_num[1] = int(thisline[5])
    elif 'Number of basis functions' in line:
      basis_number = int(thisline[5])
    elif 'Atomic numbers'  in line:
      sec_flag = 'geo_info'
      index = 0
      at_num = int(thisline[-1])
      count = 0
      qc.geo_info[1] = list(range(1,at_num+1))
    elif 'Nuclear charges' in line:
      sec_flag = 'geo_info'
      index = 2
      at_num = int(thisline[-1])
      count = 0
    elif 'Total Energy' in line:
      qc.etot = float(thisline[3])
    elif 'Current cartesian coordinates' in line:
      at_num = int(thisline[-1])/3
      sec_flag = 'geo_pos'
      qc.geo_spec = []
      count = 0
      xyz = []
    elif 'Shell types' in line:
      sec_flag = 'ao_info'
      index = 'type'
      ao_num = int(thisline[-1])
      count = 0
      if qc.ao_spec == []:
        for ii in range(ao_num):
          qc.ao_spec.append({})
    elif 'Number of primitives per shell' in line:
      sec_flag = 'ao_info'
      index = 'pnum'
      ao_num = int(thisline[-1])
      count = 0
      if qc.ao_spec == []:
        for ii in range(ao_num):
          qc.ao_spec.append({})
    elif 'Shell to atom map' in line:
      sec_flag = 'ao_info'
      index = 'atom'
      ao_num = int(thisline[-1])
      count = 0
      if qc.ao_spec == []:
        for ii in range(ao_num):
          qc.ao_spec.append({})
    elif 'Primitive exponents' in line:
      sec_flag = 'ao_coeffs'
      ao_num = int(thisline[-1])
      count = 0
      switch = 0
      index = 0
      if qc.ao_spec == []:
        raise IOError('Shell types need to be defined before the AO exponents!')
      if not 'coeffs' in qc.ao_spec[0].keys():
        for ii in range(len(qc.ao_spec)):
          pnum = qc.ao_spec[ii]['pnum']
          qc.ao_spec[ii]['coeffs'] = numpy.zeros((pnum, 2))
    elif 'Contraction coefficients' in line:
      if 'P(S=P)' not in line:
        sec_flag = 'ao_coeffs'  
      else:
        sec_flag = 'ao_sp_coeffs'  
        ao_sp_coeffs = {0: []}
      ao_num = int(thisline[-1])
      count = 0
      switch = 1
      index = 0
      if qc.ao_spec == []:
        raise IOError('Shell types need to be defined before the AO exponents!')
      if not 'coeffs' in qc.ao_spec[0].keys():
        for ii in range(len(qc.ao_spec)):
          pnum = qc.ao_spec[ii]['pnum']
          qc.ao_spec[ii]['coeffs'] = numpy.zeros((pnum, 2))
    elif 'Orbital Energies' in line:
      sec_flag = 'mo_eorb'
      mo_num = int(thisline[-1])      
      mo_i0[thisline[0].lower()] = len(qc.mo_spec)
      if restricted:
        if el_num[0] == el_num[1]:
          i = el_num[0]
          occ = 2
        else:
          i = el_num[0 if 'Alpha' in line else 1]
          occ = 1
      else:
        i = el_num[0 if 'Alpha' in line else 1]
        occ = 1      
      for ii in range(mo_num):
        qc.mo_spec.append({'coeffs': numpy.zeros(basis_number),
                        'energy': 0.0,
                        'occ_num': float(occ if ii < i else 0),
                        'sym': '%i.1' % (ii+1),
                        'spin':thisline[0].lower()
                        })
    elif 'MO coefficients' in line:
      sec_flag = 'mo_coeffs'
      count = 0
      index = 0
      mo_num = int(thisline[-1])
      what = thisline[0].lower()
    else:
      # Check if we are in a specific section 
      if sec_flag == 'geo_info':
        for ii in thisline:
          qc.geo_info[index].append(ii)
          count += 1
          if count == at_num:
            sec_flag = None
      elif sec_flag == 'geo_pos':
        for ii in thisline:
          xyz.append(float(ii))
          if len(xyz) == 3:
            qc.geo_spec.append(xyz)
            xyz = []
            count += 1
            if count == at_num:
              sec_flag = None
      elif sec_flag == 'ao_info':
        for ii in thisline:
          ii = int(ii)
          if index is 'type':         
            ii = orbit[abs(ii)]
            l = lquant[ii]
            if not cartesian_basis:
              for m in (range(0,l+1) if l != 1 else [1,0]):
                qc.ao_spherical.append([count,(l,m)])
                if m != 0:
                  qc.ao_spherical.append([count,(l,-m)])
          elif index is 'atom':
            ii -= 1
          qc.ao_spec[count][index] = ii
          count += 1
          if count == ao_num:
            sec_flag = None
      elif sec_flag == 'ao_coeffs':
        for ii in thisline:
          qc.ao_spec[index]['coeffs'][count,switch] = float(ii)
          count += 1
          ao_num -= 1
          if count == qc.ao_spec[index]['pnum']:
            index += 1
            count = 0
        if not ao_num:
          sec_flag = None
      elif sec_flag == 'ao_sp_coeffs':
        for ii in thisline:
          ao_sp_coeffs[index].append(float(ii))
          count += 1
          ao_num -= 1
          if count == qc.ao_spec[index]['pnum']:
            index += 1
            ao_sp_coeffs[index] = []
            count = 0
        if not ao_num:
          sec_flag = None
      elif sec_flag == 'mo_eorb':
        for ii in thisline:
          qc.mo_spec[count]['energy'] = float(ii)
          count += 1
          if index != 0 and not count % basis_number:
            sec_flag = None
      elif sec_flag == 'mo_coeffs':
        for ii in thisline:    
          qc.mo_spec[mo_i0[what]+index]['coeffs'][count] = float(ii)
          count += 1
          if count == basis_number:
            count = 0
            index += 1
          if index != 0 and not index % basis_number:
            sec_flag = None
  
  # Look for SP atomic orbitals
  if ao_sp_coeffs:
    ao_new = []
    for i,ao in enumerate(qc.ao_spec):
      if ao['type'] == 'p' and sum(numpy.abs(ao_sp_coeffs[i])) > 0:
        ao_new.append(copy.deepcopy(ao))
        ao_new[-1]['type'] = 's'
        ao_new.append(ao)
        ao_new[-1]['type'] = 'p'
        ao_new[-1]['coeffs'][:,1] = numpy.array(ao_sp_coeffs[i])        
      else:
        ao_new.append(ao)
    qc.ao_spec = ao_new   
    
  # Are all MOs requested for the calculation? 
  if not all_mo:
    for i in range(len(qc.mo_spec))[::-1]:
      if qc.mo_spec[i]['occ_num'] < 0.0000001:
        del qc.mo_spec[i]
  
  # Only molecular orbitals of one spin requested?
  if spin is not None:
    for i in range(len(qc.mo_spec))[::-1]:
      if qc.mo_spec[i]['spin'] != spin:
        del qc.mo_spec[i]
  
  if restricted:
    # Closed shell calculation
    for mo in qc.mo_spec:
      del mo['spin']
  else:
    # Rename MOs according to spin
    for mo in qc.mo_spec:
      mo['sym'] += '_%s' % mo['spin'][0]
  
  # Check for natural orbital occupations
  energy_sum = sum([abs(i['energy']) for i in qc.mo_spec])
  if energy_sum < 0.0000001:
    display('Attention!\n\tThis FChk file contains natural orbitals. '+
            '(There are no energy eigenvalues.)\n\t' + 
            'In this case, Gaussian does not print the respective natural' +
            'occupation numbers!' )
  
  qc.geo_info = numpy.array(qc.geo_info).T
  # Convert geo_info and geo_spec to numpy.ndarrays
  qc.format_geo(is_angstrom=False)
  
  return qc
Esempio n. 6
0
def convert_cclib(ccData, all_mo=False, spin=None):
  '''Converts a ccData class created by cclib to an instance of
  orbkit's QCinfo class.

  **Parameters:**
  
    ccData : class
      Contains the input data created by cclib.
    all_mo : bool, optional
      If True, all molecular orbitals are returned.
    spin : {None, 'alpha', or 'beta'}, optional
      If not None, returns exclusively 'alpha' or 'beta' molecular orbitals.


  **Returns:**

    qc (class QCinfo) with attributes geo_spec, geo_info, ao_spec, mo_spec, etot :
          See :ref:`Central Variables` for details.
  '''
  # Initialize the variables 
  qc = QCinfo()
  qc.ao_spec = AOClass([])
  qc.mo_spec = MOClass([])
  
  # Converting all information concerning atoms and geometry
  qc.geo_spec = ccData.atomcoords[0] * aa_to_a0
  for ii in range(ccData.natom):
    symbol = get_atom_symbol(atom=ccData.atomnos[ii])
    qc.geo_info.append([symbol,str(ii+1),str(ccData.atomnos[ii])])
  
  # Convert geo_info and geo_spec to numpy.ndarrays
  qc.format_geo()
  
  # Converting all information about atomic basis set

  for ii in range(ccData.natom):
    for jj in range(len(ccData.gbasis[ii])):
      pnum = len(ccData.gbasis[ii][jj][1])
      qc.ao_spec.append({'atom': ii,
                  'type': str(ccData.gbasis[ii][jj][0]).lower(),
                  'pnum':  pnum,
                  'coeffs': numpy.zeros((pnum, 2))
                  })
      for kk in range(pnum):
        qc.ao_spec[-1]['coeffs'][kk][0] = ccData.gbasis[ii][jj][1][kk][0]
        qc.ao_spec[-1]['coeffs'][kk][1] = ccData.gbasis[ii][jj][1][kk][1]
  
  if hasattr(ccData,'aonames'):
    # Reconstruct exponents list for ao_spec
    cartesian_basis = True
    for i in ccData.aonames:
      if '+' in i or '-' in i:
        cartesian_basis = False

    if not cartesian_basis:
        qc.ao_spec.spherical = True
    
    count = 0
    for i,ao in enumerate(qc.ao_spec):
      l = l_deg(lquant[ao['type']],cartesian_basis=cartesian_basis)
      if cartesian_basis:
        ao['lxlylz'] = []
      else:
        ao['lm'] = []
      for ll in range(l):
        if cartesian_basis:
          ao['lxlylz'].append((ccData.aonames[count].lower().count('x'),
                               ccData.aonames[count].lower().count('y'),
                               ccData.aonames[count].lower().count('z')))
        else:
          m = ccData.aonames[count].lower().split('_')[-1]
          m = m.replace('+',' +').replace('-',' -').replace('s','s 0').split(' ') 
          p = 'yzx'.find(m[0][-1])
          if p != -1:
            m = p - 1
          else:
            m = int(m[-1])
          ao['lm'].append((lquant[ao['type']],m))
        count += 1
  
  # Converting all information about molecular orbitals
  ele_num = numpy.sum(ccData.atomnos) - numpy.sum(ccData.coreelectrons) - ccData.charge
  ue = (ccData.mult-1)
  
  # Check for natural orbitals and occupation numbers
  is_natorb = False
  if hasattr(ccData,'nocoeffs'):
    if not hasattr(ccData,'nooccnos'):
      raise IOError('There are natural orbital coefficients (`nocoeffs`) in the cclib' + 
                    ' ccData, but no natural occupation numbers (`nooccnos`)!')
    is_natorb = True
  
  restricted = (len(ccData.mosyms) == 1)
  if spin is not None:
    if spin != 'alpha' and spin != 'beta':
      raise IOError('`spin=%s` is not a valid option' % spin)
    elif restricted:
      raise IOError('The keyword `spin` is only supported for unrestricted calculations.')
    else:
      qc.mo_spec.spinpola
      display('Converting only molecular orbitals of spin %s.' % spin)
  
  sym = {}
  if len(ccData.mosyms) == 1:
    add = ['']
    orb_sym = [None]
  else:
    add = ['_a','_b']      
    orb_sym = ['alpha','beta']
  
  nmo = ccData.nmo if hasattr(ccData,'nmo') else len(ccData.mocoeffs[0])  
  for ii in range(nmo):    
    for i,j in enumerate(add):
      a = '%s%s' % (ccData.mosyms[i][ii],j)
      if a not in sym.keys(): sym[a] = 1
      else: sym[a] += 1
      if is_natorb:
        occ_num = ccData.nooccnos[ii]
      elif not restricted:
        occ_num = 1.0 if ii <= ccData.homos[i] else 0.0
      elif ele_num > ue:
        occ_num = 2.0
        ele_num -= 2.0
      elif ele_num > 0.0 and ele_num <= ue: 
        occ_num = 1.0
        ele_num -= 1.0
        ue -= 1.0
      else:
        occ_num = 0.0
        
      qc.mo_spec.append({'coeffs': (ccData.nocoeffs if is_natorb else ccData.mocoeffs[i])[ii],
              'energy': 0.0 if is_natorb else ccData.moenergies[i][ii]*ev_to_ha,
              'occ_num': occ_num,
              'sym': '%d.%s' %(sym[a],a)
              })
      if orb_sym[i] is not None:
        qc.mo_spec[-1]['spin'] = orb_sym[i]
        if spin is not None and spin != orb_sym[i]:
          del qc.mo_spec[-1]
  
  # Use default order for atomic basis functions if aonames is not present
  if not hasattr(ccData,'aonames'):
    display('The attribute `aonames` is not present in the parsed data.')
    display('Using the default order of basis functions.')
    
    # Check which basis functions have been used
    c_cart = sum([l_deg(l=ao['type'], cartesian_basis=True) for ao in qc.ao_spec])
    c_sph = sum([l_deg(l=ao['type'], cartesian_basis=False) for ao in qc.ao_spec])
    
    c = qc.mo_spec.get_coeffs().shape[-1]
    if c != c_cart and c == c_sph: # Spherical basis
      qc.ao_spec.set_lm_dict(p=[0,1])
    elif c != c_cart:
      display('Warning: The basis set type does not match with pure spherical ' +
              'or pure Cartesian basis!') 
      display('Please specify qc.ao_spec["lxlylz"] and/or qc.ao_spec["lm"] by your self.')
  
  # Are all MOs requested for the calculation? 
  if not all_mo:
    for i in range(len(qc.mo_spec))[::-1]:
      if qc.mo_spec[i]['occ_num'] < 0.0000001:
        del qc.mo_spec[i]

  qc.mo_spec.update()
  qc.ao_spec.update()
  return qc