mirror of
https://github.com/vale981/master-thesis
synced 2025-03-06 02:21:38 -05:00
464 lines
13 KiB
Python
464 lines
13 KiB
Python
import numpy as np
|
|
from scipy.special import gamma as gamma_func
|
|
|
|
# from scipy.special import zeta as zeta_func
|
|
from scipy.integrate import quad
|
|
from collections import namedtuple
|
|
from functools import partial
|
|
|
|
# import mpmath
|
|
import warnings
|
|
import traceback
|
|
import mpmath
|
|
|
|
# import mypath
|
|
# import convenientMath as cm
|
|
# import persistentdata as pd
|
|
# import binfootprint as bf
|
|
# import fcSpline
|
|
|
|
import logging
|
|
|
|
log = logging.getLogger(__name__)
|
|
|
|
#############################################################
|
|
#
|
|
# parameter types
|
|
#
|
|
#############################################################
|
|
|
|
|
|
def zeta_func(x, q):
|
|
return np.complex128(mpmath.zeta(x, q))
|
|
|
|
|
|
obcf_param_type = namedtuple(
|
|
typename="obcf_param_type", field_names=["s", "eta", "gamma"]
|
|
)
|
|
|
|
obcf_param_KAST_ANKERHOLD_type = namedtuple(
|
|
typename="obcf_param_KAST_ANKERHOLD_type", field_names=["s", "alpha", "omega_c"]
|
|
)
|
|
|
|
#############################################################
|
|
#
|
|
# class definitiona
|
|
#
|
|
#############################################################
|
|
|
|
|
|
class OSD(object):
|
|
r"""ohmic spectral density
|
|
|
|
J(w) = eta w^s exp(-w / gamma)
|
|
"""
|
|
|
|
def __init__(self, s, eta, gamma, normed=False, mp=False):
|
|
"""
|
|
represents an super / sub Ohmic spectral density function
|
|
|
|
normed: if set True, scaled eta, such that int_0^infty J(w) = 1
|
|
|
|
mp: if set True, use mpmath for evaluation
|
|
|
|
this class implements set_state and get_state and is suitable for binfootprint
|
|
|
|
make sure, that s, eta, and gamma are regular float objects
|
|
"""
|
|
|
|
# the "args" attributes are use for get_state and set_state
|
|
self.s_arg = s
|
|
self.gamma_arg = gamma
|
|
self.normed_arg = normed
|
|
self.mp_arg = mp
|
|
if normed:
|
|
self.eta_arg = None
|
|
else:
|
|
self.eta_arg = eta
|
|
|
|
# non non math version
|
|
if not mp:
|
|
self._exp = np.exp
|
|
self._gamma_func = gamma_func
|
|
self.s = s
|
|
self.gamma = gamma
|
|
self.eta = eta
|
|
|
|
# mpmath version
|
|
else:
|
|
self._exp = mpmath.exp
|
|
self._gamma_func = mpmath.gamma
|
|
self.s = mpmath.mpf(s)
|
|
self.gamma = mpmath.mpf(gamma)
|
|
self.eta = mpmath.mpf(eta)
|
|
|
|
if normed:
|
|
self.eta = self.eta / self.integral()
|
|
self.c = self.eta * self._gamma_func(self.s)
|
|
|
|
def __call__(self, w):
|
|
if isinstance(w, np.ndarray):
|
|
res = np.empty_like(w)
|
|
idx_l0 = np.where(w < 0)
|
|
res[idx_l0] = 0
|
|
idx_ge0 = np.where(w >= 0)
|
|
res[idx_ge0] = (
|
|
self.eta * w[idx_ge0] ** self.s * self._exp(-w[idx_ge0] / self.gamma)
|
|
)
|
|
return res
|
|
else:
|
|
if w < 0:
|
|
return 0
|
|
else:
|
|
return self.eta * w ** self.s * self._exp(-w / self.gamma)
|
|
|
|
def maximum_at(self):
|
|
return self.s * self.gamma
|
|
|
|
def maximum_val(self):
|
|
return self(self.maximum_at())
|
|
|
|
def integral(self):
|
|
return self.eta * self.gamma ** (self.s + 1) * self._gamma_func(self.s + 1)
|
|
|
|
def reorganization_energy(self):
|
|
"""
|
|
int_0^infity J(w) / w
|
|
"""
|
|
return self.eta * self.gamma ** self.s * self._gamma_func(self.s)
|
|
|
|
def shifted_bath_influence(self, t):
|
|
"""
|
|
int_0^infity J(w) / w exp(-i w t) = eta [ gamma/(1+i gamma t) ]**s * Gamma(s)
|
|
"""
|
|
return self.c * (self.gamma / (1 + 1j * self.gamma * t)) ** self.s
|
|
|
|
def __str__(self):
|
|
return (
|
|
"J(w) = eta w^s exp(-w / w_c)\n"
|
|
+ "eta = {}\n".format(self.eta)
|
|
+ "s = {}\n".format(self.s)
|
|
+ "w_c = {}\n".format(self.gamma)
|
|
)
|
|
|
|
def __getstate__(self):
|
|
return self.s_arg, self.eta_arg, self.gamma_arg, self.normed_arg, self.mp_arg
|
|
|
|
def __setstate__(self, state):
|
|
self.__init__(*state)
|
|
|
|
|
|
class MakeItReal(object):
|
|
def __init__(self, func):
|
|
self.func = func
|
|
|
|
def __call__(self, x):
|
|
return np.real(self.func(x))
|
|
|
|
def __bfkey__(self):
|
|
return self.func
|
|
|
|
|
|
class OBCF(object):
|
|
r"""ohmic bath correlation functions (BCF)
|
|
|
|
consider ohmic spectral density
|
|
J(w) = eta w^s exp(-w / gamma)
|
|
|
|
general BCF
|
|
alpha(tau) = 1/pi int_0^infty d w J(w) exp(-i w tau)
|
|
|
|
-> ohmic BCF
|
|
alpha(tau) = eta/pi gamma^(s+1) int_0^infty d x x^s exp(-x) exp(-i x gamma t)
|
|
= eta/pi gamma^(s+1) (1 + i gamma t)^(-s-1) gamma_func(s+1)
|
|
"""
|
|
|
|
def __init__(self, s, eta, gamma, normed=False):
|
|
self.s = float(s)
|
|
self.gamma = float(gamma)
|
|
|
|
if normed:
|
|
self.eta = None
|
|
self._c1 = 1 / np.pi
|
|
else:
|
|
self.eta = eta
|
|
self._c1 = self.eta * gamma_func(s + 1) * gamma ** (s + 1) / np.pi
|
|
|
|
def __call__(self, tau):
|
|
# with warnings.catch_warnings():
|
|
# warnings.filterwarnings('error')
|
|
# try:
|
|
# a1 = 1 + 1j*self.gamma * tau
|
|
# a2 = a1**(-(self.s+1))
|
|
# return self._c1 * a2
|
|
# except:
|
|
# traceback.print_stack()
|
|
# print("gamma", self.gamma)
|
|
# print("tau", tau)
|
|
# print('a1', a1)
|
|
# print('a2', a2)
|
|
# raise
|
|
return self._c1 * (1 + 1j * self.gamma * tau) ** (-(self.s + 1))
|
|
|
|
def div1(self, tau):
|
|
return (
|
|
-self._c1
|
|
* (self.s + 1)
|
|
* (1 + 1j * self.gamma * tau) ** (-(self.s + 2))
|
|
* 1j
|
|
* self.gamma
|
|
)
|
|
|
|
def __getstate__(self):
|
|
return self.s, self.eta, self.gamma
|
|
|
|
def __setstate__(self, state):
|
|
eta = state[1]
|
|
if eta is None:
|
|
self.__init__(*state, normed=True)
|
|
else:
|
|
self.__init__(*state, normed=False)
|
|
|
|
def __eq__(self, other):
|
|
return (
|
|
(self.s == other.s)
|
|
and (self.eta == other.eta)
|
|
and (self.gamma == other.gamma)
|
|
)
|
|
|
|
def __repr__(self):
|
|
s = "\nbcf with J(w) = eta w^s exp(-w / gamma)"
|
|
s += "\ns :{}".format(self.s)
|
|
s += "\neta :{}".format(self.eta)
|
|
s += "\ngamma:{}".format(self.gamma)
|
|
|
|
return s
|
|
|
|
|
|
class OBCF_FT(object):
|
|
def __init__(self, s, eta, gamma, beta):
|
|
self.s_p1 = s + 1
|
|
self.eta = eta
|
|
self.gamma = gamma
|
|
self.beta = beta
|
|
self._c = self.eta / self.beta ** (self.s_p1) * gamma_func(self.s_p1) / np.pi
|
|
self._beta_gamma = self.beta * self.gamma
|
|
|
|
def __call__(self, tau):
|
|
if isinstance(tau, np.ndarray):
|
|
res = np.empty(shape=tau.shape, dtype=np.complex128)
|
|
res_flat = res.flat
|
|
tau_flat = tau.flat
|
|
for i, ti in enumerate(tau_flat):
|
|
zf = zeta_func(
|
|
self.s_p1,
|
|
(1 + self._beta_gamma + 1j * self.gamma * ti) / self._beta_gamma,
|
|
)
|
|
res_flat[i] = self._c * (
|
|
(self._beta_gamma / (1 + 1j * self.gamma * ti)) ** self.s_p1
|
|
+ zf
|
|
+ np.conj(zf)
|
|
)
|
|
return res
|
|
else:
|
|
zf = zeta_func(
|
|
self.s_p1,
|
|
(1 + self._beta_gamma + 1j * self.gamma * tau) / self._beta_gamma,
|
|
)
|
|
return self._c * (
|
|
(self._beta_gamma / (1 + 1j * self.gamma * tau)) ** self.s_p1
|
|
+ zf
|
|
+ np.conj(zf)
|
|
)
|
|
|
|
def __bfkey__(self):
|
|
return self.s_p1, self.eta, self.gamma, self.beta
|
|
|
|
|
|
class OBCF_FT_mp(object):
|
|
def __init__(self, s, eta, gamma, beta):
|
|
self.s_p1 = mpmath.mpf(s) + 1
|
|
self.eta = mpmath.mpf(eta)
|
|
self.gamma = mpmath.mpf(gamma)
|
|
self.beta = mpmath.mpf(beta)
|
|
self._c = (
|
|
self.eta / self.beta ** (self.s_p1) * mpmath.gamma(self.s_p1) / mpmath.pi
|
|
)
|
|
self._beta_gamma = self.beta * self.gamma
|
|
|
|
def __call__(self, tau):
|
|
zf = mpmath.zeta(
|
|
self.s_p1, (1 + self._beta_gamma + 1j * self.gamma * tau) / self._beta_gamma
|
|
)
|
|
return self._c * (
|
|
(self._beta_gamma / (1 + 1j * self.gamma * tau)) ** self.s_p1
|
|
+ zf
|
|
+ mpmath.conj(zf)
|
|
)
|
|
|
|
|
|
class OFTCorr(object):
|
|
def __init__(self, s, eta, gamma, beta):
|
|
self.s_p1 = s + 1
|
|
self.eta = eta
|
|
self.gamma = gamma
|
|
self.beta = beta
|
|
self._c = self.eta / self.beta ** (self.s_p1) * gamma_func(self.s_p1) / np.pi
|
|
self._beta_gamma = self.beta * self.gamma
|
|
|
|
def __call__(self, tau):
|
|
if isinstance(tau, np.ndarray):
|
|
res = np.empty(shape=tau.shape, dtype=np.complex128)
|
|
res_flat = res.flat
|
|
tau_flat = tau.flat
|
|
for i, ti in enumerate(tau_flat):
|
|
res_flat[i] = self._c * zeta_func(
|
|
self.s_p1,
|
|
(1 + self._beta_gamma + 1j * self.gamma * ti) / self._beta_gamma,
|
|
)
|
|
return res
|
|
else:
|
|
return self._c * zeta_func(
|
|
self.s_p1,
|
|
(1 + self._beta_gamma + 1j * self.gamma * tau) / self._beta_gamma,
|
|
)
|
|
|
|
def __bfkey__(self):
|
|
return self.s_p1, self.eta, self.gamma, self.beta
|
|
|
|
|
|
def BOSE_distribution_single(x):
|
|
"""calc (exp(x)-1)^-1"""
|
|
if x < 0:
|
|
raise ValueError("x < 0")
|
|
elif x > 40:
|
|
return np.exp(-x)
|
|
else:
|
|
return 1 / np.expm1(x)
|
|
|
|
|
|
def BOSE_distribution(x):
|
|
try:
|
|
return np.asarray([BOSE_distribution_single(xi) for xi in x])
|
|
except:
|
|
return BOSE_distribution_single(x)
|
|
|
|
|
|
class OFTDens(object):
|
|
def __init__(self, s, eta, gamma, beta):
|
|
self.osd = OSD(s, eta, gamma)
|
|
self.beta = beta
|
|
|
|
def __call__(self, w):
|
|
return BOSE_distribution(self.beta * w) * self.osd(w)
|
|
|
|
def __bfkey__(self):
|
|
return self.osd, self.beta
|
|
|
|
|
|
class BCF_aprx(object):
|
|
r"""approximation of the bath correlation function using exponentials
|
|
|
|
alpha(tau) = sum_i=1^n g_i exp(-omega_i tau)
|
|
"""
|
|
|
|
def __init__(self, g, omega):
|
|
g = np.asarray(g)
|
|
omega = np.asarray(omega)
|
|
self._n = g.shape
|
|
assert self._n == omega.shape
|
|
self.g = g
|
|
self.omega = omega
|
|
|
|
def __call__(self, tau):
|
|
r"""return alpha(tau) = sum_i=1^n g_i exp(-w_i tau) for arbitrary shape of tau"""
|
|
try:
|
|
s_tau = tau.shape
|
|
dims_tau = len(s_tau)
|
|
|
|
tau_ = tau.reshape((1,) + s_tau)
|
|
g_ = self.g.reshape(self._n + dims_tau * (1,))
|
|
omega_ = self.omega.reshape(self._n + dims_tau * (1,))
|
|
|
|
res = np.empty(shape=s_tau, dtype=np.complex128)
|
|
idx_pos = np.where(tau >= 0)
|
|
res[idx_pos] = np.sum(g_ * np.exp(-omega_ * tau_[(0,) + idx_pos]), axis=0)
|
|
idx_neg = np.where(tau < 0)
|
|
res[idx_neg] = np.conj(
|
|
np.sum(g_ * np.exp(-omega_ * np.abs(tau_[(0,) + idx_neg])), axis=0)
|
|
)
|
|
except Exception as e:
|
|
if tau >= 0:
|
|
res = np.sum(self.g * np.exp(-self.omega * tau))
|
|
else:
|
|
res = np.sum(self.g * np.exp(self.omega * tau)).conj()
|
|
|
|
return res
|
|
|
|
def get_SD(self, t_max, n):
|
|
"""
|
|
t0, t1, t2 ... ,tn=t_max
|
|
|
|
al(t0), al(t1), al(t2), ... al(t n-1) + al(tn), al(t n+1) = al(t n-1)^ast, ... al(t 2n-1) = al(-1)^ast
|
|
|
|
idx: 0 ... n-1 n .. -1
|
|
|
|
-> dt = t_max / n-1
|
|
|
|
"""
|
|
t, dt = np.linspace(0, t_max, n, retstep=True)
|
|
alpha_t = self.__call__(t)
|
|
alpha_t = np.hstack((alpha_t[:-1], np.conj(alpha_t[:0:-1])))
|
|
|
|
N = 2 * (n - 1)
|
|
t_, dt_ = np.linspace(0, 2 * t_max, N, endpoint=False, retstep=True)
|
|
assert dt == dt_
|
|
assert len(t_) == len(alpha_t)
|
|
|
|
fft_alpha = np.fft.ifft(alpha_t) * dt * N
|
|
|
|
dw = 2 * np.pi / N / dt
|
|
|
|
freq = np.hstack((np.arange(N // 2), -np.arange(1, N // 2 + 1)[::-1])) * dw
|
|
|
|
return 0.5 * np.fft.ifftshift(np.real(fft_alpha)), np.fft.ifftshift(freq)
|
|
|
|
def get_SD_analyt(self, w):
|
|
sd = np.asarray(
|
|
[
|
|
np.sum(
|
|
(
|
|
self.g.real * self.omega.real
|
|
- self.g.imag * (wi - self.omega.imag)
|
|
)
|
|
/ (self.omega.real ** 2 + (wi - self.omega.imag) ** 2)
|
|
)
|
|
for wi in w
|
|
]
|
|
)
|
|
return sd
|
|
|
|
def ft(self, om):
|
|
if isinstance(om, np.ndarray):
|
|
res = np.empty(shape=om.shape, dtype=np.complex128)
|
|
res_flat_view = res.flat
|
|
om_flat = om.flatten().reshape(-1, 1)
|
|
res_flat_view[:] = np.sum(
|
|
self.g
|
|
* 2
|
|
* self.omega
|
|
/ ((om_flat - 1j * self.omega) * (om_flat + 1j * self.omega)),
|
|
axis=1,
|
|
)
|
|
return res
|
|
|
|
else:
|
|
return (
|
|
self.g
|
|
* 2
|
|
* self.omega
|
|
/ ((om - 1j * self.omega) * (om + 1j * self.omega))
|
|
)
|
|
|
|
def n(self):
|
|
return self._n[0]
|