def check_dm(dm, overlap, eps=1e-4, occ_max=1.0): '''Check if the density matrix has eigenvalues in the proper range. Parameters ---------- dm : np.ndarray, shape=(nbasis, nbasis), dtype=float The density matrix overlap : np.ndarray, shape=(nbasis, nbasis), dtype=float The overlap matrix eps : float The threshold on the eigenvalue inequalities. occ_max : float The maximum occupation. Raises ------ ValueError When the density matrix has wrong eigenvalues. ''' # construct natural orbitals orb = Orbitals(dm.shape[0]) orb.derive_naturals(dm, overlap) if orb.occupations.min() < -eps: raise ValueError('The density matrix has eigenvalues considerably smaller than ' 'zero. error=%e' % (orb.occupations.min())) if orb.occupations.max() > occ_max+eps: raise ValueError('The density matrix has eigenvalues considerably larger than ' 'max. error=%e' % (orb.occupations.max()-1))
def get_naturalorbs(nbasis, dm1, orb_alpha): """ Get the natural orbitals from the 1-DM """ dm1 /= 2.0 norb = Orbitals(nbasis) # Diagonalize and compute eigenvalues evals, evecs = eigh(dm1) # Reorder the matrix and the eigenvalues evecs1 = evecs.copy() evecs1[:] = evecs[:, ::-1] norb.occupations[::-1] = evals # Get the natural orbitals norb.coeffs[:] = np.dot(orb_alpha.coeffs, evecs1) norb.energies[:] = 0.0 return norb, evecs
def test_integrals_wrapper_init(): """ Test the integral wrapper. """ function = IntegralsWrapper mol = [1, 2] obasis = [2, 3, 1, 3] one_approx = [0, 1] two_approx = [0] pars = [0, 3] orbs = 'orbs' # Check mol assert_raises(TypeError, function, mol, obasis, one_approx, two_approx, pars, orbs) mol = IOData(numbers=np.array([1]), coordinates=np.array([[0., 0., 0]])) # Check obasis assert_raises(TypeError, function, mol, obasis, one_approx, two_approx, pars, orbs) obasis = get_gobasis(mol.coordinates, mol.numbers, '3-21G') # Check one_approx assert_raises(TypeError, function, mol, obasis, one_approx, two_approx, pars, orbs) one_approx = ['man'] # Check options for one_approx assert_raises(ValueError, function, mol, obasis, one_approx, two_approx, pars, orbs) one_approx = ['standard'] del function function = IntegralsWrapper # Check two_approx assert_raises(TypeError, function, mol, obasis, one_approx, two_approx, pars, orbs) two_approx = ['nada'] # Check for options two_approx assert_raises(ValueError, function, mol, obasis, one_approx, two_approx, pars, orbs) two_approx = ['erf', 'gauss'] # Check for pars assert_raises(TypeError, function, mol, obasis, one_approx, two_approx, pars, orbs) pars = [[0.1], [1.0, 1.5]] # Check for orbs assert_raises(TypeError, function, mol, obasis, one_approx, two_approx, pars, orbs) orbs = [Orbitals(obasis.nbasis)]
def check_vanadium_sc_hf(scf_solver): """Try to converge the SCF for the neutral vanadium atom with fixe fractional occupations. Parameters ---------- scf_solver : one of the SCFSolver types in HORTON A configured SCF solver that must be tested. """ # vanadium atoms numbers = np.array([23]) pseudo_numbers = numbers.astype(float) coordinates = np.zeros((1, 3), float) # Simple basis set obasis = get_gobasis(coordinates, numbers, 'def2-tzvpd') # Compute integrals olp = obasis.compute_overlap() kin = obasis.compute_kinetic() na = obasis.compute_nuclear_attraction(coordinates, pseudo_numbers) er = obasis.compute_electron_repulsion() # Setup of restricted HF Hamiltonian terms = [ RTwoIndexTerm(kin, 'kin'), RDirectTerm(er, 'hartree'), RExchangeTerm(er, 'x_hf'), RTwoIndexTerm(na, 'ne'), ] ham = REffHam(terms) # Define fractional occupations of interest. (Spin-compensated case) occ_model = FixedOccModel(np.array([1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 0.5])) # Allocate orbitals and make the initial guess orb_alpha = Orbitals(obasis.nbasis) guess_core_hamiltonian(olp, kin+na, orb_alpha) # SCF test check_solve(ham, scf_solver, occ_model, olp, kin, na, orb_alpha)
def __call__(self, ham, overlap, occ_model, *dm0s): '''Find a self-consistent set of density matrices. **Arguments:** ham An effective Hamiltonian. overlap The overlap operator. occ_model Model for the orbital occupations. dm1, dm2, ... The initial density matrices. The number of dms must match ham.ndm. ''' # Some type checking if ham.ndm != len(dm0s): raise TypeError( 'The number of initial density matrices does not match the Hamiltonian.' ) # Check input density matrices. for i in xrange(ham.ndm): check_dm(dm0s[i], overlap) occ_model.check_dms(overlap, *dm0s) if log.do_medium: log('Starting SCF with optimal damping. ham.ndm=%i' % ham.ndm) log.hline() log(' Iter Energy Error Mixing') log.hline() fock0s = [np.zeros(overlap.shape) for i in xrange(ham.ndm)] fock1s = [np.zeros(overlap.shape) for i in xrange(ham.ndm)] dm1s = [np.zeros(overlap.shape) for i in xrange(ham.ndm)] orbs = [Orbitals(overlap.shape[0]) for i in xrange(ham.ndm)] work = np.zeros(dm0s[0].shape) commutator = np.zeros(dm0s[0].shape) converged = False counter = 0 mixing = None error = None while self.maxiter is None or counter < self.maxiter: # feed the latest density matrices in the hamiltonian ham.reset(*dm0s) # Construct the Fock operators in point 0 ham.compute_fock(*fock0s) # Compute the energy in point 0 energy0 = ham.compute_energy() if log.do_medium: if mixing is None: log('%5i %20.13f' % (counter, energy0)) else: log('%5i %20.13f %12.5e %10.5f' % (counter, energy0, error, mixing)) # go to point 1 by diagonalizing the fock matrices for i in xrange(ham.ndm): orbs[i].from_fock(fock0s[i], overlap) # Assign new occupation numbers. occ_model.assign(*orbs) # Construct the density matrices for i in xrange(ham.ndm): dm1s[i][:] = orbs[i].to_dm() # feed the latest density matrices in the hamiltonian ham.reset(*dm1s) # Compute the fock matrices in point 1 ham.compute_fock(*fock1s) # Compute the energy in point 1 energy1 = ham.compute_energy() # Compute the derivatives of the energy at point 0 and 1 for a # linear interpolation of the density matrices deriv0 = 0.0 deriv1 = 0.0 for i in xrange(ham.ndm): deriv0 += np.einsum('ab,ab', fock0s[i], dm1s[i]) deriv0 -= np.einsum('ab,ab', fock0s[i], dm0s[i]) deriv1 += np.einsum('ab,ab', fock1s[i], dm1s[i]) deriv1 -= np.einsum('ab,ab', fock1s[i], dm0s[i]) deriv0 *= ham.deriv_scale deriv1 *= ham.deriv_scale # find the lambda that minimizes the cubic polynomial in the range [0,1] if log.do_high: log(' E0: % 10.5e D0: % 10.5e' % (energy0, deriv0)) log(' E1-E0: % 10.5e D1: % 10.5e' % (energy1 - energy0, deriv1)) mixing = find_min_cubic(energy0, energy1, deriv0, deriv1) if self.debug: check_cubic(ham, dm0s, dm1s, energy0, energy1, deriv0, deriv1) # compute the mixed density and fock matrices (in-place in dm0s and fock0s) for i in xrange(ham.ndm): dm0s[i][:] *= 1 - mixing dm0s[i][:] += dm1s[i] * mixing fock0s[i][:] *= 1 - mixing fock0s[i][:] += fock1s[i] * mixing # Compute the convergence criterion. errorsq = 0.0 for i in xrange(ham.ndm): commutator = compute_commutator(dm0s[i], fock0s[i], overlap) errorsq += np.einsum('ab,ab', commutator, commutator) error = errorsq**0.5 if error < self.threshold: converged = True break elif mixing == 0.0: raise NoSCFConvergence( 'The ODA algorithm made a zero step without reaching convergence.' ) # counter counter += 1 if log.do_medium: ham.log() if not converged: raise NoSCFConvergence return counter
def load_mkl(filename): '''Load data from a Molekel file. Parameters ---------- filename : str The filename of the mkl file. Returns ------- results : dict Data loaded from file, with keys: ``coordinates``, ``numbers``, ``obasis``, ``orb_alpha``. It may also contain: ``orb_beta``, ``signs``. ''' def helper_char_mult(f): return [int(word) for word in f.readline().split()] def helper_coordinates(f): numbers = [] coordinates = [] while True: line = f.readline() if len(line) == 0 or line.strip() == '$END': break words = line.split() numbers.append(int(words[0])) coordinates.append([float(words[1]), float(words[2]), float(words[3])]) numbers = np.array(numbers, int) coordinates = np.array(coordinates)*angstrom return numbers, coordinates def helper_obasis(f, coordinates): shell_types = [] shell_map = [] nprims = [] alphas = [] con_coeffs = [] center_counter = 0 in_shell = False nprim = None while True: line = f.readline() lstrip = line.strip() if len(line) == 0 or lstrip == '$END': break if len(lstrip) == 0: continue if lstrip == '$$': center_counter += 1 in_shell = False else: words = line.split() if len(words) == 2: assert in_shell alpha = float(words[0]) alphas.append(alpha) con_coeffs.append(float(words[1])) nprim += 1 else: if nprim is not None: nprims.append(nprim) shell_map.append(center_counter) # always assume pure basis functions shell_type = str_to_shell_types(words[1], pure=True)[0] shell_types.append(shell_type) in_shell = True nprim = 0 if nprim is not None: nprims.append(nprim) shell_map = np.array(shell_map) nprims = np.array(nprims) shell_types = np.array(shell_types) alphas = np.array(alphas) con_coeffs = np.array(con_coeffs) return GOBasis(coordinates, shell_map, nprims, shell_types, alphas, con_coeffs) def helper_coeffs(f, nbasis): coeffs = [] energies = [] in_orb = 0 while True: line = f.readline() lstrip = line.strip() if len(line) == 0 or lstrip == '$END': break if in_orb == 0: # read a1g line words = lstrip.split() ncol = len(words) assert ncol > 0 for word in words: assert word == 'a1g' cols = [np.zeros((nbasis,1), float) for icol in xrange(ncol)] in_orb = 1 elif in_orb == 1: # read energies words = lstrip.split() assert len(words) == ncol for word in words: energies.append(float(word)) in_orb = 2 ibasis = 0 elif in_orb == 2: # read expansion coefficients words = lstrip.split() assert len(words) == ncol for icol in xrange(ncol): cols[icol][ibasis] = float(words[icol]) ibasis += 1 if ibasis == nbasis: in_orb = 0 coeffs.extend(cols) return np.hstack(coeffs), np.array(energies) def helper_occ(f): occs = [] while True: line = f.readline() lstrip = line.strip() if len(line) == 0 or lstrip == '$END': break for word in lstrip.split(): occs.append(float(word)) return np.array(occs) charge = None spinmult = None numbers = None coordinates = None obasis = None coeff_alpha = None ener_alpha = None occ_alpha = None coeff_beta = None ener_beta = None occ_beta = None with open(filename) as f: while True: line = f.readline() if len(line) == 0: break line = line.strip() if line == '$CHAR_MULT': charge, spinmult = helper_char_mult(f) elif line == '$COORD': numbers, coordinates = helper_coordinates(f) elif line == '$BASIS': obasis = helper_obasis(f, coordinates) elif line == '$COEFF_ALPHA': coeff_alpha, ener_alpha = helper_coeffs(f, obasis.nbasis) elif line == '$OCC_ALPHA': occ_alpha = helper_occ(f) elif line == '$COEFF_BETA': coeff_beta, ener_beta = helper_coeffs(f, obasis.nbasis) elif line == '$OCC_BETA': occ_beta = helper_occ(f) if charge is None: raise IOError('Charge and multiplicity not found in mkl file.') if coordinates is None: raise IOError('Coordinates not found in mkl file.') if obasis is None: raise IOError('Orbital basis not found in mkl file.') if coeff_alpha is None: raise IOError('Alpha orbitals not found in mkl file.') if occ_alpha is None: raise IOError('Alpha occupation numbers not found in mkl file.') nelec = numbers.sum() - charge if coeff_beta is None: assert nelec % 2 == 0 assert abs(occ_alpha.sum() - nelec) < 1e-7 orb_alpha = Orbitals(obasis.nbasis, coeff_alpha.shape[1]) orb_alpha.coeffs[:] = coeff_alpha orb_alpha.energies[:] = ener_alpha orb_alpha.occupations[:] = occ_alpha/2 orb_beta = None else: if occ_beta is None: raise IOError('Beta occupation numbers not found in mkl file while beta orbitals were present.') nalpha = int(np.round(occ_alpha.sum())) nbeta = int(np.round(occ_beta.sum())) assert nelec == nalpha+nbeta assert coeff_alpha.shape == coeff_beta.shape assert ener_alpha.shape == ener_beta.shape assert occ_alpha.shape == occ_beta.shape orb_alpha = Orbitals(obasis.nbasis, coeff_alpha.shape[1]) orb_alpha.coeffs[:] = coeff_alpha orb_alpha.energies[:] = ener_alpha orb_alpha.occupations[:] = occ_alpha orb_beta = Orbitals(obasis.nbasis, coeff_beta.shape[1]) orb_beta.coeffs[:] = coeff_beta orb_beta.energies[:] = ener_beta orb_beta.occupations[:] = occ_beta result = { 'coordinates': coordinates, 'orb_alpha': orb_alpha, 'numbers': numbers, 'obasis': obasis, } if orb_beta is not None: result['orb_beta'] = orb_beta _fix_molden_from_buggy_codes(result, filename) return result
def load_molden(filename): """Load data from a molden input file. Parameters ---------- filename : str The filename of the molden input file. Returns ------- results : dict Data loaded from file, with with: ``coordinates``, ``numbers``, ``pseudo_numbers``, ``obasis``, ``orb_alpha``, ``signs``. It may also contain: ``title``, ``orb_beta``. """ def helper_coordinates(f, cunit): """Load element numbers and coordinates""" numbers = [] pseudo_numbers = [] coordinates = [] while True: last_pos = f.tell() line = f.readline() if len(line) == 0: break words = line.split() if len(words) != 6: # Go back to previous line and stop f.seek(last_pos) break else: numbers.append(periodic[words[0]].number) pseudo_numbers.append(float(words[2])) coordinates.append( [float(words[3]), float(words[4]), float(words[5])]) numbers = np.array(numbers, int) pseudo_numbers = np.array(pseudo_numbers) coordinates = np.array(coordinates) * cunit return numbers, pseudo_numbers, coordinates def helper_obasis(f, coordinates, pure): """Load the orbital basis""" shell_types = [] shell_map = [] nprims = [] alphas = [] con_coeffs = [] icenter = 0 in_atom = False in_shell = False while True: last_pos = f.tell() line = f.readline() if len(line) == 0: break words = line.split() if len(words) == 0: in_atom = False in_shell = False elif len(words) == 2 and not in_atom: icenter = int(words[0]) - 1 in_atom = True in_shell = False elif len(words) == 3: in_shell = True shell_map.append(icenter) shell_label = words[0].lower() shell_type = str_to_shell_types(shell_label, pure.get(shell_label, False))[0] shell_types.append(shell_type) nprims.append(int(words[1])) elif len(words) == 2 and in_atom: assert in_shell alpha = float(words[0].replace('D', 'E')) alphas.append(alpha) con_coeff = float(words[1].replace('D', 'E')) con_coeffs.append(con_coeff) else: # done, go back one line f.seek(last_pos) break shell_map = np.array(shell_map) nprims = np.array(nprims) shell_types = np.array(shell_types) alphas = np.array(alphas) con_coeffs = np.array(con_coeffs) return GOBasis(coordinates, shell_map, nprims, shell_types, alphas, con_coeffs) def helper_coeffs(f, nbasis): """Load the orbital coefficients""" coeff_alpha = [] ener_alpha = [] occ_alpha = [] coeff_beta = [] ener_beta = [] occ_beta = [] new_orb = True icoeff = nbasis while True: line = f.readline() if len(line) == 0 or '[' in line: break # prepare array with orbital coefficients if '=' in line: if line.startswith(' Ene='): energy = float(line[5:]) elif line.startswith(' Spin='): spin = line[6:].strip() elif line.startswith(' Occup='): occ = float(line[7:]) new_orb = True else: if new_orb: # store col, energy and occ col = np.zeros((nbasis, 1)) if spin.lower() == 'alpha': coeff_alpha.append(col) ener_alpha.append(energy) occ_alpha.append(occ) else: coeff_beta.append(col) ener_beta.append(energy) occ_beta.append(occ) new_orb = False if icoeff < nbasis: raise IOError( 'Too little expansions coefficients in one orbital in molden file.' ) icoeff = 0 words = line.split() if icoeff >= nbasis: raise IOError( 'Too many expansions coefficients in one orbital in molden file.' ) col[icoeff] = float(words[1]) icoeff += 1 assert int(words[0]) == icoeff coeff_alpha = np.hstack(coeff_alpha) ener_alpha = np.array(ener_alpha) occ_alpha = np.array(occ_alpha) if len(coeff_beta) == 0: coeff_beta = None ener_beta = None occ_beta = None else: coeff_beta = np.hstack(coeff_beta) ener_beta = np.array(ener_beta) occ_beta = np.array(occ_beta) return (coeff_alpha, ener_alpha, occ_alpha), (coeff_beta, ener_beta, occ_beta) # First pass: scan the file for pure/cartesian modifiers. # Unfortunately, some program put this information _AFTER_ the basis # set specification. pure = {'d': False, 'f': False, 'g': False} with open(filename) as f: for line in f: line = line.lower() if line.startswith('[5d]') or line.startswith('[5d7f]'): pure['d'] = True pure['f'] = True elif line.lower().startswith('[7f]'): pure['f'] = True elif line.lower().startswith('[5d10f]'): pure['d'] = True pure['f'] = False elif line.lower().startswith('[9g]'): pure['g'] = True # Second pass: read all the other info. numbers = None coordinates = None obasis = None coeff_alpha = None ener_alpha = None occ_alpha = None coeff_beta = None ener_beta = None occ_beta = None title = None with open(filename) as f: line = f.readline() if line != '[Molden Format]\n': raise IOError('Molden header not found') while True: line = f.readline() if len(line) == 0: break line = line.strip() if line == '[Title]': title = f.readline().strip() elif line.startswith('[Atoms]'): if 'au' in line.lower(): cunit = 1.0 elif 'angs' in line.lower(): cunit = angstrom numbers, pseudo_numbers, coordinates = helper_coordinates( f, cunit) elif line == '[GTO]': obasis = helper_obasis(f, coordinates, pure) elif line == '[STO]': raise NotImplementedError( 'Slater-type orbitals are not supported in HORTON.') elif line == '[MO]': data_alpha, data_beta = helper_coeffs(f, obasis.nbasis) coeff_alpha, ener_alpha, occ_alpha = data_alpha coeff_beta, ener_beta, occ_beta = data_beta if coordinates is None: raise IOError('Coordinates not found in molden input file.') if obasis is None: raise IOError('Orbital basis not found in molden input file.') if coeff_alpha is None: raise IOError('Alpha orbitals not found in molden input file.') if coeff_beta is None: nalpha = int(np.round(occ_alpha.sum())) / 2 orb_alpha = Orbitals(obasis.nbasis, coeff_alpha.shape[1]) orb_alpha.coeffs[:] = coeff_alpha orb_alpha.energies[:] = ener_alpha orb_alpha.occupations[:] = occ_alpha / 2 orb_beta = None else: nalpha = int(np.round(occ_alpha.sum())) nbeta = int(np.round(occ_beta.sum())) assert coeff_alpha.shape == coeff_beta.shape assert ener_alpha.shape == ener_beta.shape assert occ_alpha.shape == occ_beta.shape orb_alpha = Orbitals(obasis.nbasis, coeff_alpha.shape[1]) orb_alpha.coeffs[:] = coeff_alpha orb_alpha.energies[:] = ener_alpha orb_alpha.occupations[:] = occ_alpha orb_beta = Orbitals(obasis.nbasis, coeff_beta.shape[1]) orb_beta.coeffs[:] = coeff_beta orb_beta.energies[:] = ener_beta orb_beta.occupations[:] = occ_beta permutation = _get_molden_permutation(obasis) # filter out ghost atoms mask = pseudo_numbers != 0 coordinates = coordinates[mask] numbers = numbers[mask] pseudo_numbers = pseudo_numbers[mask] result = { 'coordinates': coordinates, 'orb_alpha': orb_alpha, 'numbers': numbers, 'obasis': obasis, 'permutation': permutation, 'pseudo_numbers': pseudo_numbers, } if title is not None: result['title'] = title if orb_beta is not None: result['orb_beta'] = orb_beta _fix_molden_from_buggy_codes(result, filename) return result
def load_atom_cp2k(filename): """Load data from a CP2K ATOM computation. Parameters --------- filename : str The name of the cp2k out file Returns ------- results : dict Contains: ``obasis``, ``orb_alpha``, ``coordinates``, ``numbers``, ``energy``, ``pseudo_numbers``. May contain: ``orb_beta``. Notes ----- This function assumes that the following subsections are present in the CP2K ATOM input file, in the section ``ATOM%PRINT``: .. code-block:: text &PRINT &POTENTIAL &END POTENTIAL &BASIS_SET &END BASIS_SET &ORBITALS &END ORBITALS &END PRINT """ with open(filename) as f: # Find the element number number = None for line in f: if line.startswith(' Atomic Energy Calculation'): number = int(line[-5:-1]) break if number is None: raise IOError( 'Could not find atomic number in CP2K ATOM output: %s.' % filename) # Go to the all-electron basis set and read it. for line in f: if line.startswith(' All Electron Basis'): break ae_obasis = _read_cp2k_obasis(f) # Go to the pseudo basis set and read it. for line in f: if line.startswith(' Pseudopotential Basis'): break pp_obasis = _read_cp2k_obasis(f) # Search for (un)restricted restricted = None for line in f: if line.startswith(' METHOD |'): if 'U' in line: restricted = False break elif 'R' in line: restricted = True break # Search for the core charge (pseudo number) pseudo_number = None for line in f: if line.startswith(' Core Charge'): pseudo_number = float(line[70:]) assert pseudo_number == int(pseudo_number) break elif line.startswith(' Electronic structure'): pseudo_number = float(number) break if pseudo_number is None: raise IOError( 'Could not find effective core charge in CP2K ATOM output:' ' %s' % filename) # Select the correct basis if pseudo_number == number: obasis = ae_obasis else: obasis = pp_obasis # Search for energy for line in f: if line.startswith( ' Energy components [Hartree] Total Energy ::'): energy = float(line[60:]) break # Read orbital energies and occupations for line in f: if line.startswith(' Orbital energies'): break f.next() oe_alpha, oe_beta = _read_cp2k_occupations_energies(f, restricted) # Read orbital expansion coefficients line = f.next() if (line != " Atomic orbital expansion coefficients [Alpha]\n") and \ (line != " Atomic orbital expansion coefficients []\n"): raise IOError( 'Could not find orbital coefficients in CP2K ATOM output: ' '%s' % filename) coeffs_alpha = _read_cp2k_orbital_coeffs(f, oe_alpha) if not restricted: line = f.next() if line != " Atomic orbital expansion coefficients [Beta]\n": raise IOError( 'Could not find beta orbital coefficient in CP2K ATOM ' 'output: %s' % filename) coeffs_beta = _read_cp2k_orbital_coeffs(f, oe_beta) # Turn orbital data into a HORTON orbital expansions if restricted: norb, nel = _get_norb_nel(oe_alpha) assert nel % 2 == 0 orb_alpha = Orbitals(obasis.nbasis, norb) orb_beta = None _fill_orbitals(orb_alpha, oe_alpha, coeffs_alpha, obasis.shell_types, restricted) else: norb_alpha = _get_norb_nel(oe_alpha)[0] norb_beta = _get_norb_nel(oe_beta)[0] assert norb_alpha == norb_beta orb_alpha = Orbitals(obasis.nbasis, norb_alpha) orb_beta = Orbitals(obasis.nbasis, norb_beta) _fill_orbitals(orb_alpha, oe_alpha, coeffs_alpha, obasis.shell_types, restricted) _fill_orbitals(orb_beta, oe_beta, coeffs_beta, obasis.shell_types, restricted) result = { 'obasis': obasis, 'orb_alpha': orb_alpha, 'coordinates': obasis.centers, 'numbers': np.array([number]), 'energy': energy, 'pseudo_numbers': np.array([pseudo_number]), } if orb_beta is not None: result['orb_beta'] = orb_beta return result
def load_molden(filename): """Load data from a molden input file. Parameters ---------- filename : str The filename of the molden input file. Returns ------- results : dict Data loaded from file, with with: ``coordinates``, ``numbers``, ``pseudo_numbers``, ``obasis``, ``orb_alpha``, ``signs``. It may also contain: ``title``, ``orb_beta``. """ def helper_coordinates(f, cunit): """Load element numbers and coordinates""" numbers = [] pseudo_numbers = [] coordinates = [] while True: last_pos = f.tell() line = f.readline() if len(line) == 0: break words = line.split() if len(words) != 6: # Go back to previous line and stop f.seek(last_pos) break else: numbers.append(periodic[words[0]].number) pseudo_numbers.append(float(words[2])) coordinates.append([float(words[3]), float(words[4]), float(words[5])]) numbers = np.array(numbers, int) pseudo_numbers = np.array(pseudo_numbers) coordinates = np.array(coordinates)*cunit return numbers, pseudo_numbers, coordinates def helper_obasis(f, coordinates, pure): """Load the orbital basis""" shell_types = [] shell_map = [] nprims = [] alphas = [] con_coeffs = [] icenter = 0 in_atom = False in_shell = False while True: last_pos = f.tell() line = f.readline() if len(line) == 0: break words = line.split() if len(words) == 0: in_atom = False in_shell = False elif len(words) == 2 and not in_atom: icenter = int(words[0])-1 in_atom = True in_shell = False elif len(words) == 3: in_shell = True shell_map.append(icenter) shell_label = words[0].lower() shell_type = str_to_shell_types(shell_label, pure.get(shell_label, False))[0] shell_types.append(shell_type) nprims.append(int(words[1])) elif len(words) == 2 and in_atom: assert in_shell alpha = float(words[0].replace('D', 'E')) alphas.append(alpha) con_coeff = float(words[1].replace('D', 'E')) con_coeffs.append(con_coeff) else: # done, go back one line f.seek(last_pos) break shell_map = np.array(shell_map) nprims = np.array(nprims) shell_types = np.array(shell_types) alphas = np.array(alphas) con_coeffs = np.array(con_coeffs) return GOBasis(coordinates, shell_map, nprims, shell_types, alphas, con_coeffs) def helper_coeffs(f, nbasis): """Load the orbital coefficients""" coeff_alpha = [] ener_alpha = [] occ_alpha = [] coeff_beta = [] ener_beta = [] occ_beta = [] new_orb = True icoeff = nbasis while True: line = f.readline() if len(line) == 0 or '[' in line: break # prepare array with orbital coefficients if '=' in line: if line.startswith(' Ene='): energy = float(line[5:]) elif line.startswith(' Spin='): spin = line[6:].strip() elif line.startswith(' Occup='): occ = float(line[7:]) new_orb = True else: if new_orb: # store col, energy and occ col = np.zeros((nbasis, 1)) if spin.lower() == 'alpha': coeff_alpha.append(col) ener_alpha.append(energy) occ_alpha.append(occ) else: coeff_beta.append(col) ener_beta.append(energy) occ_beta.append(occ) new_orb = False if icoeff < nbasis: raise IOError('Too little expansions coefficients in one orbital in molden file.') icoeff = 0 words = line.split() if icoeff >= nbasis: raise IOError('Too many expansions coefficients in one orbital in molden file.') col[icoeff] = float(words[1]) icoeff += 1 assert int(words[0]) == icoeff coeff_alpha = np.hstack(coeff_alpha) ener_alpha = np.array(ener_alpha) occ_alpha = np.array(occ_alpha) if len(coeff_beta) == 0: coeff_beta = None ener_beta = None occ_beta = None else: coeff_beta = np.hstack(coeff_beta) ener_beta = np.array(ener_beta) occ_beta = np.array(occ_beta) return (coeff_alpha, ener_alpha, occ_alpha), (coeff_beta, ener_beta, occ_beta) # First pass: scan the file for pure/cartesian modifiers. # Unfortunately, some program put this information _AFTER_ the basis # set specification. pure = {'d': False, 'f': False, 'g': False} with open(filename) as f: for line in f: line = line.lower() if line.startswith('[5d]') or line.startswith('[5d7f]'): pure['d'] = True pure['f'] = True elif line.lower().startswith('[7f]'): pure['f'] = True elif line.lower().startswith('[5d10f]'): pure['d'] = True pure['f'] = False elif line.lower().startswith('[9g]'): pure['g'] = True # Second pass: read all the other info. numbers = None coordinates = None obasis = None coeff_alpha = None ener_alpha = None occ_alpha = None coeff_beta = None ener_beta = None occ_beta = None title = None with open(filename) as f: line = f.readline() if line != '[Molden Format]\n': raise IOError('Molden header not found') while True: line = f.readline() if len(line) == 0: break line = line.strip() if line == '[Title]': title = f.readline().strip() elif line.startswith('[Atoms]'): if 'au' in line.lower(): cunit = 1.0 elif 'angs' in line.lower(): cunit = angstrom numbers, pseudo_numbers, coordinates = helper_coordinates(f, cunit) elif line == '[GTO]': obasis = helper_obasis(f, coordinates, pure) elif line == '[STO]': raise NotImplementedError('Slater-type orbitals are not supported in HORTON.') elif line == '[MO]': data_alpha, data_beta = helper_coeffs(f, obasis.nbasis) coeff_alpha, ener_alpha, occ_alpha = data_alpha coeff_beta, ener_beta, occ_beta = data_beta if coordinates is None: raise IOError('Coordinates not found in molden input file.') if obasis is None: raise IOError('Orbital basis not found in molden input file.') if coeff_alpha is None: raise IOError('Alpha orbitals not found in molden input file.') if coeff_beta is None: nalpha = int(np.round(occ_alpha.sum()))/2 orb_alpha = Orbitals(obasis.nbasis, coeff_alpha.shape[1]) orb_alpha.coeffs[:] = coeff_alpha orb_alpha.energies[:] = ener_alpha orb_alpha.occupations[:] = occ_alpha/2 orb_beta = None else: nalpha = int(np.round(occ_alpha.sum())) nbeta = int(np.round(occ_beta.sum())) assert coeff_alpha.shape == coeff_beta.shape assert ener_alpha.shape == ener_beta.shape assert occ_alpha.shape == occ_beta.shape orb_alpha = Orbitals(obasis.nbasis, coeff_alpha.shape[1]) orb_alpha.coeffs[:] = coeff_alpha orb_alpha.energies[:] = ener_alpha orb_alpha.occupations[:] = occ_alpha orb_beta = Orbitals(obasis.nbasis, coeff_beta.shape[1]) orb_beta.coeffs[:] = coeff_beta orb_beta.energies[:] = ener_beta orb_beta.occupations[:] = occ_beta permutation = _get_molden_permutation(obasis) # filter out ghost atoms mask = pseudo_numbers != 0 coordinates = coordinates[mask] numbers = numbers[mask] pseudo_numbers = pseudo_numbers[mask] result = { 'coordinates': coordinates, 'orb_alpha': orb_alpha, 'numbers': numbers, 'obasis': obasis, 'permutation': permutation, 'pseudo_numbers': pseudo_numbers, } if title is not None: result['title'] = title if orb_beta is not None: result['orb_beta'] = orb_beta _fix_molden_from_buggy_codes(result, filename) return result
def load_wfn(filename): """Load data from a WFN file. Parameters ---------- filename : str The filename of the wfn file. Returns ------- results : dict Data loaded from file, with keys ``title``, ``coordinates``, ``numbers``, ``energy``, ``obasis`` and ``orb_alpha``. May contain ``orb_beta``. """ title, numbers, coordinates, centers, type_assignment, exponents, \ mo_count, mo_occ, mo_energy, coefficients, energy = load_wfn_low(filename) permutation = get_permutation_basis(type_assignment) # permute arrays containing wfn data type_assignment = type_assignment[permutation] mask = get_mask(type_assignment) reduced_size = np.array(mask, int).sum() num_mo = coefficients.shape[1] alphas = np.empty(reduced_size) alphas[:] = exponents[permutation][mask] assert (centers == centers[permutation]).all() shell_map = centers[mask] # cartesian basis: {S:0, P:1, D:2, F:3, G:4, H:5} shell = {1: 0, 2: 1, 5: 2, 11: 3, 21: 4, 36: 5} shell_types = type_assignment[mask] shell_types = np.array([shell[i] for i in shell_types]) assert shell_map.size == shell_types.size == reduced_size nprims = np.ones(reduced_size, int) con_coeffs = np.ones(reduced_size) # build basis set obasis = GOBasis(coordinates, shell_map, nprims, shell_types, alphas, con_coeffs) coefficients = coefficients[permutation] coefficients /= obasis.get_scales().reshape(-1, 1) # make the wavefunction if mo_occ.max() > 1.0: # close shell system orb_alpha = Orbitals(obasis.nbasis, coefficients.shape[1]) orb_alpha.coeffs[:] = coefficients orb_alpha.energies[:] = mo_energy orb_alpha.occupations[:] = mo_occ / 2 orb_beta = None else: # open shell system # counting the number of alpha and beta orbitals index = 1 while index < num_mo and mo_energy[index] >= mo_energy[index - 1] and mo_count[index] == mo_count[index - 1] + 1: index += 1 orb_alpha = Orbitals(obasis.nbasis, index) orb_alpha.coeffs[:] = coefficients[:, :index] orb_alpha.energies[:] = mo_energy[:index] orb_alpha.occupations[:] = mo_occ[:index] orb_beta = Orbitals(obasis.nbasis, num_mo - index) orb_beta.coeffs[:] = coefficients[:, index:] orb_beta.energies[:] = mo_energy[index:] orb_beta.occupations[:] = mo_occ[index:] result = { 'title': title, 'coordinates': coordinates, 'orb_alpha': orb_alpha, 'numbers': numbers, 'obasis': obasis, 'energy': energy, } if orb_beta is not None: result['orb_beta'] = orb_beta return result
def load_fchk(filename): '''Load from a formatted checkpoint file. **Arguments:** filename The filename of the Gaussian formatted checkpoint file. **Returns** a dictionary with: ``title``, ``coordinates``, ``numbers``, ``obasis``, ``orb_alpha``, ``permutation``, ``energy``, ``pseudo_numbers``, ``mulliken_charges``. The dictionary may also contain: ``npa_charges``, ``esp_charges``, ``orb_beta``, ``dm_full_mp2``, ``dm_spin_mp2``, ``dm_full_mp3``, ``dm_spin_mp3``, ``dm_full_cc``, ``dm_spin_cc``, ``dm_full_ci``, ``dm_spin_ci``, ``dm_full_scf``, ``dm_spin_scf``, ``polar``, ``dipole_moment``, ``quadrupole_moment``. ''' from horton.gbasis.cext import GOBasis fchk = FCHKFile(filename, [ "Number of electrons", "Number of independant functions", "Number of independent functions", "Number of alpha electrons", "Number of beta electrons", "Atomic numbers", "Current cartesian coordinates", "Shell types", "Shell to atom map", "Shell to atom map", "Number of primitives per shell", "Primitive exponents", "Contraction coefficients", "P(S=P) Contraction coefficients", "Alpha Orbital Energies", "Alpha MO coefficients", "Beta Orbital Energies", "Beta MO coefficients", "Total Energy", "Nuclear charges", 'Total SCF Density', 'Spin SCF Density', 'Total MP2 Density', 'Spin MP2 Density', 'Total MP3 Density', 'Spin MP3 Density', 'Total CC Density', 'Spin CC Density', 'Total CI Density', 'Spin CI Density', 'Mulliken Charges', 'ESP Charges', 'NPA Charges', 'Polarizability', 'Dipole Moment', 'Quadrupole Moment', ]) # A) Load the geometry numbers = fchk["Atomic numbers"] coordinates = fchk["Current cartesian coordinates"].reshape(-1,3) pseudo_numbers = fchk["Nuclear charges"] # Mask out ghost atoms mask = pseudo_numbers != 0.0 numbers = numbers[mask] # Do not overwrite coordinates array, because it is needed to specify basis system_coordinates = coordinates[mask] pseudo_numbers = pseudo_numbers[mask] # B) Load the orbital basis set shell_types = fchk["Shell types"] shell_map = fchk["Shell to atom map"] - 1 nprims = fchk["Number of primitives per shell"] alphas = fchk["Primitive exponents"] ccoeffs_level1 = fchk["Contraction coefficients"] ccoeffs_level2 = fchk.get("P(S=P) Contraction coefficients") my_shell_types = [] my_shell_map = [] my_nprims = [] my_alphas = [] con_coeffs = [] counter = 0 for i, n in enumerate(nprims): if shell_types[i] == -1: # Special treatment for SP shell type my_shell_types.append(0) my_shell_types.append(1) my_shell_map.append(shell_map[i]) my_shell_map.append(shell_map[i]) my_nprims.append(nprims[i]) my_nprims.append(nprims[i]) my_alphas.append(alphas[counter:counter+n]) my_alphas.append(alphas[counter:counter+n]) con_coeffs.append(ccoeffs_level1[counter:counter+n]) con_coeffs.append(ccoeffs_level2[counter:counter+n]) else: my_shell_types.append(shell_types[i]) my_shell_map.append(shell_map[i]) my_nprims.append(nprims[i]) my_alphas.append(alphas[counter:counter+n]) con_coeffs.append(ccoeffs_level1[counter:counter+n]) counter += n my_shell_types = np.array(my_shell_types) my_shell_map = np.array(my_shell_map) my_nprims = np.array(my_nprims) my_alphas = np.concatenate(my_alphas) con_coeffs = np.concatenate(con_coeffs) del shell_map del shell_types del nprims del alphas obasis = GOBasis(coordinates, my_shell_map, my_nprims, my_shell_types, my_alphas, con_coeffs) # permutation of the orbital basis functions permutation_rules = { -9: np.arange(19), -8: np.arange(17), -7: np.arange(15), -6: np.arange(13), -5: np.arange(11), -4: np.arange(9), -3: np.arange(7), -2: np.arange(5), 0: np.array([0]), 1: np.arange(3), 2: np.array([0, 3, 4, 1, 5, 2]), 3: np.array([0, 4, 5, 3, 9, 6, 1, 8, 7, 2]), 4: np.arange(15)[::-1], 5: np.arange(21)[::-1], 6: np.arange(28)[::-1], 7: np.arange(36)[::-1], 8: np.arange(45)[::-1], 9: np.arange(55)[::-1], } permutation = [] for shell_type in my_shell_types: permutation.extend(permutation_rules[shell_type]+len(permutation)) permutation = np.array(permutation, dtype=int) result = { 'title': fchk.title, 'coordinates': system_coordinates, 'numbers': numbers, 'obasis': obasis, 'permutation': permutation, 'pseudo_numbers': pseudo_numbers, } # C) Load density matrices def load_dm(label): if label in fchk: dm = np.zeros((obasis.nbasis, obasis.nbasis)) start = 0 for i in xrange(obasis.nbasis): stop = start+i+1 dm[i, :i+1] = fchk[label][start:stop] dm[:i+1, i] = fchk[label][start:stop] start = stop return dm # First try to load the post-hf density matrices. load_orbitals = True for key in 'MP2', 'MP3', 'CC', 'CI', 'SCF': dm_full = load_dm('Total %s Density' % key) if dm_full is not None: result['dm_full_%s' % key.lower()] = dm_full dm_spin = load_dm('Spin %s Density' % key) if dm_spin is not None: result['dm_spin_%s' % key.lower()] = dm_spin # D) Load the wavefunction # Handle small difference in fchk files from g03 and g09 nbasis_indep = fchk.get("Number of independant functions") or \ fchk.get("Number of independent functions") if nbasis_indep is None: nbasis_indep = obasis.nbasis # Load orbitals nalpha = fchk['Number of alpha electrons'] nbeta = fchk['Number of beta electrons'] if nalpha < 0 or nbeta < 0 or nalpha+nbeta <= 0: raise ValueError('The file %s does not contain a positive number of electrons.' % filename) orb_alpha = Orbitals(obasis.nbasis, nbasis_indep) orb_alpha.coeffs[:] = fchk['Alpha MO coefficients'].reshape(nbasis_indep, obasis.nbasis).T orb_alpha.energies[:] = fchk['Alpha Orbital Energies'] orb_alpha.occupations[:nalpha] = 1.0 result['orb_alpha'] = orb_alpha if 'Beta Orbital Energies' in fchk: # UHF case orb_beta = Orbitals(obasis.nbasis, nbasis_indep) orb_beta.coeffs[:] = fchk['Beta MO coefficients'].reshape(nbasis_indep, obasis.nbasis).T orb_beta.energies[:] = fchk['Beta Orbital Energies'] orb_beta.occupations[:nbeta] = 1.0 result['orb_beta'] = orb_beta elif fchk['Number of beta electrons'] != fchk['Number of alpha electrons']: # ROHF case orb_beta = Orbitals(obasis.nbasis, nbasis_indep) orb_beta.coeffs[:] = fchk['Alpha MO coefficients'].reshape(nbasis_indep, obasis.nbasis).T orb_beta.energies[:] = fchk['Alpha Orbital Energies'] orb_beta.occupations[:nbeta] = 1.0 result['orb_beta'] = orb_beta # Delete dm_full_scf because it is known to be buggy result.pop('dm_full_scf') # E) Load properties result['energy'] = fchk['Total Energy'] if 'Polarizability' in fchk: result['polar'] = triangle_to_dense(fchk['Polarizability']) if 'Dipole Moment' in fchk: result['dipole_moment'] = fchk['Dipole Moment'] if 'Quadrupole Moment' in fchk: # Convert to HORTON ordering: xx, xy, xz, yy, yz, zz result['quadrupole_moment'] = fchk['Quadrupole Moment'][[0, 3, 4, 1, 5, 2]] # F) Load optional properties # Mask out ghost atoms from charges if 'Mulliken Charges' in fchk: result['mulliken_charges'] = fchk['Mulliken Charges'][mask] if 'ESP Charges' in fchk: result['esp_charges'] = fchk['ESP Charges'][mask] if 'NPA Charges' in fchk: result['npa_charges'] = fchk['NPA Charges'][mask] return result
def __call__(self, ham, overlap, occ_model, *dms): '''Find a self-consistent set of density matrices. **Arguments:** ham An effective Hamiltonian. overlap The overlap operator. occ_model Model for the orbital occupations. dm1, dm2, ... The initial density matrices. The number of dms must match ham.ndm. ''' # Some type checking if ham.ndm != len(dms): raise TypeError( 'The number of initial density matrices does not match the Hamiltonian.' ) # Check input density matrices. for i in xrange(ham.ndm): check_dm(dms[i], overlap) occ_model.check_dms(overlap, *dms) # keep local variables as attributes for inspection/debugging by caller self._history = self.DIISHistoryClass(self.nvector, ham.ndm, ham.deriv_scale, overlap) self._focks = [np.zeros(overlap.shape) for i in xrange(ham.ndm)] self._orbs = [Orbitals(overlap.shape[0]) for i in xrange(ham.ndm)] if log.do_medium: log('Starting restricted closed-shell %s-SCF' % self._history.name) log.hline() log('Iter Error CN Last nv Method Energy Change' ) log.hline() converged = False counter = 0 while self.maxiter is None or counter < self.maxiter: # Construct the Fock operator from scratch if the history is empty: if self._history.nused == 0: # feed the latest density matrices in the hamiltonian ham.reset(*dms) # Construct the Fock operators ham.compute_fock(*self._focks) # Compute the energy if needed by the history energy = ham.compute_energy() if self._history.need_energy \ else None # Add the current fock+dm pair to the history error = self._history.add(energy, dms, self._focks) # Screen logging if log.do_high: log(' DIIS add') if error < self.threshold: converged = True break if log.do_high: log.blank() if log.do_medium: energy_str = ' ' * 20 if energy is None else '% 20.13f' % energy log('%4i %12.5e %2i %20s' % (counter, error, self._history.nused, energy_str)) if log.do_high: log.blank() fock_interpolated = False else: energy = None fock_interpolated = True # Take a regular SCF step using the current fock matrix. Then # construct a new density matrix and fock matrix. for i in xrange(ham.ndm): self._orbs[i].from_fock(self._focks[i], overlap) occ_model.assign(*self._orbs) for i in xrange(ham.ndm): dms[i][:] = self._orbs[i].to_dm() ham.reset(*dms) energy = ham.compute_energy( ) if self._history.need_energy else None ham.compute_fock(*self._focks) # Add the current (dm, fock) pair to the history if log.do_high: log(' DIIS add') error = self._history.add(energy, dms, self._focks) # break when converged if error < self.threshold: converged = True break # Screen logging if log.do_high: log.blank() if log.do_medium: energy_str = ' ' * 20 if energy is None else '% 20.13f' % energy log('%4i %12.5e %2i %20s' % (counter, error, self._history.nused, energy_str)) if log.do_high: log.blank() # get extra/intra-polated Fock matrix while True: # The following method writes the interpolated dms and focks # in-place. energy_approx, coeffs, cn, method, error = self._history.solve( dms, self._focks) # if the error is small on the interpolated state, we have # converged to a solution that may have fractional occupation # numbers. if error < self.threshold: converged = True break #if coeffs[coeffs<0].sum() < -1: # if log.do_high: # log(' DIIS (coeffs too negative) -> drop %i and retry' % self._history.stack[0].identity) # self._history.shrink() if self._history.nused <= 2: break if coeffs[-1] == 0.0: if log.do_high: log(' DIIS (last coeff zero) -> drop %i and retry' % self._history.stack[0].identity) self._history.shrink() else: break if False and len(coeffs) == 2: dms_tmp = [dm.copy() for dm in dms] import matplotlib.pyplot as pt xs = np.linspace(0.0, 1.0, 25) a, b = self._history._setup_equations() energies1 = [] energies2 = [] for x in xs: x_coeffs = np.array([1 - x, x]) energies1.append( np.dot(x_coeffs, 0.5 * np.dot(a, x_coeffs) - b)) self._history._build_combinations(x_coeffs, dms_tmp, None) ham.reset(*dms_tmp) energies2.append(ham.compute_energy()) print x, energies1[-1], energies2[-1] pt.clf() pt.plot(xs, energies1, label='est') pt.plot(xs, energies2, label='ref') pt.axvline(coeffs[1], color='k') pt.legend(loc=0) pt.savefig('diis_test_%05i.png' % counter) if energy_approx is not None: energy_change = energy_approx - min( state.energy for state in self._history.stack) else: energy_change = None # log if log.do_high: self._history.log(coeffs) if log.do_medium: change_str = ' ' * 10 if energy_change is None else '% 12.7f' % energy_change log('%4i %10.3e %12.7f %2i %s %12s' % (counter, cn, coeffs[-1], self._history.nused, method, change_str)) if log.do_high: log.blank() if self.prune_old_states: # get rid of old states with zero coeff for i in xrange(self._history.nused): if coeffs[i] == 0.0: if log.do_high: log(' DIIS insignificant -> drop %i' % self._history.stack[0].identity) self._history.shrink() else: break # counter counter += 1 if log.do_medium: if converged: log('%4i %12.5e (converged)' % (counter, error)) log.blank() if not self.skip_energy or self._history.need_energy: if not self._history.need_energy: ham.compute_energy() if log.do_medium: ham.log() if not converged: raise NoSCFConvergence return counter
def load_fchk(filename): '''Load from a formatted checkpoint file. **Arguments:** filename The filename of the Gaussian formatted checkpoint file. **Returns** a dictionary with: ``title``, ``coordinates``, ``numbers``, ``obasis``, ``orb_alpha``, ``permutation``, ``energy``, ``pseudo_numbers``, ``mulliken_charges``. The dictionary may also contain: ``npa_charges``, ``esp_charges``, ``orb_beta``, ``dm_full_mp2``, ``dm_spin_mp2``, ``dm_full_mp3``, ``dm_spin_mp3``, ``dm_full_cc``, ``dm_spin_cc``, ``dm_full_ci``, ``dm_spin_ci``, ``dm_full_scf``, ``dm_spin_scf``, ``polar``, ``dipole_moment``, ``quadrupole_moment``. ''' from horton.gbasis.cext import GOBasis fchk = FCHKFile(filename, [ "Number of electrons", "Number of independant functions", "Number of independent functions", "Number of alpha electrons", "Number of beta electrons", "Atomic numbers", "Current cartesian coordinates", "Shell types", "Shell to atom map", "Shell to atom map", "Number of primitives per shell", "Primitive exponents", "Contraction coefficients", "P(S=P) Contraction coefficients", "Alpha Orbital Energies", "Alpha MO coefficients", "Beta Orbital Energies", "Beta MO coefficients", "Total Energy", "Nuclear charges", 'Total SCF Density', 'Spin SCF Density', 'Total MP2 Density', 'Spin MP2 Density', 'Total MP3 Density', 'Spin MP3 Density', 'Total CC Density', 'Spin CC Density', 'Total CI Density', 'Spin CI Density', 'Mulliken Charges', 'ESP Charges', 'NPA Charges', 'Polarizability', 'Dipole Moment', 'Quadrupole Moment', ]) # A) Load the geometry numbers = fchk["Atomic numbers"] coordinates = fchk["Current cartesian coordinates"].reshape(-1, 3) pseudo_numbers = fchk["Nuclear charges"] # Mask out ghost atoms mask = pseudo_numbers != 0.0 numbers = numbers[mask] # Do not overwrite coordinates array, because it is needed to specify basis system_coordinates = coordinates[mask] pseudo_numbers = pseudo_numbers[mask] # B) Load the orbital basis set shell_types = fchk["Shell types"] shell_map = fchk["Shell to atom map"] - 1 nprims = fchk["Number of primitives per shell"] alphas = fchk["Primitive exponents"] ccoeffs_level1 = fchk["Contraction coefficients"] ccoeffs_level2 = fchk.get("P(S=P) Contraction coefficients") my_shell_types = [] my_shell_map = [] my_nprims = [] my_alphas = [] con_coeffs = [] counter = 0 for i, n in enumerate(nprims): if shell_types[i] == -1: # Special treatment for SP shell type my_shell_types.append(0) my_shell_types.append(1) my_shell_map.append(shell_map[i]) my_shell_map.append(shell_map[i]) my_nprims.append(nprims[i]) my_nprims.append(nprims[i]) my_alphas.append(alphas[counter:counter + n]) my_alphas.append(alphas[counter:counter + n]) con_coeffs.append(ccoeffs_level1[counter:counter + n]) con_coeffs.append(ccoeffs_level2[counter:counter + n]) else: my_shell_types.append(shell_types[i]) my_shell_map.append(shell_map[i]) my_nprims.append(nprims[i]) my_alphas.append(alphas[counter:counter + n]) con_coeffs.append(ccoeffs_level1[counter:counter + n]) counter += n my_shell_types = np.array(my_shell_types) my_shell_map = np.array(my_shell_map) my_nprims = np.array(my_nprims) my_alphas = np.concatenate(my_alphas) con_coeffs = np.concatenate(con_coeffs) del shell_map del shell_types del nprims del alphas obasis = GOBasis(coordinates, my_shell_map, my_nprims, my_shell_types, my_alphas, con_coeffs) # permutation of the orbital basis functions permutation_rules = { -9: np.arange(19), -8: np.arange(17), -7: np.arange(15), -6: np.arange(13), -5: np.arange(11), -4: np.arange(9), -3: np.arange(7), -2: np.arange(5), 0: np.array([0]), 1: np.arange(3), 2: np.array([0, 3, 4, 1, 5, 2]), 3: np.array([0, 4, 5, 3, 9, 6, 1, 8, 7, 2]), 4: np.arange(15)[::-1], 5: np.arange(21)[::-1], 6: np.arange(28)[::-1], 7: np.arange(36)[::-1], 8: np.arange(45)[::-1], 9: np.arange(55)[::-1], } permutation = [] for shell_type in my_shell_types: permutation.extend(permutation_rules[shell_type] + len(permutation)) permutation = np.array(permutation, dtype=int) result = { 'title': fchk.title, 'coordinates': system_coordinates, 'numbers': numbers, 'obasis': obasis, 'permutation': permutation, 'pseudo_numbers': pseudo_numbers, } # C) Load density matrices def load_dm(label): if label in fchk: dm = np.zeros((obasis.nbasis, obasis.nbasis)) start = 0 for i in xrange(obasis.nbasis): stop = start + i + 1 dm[i, :i + 1] = fchk[label][start:stop] dm[:i + 1, i] = fchk[label][start:stop] start = stop return dm # First try to load the post-hf density matrices. load_orbitals = True for key in 'MP2', 'MP3', 'CC', 'CI', 'SCF': dm_full = load_dm('Total %s Density' % key) if dm_full is not None: result['dm_full_%s' % key.lower()] = dm_full dm_spin = load_dm('Spin %s Density' % key) if dm_spin is not None: result['dm_spin_%s' % key.lower()] = dm_spin # D) Load the wavefunction # Handle small difference in fchk files from g03 and g09 nbasis_indep = fchk.get("Number of independant functions") or \ fchk.get("Number of independent functions") if nbasis_indep is None: nbasis_indep = obasis.nbasis # Load orbitals nalpha = fchk['Number of alpha electrons'] nbeta = fchk['Number of beta electrons'] if nalpha < 0 or nbeta < 0 or nalpha + nbeta <= 0: raise ValueError( 'The file %s does not contain a positive number of electrons.' % filename) orb_alpha = Orbitals(obasis.nbasis, nbasis_indep) orb_alpha.coeffs[:] = fchk['Alpha MO coefficients'].reshape( nbasis_indep, obasis.nbasis).T orb_alpha.energies[:] = fchk['Alpha Orbital Energies'] orb_alpha.occupations[:nalpha] = 1.0 result['orb_alpha'] = orb_alpha if 'Beta Orbital Energies' in fchk: # UHF case orb_beta = Orbitals(obasis.nbasis, nbasis_indep) orb_beta.coeffs[:] = fchk['Beta MO coefficients'].reshape( nbasis_indep, obasis.nbasis).T orb_beta.energies[:] = fchk['Beta Orbital Energies'] orb_beta.occupations[:nbeta] = 1.0 result['orb_beta'] = orb_beta elif fchk['Number of beta electrons'] != fchk['Number of alpha electrons']: # ROHF case orb_beta = Orbitals(obasis.nbasis, nbasis_indep) orb_beta.coeffs[:] = fchk['Alpha MO coefficients'].reshape( nbasis_indep, obasis.nbasis).T orb_beta.energies[:] = fchk['Alpha Orbital Energies'] orb_beta.occupations[:nbeta] = 1.0 result['orb_beta'] = orb_beta # Delete dm_full_scf because it is known to be buggy result.pop('dm_full_scf') # E) Load properties result['energy'] = fchk['Total Energy'] if 'Polarizability' in fchk: result['polar'] = triangle_to_dense(fchk['Polarizability']) if 'Dipole Moment' in fchk: result['dipole_moment'] = fchk['Dipole Moment'] if 'Quadrupole Moment' in fchk: # Convert to HORTON ordering: xx, xy, xz, yy, yz, zz result['quadrupole_moment'] = fchk['Quadrupole Moment'][[ 0, 3, 4, 1, 5, 2 ]] # F) Load optional properties # Mask out ghost atoms from charges if 'Mulliken Charges' in fchk: result['mulliken_charges'] = fchk['Mulliken Charges'][mask] if 'ESP Charges' in fchk: result['esp_charges'] = fchk['ESP Charges'][mask] if 'NPA Charges' in fchk: result['npa_charges'] = fchk['NPA Charges'][mask] return result
def load_mkl(filename): '''Load data from a Molekel file. Parameters ---------- filename : str The filename of the mkl file. Returns ------- results : dict Data loaded from file, with keys: ``coordinates``, ``numbers``, ``obasis``, ``orb_alpha``. It may also contain: ``orb_beta``, ``signs``. ''' def helper_char_mult(f): return [int(word) for word in f.readline().split()] def helper_coordinates(f): numbers = [] coordinates = [] while True: line = f.readline() if len(line) == 0 or line.strip() == '$END': break words = line.split() numbers.append(int(words[0])) coordinates.append( [float(words[1]), float(words[2]), float(words[3])]) numbers = np.array(numbers, int) coordinates = np.array(coordinates) * angstrom return numbers, coordinates def helper_obasis(f, coordinates): shell_types = [] shell_map = [] nprims = [] alphas = [] con_coeffs = [] center_counter = 0 in_shell = False nprim = None while True: line = f.readline() lstrip = line.strip() if len(line) == 0 or lstrip == '$END': break if len(lstrip) == 0: continue if lstrip == '$$': center_counter += 1 in_shell = False else: words = line.split() if len(words) == 2: assert in_shell alpha = float(words[0]) alphas.append(alpha) con_coeffs.append(float(words[1])) nprim += 1 else: if nprim is not None: nprims.append(nprim) shell_map.append(center_counter) # always assume pure basis functions shell_type = str_to_shell_types(words[1], pure=True)[0] shell_types.append(shell_type) in_shell = True nprim = 0 if nprim is not None: nprims.append(nprim) shell_map = np.array(shell_map) nprims = np.array(nprims) shell_types = np.array(shell_types) alphas = np.array(alphas) con_coeffs = np.array(con_coeffs) return GOBasis(coordinates, shell_map, nprims, shell_types, alphas, con_coeffs) def helper_coeffs(f, nbasis): coeffs = [] energies = [] in_orb = 0 while True: line = f.readline() lstrip = line.strip() if len(line) == 0 or lstrip == '$END': break if in_orb == 0: # read a1g line words = lstrip.split() ncol = len(words) assert ncol > 0 for word in words: assert word == 'a1g' cols = [np.zeros((nbasis, 1), float) for icol in xrange(ncol)] in_orb = 1 elif in_orb == 1: # read energies words = lstrip.split() assert len(words) == ncol for word in words: energies.append(float(word)) in_orb = 2 ibasis = 0 elif in_orb == 2: # read expansion coefficients words = lstrip.split() assert len(words) == ncol for icol in xrange(ncol): cols[icol][ibasis] = float(words[icol]) ibasis += 1 if ibasis == nbasis: in_orb = 0 coeffs.extend(cols) return np.hstack(coeffs), np.array(energies) def helper_occ(f): occs = [] while True: line = f.readline() lstrip = line.strip() if len(line) == 0 or lstrip == '$END': break for word in lstrip.split(): occs.append(float(word)) return np.array(occs) charge = None spinmult = None numbers = None coordinates = None obasis = None coeff_alpha = None ener_alpha = None occ_alpha = None coeff_beta = None ener_beta = None occ_beta = None with open(filename) as f: while True: line = f.readline() if len(line) == 0: break line = line.strip() if line == '$CHAR_MULT': charge, spinmult = helper_char_mult(f) elif line == '$COORD': numbers, coordinates = helper_coordinates(f) elif line == '$BASIS': obasis = helper_obasis(f, coordinates) elif line == '$COEFF_ALPHA': coeff_alpha, ener_alpha = helper_coeffs(f, obasis.nbasis) elif line == '$OCC_ALPHA': occ_alpha = helper_occ(f) elif line == '$COEFF_BETA': coeff_beta, ener_beta = helper_coeffs(f, obasis.nbasis) elif line == '$OCC_BETA': occ_beta = helper_occ(f) if charge is None: raise IOError('Charge and multiplicity not found in mkl file.') if coordinates is None: raise IOError('Coordinates not found in mkl file.') if obasis is None: raise IOError('Orbital basis not found in mkl file.') if coeff_alpha is None: raise IOError('Alpha orbitals not found in mkl file.') if occ_alpha is None: raise IOError('Alpha occupation numbers not found in mkl file.') nelec = numbers.sum() - charge if coeff_beta is None: assert nelec % 2 == 0 assert abs(occ_alpha.sum() - nelec) < 1e-7 orb_alpha = Orbitals(obasis.nbasis, coeff_alpha.shape[1]) orb_alpha.coeffs[:] = coeff_alpha orb_alpha.energies[:] = ener_alpha orb_alpha.occupations[:] = occ_alpha / 2 orb_beta = None else: if occ_beta is None: raise IOError( 'Beta occupation numbers not found in mkl file while beta orbitals were present.' ) nalpha = int(np.round(occ_alpha.sum())) nbeta = int(np.round(occ_beta.sum())) assert nelec == nalpha + nbeta assert coeff_alpha.shape == coeff_beta.shape assert ener_alpha.shape == ener_beta.shape assert occ_alpha.shape == occ_beta.shape orb_alpha = Orbitals(obasis.nbasis, coeff_alpha.shape[1]) orb_alpha.coeffs[:] = coeff_alpha orb_alpha.energies[:] = ener_alpha orb_alpha.occupations[:] = occ_alpha orb_beta = Orbitals(obasis.nbasis, coeff_beta.shape[1]) orb_beta.coeffs[:] = coeff_beta orb_beta.energies[:] = ener_beta orb_beta.occupations[:] = occ_beta result = { 'coordinates': coordinates, 'orb_alpha': orb_alpha, 'numbers': numbers, 'obasis': obasis, } if orb_beta is not None: result['orb_beta'] = orb_beta _fix_molden_from_buggy_codes(result, filename) return result