class DQDHEOMModelSparse(): def __init__(self, Gamma_L, Gamma_R, bias, T_c, environment=[], beta=1., K=0, tc=True, trunc_level=5, \ dissipator_test=False): self.system_dimension = 3 self.bias = bias self.T_c = T_c self.Gamma_L = Gamma_L self.Gamma_R = Gamma_R self.jump_operators = np.array([np.array([[0, 0, 0], [1., 0, 0], [0, 0, 0]]), np.array([[0, 0, 1.], [0, 0, 0], [0, 0, 0]])]) self.jump_rates = np.array([self.Gamma_L, self.Gamma_R]) self.beta = beta self.environment = environment self.filter = False self.K = K self.tc = tc self.heom_solver = HierarchySolver(self.system_hamiltonian(), self.environment, \ self.beta, self.jump_operators, self.jump_rates, N=trunc_level,\ num_matsubara_freqs=self.K, temperature_correction=self.tc, \ dissipator_test=dissipator_test) self.truncation_level = trunc_level #self.heom_solver.truncation_level = self.truncation_level self.dv_pops = np.zeros(self.system_dimension**2 * self.heom_solver.number_density_matrices()) self.dv_pops[:self.system_dimension**2] = np.array([1., 0, 0, 0, 1., 0, 0, 0, 1.]) def system_hamiltonian(self): return np.array([[0, 0, 0], [0, self.bias/2., self.T_c], [0, self.T_c, -self.bias/2.]]) def heom_matrix(self): self.heom_solver = HierarchySolver(self.system_hamiltonian(), self.environment, \ self.beta, self.jump_operators, self.jump_rates, N=self.truncation_level, \ num_matsubara_freqs=self.K, temperature_correction=self.tc) #self.heom_solver.truncation_level = self.truncation_level return sp.csr_matrix(self.heom_solver.construct_hierarchy_matrix_super_fast()) def jump_matrix(self): if not self.filter: heom_dim = self.heom_solver.number_density_matrices() * self.heom_solver.system_dimension**2 else: heom_dim = self.heom_solver.num_dms * self.heom_solver.system_dimension**2 LJ = sp.lil_matrix((heom_dim, heom_dim), dtype='complex128') LJ[0,8] = self.Gamma_R return LJ
class SRLHEOMModel(): def __init__(self): self.hamiltonian = np.array([[100.,8.],[8.,0]]) self.jump_operators = np.array([np.array([[0, 0],[1., 0]]), np.array([[0, 1.],[0, 0]])]) self.Gamma_L = 1. self.Gamma_R = 1.#e-4 self.jump_rates = np.array([self.Gamma_L, self.Gamma_R]) self.dt = 1.e4 self.hs = HierarchySolver(self.hamiltonian, 35., 40., temperature=300., jump_operators=self.jump_operators, jump_rates=self.jump_rates) self.hs.truncation_level = 4 self.hm = self.hs.construct_hierarchy_matrix_super_fast().tolil() #print self.hm.todense() self.init_ss_vector = spla.eigs(self.hm.tocsc(), k=1, sigma=0, which='LM', maxiter=1000)[1] def hm_chi(self, chi): new_hm = self.hm.copy() # sys_dim = self.hs.system_dimension # for i in range(self.hs.number_density_matrices()): # print 'i = ' + str(i*sys_dim**2) # print 'j = ' + str((i+1)*sys_dim**2 - 1) # new_hm[i*sys_dim**2,(i+1)*sys_dim**2 - 1] *= np.exp(chi) new_hm[0,3] *= np.exp(chi) return new_hm # define cumulant generating function def cumulant_generating_function(self, chi): W = self.hm_chi(chi).tocsc() ss = spla.eigs(W, k=1, sigma=0, which='LM', maxiter=1000, v0=self.init_ss_vector)[1] #ss = spla.svds(W, k=1, which='SM', maxiter=10000, v0=self.init_ss_vector)[0] ss /= np.trace(self.hs.extract_system_density_matrix(ss)) dv_pops = np.zeros(4 * self.hs.number_density_matrices()) dv_pops[:4] = np.array([1., 0, 0, 1.]) #ss = utils.stationary_state_svd(W.todense(), dv_pops) return np.log(dv_pops.dot(spla.expm(W.multiply(self.dt)).dot(ss))) def zero_frequency_noise(self): CGF_dd = ndt.Derivative(self.cumulant_generating_function, n=2, method='central') return CGF_dd(0)[0] / self.dt def mean(self): ss = spla.eigs(self.hm_chi(0).tocsc(), k=1, sigma=0, which='LM', v0=self.init_ss_vector)[1] #ss = spla.svds(self.hm_chi(0).tocsc(), k=1, which='SM', maxiter=10000, v0=self.init_ss_vector)[0] ss = self.hs.extract_system_density_matrix(ss) ss /= np.trace(ss) return self.Gamma_R * ss[1,1] def fano_factor(self): return self.zero_frequency_noise() / self.mean()
class PhotocellModel(object): def __init__(self, delta_E, J, drude_reorg_energy, drude_cutoff, mode_freq, mode_S, mode_damping, CT_scaling, temperature=300., N=6, K=0, v=1., no_mode=False): ''' Parameters... delta_E: unshifted electronic energy splitting in site basis J: electronic coupling drude_reorg_energy: reorg energy due to Drude bath drude_cutoff: relaxation parameter of the Drude bath mode_freq: frequency of the mode mode_S: Huang-Rhys factor of the mode mode_damping: damping constant for the mode CT_scaling: the scaling parameter for the CT state reorg energy temperature N: number of tiers to use with HEOM K: number of Matsubara terms to use in correlation function expansion with HEOM v: the scaling parameter for the excited state reorg energy no_mode: whether to include the mode in the spectral density or not ''' self.el_dim = 5 # the dimension of the electronic system self.delta_E = delta_E # 100. self.J = J # 5. #evals,evecs = np.linalg.eig(H) self.T = temperature # 300. # temperature in Kelvin self.beta = 1. / (utils.KELVIN_TO_WAVENUMS * self.T ) # the inverse temperature in wavenums self.drude_reorg_energy = drude_reorg_energy # 35. self.drude_cutoff = drude_cutoff # 40. self.no_mode = no_mode self.mode_freq = mode_freq # 342. self.mode_S = mode_S # 0.0438 self.mode_damping = mode_damping # 10. self.mode_reorg_energy = self.mode_freq * self.mode_S self.total_reorg_energy = ( self.drude_reorg_energy + self.mode_reorg_energy ) if not self.no_mode else self.drude_reorg_energy self.v = v # scaling of site 1 reorg energy (in case we want to vary it too) self.c = CT_scaling # scaling of CT state reorg energy self.N = N # truncation level self.K = K # num Matsubara terms ''' Where did I take the following parameters from? ''' self.n_ex = 6000 # number of photons in the 'hot' bath self.gamma = 1.e-2 # bare transition rate between ground and excited states self.gamma_ex = self.gamma * self.n_ex # rate from ground to excited state self.gamma_deex = self.gamma * (self.n_ex + 1. ) # rate from excited to ground state self.secondary_CT_energy_gap = 300. self.bare_secondary_CT = 1. #0.0025 # rate from CT1 to CT2 self.n_c = utils.planck_distribution(self.secondary_CT_energy_gap, self.T) self.forward_secondary_CT = (self.n_c + 1) * self.bare_secondary_CT self.backward_secondary_CT = self.n_c * self.bare_secondary_CT self.Gamma_L = 1. # 0.025 # the rate from left lead to populate ground state self.Gamma_R = 1. # rate from CT2 state to right lead # set up environment depending on no_mode if not self.no_mode: env = [(), (OBOscillator(self.v * self.drude_reorg_energy, self.drude_cutoff, beta=self.beta, K=self.K), UBOscillator(self.mode_freq, self.v * self.mode_S, self.mode_damping, beta=self.beta, K=self.K)), (OBOscillator(self.c * self.drude_reorg_energy, self.drude_cutoff, beta=self.beta, K=self.K), UBOscillator(self.mode_freq, self.c * self.mode_S, self.mode_damping, beta=self.beta, K=self.K)), (), ()] else: env = [(), (OBOscillator(self.v * self.drude_reorg_energy, self.drude_cutoff, beta=self.beta, K=self.K), ), (OBOscillator(self.c * self.drude_reorg_energy, self.drude_cutoff, beta=self.beta, K=self.K), ), (), ()] # I think this dummy solver is set up so we can get info about the size of hierarchy # etc using methods from HierarchySolver before actually creating the hierarchy matrix self.dummy_solver = HierarchySolver(self.el_hamiltonian(), env, self.beta, N=self.N, num_matsubara_freqs=self.K) def el_hamiltonian(self): ''' The electronic Hamiltonian. We can ignore the energies of ground, CT2 and empty states as there is no coherent evolution between them. We take into account the relative energies through the incoherent rates instead. ''' H = np.zeros((self.el_dim, self.el_dim)) H[1:3, 1:3] = np.array([[self.delta_E / 2., self.J], [self.J, -self.delta_E / 2.]]) + np.diag([ self.v * self.total_reorg_energy, self.c * self.total_reorg_energy ]) return H def excitation_op(self): ''' Operator to take the system from ground state to site 1 (excited state) transformed to the electronic eigenstate basis. ''' op = np.zeros((self.el_dim, self.el_dim)) op[1, 0] = 1. # eigenstate basis evals, evecs = np.linalg.eig(self.el_hamiltonian()[1:3, 1:3]) transform = np.eye(self.el_dim, dtype='complex128') transform[1:3, 1:3] = evecs return np.dot(transform.T, np.dot(op, transform)) def deexcitation_op(self): ''' Operator to take the system from site 1 (excited state) to ground state transformed to the electronic eigenstate basis. ''' op = np.zeros((self.el_dim, self.el_dim)) op[0, 1] = 1. # eigenstate basis evals, evecs = np.linalg.eig(self.el_hamiltonian()[1:3, 1:3]) transform = np.eye(self.el_dim, dtype='complex128') transform[1:3, 1:3] = evecs return np.dot(transform.T, np.dot(op, transform)) def forward_secondary_CT_op(self): op = np.zeros((self.el_dim, self.el_dim)) op[3, 2] = 1. return op def backward_secondary_CT_op(self): op = np.zeros((self.el_dim, self.el_dim)) op[2, 3] = 1. return op def drain_lead_op(self): op = np.zeros((self.el_dim, self.el_dim)) op[4, 3] = 1. return op def source_lead_op(self): op = np.zeros((self.el_dim, self.el_dim)) op[0, 4] = 1. return op def construct_heom_matrix(self): if not self.no_mode: env = [(), (OBOscillator(self.v * self.drude_reorg_energy, self.drude_cutoff, beta=self.beta, K=self.K), UBOscillator(self.mode_freq, self.v * self.mode_S, self.mode_damping, beta=self.beta, K=self.K)), (OBOscillator(self.c * self.drude_reorg_energy, self.drude_cutoff, beta=self.beta, K=self.K), UBOscillator(self.mode_freq, self.c * self.mode_S, self.mode_damping, beta=self.beta, K=self.K)), (), ()] else: env = [(), (OBOscillator(self.v * self.drude_reorg_energy, self.drude_cutoff, beta=self.beta, K=self.K), ), (OBOscillator(self.c * self.drude_reorg_energy, self.drude_cutoff, beta=self.beta, K=self.K), ), (), ()] jump_operators = [self.excitation_op(), self.deexcitation_op(), \ self.forward_secondary_CT_op(), self.backward_secondary_CT_op(), \ self.drain_lead_op(), self.source_lead_op()] jump_rates = np.array([self.gamma_ex, self.gamma_deex, self.forward_secondary_CT, self.backward_secondary_CT, \ self.Gamma_R, self.Gamma_L]) self.solver = HierarchySolver(self.el_hamiltonian(), environment=env, beta=self.beta, \ jump_operators=jump_operators, jump_rates=jump_rates, \ N=self.N, num_matsubara_freqs=self.K, temperature_correction=True) return self.solver.construct_hierarchy_matrix_super_fast() def jump_matrix(self): dim = self.dummy_solver.M_dimension() jop = sp.csr_matrix((dim, dim)) jop[:self.el_dim**2, :self.el_dim**2] = self.Gamma_R * np.kron( self.drain_lead_op(), self.drain_lead_op()) return jop def dv_pops(self): dv_pops = np.zeros(self.dummy_solver.system_dimension**2 * self.dummy_solver.number_density_matrices()) dv_pops[:self.dummy_solver.system_dimension**2] = np.eye( self.dummy_solver.system_dimension).flatten() return dv_pops def update_Gamma_R(self, Gamma_R): self.Gamma_R = Gamma_R self.solver.jump_rates[-2] = Gamma_R
pops2 = np.array([dm[1, 1] for dm in HEOM_history], dtype='float64') result, covars = curve_fit(fit_func, time, pops1[:-1]) # y = np.array(utils.differentiate_function(pops1, time), dtype='float64') # # rates0 = np.array([0.1 ,0.1]) # result = leastsq(residuals, rates0, args=(y, pops1, pops2)) # rates = result[0] HEOM_rates[i] = result[0] hs = HierarchySolver(system_hamiltonian, environment(v, beta, K), beta, N=trunc_level) v0 = np.zeros(hs.M_dimension()) v0[0] = v0[3] = 0.5 ss = spla.eigs(hs.construct_hierarchy_matrix_super_fast().tocsc(), k=1, sigma=None, which='SM', v0=v0)[1] ss /= (ss[0] + ss[3]) steady_states.append(ss[:4]) steady_states_te.append(HEOM_history[-1][1, 1]) print 'Finished at: ' + str(time_utils.getTime()) # np.savez('../../data/HEOM_forster_rates_reorg_energy_5ps.npz', forster_rates=forster_rates, HEOM_rates=HEOM_rates, HEOM_dynamics=HEOM_dynamics, \ # time=time, reorg_energy_values=reorg_energy_values) plt.semilogx(reorg_energy_values, forster_rates * utils.WAVENUMS_TO_INVERSE_PS,
if mode_params: # assuming that there is a single identical mode on each site environment = [(OBOscillator(reorg_energy, cutoff_freq, beta, K=K), UBOscillator(mode_params[0][0], mode_params[0][1], mode_params[0][2], beta, K=K)), \ (OBOscillator(reorg_energy, cutoff_freq, beta, K=K), UBOscillator(mode_params[0][0], mode_params[0][1], mode_params[0][2], beta, K=K))] else: environment = [(), (OBOscillator(reorg_energy, cutoff_freq, beta, K=K), ), (OBOscillator(reorg_energy, cutoff_freq, beta, K=K), )] hs = HierarchySolver(system_hamiltonian, environment, beta, jump_ops, jump_rates, num_matsubara_freqs=K, temperature_correction=True) hs.truncation_level = 7 hm = hs.construct_hierarchy_matrix_super_fast() print 'hierarchy matrix shape: ' + str(hm.shape) print hs.dm_per_tier() np.savez('DQD_heom_matrix_N7_K4.npz', hm=hm) import scipy.sparse.linalg as spla np.set_printoptions(precision=6, linewidth=150, suppress=True) v0 = np.zeros(hm.shape[0]) v0[0] = 1. / 3 v0[4] = 1. / 3 v0[8] = 1. / 3 evals, evec = spla.eigs(hm.tocsc(), k=1, sigma=0, which='LM', v0=v0) #, ncv=100) print evals
[0, 0, 0]])]) jump_rates = np.array([0.1, 0.0025]) K = 4 environment = [] if mode_params: # assuming that there is a single identical mode on each site environment = [(OBOscillator(reorg_energy, cutoff_freq, beta, K=K), UBOscillator(mode_params[0][0], mode_params[0][1], mode_params[0][2], beta, K=K)), \ (OBOscillator(reorg_energy, cutoff_freq, beta, K=K), UBOscillator(mode_params[0][0], mode_params[0][1], mode_params[0][2], beta, K=K))] else: environment = [(), (OBOscillator(reorg_energy, cutoff_freq, beta, K=K),), (OBOscillator(reorg_energy, cutoff_freq, beta, K=K),)] hs = HierarchySolver(system_hamiltonian, environment, beta, jump_ops, jump_rates, num_matsubara_freqs=K, temperature_correction=True) hs.truncation_level = 7 hm = hs.construct_hierarchy_matrix_super_fast() print 'hierarchy matrix shape: ' + str(hm.shape) print hs.dm_per_tier() np.savez('DQD_heom_matrix_N7_K4.npz', hm=hm) import scipy.sparse.linalg as spla np.set_printoptions(precision=6, linewidth=150, suppress=True) v0 = np.zeros(hm.shape[0]) v0[0] = 1./3 v0[4] = 1./3 v0[8] = 1./3 evals,evec = spla.eigs(hm.tocsc(), k=1, sigma=0, which='LM', v0=v0)#, ncv=100) print evals evec = evec[:9]
# HEOM_dynamics.append(HEOM_history) HEOM_history = HEOM_dynamics[i] pops1 = np.array([dm[0,0] for dm in HEOM_history], dtype='float64') pops2 = np.array([dm[1,1] for dm in HEOM_history], dtype='float64') result, covars = curve_fit(fit_func, time, pops1[:-1]) # y = np.array(utils.differentiate_function(pops1, time), dtype='float64') # # rates0 = np.array([0.1 ,0.1]) # result = leastsq(residuals, rates0, args=(y, pops1, pops2)) # rates = result[0] HEOM_rates[i] = result[0] hs = HierarchySolver(system_hamiltonian, environment(v, beta, K), beta, N=trunc_level) v0 = np.zeros(hs.M_dimension()) v0[0] = v0[3] = 0.5 ss = spla.eigs(hs.construct_hierarchy_matrix_super_fast().tocsc(), k=1, sigma=None, which='SM', v0=v0)[1] ss /= (ss[0] + ss[3]) steady_states.append(ss[:4]) steady_states_te.append(HEOM_history[-1][1,1]) print 'Finished at: ' + str(time_utils.getTime()) # np.savez('../../data/HEOM_forster_rates_reorg_energy_5ps.npz', forster_rates=forster_rates, HEOM_rates=HEOM_rates, HEOM_dynamics=HEOM_dynamics, \ # time=time, reorg_energy_values=reorg_energy_values) plt.semilogx(reorg_energy_values, forster_rates*utils.WAVENUMS_TO_INVERSE_PS, label='Forster') plt.semilogx(reorg_energy_values, HEOM_rates*utils.WAVENUMS_TO_INVERSE_PS, label='HEOM') plt.show() plt.semilogx(reorg_energy_values, [steady_states[i][3] for i in range(len(steady_states))]) plt.semilogx(reorg_energy_values, steady_states_te)