Source code for MoleCool.tools.ODEs

# -*- coding: utf-8 -*-
"""
This module contains the definitions of all ordinary differential equations
(ODEs) which are used in scipy's :func:`scipy.integrate.solve_ivp()`, 
e.g. within the rate model and optical Bloch equations (see
:meth:`~.System.System.calc_rateeqs` and :meth:`~.System.System.calc_OBEs`).
"""
import numpy as np
from scipy.constants import c,h,hbar,pi,g,physical_constants
from scipy.constants import k as k_B
from scipy.constants import u as u_mass
from math import floor
from numba import jit, prange
#%%
[docs] @jit(nopython=True,parallel=False,fastmath=True) #original (slow) ODE form from the Fokker-Planck paper def ode0_OBEs(T,y_vec,lNum,uNum,pNum,G,f,om_eg,om_k,betaB,dMat,muMat,M_indices,h_gek,h_gege,phi): N = lNum+uNum dymat = np.zeros((N,N),dtype=np.complex64) ymat = np.zeros((N,N),dtype=np.complex64) count = 0 for i in range(N): for j in range(i,N): ymat[i,j] = y_vec[count] + 1j* y_vec[count+1] count += 2 ymat += np.conj(ymat.T) #is diagonal remaining purely real or complex? for index in range(N): ymat[index,index] *=0.5 for a in range(lNum): for b in range(uNum): for q in [-1,0,1]: for k in range(pNum): for c in range(lNum): dymat[a,lNum+b] += 1j*G[k]*f[k,q+1]/2/(2**0.5) *h_gek[c,b,k]*np.exp(1j*phi[k]+1j*T*(om_eg[c,b]-om_k[k]))* dMat[c,b,q+1]* ymat[a,c] for c_ in range(uNum): dymat[a,lNum+b] -= 1j*G[k]*f[k,q+1]/2/(2**0.5) *h_gek[a,c_,k]*np.exp(1j*phi[k]+1j*T*(om_eg[a,c_]-om_k[k]))* dMat[a,c_,q+1]* ymat[lNum+c_,lNum+b] for n in M_indices[1][b]: dymat[a,lNum+b] += 1j*(-1.)**q* betaB[q+1]* muMat[1][b,n,-q+1]* ymat[a,lNum+n] for m in M_indices[0][a]: dymat[a,lNum+b] -= 1j*(-1.)**q* betaB[q+1]* muMat[0][m,a,-q+1]* ymat[m,lNum+b] dymat[a,lNum+b] -= 0.5*ymat[a,lNum+b] for a in range(uNum): for b in range(a,uNum): for q in [-1,0,1]: for k in range(pNum): for c in range(lNum): dymat[lNum+a,lNum+b] += 1j*G[k]/2/(2**0.5)*( f[k,q+1] *h_gek[c,b,k]*np.exp(1j*phi[k]+1j*T*(om_eg[c,b]-om_k[k]))* dMat[c,b,q+1]* ymat[lNum+a,c] - np.conj(f[k,q+1]) *h_gek[c,a,k]*np.exp(-1j*phi[k]-1j*T*(om_eg[c,a]-om_k[k]))* dMat[c,a,q+1]* ymat[c,lNum+b]) for n in M_indices[1][b]: dymat[lNum+a,lNum+b] += 1j*(-1.)**q* betaB[q+1]* muMat[1][b,n,-q+1]* ymat[lNum+a,lNum+n] for m in M_indices[1][a]: dymat[lNum+a,lNum+b] -= 1j*(-1.)**q* betaB[q+1]* muMat[1][m,a,-q+1]* ymat[lNum+m,lNum+b] dymat[lNum+a,lNum+b] -= ymat[lNum+a,lNum+b] for a in range(lNum): for b in range(a,lNum): for q in [-1,0,1]: for k in range(pNum): for c_ in range(uNum): dymat[a,b] -= 1j*G[k]/2/(2**0.5)*( f[k,q+1] *h_gek[a,c_,k]*np.exp(1j*phi[k]+1j*T*(om_eg[a,c_]-om_k[k]))* dMat[a,c_,q+1]* ymat[lNum+c_,b] - np.conj(f[k,q+1]) *h_gek[b,c_,k]*np.exp(-1j*phi[k]-1j*T*(om_eg[b,c_]-om_k[k]))* dMat[b,c_,q+1]* ymat[a,lNum+c_]) for n in M_indices[0][b]: dymat[a,b] += 1j*(-1.)**q* betaB[q+1]* muMat[0][b,n,-q+1]* ymat[a,n] for m in M_indices[0][a]: dymat[a,b] -= 1j*(-1.)**q* betaB[q+1]* muMat[0][m,a,-q+1]* ymat[m,b] for c_ in range(uNum): for c__ in range(uNum): dymat[a,b] += dMat[a,c_,q+1]* dMat[b,c__,q+1] *h_gege[a,c_,b,c__]*np.exp(1j*T*(om_eg[a,c_]-om_eg[b,c__]))* ymat[lNum+c_,lNum+c__] dy_vec = np.zeros( N*(N+1) ) count = 0 for i in range(N): for j in range(i,N): dy_vec[count] = dymat[i,j].real dy_vec[count+1] = dymat[i,j].imag count += 2 return dy_vec
#%%
[docs] def ode_3level(T,y_vec,Om_gi , Om_ie, Gamma_e, Gamma_i): N = 4 dymat = np.zeros((N,N),dtype=np.complex128) ymat = np.zeros((N,N),dtype=np.complex128) count = 0 for i in range(N): for j in range(i,N): ymat[i,j] = y_vec[count] + 1j* y_vec[count+1] count += 2 ymat += np.conj(ymat.T) #is diagonal remaining purely real or complex? for index in range(N): ymat[index,index] *=0.5 print(ymat) # gg dymat[0,0] = 0.5j*Om_gi*(ymat[0,1]-ymat[1,0]) + Gamma_i*ymat[1,1] # ii dymat[1,1] = -0.5j*Om_gi*(ymat[0,1]-ymat[1,0]) +0.5j*Om_ie*(ymat[1,2]-ymat[2,1]) \ - Gamma_i*ymat[1,1] + Gamma_e*ymat[2,2] # ee dymat[2,2] = -0.5j*Om_ie*(ymat[1,2]-ymat[2,1]) - Gamma_e*ymat[2,2] # gi dymat[0,1] = -0.5j*Om_gi*(ymat[1,1]-ymat[0,0]) + 0.5j*Om_ie*ymat[0,2] - Gamma_i/2*ymat[0,1] # ge dymat[0,2] = -0.5j*Om_gi*ymat[1,2] + 0.5j*Om_ie*ymat[0,1] - Gamma_e/2*ymat[0,2] # ie dymat[1,2] = -0.5j*Om_ie*(ymat[2,2]-ymat[1,1]) - 0.5j*Om_gi*ymat[0,2] \ -(Gamma_i+Gamma_e)/2*ymat[1,2] dy_vec = np.zeros( N*(N+1) ) count = 0 for i in range(N): for j in range(i,N): dy_vec[count] = dymat[i,j].real dy_vec[count+1] = dymat[i,j].imag count += 2 if i==j: if dymat[i,j].imag != 0.0: print(i) return dy_vec
#%%
[docs] @jit(nopython=True,parallel=False,fastmath=True) #same as ode1_OBEs_opt3 but compatible with immediate states def ode1_OBEs_opt4(T,y_vec,lNum,uNum,iNum,pNum,M_indices,Gfd,om_gek,betamu,dd,ck_indices,Gam_fac): N = lNum+uNum dymat = np.zeros((N,N),dtype=np.complex128) ymat = np.zeros((N,N),dtype=np.complex128) count = 0 for i in range(N): for j in range(i,N): ymat[i,j] = y_vec[count] + 1j* y_vec[count+1] count += 2 ymat += np.conj(ymat.T) #is diagonal remaining purely real or complex? for index in range(N): ymat[index,index] *=0.5 # print(ymat) for a in range(uNum): for c,k in zip(ck_indices[1][a][0],ck_indices[1][a][1]): for b in range(lNum): dymat[b,lNum+a] += Gfd[c,a,k]* np.exp(1j*om_gek[c,a,k]*T)* ymat[b,c] for b in range(a,uNum): if not ( (b >= uNum-iNum) and (a < uNum-iNum)): dymat[lNum+a,lNum+b] += np.conj(Gfd[c,a,k])* np.exp(-1j*om_gek[c,a,k]*T)* ymat[c,lNum+b] # for n in M_indices[1][a]: # for b in range(lNum): # dymat[b,lNum+a] += betamu[1][a,n] * ymat[b,lNum+n] # for m in M_indices[1][a]: # for b in range(a,uNum): # if not (a >= iNum and b >= iNum): # dymat[lNum+a,lNum+b] -= betamu[1][m,a] * ymat[lNum+m,lNum+b] for b in range(a,uNum): # for n in M_indices[1][b]: # if not (a >= iNum and b >= iNum): # dymat[lNum+a,lNum+b] += betamu[1][b,n] * ymat[lNum+a,lNum+n] dymat[lNum+a,lNum+b] -= ymat[lNum+a,lNum+b]*(Gam_fac[a]+Gam_fac[b])/2 #!!!!!!!!!!!! for b in range(uNum): for c,k in zip(ck_indices[1][b][0],ck_indices[1][b][1]): for a in range(0,b+1): dymat[lNum+a,lNum+b] += Gfd[c,b,k]* np.exp(1j*om_gek[c,b,k]*T)* ymat[lNum+a,c] for a in range(lNum): for c_,k in zip(ck_indices[0][a][0],ck_indices[0][a][1]): for b in range(uNum): dymat[a,lNum+b] -= Gfd[a,c_,k]* np.exp(1j*om_gek[a,c_,k]*T)* ymat[lNum+c_,lNum+b] for b in range(a,lNum): if not ( (a < lNum-iNum) and (b >= lNum-iNum) ): dymat[a,b] -= Gfd[a,c_,k]* np.exp(1j*om_gek[a,c_,k]*T)* ymat[lNum+c_,b] # for m in M_indices[0][a]: # for b in range(uNum): # dymat[a,lNum+b] -= betamu[0][m,a] * ymat[m,lNum+b] # for b in range(a,lNum): # dymat[a,b] -= betamu[0][m,a] * ymat[m,b] for b in range(uNum): if not ( (a >= lNum-iNum) and (b < uNum-iNum)): dymat[a,lNum+b] -= 0.5*Gam_fac[b]*ymat[a,lNum+b] #!!!!!!!!!!!! for b in range(a,lNum): # for n in M_indices[0][b]: # dymat[a,b] += betamu[0][b,n] * ymat[a,n] if not ( (a < lNum-iNum) and (b >= lNum-iNum) ): for c_ in range(uNum): for c__ in range(uNum): dymat[a,b] += dd[a,c_,b,c__] * np.exp(1j*T*(om_gek[a,c_,0]-om_gek[b,c__,0])) * ymat[lNum+c_,lNum+c__] *(Gam_fac[c_]+Gam_fac[c__])/2#!!!!!!!!!!!!???? for b in range(lNum-1,-1,-1): for c_,k in zip(ck_indices[0][b][0],ck_indices[0][b][1]): for a in range(0,b+1): dymat[a,b] -= np.conj(Gfd[b,c_,k])* np.exp(-1j*om_gek[b,c_,k]*T)* ymat[a,lNum+c_] dymat[0:lNum-iNum,lNum-iNum:lNum] += dymat[0:lNum-iNum,N-iNum:N] dymat[lNum-iNum:lNum,lNum:N-iNum] += np.conj(dymat[lNum:N-iNum,N-iNum:N].T) dymat[lNum-iNum:lNum,lNum-iNum:lNum] += dymat[N-iNum:N,N-iNum:N] dymat[0:lNum-iNum,N-iNum:N] = dymat[0:lNum-iNum,lNum-iNum:lNum] dymat[lNum:N-iNum,N-iNum:N] = np.conj(dymat[lNum-iNum:lNum,lNum:N-iNum].T) dymat[N-iNum:N,N-iNum:N] = dymat[lNum-iNum:lNum,lNum-iNum:lNum] dymat[lNum-iNum:lNum,N-iNum:N] = 0.0 dy_vec = np.zeros( N*(N+1) ) count = 0 for i in range(N): for j in range(i,N): dy_vec[count] = dymat[i,j].real dy_vec[count+1] = dymat[i,j].imag count += 2 if i==j: tol = 1e-12 if np.abs(dymat[i,j].imag) > tol: print('Populations got imaginary (>1e-12)') dy_vec[count+1] = 0.0 return dy_vec
#%%
[docs] @jit(nopython=True,parallel=False,fastmath=True) #same as ode1_OBEs_opt2 but compatible with two different electr. ex. states def ode1_OBEs_opt3(T,y_vec,lNum,uNum,pNum,M_indices,Gfd,om_gek,betamu,dd,ck_indices,Gam_fac): N = lNum+uNum dymat = np.zeros((N,N),dtype=np.complex128) ymat = np.zeros((N,N),dtype=np.complex128) count = 0 for i in range(N): for j in range(i,N): ymat[i,j] = y_vec[count] + 1j* y_vec[count+1] count += 2 ymat += np.conj(ymat.T) #is diagonal remaining purely real or complex? for index in range(N): ymat[index,index] *=0.5 for a in range(uNum): for c,k in zip(ck_indices[1][a][0],ck_indices[1][a][1]): for b in range(lNum): dymat[b,lNum+a] += Gfd[c,a,k]* np.exp(1j*om_gek[c,a,k]*T)* ymat[b,c] for b in range(a,uNum): dymat[lNum+a,lNum+b] += np.conj(Gfd[c,a,k])* np.exp(-1j*om_gek[c,a,k]*T)* ymat[c,lNum+b] for n in M_indices[1][a]: for b in range(lNum): dymat[b,lNum+a] += betamu[1][a,n] * ymat[b,lNum+n] for m in M_indices[1][a]: for b in range(a,uNum): dymat[lNum+a,lNum+b] -= betamu[1][m,a] * ymat[lNum+m,lNum+b] for b in range(a,uNum): for n in M_indices[1][b]: dymat[lNum+a,lNum+b] += betamu[1][b,n] * ymat[lNum+a,lNum+n] dymat[lNum+a,lNum+b] -= ymat[lNum+a,lNum+b]*(Gam_fac[a]+Gam_fac[b])/2 #!!!!!!!!!!!! for b in range(uNum-1,-1,-1): for c,k in zip(ck_indices[1][b][0],ck_indices[1][b][1]): for a in range(0,b+1): dymat[lNum+a,lNum+b] += Gfd[c,b,k]* np.exp(1j*om_gek[c,b,k]*T)* ymat[lNum+a,c] for a in range(lNum): for c_,k in zip(ck_indices[0][a][0],ck_indices[0][a][1]): for b in range(uNum): dymat[a,lNum+b] -= Gfd[a,c_,k]* np.exp(1j*om_gek[a,c_,k]*T)* ymat[lNum+c_,lNum+b] for b in range(a,lNum): dymat[a,b] -= Gfd[a,c_,k]* np.exp(1j*om_gek[a,c_,k]*T)* ymat[lNum+c_,b] for m in M_indices[0][a]: for b in range(uNum): dymat[a,lNum+b] -= betamu[0][m,a] * ymat[m,lNum+b] for b in range(a,lNum): dymat[a,b] -= betamu[0][m,a] * ymat[m,b] for b in range(uNum): dymat[a,lNum+b] -= 0.5*Gam_fac[b]*ymat[a,lNum+b] #!!!!!!!!!!!! for b in range(a,lNum): for n in M_indices[0][b]: dymat[a,b] += betamu[0][b,n] * ymat[a,n] for c_ in range(uNum): for c__ in range(uNum): dymat[a,b] += dd[a,c_,b,c__] * np.exp(1j*T*(om_gek[a,c_,0]-om_gek[b,c__,0])) * ymat[lNum+c_,lNum+c__] *(Gam_fac[c_]+Gam_fac[c__])/2#!!!!!!!!!!!!???? for b in range(lNum-1,-1,-1): for c_,k in zip(ck_indices[0][b][0],ck_indices[0][b][1]): for a in range(0,b+1): dymat[a,b] -= np.conj(Gfd[b,c_,k])* np.exp(-1j*om_gek[b,c_,k]*T)* ymat[a,lNum+c_] dy_vec = np.zeros( N*(N+1) ) count = 0 for i in range(N): for j in range(i,N): dy_vec[count] = dymat[i,j].real dy_vec[count+1] = dymat[i,j].imag count += 2 return dy_vec
#%%
[docs] @jit(nopython=True,parallel=False,fastmath=True) #same as ode1_OBEs_opt1 but further optimized by rearranging the loops def ode1_OBEs_opt2(T,y_vec,lNum,uNum,pNum,M_indices,Gfd,om_gek,betamu,dd,ck_indices): N = lNum+uNum dymat = np.zeros((N,N),dtype=np.complex128) ymat = np.zeros((N,N),dtype=np.complex128) count = 0 for i in range(N): for j in range(i,N): ymat[i,j] = y_vec[count] + 1j* y_vec[count+1] count += 2 ymat += np.conj(ymat.T) #is diagonal remaining purely real or complex? for index in range(N): ymat[index,index] *=0.5 for a in range(uNum): for c,k in zip(ck_indices[1][a][0],ck_indices[1][a][1]): for b in range(lNum): dymat[b,lNum+a] += Gfd[c,a,k]* np.exp(1j*om_gek[c,a,k]*T)* ymat[b,c] for b in range(a,uNum): dymat[lNum+a,lNum+b] += np.conj(Gfd[c,a,k])* np.exp(-1j*om_gek[c,a,k]*T)* ymat[c,lNum+b] for n in M_indices[1][a]: for b in range(lNum): dymat[b,lNum+a] += betamu[1][a,n] * ymat[b,lNum+n] for m in M_indices[1][a]: for b in range(a,uNum): dymat[lNum+a,lNum+b] -= betamu[1][m,a] * ymat[lNum+m,lNum+b] for b in range(a,uNum): for n in M_indices[1][b]: dymat[lNum+a,lNum+b] += betamu[1][b,n] * ymat[lNum+a,lNum+n] dymat[lNum+a,lNum+b] -= ymat[lNum+a,lNum+b] #!!!!!!!!!!!! for b in range(uNum-1,-1,-1): for c,k in zip(ck_indices[1][b][0],ck_indices[1][b][1]): for a in range(0,b+1): dymat[lNum+a,lNum+b] += Gfd[c,b,k]* np.exp(1j*om_gek[c,b,k]*T)* ymat[lNum+a,c] for a in range(lNum): for c_,k in zip(ck_indices[0][a][0],ck_indices[0][a][1]): for b in range(uNum): dymat[a,lNum+b] -= Gfd[a,c_,k]* np.exp(1j*om_gek[a,c_,k]*T)* ymat[lNum+c_,lNum+b] for b in range(a,lNum): dymat[a,b] -= Gfd[a,c_,k]* np.exp(1j*om_gek[a,c_,k]*T)* ymat[lNum+c_,b] for m in M_indices[0][a]: for b in range(uNum): dymat[a,lNum+b] -= betamu[0][m,a] * ymat[m,lNum+b] for b in range(a,lNum): dymat[a,b] -= betamu[0][m,a] * ymat[m,b] for b in range(uNum): dymat[a,lNum+b] -= 0.5*ymat[a,lNum+b] #!!!!!!!!!!!! for b in range(a,lNum): for n in M_indices[0][b]: dymat[a,b] += betamu[0][b,n] * ymat[a,n] for c_ in range(uNum): for c__ in range(uNum): dymat[a,b] += dd[a,c_,b,c__] * np.exp(1j*T*(om_gek[a,c_,0]-om_gek[b,c__,0])) * ymat[lNum+c_,lNum+c__] #!!!!!!!!!!!! for b in range(lNum-1,-1,-1): for c_,k in zip(ck_indices[0][b][0],ck_indices[0][b][1]): for a in range(0,b+1): dymat[a,b] -= np.conj(Gfd[b,c_,k])* np.exp(-1j*om_gek[b,c_,k]*T)* ymat[a,lNum+c_] dy_vec = np.zeros( N*(N+1) ) count = 0 for i in range(N): for j in range(i,N): dy_vec[count] = dymat[i,j].real dy_vec[count+1] = dymat[i,j].imag count += 2 return dy_vec
#%%
[docs] @jit(nopython=True,parallel=False,fastmath=True) #same as ode1_OBEs but in optimized form with ck_indices variable def ode1_OBEs_opt1(T,y_vec,lNum,uNum,pNum,M_indices,Gfd,om_gek,betamu,dd,ck_indices): N = lNum+uNum dymat = np.zeros((N,N),dtype=np.complex128) ymat = np.zeros((N,N),dtype=np.complex128) count = 0 for i in range(N): for j in range(i,N): ymat[i,j] = y_vec[count] + 1j* y_vec[count+1] count += 2 ymat += np.conj(ymat.T) #is diagonal remaining purely real or complex? for index in range(N): ymat[index,index] *=0.5 for a in range(lNum): for b in range(uNum): for c,k in zip(*ck_indices[1][b]): dymat[a,lNum+b] += Gfd[c,b,k]* np.exp(1j*om_gek[c,b,k]*T)* ymat[a,c] for c_,k in zip(*ck_indices[0][a]): dymat[a,lNum+b] -= Gfd[a,c_,k]* np.exp(1j*om_gek[a,c_,k]*T)* ymat[lNum+c_,lNum+b] for n in M_indices[1][b]: dymat[a,lNum+b] += betamu[1][b,n] * ymat[a,lNum+n] for m in M_indices[0][a]: dymat[a,lNum+b] -= betamu[0][m,a] * ymat[m,lNum+b] dymat[a,lNum+b] -= 0.5*ymat[a,lNum+b] for a in range(uNum): for b in range(a,uNum): for c,k in zip(*ck_indices[1][b]): dymat[lNum+a,lNum+b] += Gfd[c,b,k]* np.exp(1j*om_gek[c,b,k]*T)* ymat[lNum+a,c] for c,k in zip(*ck_indices[1][a]): dymat[lNum+a,lNum+b] += np.conj(Gfd[c,a,k])* np.exp(-1j*om_gek[c,a,k]*T)* ymat[c,lNum+b] for n in M_indices[1][b]: dymat[lNum+a,lNum+b] += betamu[1][b,n] * ymat[lNum+a,lNum+n] for m in M_indices[1][a]: dymat[lNum+a,lNum+b] -= betamu[1][m,a] * ymat[lNum+m,lNum+b] dymat[lNum+a,lNum+b] -= ymat[lNum+a,lNum+b] for a in range(lNum): for b in range(a,lNum): for c_,k in zip(*ck_indices[0][a]): dymat[a,b] -= Gfd[a,c_,k]* np.exp(1j*om_gek[a,c_,k]*T)* ymat[lNum+c_,b] for c_,k in zip(*ck_indices[0][b]): dymat[a,b] -= np.conj(Gfd[b,c_,k])* np.exp(-1j*om_gek[b,c_,k]*T)* ymat[a,lNum+c_] for n in M_indices[0][b]: dymat[a,b] += betamu[0][b,n] * ymat[a,n] for m in M_indices[0][a]: dymat[a,b] -= betamu[0][m,a] * ymat[m,b] for c_ in range(uNum): for c__ in range(uNum): dymat[a,b] += dd[a,c_,b,c__] * np.exp(1j*T*(om_gek[a,c_,0]-om_gek[b,c__,0])) * ymat[lNum+c_,lNum+c__] dy_vec = np.zeros( N*(N+1) ) count = 0 for i in range(N): for j in range(i,N): dy_vec[count] = dymat[i,j].real dy_vec[count+1] = dymat[i,j].imag count += 2 return dy_vec
#%%
[docs] @jit(nopython=True,parallel=False,fastmath=True) #same as ode0_OBEs but in optimized form with less input variables def ode1_OBEs(T,y_vec,lNum,uNum,pNum,M_indices,Gfd,om_gek,betamu,dd): N = lNum+uNum dymat = np.zeros((N,N),dtype=np.complex128) ymat = np.zeros((N,N),dtype=np.complex128) count = 0 for i in range(N): for j in range(i,N): ymat[i,j] = y_vec[count] + 1j* y_vec[count+1] count += 2 ymat += np.conj(ymat.T) #is diagonal remaining purely real or complex? for index in range(N): ymat[index,index] *=0.5 for a in range(lNum): for b in range(uNum): for c in range(lNum): tmp = 0 for k in range(pNum): tmp += Gfd[c,b,k]* np.exp(1j*om_gek[c,b,k]*T) dymat[a,lNum+b] += tmp* ymat[a,c] for c_ in range(uNum): tmp = 0 for k in range(pNum): tmp += Gfd[a,c_,k]* np.exp(1j*om_gek[a,c_,k]*T) dymat[a,lNum+b] -= tmp* ymat[lNum+c_,lNum+b] for n in M_indices[1][b]: dymat[a,lNum+b] += betamu[1][b,n] * ymat[a,lNum+n] for m in M_indices[0][a]: dymat[a,lNum+b] -= betamu[0][m,a] * ymat[m,lNum+b] dymat[a,lNum+b] -= 0.5*ymat[a,lNum+b] for a in range(uNum): for b in range(a,uNum): for c in range(lNum): tmp = 0 for k in range(pNum): tmp += Gfd[c,b,k]* np.exp(1j*om_gek[c,b,k]*T) dymat[lNum+a,lNum+b] += tmp* ymat[lNum+a,c] for c in range(lNum): tmp = 0 for k in range(pNum): tmp += np.conj(Gfd[c,a,k])* np.exp(-1j*om_gek[c,a,k]*T) dymat[lNum+a,lNum+b] += tmp* ymat[c,lNum+b] for n in M_indices[1][b]: dymat[lNum+a,lNum+b] += betamu[1][b,n] * ymat[lNum+a,lNum+n] for m in M_indices[1][a]: dymat[lNum+a,lNum+b] -= betamu[1][m,a] * ymat[lNum+m,lNum+b] dymat[lNum+a,lNum+b] -= ymat[lNum+a,lNum+b] for a in range(lNum): for b in range(a,lNum): for c_ in range(uNum): tmp = 0 for k in range(pNum): tmp += Gfd[a,c_,k]* np.exp(1j*om_gek[a,c_,k]*T) dymat[a,b] -= tmp* ymat[lNum+c_,b] for c_ in range(uNum): tmp = 0 for k in range(pNum): tmp += np.conj(Gfd[b,c_,k])* np.exp(-1j*om_gek[b,c_,k]*T) dymat[a,b] -= tmp* ymat[a,lNum+c_] for n in M_indices[0][b]: dymat[a,b] += betamu[0][b,n] * ymat[a,n] for m in M_indices[0][a]: dymat[a,b] -= betamu[0][m,a] * ymat[m,b] for c_ in range(uNum): for c__ in range(uNum): dymat[a,b] += dd[a,c_,b,c__] * np.exp(1j*T*(om_gek[a,c_,0]-om_gek[b,c__,0])) * ymat[lNum+c_,lNum+c__] dy_vec = np.zeros( N*(N+1) ) count = 0 for i in range(N): for j in range(i,N): dy_vec[count] = dymat[i,j].real dy_vec[count+1] = dymat[i,j].imag count += 2 return dy_vec
#%%
[docs] @jit(nopython=True,parallel=False,fastmath=False) def ode0_rateeqs_jit(t,N,lNum,uNum,pNum,Gamma,r,R1sum,R2sum,tswitch,M): dNdt = np.zeros(lNum+uNum) if floor(t/tswitch)%2 == 1: R_sum=R1sum else: R_sum=R2sum for l in prange(lNum): for u in prange(uNum): dNdt[l] += Gamma[u]* r[l,u] * N[lNum+u] + R_sum[l,u] * (N[lNum+u] - N[l]) if not M.size == 0: for k in prange(lNum): dNdt[l] -= M[l,k] * (N[l]-N[k]) for u in prange(uNum): dNdt[lNum+u] = -Gamma[u]*N[lNum+u] for l in prange(lNum): dNdt[lNum+u] += R_sum[l,u] * (N[l] - N[lNum+u]) return dNdt
#%%
[docs] def ode0_rateeqs(t,N,lNum,uNum,pNum,Gamma,r,R1sum,R2sum,tswitch,M): dNdt = np.zeros(lNum+uNum) if floor(t/tswitch)%2 == 1: R_sum=R1sum else: R_sum=R2sum Nlu_matr = np.subtract.outer(N[:lNum], N[lNum:lNum+uNum]) dNdt[:lNum] = - np.sum(R_sum*Nlu_matr, axis=1) + Gamma*np.dot(r,N[lNum:lNum+uNum]) if not M.size == 0: dNdt[:lNum] -= np.sum(M * np.subtract.outer(N[:lNum], N[:lNum]), axis=1) dNdt[lNum:lNum+uNum] = -Gamma*N[lNum:lNum+uNum] + np.sum(R_sum*Nlu_matr, axis=0) return dNdt
#%%
[docs] def ode1_rateeqs(t,y,lNum,uNum,pNum,Gamma,r,rx1,rx2,delta,sp_,w,k,r_k,m,tswitch,M,pos_dep): dydt = np.zeros(lNum+uNum+3+3) if floor(t/tswitch)%2 == 1: rx=rx1 else: rx=rx2 sp = sp_.copy() # position dependent Force on particle due to Gaussian shape of Laserbeam: if pos_dep: for p in range(pNum): #if abs(y[-3]) > 1e-3: sp[p] = 1e-1 d = np.linalg.norm(np.cross( y[-3:]-r_k[p] , k[p]/np.linalg.norm(k[p]) )) # r2 = np.dot(y[-3:], y[-3:]) - (np.dot(k[p], y[-3:])/np.linalg.norm(k[p]))**2 sp[p] = sp[p] * np.exp(-2 * d**2 / ((0.2*w[p])**2) ) # shape of k: (pNum,3) # shape of rx = (lNum,uNum,pNum), sp.shape = (pNum) ==> (rx*sp).shape = (lNum,uNum,pNum) # R = Gamma/2 * (rx*sp) / ( 1+4*(delta)**2/Gamma**2 ) R = Gamma/2 * (rx*sp) / ( 1+4*( delta - np.dot(k,y[lNum+uNum:lNum+uNum+3]) )**2/Gamma**2 ) # sum R over pNum R_sum = np.sum(R,axis=2) # shape(Nlu_matr) = (lNum,uNum) Nlu_matr = y[:lNum,None] - y[None,lNum:lNum+uNum]#np.subtract.outer(y[:lNum], y[lNum:lNum+uNum]) # __________ODE:__________ # N_l' = ... dydt[:lNum] = - np.sum(R_sum*Nlu_matr, axis=1) + Gamma*np.dot(r,y[lNum:lNum+uNum]) if not M.size == 0: # magnetic remixing of the ground states dydt[:lNum] -= np.sum(M * np.subtract.outer(y[:lNum], y[:lNum]), axis=1) # N_u' = ... dydt[lNum:lNum+uNum] = -Gamma*y[lNum:lNum+uNum] + np.sum(R_sum*Nlu_matr, axis=0) # v' = ... dydt[lNum+uNum:lNum+uNum+3] = hbar/m * np.sum(np.dot(R,k) * Nlu_matr[:,:,None], axis=(0,1)) #+ g # r' = ... dydt[lNum+uNum+3:lNum+uNum+3+3] = y[lNum+uNum:lNum+uNum+3] return dydt
#%%
[docs] @jit(nopython=True,parallel=False,fastmath=False) def ode1_rateeqs_jit(t,y,lNum,uNum,pNum,Gamma,r,rx1,rx2,delta,sp_,w,w_cyl,k,kabs,r_k,r_cyl_trunc,dir_cyl,m,tswitch,M,pos_dep,beta): dydt = np.zeros(lNum+uNum+3+3) if floor(t/tswitch)%2 == 1: rx=rx1 else: rx=rx2 sp = sp_.copy() # position dependent Force on particle due to Gaussian shape of Laserbeam: if pos_dep: for p in range(pNum): r_ = y[-3:] - r_k[p] if w_cyl[p] != 0.0: # calculation for a beam which is widened by a cylindrical lens d2_w = np.dot(dir_cyl[p],r_)**2 if d2_w > r_cyl_trunc[p]**2: #test if position is larger than the truncation radius along the dir_cyl direction sp[:,:,p] = 0.0 else: d2 = np.dot(np.cross(dir_cyl[p],k[p]/kabs[p]),r_)**2 sp[:,:,p] *= np.exp(-2*(d2_w/w_cyl[p]**2 + d2/w[p]**2)) else: r_perp = np.cross( r_ , k[p]/kabs[p] ) sp[:,:,p] *= np.exp(-2 * np.dot(r_perp,r_perp) / w[p]**2 ) delta_ = delta + 2*pi*beta*t #frequency chirping # shape of k: (pNum,3) # shape of rx = (lNum,uNum,pNum), sp.shape = (pNum) ==> (rx*sp).shape = (lNum,uNum,pNum) # R = Gamma/2 * (rx*sp) / ( 1+4*(delta)**2/Gamma**2 ) R = Gamma/2 * (rx*sp) / ( 1+4*( delta_ - np.dot(k,y[lNum+uNum:lNum+uNum+3]) )**2/(Gamma**2) ) # sum R over pNum R_sum = np.sum(R,axis=2) # __________ODE:__________ # N_l' = ... for l in range(lNum): for u in range(uNum): dydt[l] += Gamma[0,u,0]* r[l,u] * y[lNum+u] + R_sum[l,u] * (y[lNum+u] - y[l]) if not M.size == 0: for l1 in range(lNum): for l2 in range(lNum): dydt[l1] -= M[l1,l2] * (y[l1]-y[l2]) # N_u' = ... for u in range(uNum): dydt[lNum+u] = -Gamma[0,u,0]*y[lNum+u] for l in range(lNum): dydt[lNum+u] += R_sum[l,u] * (y[l] - y[lNum+u]) # v' = ... for i in range(3): for l in range(lNum): for u in range(uNum): for p in range(pNum): dydt[lNum+uNum+i] += hbar/m * k[p,i] * R[l,u,p] * ( y[l] - y[lNum+u] ) # r' = ... dydt[lNum+uNum+3:lNum+uNum+3+3] = y[lNum+uNum:lNum+uNum+3] return dydt
#%%
[docs] @jit(nopython=True,parallel=False,fastmath=False) def ode1_rateeqs_jit_testI(t,y,lNum,uNum,pNum,Gamma,r,rx1,rx2,delta,sp_,k,m,tswitch,M,pos_dep,beta,I_tot): dydt = np.zeros(lNum+uNum+3+3) if floor(t/tswitch)%2 == 1: rx=rx1 else: rx=rx2 sp = sp_.copy() # position dependent Force on particle due to Gaussian shape of Laserbeam: if pos_dep: sp *= I_tot(y[-3:]) #shape of sp: (uNum,pNum), shape of I_tot: (pNum) delta_ = delta + 2*pi*beta*t #frequency chirping # shape of k: (pNum,3) # shape of rx = (lNum,uNum,pNum), sp.shape = (pNum) ==> (rx*sp).shape = (lNum,uNum,pNum) # R = Gamma/2 * (rx*sp) / ( 1+4*(delta)**2/Gamma**2 ) R = np.reshape(Gamma,(1,-1,1))/2 * (rx*sp) / ( 1+4*( delta_ - np.dot(k,y[lNum+uNum:lNum+uNum+3]) )**2/np.reshape(Gamma,(1,-1,1))**2 ) # sum R over pNum R_sum = np.sum(R,axis=2) # __________ODE:__________ # N_l' = ... for l in range(lNum): for u in range(uNum): dydt[l] += Gamma[u]* r[l,u] * y[lNum+u] + R_sum[l,u] * (y[lNum+u] - y[l]) if not M.size == 0: for l1 in range(lNum): for l2 in range(lNum): dydt[l1] -= M[l1,l2] * (y[l1]-y[l2]) # N_u' = ... for u in range(uNum): dydt[lNum+u] = -Gamma[u]*y[lNum+u] for l in range(lNum): dydt[lNum+u] += R_sum[l,u] * (y[l] - y[lNum+u]) # v' = ... for i in range(3): for l in range(lNum): for u in range(uNum): for p in range(pNum): dydt[lNum+uNum+i] += hbar/m * k[p,i] * R[l,u,p] * ( y[l] - y[lNum+u] ) # r' = ... dydt[lNum+uNum+3:lNum+uNum+3+3] = y[lNum+uNum:lNum+uNum+3] return dydt