def generalised_forster_rate(hamiltonian, cluster1_dim, cluster2_dim, total_site_reorg_energies, site_cutoff_freqs, site_lbfs, time, temperature, high_energy_modes=None): sys_dim = cluster1_dim+cluster2_dim cluster1_hamiltonian = hamiltonian[:cluster1_dim, :cluster1_dim]# - np.diag(site_reorg_energies[:cluster1_dim]) cluster2_hamiltonian = hamiltonian[cluster1_dim:, cluster1_dim:]# - np.diag(site_reorg_energies[cluster1_dim:]) # diagonalise individual clusters cluster1_evals, cluster1_evecs = utils.sorted_eig(cluster1_hamiltonian) cluster2_evals, cluster2_evecs = utils.sorted_eig(cluster2_hamiltonian) # calculate inter-cluster exciton couplings couplings = hamiltonian[:cluster1_dim,cluster1_dim:] couplings_matrix = np.dot(cluster1_evecs.conj(), np.dot(couplings, cluster2_evecs.T)) # construct partially diagonalised Hamiltonian exciton_hamiltonian = np.asarray(np.bmat([[np.diag(cluster1_evals), couplings_matrix], [couplings_matrix.conj().T, np.diag(cluster2_evals)]])) # calculate exciton reorg energies cluster1_exciton_reorg_energies = np.array([exciton_reorg_energy(cluster1_evecs[i], total_site_reorg_energies[:cluster1_dim]) for i in range(cluster1_dim)]) cluster2_exciton_reorg_energies = np.array([exciton_reorg_energy(cluster2_evecs[i], total_site_reorg_energies[cluster1_dim:]) for i in range(cluster2_dim)]) # calculate exciton line broadening functions cluster1_lbfs = np.array([exciton_lbf(cluster1_evecs[i], site_lbfs[:cluster1_dim]) for i in range(cluster1_dim)]) cluster2_lbfs = np.array([exciton_lbf(cluster2_evecs[i], site_lbfs[cluster1_dim:]) for i in range(cluster2_dim)]) # calculate lifetimes OBO_reorg_energies = total_site_reorg_energies - np.sum([s[0]*s[1] for s in high_energy_modes]) cluster1_lifetimes = exciton_lifetimes(cluster1_hamiltonian, OBO_reorg_energies[:cluster1_dim], site_cutoff_freqs[:cluster1_dim], temperature, high_energy_modes=high_energy_modes) cluster2_lifetimes = exciton_lifetimes(cluster2_hamiltonian, OBO_reorg_energies[cluster1_dim:], site_cutoff_freqs[cluster1_dim:], temperature, high_energy_modes=high_energy_modes) bare_cluster1_evals = cluster1_evals - cluster1_exciton_reorg_energies bare_cluster2_evals = cluster2_evals - cluster2_exciton_reorg_energies # calculate individual Forster rates forster_rates = np.zeros((cluster1_dim, cluster2_dim)) for i in range(cluster1_dim): for j in range(cluster2_dim): cluster1_state = np.zeros(sys_dim) cluster1_state[i] = 1. cluster2_state = np.zeros(sys_dim) cluster2_state[cluster1_dim+j] = 1. forster_rates[i,j] = forster_rate(bare_cluster1_evals[i], bare_cluster2_evals[j], cluster1_exciton_reorg_energies[i], \ cluster2_exciton_reorg_energies[j], cluster1_lbfs[i], cluster2_lbfs[j], cluster1_lifetimes[i], cluster2_lifetimes[j], \ cluster1_state, cluster2_state, exciton_hamiltonian, time) # calculate generalised Forster rate B800_thermal_state = utils.general_thermal_state(np.diag(cluster1_evals), temperature) result = np.dot(np.diag(B800_thermal_state), np.sum(forster_rates, axis=1)) return result
def exciton_relaxation_rates(site_hamiltonian, E_reorg, cutoff_freq, spectral_density, temperature, high_energy_params=None): # calculate sorted exciton energies and eigenvectors evals, evecs = utils.sorted_eig(site_hamiltonian) rates = np.zeros((site_hamiltonian.shape[0], site_hamiltonian.shape[1])) for i in range(evals.size): for j in range(evals.size): E_i = evals[i] E_j = evals[j] w = E_j - E_i rates[i,j] = Gamma(w, cutoff_freq, spectral_density, E_reorg, temperature, evecs[i], evecs[j], high_energy_params) if w != 0 else 0 return rates
def redfield_tensor_identical_drude(site_hamiltonian, reorg_energy, cutoff_freq, temperature): system_dimension = site_hamiltonian.shape[0] evalues,evectors = utils.sorted_eig(site_hamiltonian) # may need to get bare exciton energies here #evalues,evectors = np.linalg.eig(site_hamiltonian) # site_correlation function in frequency space for Drude-Lorentz spectral density def site_correlation_function(freq): if freq != 0 : return overdamped_BO_spectral_density(np.abs(freq), reorg_energy, cutoff_freq) * np.abs(utils.planck_distribution(freq, temperature)) else: return 0 # construct system part of system-bath coupling operators (single diagonal elements for this simple case) site_bath_coupling_matrices = np.zeros((system_dimension, system_dimension, system_dimension)) for i in range(system_dimension): site_bath_coupling_matrices[i,i,i] = 1. # construct pairs of indices to iterate over when constructing the tensor from itertools import product indices = [(i,j) for i,j in product(range(system_dimension), range(system_dimension))] def Gamma(a, b, c, d): return np.real(np.sum([np.dot(evectors[a], np.dot(site_bath_coupling_matrices[n], evectors[b])) \ * np.dot(evectors[c], np.dot(site_bath_coupling_matrices[n], evectors[d])) for n in range(system_dimension)]) \ * site_correlation_function(evalues[c]-evalues[d])) def tensor_element(a, b, c, d): element = 0 if a == c: for e in range(system_dimension): element += Gamma(b, e, e, d) if b == d: for e in range(system_dimension): element += Gamma(a, e, e ,c) element -= Gamma(c, a, b, d) + Gamma(d, b, a, c) return element result = np.zeros((system_dimension**2, system_dimension**2)) for i,ab in enumerate(indices): for j,cd in enumerate(indices): result[i,j] = tensor_element(ab[0], ab[1], cd[0], cd[1]) return result
def MRT_rate_PE545(site_hamiltonian, site_reorg_energy1, cutoff_freq1, site_reorg_energy2, cutoff_freq2, temperature, high_energy_mode_params, num_expansion_terms=0, time_interval=0.5): time = np.linspace(0,time_interval, int(time_interval*16000.)) evals, evecs = utils.sorted_eig(site_hamiltonian) # correlation function coefficients for first over-damped oscillator and high energy modes coeffs = lbf_coeffs(site_reorg_energy1, cutoff_freq1, temperature, high_energy_mode_params, num_expansion_terms) # add correlation function coefficients for second over-damped oscillator coeffs = np.concatenate((coeffs, lbf_coeffs(site_reorg_energy2, cutoff_freq2, temperature, None, num_expansion_terms))) g_site = site_lbf_ed(time, coeffs) g_site_dot = site_lbf_dot_ed(time, coeffs) g_site_dot_dot = site_lbf_dot_dot_ed(time, coeffs) total_site_reorg_energy = site_reorg_energy1 + site_reorg_energy2 if high_energy_mode_params is not None and high_energy_mode_params.any(): total_site_reorg_energy += np.sum([mode[0]*mode[1] for mode in high_energy_mode_params]) system_dim = site_hamiltonian.shape[0] rates = np.zeros((system_dim, system_dim)) integrands = np.zeros((system_dim, system_dim, time.size), dtype='complex') # excitons are labelled from lowest in energy to highest for i in range(system_dim): for j in range(system_dim): if i != j: # get energy gap E_i = evals[i] E_j = evals[j] omega_ij = E_j - E_i #E_i - E_j if E_i > E_j else E_j - E_i # calculate overlaps (c_alpha and c_beta's) c_alphas = evecs[i] c_betas = evecs[j] # calculate integrand integrand = np.array([np.exp(1.j*omega_ij*t - (np.sum(c_alphas**4) + np.sum(c_betas**4))*(1.j*total_site_reorg_energy*t + g_site[k]) + 2. * np.sum(c_alphas**2 * c_betas**2) * (g_site[k] + 1.j*total_site_reorg_energy*t)) * ((np.sum(c_alphas**2 * c_betas**2)*g_site_dot_dot[k]) - ((np.sum(c_alphas * c_betas**3) - np.sum(c_alphas**3 * c_betas))*g_site_dot[k] + 2.j*np.sum(c_betas**3 * c_alphas)*total_site_reorg_energy)**2) for k,t in enumerate(time)]) # perform integration rates[i,j] = 2.* integrate.simps(np.real(integrand), time) integrands[i,j] = integrand return rates#, integrands, time
def MRT_rates(site_hamiltonian, site_reorg_energies, cutoff_freq, temperature, high_energy_mode_params, num_expansion_terms=0, time_interval=0.5): time = np.linspace(0,time_interval, int(time_interval*2000.)) evals, evecs = utils.sorted_eig(site_hamiltonian) system_dim = site_hamiltonian.shape[0] coeffs = np.array([lbf_coeffs(site_reorg_energies[i], cutoff_freq, temperature, high_energy_mode_params, num_expansion_terms) for i in range(system_dim)]) g_site = np.array([site_lbf_ed(time, coeffs[i]) for i in range(system_dim)]) g_site_dot = np.array([site_lbf_dot_ed(time, coeffs[i]) for i in range(system_dim)]) g_site_dot_dot = np.array([site_lbf_dot_dot_ed(time, coeffs[i]) for i in range(system_dim)]) if high_energy_mode_params is not None and high_energy_mode_params.any(): site_reorg_energies += np.sum([mode[0]*mode[1] for mode in high_energy_mode_params]) rates = np.zeros((system_dim, system_dim)) integrands = np.zeros((system_dim, system_dim, time.size), dtype='complex') # excitons are labelled from lowest in energy to highest for i in range(system_dim): for j in range(system_dim): if i != j: # get energy gap E_i = evals[i] E_j = evals[j] omega_ij = E_i - E_j if E_i > E_j else E_j - E_i # calculate overlaps (c_alpha and c_beta's) c_alphas = evecs[i] c_betas = evecs[j] # calculate integrand integrand = np.array([np.exp(1.j*omega_ij*t - np.sum((c_alphas**4 + c_betas**4)*(1.j*site_reorg_energies*t + g_site.T[k])) + 2. * np.sum((c_alphas**2 * c_betas**2) * (g_site.T[k] + 1.j*site_reorg_energies*t))) * (np.sum(c_alphas**2 * c_betas**2 * g_site_dot_dot.T[k]) - (np.sum((c_alphas * c_betas**3 - c_alphas**3 * c_betas)*g_site_dot.T[k]) + 2.j*np.sum(c_betas**3 * c_alphas * site_reorg_energies))**2) for k,t in enumerate(time)]) # perform integration rates[i,j] = 2.* integrate.simps(np.real(integrand), time) integrands[i,j] = integrand return rates#, integrands, time
timestep = 0.01 time = np.arange(0, duration + timestep, timestep) #init_dv = np.array([0.35, 0.12, 0.1, 0.1, 0.34, 0.61, 0.46, 0.5]) # init state in site basis site_history_sum = np.zeros((average_site_hamiltonian.shape[0], time.size)) for n in range(num_realisations): print 'Calculating realisation ' + str(n + 1) + ' at time ' + str( datetime.now().time()) # calculate Hamiltonian for this realisation ie. add site_shifts to average site energies and construct Hamiltonian site_hamiltonian = np.diag( average_site_energies + total_site_reorg_energy + site_shifts[n] ) + couplings + couplings.T # now including reorganisation shift evals, evecs = utils.sorted_eig( site_hamiltonian ) # make sure to return excitons in basis going from lowest to highest energy with sorted_eig site_reorg_energies = np.zeros(site_hamiltonian.shape[0]) site_reorg_energies.fill(total_site_reorg_energy) exciton_reorg_energies = np.zeros(site_hamiltonian.shape[0]) for i in range(site_hamiltonian.shape[0]): exciton_reorg_energies[i] = os.exciton_reorg_energy( evecs[i], site_reorg_energies) # calculate exciton reorg energies evals = evals - exciton_reorg_energies # shift exciton energies down by exciton reorg energies # calculate modified Redfield rates rates = os.MRT_rate_PE545_quick(evals, evecs, g_site, g_site_dot, g_site_dot_dot, total_site_reorg_energy, temperature, integration_time) # construct Liouvillian for system liouvillian = np.zeros((rates.shape[0], rates.shape[1]))
font = {'size':16} matplotlib.rc('font', **font) np.set_printoptions(precision=3, linewidth=150, suppress=True) data = np.load('../../data/PSIIRC_incoherent_HEOM_full_system_data.npz') dm_history = data['dm_history'] system_hamiltonian = data['system_hamiltonian'] time = data['time'] jump_rates = data['jump_rates'] jump_operators = data['jump_operators'] print jump_rates print jump_operators evalues, evectors = utils.sorted_eig(system_hamiltonian[1:7,1:7]) site_exciton_transform = np.eye(10) site_exciton_transform[1:7,1:7] = evectors.T exciton_dm_history = np.array([np.dot(site_exciton_transform.T, np.dot(dm, site_exciton_transform)) for dm in dm_history]) labels = ['g', 'X1', 'X2', 'X3', 'X4', 'X5', 'X6', 'CT1', 'CT2', 'empty'] for i in range(system_hamiltonian.shape[0]): plt.plot(time, exciton_dm_history[:,i,i], linewidth=2, label=labels[i]) plt.legend().draggable() plt.xlabel('time (ps)') plt.ylabel('population') plt.show()
def __init__(self, hamiltonian, environment, beta, jump_operators=None, jump_rates=None, N=1, \ num_matsubara_freqs=0, temperature_correction=False, dissipator_test=False): ''' Constructor ''' self.system_hamiltonian = hamiltonian self.environment = environment self.beta = beta self.jump_operators = jump_operators self.jump_rates = jump_rates self.truncation_level = N self.num_matsubara_freqs = num_matsubara_freqs self.temperature_correction = temperature_correction ''' dissipator_test is a flag to include the Lindblad dissipator in the top level of the hierarchy only It is used in self.liouvillian() and self.construct_hierarchy_matrix_super_fast() ''' self.dissipator_test = dissipator_test self.system_evalues, self.system_evectors = utils.sorted_eig( self.system_hamiltonian) self.system_evectors = self.system_evectors.T self.system_dimension = self.system_hamiltonian.shape[0] if self.num_matsubara_freqs > 0 or self.temperature_correction: self.matsubara_freqs = self.calculate_matsubara_freqs() self.Vx_operators, self.Vo_operators = [], [] # if tuple or UBOscillator or OBOscillator put into list of tuples if type(self.environment ) is tuple: # multiple oscillators, identical on each site self.environment = [self.environment] * self.system_dimension elif isinstance( environment, (OBOscillator, UBOscillator)): # single oscillator identical on each site self.environment = [(self.environment, )] * self.system_dimension if type(self.environment) is list: # environment defined per site self.diag_coeffs = [] self.phix_coeffs = [] self.thetax_coeffs = [] self.thetao_coeffs = [] self.tc_terms = [] for i, site in enumerate(self.environment): if site: site_coupling_operator = np.zeros( (self.system_dimension, self.system_dimension)) site_coupling_operator[i, i] = 1. site_Vx_operator = self.commutator_to_superoperator( site_coupling_operator, type='-') site_Vo_operator = self.commutator_to_superoperator( site_coupling_operator, type='+') tc_term = 0 for osc in site: if isinstance(osc, OBOscillator): self.diag_coeffs.append(osc.cutoff_freq) self.phix_coeffs.append(self.phix_coeff_OBO(osc)) #site_coupling_operator = np.zeros(self.system_dimension) self.thetax_coeffs.append( self.thetax_coeff_OBO(osc)) self.thetao_coeffs.append( self.thetao_coeff_OBO(osc)) self.Vx_operators.append(site_Vx_operator) self.Vo_operators.append(site_Vo_operator) elif isinstance(osc, UBOscillator): self.diag_coeffs.append(0.5 * osc.damping) self.diag_coeffs.append(-1.j * osc.zeta) self.phix_coeffs.append( self.phix_coeff_UBO(osc, pm=1)) self.phix_coeffs.append( self.phix_coeff_UBO(osc, pm=-1)) self.thetax_coeffs.append( self.thetax_coeff_UBO(osc, pm=1)) self.thetax_coeffs.append( self.thetax_coeff_UBO(osc, pm=-1)) self.thetao_coeffs.append( self.thetao_coeff_UBO(osc, pm=1)) self.thetao_coeffs.append( self.thetao_coeff_UBO(osc, pm=-1)) self.Vx_operators.append(site_Vx_operator) self.Vx_operators.append(site_Vx_operator) self.Vo_operators.append(site_Vo_operator) self.Vo_operators.append(site_Vo_operator) tc_term += osc.temp_correction_sum() \ - np.sum([osc.temp_correction_sum_kth_term(k) for k in range(1,self.num_matsubara_freqs+1)]) self.tc_terms.append( tc_term * np.dot(site_Vx_operator, site_Vx_operator)) for k in range(1, self.num_matsubara_freqs + 1): self.diag_coeffs.append(self.matsubara_freqs[k - 1]) self.phix_coeffs.append(self.phix_coeff_MF(site, k)) self.thetax_coeffs.append(self.thetax_coeff_MF( site, k)) self.thetao_coeffs.append(0) self.Vx_operators.append(site_Vx_operator) self.Vo_operators.append(site_Vo_operator) else: raise ValueError("Environment defined in invalid format!") self.num_aux_dm_indices = len(self.diag_coeffs) self.diag_coeffs = np.array(self.diag_coeffs) self.phix_coeffs = np.array(self.phix_coeffs) self.thetax_coeffs = np.array(self.thetax_coeffs) self.thetao_coeffs = np.array(self.thetao_coeffs) self.Vx_operators = np.array(self.Vx_operators) self.Vo_operators = np.array(self.Vo_operators)
def __init__(self, hamiltonian, drude_reorg_energy, drude_cutoff, beta, \ jump_operators=None, jump_rates=None, underdamped_mode_params=[], \ num_matsubara_freqs=0, temperature_correction=False, \ sites_to_couple=[]): ''' Constructor ''' self.init_system_dm = None self.drude_reorg_energy = drude_reorg_energy self.drude_cutoff = drude_cutoff self.beta = beta self.system_hamiltonian = hamiltonian self.system_evalues, self.system_evectors = utils.sorted_eig(self.system_hamiltonian) self.system_evectors = self.system_evectors.T self.jump_operators = jump_operators self.jump_rates = jump_rates self.system_dimension = self.system_hamiltonian.shape[0] if np.any(sites_to_couple): if sites_to_couple.ndim != 1 or len(sites_to_couple) != self.system_hamiltonian.shape[0] or np.all(sites_to_couple == 0): raise ValueError("sites_to_couple should be a 1d array containing 1s and 0s indicating the index of sites" + \ " to couple to a bath described by hierarchical equations of motion.") self.sites_to_couple = sites_to_couple self.heom_sys_dim = np.count_nonzero(self.sites_to_couple) else: self.heom_sys_dim = self.system_dimension self.sites_to_couple = np.ones(self.system_dimension) # commutator operators for each site coupling to a bath self.Vx_operators, self.Vo_operators = self.construct_commutator_operators() # zeroth order freqs for identical Drude spectral density on each site are just cutoff freq self.drude_zeroth_order_freqs = np.zeros(self.heom_sys_dim, dtype='complex64') self.drude_zeroth_order_freqs.fill(self.drude_cutoff) # underdamped_mode_params should be a list of tuples # each tuple having frequency, Huang-Rhys factor and damping for each mode in that order self.underdamped_mode_params = underdamped_mode_params self.num_modes = len(self.underdamped_mode_params) if np.any(self.underdamped_mode_params): self.BO_zeroth_order_freqs = np.zeros((self.num_modes, self.heom_sys_dim)) self.BO_zetas = np.zeros((self.num_modes, self.heom_sys_dim)) for i,mode in enumerate(self.underdamped_mode_params): self.BO_zeroth_order_freqs[i].fill(mode[2]) self.BO_zetas[i].fill(np.sqrt(mode[0]**2 - mode[2]**2/4.)) self.num_matsubara_freqs = num_matsubara_freqs self.temperature_correction = temperature_correction if self.num_matsubara_freqs > 0 or self.temperature_correction: self.matsubara_freqs = self.calculate_matsubara_freqs() '''Currently assumes there will be one Drude spectral density, with optional number of modes. Need to change to allow arbitrary combination of over and underdamped Brownian oscillators.''' self.num_aux_dm_indices = (1 + 2*self.num_modes + self.num_matsubara_freqs)*self.heom_sys_dim '''Also assumes that spectral density is same on each site. Change to have different spectral densities on each site.''' # calculate coefficients of operators coupling to lower tiers and fill in vectors self.phix_coeffs = np.zeros(self.num_aux_dm_indices, dtype='complex64') self.thetax_coeffs = np.zeros(self.num_aux_dm_indices, dtype='complex64') self.thetao_coeffs = np.zeros(self.num_aux_dm_indices, dtype='complex64') self.phix_coeffs[:self.heom_sys_dim].fill(self.drude_phix_coeffs(0)) self.thetax_coeffs[:self.heom_sys_dim].fill(self.drude_thetax_coeff(0)) self.thetao_coeffs[:self.heom_sys_dim].fill(self.drude_thetao_coeff(0)) for i in range(self.num_modes): freq = self.underdamped_mode_params[i][0] reorg_energy = self.underdamped_mode_params[i][0]*self.underdamped_mode_params[i][1] damping = self.underdamped_mode_params[i][2] self.phix_coeffs[self.heom_sys_dim*(1+2*i):self.heom_sys_dim*(2+2*i)].fill(self.mode_phix_coeff(freq, reorg_energy, damping, 0, 1.)) self.phix_coeffs[self.heom_sys_dim*(2+2*i):self.heom_sys_dim*(3+2*i)].fill(self.mode_phix_coeff(freq, reorg_energy, damping, 0, -1.)) self.thetax_coeffs[self.heom_sys_dim*(1+2*i):self.heom_sys_dim*(2+2*i)].fill(self.mode_thetax_coeff(freq, reorg_energy, damping, 0, -1.)) self.thetax_coeffs[self.heom_sys_dim*(2+2*i):self.heom_sys_dim*(3+2*i)].fill(self.mode_thetax_coeff(freq, reorg_energy, damping, 0, 1.)) self.thetao_coeffs[self.heom_sys_dim*(1+2*i):self.heom_sys_dim*(2+2*i)].fill(self.mode_thetao_coeff(freq, reorg_energy, damping, 0, -1.)) self.thetao_coeffs[self.heom_sys_dim*(2+2*i):self.heom_sys_dim*(3+2*i)].fill(self.mode_thetao_coeff(freq, reorg_energy, damping, 0, 1.)) mf_start_idx = (1 + 2*self.num_modes)*self.heom_sys_dim for k in range(1, self.num_matsubara_freqs+1): self.phix_coeffs[mf_start_idx+((k-1)*self.heom_sys_dim):mf_start_idx+(k*self.heom_sys_dim)].fill(self.mf_phix_coeff(self.underdamped_mode_params, k)) self.thetax_coeffs[mf_start_idx+((k-1)*self.heom_sys_dim):mf_start_idx+(k*self.heom_sys_dim)].fill(self.mf_thetax_coeff(self.underdamped_mode_params, k))
(np.sum(c_alphas ** 2 * c_betas ** 2) * g_site_dot_dot[k]) - ( (np.sum(c_alphas * c_betas ** 3) - np.sum(c_alphas ** 3 * c_betas)) * g_site_dot[k] + 2.0j * np.sum(c_betas ** 3 * c_alphas) * site_reorg_energy ) ** 2 ) for k, t in enumerate(time) ] ) mixing_integral1 = [] mixing_integral2 = [] for i, delta_E in enumerate(delta_E_values): evals, evecs = utils.sorted_eig(hamiltonian(delta_E, 20.0)) rates, abs_lineshapes, fl_lineshapes, mixing_function, time2 = os.modified_redfield_relaxation_rates( hamiltonian(delta_E, 20.0), np.array([reorg_energy, reorg_energy]), cutoff_freq, None, temperature, 0, 0.5 ) # mixing_integral1.append(integrate.simps(mixing_function[0,1], time2[:-5])) # mixing_integral2.append(integrate.simps(new_mixing_function(time, evecs[0], evecs[1], reorg_energy, site_lbf, site_lbf_dot, site_lbf_dot_dot))) mixing_integral1.append(mixing_function[0, 1]) mixing_integral2.append( new_mixing_function(time, evecs[0], evecs[1], reorg_energy, site_lbf, site_lbf_dot, site_lbf_dot_dot) ) plt.plot(delta_E_values, mixing_integral1[0], label="1") plt.plot(delta_E_values, mixing_integral2[0], label="2", linewidth=2, ls="--", color="red") plt.legend() # plt.plot(delta_E_values, [np.abs(mixing_integral1[i] - mixing_integral2[i]) for i in range(len(mixing_integral1))]) plt.show()
def __init__(self, hamiltonian, environment, beta, jump_operators=None, jump_rates=None, N=1, \ num_matsubara_freqs=0, temperature_correction=False, dissipator_test=False): ''' Constructor ''' self.system_hamiltonian = hamiltonian self.environment = environment self.beta = beta self.jump_operators = jump_operators self.jump_rates = jump_rates self.truncation_level = N self.num_matsubara_freqs = num_matsubara_freqs self.temperature_correction = temperature_correction ''' dissipator_test is a flag to include the Lindblad dissipator in the top level of the hierarchy only It is used in self.liouvillian() and self.construct_hierarchy_matrix_super_fast() ''' self.dissipator_test = dissipator_test self.system_evalues, self.system_evectors = utils.sorted_eig(self.system_hamiltonian) self.system_evectors = self.system_evectors.T self.system_dimension = self.system_hamiltonian.shape[0] if self.num_matsubara_freqs>0 or self.temperature_correction: self.matsubara_freqs = self.calculate_matsubara_freqs() self.Vx_operators, self.Vo_operators = [],[] # if tuple or UBOscillator or OBOscillator put into list of tuples if type(self.environment) is tuple: # multiple oscillators, identical on each site self.environment = [self.environment] * self.system_dimension elif isinstance(environment, (OBOscillator, UBOscillator)): # single oscillator identical on each site self.environment = [(self.environment,)] * self.system_dimension if type(self.environment) is list:# environment defined per site self.diag_coeffs = [] self.phix_coeffs = [] self.thetax_coeffs = [] self.thetao_coeffs = [] self.tc_terms = [] for i,site in enumerate(self.environment): if site: site_coupling_operator = np.zeros((self.system_dimension, self.system_dimension)) site_coupling_operator[i,i] = 1. site_Vx_operator = self.commutator_to_superoperator(site_coupling_operator, type='-') site_Vo_operator = self.commutator_to_superoperator(site_coupling_operator, type='+') tc_term = 0 for osc in site: if isinstance(osc, OBOscillator): self.diag_coeffs.append(osc.cutoff_freq) self.phix_coeffs.append(self.phix_coeff_OBO(osc)) #site_coupling_operator = np.zeros(self.system_dimension) self.thetax_coeffs.append(self.thetax_coeff_OBO(osc)) self.thetao_coeffs.append(self.thetao_coeff_OBO(osc)) self.Vx_operators.append(site_Vx_operator) self.Vo_operators.append(site_Vo_operator) elif isinstance(osc, UBOscillator): self.diag_coeffs.append(0.5*osc.damping) self.diag_coeffs.append(-1.j*osc.zeta) self.phix_coeffs.append(self.phix_coeff_UBO(osc, pm=1)) self.phix_coeffs.append(self.phix_coeff_UBO(osc, pm=-1)) self.thetax_coeffs.append(self.thetax_coeff_UBO(osc, pm=1)) self.thetax_coeffs.append(self.thetax_coeff_UBO(osc, pm=-1)) self.thetao_coeffs.append(self.thetao_coeff_UBO(osc, pm=1)) self.thetao_coeffs.append(self.thetao_coeff_UBO(osc, pm=-1)) self.Vx_operators.append(site_Vx_operator) self.Vx_operators.append(site_Vx_operator) self.Vo_operators.append(site_Vo_operator) self.Vo_operators.append(site_Vo_operator) tc_term += osc.temp_correction_sum() \ - np.sum([osc.temp_correction_sum_kth_term(k) for k in range(1,self.num_matsubara_freqs+1)]) self.tc_terms.append(tc_term * np.dot(site_Vx_operator, site_Vx_operator)) for k in range(1,self.num_matsubara_freqs+1): self.diag_coeffs.append(self.matsubara_freqs[k-1]) self.phix_coeffs.append(self.phix_coeff_MF(site, k)) self.thetax_coeffs.append(self.thetax_coeff_MF(site, k)) self.thetao_coeffs.append(0) self.Vx_operators.append(site_Vx_operator) self.Vo_operators.append(site_Vo_operator) else: raise ValueError("Environment defined in invalid format!") self.num_aux_dm_indices = len(self.diag_coeffs) self.diag_coeffs = np.array(self.diag_coeffs) self.phix_coeffs = np.array(self.phix_coeffs) self.thetax_coeffs = np.array(self.thetax_coeffs) self.thetao_coeffs = np.array(self.thetao_coeffs) self.Vx_operators = np.array(self.Vx_operators) self.Vo_operators = np.array(self.Vo_operators)
try: data = np.load(lbf_fn) lbf = data['lbf'] lbf_dot = data['lbf_dot'] lbf_dot_dot = data['lbf_dot_dot'] except IOError: lbf_coeffs = os.lbf_coeffs(reorg_energy, cutoff_freq, temperature, None, num_expansion_terms) lbf = os.site_lbf_ed(time, lbf_coeffs) lbf_dot = os.site_lbf_dot_ed(time, lbf_coeffs) lbf_dot_dot = os.site_lbf_dot_ed(time, lbf_coeffs) np.savez(lbf_fn, lbf=lbf, lbf_dot=lbf_dot, lbf_dot_dot=lbf_dot_dot, time=time) print 'Calculating MRT rates at ' + str(time_utils.getTime()) MRT_rates = np.zeros(energy_gap_values.size) for i,H in enumerate(hamiltonians): evals,evecs = utils.sorted_eig(H) MRT_rates[i] = os.modified_redfield_rates_general(evals, evecs, lbf, lbf_dot, lbf_dot_dot, reorg_energy, temperature, time)[0][0,1] # define function for HEOM calculation pool def HEOM_calculation(hamiltonian): reorg_energy = 100. cutoff_freq = 53. temperature = 300. init_state = np.array([[1., 0], [0, 0]]) duration = 5. time_step = 0.00005 hs = HierarchySolver(hamiltonian, reorg_energy, cutoff_freq, temperature) HEOM_history, time = hs.hierarchy_time_evolution(init_state, 18, time_step, duration) return HEOM_history
g_site, g_site_dot, g_site_dot_dot, total_site_reorg_energy = os.modified_redfield_params( time, site_reorg_energy, cutoff_freq, temperature, mode_params, num_expansion_terms) total_site_reorg_energies = np.array( [total_site_reorg_energy, total_site_reorg_energy]) rates_data = [] print 'Calculating rates with high energy modes....' for V in coupling_values: print 'Calculating rates for coupling ' + str(V) rates = [] for i, delta_E in enumerate(delta_E_values): site_hamiltonian = hamiltonian(delta_E, V) + np.diag( total_site_reorg_energies ) # adjust site energies by reorganisation shift evals, evecs = utils.sorted_eig(site_hamiltonian) # calculate exciton reorganisation energies exciton_reorg_energies = np.array([ os.exciton_reorg_energy(evecs[i], total_site_reorg_energies) for i in range(site_hamiltonian.shape[0]) ]) print exciton_reorg_energies evals = evals - exciton_reorg_energies # adjust to bare exciton reorganisation energies MRT = os.modified_redfield_rates(evals, evecs, g_site, g_site_dot, g_site_dot_dot, total_site_reorg_energy, temperature, time) rates.append(MRT[0, 1]) rates_data.append(rates) #np.savez('../../data/modified_redfield_test_high_energy_modes_data.npz', delta_E_values=delta_E_values, coupling_values=coupling_values, rates=rates_data)
g_site = os.site_lbf_ed(integration_time, coeffs) g_site_dot = os.site_lbf_dot_ed(integration_time, coeffs) g_site_dot_dot = os.site_lbf_dot_dot_ed(integration_time, coeffs) total_site_reorg_energy = reorg_energy1 + reorg_energy2 + np.sum([mode[0]*mode[1] for mode in mode_params]) shift_before_diagonalisation = True # in each realisation pick the next value from the distribution for each site to construct the Hamiltonian for n in range(num_realisations): print 'Calculating realisation number ' + str(n+1) realisation_energies = np.zeros(site_energies.size) for i in range(site_energies.size): realisation_energies[i] = site_energy_samples[i][n] if not shift_before_diagonalisation: hamiltonian = np.diag(realisation_energies) + couplings + couplings.T evals, evecs = utils.sorted_eig(hamiltonian) else: hamiltonian = np.diag(realisation_energies + total_site_reorg_energy) + couplings + couplings.T # shift site energies by reorg energy evals, evecs = utils.sorted_eig(hamiltonian) # diagonalise site_reorg_energies = np.zeros(hamiltonian.shape[0]) site_reorg_energies.fill(total_site_reorg_energy) exciton_reorg_energies = np.zeros(hamiltonian.shape[0]) for i in range(hamiltonian.shape[0]): exciton_reorg_energies[i] = os.exciton_reorg_energy(evecs[i], site_reorg_energies) # calculate exciton reorg energies evals = evals - exciton_reorg_energies # shift exciton energies down by exciton reorg energies # calculate the rates and time evolution for the realisation realisation_rates = os.MRT_rate_PE545_quick(evals, evecs, g_site, g_site_dot, g_site_dot_dot, total_site_reorg_energy, temperature, integration_time) liouvillian = np.zeros((realisation_rates.shape[0], realisation_rates.shape[1])) for i,row in enumerate(realisation_rates.T):
except IOError: print 'Starting lbf calculations at ' + str(time_utils.getTime()) lbf_coeffs = os.lbf_coeffs(site_drude_reorg_energy, cutoff_freq, temperature, single_mode_params, 100) lbf = os.site_lbf_ed(time, lbf_coeffs) lbf_dot = os.site_lbf_dot_ed(time, lbf_coeffs) lbf_dot_dot = os.site_lbf_dot_dot_ed(time, lbf_coeffs) print 'Finished calculating lbfs at ' + str(time_utils.getTime()) np.savez(lbf_fn, lbf=lbf, lbf_dot=lbf_dot, lbf_dot_dot=lbf_dot_dot, time=time) # calculate MRT rates evals, evecs = utils.sorted_eig(hamiltonian[:6, :6]) exciton_reorg_energies = np.array([ os.exciton_reorg_energy(exciton, site_reorg_energies) for exciton in evecs ]) # print evals - exciton_reorg_energies # evals,evecs = utils.sort_evals_evecs(evals-exciton_reorg_energies, evecs) # print evals # MRT_rates2 = os.modified_redfield_rates_general_unordered(evals-exciton_reorg_energies, evecs, lbf, lbf_dot, lbf_dot_dot, total_site_reorg_energy, temperature, time) # # liouvillian2 = MRT_rates2 - np.diag(np.sum(MRT_rates2, axis=0)) # # print evals # print MRT_rates2
matplotlib.rc('font', **font) np.set_printoptions(precision=3, linewidth=150, suppress=True) data = np.load('../../data/PSIIRC_incoherent_HEOM_full_system_data.npz') dm_history = data['dm_history'] system_hamiltonian = data['system_hamiltonian'] time = data['time'] jump_rates = data['jump_rates'] jump_operators = data['jump_operators'] print jump_rates print jump_operators evalues, evectors = utils.sorted_eig(system_hamiltonian[1:7, 1:7]) site_exciton_transform = np.eye(10) site_exciton_transform[1:7, 1:7] = evectors.T exciton_dm_history = np.array([ np.dot(site_exciton_transform.T, np.dot(dm, site_exciton_transform)) for dm in dm_history ]) labels = ['g', 'X1', 'X2', 'X3', 'X4', 'X5', 'X6', 'CT1', 'CT2', 'empty'] for i in range(system_hamiltonian.shape[0]): plt.plot(time, exciton_dm_history[:, i, i], linewidth=2, label=labels[i]) plt.legend().draggable() plt.xlabel('time (ps)') plt.ylabel('population')
# basis { PEB_50/61C, DBV_A, DVB_B, PEB_82C, PEB_158C, PEB_50/61D, PEB_82D, PEB_158D } site_energies = np.array( [18532., 18008., 17973., 18040., 18711., 19574., 19050., 18960.]) # no reorganisation shift included couplings = np.array([[0, 1., -37., 37., 23., 92., -16., 12.], [0, 0, 4., -11., 33., -39., -46., 3.], [0, 0, 0, 45., 3., 2., -11., 34.], [0, 0, 0, 0, -7., -17., -3., 6.], [0, 0, 0, 0, 0, 18., 7., 6.], [0, 0, 0, 0, 0, 0, 40., 26.], [0, 0, 0, 0, 0, 0, 0, 7.], [0, 0, 0, 0, 0, 0, 0, 0]]) site_hamiltonian = np.diag(site_energies) + couplings + couplings.T evals, evecs = utils.sorted_eig(site_hamiltonian) site_reorg_shift = 110. site_reorg_energies = np.zeros(site_energies.size) site_reorg_energies.fill(site_reorg_shift) shifted_site_hamiltonian = site_hamiltonian + np.diag(site_reorg_energies) shifted_evals, shifted_evecs = utils.sorted_eig(shifted_site_hamiltonian) exciton_reorg_energies = os.exciton_reorg_energy(shifted_evecs, site_reorg_energies) print evals print shifted_evals - exciton_reorg_energies print exciton_reorg_energies for i, el in enumerate(evecs.flatten()): print shifted_evecs.flatten()[i] + 0.0000001 > el > shifted_evecs.flatten(
except IOError: lbf_coeffs = os.lbf_coeffs(reorg_energy, cutoff_freq, temperature, None, num_expansion_terms) lbf = os.site_lbf_ed(time, lbf_coeffs) lbf_dot = os.site_lbf_dot_ed(time, lbf_coeffs) lbf_dot_dot = os.site_lbf_dot_ed(time, lbf_coeffs) np.savez(lbf_fn, lbf=lbf, lbf_dot=lbf_dot, lbf_dot_dot=lbf_dot_dot, time=time) print 'Calculating MRT rates at ' + str(time_utils.getTime()) MRT_rates = np.zeros(energy_gap_values.size) for i, H in enumerate(hamiltonians): evals, evecs = utils.sorted_eig(H) MRT_rates[i] = os.modified_redfield_rates_general(evals, evecs, lbf, lbf_dot, lbf_dot_dot, reorg_energy, temperature, time)[0][0, 1] # define function for HEOM calculation pool def HEOM_calculation(hamiltonian): reorg_energy = 100. cutoff_freq = 53. temperature = 300. init_state = np.array([[1., 0], [0, 0]]) duration = 5.
data = np.load(lbf_fn) lbf = data['lbf'] lbf_dot = data['lbf_dot'] lbf_dot_dot = data['lbf_dot_dot'] time = data['time'] except IOError: print 'Starting lbf calculations at ' + str(time_utils.getTime()) lbf_coeffs = os.lbf_coeffs(site_drude_reorg_energy, cutoff_freq, temperature, single_mode_params, 100) lbf = os.site_lbf_ed(time, lbf_coeffs) lbf_dot = os.site_lbf_dot_ed(time, lbf_coeffs) lbf_dot_dot = os.site_lbf_dot_dot_ed(time, lbf_coeffs) print 'Finished calculating lbfs at ' + str(time_utils.getTime()) np.savez(lbf_fn, lbf=lbf, lbf_dot=lbf_dot, lbf_dot_dot=lbf_dot_dot, time=time) # calculate MRT rates evals,evecs = utils.sorted_eig(hamiltonian[:6,:6]) exciton_reorg_energies = np.array([os.exciton_reorg_energy(exciton, site_reorg_energies) for exciton in evecs]) # print evals - exciton_reorg_energies # evals,evecs = utils.sort_evals_evecs(evals-exciton_reorg_energies, evecs) # print evals # MRT_rates2 = os.modified_redfield_rates_general_unordered(evals-exciton_reorg_energies, evecs, lbf, lbf_dot, lbf_dot_dot, total_site_reorg_energy, temperature, time) # # liouvillian2 = MRT_rates2 - np.diag(np.sum(MRT_rates2, axis=0)) # # print evals # print MRT_rates2 print evals - exciton_reorg_energies #MRT_rates,evals,evecs = os.modified_redfield_rates_general(evals-exciton_reorg_energies, evecs, lbf, lbf_dot, lbf_dot_dot, total_site_reorg_energy, temperature, time)
# calculate line broadening functions etc... time_interval = 10 time = np.linspace(0, time_interval, 32000.*time_interval) num_expansion_terms = 20 g_site, g_site_dot, g_site_dot_dot, total_site_reorg_energy = os.modified_redfield_params(time, site_reorg_energy, cutoff_freq, temperature, mode_params, num_expansion_terms) total_site_reorg_energies = np.array([total_site_reorg_energy, total_site_reorg_energy]) rates_data = [] print 'Calculating rates with high energy modes....' for V in coupling_values: print 'Calculating rates for coupling ' + str(V) rates = [] for i,delta_E in enumerate(delta_E_values): site_hamiltonian = hamiltonian(delta_E, V) + np.diag(total_site_reorg_energies) # adjust site energies by reorganisation shift evals, evecs = utils.sorted_eig(site_hamiltonian) # calculate exciton reorganisation energies exciton_reorg_energies = np.array([os.exciton_reorg_energy(evecs[i], total_site_reorg_energies) for i in range(site_hamiltonian.shape[0])]) print exciton_reorg_energies evals = evals - exciton_reorg_energies # adjust to bare exciton reorganisation energies MRT = os.modified_redfield_rates(evals, evecs, g_site, g_site_dot, g_site_dot_dot, total_site_reorg_energy, temperature, time) rates.append(MRT[0,1]) rates_data.append(rates) #np.savez('../../data/modified_redfield_test_high_energy_modes_data.npz', delta_E_values=delta_E_values, coupling_values=coupling_values, rates=rates_data) for i,rates in enumerate(rates_data): plt.subplot(1,3,i+1) plt.plot(delta_E_values, utils.WAVENUMS_TO_INVERSE_PS*np.array(rates)) plt.show() # data = np.load('../../data/modified_redfield_test_high_energy_modes_data.npz')
# site-CT couplings couplings = np.array([[0,150.,-42.,-55.,-6.,17.,0,0], [0,0,-56.,-36.,20.,-2.,0,0], [0,0,0,7.,46.,-4.,70.,0], [0,0,0,0,-5.,37.,0,0], [0,0,0,0,0,-3.,70.,0], [0,0,0,0,0,0,0,0], [0,0,0,0,0,0,0,40.], [0,0,0,0,0,0,0,0]]) coherent_hamiltonian = np.diag(average_site_CT_energies) + couplings + couplings.T system_dimension = 10 system_hamiltonian = np.zeros((system_dimension,system_dimension)) system_hamiltonian[1:7,1:7] = coherent_hamiltonian[:6,:6] evalues, evectors = utils.sorted_eig(coherent_hamiltonian[:6,:6]) site_exciton_transform = np.eye(10) site_exciton_transform[1:7, 1:7] = evectors.T reorg_energy = 35. cutoff_freq = 40. temperature = 300. # jump operators. basis { ground , P_D1 , P_D2 , Chl_D1 , Chl_D2 , Phe_D1 , Phe_D2 , CT1 , CT2 , empty } # 2 for pumping from ground state # 12 for primary CT # 2 for secondary CT # 2 for coupling to leads jump_operators = np.zeros((18, system_dimension, system_dimension)) # excitation: ground -> Chl_D1 jump_operators[0] = np.dot(site_exciton_transform, np.dot(np.array([[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
# basis { PEB_50/61C, DBV_A, DVB_B, PEB_82C, PEB_158C, PEB_50/61D, PEB_82D, PEB_158D } site_energies = np.array([18532., 18008., 17973., 18040., 18711., 19574., 19050., 18960.]) # no reorganisation shift included couplings = np.array([[0, 1., -37., 37., 23., 92., -16., 12.], [0, 0, 4., -11., 33., -39., -46., 3.], [0, 0, 0, 45., 3., 2., -11., 34.], [0, 0, 0, 0, -7., -17., -3., 6.], [0, 0, 0, 0, 0, 18., 7., 6.], [0, 0, 0, 0, 0, 0, 40., 26.], [0, 0, 0, 0, 0, 0, 0, 7.], [0, 0, 0, 0, 0, 0, 0, 0]]) site_hamiltonian = np.diag(site_energies) + couplings + couplings.T evals, evecs = utils.sorted_eig(site_hamiltonian) site_reorg_shift = 110. site_reorg_energies = np.zeros(site_energies.size) site_reorg_energies.fill(site_reorg_shift) shifted_site_hamiltonian = site_hamiltonian + np.diag(site_reorg_energies) shifted_evals, shifted_evecs = utils.sorted_eig(shifted_site_hamiltonian) exciton_reorg_energies = os.exciton_reorg_energy(shifted_evecs, site_reorg_energies) print evals print shifted_evals - exciton_reorg_energies print exciton_reorg_energies for i,el in enumerate(evecs.flatten()): print shifted_evecs.flatten()[i] + 0.0000001 > el > shifted_evecs.flatten()[i] - 0.0000001
# try to reproduce dimer rates using code copied from Mathematica rates_data = [] time_interval = 0.5 time = np.linspace(0, time_interval, int(time_interval * 2000)) lbf_coeffs = os.lbf_coeffs(reorg_energy, cutoff_freq, temperature, None, 0) g_site = os.site_lbf_ed(time, lbf_coeffs) g_site_dot = os.site_lbf_dot_ed(time, lbf_coeffs) g_site_dot_dot = os.site_lbf_dot_dot_ed(time, lbf_coeffs) scaling = 1. for i, V in enumerate(coupling_values): print 'calculating rates for coupling ' + str(V) rates = [] for delta_E in delta_E_values: evals, evecs = utils.sorted_eig(hamiltonian(delta_E, V)) print evals exciton_reorg_energies = np.array([ os.exciton_reorg_energy(exciton, [reorg_energy, reorg_energy]) for exciton in evecs ]) print exciton_reorg_energies rates.append( os.modified_redfield_rates_general( evals, evecs, np.array([g_site, scaling * g_site]), np.array([g_site_dot, scaling * g_site_dot]), np.array([g_site_dot_dot, scaling * g_site_dot_dot]), np.array([reorg_energy, scaling * reorg_energy]), temperature, time)[0][0, 1]) #rates.append(os.MRT_rates(hamiltonian(delta_E, V), np.array([reorg_energy, reorg_energy]), cutoff_freq, temperature, None)[0,1]) plt.subplot(1, coupling_values.size, i + 1)
g_site_dot_dot = os.site_lbf_dot_dot_ed(integration_time, coeffs) total_site_reorg_energy = reorg_energy1 + reorg_energy2 + np.sum([mode[0]*mode[1] for mode in mode_params]) # parameters for time evolution duration = 5. timestep = 0.01 time = np.arange(0, duration+timestep, timestep) #init_dv = np.array([0.35, 0.12, 0.1, 0.1, 0.34, 0.61, 0.46, 0.5]) # init state in site basis site_history_sum = np.zeros((average_site_hamiltonian.shape[0], time.size)) for n in range(num_realisations): print 'Calculating realisation ' + str(n+1) + ' at time ' +str(datetime.now().time()) # calculate Hamiltonian for this realisation ie. add site_shifts to average site energies and construct Hamiltonian site_hamiltonian = np.diag(average_site_energies + total_site_reorg_energy + site_shifts[n]) + couplings + couplings.T # now including reorganisation shift evals, evecs = utils.sorted_eig(site_hamiltonian) # make sure to return excitons in basis going from lowest to highest energy with sorted_eig site_reorg_energies = np.zeros(site_hamiltonian.shape[0]) site_reorg_energies.fill(total_site_reorg_energy) exciton_reorg_energies = np.zeros(site_hamiltonian.shape[0]) for i in range(site_hamiltonian.shape[0]): exciton_reorg_energies[i] = os.exciton_reorg_energy(evecs[i], site_reorg_energies) # calculate exciton reorg energies evals = evals - exciton_reorg_energies # shift exciton energies down by exciton reorg energies # calculate modified Redfield rates rates = os.MRT_rate_PE545_quick(evals, evecs, g_site, g_site_dot, g_site_dot_dot, total_site_reorg_energy, temperature, integration_time) # construct Liouvillian for system liouvillian = np.zeros((rates.shape[0], rates.shape[1])) for i,row in enumerate(rates.T): liouvillian[i,i] = -np.sum(row) liouvillian += rates