def _fix_molden_from_buggy_codes(result, filename): """Detect errors in the data loaded from a molden/mkl/... file and correct. **Argument:** result A dictionary with the data loaded in the ``load_molden`` function. This function can recognize erroneous files created by PSI4 and ORCA. The data in the obasis and signs fields will be updated accordingly. """ obasis = result['obasis'] permutation = result.get('permutation', None) if _is_normalized_properly(result['lf'], obasis, permutation, result['exp_alpha'], result.get('exp_beta')): # The file is good. No need to change data. return if log.do_medium: log('Detected incorrect normalization of orbitals loaded from a file.') # Try to fix it as if it was a file generated by ORCA. orca_signs = _get_orca_signs(obasis) orca_con_coeffs = _get_fixed_con_coeffs(obasis, 'orca') orca_obasis = GOBasis(obasis.centers, obasis.shell_map, obasis.nprims, obasis.shell_types, obasis.alphas, orca_con_coeffs) if _is_normalized_properly(result['lf'], orca_obasis, permutation, result['exp_alpha'], result.get('exp_beta'), orca_signs): if log.do_medium: log('Detected typical ORCA errors in file. Fixing them...') result['obasis'] = orca_obasis result['signs'] = orca_signs return # Try to fix it as if it was a file generated by PSI4 (pre 1.0). psi4_con_coeffs = _get_fixed_con_coeffs(obasis, 'psi4') psi4_obasis = GOBasis(obasis.centers, obasis.shell_map, obasis.nprims, obasis.shell_types, obasis.alphas, psi4_con_coeffs) if _is_normalized_properly(result['lf'], psi4_obasis, permutation, result['exp_alpha'], result.get('exp_beta')): if log.do_medium: log('Detected typical PSI4 errors in file. Fixing them...') result['obasis'] = psi4_obasis return # Last resort: simply renormalize all contractions normed_con_coeffs = _normalized_contractions(obasis) normed_obasis = GOBasis(obasis.centers, obasis.shell_map, obasis.nprims, obasis.shell_types, obasis.alphas, normed_con_coeffs) if _is_normalized_properly(result['lf'], normed_obasis, permutation, result['exp_alpha'], result.get('exp_beta')): if log.do_medium: log('Detected unnormalized contractions in file. Fixing them...') result['obasis'] = normed_obasis return raise IOError(('Could not correct the data read from %s. The molden or ' 'mkl file you are trying to load contains errors. Please ' 'report this problem to [email protected], so he ' 'can fix it.') % filename)
def _read_cp2k_contracted_obasis(f): """Read a contracted basis set from an open CP2K ATOM output file. Parameters ---------- f : file An open readable file object. Returns ------- obasis : GOBasis The orbital basis read from the file. """ # Load the relevant data from the file basis_desc = [] for line in f: if line.startswith(' *******************'): break elif line[3:12] == 'Functions': shell_type = str_to_shell_types(line[1:2], pure=True)[0] a = [] # exponents (alpha) c = [] # contraction coefficients basis_desc.append((shell_type, a, c)) else: values = [float(w) for w in line.split()] a.append(values[0]) # one exponent per line c.append(values[1:]) # many contraction coefficients per line # Convert the basis into HORTON format shell_map = [] shell_types = [] nprims = [] alphas = [] con_coeffs = [] for shell_type, a, c in basis_desc: # get correction to contraction coefficients. CP2K uses different normalization # conventions. corrections = _get_cp2k_norm_corrections(abs(shell_type), np.array(a)) c = np.array(c) / corrections.reshape(-1, 1) # fill in arrays for col in c.T: shell_map.append(0) shell_types.append(shell_type) nprims.append(len(col)) alphas.extend(a) con_coeffs.extend(col) # Create the basis object coordinates = np.zeros((1, 3)) 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) obasis = GOBasis(coordinates, shell_map, nprims, shell_types, alphas, con_coeffs) return obasis
def apply_to(self, system): """Construct a GOBasis object for the system""" shell_map = [] nprims = [] shell_types = [] alphas = [] con_coeffs = [] def get_basis(i, n): """Look up the basis for a given atom""" basis = self.index_map.get(i) if basis is not None: return basis basis = self.element_map.get(n) if basis is not None: return basis if self.default is None: raise KeyError('Could not find basis for atom %i.' % i) else: return self.default def translate_basis(basis_x, n): """Translate the first argument into a GOBasisAtom instance""" if isinstance(basis_x, basestring): basis_fam = go_basis_families.get(basis_x.lower()) if basis_fam is None: raise ValueError('Unknown basis family: %s' % basis_x) basis_atom = basis_fam.get(n) if basis_atom is None: raise ValueError( 'The basis family %s does not contain element %i.' % (basis_x, n)) return basis_atom elif isinstance(basis_x, GOBasisFamily): basis_atom = basis_x.get(n) if basis_atom is None: raise ValueError( 'The basis family %s does not contain element %i.' % (basis_x.name, n)) return basis_atom elif isinstance(basis_x, GOBasisAtom): return basis_x else: raise ValueError( 'Can not interpret %s as an atomic basis function.' % basis_x) # Loop over the atoms and fill in all the lists for i in xrange(system.natom): n = system.numbers[i] basis_x = get_basis(i, n) basis_atom = translate_basis(basis_x, n) basis_atom.extend(i, shell_map, nprims, shell_types, alphas, con_coeffs, self.pure) # Return the Gaussian basis object. return GOBasis(system.coordinates, shell_map, nprims, shell_types, alphas, con_coeffs)
def normalize(self): """Normalize the contraction.""" if self.is_generalized(): raise NotImplementedError("Only segmented contractions can be normalized.") # Warning! Ugly code ahead to avoid re-implementing the norm of contraction. The # code below (ab)uses the GOBasis machinery to get that result. # 1) Constract a GOBasis object with only this contraction. centers = np.array([[0.0, 0.0, 0.0]]) shell_map = np.array([0]) nprims = np.array([len(self.alphas)]) shell_types = np.array([self.shell_type]) alphas = self.alphas con_coeffs = self.con_coeffs gobasis = GOBasis(centers, shell_map, nprims, shell_types, alphas, con_coeffs) # 2) Get the first diagonal element of the overlap matrix olpdiag = gobasis.compute_overlap()[0, 0] # 3) Normalize the contraction self.con_coeffs /= np.sqrt(olpdiag)
def normalize(self): """Normalize the contraction.""" if self.is_generalized(): raise NotImplementedError( "Only segmented contractions can be normalized.") # Warning! Ugly code ahead to avoid re-implementing the norm of contraction. The # code below (ab)uses the GOBasis machinery to get that result. # 1) Constract a GOBasis object with only this contraction. centers = np.array([[0.0, 0.0, 0.0]]) shell_map = np.array([0]) nprims = np.array([len(self.alphas)]) shell_types = np.array([self.shell_type]) alphas = self.alphas con_coeffs = self.con_coeffs gobasis = GOBasis(centers, shell_map, nprims, shell_types, alphas, con_coeffs) # 2) Get the first diagonal element of the overlap matrix olpdiag = gobasis.compute_overlap()[0, 0] # 3) Normalize the contraction self.con_coeffs /= np.sqrt(olpdiag)
def _read_cp2k_uncontracted_obasis(f): """Read an uncontracted basis set from an open CP2K ATOM output file. Parameters ---------- f : file An open readable file object. Returns ------- obasis : GOBasis The orbital basis read from the file. """ # Load the relevant data from the file basis_desc = [] shell_type = None for line in f: if line.startswith(' *******************'): break elif line[3:13] == 'Exponents:': shell_type = str_to_shell_types(line[1:2], pure=True)[0] words = line.split() if len(words) >= 2: # read the exponent alpha = float(words[-1]) basis_desc.append((shell_type, alpha)) # Convert the basis into HORTON format shell_map = [] shell_types = [] nprims = [] alphas = [] con_coeffs = [] # fill in arrays for shell_type, alpha in basis_desc: correction = _get_cp2k_norm_corrections(abs(shell_type), alpha) shell_map.append(0) shell_types.append(shell_type) nprims.append(1) alphas.append(alpha) con_coeffs.append(1.0 / correction) # Create the basis object centers = np.zeros((1, 3)) 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) obasis = GOBasis(centers, shell_map, nprims, shell_types, alphas, con_coeffs) return obasis
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 load_fchk(filename, lf): '''Load from a formatted checkpoint file. **Arguments:** filename The filename of the Gaussian formatted checkpoint file. lf A LinalgFactory instance. **Returns** a dictionary with: ``title``, ``coordinates``, ``numbers``, ``obasis``, ``exp_alpha``, ``permutation``, ``energy``, ``pseudo_numbers``, ``mulliken_charges``. The dictionary may also contain: ``npa_charges``, ``esp_charges``, ``exp_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) if lf.default_nbasis is not None and lf.default_nbasis != obasis.nbasis: raise TypeError( 'The value of lf.default_nbasis does not match nbasis reported in the fchk file.' ) lf.default_nbasis = obasis.nbasis # 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, 'lf': lf, 'numbers': numbers, 'obasis': obasis, 'permutation': permutation, 'pseudo_numbers': pseudo_numbers, } # C) Load density matrices def load_dm(label): if label in fchk: dm = lf.create_two_index(obasis.nbasis) start = 0 for i in xrange(obasis.nbasis): stop = start + i + 1 dm._array[i, :i + 1] = fchk[label][start:stop] dm._array[: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) exp_alpha = lf.create_expansion(obasis.nbasis, nbasis_indep) exp_alpha.coeffs[:] = fchk['Alpha MO coefficients'].reshape( nbasis_indep, obasis.nbasis).T exp_alpha.energies[:] = fchk['Alpha Orbital Energies'] exp_alpha.occupations[:nalpha] = 1.0 result['exp_alpha'] = exp_alpha if 'Beta Orbital Energies' in fchk: # UHF case exp_beta = lf.create_expansion(obasis.nbasis, nbasis_indep) exp_beta.coeffs[:] = fchk['Beta MO coefficients'].reshape( nbasis_indep, obasis.nbasis).T exp_beta.energies[:] = fchk['Beta Orbital Energies'] exp_beta.occupations[:nbeta] = 1.0 result['exp_beta'] = exp_beta elif fchk['Number of beta electrons'] != fchk['Number of alpha electrons']: # ROHF case exp_beta = lf.create_expansion(obasis.nbasis, nbasis_indep) exp_beta.coeffs[:] = fchk['Alpha MO coefficients'].reshape( nbasis_indep, obasis.nbasis).T exp_beta.energies[:] = fchk['Alpha Orbital Energies'] exp_beta.occupations[:nbeta] = 1.0 result['exp_beta'] = exp_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_atom_cp2k(filename, lf): with open(filename) as f: # Find the element number for line in f: if line.startswith(' Atomic Energy Calculation'): number = int(line[-5:-1]) break # Go to the pseudo basis set for line in f: if line.startswith(' Pseudopotential Basis'): break # TODO: add support for uncontracted and all-electron basis f.next() # empty line line = f.next() # Check for GTO assert line == ' ********************** Contracted Gaussian Type Orbitals **********************\n' # Load the basis used for the PP wavefn basis_desc = [] for line in f: if line.startswith(' *******************'): break elif line[3:12] == 'Functions': shell_type = str_to_shell_types(line[1:2], pure=True)[0] a = [] c = [] basis_desc.append((shell_type, a, c)) else: values = [float(w) for w in line.split()] a.append(values[0]) c.append(values[1:]) # Convert the basis into Horton format shell_map = [] shell_types = [] nprims = [] alphas = [] con_coeffs = [] for shell_type, a, c in basis_desc: # get correction to contraction coefficients. corrections = _get_cp2k_norm_corrections(abs(shell_type), a) c = np.array(c) / corrections.reshape(-1, 1) # fill in arrays for col in c.T: shell_map.append(0) shell_types.append(shell_type) nprims.append(len(col)) alphas.extend(a) con_coeffs.extend(col) # Create the basis object coordinates = np.zeros((1, 3)) 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) obasis = GOBasis(coordinates, shell_map, nprims, shell_types, alphas, con_coeffs) # Search for (un)restricted restricted = None for line in f: if line.startswith(' METHOD |'): if 'Unrestricted' in line: restricted = False break elif 'Restricted' in line: restricted = True break # Search for the core charge (pseudo number) for line in f: if line.startswith(' Core Charge'): pseudo_number = float(line[70:]) assert pseudo_number == int(pseudo_number) pseudo_number = int(pseudo_number) break # 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 = [] empty = 0 while empty < 2: line = f.next() words = line.split() if len(words) == 0: empty += 1 continue empty = 0 s = int(words[0]) l = int(words[2 - restricted]) occ = float(words[3 - restricted]) ener = float(words[4 - restricted]) if restricted or words[1] == 'alpha': oe_alpha.append((l, s, occ, ener)) else: oe_beta.append((l, s, occ, ener)) # Read orbital expansion coefficients line = f.next() assert (line == " Atomic orbital expansion coefficients [Alpha]\n") or \ (line == " Atomic orbital expansion coefficients []\n") coeffs_alpha = _read_coeffs_helper(f, oe_alpha) if not restricted: line = f.next() assert (line == " Atomic orbital expansion coefficients [Beta]\n") coeffs_beta = _read_coeffs_helper(f, oe_beta) # Turn orbital data into a Horton wfn expansion if restricted: norb, nel = _helper_norb(oe_alpha) assert nel % 2 == 0 wfn = RestrictedWFN(lf, obasis.nbasis, norb=norb) exp_alpha = wfn.init_exp('alpha') _helper_exp(exp_alpha, oe_alpha, coeffs_alpha, shell_types, restricted) else: norb_alpha, nalpha = _helper_norb(oe_alpha) norb_beta, nbeta = _helper_norb(oe_beta) assert norb_alpha == norb_beta wfn = UnrestrictedWFN(lf, obasis.nbasis, norb=norb_alpha) exp_alpha = wfn.init_exp('alpha') exp_beta = wfn.init_exp('beta') _helper_exp(exp_alpha, oe_alpha, coeffs_alpha, shell_types, restricted) _helper_exp(exp_beta, oe_beta, coeffs_beta, shell_types, restricted) return { 'obasis': obasis, 'wfn': wfn, 'coordinates': coordinates, 'numbers': np.array([number]), 'extra': { 'energy': energy }, 'pseudo_numbers': np.array([pseudo_number]), }
def apply_to(self, coordinates, numbers): """Construct a GOBasis object for the given molecular geometry **Arguments:** coordinates A (N, 3) float numpy array with Cartesian coordinates of the atoms. numbers A (N,) numpy vector with the atomic numbers. Note that the geometry specified by the arguments may also contain ghost atoms. """ natom, coordinates, numbers = typecheck_geo(coordinates, numbers, need_pseudo_numbers=False) shell_map = [] nprims = [] shell_types = [] alphas = [] con_coeffs = [] def get_basis(i, n): """Look up the basis for a given atom""" basis = self.index_map.get(i) if basis is not None: return basis basis = self.element_map.get(n) if basis is not None: return basis if self.default is None: raise KeyError('Could not find basis for atom %i.' % i) else: return self.default def translate_basis(basis_x, n): """Translate the first argument into a GOBasisAtom instance""" if isinstance(basis_x, basestring): basis_fam = go_basis_families.get(basis_x.lower()) if basis_fam is None: raise ValueError('Unknown basis family: %s' % basis_x) basis_atom = basis_fam.get(n) if basis_atom is None: raise ValueError('The basis family %s does not contain element %i.' % (basis_x, n)) return basis_atom elif isinstance(basis_x, GOBasisFamily): basis_atom = basis_x.get(n) if basis_atom is None: raise ValueError('The basis family %s does not contain element %i.' % (basis_x.name, n)) return basis_atom elif isinstance(basis_x, GOBasisAtom): return basis_x else: raise ValueError('Can not interpret %s as an atomic basis function.' % basis_x) # Loop over the atoms and fill in all the lists for i in xrange(natom): n = numbers[i] basis_x = get_basis(i, n) basis_atom = translate_basis(basis_x, n) basis_atom.extend(i, shell_map, nprims, shell_types, alphas, con_coeffs, self.pure) # Return the Gaussian basis object. return GOBasis(coordinates, shell_map, nprims, shell_types, alphas, con_coeffs)
def project_orbitals_mgs(obasis0, obasis1, orb0, orb1, eps=1e-10): '''Project the orbitals onto a new basis set with the modified Gram-Schmidt algorithm. The orbitals in ``orb0`` (w.r.t. ``obasis0``) are projected onto ``obasis1`` and stored in ``orb1``. Parameters ---------- obasis0 : GOBasis The orbital basis for the original wavefunction expansion. obasis1 : GOBasis The new orbital basis for the projected wavefunction expansion. orb0 : Orbitals The expansion of the original orbitals. orb1 : Orbitals An output argument in which the projected orbitals will be stored. eps : float A threshold for the renormalization in the Gram-Schmidt procedure Notes ----- The projection is based on the Modified Gram-Schmidt (MGS) process. In each iteration of the MGS, a renormalization is carried out. If the norm in this step is smaller than ``eps``, an error is raised. Note that ``orb1`` will be incomplete in several ways. The orbital energies are not copied. Only the occupied orbitals in ``orb0`` are projected. Coefficients of higher orbitals are set to zero. The orbital occupations are simply copied. This should be sufficient to construct an initial guess in a new orbital basis set based on a previous solution. If the number of orbitals in ``orb1`` is too small to store all projected orbitals, an error is raised. ''' # Compute the overlap matrix of the combined orbital basis obasis_both = GOBasis.concatenate(obasis0, obasis1) olp_both = obasis_both.compute_overlap() # Select the blocks of interest from the big overlap matrix olp_21 = olp_both[obasis0.nbasis:, :obasis0.nbasis] olp_22 = olp_both[obasis0.nbasis:, obasis0.nbasis:] # Construct the projector. This minimizes the L2 norm between the new and old # orbitals, which does not account for orthonormality. projector = np.dot(np.linalg.pinv(olp_22), olp_21) # Project occupied orbitals. i1 = 0 for i0 in xrange(orb0.nfn): if orb0.occupations[i0] == 0.0: continue if i1 > orb1.nfn: raise ProjectionError('Not enough functions available in orb1 to store the ' 'projected orbitals.') orb1.coeffs[:,i1] = np.dot(projector, orb0.coeffs[:,i0]) orb1.occupations[i1] = orb0.occupations[i0] i1 += 1 # clear all parts of orb1 that were not touched by the projection loop ntrans = i1 del i1 orb1.coeffs[:,ntrans:] = 0.0 orb1.occupations[ntrans:] = 0.0 orb1.energies[:] = 0.0 # auxiliary function for the MGS algo def dot22(a, b): return np.dot(np.dot(a, olp_22), b) # Apply the MGS algorithm to orthogonalize the orbitals for i1 in xrange(ntrans): orb = orb1.coeffs[:, i1] # Subtract overlap with previous orbitals for j1 in xrange(i1): other = orb1.coeffs[:, j1] orb -= other*dot22(other, orb)/np.sqrt(dot22(other, other)) # Renormalize norm = np.sqrt(dot22(orb, orb)) if norm < eps: raise ProjectionError('The norm of a vector in the MGS algorithm becomes too ' 'small. Orbitals are redundant in new basis.') orb /= norm
def load_atom_cp2k(filename, lf): '''Load data from a CP2K ATOM computation **Arguments:** filename The name of the cp2k out file **Returns** a dictionary with ``obasis``, ``exp_alpha``, ``coordinates``, ``numbers``, ``energy``, ``pseudo_numbers``. The dictionary may also contain: ``exp_beta``. ''' with open(filename) as f: # Find the element number for line in f: if line.startswith(' Atomic Energy Calculation'): number = int(line[-5:-1]) break # Go to the pseudo basis set for line in f: if line.startswith(' Pseudopotential Basis'): break f.next() # empty line line = f.next() # Check for GTO assert line == ' ********************** Contracted Gaussian Type Orbitals **********************\n' # Load the basis used for the PP wavefn basis_desc = [] for line in f: if line.startswith(' *******************'): break elif line[3:12] == 'Functions': shell_type = str_to_shell_types(line[1:2], pure=True)[0] a = [] c = [] basis_desc.append((shell_type, a, c)) else: values = [float(w) for w in line.split()] a.append(values[0]) c.append(values[1:]) # Convert the basis into HORTON format shell_map = [] shell_types = [] nprims = [] alphas = [] con_coeffs = [] for shell_type, a, c in basis_desc: # get correction to contraction coefficients. corrections = _get_cp2k_norm_corrections(abs(shell_type), a) c = np.array(c)/corrections.reshape(-1,1) # fill in arrays for col in c.T: shell_map.append(0) shell_types.append(shell_type) nprims.append(len(col)) alphas.extend(a) con_coeffs.extend(col) # Create the basis object coordinates = np.zeros((1, 3)) 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) obasis = GOBasis(coordinates, shell_map, nprims, shell_types, alphas, con_coeffs) if lf.default_nbasis is not None and lf.default_nbasis != obasis.nbasis: raise TypeError('The value of lf.default_nbasis does not match nbasis reported in the cp2k.out file.') lf.default_nbasis = obasis.nbasis # 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) for line in f: if line.startswith(' Core Charge'): pseudo_number = float(line[70:]) assert pseudo_number == int(pseudo_number) pseudo_number = int(pseudo_number) break # 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 = [] empty = 0 while empty < 2: line = f.next() words = line.split() if len(words) == 0: empty += 1 continue empty = 0 s = int(words[0]) l = int(words[2-restricted]) occ = float(words[3-restricted]) ener = float(words[4-restricted]) if restricted or words[1] == 'alpha': oe_alpha.append((l, s, occ, ener)) else: oe_beta.append((l, s, occ, ener)) # Read orbital expansion coefficients line = f.next() assert (line == " Atomic orbital expansion coefficients [Alpha]\n") or \ (line == " Atomic orbital expansion coefficients []\n") coeffs_alpha = _read_coeffs_helper(f, oe_alpha) if not restricted: line = f.next() assert (line == " Atomic orbital expansion coefficients [Beta]\n") coeffs_beta = _read_coeffs_helper(f, oe_beta) # Turn orbital data into a HORTON orbital expansions if restricted: norb, nel = _helper_norb(oe_alpha) assert nel%2 == 0 exp_alpha = lf.create_expansion(obasis.nbasis, norb) exp_beta = None _helper_exp(exp_alpha, oe_alpha, coeffs_alpha, shell_types, restricted) else: norb_alpha, nalpha = _helper_norb(oe_alpha) norb_beta, nbeta = _helper_norb(oe_beta) assert norb_alpha == norb_beta exp_alpha = lf.create_expansion(obasis.nbasis, norb_alpha) exp_beta = lf.create_expansion(obasis.nbasis, norb_beta) _helper_exp(exp_alpha, oe_alpha, coeffs_alpha, shell_types, restricted) _helper_exp(exp_beta, oe_beta, coeffs_beta, shell_types, restricted) result = { 'obasis': obasis, 'lf': lf, 'exp_alpha': exp_alpha, 'coordinates': coordinates, 'numbers': np.array([number]), 'energy': energy, 'pseudo_numbers': np.array([pseudo_number]), } if exp_beta is not None: result['exp_beta'] = exp_beta return result
def _fix_molden_from_buggy_codes(result, filename): """Detect errors in the data loaded from a molden/mkl/... file and correct. Parameters ---------- result: dict A dictionary with the data loaded in the ``load_molden`` function. This function can recognize erroneous files created by PSI4, ORCA and Turbomole. The values in `results` for the `obasis` and `signs` keys will be updated accordingly. """ obasis = result['obasis'] permutation = result.get('permutation', None) if _is_normalized_properly(obasis, permutation, result['orb_alpha'], result.get('orb_beta')): # The file is good. No need to change data. return if log.do_medium: log('Detected incorrect normalization of orbitals loaded from a file.') # --- ORCA if log.do_medium: log('Trying to fix it as if it was a file generated by ORCA.') orca_signs = _get_orca_signs(obasis) orca_con_coeffs = _get_fixed_con_coeffs(obasis, 'orca') if orca_con_coeffs is not None: # Only try if some changes were made to the contraction coefficients. orca_obasis = GOBasis(obasis.centers, obasis.shell_map, obasis.nprims, obasis.shell_types, obasis.alphas, orca_con_coeffs) if _is_normalized_properly(orca_obasis, permutation, result['orb_alpha'], result.get('orb_beta'), orca_signs): if log.do_medium: log('Detected typical ORCA errors in file. Fixing them...') result['obasis'] = orca_obasis result['signs'] = orca_signs return # --- PSI4 if log.do_medium: log('Trying to fix it as if it was a file generated by PSI4 (pre 1.0).' ) psi4_con_coeffs = _get_fixed_con_coeffs(obasis, 'psi4') if psi4_con_coeffs is not None: # Only try if some changes were made to the contraction coefficients. psi4_obasis = GOBasis(obasis.centers, obasis.shell_map, obasis.nprims, obasis.shell_types, obasis.alphas, psi4_con_coeffs) if _is_normalized_properly(psi4_obasis, permutation, result['orb_alpha'], result.get('orb_beta')): if log.do_medium: log('Detected typical PSI4 errors in file. Fixing them...') result['obasis'] = psi4_obasis return # -- Turbomole if log.do_medium: log('Trying to fix it as if it was a file generated by Turbomole.') tb_con_coeffs = _get_fixed_con_coeffs(obasis, 'turbomole') if tb_con_coeffs is not None: # Only try if some changes were made to the contraction coefficients. tb_obasis = GOBasis(obasis.centers, obasis.shell_map, obasis.nprims, obasis.shell_types, obasis.alphas, tb_con_coeffs) if _is_normalized_properly(tb_obasis, permutation, result['orb_alpha'], result.get('orb_beta')): if log.do_medium: log('Detected typical Turbomole errors in file. Fixing them...' ) result['obasis'] = tb_obasis return # --- Renormalized contractions if log.do_medium: log('Last resort: trying by renormalizing all contractions') normed_con_coeffs = _normalized_contractions(obasis) normed_obasis = GOBasis(obasis.centers, obasis.shell_map, obasis.nprims, obasis.shell_types, obasis.alphas, normed_con_coeffs) if _is_normalized_properly(normed_obasis, permutation, result['orb_alpha'], result.get('orb_beta')): if log.do_medium: log('Detected unnormalized contractions in file. Fixing them...') result['obasis'] = normed_obasis return raise IOError(('Could not correct the data read from %s. The molden or ' 'mkl file you are trying to load contains errors. Please ' 'report this problem to [email protected], so he ' 'can fix it.') % filename)
def project_orbitals_mgs(obasis0, obasis1, exp0, exp1, eps=1e-10): '''Project the orbitals in ``exp0`` (wrt ``obasis0``) on ``obasis1`` and store in ``exp1`` with the modified Gram-Schmidt algorithm. **Arguments:** obasis0 The orbital basis for the original wavefunction expansion. obasis1 The new orbital basis for the projected wavefunction expansion. exp0 The expansion of the original orbitals. exp1 (output) An output argument in which the projected orbitals will be stored. **Optional arguments:** eps A threshold for the renormalization in the Gram-Schmidt procedure The projection is based on the Modified Gram-Schmidt (MGS) process. In each iteration of the MGS, a renormalization is carried out. If the norm in this step is smaller than ``eps``, an error is raised. Note that ``exp1`` will be incomplete in several ways. The orbital energies are not copied. Only the occupied orbitals in ``exp0`` are projected. Coefficients of higher orbitals are set to zero. The orbital occupations are simply copied. This should be sufficient to construct an initial guess in a new orbital basis set based on a previous solution. If the number of orbitals in ``exp1`` is too small to store all projected orbitals, an error is raised. ''' # Compute the overlap matrix of the combined orbital basis obasis_both = GOBasis.concatenate(obasis0, obasis1) lf = DenseLinalgFactory(obasis_both.nbasis) olp_both = obasis_both.compute_overlap(lf) # Select the blocks of interest from the big overlap matrix olp_21 = olp_both._array[obasis0.nbasis:, :obasis0.nbasis] olp_22 = olp_both._array[obasis0.nbasis:, obasis0.nbasis:] # construct the projector projector = np.dot(np.linalg.pinv(olp_22), olp_21) # project occupied orbitals i1 = 0 for i0 in xrange(exp0.nfn): if exp0.occupations[i0] == 0.0: continue if i1 > exp1.nfn: raise ProjectionError('Not enough functions available in exp1 to store the projected orbitals.') exp1.coeffs[:,i1] = np.dot(projector, exp0.coeffs[:,i0]) exp1.occupations[i1] = exp0.occupations[i0] i1 += 1 # clear all parts of exp1 that were not touched by the projection loop ntrans = i1 del i1 exp1.coeffs[:,ntrans:] = 0.0 exp1.occupations[ntrans:] = 0.0 exp1.energies[:] = 0.0 # auxiliary function for the MGS algo def dot22(a, b): return np.dot(np.dot(a, olp_22), b) # Apply the MGS algorithm to orthogonalize the orbitals for i1 in xrange(ntrans): orb = exp1.coeffs[:,i1] # Subtract overlap with previous orbitals for j1 in xrange(i1): other = exp1.coeffs[:,j1] orb -= other*dot22(other, orb)/np.sqrt(dot22(orb, orb)) # Renormalize norm = np.sqrt(dot22(orb, orb)) if norm < eps: raise ProjectionError('The norm of a vector in the MGS algorithm becomes too small. Orbitals are redundant in new basis.') orb /= norm
def project_orbitals_mgs(obasis0, obasis1, exp0, exp1, eps=1e-10): '''Project the orbitals onto a new basis set with the modified Gram-Schmidt algorithm. The orbitals in ``exp0`` (w.r.t. ``obasis0``) are projected onto ``obasis1`` and stored in ``exp1``. Parameters ---------- obasis0 : GOBasis The orbital basis for the original wavefunction expansion. obasis1 : GOBasis The new orbital basis for the projected wavefunction expansion. exp0 : DenseExpansion The expansion of the original orbitals. exp1 : DenseExpansion An output argument in which the projected orbitals will be stored. eps : float A threshold for the renormalization in the Gram-Schmidt procedure Notes ----- The projection is based on the Modified Gram-Schmidt (MGS) process. In each iteration of the MGS, a renormalization is carried out. If the norm in this step is smaller than ``eps``, an error is raised. Note that ``exp1`` will be incomplete in several ways. The orbital energies are not copied. Only the occupied orbitals in ``exp0`` are projected. Coefficients of higher orbitals are set to zero. The orbital occupations are simply copied. This should be sufficient to construct an initial guess in a new orbital basis set based on a previous solution. If the number of orbitals in ``exp1`` is too small to store all projected orbitals, an error is raised. ''' # Compute the overlap matrix of the combined orbital basis obasis_both = GOBasis.concatenate(obasis0, obasis1) lf = DenseLinalgFactory(obasis_both.nbasis) olp_both = obasis_both.compute_overlap(lf) # Select the blocks of interest from the big overlap matrix olp_21 = olp_both._array[obasis0.nbasis:, :obasis0.nbasis] olp_22 = olp_both._array[obasis0.nbasis:, obasis0.nbasis:] # Construct the projector. This minimizes the L2 norm between the new and old # orbitals, which does not account for orthonormality. projector = np.dot(np.linalg.pinv(olp_22), olp_21) # Project occupied orbitals. i1 = 0 for i0 in xrange(exp0.nfn): if exp0.occupations[i0] == 0.0: continue if i1 > exp1.nfn: raise ProjectionError( 'Not enough functions available in exp1 to store the ' 'projected orbitals.') exp1.coeffs[:, i1] = np.dot(projector, exp0.coeffs[:, i0]) exp1.occupations[i1] = exp0.occupations[i0] i1 += 1 # clear all parts of exp1 that were not touched by the projection loop ntrans = i1 del i1 exp1.coeffs[:, ntrans:] = 0.0 exp1.occupations[ntrans:] = 0.0 exp1.energies[:] = 0.0 # auxiliary function for the MGS algo def dot22(a, b): return np.dot(np.dot(a, olp_22), b) # Apply the MGS algorithm to orthogonalize the orbitals for i1 in xrange(ntrans): orb = exp1.coeffs[:, i1] # Subtract overlap with previous orbitals for j1 in xrange(i1): other = exp1.coeffs[:, j1] orb -= other * dot22(other, orb) / np.sqrt(dot22(other, other)) # Renormalize norm = np.sqrt(dot22(orb, orb)) if norm < eps: raise ProjectionError( 'The norm of a vector in the MGS algorithm becomes too ' 'small. Orbitals are redundant in new basis.') orb /= norm