def get_dot_product(self, i, j): key = frozenset([i, j]) try: return self.cached_dot_products[key] except KeyError: if self.storage_policy == StoragePolicy.InCore: Ri = self.stored_vectors[i][0] Rj = self.stored_vectors[j][0] dot_product = sum(Rix.vector_dot(Rjx) for Rix, Rjx in zip(Ri, Rj)) elif self.storage_policy == StoragePolicy.OnDisk: dot_product = 0 psio = core.IO.shared_object() for x, entry_dims in enumerate(self.R_template): if len(entry_dims) == 2: Rix = core.Matrix(self.get_name("R", i, x), *entry_dims) Rjx = core.Matrix(self.get_name("R", j, x), *entry_dims) Rix.load(psio, psif.PSIF_LIBDIIS, core.SaveType.SubBlocks) Rjx.load(psio, psif.PSIF_LIBDIIS, core.SaveType.SubBlocks) elif len(entry_dims) == 1: Rix = core.Vector(self.get_name("R", i, x), *entry_dims) Rjx = core.Vector(self.get_name("R", j, x), *entry_dims) Rix.load(psio, psif.PSIF_LIBDIIS) Rjx.load(psio, psif.PSIF_LIBDIIS) else: raise Exception("R_template may only have 1 or 2 dimensions. This is a bug: contact developers.") dot_product += Rix.vector_dot(Rjx) else: raise Exception(f"StoragePolicy {self.storage_policy} not recognized. This is a bug: contact developers.") self.cached_dot_products[key] = dot_product return dot_product
def orthogonalize(C, S): nbf, nocc = C.shape eigenvectors = core.Matrix(nocc, nocc) eigvals = core.Vector(nocc) sqrt_eigvals = core.Vector(nocc) CTSC = core.Matrix.triplet(C, S, C, True, False, False) CTSC.diagonalize(eigenvectors, eigvals, core.DiagonalizeOrder.Ascending) orthonormal = core.Matrix.doublet(C, eigenvectors, False, False) sqrt_eigvals.np[:] = np.sqrt(eigvals.np) orthonormal.np[:, :] /= sqrt_eigvals.np[np.newaxis, :] return orthonormal
def _np_read(self, filename, prefix=""): if isinstance(filename, np.lib.npyio.NpzFile): data = filename elif isinstance(filename, (str, unicode)): if not filename.endswith('.npz'): filename = filename + '.npz' data = np.load(filename) else: raise Exception("Filename not understood: %s" % filename) ret_data = [] if ((prefix + "Irreps") not in data.keys()) or ((prefix + "Name") not in data.keys()): raise KeyError("File %s does not appear to be a numpyz save" % filename) for h in range(data[prefix + "Irreps"]): ret_data.append(data[prefix + "IrrepData" + str(h)]) arr_type = self.__mro__[0] if arr_type == core.Matrix: dim1 = core.Dimension.from_list(data[prefix + "Dim1"]) dim2 = core.Dimension.from_list(data[prefix + "Dim2"]) ret = core.Matrix(str(data[prefix + "Name"]), dim1, dim2) elif arr_type == core.Vector: dim1 = core.Dimension.from_list(data[prefix + "Dim"]) ret = core.Vector(str(data[prefix + "Name"]), dim1) for h in range(data[prefix + "Irreps"]): ret.nph[h][:] = ret_data[h] return ret
def load_quantity(self, name, entry_num, item_num, force_new=True): """ Load quantity from wherever it's stored, constructing a new object if needed. """ template_object = self.template[name][item_num] if isinstance(template_object, float) or self.storage_policy == StoragePolicy.InCore: quantity = self.stored_vectors[entry_num][name][item_num] try: quantity = quantity.clone() except AttributeError: # The quantity must have been a float. No need to clone. pass elif self.storage_policy == StoragePolicy.OnDisk: entry_dims = template_object full_name = self.get_name(name, entry_num, item_num) psio = core.IO.shared_object() if len(entry_dims) == 2: quantity = core.Matrix(full_name, *entry_dims) quantity.load(psio, psif.PSIF_LIBDIIS, core.SaveType.SubBlocks) elif len(entry_dims) == 1: quantity = core.Vector(full_name, *entry_dims) quantity.load(psio, psif.PSIF_LIBDIIS) else: raise Exception( f"StoragePolicy {self.storage_policy} not recognized. This is a bug: contact developers." ) return quantity
def load_quantity(self, name, entry_num, item_num, force_new = True): """ Load quantity from wherever it's stored, constructing a new object if needed. """ template_object = self.template[name][item_num] if isinstance(template_object, float) or self.storage_policy == StoragePolicy.InCore: quantity = self.stored_vectors[entry_num][name][item_num] try: quantity = quantity.clone() except AttributeError: # The quantity must have been a float. No need to clone. pass elif self.storage_policy == StoragePolicy.OnDisk: full_name = self.get_name(name, entry_num, item_num) psio = core.IO.shared_object() if hasattr(template_object, "__len__"): # Looks like we have dimensions. if len(template_object) == 2: quantity = core.Matrix(full_name, *template_object) quantity.load(psio, psif.PSIF_LIBDIIS, core.SaveType.SubBlocks) elif len(template_object) == 1: quantity = core.Vector(full_name, *template_object) quantity.load(psio, psif.PSIF_LIBDIIS) elif which_import("ambit", return_bool=True): import ambit if template_object == ambit.BlockedTensor: quantity = ambit.BlockedTensor.load_and_build(f"libdiis.{full_name}") else: raise Exception(f"StoragePolicy {self.storage_policy} not recognized. This is a bug: contact developers.") return quantity
def _compute_fxc(PQrho, half_Saux, halfp_Saux, rho_thresh=1.e-8): """ Computes the gridless (P|fxc|Q) ALDA tensor. """ naux = PQrho.shape[0] # Level it out PQrho_lvl = core.Matrix.triplet(half_Saux, PQrho, half_Saux, False, False, False) # Rotate into a diagonal basis rho = core.Vector("rho eigenvalues", naux) U = core.Matrix("rho eigenvectors", naux, naux) PQrho_lvl.diagonalize(U, rho, core.DiagonalizeOrder.Ascending) # "Gridless DFT" mask = rho.np < rho_thresh # Values too small cause singularities rho.np[mask] = rho_thresh dft_size = rho.shape[0] inp = {"RHO_A": rho} out = { "V": core.Vector(dft_size), "V_RHO_A": core.Vector(dft_size), "V_RHO_A_RHO_A": core.Vector(dft_size) } func_x = core.LibXCFunctional('XC_LDA_X', True) func_x.compute_functional(inp, out, dft_size, 2) func_c = core.LibXCFunctional('XC_LDA_C_VWN', True) func_c.compute_functional(inp, out, dft_size, 2) out["V_RHO_A_RHO_A"].np[mask] = 0 # Rotate back Ul = U.clone() Ul.np[:] *= out["V_RHO_A_RHO_A"].np tmp = core.Matrix.doublet(Ul, U, False, True) # Undo the leveling return core.Matrix.triplet(halfp_Saux, tmp, halfp_Saux, False, False, False)
def extrapolate(self, *args): dim = len(self.stored_vectors) + 1 B = np.zeros((dim, dim)) for i in range(len(self.stored_vectors)): for j in range(len(self.stored_vectors)): B[i, j] = self.get_dot_product(i, j) B[-1, :-1] = B[:-1, -1] = -1 rhs = np.zeros((dim)) rhs[-1] = -1 # Trick to improve numerical conditioning. # Instead of solving B c = r, we solve D B D^-1 D c = D r, using # D r = r. D is the diagonals ^ -1/2 matrix. # This improves the conditioning of the problem. diagonals = B.diagonal().copy() diagonals[-1] = 1 if np.all(diagonals > 0): diagonals = diagonals ** (- 0.5) B = np.einsum("i,ij,j -> ij", diagonals, B, diagonals) coeffs = np.linalg.lstsq(B, rhs, rcond=None)[0][:-1] * diagonals[:-1] else: coeffs = np.linalg.lstsq(B, rhs, rcond=None)[0][:-1] for j, Tj in enumerate(args): Tj.zero() if self.storage_policy == StoragePolicy.InCore: for ci, (_, Ti) in zip(coeffs, self.stored_vectors): axpy(Tj, ci, Ti[j]) elif self.storage_policy == StoragePolicy.OnDisk: for i, ci in enumerate(coeffs): psio = core.IO.shared_object() if isinstance(Tj, core.Vector): Tij = core.Vector(self.get_name("T", i, j), *self.T_template[j]) Tij.load(psio, psif.PSIF_LIBDIIS) elif isinstance(Tj, (core.Matrix, core.dpdfile2, core.dpdbuf4)): Tij = core.Matrix(self.get_name("T", i, j), *self.T_template[j]) Tij.load(psio, psif.PSIF_LIBDIIS, core.SaveType.SubBlocks) else: raise TypeError("Unrecognized object type for DIIS.") axpy(Tj, ci, Tij) else: raise Exception(f"StoragePolicy {self.storage_policy} not recognized. This is a bug: contact developers.") return True
def test_ccl_functional(functional, ccl_functional): check = True if (not os.path.exists('data_pt_%s.html' % (ccl_functional))): os.system('wget ftp://ftp.dl.ac.uk/qcg/dft_library/data_pt_%s.html' % ccl_functional) fh = open('data_pt_%s.html' % (ccl_functional)) lines = fh.readlines() fh.close() points = [] point = {} rho_line = re.compile( r'^\s*rhoa=\s*(-?\d+\.\d+E[+-]\d+)\s*rhob=\s*(-?\d+\.\d+E[+-]\d+)\s*sigmaaa=\s*(-?\d+\.\d+E[+-]\d+)\s*sigmaab=\s*(-?\d+\.\d+E[+-]\d+)\s*sigmabb=\s*(-?\d+\.\d+E[+-]\d+)\s*' ) val_line = re.compile(r'^\s*(\w*)\s*=\s*(-?\d+\.\d+E[+-]\d+)') aliases = { 'zk': 'v', 'vrhoa': 'v_rho_a', 'vrhob': 'v_rho_b', 'vsigmaaa': 'v_gamma_aa', 'vsigmaab': 'v_gamma_ab', 'vsigmabb': 'v_gamma_bb', 'v2rhoa2': 'v_rho_a_rho_a', 'v2rhoab': 'v_rho_a_rho_b', 'v2rhob2': 'v_rho_b_rho_b', 'v2rhoasigmaaa': 'v_rho_a_gamma_aa', 'v2rhoasigmaab': 'v_rho_a_gamma_ab', 'v2rhoasigmabb': 'v_rho_a_gamma_bb', 'v2rhobsigmaaa': 'v_rho_b_gamma_aa', 'v2rhobsigmaab': 'v_rho_b_gamma_ab', 'v2rhobsigmabb': 'v_rho_b_gamma_bb', 'v2sigmaaa2': 'v_gamma_aa_gamma_aa', 'v2sigmaaaab': 'v_gamma_aa_gamma_ab', 'v2sigmaaabb': 'v_gamma_aa_gamma_bb', 'v2sigmaab2': 'v_gamma_ab_gamma_ab', 'v2sigmaabbb': 'v_gamma_ab_gamma_bb', 'v2sigmabb2': 'v_gamma_bb_gamma_bb', } for line in lines: mobj = re.match(rho_line, line) if (mobj): if len(point): points.append(point) point = {} point['rho_a'] = float(mobj.group(1)) point['rho_b'] = float(mobj.group(2)) point['gamma_aa'] = float(mobj.group(3)) point['gamma_ab'] = float(mobj.group(4)) point['gamma_bb'] = float(mobj.group(5)) continue mobj = re.match(val_line, line) if (mobj): point[aliases[mobj.group(1)]] = float(mobj.group(2)) points.append(point) N = len(points) rho_a = core.Vector(N) rho_b = core.Vector(N) gamma_aa = core.Vector(N) gamma_ab = core.Vector(N) gamma_bb = core.Vector(N) tau_a = core.Vector(N) tau_b = core.Vector(N) index = 0 for point in points: rho_a[index] = point['rho_a'] rho_b[index] = point['rho_b'] gamma_aa[index] = point['gamma_aa'] gamma_ab[index] = point['gamma_ab'] gamma_bb[index] = point['gamma_bb'] index = index + 1 super = build_superfunctional(functional, N, 1) super.test_functional(rho_a, rho_b, gamma_aa, gamma_ab, gamma_bb, tau_a, tau_b) v = super.value('V') v_rho_a = super.value('V_RHO_A') v_rho_b = super.value('V_RHO_B') v_gamma_aa = super.value('V_GAMMA_AA') v_gamma_ab = super.value('V_GAMMA_AB') v_gamma_bb = super.value('V_GAMMA_BB') if not v_gamma_aa: v_gamma_aa = tau_a v_gamma_ab = tau_a v_gamma_bb = tau_a tasks = [ 'v', 'v_rho_a', 'v_rho_b', 'v_gamma_aa', 'v_gamma_ab', 'v_gamma_bb' ] mapping = { 'v': v, 'v_rho_a': v_rho_a, 'v_rho_b': v_rho_b, 'v_gamma_aa': v_gamma_aa, 'v_gamma_ab': v_gamma_ab, 'v_gamma_bb': v_gamma_bb, } super.print_detail(3) index = 0 for point in points: core.print_out( 'rho_a= %11.3E, rho_b= %11.3E, gamma_aa= %11.3E, gamma_ab= %11.3E, gamma_bb= %11.3E\n' % (rho_a[index], rho_b[index], gamma_aa[index], gamma_ab[index], gamma_bb[index])) for task in tasks: v_ref = point[task] v_obs = mapping[task][index] delta = v_obs - v_ref if (v_ref == 0.0): epsilon = 0.0 else: epsilon = abs(delta / v_ref) if (epsilon < 1.0E-11): passed = 'PASSED' else: passed = 'FAILED' check = False core.print_out('\t%-15s %24.16E %24.16E %24.16E %24.16E %6s\n' % (task, v_ref, v_obs, delta, epsilon, passed)) index = index + 1 core.print_out('\n') return check
def _write_molden( self: core.Wavefunction, filename: Optional[str] = None, do_virtual: Optional[bool] = None, use_natural: bool = False, ): """Writes wavefunction information in *wfn* to *filename* in molden format. Will write natural orbitals from *density* (MO basis) if supplied. Warning! most post-SCF wavefunctions do not build the density as this is often much more costly than the energy. In addition, the wavefunction density attributes (Da and Db) return the SO density and must be transformed to the MO basis to use with this function. .. versionadded:: 0.5 *wfn* parameter passed explicitly :returns: None :type filename: :param filename: Destination file name for MOLDEN file. If unspecified (None), a file name will be generated from the molecule name. :type do_virtual: :param do_virtual: Do write all the MOs to the MOLDEN file (True) or discard the unoccupied MOs (False). Not valid for NO's. If unspecified (None), value taken from :term:`MOLDEN_WITH_VIRTUAL <MOLDEN_WITH_VIRTUAL (GLOBALS)>`. :type use_natural: :param use_natural: Write natural orbitals determined from density on wavefunction. :examples: 1. Molden file with the Kohn-Sham orbitals of a DFT calculation. >>> E, wfn = energy('b3lyp', return_wfn=True) >>> wfn.molden('mycalc.molden') 2. Molden file with the natural orbitals of a CCSD computation. For correlated methods, an energy call will not compute the density. "properties" or "gradient" must be called. >>> E, wfn = properties('ccsd', return_wfn=True) >>> wfn.molden('ccsd_no.molden', use_natural=True) 3. To supply a custom density matrix, manually set the Da and Db of the wavefunction. This is used, for example, to write natural orbitals coming from a root computed by a ``CIWavefunction`` computation, e.g., ``detci``, ``fci``, ``casscf``. The first two arguments of :py:meth:`~psi4.core.CIWavefunction.get_opdm` can be set to ``n, n`` where n => 0 selects the root to write out, provided these roots were computed, see :term:`NUM_ROOTS <NUM_ROOTS (DETCI)>`. The third argument controls the spin (``"A"``, ``"B"`` or ``"SUM"``) and the final boolean option determines whether inactive orbitals are included. >>> E, wfn = energy('detci', return_wfn=True) >>> wfn.Da() = wfn.get_opdm(0, 0, "A", True) >>> wfn.Db() = wfn.get_opdm(0, 0, "B", True) >>> molden(wfn, 'no_root1.molden', use_natural=True) """ if filename is None: filename = core.get_writer_file_prefix( self.molecule().name()) + ".molden" if do_virtual is None: do_virtual = bool(core.get_option("SCF", "MOLDEN_WITH_VIRTUAL")) basisset = self.basisset() mol = self.molecule() # Header and geometry (Atom, Atom #, Z, x, y, z) mol_string = '[Molden Format]\n[Atoms] (AU)\n' for atom in range(mol.natom()): mol_string += f"{mol.symbol(atom):2s} {atom+1:2d} {int(mol.Z(atom)):3d} {mol.x(atom):20.10f} {mol.y(atom):20.10f} {mol.z(atom):20.10f}\n" # Dump basis set mol_string += '[GTO]\n' for atom in range(mol.natom()): mol_string += f" {atom+1:d} 0\n" for rel_shell_idx in range(basisset.nshell_on_center(atom)): abs_shell_idx = basisset.shell_on_center(atom, rel_shell_idx) shell = basisset.shell(abs_shell_idx) mol_string += f" {shell.amchar:s}{shell.nprimitive:5d} 1.00\n" for prim in range(shell.nprimitive): mol_string += f"{shell.exp(prim):20.10f} {shell.original_coef(prim):20.10f}\n" mol_string += '\n' # if use_natural: # Alphas nmopi = self.nmopi() #MO_Da = core.Matrix("MO Alpha Density Matrix", nmopi, nmopi) #MO_Da.transform(self.Da(), self.Ca().transpose()) MO_Da = self.Da_subset("MO") #MO_Da.transform(self.Da(), self.Ca()) NO_Ra = core.Matrix("NO Alpha Rotation Matrix", nmopi, nmopi) occupation_a = core.Vector(nmopi) MO_Da.diagonalize(NO_Ra, occupation_a, core.DiagonalizeOrder.Descending) Ca = core.doublet(self.Ca(), NO_Ra, False, False) epsilon_a = occupation_a # Betas #MO_Db = core.Matrix("MO Beta Density Matrix", nmopi, nmopi) #MO_Db.transform(self.Db(), self.Cb().transpose()) MO_Db = self.Db_subset("MO") NO_Rb = core.Matrix("NO Beta Rotation Matrix", nmopi, nmopi) occupation_b = core.Vector(nmopi) MO_Db.diagonalize(NO_Rb, occupation_b, core.DiagonalizeOrder.Descending) Cb = core.doublet(self.Cb(), NO_Rb, False, False) epsilon_b = occupation_b else: Ca = self.Ca() Cb = self.Cb() occupation_a = self.occupation_a() occupation_b = self.occupation_b() epsilon_a = self.epsilon_a() epsilon_b = self.epsilon_b() # Convert C matrices to AO MO basis. Ca_subset costs information about which symmetry an orbital originally had, which is why we can't use it. aotoso = self.aotoso() Ca_ao_mo = core.doublet(aotoso, Ca, False, False).nph Cb_ao_mo = core.doublet(aotoso, Cb, False, False).nph ao_overlap = self.mintshelper().ao_overlap().np # Convert from Psi4 internal normalization to the unit normalization expected by Molden ao_normalizer = ao_overlap.diagonal()**(-1 / 2) Ca_ao_mo = core.Matrix.from_array([(i.T / ao_normalizer).T for i in Ca_ao_mo]) Cb_ao_mo = core.Matrix.from_array([(i.T / ao_normalizer).T for i in Cb_ao_mo]) # Reorder AO x MO matrix to fit Molden conventions ''' Reordering expected by Molden P: x, y, z 5D: D 0, D+1, D-1, D+2, D-2 6D: xx, yy, zz, xy, xz, yz 7F: F 0, F+1, F-1, F+2, F-2, F+3, F-3 10F: xxx, yyy, zzz, xyy, xxy, xxz, xzz, yzz, yyz, xyz 9G: G 0, G+1, G-1, G+2, G-2, G+3, G-3, G+4, G-4 15G: xxxx, yyyy, zzzz, xxxy, xxxz, yyyz, zzzx, zzzy, xxyy, xxzz, yyzz, xxyz, yyxz, zzxy Molden does not handle angular momenta higher than G ''' molden_cartesian_order = [ [2, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], # p [0, 3, 4, 1, 5, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0], # d [0, 4, 5, 3, 9, 6, 1, 8, 7, 2, 0, 0, 0, 0, 0], # f [0, 3, 4, 9, 12, 10, 5, 13, 14, 7, 1, 6, 11, 8, 2] # g ] nirrep = self.nirrep() count = 0 # Keeps track of count for reordering temp_a = Ca_ao_mo.clone() # Placeholders for original AO x MO matrices temp_b = Cb_ao_mo.clone() for i in range(basisset.nshell()): am = basisset.shell(i).am if (am == 1 and basisset.has_puream()) or ( am > 1 and am < 5 and basisset.shell(i).is_cartesian()): for j in range(basisset.shell(i).nfunction): for h in range(nirrep): for k in range(Ca_ao_mo.coldim()[h]): Ca_ao_mo.set(h, count + molden_cartesian_order[am - 1][j], k, temp_a.get(h, count + j, k)) Cb_ao_mo.set(h, count + molden_cartesian_order[am - 1][j], k, temp_b.get(h, count + j, k)) count += basisset.shell(i).nfunction # Dump MO information if basisset.has_puream(): # For historical reasons, D and F can go on the same line, but setting D without F implicitly sets F. G must be on its own. mol_string += '[5D7F]\n[9G]\n\n' ct = mol.point_group().char_table() mol_string += '[MO]\n' mo_dim = self.nmopi() if do_virtual else (self.doccpi() + self.soccpi()) # Alphas. If Alphas and Betas are the same, then only Alphas with double occupation will be written (see line marked "***") mos = [] for h in range(nirrep): for n in range(mo_dim[h]): mos.append((epsilon_a.get(h, n), (h, n))) # Sort mos based on energy def mosSort(element): return element[0] mos.sort(key=mosSort) for i in range(len(mos)): h, n = mos[i][1] mol_string += f" Sym= {ct.gamma(h).symbol():s}\n Ene= {epsilon_a.get(h, n):24.10e}\n Spin= Alpha\n" if self.same_a_b_orbs() and self.epsilon_a() == self.epsilon_b( ) and self.same_a_b_dens(): mol_string += f" Occup= {occupation_a.get(h, n) + occupation_b.get(h, n):24.10e}\n" else: mol_string += f" Occup= {occupation_a.get(h, n):24.10e}\n" for so in range(self.nso()): mol_string += f"{so+1:3d} {Ca_ao_mo.get(h, so, n):24.10e}\n" # Betas mos = [] if not self.same_a_b_orbs( ) or self.epsilon_a() != self.epsilon_b() or not self.same_a_b_dens(): for h in range(nirrep): for n in range(mo_dim[h]): mos.append((self.epsilon_b().get(h, n), (h, n))) mos.sort(key=mosSort) for i in range(len(mos)): h, n = mos[i][1] mol_string += f" Sym= {ct.gamma(h).symbol():s}\n Ene= {epsilon_b.get(h, n):24.10e}\n Spin= Beta\n " \ f"Occup= {occupation_b.get(h, n):24.10e}\n" for so in range(self.nso()): mol_string += f"{so+1:3d} {Cb_ao_mo.get(h, so, n):24.10e}\n" # Write Molden string to file with open(filename, 'w') as fn: fn.write(mol_string)