def lowdin(qc): '''Calculates the Lowdin populations and charges for each atom in the system. **Parameters:** qc.geo_spec, qc.geo_info, qc.ao_spec, qc.mo_spec : See :ref:`Central Variables` for details. **Returns:** lowdin_pop : dict Contains information of Lowdin charge analysis and has following members: :population: - Lowdin population for each atom. :charges: - Lowdin charges for each atom. ''' # Calculate AO-overlap matrix S = get_ao_overlap(qc.geo_spec, qc.geo_spec, qc.ao_spec) # Get MO-coefficients mo = qc.mo_spec.get_coeffs() mo_dim, ao_dim = mo.shape # Orthogonalize basis set s_eval, S_evec = numpy.linalg.eigh(S) S_eval = numpy.eye(len(s_eval)) * s_eval**(0.5) S12 = numpy.dot(S_evec, numpy.dot(S_eval, S_evec.T)) # Calculate density matrix P = numpy.zeros((ao_dim, ao_dim)) P_i = numpy.zeros((mo_dim, ao_dim, ao_dim)) for i, m, n in itertools.product(range(mo_dim), range(ao_dim), range(ao_dim)): P_i[i, m, n] = mo[i, m] * mo[i, n] k = qc.mo_spec[i] if k['occ_num'] > 0: P[m, n] += k['occ_num'] * P_i[i, m, n] # Calculate Lowdin population PS = 0 for m in itertools.product(range(ao_dim)): PS += (P[:, m] * S12[m, :]) N = 0 for m in itertools.product(range(ao_dim)): N += (S12[:, m] * PS[m, :]) GP = N.diagonal() atom2mo = get_atom2mo(qc) GP_A = numpy.zeros(len(qc.geo_spec)) for a in range(len(qc.geo_spec)): GP_A[a] = GP[get_lc(a, atom2mo)].sum() lowdin = { 'population': GP_A, 'charge': numpy.array(qc.geo_info[:, 2], dtype=float) - GP_A } return lowdin
def get_overlap_matrix(self): self.ao_overlap_matrix = ai.get_ao_overlap( self.qc.geo_spec, self.qc.geo_spec, self.qc.ao_spec, ao_spherical=self.qc.ao_spherical) return self.ao_overlap_matrix
def mulliken(qc): '''Calculates the Mulliken populations (gross atomic populations) and Mulliken charges for each atom in the system. **Parameters:** qc.geo_spec, qc.geo_info, qc.ao_spec, qc.mo_spec : See :ref:`Central Variables` for details. **Returns:** mulliken_pop : dict Contains information of Mulliken charge analysis and has following members: :population: - Mulliken population for each atom. :charges: - Mulliken charges for each atom. ''' # Calculate AO-overlap matrix S = get_ao_overlap(qc.geo_spec, qc.geo_spec, qc.ao_spec, ao_spherical=qc.ao_spherical) # Get MO-coefficients mo = create_mo_coeff(qc.mo_spec) mo_dim, ao_dim = mo.shape # Calculate density matrix P = numpy.zeros((ao_dim, ao_dim)) P_i = numpy.zeros((mo_dim, ao_dim, ao_dim)) for i, m, n in itertools.product(range(mo_dim), range(ao_dim), range(ao_dim)): P_i[i, m, n] = mo[i, m] * mo[i, n] k = qc.mo_spec[i] if k['occ_num'] > 0: P[m, n] += k['occ_num'] * P_i[i, m, n] # Calculate Mulliken population N = 0 for m in itertools.product(range(ao_dim)): N += (P[:, m] * S[m, :]) GP = N.diagonal() atom2mo = get_atom2mo(qc) GP_A = numpy.zeros(len(qc.geo_spec)) for a in range(len(qc.geo_spec)): GP_A[a] = GP[get_lc(a, atom2mo)].sum() # Save Mulliken population and charges to dictionary mulliken_pop = { 'population': GP_A, 'charge': numpy.array(qc.geo_info[:, 2], dtype=float) - GP_A } return mulliken_pop
def check_mo_norm(qc): geo_spec = qc.geo_spec ao_spec = qc.ao_spec ao_spherical = qc.ao_spherical aoom = analytical_integrals.get_ao_overlap(geo_spec, geo_spec, ao_spec, ao_spherical) moom = analytical_integrals.get_mo_overlap_matrix(mo_spec, mo_spec, aoom, numproc=options.numproc) deviation = numpy.linalg.norm(moom - numpy.eye(len(moom))) return deviation
def mulliken(qc): '''Calculates the Mulliken populations (gross atomic populations) and Mulliken charges for each atom in the system. **Parameters:** qc.geo_spec, qc.geo_info, qc.ao_spec, qc.mo_spec : See :ref:`Central Variables` for details. **Returns:** mulliken_pop : dict Contains information of Mulliken charge analysis and has following members: :population: - Mulliken population for each atom. :charges: - Mulliken charges for each atom. ''' # Calculate AO-overlap matrix S = get_ao_overlap(qc.geo_spec, qc.geo_spec, qc.ao_spec) # Get MO-coefficients mo = qc.mo_spec.get_coeffs() modim = mo.shape[0] # Calculate population matrix atom2mo = get_atom2mo(qc) mull_pop = numpy.zeros(len(qc.geo_spec)) for i in range(len(qc.geo_spec)): for j in range(modim): k = qc.mo_spec[j] if k['occ_num'] > 0: for l in get_lc(i, atom2mo): mull_pop[i] += numpy.sum(k['occ_num'] * mo[j, l] * mo[j, :] * S[l, :]) # Save Mulliken population and charges to dictionary mulliken = { 'population': mull_pop, 'charge': numpy.array(qc.geo_info[:, 2], dtype=float) - mull_pop } return mulliken
def order_using_analytical_overlap(fid_list, itype='molden', deg=0, numproc=1, **kwargs): '''Performs an ordering routine using analytical overlap integrals between molecular orbitals. Set fid_list to None to omit the reading of input files. If :literal:`deg` is set to a value larger than zero, the molecular orbital coefficients are extrapolated with a polynomial of degree :literal:`deg`, before computing the molecular orbital overlap matrix. **Paramerters:** fid_list : list of str or None If not None, it contains the list of input file names. itype : str, choices={'molden', 'gamess', 'gaussian.log', 'gaussian.fchk'} Specifies the type of the input files. deg : int, optional If greater than zero, specifies the degree of the extrapolation polynomial for the molecular orbital coefficients. **Returns:** index_list : numpy.ndarray, shape=(Nfiles,NMO) Contains the new indices of the molecular orbitals. If index < 0, the molecular orbital changes its sign. mo_overlap : numpy.ndarray, shape=((Nfiles - 1),NMO,NMO) Contains the overlap matrix between the molecular orbitals of two neighboring geometries, i.e., mo_overlap[i,j,k] corresponds to overlap between the jth molecular orbital at geometry i to the kth molecular orbital at geometry (i+1). **Global Variables:** geo_spec_all, geo_info, ao_spec, ao_spherical, mo_coeff_all, mo_energy_all, mo_occ_all, sym, index_list ''' global geo_spec_all, geo_info, ao_spec, ao_spherical, mo_coeff_all, mo_energy_all, mo_occ_all, sym, index_list if fid_list is not None: read(fid_list, itype=itype, **kwargs) display( '\nStarting the ordering routine using the molecular orbital overlap...' ) iterate = list(range(1, len(geo_spec_all))) if deg > 0: display('\tThe molecular orbital coefficients will be extrapolated') display('\tusing a least squares polynomial fit of degree %d.' % deg) std = numpy.array( [numpy.std(i - geo_spec_all[0]) for i in geo_spec_all]) mo_overlap = [[] for i in sym.keys()] index_list = [[] for i in sym.keys()] for s in sym.values(): shape = numpy.shape(mo_coeff_all[s]) index_list[s] = numpy.ones((shape[0], shape[1]), dtype=int) index_list[s] *= numpy.arange(shape[1], dtype=int) c = 0 t = time() for rr in iterate: r1 = rr - 1 r2 = rr if (deg is None) or (deg > 0 and r1 >= deg): ao_overlap = get_ao_overlap(geo_spec_all[r2], geo_spec_all[r2], ao_spec, ao_spherical=ao_spherical) else: ao_overlap = get_ao_overlap(geo_spec_all[r1], geo_spec_all[r2], ao_spec, ao_spherical=ao_spherical) cs = 0 for s in sym.values(): mo_coeff = mo_coeff_all[s] shape = numpy.shape(mo_coeff) if deg is not None and deg > 0 and r1 >= deg: mo_r1 = get_extrapolation(r1, r2, mo_coeff, grid1d=std, deg=deg) else: mo_r1 = mo_coeff[r1] overlap = get_mo_overlap_matrix(mo_r1, mo_coeff[r2], ao_overlap, numproc=numproc) for i in range(shape[1]): # Iterate the rows of the overlap matrix line_max = None # variable for maximum value in the current row line_sort = numpy.argsort(numpy.abs( overlap[i, :]))[::-1] # sort the row for k in line_sort[::-1]: # Is this value the maximum in the current column? col_max = numpy.argmax(numpy.abs(overlap[:, k])) if i == col_max: line_max = k break if line_max is not None: # Interchange the coefficients mo_coeff[r2, [i, line_max], :] = mo_coeff[r2, [line_max, i], :] overlap[:, [i, line_max]] = overlap[:, [line_max, i]] index_list[s][r2, [i, line_max]] = index_list[s][r2, [line_max, i]] for i in range(shape[1]): # Change the signs mo_coeff[r2, i, :] *= numpy.sign(overlap[i, i]) overlap[:, i] *= numpy.sign(overlap[i, i]) index_list[s][r2, i] *= numpy.sign(overlap[i, i]) mo_overlap[cs].append(overlap) cs += 1 mo_coeff_all[s] = mo_coeff index = numpy.abs(index_list[s])[r2, :] mo_energy_all[s][r2, :] = mo_energy_all[s][r2, index] mo_occ_all[s][r2, :] = mo_occ_all[s][r2, index] c += 1 #if not c % int(numpy.ceil(len(iterate)/10.)): display('\tFinished %d of %d geometries (%.1f s)' % (c, len(iterate), time() - t)) t = time() tmp = [] for i in mo_overlap: tmp.append(numpy.array(i)) mo_overlap = tmp return index_list, mo_overlap
print(grid.get_grid()) print('Computing the Molecular Orbitals and the Derivatives Thereof...\n') molist = core.rho_compute(qc, calc_mo=True, slice_length=slice_length, drv=[None, 'x', 'y', 'z', 'xx', 'yy', 'zz'], numproc=numproc) molistdrv = molist[1:4] # \nabla of MOs molistdrv2 = molist[-3:] # \nabla^2 of MOs molist = molist[0] # MOs print('\nComputing the Analytical Overlaps of the Molecular Orbitals...\n') aoom = get_ao_overlap(qc.geo_spec, qc.geo_spec, qc.ao_spec, ao_spherical=qc.ao_spherical, drv=[None, 'x', 'y', 'z']) dm_aoom = get_ao_dipole_matrix(qc, component=['x', 'y', 'z']) moom = get_mo_overlap_matrix(qc.mo_spec, qc.mo_spec, aoom[0], numproc=numproc) # <m|n> omr = numpy.zeros((3, ) + moom.shape) # <m|r|n> omv = numpy.zeros((3, ) + moom.shape) # <m|\nabla|n> for i in range(3): omr[i] = get_mo_overlap_matrix(qc.mo_spec, qc.mo_spec, dm_aoom[i], numproc=numproc) omv[i] = get_mo_overlap_matrix(qc.mo_spec, qc.mo_spec,
grid.delta_ = [ 0.2, 0.0, 0.2] grid.grid_init() print(grid.get_grid()) print('Computing the Molecular Orbitals and the Derivatives Thereof...\n') molist = core.rho_compute(qc, calc_mo=True, slice_length=slice_length, drv=[None,'x','y','z','xx','yy','zz'], numproc=numproc) molistdrv = molist[1:4] # \nabla of MOs molistdrv2 = molist[-3:] # \nabla^2 of MOs molist = molist[0] # MOs print('\nComputing the Analytical Overlaps of the Molecular Orbitals...\n') aoom = get_ao_overlap(qc.geo_spec,qc.geo_spec,qc.ao_spec, drv=[None,'x','y','z']) dm_aoom = get_ao_dipole_matrix(qc,component=['x','y','z']) moom = get_mo_overlap_matrix(qc.mo_spec,qc.mo_spec,aoom[0], numproc=numproc) # <m|n> omr = numpy.zeros((3,) + moom.shape) # <m|r|n> omv = numpy.zeros((3,) + moom.shape) # <m|\nabla|n> for i in range(3): omr[i] = get_mo_overlap_matrix(qc.mo_spec,qc.mo_spec,dm_aoom[i],numproc=numproc) omv[i] = get_mo_overlap_matrix(qc.mo_spec,qc.mo_spec,aoom[i+1],numproc=numproc) print(''' ========================================== Starting the detCI@ORBKIT Computations ========================================== ''')
<\phi_k|z|\phi_l> = \int dxdydz (z - Z_k)^{k_z} e^{\alpha_k r_k^2} (z - Z_l)^{l_z+1} e^{\alpha_l r_l^2} + Z_l \int dxdydz (z - Z_k)^{k_z} e^{\alpha_k r_k^2} (z - Z_l)^{l_z} e^{\alpha_l r_l^2} = ao_part_1 + ao_part_2 with :math:`r_l = \sqrt{(x-X_l)^2 + (y-Y_l)^2 + (z - Z_l)^2)}` ''' from orbkit.read import main_read from orbkit.tools import * from orbkit.analytical_integrals import get_ao_overlap, get_mo_overlap, print2D in_fid = 'h2o.molden' # Read the input file qc = main_read(in_fid, itype='molden', all_mo=False) # Compute atomic orbital overlap matrix ao_overlap_matrix = get_ao_overlap(qc.geo_spec, qc.geo_spec, qc.ao_spec) # Compute the overlap of the molecular orbitals and weight it with the occupation number electron_number = 0. for i_mo in qc.mo_spec: electron_number += i_mo['occ_num'] * get_mo_overlap( i_mo['coeffs'], i_mo['coeffs'], ao_overlap_matrix) print('The total number of electrons is %.8f' % electron_number) # Compute the x-, y-, and z-component of the dipole moment dipole_moment = [] for component in range(3): # Compute the first part of the expectation value: # Get the the exponents lx, ly, lz for the primitive Cartesian Gaussians of # the `Ket` basis set, and increase lz by one.
+ Z_l \int dxdydz (z - Z_k)^{k_z} e^{\alpha_k r_k^2} (z - Z_l)^{l_z} e^{\alpha_l r_l^2} = ao_part_1 + ao_part_2 with :math:`r_l = \sqrt{(x-X_l)^2 + (y-Y_l)^2 + (z - Z_l)^2)}` ''' from orbkit.read import main_read from orbkit.core import get_lxlylz, l_deg from orbkit.analytical_integrals import get_ao_overlap, get_mo_overlap, print2D in_fid = 'h2o.molden' # Read the input file qc = main_read(in_fid, itype='molden', all_mo=False) # Compute atomic orbital overlap matrix ao_overlap_matrix = get_ao_overlap(qc.geo_spec, qc.geo_spec, qc.ao_spec, ao_spherical=qc.ao_spherical) # Compute the overlap of the molecular orbitals and weight it with the occupation number electron_number = 0. for i_mo in qc.mo_spec: electron_number += i_mo['occ_num'] * get_mo_overlap( i_mo['coeffs'], i_mo['coeffs'], ao_overlap_matrix) print('The total number of electrons is %.8f' % electron_number) # Compute the x-, y-, and z-component of the dipole moment dipole_moment = [] for component in range(3): # Compute the first part of the expectation value: # Get the the exponents lx, ly, lz for the primitive Cartesian Gaussians of
save_data_values = False #: Saves the grid and output values computed. This requires more RAM and time. # create_plot needs the data values if create_plot and not save_data_values: save_data_values = True numproc = 4 #: Specifies number of subprocesses. slice_length = 1e4 #: Specifies number of points per subprocess. in_fid = 'h2o.molden' #: Specifies input file name. # Open molden file and read parameters qc = read.main_read(in_fid, itype='molden', all_mo=False) # Compute atomic orbital overlap matrix ao_overlap_matrix = get_ao_overlap(qc.geo_spec, qc.geo_spec, qc.ao_spec) # Compute the overlap of the molecular orbitals and weight it with the occupation number analytical_integral = 0. for i_mo in qc.mo_spec: analytical_integral += i_mo['occ_num'] * get_mo_overlap( i_mo['coeffs'], i_mo['coeffs'], ao_overlap_matrix) print('Analytical Integral: %.12f' % analytical_integral) # Disable orbkit terminal output for each run options.quiet = True options.no_log = True # Initialize some variables t = [time()]
def read_molden(fname, all_mo=False, spin=None, i_md=-1, interactive=True, **kwargs): '''Reads all information desired from a molden 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. i_md : int, default=-1 Selects the `[Molden Format]` 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, mo_spec, etot : See :ref:`Central Variables` for details. ''' if 'index' not in kwargs.keys(): kwargs['index'] = 0 if isinstance(fname, str): fd = descriptor_from_file(fname, index=kwargs['index']) else: fd = fname fname = fd.name ### read the whole file into RAM # TODO: optimize for large files molden = fd.read() if isinstance(molden, bytes): molden = molden.decode() ### find number of [Molden Format] entries and figure our which one to use entries = [m.start() for m in regex_molden.finditer(molden)] count = len(entries) if count == 0: raise IOError('The input file {:s} is no valid molden file!\n\nIt does' .format(fname) + ' not contain the keyword: [Molden Format]\n') if count > 1: display('\nContent of the molden file:') display('\tFound {:d} [Molden Format] keywords, i.e., '.format(count) + 'this file contains {:d} molden files.'.format(count)) if interactive: message = '\tPlease give an integer from 0 to {0}: '.format(count - 1) from builtins import input # Python2 compatibility while 1: try: i_md = int(input(message)) except ValueError: print('An Integer is required!') else: if i_md >= count or i_md < -count: # invalid index continue break i_md = list(range(count))[i_md] # log selected index display('\tSelecting the element with index {:d}.'.format(i_md)) # select molden entry start = entries[i_md] end = (entries + [None])[i_md + 1] molden = molden[start:end] molden = molden.splitlines() ### parse [Atoms] and [GTO] section qc = QCinfo() has_alpha = False has_beta = False restricted = False spherical_basis = [] # found flags for spherical basis cartesian_basis = [] # found flags for cartesian basis angular = [] # angular momentum actually used by_orca = False for iline, line in enumerate(molden): if 'orca' in line.lower(): by_orca = True continue if '_ENERGY=' in line: try: qc.etot = float(line.split()[1]) except IndexError: pass continue # [Atoms] section (geo_info) m = regex_atoms.match(line) if m: angstrom = 'angs' == m.group(1).lower() continue m = regex_atom.match(line) if m: qc.geo_info.append(list(m.groups()[:3])) qc.geo_spec.append([float(f) for f in m.groups()[3:]]) continue # [GTO] section (ao_info) if '[sto]' in line.lower(): # orbkit does not support Slater type orbitals raise IOError('orbkit does not work for STOs!\nEXIT\n') m = regex_basis.match(line) if m: at_num = int(m.group(1)) - 1 #ao_num = 0 continue # check spherical/cartesian flags m = regex_flagline.match(line.lower()) if m: # get list of all flags in line flags = regex_flag.findall(m.group(1)) # check whether cartesian or spherical for flag in flags: if flag in FLAGS_SPH: spherical_basis.append(flag) if flag in FLAGS_CART: cartesian_basis.append(flag) m = regex_contraction.match(line) if m: ao_num = 0 # Initialize number of atomic orbitals ao_type = m.group(1).lower() # angular momentum pnum = int(m.group(2)) # Number of primatives for l in ao_type: qc.ao_spec.append({ 'atom': at_num, 'type': l, 'pnum': -pnum if by_orca else pnum, 'coeffs': numpy.zeros((pnum, 2)) }) if not l in angular: angular.append(l) continue m = regex_primitive.match(line) if m: # split line as regex only captures the first two floats, and there may be more coeffs = numpy.array(line.lower().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 continue if '[mo]' in line.lower(): break ### checks for cartesion/spherical basis # check for mixed spherical/cartesian basis functions max_l = max(lquant[l] for l in angular) if max_l >= 2: # remove flags for unused angular momentum l = orbit[2:max_l + 1] sph = [f for f in spherical_basis if f[-1] in l] cart = [f for f in cartesian_basis if f[-1] in l] if sph and cart: raise IOError( '''The input file {} contains mixed spherical and Cartesian function ({}). ORBKIT does not support these basis functions yet. Pleas contact us, if you need this feature!'''.format( fname, ', '.join(sph + cart))) # check for ambiguous spherical/cartesian flags sph = [l[-1] for l in sph] cart = [l[-1] for l in cart] if set(sph) & set(cart): raise IOError( 'The input file {} contains ambiguous flags for spherical and cartesian basis functions: {}' .format(fname, ', '.join(spherical_basis + cartesian_basis))) cartesian = not bool(sph) else: cartesian = True # does not matter for s and p orbitals # count number of basis functions basis_count = 0 for AO in qc.ao_spec: l = AO['type'] # TODO: check for mixed sph/cart basis basis_count += l_deg(lquant[l], cartesian_basis=cartesian) ### parse [MO] section (mo_info) newMO = False MO_sym = None MO_spin = None MO_energy = None MO_occ = None sym = defaultdict(int) # counter for MOs per IRREP for line in molden[iline:]: m = regex_coeff.match(line) if m: if newMO: # infer incomplete data MO_spin = MO_spin or 'alpha' m2 = re.search(r'\d+', MO_sym) if m2: a = m2.group() if MO_sym == a: MO_sym = '{:s}.1'.format(a) elif MO_sym.startswith(a): MO_sym.replace(a, '{:s}.'.format(a), 1) else: sym[a] += 1 MO_sym = '{:d}.{:s}'.format(sym[a], MO_sym) MO_sym = MO_sym or '%d.1' % (len(qc.mo_spec) + 1) # create a new MO entry qc.mo_spec.append({ 'coeffs': numpy.zeros(basis_count), 'sym': MO_sym, 'energy': MO_energy, 'occ_num': MO_occ, 'spin': MO_spin, }) # reset variables newMO = False MO_sym = None MO_spin = None MO_energy = None MO_occ = None # parse and store current coefficient iMO = int(m.group(1)) - 1 coeff = float(m.group(2)) if numpy.isnan(coeff): display( 'Warning: coefficient {:d} of MO {:s} is NaN! Using zero instead' .format(iMO, qc.mo_spec[-1]['sym'])) else: qc.mo_spec[-1]['coeffs'][iMO] = coeff continue newMO = True m = regex_sym.match(line) if m: MO_sym = m.group(1) continue m = regex_energy.match(line) if m: MO_energy = m.group(1) continue m = regex_spin.match(line) if m: MO_spin = m.group(1).lower() has_alpha = has_alpha or MO_spin == 'alpha' has_beta = has_beta or MO_spin == 'beta' continue m = regex_occu.match(line) if m: MO_occ = float(m.group(1)) restricted = restricted or (MO_occ > 1.0001) continue ### post checks and clean up if spin is not None: if restricted: raise IOError( 'The keyword `spin` is only supported for unrestricted calculations.' ) if spin != 'alpha' and spin != 'beta': raise IOError('`spin=%s` is not a valid option' % spin) elif spin == 'alpha' and has_alpha: display('Reading only molecular orbitals of spin alpha.') elif spin == 'beta' and has_beta: display('Reading only molecular orbitals of spin beta.') elif (not has_alpha) and (not has_beta): raise IOError( 'Molecular orbitals in `molden` file do not contain `Spin=` keyword' ) elif ((spin == 'alpha' and not has_alpha) or (spin == 'beta' and not has_beta)): raise IOError( 'You requested `%s` orbitals, but None of them are present.' % spin) # Spherical basis? if spherical_basis: qc.ao_spec.set_lm_dict(p=[1, 0]) # 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] # Orca uses for all molecular orbitals the same name sym = [i['sym'] for i in qc.mo_spec] if sym[1:] == sym[:-1]: sym = sym[0].split('.')[-1] for i in range(len(qc.mo_spec)): qc.mo_spec[i]['sym'] = '%d.%s' % (i + 1, sym) # Convert geo_info and geo_spec to numpy.ndarrays qc.format_geo(is_angstrom=angstrom) # Check the normalization from orbkit.analytical_integrals import get_ao_overlap spher_tmp = qc.ao_spec.spherical qc.ao_spec.spherical = False norm = numpy.diagonal(get_ao_overlap(qc.geo_spec, qc.geo_spec, qc.ao_spec)) qc.ao_spec.spherical = spher_tmp if max(numpy.abs(norm - 1.)) > 1e-5: display( 'The atomic orbitals are not normalized correctly, renormalizing...\n' ) if not by_orca: j = 0 for i in range(len(qc.ao_spec)): qc.ao_spec[i]['coeffs'][:, 1] /= numpy.sqrt(norm[j]) for n in range( l_deg(lquant[qc.ao_spec[i]['type']], cartesian_basis=True)): j += 1 else: qc.ao_spec[0]['N'] = 1 / numpy.sqrt(norm[:, numpy.newaxis]) if cartesian_basis: from orbkit.cy_overlap import ommited_cca_norm cca = ommited_cca_norm(qc.ao_spec.get_lxlylz()) for mo in qc.mo_spec: mo['coeffs'] *= cca qc.mo_spec.update() qc.ao_spec.update() return qc
import numpy import os, inspect from orbkit import read from orbkit import analytical_integrals as ai from orbkit import options from orbkit.test.tools import equal options.quiet = True tests_home = os.path.dirname(inspect.getfile(inspect.currentframe())) folder = os.path.join(tests_home, '../read/outputs_for_testing') filepath = os.path.join(folder, 'h2o_rhf_sph.molden') qc = read.main_read(filepath, all_mo=True) ao_overlap_matrix = ai.get_ao_overlap(qc.geo_spec, qc.geo_spec, qc.ao_spec, ao_spherical=qc.ao_spherical) moom = ai.get_mo_overlap_matrix(qc.mo_spec, qc.mo_spec, ao_overlap_matrix, numproc=options.numproc) equal(moom, numpy.eye(len(moom)))
def read_molden(fname, all_mo=False, spin=None, i_md=-1, interactive=True, **kwargs): '''Reads all information desired from a molden 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. i_md : int, default=-1 Selects the `[Molden Format]` 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, mo_spec, etot : See :ref:`Central Variables` for details. ''' molden_regex = re.compile(r"\[[ ]{,}[Mm]olden[ ]+[Ff]ormat[ ]{,}\]") 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 def check_sel(count, i, interactive=False): if count == 0: raise IndexError elif count == 1: return 0 message = '\tPlease give an integer from 0 to {0}: '.format(count - 1) try: if interactive: i = int(raw_input(message)) 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 has_alpha = [] has_beta = [] restricted = [] cartesian_basis = [] mixed_warning = [] by_orca = [] count = 0 # 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 molden_regex.search(line): count += 1 has_alpha.append(False) has_beta.append(False) restricted.append(False) cartesian_basis.append(True) mixed_warning.append(False) by_orca.append(False) if 'orca' in line.lower(): by_orca[-1] = True if '[5d]' in line.lower() or '[5d7f]' in line.lower(): cartesian_basis[-1] = False if '[5d10f]' in line.lower(): mixed_warning[-1] = '5D, 10F' cartesian_basis[-1] = False if '[7f]' in line.lower(): mixed_warning[-1] = '6D, 7F' cartesian_basis[-1] = True if 'Spin' in line and 'alpha' in line.lower(): has_alpha[-1] = True if 'Spin' in line and 'beta' in line.lower(): has_beta[-1] = True if 'Occup' in line: restricted[-1] = restricted[-1] or (float(line.split('=')[1]) > 1. + 1e-4) if count == 0: raise IOError('The input file %s is no valid molden file!\n\nIt does' % filename + ' not contain the keyword: [Molden Format]\n') else: if count > 1: display('\nContent of the molden file:') display('\tFound %d [Molden Format] keywords, i.e., ' % count + 'this file contains %d molden files.' % count) i_md = check_sel(count, i_md, interactive=interactive) if spin is not None: if restricted[i_md]: raise IOError( 'The keyword `spin` is only supported for unrestricted calculations.' ) if spin != 'alpha' and spin != 'beta': raise IOError('`spin=%s` is not a valid option' % spin) elif spin == 'alpha' and has_alpha[i_md]: display('Reading only molecular orbitals of spin alpha.') elif spin == 'beta' and has_beta[i_md]: display('Reading only molecular orbitals of spin beta.') elif (not has_alpha[i_md]) and (not has_beta[i_md]): raise IOError( 'Molecular orbitals in `molden` file do not contain `Spin=` keyword' ) elif ((spin == 'alpha' and not has_alpha[i_md]) or (spin == 'beta' and not has_beta[i_md])): raise IOError( 'You requested `%s` orbitals, but None of them are present.' % spin) # Set a counter for the AOs basis_count = 0 sym = {} # Declare synonyms for molden keywords synonyms = { 'Sym': 'sym', 'Ene': 'energy', 'Occup': 'occ_num', 'Spin': 'spin' } MO_keys = synonyms.keys() count = 0 max_l = 0 start_reading = False # 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 '[molden format]' in line.lower(): # A new file begins # Initialize the variables if i_md == count: qc = QCinfo() sec_flag = False # A Flag specifying the current section start_reading = True # Found the selected section else: start_reading = False count += 1 continue if start_reading: if '_ENERGY=' in line: try: qc.etot = float(thisline[1]) except IndexError: pass elif '[atoms]' in line.lower(): # The section containing information about # the molecular geometry begins sec_flag = 'geo_info' if 'Angs' in line: # The length are given in Angstroem # and have to be converted to Bohr radii -- aa_to_au = 1 / 0.52917720859 else: # The length are given in Bohr radii aa_to_au = 1.0 elif '[gto]' in line.lower(): # The section containing information about # the atomic orbitals begins sec_flag = 'ao_info' bNew = True # Indication for start of new AO section elif '[mo]' in line.lower(): # The section containing information about # the molecular orbitals begins sec_flag = 'mo_info' bNew = True # Indication for start of new MO section elif '[sto]' in line.lower(): # The orbkit does not support Slater type orbitals raise IOError('orbkit does not work for STOs!\nEXIT\n') elif '[' in line: sec_flag = None else: # Check if we are in a specific section if sec_flag == 'geo_info' and thisline != []: # Geometry section qc.geo_info.append(thisline[0:3]) qc.geo_spec.append( [float(ii) * aa_to_au for ii in thisline[3:]]) if sec_flag == 'ao_info': # Atomic orbital section def check_int(i): try: int(i) return True except ValueError: return False if thisline == []: # There is a blank line after every AO bNew = True elif bNew: # The following AOs are for which atom? bNew = False at_num = int(thisline[0]) - 1 ao_num = 0 elif len(thisline) == 3 and check_int(thisline[1]): # AO information section # Initialize a new dict for this AO ao_num = 0 # Initialize number of atomic orbiatls ao_type = thisline[ 0] # Which type of atomic orbital do we have pnum = int(thisline[1]) # Number of primatives # Calculate the degeneracy of this AO and increase basis_count 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[i_md]) max_l = max(max_l, lquant[i_ao]) qc.ao_spec.append({ 'atom': at_num, 'type': i_ao, 'pnum': -pnum if by_orca[i_md] else 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_info': # Molecular orbital section if '=' in line: # MO information section if bNew: # Create a numpy array for the MO coefficients and # for backward compability create a simple counter for 'sym' qc.mo_spec.append({ 'coeffs': numpy.zeros(basis_count), 'sym': '%d.1' % (len(qc.mo_spec) + 1) }) bNew = False # Append information to dict of this MO info = line.replace('\n', '').replace(' ', '') info = info.split('=') if info[0] in MO_keys: if info[0] == 'Spin': info[1] = info[1].lower() elif info[0] != 'Sym': info[1] = float(info[1]) elif not '.' in info[1]: from re import search try: a = search(r'\d+', info[1]).group() if a == info[1]: info[1] = '%s.1' % a elif info[1].startswith(a): info[1] = info[1].replace( a, '%s.' % a, 1) else: raise AttributeError except AttributeError: if info[1] not in sym.keys(): sym[info[1]] = 1 else: sym[info[1]] += 1 info[1] = '%d.%s' % (sym[info[1]], info[1]) qc.mo_spec[-1][synonyms[info[0]]] = info[1] else: if ('[' or ']') in line: # start of another section that is not (yet) read sec_flag = None else: # Append the MO coefficients bNew = True # Reset bNew index = int(thisline[0]) - 1 try: # Try to convert coefficient to float qc.mo_spec[-1]['coeffs'][index] = float( thisline[1]) except ValueError: # If it cannot be converted print error message raise ValueError( 'Error in coefficient %d of MO %s!' % (index, qc.mo_spec[-1]['sym']) + '\nSetting this coefficient to zero...') # Spherical basis? if not cartesian_basis[i_md]: qc.ao_spherical = get_ao_spherical(qc.ao_spec, p=[1, 0]) if max_l > 2 and mixed_warning[i_md]: raise IOError('The input file %s contains ' % filename + 'mixed spherical and Cartesian function (%s).' % mixed_warning[i_md] + 'ORBKIT does not support these basis functions yet. ' + 'Pleas contact us, if you need this feature!') # 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[i_md]: # 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] # Orca uses for all molecular orbitals the same name sym = [i['sym'] for i in qc.mo_spec] if sym[1:] == sym[:-1]: sym = sym[0].split('.')[-1] for i in range(len(qc.mo_spec)): qc.mo_spec[i]['sym'] = '%d.%s' % (i + 1, sym) # Convert geo_info and geo_spec to numpy.ndarrays qc.format_geo() # Check the normalization from orbkit.analytical_integrals import get_ao_overlap, get_lxlylz norm = numpy.diagonal(get_ao_overlap(qc.geo_spec, qc.geo_spec, qc.ao_spec)) if sum(numpy.abs(norm - 1.)) > 1e-8: display( 'The atomic orbitals are not normalized correctly, renormalizing...\n' ) if not by_orca[i_md]: j = 0 for i in range(len(qc.ao_spec)): qc.ao_spec[i]['coeffs'][:, 1] /= numpy.sqrt(norm[j]) for n in range( l_deg(lquant[qc.ao_spec[i]['type']], cartesian_basis=True)): j += 1 else: qc.ao_spec[0]['N'] = 1 / numpy.sqrt(norm[:, numpy.newaxis]) if cartesian_basis[i_md]: from orbkit.cy_overlap import ommited_cca_norm cca = ommited_cca_norm(get_lxlylz(qc.ao_spec)) for mo in qc.mo_spec: mo['coeffs'] *= cca return qc