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
Пример #2
0
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()
Пример #3
0
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,
Пример #5
0
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)