def get_orbsym(mol, mo_coeff, s=None, check=False): if hasattr(mo_coeff, 'orbsym'): orbsym = mo_coeff.orbsym else: orbsym = (hf_symm.get_orbsym(mol, mo_coeff[0], s, check), hf_symm.get_orbsym(mol, mo_coeff[1], s, check)) return numpy.asarray(orbsym)
def get_orbsym(mol, mo_coeff, s=None, check=False): if getattr(mo_coeff, 'orbsym', None) is not None: orbsym = numpy.asarray(mo_coeff.orbsym) else: orbsym = (hf_symm.get_orbsym(mol, mo_coeff[0], s, check), hf_symm.get_orbsym(mol, mo_coeff[1], s, check)) return orbsym
def get_orbsym(mol, mo_coeff, s=None, check=False): if getattr(mo_coeff, 'orbsym', None) is not None: orbsym = numpy.asarray(mo_coeff.orbsym) else: orbsym = (hf_symm.get_orbsym(mol, mo_coeff[0], s, check), hf_symm.get_orbsym(mol, mo_coeff[1], s, check)) return orbsym
def init_guess(self, mf, nstates=None, wfnsym=None): if nstates is None: nstates = self.nstates if wfnsym is None: wfnsym = self.wfnsym mo_energy = mf.mo_energy mo_occ = mf.mo_occ occidx = numpy.where(mo_occ == 2)[0] viridx = numpy.where(mo_occ == 0)[0] e_ia = mo_energy[viridx] - mo_energy[occidx, None] if wfnsym is not None and mf.mol.symmetry: if isinstance(wfnsym, str): wfnsym = symm.irrep_name2id(mf.mol.groupname, wfnsym) wfnsym = wfnsym % 10 # convert to D2h subgroup orbsym = hf_symm.get_orbsym(mf.mol, mf.mo_coeff) orbsym_in_d2h = numpy.asarray(orbsym) % 10 # convert to D2h irreps e_ia[(orbsym_in_d2h[occidx, None] ^ orbsym_in_d2h[viridx]) != wfnsym] = 1e99 nov = e_ia.size nroot = min(nstates, nov) x0 = numpy.zeros((nroot, nov)) idx = numpy.argsort(e_ia.ravel()) for i in range(nroot): x0[i, idx[i]] = 1 # Koopmans' excitations return x0
def init_guess(self, mf, nstates=None, wfnsym=None): if nstates is None: nstates = self.nstates if wfnsym is None: wfnsym = self.wfnsym mo_energy = mf.mo_energy mo_occ = mf.mo_occ occidx = numpy.where(mo_occ == 2)[0] viridx = numpy.where(mo_occ == 0)[0] e_ia = mo_energy[viridx] - mo_energy[occidx, None] e_ia_max = e_ia.max() if wfnsym is not None and mf.mol.symmetry: if isinstance(wfnsym, str): wfnsym = symm.irrep_name2id(mf.mol.groupname, wfnsym) wfnsym = wfnsym % 10 # convert to D2h subgroup orbsym = hf_symm.get_orbsym(mf.mol, mf.mo_coeff) orbsym_in_d2h = numpy.asarray(orbsym) % 10 # convert to D2h irreps e_ia[(orbsym_in_d2h[occidx, None] ^ orbsym_in_d2h[viridx]) != wfnsym] = 1e99 nov = e_ia.size nstates = min(nstates, nov) e_ia = e_ia.ravel() e_threshold = min(e_ia_max, e_ia[numpy.argsort(e_ia)[nstates - 1]]) # Handle degeneracy, include all degenerated states in initial guess e_threshold += 1e-6 idx = numpy.where(e_ia <= e_threshold)[0] x0 = numpy.zeros((idx.size, nov)) for i, j in enumerate(idx): x0[i, j] = 1 # Koopmans' excitations return x0
def gen_g_hop_rhf(mf, mo_coeff, mo_occ, fock_ao=None, h1e=None, with_symmetry=True): mol = mf.mol occidx = numpy.where(mo_occ==2)[0] viridx = numpy.where(mo_occ==0)[0] nocc = len(occidx) nvir = len(viridx) orbo = mo_coeff[:,occidx] orbv = mo_coeff[:,viridx] if with_symmetry and mol.symmetry: orbsym = hf_symm.get_orbsym(mol, mo_coeff) sym_forbid = orbsym[viridx].reshape(-1,1) != orbsym[occidx] if fock_ao is None: if h1e is None: h1e = mf.get_hcore() dm0 = mf.make_rdm1(mo_coeff, mo_occ) fock_ao = h1e + mf.get_veff(mol, dm0) fock = reduce(numpy.dot, (mo_coeff.T, fock_ao, mo_coeff)) g = fock[viridx[:,None],occidx] * 2 foo = fock[occidx[:,None],occidx] fvv = fock[viridx[:,None],viridx] h_diag = (fvv.diagonal().reshape(-1,1)-foo.diagonal()) * 2 if with_symmetry and mol.symmetry: g[sym_forbid] = 0 h_diag[sym_forbid] = 0 # To project Hessians from another basis if different basis sets are used # in newton solver and underlying mean-filed solver. if hasattr(mf, '_scf') and id(mf._scf.mol) != id(mol): mo_coeff = addons.project_mo_nr2nr(mf._scf.mol, mo_coeff, mol) vind = _gen_rhf_response(mf, mo_coeff, mo_occ, singlet=None, hermi=1) def h_op(x): x = x.reshape(nvir,nocc) if with_symmetry and mol.symmetry: x = x.copy() x[sym_forbid] = 0 x2 = numpy.einsum('ps,sq->pq', fvv, x) #x2-= .5*numpy.einsum('ps,rp->rs', foo, x) #x2-= .5*numpy.einsum('sp,rp->rs', foo, x) x2-= numpy.einsum('ps,rp->rs', foo, x) # *2 for double occupancy d1 = reduce(numpy.dot, (orbv, x*2, orbo.T.conj())) dm1 = d1 + d1.T.conj() v1 = vind(dm1) x2 += reduce(numpy.dot, (orbv.T.conj(), v1, orbo)) if with_symmetry and mol.symmetry: x2[sym_forbid] = 0 return x2.reshape(-1) * 2 return g.reshape(-1), h_op, h_diag.reshape(-1)
def update_rotate_matrix(self, dx, mo_occ, u0=1, mo_coeff=None): dr = hf.unpack_uniq_var(dx, mo_occ) if WITH_EX_EY_DEGENERACY: mol = self._scf.mol if mol.symmetry and mol.groupname in ('Dooh', 'Coov'): orbsym = hf_symm.get_orbsym(mol, mo_coeff) _force_Ex_Ey_degeneracy_(dr, orbsym) return numpy.dot(u0, expmat(dr))
def update_rotate_matrix(self, dx, mo_occ, u0=1, mo_coeff=None): dr = hf.unpack_uniq_var(dx, mo_occ) if WITH_EX_EY_DEGENERACY: mol = self._scf.mol if mol.symmetry and mol.groupname in ('Dooh', 'Coov'): orbsym = hf_symm.get_orbsym(mol, mo_coeff) _force_Ex_Ey_degeneracy_(dr, orbsym) return numpy.dot(u0, expmat(dr))
def gen_vind(self, mf=None): if mf is None: mf = self._scf wfnsym = self.wfnsym singlet = self.singlet mol = mf.mol mo_coeff = mf.mo_coeff assert mo_coeff.dtype == numpy.double mo_energy = mf.mo_energy mo_occ = mf.mo_occ nao, nmo = mo_coeff.shape occidx = numpy.where(mo_occ == 2)[0] viridx = numpy.where(mo_occ == 0)[0] nocc = len(occidx) nvir = len(viridx) orbv = mo_coeff[:, viridx] orbo = mo_coeff[:, occidx] if wfnsym is not None and mol.symmetry: if isinstance(wfnsym, str): wfnsym = symm.irrep_name2id(mol.groupname, wfnsym) wfnsym = wfnsym % 10 # convert to D2h subgroup orbsym = hf_symm.get_orbsym(mol, mo_coeff) orbsym_in_d2h = numpy.asarray(orbsym) % 10 # convert to D2h irreps sym_forbid = (orbsym_in_d2h[occidx, None] ^ orbsym_in_d2h[viridx]) != wfnsym e_ia = (mo_energy[viridx].reshape(-1, 1) - mo_energy[occidx]).T if wfnsym is not None and mol.symmetry: e_ia[sym_forbid] = 0 d_ia = numpy.sqrt(e_ia) ed_ia = e_ia * d_ia hdiag = e_ia.ravel()**2 vresp = mf.gen_response(singlet=singlet, hermi=1) def vind(zs): zs = numpy.asarray(zs).reshape(-1, nocc, nvir) # *2 for double occupancy dmov = lib.einsum('xov,ov,po,qv->xpq', zs, d_ia * 2, orbo, orbv.conj()) # +cc for A+B and K_{ai,jb} in A == K_{ai,bj} in B dmov = dmov + dmov.conj().transpose(0, 2, 1) v1ao = vresp(dmov) v1ov = lib.einsum('xpq,po,qv->xov', v1ao, orbo.conj(), orbv) # numpy.sqrt(e_ia) * (e_ia*d_ia*z + v1ov) v1ov += numpy.einsum('xov,ov->xov', zs, ed_ia) v1ov *= d_ia return v1ov.reshape(v1ov.shape[0], -1) return vind, hdiag
def gen_vind(self, mf): wfnsym = self.wfnsym singlet = self.singlet mol = mf.mol mo_coeff = mf.mo_coeff assert (mo_coeff.dtype == numpy.double) mo_energy = mf.mo_energy mo_occ = mf.mo_occ nao, nmo = mo_coeff.shape occidx = numpy.where(mo_occ == 2)[0] viridx = numpy.where(mo_occ == 0)[0] nocc = len(occidx) nvir = len(viridx) orbv = mo_coeff[:, viridx] orbo = mo_coeff[:, occidx] if wfnsym is not None and mol.symmetry: if isinstance(wfnsym, str): wfnsym = symm.irrep_name2id(mol.groupname, wfnsym) wfnsym = wfnsym % 10 # convert to D2h subgroup orbsym = hf_symm.get_orbsym(mol, mo_coeff) % 10 sym_forbid = (orbsym[occidx, None] ^ orbsym[viridx]) != wfnsym e_ia = (mo_energy[viridx].reshape(-1, 1) - mo_energy[occidx]).T if wfnsym is not None and mol.symmetry: e_ia[sym_forbid] = 0 d_ia = numpy.sqrt(e_ia).ravel() ed_ia = e_ia.ravel() * d_ia hdiag = e_ia.ravel()**2 vresp = _gen_rhf_response(mf, singlet=singlet, hermi=1) def vind(zs): nz = len(zs) dmov = numpy.empty((nz, nao, nao)) for i, z in enumerate(zs): # *2 for double occupancy dm = reduce(numpy.dot, (orbo, (d_ia * z).reshape(nocc, nvir) * 2, orbv.T)) dmov[ i] = dm + dm.T # +cc for A+B and K_{ai,jb} in A == K_{ai,bj} in B v1ao = vresp(dmov) v1ov = _ao2mo.nr_e2(v1ao, mo_coeff, (0, nocc, nocc, nmo)).reshape(-1, nocc * nvir) for i, z in enumerate(zs): # numpy.sqrt(e_ia) * (e_ia*d_ia*z + v1ov) v1ov[i] += ed_ia * z v1ov[i] *= d_ia return v1ov.reshape(nz, -1) return vind, hdiag
def gen_vind(self, mf): wfnsym = self.wfnsym singlet = self.singlet mol = mf.mol mo_coeff = mf.mo_coeff assert (mo_coeff.dtype == numpy.double) mo_energy = mf.mo_energy mo_occ = mf.mo_occ nao, nmo = mo_coeff.shape occidx = numpy.where(mo_occ == 2)[0] viridx = numpy.where(mo_occ == 0)[0] nocc = len(occidx) nvir = len(viridx) orbv = mo_coeff[:, viridx] orbo = mo_coeff[:, occidx] if wfnsym is not None and mol.symmetry: orbsym = hf_symm.get_orbsym(mol, mo_coeff) sym_forbid = (orbsym[viridx].reshape(-1, 1) ^ orbsym[occidx]) != wfnsym eai = mo_energy[viridx].reshape(-1, 1) - mo_energy[occidx] if wfnsym is not None and mol.symmetry: eai[sym_forbid] = 0 dai = numpy.sqrt(eai).ravel() edai = eai.ravel() * dai hdiag = eai.ravel()**2 vresp = _gen_rhf_response(mf, singlet=singlet, hermi=1) def vind(zs): nz = len(zs) dmvo = numpy.empty((nz, nao, nao)) for i, z in enumerate(zs): # *2 for double occupancy dm = reduce(numpy.dot, (orbv, (dai * z).reshape(nvir, nocc) * 2, orbo.T)) dmvo[ i] = dm + dm.T # +cc for A+B and K_{ai,jb} in A == K_{ai,bj} in B v1ao = vresp(dmvo) v1vo = _ao2mo.nr_e2(v1ao, mo_coeff, (nocc, nmo, 0, nocc)).reshape(-1, nvir * nocc) for i, z in enumerate(zs): # numpy.sqrt(eai) * (eai*dai*z + v1vo) v1vo[i] += edai * z v1vo[i] *= dai return v1vo.reshape(nz, -1) return vind, hdiag
def gen_vind(self, mf): wfnsym = self.wfnsym singlet = self.singlet mol = mf.mol mo_coeff = mf.mo_coeff assert(mo_coeff.dtype == numpy.double) mo_energy = mf.mo_energy mo_occ = mf.mo_occ nao, nmo = mo_coeff.shape occidx = numpy.where(mo_occ==2)[0] viridx = numpy.where(mo_occ==0)[0] nocc = len(occidx) nvir = len(viridx) orbv = mo_coeff[:,viridx] orbo = mo_coeff[:,occidx] if wfnsym is not None and mol.symmetry: if isinstance(wfnsym, str): wfnsym = symm.irrep_name2id(mol.groupname, wfnsym) wfnsym = wfnsym % 10 # convert to D2h subgroup orbsym = hf_symm.get_orbsym(mol, mo_coeff) % 10 sym_forbid = (orbsym[occidx,None] ^ orbsym[viridx]) != wfnsym e_ia = (mo_energy[viridx].reshape(-1,1) - mo_energy[occidx]).T if wfnsym is not None and mol.symmetry: e_ia[sym_forbid] = 0 d_ia = numpy.sqrt(e_ia).ravel() ed_ia = e_ia.ravel() * d_ia hdiag = e_ia.ravel() ** 2 vresp = _gen_rhf_response(mf, singlet=singlet, hermi=1) def vind(zs): nz = len(zs) dmov = numpy.empty((nz,nao,nao)) for i, z in enumerate(zs): # *2 for double occupancy dm = reduce(numpy.dot, (orbo, (d_ia*z).reshape(nocc,nvir)*2, orbv.T)) dmov[i] = dm + dm.T # +cc for A+B and K_{ai,jb} in A == K_{ai,bj} in B v1ao = vresp(dmov) v1ov = _ao2mo.nr_e2(v1ao, mo_coeff, (0,nocc,nocc,nmo)).reshape(-1,nocc*nvir) for i, z in enumerate(zs): # numpy.sqrt(e_ia) * (e_ia*d_ia*z + v1ov) v1ov[i] += ed_ia*z v1ov[i] *= d_ia return v1ov.reshape(nz,-1) return vind, hdiag
def rotate_mo(self, mo_coeff, u, log=None): mo = numpy.dot(mo_coeff, u) if self._scf.mol.symmetry: orbsym = hf_symm.get_orbsym(self._scf.mol, mo_coeff) mo = lib.tag_array(mo, orbsym=orbsym) if isinstance(log, logger.Logger) and log.verbose >= logger.DEBUG: idx = self.mo_occ > 0 s = reduce(numpy.dot, (mo[:, idx].conj().T, self._scf.get_ovlp(), self.mo_coeff[:, idx])) log.debug('Overlap to initial guess, SVD = %s', _effective_svd(s, 1e-5)) log.debug('Overlap to last step, SVD = %s', _effective_svd(u[idx][:, idx], 1e-5)) return mo
def _finalize(self): if isinstance(self, rohf.ROHF): rohf.ROHF._finalize(self) elif isinstance(self, hf.RHF): hf.RHF._finalize(self) if (self.mol.groupname in ('Dooh', 'Coov')): self.partner_orbs = self.get_partner_orbs() # sort MOs wrt orbital energies, it should be done last. # Using mergesort because it is stable. We don't want to change the # ordering of the symmetry labels when two orbitals are degenerated. if isinstance(self, rohf.ROHF): c_sort = numpy.argsort(self.mo_energy[self.mo_occ==2].round(9), kind='mergesort') o_sort = numpy.argsort(self.mo_energy[self.mo_occ==1].round(9), kind='mergesort') v_sort = numpy.argsort(self.mo_energy[self.mo_occ==0].round(9), kind='mergesort') idx = numpy.arange(self.mo_energy.size) idx = numpy.hstack((idx[self.mo_occ==2][c_sort], idx[self.mo_occ==1][o_sort], idx[self.mo_occ==0][v_sort])) if hasattr(self.mo_energy, 'mo_ea'): mo_ea = self.mo_energy.mo_ea[idx] mo_eb = self.mo_energy.mo_eb[idx] self.mo_energy = lib.tag_array(self.mo_energy[idx], mo_ea=mo_ea, mo_eb=mo_eb) else: self.mo_energy = self.mo_energy[idx] elif isinstance(self, hf.RHF): o_sort = numpy.argsort(self.mo_energy[self.mo_occ> 0].round(9), kind='mergesort') v_sort = numpy.argsort(self.mo_energy[self.mo_occ==0].round(9), kind='mergesort') idx = numpy.arange(self.mo_energy.size) idx = numpy.hstack((idx[self.mo_occ> 0][o_sort], idx[self.mo_occ==0][v_sort])) self.mo_energy = self.mo_energy[idx] orbsym = hf_symm.get_orbsym(self.mol, self.mo_coeff) self.mo_coeff = lib.tag_array(self.mo_coeff[:,idx], orbsym=orbsym[idx]) self.mo_occ = self.mo_occ[idx] if hasattr(self, 'partner_orbs'): idx_inv = numpy.argsort(idx) self.partner_orbs = [idx_inv[orb] for orb in self.partner_orbs[idx]] if self.chkfile: chkfile.dump_scf(self.mol, self.chkfile, self.e_tot, self.mo_energy, self.mo_coeff, self.mo_occ, overwrite_mol=False) return self
def init_guess(self, mf, nstates=None, wfnsym=None): if nstates is None: nstates = self.nstates if wfnsym is None: wfnsym = self.wfnsym mo_energy = mf.mo_energy mo_occ = mf.mo_occ occidx = numpy.where(mo_occ == 2)[0] viridx = numpy.where(mo_occ == 0)[0] eai = (mo_energy[viridx, None] - mo_energy[occidx]).T if wfnsym is not None and mf.mol.symmetry: orbsym = hf_symm.get_orbsym(mf.mol, mf.mo_coeff) eai[(orbsym[occidx, None] ^ orbsym[viridx]) != wfnsym] = 1e99 nov = eai.size nroot = min(nstates, nov) x0 = numpy.zeros((nroot, nov)) idx = numpy.argsort(eai.ravel()) for i in range(nroot): x0[i, idx[i]] = 1 # Koopmans' excitations return x0
def init_guess(self, mf, nstates=None, wfnsym=None): if nstates is None: nstates = self.nstates if wfnsym is None: wfnsym = self.wfnsym mo_energy = mf.mo_energy mo_occ = mf.mo_occ occidx = numpy.where(mo_occ == 2)[0] viridx = numpy.where(mo_occ == 0)[0] eai = lib.direct_sum('a-i->ai', mo_energy[viridx], mo_energy[occidx]) if wfnsym is not None and mf.mol.symmetry: orbsym = hf_symm.get_orbsym(mf.mol, mf.mo_coeff) sym_forbid = (orbsym[viridx].reshape(-1, 1) ^ orbsym[occidx]) != wfnsym eai[sym_forbid] = 1e99 nov = eai.size nroot = min(nstates, nov) x0 = numpy.zeros((nroot, nov)) idx = numpy.argsort(eai.ravel()) for i in range(nroot): x0[i, idx[i]] = 1 # lowest excitations return x0
def init_guess(self, mf, nstates=None, wfnsym=None): if nstates is None: nstates = self.nstates if wfnsym is None: wfnsym = self.wfnsym mo_energy = mf.mo_energy mo_occ = mf.mo_occ occidx = numpy.where(mo_occ==2)[0] viridx = numpy.where(mo_occ==0)[0] e_ia = mo_energy[viridx] - mo_energy[occidx,None] if wfnsym is not None and mf.mol.symmetry: if isinstance(wfnsym, str): wfnsym = symm.irrep_name2id(mf.mol.groupname, wfnsym) wfnsym = wfnsym % 10 # convert to D2h subgroup orbsym = hf_symm.get_orbsym(mf.mol, mf.mo_coeff) % 10 e_ia[(orbsym[occidx,None] ^ orbsym[viridx]) != wfnsym] = 1e99 nov = e_ia.size nroot = min(nstates, nov) x0 = numpy.zeros((nroot, nov)) idx = numpy.argsort(e_ia.ravel()) for i in range(nroot): x0[i,idx[i]] = 1 # Koopmans' excitations return x0
def get_nto(tdobj, state=1, threshold=OUTPUT_THRESHOLD, verbose=None): r''' Natural transition orbital analysis. The natural transition density matrix between ground state and excited state :math:`Tia = \langle \Psi_{ex} | i a^\dagger | \Psi_0 \rangle` can be transformed to diagonal form through SVD :math:`T = O \sqrt{\lambda} V^\dagger`. O and V are occupied and virtual natural transition orbitals. The diagonal elements :math:`\lambda` are the weights of the occupied-virtual orbital pair in the excitation. Ref: Martin, R. L., JCP, 118, 4775-4777 Note in the TDHF/TDDFT calculations, the excitation part (X) is interpreted as the CIS coefficients and normalized to 1. The de-excitation part (Y) is ignored. Args: tdobj : TDA, or TDHF, or TDDFT object state : int Excited state ID. state = 1 means the first excited state. If state < 0, state ID is counted from the last excited state. Kwargs: threshold : float Above which the NTO coefficients will be printed in the output. Returns: A list (weights, NTOs). NTOs are natural orbitals represented in AO basis. The first N_occ NTOs are occupied NTOs and the rest are virtual NTOs. ''' if state == 0: logger.warn( tdobj, 'Excited state starts from 1. ' 'Set state=1 for first excited state.') state_id = state elif state < 0: state_id = state else: state_id = state - 1 mol = tdobj.mol mo_coeff = tdobj._scf.mo_coeff mo_occ = tdobj._scf.mo_occ orbo = mo_coeff[:, mo_occ == 2] orbv = mo_coeff[:, mo_occ == 0] nocc = orbo.shape[1] nvir = orbv.shape[1] cis_t1 = tdobj.xy[state_id][0] # TDDFT (X,Y) has X^2-Y^2=1. # Renormalizing X (X^2=1) to map it to CIS coefficients cis_t1 *= 1. / numpy.linalg.norm(cis_t1) # TODO: Comparing to the NTOs defined in JCP, 142, 244103. JCP, 142, 244103 # provides a method to incorporate the Y matrix in the transition density # matrix. However, it may break the point group symmetry of the NTO orbitals # when the system has degenerated irreducible representations. if mol.symmetry: orbsym = hf_symm.get_orbsym(mol, mo_coeff) orbsym_in_d2h = numpy.asarray(orbsym) % 10 # convert to D2h irreps o_sym = orbsym_in_d2h[mo_occ == 2] v_sym = orbsym_in_d2h[mo_occ == 0] nto_o = numpy.eye(nocc) nto_v = numpy.eye(nvir) weights_o = numpy.zeros(nocc) weights_v = numpy.zeros(nvir) for ir in set(orbsym_in_d2h): idx = numpy.where(o_sym == ir)[0] if idx.size > 0: dm_oo = numpy.dot(cis_t1[idx], cis_t1[idx].T) weights_o[idx], nto_o[idx[:, None], idx] = numpy.linalg.eigh(dm_oo) idx = numpy.where(v_sym == ir)[0] if idx.size > 0: dm_vv = numpy.dot(cis_t1[:, idx].T, cis_t1[:, idx]) weights_v[idx], nto_v[idx[:, None], idx] = numpy.linalg.eigh(dm_vv) # weights in descending order idx = numpy.argsort(-weights_o) weights_o = weights_o[idx] nto_o = nto_o[:, idx] o_sym = o_sym[idx] idx = numpy.argsort(-weights_v) weights_v = weights_v[idx] nto_v = nto_v[:, idx] v_sym = v_sym[idx] nto_orbsym = numpy.hstack((o_sym, v_sym)) if nocc < nvir: weights = weights_o else: weights = weights_v else: nto_o, w, nto_vT = numpy.linalg.svd(cis_t1) nto_v = nto_vT.conj().T weights = w**2 nto_orbsym = None idx = numpy.argmax(abs(nto_o.real), axis=0) nto_o[:, nto_o[idx, numpy.arange(nocc)].real < 0] *= -1 idx = numpy.argmax(abs(nto_v.real), axis=0) nto_v[:, nto_v[idx, numpy.arange(nvir)].real < 0] *= -1 occupied_nto = numpy.dot(orbo, nto_o) virtual_nto = numpy.dot(orbv, nto_v) nto_coeff = numpy.hstack((occupied_nto, virtual_nto)) if mol.symmetry: nto_coeff = lib.tag_array(nto_coeff, orbsym=nto_orbsym) log = logger.new_logger(tdobj, verbose) if log.verbose >= logger.INFO: log.info('State %d: %g eV NTO largest component %s', state_id + 1, tdobj.e[state_id] * nist.HARTREE2EV, weights[0]) o_idx = numpy.where(abs(nto_o[:, 0]) > threshold)[0] v_idx = numpy.where(abs(nto_v[:, 0]) > threshold)[0] fmt = '%' + str(lib.param.OUTPUT_DIGITS) + 'f (MO #%d)' log.info(' occ-NTO: ' + ' + '.join([(fmt % (nto_o[i, 0], i + MO_BASE)) for i in o_idx])) log.info(' vir-NTO: ' + ' + '.join([(fmt % (nto_v[i, 0], i + MO_BASE + nocc)) for i in v_idx])) return weights, nto_coeff
def gen_g_hop_rhf(mf, mo_coeff, mo_occ, fock_ao=None, h1e=None, with_symmetry=True): mo_coeff0 = mo_coeff mol = mf.mol if getattr(mf, '_scf', None) and mf._scf.mol != mol: #TODO: construct vind with dual-basis treatment, (see also JCP, 118, 9497) # To project Hessians from another basis if different basis sets are used # in newton solver and underlying mean-filed solver. mo_coeff = addons.project_mo_nr2nr(mf._scf.mol, mo_coeff, mol) occidx = numpy.where(mo_occ == 2)[0] viridx = numpy.where(mo_occ == 0)[0] nocc = len(occidx) nvir = len(viridx) orbo = mo_coeff[:, occidx] orbv = mo_coeff[:, viridx] if with_symmetry and mol.symmetry: orbsym = hf_symm.get_orbsym(mol, mo_coeff) sym_forbid = orbsym[viridx, None] != orbsym[occidx] if fock_ao is None: # dm0 is the density matrix in projected basis. Computing fock in # projected basis. if getattr(mf, '_scf', None) and mf._scf.mol != mol: h1e = mf.get_hcore(mol) dm0 = mf.make_rdm1(mo_coeff, mo_occ) fock_ao = mf.get_fock(h1e, dm=dm0) fock = reduce(numpy.dot, (mo_coeff.conj().T, fock_ao, mo_coeff)) else: # If fock is given, it corresponds to main basis. It needs to be # diagonalized with the mo_coeff of the main basis. fock = reduce(numpy.dot, (mo_coeff0.conj().T, fock_ao, mo_coeff0)) g = fock[viridx[:, None], occidx] * 2 foo = fock[occidx[:, None], occidx] fvv = fock[viridx[:, None], viridx] h_diag = (fvv.diagonal().real[:, None] - foo.diagonal().real) * 2 if with_symmetry and mol.symmetry: g[sym_forbid] = 0 h_diag[sym_forbid] = 0 vind = _gen_rhf_response(mf, mo_coeff, mo_occ, singlet=None, hermi=1) def h_op(x): x = x.reshape(nvir, nocc) if with_symmetry and mol.symmetry: x = x.copy() x[sym_forbid] = 0 x2 = numpy.einsum('ps,sq->pq', fvv, x) #x2-= .5*numpy.einsum('ps,rp->rs', foo, x) #x2-= .5*numpy.einsum('sp,rp->rs', foo, x) x2 -= numpy.einsum('ps,rp->rs', foo, x) # *2 for double occupancy d1 = reduce(numpy.dot, (orbv, x * 2, orbo.conj().T)) dm1 = d1 + d1.conj().T v1 = vind(dm1) x2 += reduce(numpy.dot, (orbv.conj().T, v1, orbo)) if with_symmetry and mol.symmetry: x2[sym_forbid] = 0 return x2.ravel() * 2 return g.reshape(-1), h_op, h_diag.reshape(-1)
def rotate_mo(self, mo_coeff, u, log=None): mo = numpy.dot(mo_coeff, u) if self._scf.mol.symmetry: orbsym = hf_symm.get_orbsym(self._scf.mol, mo_coeff) mo = lib.tag_array(mo, orbsym=orbsym) return mo
def gen_g_hop_rhf(mf, mo_coeff, mo_occ, fock_ao=None, h1e=None, with_symmetry=True): mo_coeff0 = mo_coeff mol = mf.mol if getattr(mf, '_scf', None) and mf._scf.mol != mol: #TODO: construct vind with dual-basis treatment, (see also JCP, 118, 9497) # To project Hessians from another basis if different basis sets are used # in newton solver and underlying mean-filed solver. mo_coeff = addons.project_mo_nr2nr(mf._scf.mol, mo_coeff, mol) occidx = numpy.where(mo_occ==2)[0] viridx = numpy.where(mo_occ==0)[0] nocc = len(occidx) nvir = len(viridx) orbo = mo_coeff[:,occidx] orbv = mo_coeff[:,viridx] if with_symmetry and mol.symmetry: orbsym = hf_symm.get_orbsym(mol, mo_coeff) sym_forbid = orbsym[viridx,None] != orbsym[occidx] if fock_ao is None: # dm0 is the density matrix in projected basis. Computing fock in # projected basis. dm0 = mf.make_rdm1(mo_coeff, mo_occ) fock_ao = mf.get_fock(h1e, dm=dm0) fock = reduce(numpy.dot, (mo_coeff.conj().T, fock_ao, mo_coeff)) else: # If fock is given, it corresponds to main basis. It needs to be # diagonalized with the mo_coeff of the main basis. fock = reduce(numpy.dot, (mo_coeff0.conj().T, fock_ao, mo_coeff0)) g = fock[viridx[:,None],occidx] * 2 foo = fock[occidx[:,None],occidx] fvv = fock[viridx[:,None],viridx] h_diag = (fvv.diagonal().real[:,None] - foo.diagonal().real) * 2 if with_symmetry and mol.symmetry: g[sym_forbid] = 0 h_diag[sym_forbid] = 0 vind = _gen_rhf_response(mf, mo_coeff, mo_occ, singlet=None, hermi=1) def h_op(x): x = x.reshape(nvir,nocc) if with_symmetry and mol.symmetry: x = x.copy() x[sym_forbid] = 0 x2 = numpy.einsum('ps,sq->pq', fvv, x) #x2-= .5*numpy.einsum('ps,rp->rs', foo, x) #x2-= .5*numpy.einsum('sp,rp->rs', foo, x) x2-= numpy.einsum('ps,rp->rs', foo, x) # *2 for double occupancy d1 = reduce(numpy.dot, (orbv, x*2, orbo.conj().T)) dm1 = d1 + d1.conj().T v1 = vind(dm1) x2 += reduce(numpy.dot, (orbv.conj().T, v1, orbo)) if with_symmetry and mol.symmetry: x2[sym_forbid] = 0 return x2.ravel() * 2 return g.reshape(-1), h_op, h_diag.reshape(-1)
def gen_tdhf_operation(mf, fock_ao=None, singlet=True, wfnsym=None): '''Generate function to compute [ A B][X] [-B -A][Y] ''' mol = mf.mol mo_coeff = mf.mo_coeff assert(mo_coeff.dtype == numpy.double) mo_energy = mf.mo_energy mo_occ = mf.mo_occ nao, nmo = mo_coeff.shape occidx = numpy.where(mo_occ==2)[0] viridx = numpy.where(mo_occ==0)[0] nocc = len(occidx) nvir = len(viridx) orbv = mo_coeff[:,viridx] orbo = mo_coeff[:,occidx] if wfnsym is not None and mol.symmetry: if isinstance(wfnsym, str): wfnsym = symm.irrep_name2id(mol.groupname, wfnsym) wfnsym = wfnsym % 10 # convert to D2h subgroup orbsym = hf_symm.get_orbsym(mol, mo_coeff) % 10 sym_forbid = (orbsym[occidx,None] ^ orbsym[viridx]) != wfnsym #dm0 = mf.make_rdm1(mo_coeff, mo_occ) #fock_ao = mf.get_hcore() + mf.get_veff(mol, dm0) #fock = reduce(numpy.dot, (mo_coeff.T, fock_ao, mo_coeff)) #foo = fock[occidx[:,None],occidx] #fvv = fock[viridx[:,None],viridx] foo = numpy.diag(mo_energy[occidx]) fvv = numpy.diag(mo_energy[viridx]) hdiag = (fvv.diagonal().reshape(-1,1) - foo.diagonal()).T if wfnsym is not None and mol.symmetry: hdiag[sym_forbid] = 0 hdiag = numpy.hstack((hdiag.ravel(), hdiag.ravel())) mo_coeff = numpy.asarray(numpy.hstack((orbo,orbv)), order='F') vresp = _gen_rhf_response(mf, singlet=singlet, hermi=0) def vind(xys): nz = len(xys) if wfnsym is not None and mol.symmetry: # shape(nz,2,nocc,nvir): 2 ~ X,Y xys = numpy.copy(xys).reshape(nz,2,nocc,nvir) xys[:,:,sym_forbid] = 0 dms = numpy.empty((nz,nao,nao)) for i in range(nz): x, y = xys[i].reshape(2,nocc,nvir) # *2 for double occupancy dmx = reduce(numpy.dot, (orbo, x*2, orbv.T)) dmy = reduce(numpy.dot, (orbv, y.T*2, orbo.T)) dms[i] = dmx + dmy # AX + BY v1ao = vresp(dms) v1ov = _ao2mo.nr_e2(v1ao, mo_coeff, (0,nocc,nocc,nmo)).reshape(-1,nocc,nvir) v1vo = _ao2mo.nr_e2(v1ao, mo_coeff, (nocc,nmo,0,nocc)).reshape(-1,nvir,nocc) hx = numpy.empty((nz,2,nocc,nvir), dtype=v1ov.dtype) for i in range(nz): x, y = xys[i].reshape(2,nocc,nvir) hx[i,0] = v1ov[i] hx[i,0]+= numpy.einsum('sp,qs->qp', fvv, x) # AX hx[i,0]-= numpy.einsum('sp,pr->sr', foo, x) # AX hx[i,1] =-v1vo[i].T hx[i,1]-= numpy.einsum('sp,qs->qp', fvv, y) #-AY hx[i,1]+= numpy.einsum('sp,pr->sr', foo, y) #-AY if wfnsym is not None and mol.symmetry: hx[:,:,sym_forbid] = 0 return hx.reshape(nz,-1) return vind, hdiag
def gen_tda_hop(mf, fock_ao=None, singlet=True, wfnsym=None, max_memory=2000): '''(A+B)x Kwargs: wfnsym : int Point group symmetry for excited CIS wavefunction. ''' mol = mf.mol mo_coeff = mf.mo_coeff assert (mo_coeff.dtype == numpy.double) mo_energy = mf.mo_energy mo_occ = mf.mo_occ nao, nmo = mo_coeff.shape occidx = numpy.where(mo_occ == 2)[0] viridx = numpy.where(mo_occ == 0)[0] nocc = len(occidx) nvir = len(viridx) orbv = mo_coeff[:, viridx] orbo = mo_coeff[:, occidx] if wfnsym is not None and mol.symmetry: orbsym = hf_symm.get_orbsym(mol, mo_coeff) sym_forbid = (orbsym[viridx].reshape(-1, 1) ^ orbsym[occidx]) != wfnsym if fock_ao is None: #dm0 = mf.make_rdm1(mo_coeff, mo_occ) #fock_ao = mf.get_hcore() + mf.get_veff(mol, dm0) foo = numpy.diag(mo_energy[occidx]) fvv = numpy.diag(mo_energy[viridx]) else: fock = reduce(numpy.dot, (mo_coeff.T, fock_ao, mo_coeff)) foo = fock[occidx[:, None], occidx] fvv = fock[viridx[:, None], viridx] hdiag = fvv.diagonal().reshape(-1, 1) - foo.diagonal() if wfnsym is not None and mol.symmetry: hdiag[sym_forbid] = 0 hdiag = hdiag.ravel() mo_coeff = numpy.asarray(numpy.hstack((orbo, orbv)), order='F') vresp = _gen_rhf_response(mf, singlet=singlet, hermi=0) def vind(zs): nz = len(zs) if wfnsym is not None and mol.symmetry: zs = numpy.copy(zs).reshape(-1, nvir, nocc) zs[:, sym_forbid] = 0 dmvo = numpy.empty((nz, nao, nao)) for i, z in enumerate(zs): # *2 for double occupancy dmvo[i] = reduce(numpy.dot, (orbv, z.reshape(nvir, nocc) * 2, orbo.T)) v1ao = vresp(dmvo) #v1vo = numpy.asarray([reduce(numpy.dot, (orbv.T, v, orbo)) for v in v1ao]) v1vo = _ao2mo.nr_e2(v1ao, mo_coeff, (nocc, nmo, 0, nocc)).reshape(-1, nvir, nocc) for i, z in enumerate(zs): v1vo[i] += numpy.einsum('ps,sq->pq', fvv, z.reshape(nvir, nocc)) v1vo[i] -= numpy.einsum('ps,rp->rs', foo, z.reshape(nvir, nocc)) if wfnsym is not None and mol.symmetry: v1vo[:, sym_forbid] = 0 return v1vo.reshape(nz, -1) return vind, hdiag
def _gen_hop_rhf_external(mf, with_symmetry=True, verbose=None): mol = mf.mol mo_coeff = mf.mo_coeff mo_occ = mf.mo_occ occidx = numpy.where(mo_occ==2)[0] viridx = numpy.where(mo_occ==0)[0] nocc = len(occidx) nvir = len(viridx) orbv = mo_coeff[:,viridx] orbo = mo_coeff[:,occidx] if with_symmetry and mol.symmetry: orbsym = hf_symm.get_orbsym(mol, mo_coeff) sym_forbid = orbsym[viridx].reshape(-1,1) != orbsym[occidx] h1e = mf.get_hcore() dm0 = mf.make_rdm1(mo_coeff, mo_occ) fock_ao = h1e + mf.get_veff(mol, dm0) fock = reduce(numpy.dot, (mo_coeff.conj().T, fock_ao, mo_coeff)) foo = fock[occidx[:,None],occidx] fvv = fock[viridx[:,None],viridx] hdiag = fvv.diagonal().reshape(-1,1) - foo.diagonal() if with_symmetry and mol.symmetry: hdiag[sym_forbid] = 0 hdiag = hdiag.ravel() vrespz = _gen_rhf_response(mf, singlet=None, hermi=2) def hop_real2complex(x1): x1 = x1.reshape(nvir,nocc) if with_symmetry and mol.symmetry: x1 = x1.copy() x1[sym_forbid] = 0 x2 = numpy.einsum('ps,sq->pq', fvv, x1) x2-= numpy.einsum('ps,rp->rs', foo, x1) d1 = reduce(numpy.dot, (orbv, x1*2, orbo.conj().T)) dm1 = d1 - d1.conj().T # No Coulomb and fxc contribution for anti-hermitian DM v1 = vrespz(dm1) x2 += reduce(numpy.dot, (orbv.conj().T, v1, orbo)) if with_symmetry and mol.symmetry: x2[sym_forbid] = 0 return x2.ravel() vresp1 = _gen_rhf_response(mf, singlet=False, hermi=1) def hop_rhf2uhf(x1): from pyscf.dft import numint # See also rhf.TDA triplet excitation x1 = x1.reshape(nvir,nocc) if with_symmetry and mol.symmetry: x1 = x1.copy() x1[sym_forbid] = 0 x2 = numpy.einsum('ps,sq->pq', fvv, x1) x2-= numpy.einsum('ps,rp->rs', foo, x1) d1 = reduce(numpy.dot, (orbv, x1*2, orbo.conj().T)) dm1 = d1 + d1.conj().T v1ao = vresp1(dm1) x2 += reduce(numpy.dot, (orbv.conj().T, v1ao, orbo)) if with_symmetry and mol.symmetry: x2[sym_forbid] = 0 return x2.real.ravel() return hop_real2complex, hdiag, hop_rhf2uhf, hdiag
def get_nto(tdobj, state=1, threshold=OUTPUT_THRESHOLD, verbose=None): r''' Natural transition orbital analysis. The natural transition density matrix between ground state and excited state :math:`Tia = \langle \Psi_{ex} | i a^\dagger | \Psi_0 \rangle` can be transformed to diagonal form through SVD :math:`T = O \sqrt{\lambda} V^\dagger`. O and V are occupied and virtual natural transition orbitals. The diagonal elements :math:`\lambda` are the weights of the occupied-virtual orbital pair in the excitation. Ref: Martin, R. L., JCP, 118, 4775-4777 Note in the TDHF/TDDFT calculations, the excitation part (X) is interpreted as the CIS coefficients and normalized to 1. The de-excitation part (Y) is ignored. Args: state : int Excited state ID. state = 1 means the first excited state. If state < 0, state ID is counted from the last excited state. Kwargs: threshold : float Above which the NTO coefficients will be printed in the output. Returns: A list (weights, NTOs). NTOs are natural orbitals represented in AO basis. The first N_occ NTOs are occupied NTOs and the rest are virtual NTOs. ''' if state == 0: logger.warn(tdobj, 'Excited state starts from 1. ' 'Set state=1 for first excited state.') state_id = state elif state < 0: state_id = state else: state_id = state - 1 mol = tdobj.mol mo_coeff = tdobj._scf.mo_coeff mo_occ = tdobj._scf.mo_occ orbo = mo_coeff[:,mo_occ==2] orbv = mo_coeff[:,mo_occ==0] nocc = orbo.shape[1] nvir = orbv.shape[1] cis_t1 = tdobj.xy[state_id][0] # TDDFT (X,Y) has X^2-Y^2=1. # Renormalizing X (X^2=1) to map it to CIS coefficients cis_t1 *= 1. / numpy.linalg.norm(cis_t1) # TODO: Comparing to the NTOs defined in JCP, 142, 244103. JCP, 142, 244103 # provides a method to incorporate the Y matrix in the transition density # matrix. However, it may break the point group symmetry of the NTO orbitals # when the system has degenerated irreducible representations. if mol.symmetry: orbsym = hf_symm.get_orbsym(mol, mo_coeff) o_sym = orbsym[mo_occ==2] v_sym = orbsym[mo_occ==0] nto_o = numpy.eye(nocc) nto_v = numpy.eye(nvir) weights_o = numpy.zeros(nocc) weights_v = numpy.zeros(nvir) for ir in set(orbsym): idx = numpy.where(o_sym == ir)[0] if idx.size > 0: dm_oo = numpy.dot(cis_t1[idx], cis_t1[idx].T) weights_o[idx], nto_o[idx[:,None],idx] = numpy.linalg.eigh(dm_oo) idx = numpy.where(v_sym == ir)[0] if idx.size > 0: dm_vv = numpy.dot(cis_t1[:,idx].T, cis_t1[:,idx]) weights_v[idx], nto_v[idx[:,None],idx] = numpy.linalg.eigh(dm_vv) # weights in descending order idx = numpy.argsort(-weights_o) weights_o = weights_o[idx] nto_o = nto_o[:,idx] o_sym = o_sym[idx] idx = numpy.argsort(-weights_v) weights_v = weights_v[idx] nto_v = nto_v[:,idx] v_sym = v_sym[idx] nto_orbsym = numpy.hstack((o_sym, v_sym)) if nocc < nvir: weights = weights_o else: weights = weights_v else: nto_o, w, nto_vT = numpy.linalg.svd(cis_t1) nto_v = nto_vT.conj().T weights = w**2 nto_orbsym = None idx = numpy.argmax(abs(nto_o.real), axis=0) nto_o[:,nto_o[idx,numpy.arange(nocc)].real<0] *= -1 idx = numpy.argmax(abs(nto_v.real), axis=0) nto_v[:,nto_v[idx,numpy.arange(nvir)].real<0] *= -1 occupied_nto = numpy.dot(orbo, nto_o) virtual_nto = numpy.dot(orbv, nto_v) nto_coeff = numpy.hstack((occupied_nto, virtual_nto)) if mol.symmetry: nto_coeff = lib.tag_array(nto_coeff, orbsym=nto_orbsym) log = logger.new_logger(tdobj, verbose) if log.verbose >= logger.INFO: log.info('State %d: %g eV NTO largest component %s', state_id+1, tdobj.e[state_id]*nist.HARTREE2EV, weights[0]) o_idx = numpy.where(abs(nto_o[:,0]) > threshold)[0] v_idx = numpy.where(abs(nto_v[:,0]) > threshold)[0] fmt = '%' + str(lib.param.OUTPUT_DIGITS) + 'f (MO #%d)' log.info(' occ-NTO: ' + ' + '.join([(fmt % (nto_o[i,0], i+MO_BASE)) for i in o_idx])) log.info(' vir-NTO: ' + ' + '.join([(fmt % (nto_v[i,0], i+MO_BASE+nocc)) for i in v_idx])) return weights, nto_coeff
def _gen_hop_rhf_external(mf, with_symmetry=True, verbose=None): mol = mf.mol mo_coeff = mf.mo_coeff mo_occ = mf.mo_occ occidx = numpy.where(mo_occ==2)[0] viridx = numpy.where(mo_occ==0)[0] nocc = len(occidx) nvir = len(viridx) orbv = mo_coeff[:,viridx] orbo = mo_coeff[:,occidx] if with_symmetry and mol.symmetry: orbsym = hf_symm.get_orbsym(mol, mo_coeff) sym_forbid = orbsym[viridx].reshape(-1,1) != orbsym[occidx] h1e = mf.get_hcore() dm0 = mf.make_rdm1(mo_coeff, mo_occ) fock_ao = h1e + mf.get_veff(mol, dm0) fock = reduce(numpy.dot, (mo_coeff.T, fock_ao, mo_coeff)) foo = fock[occidx[:,None],occidx] fvv = fock[viridx[:,None],viridx] hdiag = fvv.diagonal().reshape(-1,1) - foo.diagonal() if with_symmetry and mol.symmetry: hdiag[sym_forbid] = 0 hdiag = hdiag.ravel() vrespz = _gen_rhf_response(mf, singlet=None, hermi=2) def hop_real2complex(x1): x1 = x1.reshape(nvir,nocc) if with_symmetry and mol.symmetry: x1 = x1.copy() x1[sym_forbid] = 0 x2 = numpy.einsum('ps,sq->pq', fvv, x1) x2-= numpy.einsum('ps,rp->rs', foo, x1) d1 = reduce(numpy.dot, (orbv, x1*2, orbo.T.conj())) dm1 = d1 - d1.T.conj() # No Coulomb and fxc contribution for anti-hermitian DM v1 = vrespz(dm1) x2 += reduce(numpy.dot, (orbv.T.conj(), v1, orbo)) if with_symmetry and mol.symmetry: x2[sym_forbid] = 0 return x2.ravel() vresp1 = _gen_rhf_response(mf, singlet=False, hermi=1) def hop_rhf2uhf(x1): from pyscf.dft import numint # See also rhf.TDA triplet excitation x1 = x1.reshape(nvir,nocc) if with_symmetry and mol.symmetry: x1 = x1.copy() x1[sym_forbid] = 0 x2 = numpy.einsum('ps,sq->pq', fvv, x1) x2-= numpy.einsum('ps,rp->rs', foo, x1) d1 = reduce(numpy.dot, (orbv, x1*2, orbo.T.conj())) dm1 = d1 + d1.T.conj() v1ao = vresp1(dm1) x2 += reduce(numpy.dot, (orbv.T.conj(), v1ao, orbo)) if with_symmetry and mol.symmetry: x2[sym_forbid] = 0 return x2.ravel() return hop_real2complex, hdiag, hop_rhf2uhf, hdiag
def analyze(tdobj, verbose=None): log = logger.new_logger(tdobj, verbose) mol = tdobj.mol mo_coeff = tdobj._scf.mo_coeff mo_occ = tdobj._scf.mo_occ nocc = numpy.count_nonzero(mo_occ == 2) e_ev = numpy.asarray(tdobj.e) * nist.HARTREE2EV e_wn = numpy.asarray(tdobj.e) * nist.HARTREE2WAVENUMBER wave_length = 1e11/e_wn if tdobj.singlet: log.note('\n** Singlet excitation energies and oscillator strengths **') else: log.note('\n** Triplet excitation energies and oscillator strengths **') if mol.symmetry: orbsym = hf_symm.get_orbsym(mol, mo_coeff) % 10 x_sym = (orbsym[mo_occ==0,None] ^ orbsym[mo_occ==2]).ravel() else: x_sym = None f_oscillator = tdobj.oscillator_strength() for i, ei in enumerate(tdobj.e): x, y = tdobj.xy[i] if x_sym is None: log.note('Excited State %3d: %12.5f eV %9.2f nm f=%.4f', i+1, e_ev[i], wave_length[i], f_oscillator[i]) else: wfnsym_id = x_sym[abs(x).argmax()] wfnsym = symm.irrep_id2name(mol.groupname, wfnsym_id) log.note('Excited State %3d: %4s %12.5f eV %9.2f nm f=%.4f', i+1, wfnsym, e_ev[i], wave_length[i], f_oscillator[i]) if log.verbose >= logger.INFO: o_idx, v_idx = numpy.where(abs(x) > 0.1) for o, v in zip(o_idx, v_idx): log.info(' %4d -> %-4d %12.5f', o+MO_BASE, v+MO_BASE+nocc, x[o,v]) if log.verbose >= logger.INFO: log.info('\n** Transition electric dipole moments (AU) **') log.info('state X Y Z Dip. S. Osc.') trans_dip = tdobj.transition_dipole() for i, ei in enumerate(tdobj.e): dip = trans_dip[i] log.info('%3d %11.4f %11.4f %11.4f %11.4f %11.4f', i+1, dip[0], dip[1], dip[2], numpy.dot(dip, dip), f_oscillator[i]) log.info('\n** Transition velocity dipole moments (imaginary part AU) **') log.info('state X Y Z Dip. S. Osc.') trans_v = tdobj.transition_velocity_dipole() f_v = tdobj.oscillator_strength(gauge='velocity', order=0) for i, ei in enumerate(tdobj.e): v = trans_v[i] log.info('%3d %11.4f %11.4f %11.4f %11.4f %11.4f', i+1, v[0], v[1], v[2], numpy.dot(v, v), f_v[i]) log.info('\n** Transition magnetic dipole moments (AU) **') log.info('state X Y Z') trans_m = tdobj.transition_magnetic_dipole() for i, ei in enumerate(tdobj.e): m = trans_m[i] log.info('%3d %11.4f %11.4f %11.4f', i+1, m[0], m[1], m[2]) return tdobj
def gen_tda_operation(mf, fock_ao=None, singlet=True, wfnsym=None): '''Generate function to compute (A+B)x Kwargs: wfnsym : int or str Point group symmetry irrep symbol or ID for excited CIS wavefunction. ''' mol = mf.mol mo_coeff = mf.mo_coeff assert(mo_coeff.dtype == numpy.double) mo_energy = mf.mo_energy mo_occ = mf.mo_occ nao, nmo = mo_coeff.shape occidx = numpy.where(mo_occ==2)[0] viridx = numpy.where(mo_occ==0)[0] nocc = len(occidx) nvir = len(viridx) orbv = mo_coeff[:,viridx] orbo = mo_coeff[:,occidx] if wfnsym is not None and mol.symmetry: if isinstance(wfnsym, str): wfnsym = symm.irrep_name2id(mol.groupname, wfnsym) wfnsym = wfnsym % 10 # convert to D2h subgroup orbsym = hf_symm.get_orbsym(mol, mo_coeff) % 10 sym_forbid = (orbsym[occidx,None] ^ orbsym[viridx]) != wfnsym if fock_ao is None: #dm0 = mf.make_rdm1(mo_coeff, mo_occ) #fock_ao = mf.get_hcore() + mf.get_veff(mol, dm0) foo = numpy.diag(mo_energy[occidx]) fvv = numpy.diag(mo_energy[viridx]) else: fock = reduce(numpy.dot, (mo_coeff.conj().T, fock_ao, mo_coeff)) foo = fock[occidx[:,None],occidx] fvv = fock[viridx[:,None],viridx] hdiag = (fvv.diagonal().reshape(-1,1) - foo.diagonal()).T if wfnsym is not None and mol.symmetry: hdiag[sym_forbid] = 0 hdiag = hdiag.ravel() mo_coeff = numpy.asarray(numpy.hstack((orbo,orbv)), order='F') vresp = _gen_rhf_response(mf, singlet=singlet, hermi=0) def vind(zs): nz = len(zs) if wfnsym is not None and mol.symmetry: zs = numpy.copy(zs).reshape(-1,nocc,nvir) zs[:,sym_forbid] = 0 dmov = numpy.empty((nz,nao,nao)) for i, z in enumerate(zs): # *2 for double occupancy dmov[i] = reduce(numpy.dot, (orbo, z.reshape(nocc,nvir)*2, orbv.conj().T)) v1ao = vresp(dmov) #v1ov = numpy.asarray([reduce(numpy.dot, (orbo.T, v, orbv)) for v in v1ao]) v1ov = _ao2mo.nr_e2(v1ao, mo_coeff, (0,nocc,nocc,nmo)).reshape(-1,nocc,nvir) for i, z in enumerate(zs): v1ov[i]+= numpy.einsum('sp,qs->qp', fvv, z.reshape(nocc,nvir)) v1ov[i]-= numpy.einsum('sp,pr->sr', foo, z.reshape(nocc,nvir)) if wfnsym is not None and mol.symmetry: v1ov[:,sym_forbid] = 0 return v1ov.reshape(nz,-1) return vind, hdiag
def gen_tdhf_operation(mf, fock_ao=None, singlet=True, wfnsym=None): '''Generate function to compute [ A B][X] [-B -A][Y] ''' mol = mf.mol mo_coeff = mf.mo_coeff assert(mo_coeff.dtype == numpy.double) mo_energy = mf.mo_energy mo_occ = mf.mo_occ nao, nmo = mo_coeff.shape occidx = numpy.where(mo_occ==2)[0] viridx = numpy.where(mo_occ==0)[0] nocc = len(occidx) nvir = len(viridx) orbv = mo_coeff[:,viridx] orbo = mo_coeff[:,occidx] if wfnsym is not None and mol.symmetry: if isinstance(wfnsym, str): wfnsym = symm.irrep_name2id(mol.groupname, wfnsym) wfnsym = wfnsym % 10 # convert to D2h subgroup orbsym = hf_symm.get_orbsym(mol, mo_coeff) % 10 sym_forbid = (orbsym[occidx,None] ^ orbsym[viridx]) != wfnsym #dm0 = mf.make_rdm1(mo_coeff, mo_occ) #fock_ao = mf.get_hcore() + mf.get_veff(mol, dm0) #fock = reduce(numpy.dot, (mo_coeff.T, fock_ao, mo_coeff)) #foo = fock[occidx[:,None],occidx] #fvv = fock[viridx[:,None],viridx] foo = numpy.diag(mo_energy[occidx]) fvv = numpy.diag(mo_energy[viridx]) hdiag = (fvv.diagonal().reshape(-1,1) - foo.diagonal()).T if wfnsym is not None and mol.symmetry: hdiag[sym_forbid] = 0 hdiag = numpy.hstack((hdiag.ravel(), hdiag.ravel())) mo_coeff = numpy.asarray(numpy.hstack((orbo,orbv)), order='F') vresp = _gen_rhf_response(mf, singlet=singlet, hermi=0) def vind(xys): nz = len(xys) if wfnsym is not None and mol.symmetry: # shape(nz,2,nocc,nvir): 2 ~ X,Y xys = numpy.copy(xys).reshape(nz,2,nocc,nvir) xys[:,:,sym_forbid] = 0 dms = numpy.empty((nz,nao,nao)) for i in range(nz): x, y = xys[i].reshape(2,nocc,nvir) # *2 for double occupancy dmx = reduce(numpy.dot, (orbo, x*2, orbv.T)) dmy = reduce(numpy.dot, (orbv, y.T*2, orbo.T)) dms[i] = dmx + dmy # AX + BY v1ao = vresp(dms) v1ov = _ao2mo.nr_e2(v1ao, mo_coeff, (0,nocc,nocc,nmo)).reshape(-1,nocc,nvir) v1vo = _ao2mo.nr_e2(v1ao, mo_coeff, (nocc,nmo,0,nocc)).reshape(-1,nvir,nocc) hx = numpy.empty((nz,2,nocc,nvir), dtype=v1ov.dtype) for i in range(nz): x, y = xys[i].reshape(2,nocc,nvir) hx[i,0] = v1ov[i] hx[i,0]+= numpy.einsum('sp,qs->qp', fvv, x) # AX hx[i,0]-= numpy.einsum('sp,pr->sr', foo, x) # AX hx[i,1] =-v1vo[i].T hx[i,1]-= numpy.einsum('sp,qs->qp', fvv, y) #-AY hx[i,1]+= numpy.einsum('sp,pr->sr', foo, y) #-AY if wfnsym is not None and mol.symmetry: hx[:,:,sym_forbid] = 0 return hx.reshape(nz,-1) return vind, hdiag
def analyze(tdobj, verbose=None): log = logger.new_logger(tdobj, verbose) mol = tdobj.mol mo_coeff = tdobj._scf.mo_coeff mo_occ = tdobj._scf.mo_occ nocc = numpy.count_nonzero(mo_occ == 2) e_ev = numpy.asarray(tdobj.e) * nist.HARTREE2EV e_wn = numpy.asarray(tdobj.e) * nist.HARTREE2WAVENUMBER wave_length = 1e7 / e_wn if tdobj.singlet: log.note( '\n** Singlet excitation energies and oscillator strengths **') else: log.note( '\n** Triplet excitation energies and oscillator strengths **') if mol.symmetry: orbsym = hf_symm.get_orbsym(mol, mo_coeff) x_sym = symm.direct_prod(orbsym[mo_occ == 2], orbsym[mo_occ == 0], mol.groupname) else: x_sym = None f_oscillator = tdobj.oscillator_strength() for i, ei in enumerate(tdobj.e): x, y = tdobj.xy[i] if x_sym is None: log.note('Excited State %3d: %12.5f eV %9.2f nm f=%.4f', i + 1, e_ev[i], wave_length[i], f_oscillator[i]) else: wfnsym = analyze_wfnsym(tdobj, x_sym, x) log.note('Excited State %3d: %4s %12.5f eV %9.2f nm f=%.4f', i + 1, wfnsym, e_ev[i], wave_length[i], f_oscillator[i]) if log.verbose >= logger.INFO: o_idx, v_idx = numpy.where(abs(x) > 0.1) for o, v in zip(o_idx, v_idx): log.info(' %4d -> %-4d %12.5f', o + MO_BASE, v + MO_BASE + nocc, x[o, v]) if log.verbose >= logger.INFO: log.info('\n** Transition electric dipole moments (AU) **') log.info( 'state X Y Z Dip. S. Osc.' ) trans_dip = tdobj.transition_dipole() for i, ei in enumerate(tdobj.e): dip = trans_dip[i] log.info('%3d %11.4f %11.4f %11.4f %11.4f %11.4f', i + 1, dip[0], dip[1], dip[2], numpy.dot(dip, dip), f_oscillator[i]) log.info( '\n** Transition velocity dipole moments (imaginary part, AU) **') log.info( 'state X Y Z Dip. S. Osc.' ) trans_v = tdobj.transition_velocity_dipole() f_v = tdobj.oscillator_strength(gauge='velocity', order=0) for i, ei in enumerate(tdobj.e): v = trans_v[i] log.info('%3d %11.4f %11.4f %11.4f %11.4f %11.4f', i + 1, v[0], v[1], v[2], numpy.dot(v, v), f_v[i]) log.info( '\n** Transition magnetic dipole moments (imaginary part, AU) **') log.info('state X Y Z') trans_m = tdobj.transition_magnetic_dipole() for i, ei in enumerate(tdobj.e): m = trans_m[i] log.info('%3d %11.4f %11.4f %11.4f', i + 1, m[0], m[1], m[2]) return tdobj
def rotate_mo(self, mo_coeff, u, log=None): mo = numpy.dot(mo_coeff, u) if self._scf.mol.symmetry: orbsym = hf_symm.get_orbsym(self._scf.mol, mo_coeff) mo = lib.tag_array(mo, orbsym=orbsym) return mo
def gen_tda_operation(mf, fock_ao=None, singlet=True, wfnsym=None): '''Generate function to compute (A+B)x Kwargs: wfnsym : int or str Point group symmetry irrep symbol or ID for excited CIS wavefunction. ''' mol = mf.mol mo_coeff = mf.mo_coeff assert (mo_coeff.dtype == numpy.double) mo_energy = mf.mo_energy mo_occ = mf.mo_occ nao, nmo = mo_coeff.shape occidx = numpy.where(mo_occ == 2)[0] viridx = numpy.where(mo_occ == 0)[0] nocc = len(occidx) nvir = len(viridx) orbv = mo_coeff[:, viridx] orbo = mo_coeff[:, occidx] if wfnsym is not None and mol.symmetry: if isinstance(wfnsym, str): wfnsym = symm.irrep_name2id(mol.groupname, wfnsym) wfnsym = wfnsym % 10 # convert to D2h subgroup orbsym = hf_symm.get_orbsym(mol, mo_coeff) orbsym_in_d2h = numpy.asarray(orbsym) % 10 # convert to D2h irreps sym_forbid = (orbsym_in_d2h[occidx, None] ^ orbsym_in_d2h[viridx]) != wfnsym if fock_ao is None: #dm0 = mf.make_rdm1(mo_coeff, mo_occ) #fock_ao = mf.get_hcore() + mf.get_veff(mol, dm0) foo = numpy.diag(mo_energy[occidx]) fvv = numpy.diag(mo_energy[viridx]) else: fock = reduce(numpy.dot, (mo_coeff.conj().T, fock_ao, mo_coeff)) foo = fock[occidx[:, None], occidx] fvv = fock[viridx[:, None], viridx] hdiag = fvv.diagonal() - foo.diagonal()[:, None] if wfnsym is not None and mol.symmetry: hdiag[sym_forbid] = 0 hdiag = hdiag.ravel() mo_coeff = numpy.asarray(numpy.hstack((orbo, orbv)), order='F') vresp = mf.gen_response(singlet=singlet, hermi=0) def vind(zs): zs = numpy.asarray(zs).reshape(-1, nocc, nvir) if wfnsym is not None and mol.symmetry: zs = numpy.copy(zs) zs[:, sym_forbid] = 0 # *2 for double occupancy dmov = lib.einsum('xov,po,qv->xpq', zs * 2, orbo, orbv.conj()) v1ao = vresp(dmov) v1ov = lib.einsum('xpq,po,qv->xov', v1ao, orbo.conj(), orbv) v1ov += lib.einsum('xqs,sp->xqp', zs, fvv) v1ov -= lib.einsum('xpr,sp->xsr', zs, foo) if wfnsym is not None and mol.symmetry: v1ov[:, sym_forbid] = 0 return v1ov.reshape(v1ov.shape[0], -1) return vind, hdiag
def gen_tda_operation(mf, fock_ao=None, singlet=True, wfnsym=None): '''Generate function to compute (A+B)x Kwargs: wfnsym : int or str Point group symmetry irrep symbol or ID for excited CIS wavefunction. ''' mol = mf.mol mo_coeff = mf.mo_coeff assert(mo_coeff.dtype == numpy.double) mo_energy = mf.mo_energy mo_occ = mf.mo_occ nao, nmo = mo_coeff.shape occidx = numpy.where(mo_occ==2)[0] viridx = numpy.where(mo_occ==0)[0] nocc = len(occidx) nvir = len(viridx) orbv = mo_coeff[:,viridx] orbo = mo_coeff[:,occidx] if wfnsym is not None and mol.symmetry: if isinstance(wfnsym, str): wfnsym = symm.irrep_name2id(mol.groupname, wfnsym) wfnsym = wfnsym % 10 # convert to D2h subgroup orbsym = hf_symm.get_orbsym(mol, mo_coeff) % 10 sym_forbid = (orbsym[occidx,None] ^ orbsym[viridx]) != wfnsym if fock_ao is None: #dm0 = mf.make_rdm1(mo_coeff, mo_occ) #fock_ao = mf.get_hcore() + mf.get_veff(mol, dm0) foo = numpy.diag(mo_energy[occidx]) fvv = numpy.diag(mo_energy[viridx]) else: fock = reduce(numpy.dot, (mo_coeff.conj().T, fock_ao, mo_coeff)) foo = fock[occidx[:,None],occidx] fvv = fock[viridx[:,None],viridx] hdiag = (fvv.diagonal().reshape(-1,1) - foo.diagonal()).T if wfnsym is not None and mol.symmetry: hdiag[sym_forbid] = 0 hdiag = hdiag.ravel() mo_coeff = numpy.asarray(numpy.hstack((orbo,orbv)), order='F') vresp = _gen_rhf_response(mf, singlet=singlet, hermi=0) def vind(zs): nz = len(zs) if wfnsym is not None and mol.symmetry: zs = numpy.copy(zs).reshape(-1,nocc,nvir) zs[:,sym_forbid] = 0 dmov = numpy.empty((nz,nao,nao)) for i, z in enumerate(zs): # *2 for double occupancy dmov[i] = reduce(numpy.dot, (orbo, z.reshape(nocc,nvir)*2, orbv.conj().T)) v1ao = vresp(dmov) #v1ov = numpy.asarray([reduce(numpy.dot, (orbo.T, v, orbv)) for v in v1ao]) v1ov = _ao2mo.nr_e2(v1ao, mo_coeff, (0,nocc,nocc,nmo)).reshape(-1,nocc,nvir) for i, z in enumerate(zs): v1ov[i]+= numpy.einsum('sp,qs->qp', fvv, z.reshape(nocc,nvir)) v1ov[i]-= numpy.einsum('sp,pr->sr', foo, z.reshape(nocc,nvir)) if wfnsym is not None and mol.symmetry: v1ov[:,sym_forbid] = 0 return v1ov.reshape(nz,-1) return vind, hdiag
def gen_tdhf_operation(mf, fock_ao=None, singlet=True, wfnsym=None): '''Generate function to compute [ A B][X] [-B -A][Y] ''' mol = mf.mol mo_coeff = mf.mo_coeff assert (mo_coeff.dtype == numpy.double) mo_energy = mf.mo_energy mo_occ = mf.mo_occ nao, nmo = mo_coeff.shape occidx = numpy.where(mo_occ == 2)[0] viridx = numpy.where(mo_occ == 0)[0] nocc = len(occidx) nvir = len(viridx) orbv = mo_coeff[:, viridx] orbo = mo_coeff[:, occidx] if wfnsym is not None and mol.symmetry: if isinstance(wfnsym, str): wfnsym = symm.irrep_name2id(mol.groupname, wfnsym) wfnsym = wfnsym % 10 # convert to D2h subgroup orbsym = hf_symm.get_orbsym(mol, mo_coeff) orbsym_in_d2h = numpy.asarray(orbsym) % 10 # convert to D2h irreps sym_forbid = (orbsym_in_d2h[occidx, None] ^ orbsym_in_d2h[viridx]) != wfnsym #dm0 = mf.make_rdm1(mo_coeff, mo_occ) #fock_ao = mf.get_hcore() + mf.get_veff(mol, dm0) #fock = reduce(numpy.dot, (mo_coeff.T, fock_ao, mo_coeff)) #foo = fock[occidx[:,None],occidx] #fvv = fock[viridx[:,None],viridx] foo = numpy.diag(mo_energy[occidx]) fvv = numpy.diag(mo_energy[viridx]) hdiag = fvv.diagonal() - foo.diagonal()[:, None] if wfnsym is not None and mol.symmetry: hdiag[sym_forbid] = 0 hdiag = numpy.hstack((hdiag.ravel(), -hdiag.ravel())) mo_coeff = numpy.asarray(numpy.hstack((orbo, orbv)), order='F') vresp = mf.gen_response(singlet=singlet, hermi=0) def vind(xys): xys = numpy.asarray(xys).reshape(-1, 2, nocc, nvir) if wfnsym is not None and mol.symmetry: # shape(nz,2,nocc,nvir): 2 ~ X,Y xys = numpy.copy(xys) xys[:, :, sym_forbid] = 0 xs, ys = xys.transpose(1, 0, 2, 3) # dms = AX + BY # *2 for double occupancy dms = lib.einsum('xov,po,qv->xpq', xs * 2, orbo, orbv.conj()) dms += lib.einsum('xov,pv,qo->xpq', ys * 2, orbv, orbo.conj()) v1ao = vresp(dms) v1ov = lib.einsum('xpq,po,qv->xov', v1ao, orbo.conj(), orbv) v1vo = lib.einsum('xpq,pv,qo->xov', v1ao, orbv.conj(), orbo) v1ov += lib.einsum('xqs,sp->xqp', xs, fvv) # AX v1ov -= lib.einsum('xpr,sp->xsr', xs, foo) # AX v1vo += lib.einsum('xqs,sp->xqp', ys, fvv) # AY v1vo -= lib.einsum('xpr,sp->xsr', ys, foo) # AY if wfnsym is not None and mol.symmetry: v1ov[:, sym_forbid] = 0 v1vo[:, sym_forbid] = 0 # (AX, -AY) nz = xys.shape[0] hx = numpy.hstack((v1ov.reshape(nz, -1), -v1vo.reshape(nz, -1))) return hx return vind, hdiag
def gen_vind(self, mf): ''' [ A B][X] [-B -A][Y] ''' wfnsym = self.wfnsym singlet = self.singlet mol = mf.mol mo_coeff = mf.mo_coeff assert (mo_coeff.dtype == numpy.double) mo_energy = mf.mo_energy mo_occ = mf.mo_occ nao, nmo = mo_coeff.shape occidx = numpy.where(mo_occ == 2)[0] viridx = numpy.where(mo_occ == 0)[0] nocc = len(occidx) nvir = len(viridx) orbv = mo_coeff[:, viridx] orbo = mo_coeff[:, occidx] if wfnsym is not None and mol.symmetry: orbsym = hf_symm.get_orbsym(mol, mo_coeff) sym_forbid = (orbsym[viridx].reshape(-1, 1) ^ orbsym[occidx]) != wfnsym #dm0 = mf.make_rdm1(mo_coeff, mo_occ) #fock_ao = mf.get_hcore() + mf.get_veff(mol, dm0) #fock = reduce(numpy.dot, (mo_coeff.T, fock_ao, mo_coeff)) #foo = fock[occidx[:,None],occidx] #fvv = fock[viridx[:,None],viridx] foo = numpy.diag(mo_energy[occidx]) fvv = numpy.diag(mo_energy[viridx]) e_ai = hdiag = fvv.diagonal().reshape(-1, 1) - foo.diagonal() if wfnsym is not None and mol.symmetry: hdiag[sym_forbid] = 0 hdiag = numpy.hstack((hdiag.ravel(), hdiag.ravel())) mo_coeff = numpy.asarray(numpy.hstack((orbo, orbv)), order='F') vresp = _gen_rhf_response(mf, singlet=singlet, hermi=0) def vind(xys): nz = len(xys) if wfnsym is not None and mol.symmetry: # shape(nz,2,nvir,nocc): 2 ~ X,Y xys = numpy.copy(zs).reshape(nz, 2, nvir, nocc) xys[:, :, sym_forbid] = 0 dms = numpy.empty((nz, nao, nao)) for i in range(nz): x, y = xys[i].reshape(2, nvir, nocc) # *2 for double occupancy dmx = reduce(numpy.dot, (orbv, x * 2, orbo.T)) dmy = reduce(numpy.dot, (orbo, y.T * 2, orbv.T)) dms[i] = dmx + dmy # AX + BY v1ao = vresp(dms) v1vo = _ao2mo.nr_e2(v1ao, mo_coeff, (nocc, nmo, 0, nocc)).reshape(-1, nvir, nocc) v1ov = _ao2mo.nr_e2(v1ao, mo_coeff, (0, nocc, nocc, nmo)) v1ov = v1ov.reshape(-1, nocc, nvir).transpose(0, 2, 1) hx = numpy.empty((nz, 2, nvir, nocc), dtype=v1vo.dtype) for i in range(nz): x, y = xys[i].reshape(2, nvir, nocc) hx[i, 0] = v1vo[i] hx[i, 0] += numpy.einsum('ps,sq->pq', fvv, x) # AX hx[i, 0] -= numpy.einsum('ps,rp->rs', foo, x) # AX hx[i, 1] = -v1ov[i] hx[i, 1] -= numpy.einsum('ps,sq->pq', fvv, y) #-AY hx[i, 1] += numpy.einsum('ps,rp->rs', foo, y) #-AY if wfnsym is not None and mol.symmetry: hx[:, :, sym_forbid] = 0 return hx.reshape(nz, -1) return vind, hdiag