def deriv(self, t, yt): rho, q, p = self.unpack(yt) cq = np.einsum('nk,nk->n',self._c,q) ham_t = self.ham.sys + np.einsum('nab,n->ab',self._hamsb,cq) F_avg = np.einsum('nab,ba->n',self._hamsb,rho).real dq = p.copy() dp = - self._omegasq*q \ - np.einsum('nk,n->nk',self._c,F_avg) drho = -1j/const.hbar * utils.commutator(ham_t,rho) return self.pack(drho, dq, dp)
def deriv(self, t, yt): rho, q, p = self.unpack(yt) cq = np.einsum('nk,nk->n', self._c, q) ham_t = np.einsum('nab,n->ab', self._hamsb, cq) ham_t = self.ham.to_interaction(self.ham.site2eig(ham_t), t) hamsb_int = list() for n, hamsb in enumerate(self._hamsb): hamsb_int_n = self.ham.to_interaction(self.ham.site2eig(hamsb), t) hamsb_int.append(hamsb_int_n) hamsb_int = np.array(hamsb_int) F_avg = np.einsum('nab,ba->n', hamsb_int, rho).real dq = p.copy() dp = - self._omegasq*q \ - np.einsum('nk,n->nk',self._c,F_avg) drho = -1j / const.hbar * utils.commutator(ham_t, rho) return self.pack(drho, dq, dp)
def heom_deriv(self, t, rho): hbar = const.hbar drho = [] if self.ham.sd[0].sd_type == 'ohmic-lorentz': # Chen Eq. (15) cjk_over_gammajk = np.einsum('jk,jk->j', self.c, 1./self.gamma) for n, nmat in enumerate(self.nmats): L = np.sum(nmat) rho_n = rho[n] drho_n = -1j/hbar * utils.commutator(self.ham.sys,rho_n) njk_gammajk = np.sum(nmat*self.gamma) for j in range(self.ham.nbath): Fj = self.ham.sysbath[j] # this is like |j><j| lamdaj = self.ham.sd[j].lamda omega_cj = self.ham.sd[j].omega_c for k in range(self.Kmax+1): if self.is_scaled: scale_minus = np.sqrt(nmat[j,k]/np.abs(self.c[j,k])) else: scale_minus = nmat[j,k] rho_njkminus = self.rho_minus(rho,n,j,k) drho_n -= ( (1j/hbar)*scale_minus *(self.c[j,k]*np.dot(Fj,rho_njkminus) -self.c[j,k].conjugate()*np.dot(rho_njkminus,Fj)) ) # Note: The real part is taken because the Ishizaki-Tanimura # truncation yields a sum over excluded Matsubara frequencies, # which are purely real; the difference between the *real* # finite K expansion and 2 lamda kT / omega_c gives the # neglected contribution (in Markovian "TNL" approximation). if self.truncation_type == 'Ishizaki-Tanimura': drho_n -= ( (2*lamdaj*const.kT/(hbar**2*omega_cj) - cjk_over_gammajk[j]/hbar).real *utils.commutator(Fj, utils.commutator(Fj, rho_n)) ) if L < self.Lmax: # If L == self.Lmax, then rho_nkplus = 0 rho_njkplus_sum = np.zeros_like(rho_n) for k in range(self.Kmax+1): if self.is_scaled: scale_plus = np.sqrt((nmat[j,k]+1)*np.abs(self.c[j,k])) else: scale_plus = 1. rho_njkplus_sum += scale_plus*self.rho_plus(rho,n,j,k) drho_n -= 1j*utils.commutator(Fj, rho_njkplus_sum) drho_n -= njk_gammajk*rho_n drho.append(drho_n) # TODO(TCB): Finish this, i.e. HEOM for single-oscillator(s) # elif self.ham.sd[0].sd_type == 'oscillators': # # Liu App. C, Eq. (C1) # for m in range(len(self.nmats)): # mmat = self.nmats[m] # Lm = np.sum(mmat) # for n in range(len(self.nmats)): # nmat = self.nmats[n] # Ln = np.sum(nmat) # rho_mn = rho[m,n] # drho_mn = np.zeros_like(rho_mn) # drho_mn = -1j/hbar * utils.commutator(self.ham.sys,rho_mn) # njk_gammajk = 0. # # # more here # drho.append(drho_mn) return np.array(drho)
def heom_deriv(self, t, rho): hbar = const.hbar drho = list() if self.ham.sd[0].sd_type == 'ohmic-lorentz': # Chen Eq. (15) cjk_over_gammajk = np.einsum('jk,jk->j', self.c, 1. / self.gamma) for n, nmat in enumerate(self.nmats): L = np.sum(nmat) rho_n = rho[n] drho_n = -1j / hbar * utils.commutator(self.ham.sys, rho_n) njk_gammajk = np.sum(nmat * self.gamma) for j in range(self.ham.nbath): Fj = self.ham.sysbath[j] # this is like |j><j| lamdaj = self.ham.sd[j].lamda omega_cj = self.ham.sd[j].omega_c for k in range(self.Kmax + 1): if self.is_scaled: scale_minus = np.sqrt(nmat[j, k] / np.abs(self.c[j, k])) else: scale_minus = nmat[j, k] rho_njkminus = self.rho_minus(rho, n, j, k) drho_n -= ((1j / hbar) * scale_minus * (self.c[j, k] * np.dot(Fj, rho_njkminus) - self.c[j, k].conjugate() * np.dot(rho_njkminus, Fj))) # Note: The real part is taken because the Ishizaki-Tanimura # truncation yields a sum over excluded Matsubara frequencies, # which are purely real; the difference between the *real* # finite K expansion and 2 lamda kT / omega_c gives the # neglected contribution (in Markovian "TNL" approximation). if self.K_truncation == 'Ishizaki-Tanimura': drho_n -= ( (2 * lamdaj * const.kT / (hbar**2 * omega_cj) - cjk_over_gammajk[j] / hbar).real * utils.commutator(Fj, utils.commutator(Fj, rho_n))) if L < self.Lmax: # If L == self.Lmax, then rho_nkplus = 0 rho_njkplus_sum = np.zeros_like(rho_n) for k in range(self.Kmax + 1): if self.is_scaled: scale_plus = np.sqrt( (nmat[j, k] + 1) * np.abs(self.c[j, k])) else: scale_plus = 1. rho_njkplus_sum += scale_plus * self.rho_plus( rho, n, j, k) drho_n -= 1j * utils.commutator(Fj, rho_njkplus_sum) if self.L_truncation == 'TL' and L == self.Lmax: E, evec = utils.diagonalize(self.ham.sys) Q_j = np.zeros((self.ham.nsite, self.ham.nsite), dtype=np.complex) for a in range(self.ham.nsite): aouter = np.outer(evec[:, a], evec[:, a]) for b in range(self.ham.nsite): bouter = np.outer(evec[:, b], evec[:, b]) if self.is_scaled: num = np.sqrt(nmat[j, k] + 1) * self.c[ j, :] * (1. - np.exp( -self.gamma[j, :] * t - 1j * self.ham.omega_diff[a, b] * t)) else: num = self.c[j, :] * (1. - np.exp( -self.gamma[j, :] * t - 1j * self.ham.omega_diff[a, b] * t)) denom = self.gamma[ j, :] + 1j * self.ham.omega_diff[a, b] Q_j += np.sum(num / denom) * np.dot( aouter, np.dot(self.ham.sysbath[j], bouter)) rho_njkplus_sum = -(1j / hbar) * ( np.dot(Q_j, rho_n) - np.dot(rho_n, np.conj(Q_j).T)) drho_n -= 1j * utils.commutator(Fj, rho_njkplus_sum) # for k in range(self.Kmax+1): # if self.is_scaled: # scale_plus = np.sqrt((nmat[j,k]+1)*np.abs(self.c[j,k])) # else: # scale_plus = 1. # rho_njkplus_sum += scale_plus*self.rho_plus(rho,n,j,k) # # drho_n -= njk_gammajk * rho_n drho.append(drho_n) # TODO(TCB): Finish this, i.e. HEOM for single-oscillator(s) # elif self.ham.sd[0].sd_type == 'oscillators': # # Liu App. C, Eq. (C1) # for m in range(len(self.nmats)): # mmat = self.nmats[m] # Lm = np.sum(mmat) # for n in range(len(self.nmats)): # nmat = self.nmats[n] # Ln = np.sum(nmat) # rho_mn = rho[m,n] # drho_mn = np.zeros_like(rho_mn) # drho_mn = -1j/hbar * utils.commutator(self.ham.sys,rho_mn) # njk_gammajk = 0. # # # more here # drho.append(drho_mn) return np.array(drho)
def heom_deriv(self, t, rho): hbar = const.hbar drho = list() if self.ham.sd[0].sd_type == 'ohmic-lorentz': # Chen Eq. (15) cjk_over_gammajk_re = np.einsum('jk,jk->j', self.c, 1./self.gamma).real for n, nmat in enumerate(self.nmats): L = np.sum(nmat) rho_n = rho[n] drho_n = -1j/hbar * utils.commutator(self.ham.sys,rho_n) drho_n -= np.sum(nmat*self.gamma)*rho_n for j in range(self.ham.nbath): Fj = self.ham.sysbath[j] # this is like |j><j| lamdaj = self.ham.sd[j].lamda omega_cj = self.ham.sd[j].omega_c if self.K_truncation == 'Ishizaki-Tanimura': drho_n -= (1./hbar**2)*( (2*lamdaj*const.kT/omega_cj - cjk_over_gammajk_re[j]) *utils.commutator(Fj, utils.commutator(Fj, rho_n)) ) if L < self.Lmax: # If L == self.Lmax, then rho_nkplus = 0 rho_njkplus_sum = np.zeros_like(rho_n) for k in range(self.Nk): rho_njkplus_sum += self.rho_plus(rho,n,j,k) drho_n -= 1j*utils.commutator(Fj, rho_njkplus_sum) if self.L_truncation == 'TL' and L==self.Lmax: E, evec = utils.diagonalize(self.ham.sys) Q_j=np.zeros((self.ham.nsite,self.ham.nsite),dtype=np.complex) for a in range(self.ham.nsite): aouter = np.outer(evec[:,a],evec[:,a]) for b in range(self.ham.nsite): bouter = np.outer(evec[:,b],evec[:,b]) num = self.c[j,:]*(1.-np.exp(-self.gamma[j,:]*t -1j*self.ham.omega_diff[a,b]*t)) denom = self.gamma[j,:]+1j*self.ham.omega_diff[a,b] Q_j += np.sum(num/denom)*np.dot(aouter,np.dot(self.ham.sysbath[j],bouter)) rho_njkplus_sum = -(1j/hbar)*(np.dot(Q_j,rho_n)-np.dot(rho_n,np.conj(Q_j).T)) drho_n -= 1j*utils.commutator(Fj, rho_njkplus_sum) for k in range(self.Nk): rho_njkminus = self.rho_minus(rho,n,j,k) drho_n -= (1j/hbar**2)*( nmat[j,k] *(self.c[j,k]*np.dot(Fj,rho_njkminus) -self.c[j,k].conjugate()*np.dot(rho_njkminus,Fj)) ) drho.append(drho_n) elif self.ham.sd[0].sd_type == 'ohmic-exp' or self.ham.sd[0].sd_type == 'cubic-exp': # Chen Eq. (15) beta = 1./const.kT cjk_over_gammajk_re = np.einsum('jk,jk->j', self.c, 1./self.gamma).real for nab, nabmat in enumerate(self.nmats): L = np.sum(nabmat) nmat = nabmat[:,:self.Nk] amat = nabmat[:,self.Nk:self.Nk+self.nlorentz] bmat = nabmat[:,self.Nk+self.nlorentz:self.Nk+2*self.nlorentz] rho_nab = rho[nab] drho_nab = -1j/hbar * utils.commutator(self.ham.sys,rho_nab) drho_nab -= np.sum((amat+bmat)*self.Gamma)*rho_nab drho_nab -= 1j*np.sum((amat-bmat)*self.Omega)*rho_nab drho_nab -= np.sum(nmat*self.gamma)*rho_nab for j in range(self.ham.nbath): Fj = self.ham.sysbath[j] # this is like |j><j| lamdaj = self.ham.sd[j].lamda omega_cj = self.ham.sd[j].omega_c #if self.truncation_type == 'Ishizaki-Tanimura': # drho_nab -= (1./hbar**2)*( # (2*lamdaj*const.kT/omega_cj - cjk_over_gammajk_re[j]) # *utils.commutator(Fj, utils.commutator(Fj, rho_nab)) ) if L < self.Lmax: # If L == self.Lmax, then rho_nabkplus = 0 rho_nabjkplus_sum = np.zeros_like(rho_nab) for k in range(self.Nk+2*self.nlorentz): rho_nabjkplus_sum += self.rho_plus(rho,nab,j,k) drho_nab -= 1j*utils.commutator(Fj, rho_nabjkplus_sum) for k in range(self.Nk): rho_nabjkminus = self.rho_minus(rho,nab,j,k) drho_nab -= (1j/hbar**2)*( nmat[j,k]*self.c[j,k] *utils.commutator(Fj, rho_nabjkminus) ) for l in range(self.nlorentz): rho_nabjkminus = self.rho_minus(rho,nab,j,self.Nk+l) drho_nab -= (1j/hbar)*( amat[j,l] * self.p[j,l]/(8*self.Omega[j,l]*self.Gamma[j,l]) * (self._coth_b_OminusG[j,l] * utils.commutator(Fj, rho_nabjkminus) + utils.anticommutator(Fj, rho_nabjkminus)) ) for l in range(self.nlorentz): rho_nabjkminus = self.rho_minus(rho,nab,j,self.Nk+self.nlorentz+l) drho_nab -= (1j/hbar)*( bmat[j,l] * self.p[j,l]/(8*self.Omega[j,l]*self.Gamma[j,l]) * (self._coth_b_OplusG[j,l] * utils.commutator(Fj, rho_nabjkminus) - utils.anticommutator(Fj, rho_nabjkminus)) ) drho.append(drho_nab) return np.array(drho)