import numpy as np import matplotlib.pyplot as plt from scipy.optimize import brentq from scipy.special import airy, hermite, factorial
def Xi(x): return 1.0 / (1.0 + np.exp(-20 * (x - 0.5)))
def potential(x): return 0.5 * x ** 2
def ho_energy(n): return n + 0.5
def ho_psi(n, x): coeff = 1.0 / np.sqrt(2 ** n * factorial(n)) * (1/np.pi) ** 0.25 Hn = hermite(n) return coeff * np.exp(-x ** 2 / 2) * Hn(x)
def sol_fun(fun, x_min, x_max, step): i = np.arange(x_min, x_max + step, step) root_E = [] for ii in range(len(i) - 1): if fun(i[ii]) * fun(i[ii+1]) < 0: try: r = brentq(fun, i[ii], i[ii+1]) root_E.append(r) except: pass return np.array(root_E)
def sol_ab(fun, x_min, x_max, step, E_n): y = lambda x: fun(x) - E_n root = sol_fun(y, x_min, x_max, step) if len(root) == 0: return np.array([[-np.inf, np.inf]]), np.array([[-np.inf, np.inf]]) d_root = np.zeros_like(root) eps = 1e-6 for i in range(len(root)): d_root[i] = (y(root[i]+eps) - y(root[i]-eps)) / (2*eps) ab = [] ab_not = [] for ii in range(len(d_root)): if d_root[ii] < 0: if ii != len(d_root)-1 and ii != 0: a, b = root[ii], root[ii+1] ab.append([a, b]) ab_not.append([root[ii-1], root[ii]]) elif ii == len(d_root)-1: a, b = root[ii], np.inf ab.append([a, b]) ab_not.append([root[ii-1], root[ii]]) elif ii == 0: a, b = root[ii], root[ii+1] ab.append([a, b]) ab_not.append([-np.inf, root[ii]]) else: if ii == 0: a, b = -np.inf, root[ii] ab.append([a, b]) elif ii == len(d_root)-1: ab_not.append([root[ii], np.inf]) return np.array(ab), np.array(ab_not)
def sol_energy(fun, xs, n, E_0): E_n = n + 0.5 x_min, x_max = np.min(xs), np.max(xs) step = 10 * (xs[1] - xs[0]) delta = 5 ab, _ = sol_ab(fun, x_min, x_max, step, E_0) if ab[0,0] < -1e5 or ab[-1,1] > 1e5: return E_0 def p(x, En): return np.sqrt(2 * np.abs(En - fun(x))) / np.pi end_ab = [] for a_b in ab: xx = np.linspace(a_b[0], a_b[1], 400) end_ab.append(np.trapz(p(xx, E_0), xx) - E_n) while np.max(end_ab) < 0: E_0 = E_0 + delta ab, _ = sol_ab(fun, x_min, x_max, step, E_0) end_ab = [] for a_b in ab: xx = np.linspace(a_b[0], a_b[1], 400) end_ab.append(np.trapz(p(xx, E_0), xx) - E_n) E_a = E_0 - delta E_b = E_0 for i in range(20): E_mid = (E_a + E_b) / 2 ab, _ = sol_ab(fun, x_min, x_max, step, E_mid) end_ab = [] for a_b in ab: xx = np.linspace(a_b[0], a_b[1], 400) end_ab.append(np.trapz(p(xx, E_mid), xx) - E_n) if np.max(end_ab) < 0: E_a = (E_a + E_b) / 2 else: E_b = (E_a + E_b) / 2 return E_b
class WKB: def __init__(self, potential, xx, n, E_0): self.potential_sys = potential self.coordinate = xx self.potential = potential(xx) self.energy_leval = n self.energy = sol_energy(potential, xx, n, E_0) x_min, x_max = np.min(xx), np.max(xx) step = xx[1] - xx[0] self.root_E, self.ab_not = sol_ab(potential, x_min, x_max, step, self.energy)
def WKB_psi_allow(self, x, a, b): p = lambda xx: np.sqrt(2 * np.abs(self.energy - self.potential_sys(xx))) int_p = np.zeros_like(x) psi = np.zeros_like(x) if a > -1e5: for i in range(len(x)): xx_ = np.linspace(a, x[i], 200) int_p[i] = np.trapz(p(xx_), xx_) psi = 1.0 / np.sqrt(p(x)) * np.cos(int_p - np.pi/4) else: for i in range(len(x)): xx_ = np.linspace(b, x[i], 200) int_p[i] = np.trapz(p(xx_), xx_) psi = 1.0 / np.sqrt(p(x)) * np.cos(int_p + np.pi/4) return psi
def WKB_psi_forbidden(self, x): a = self.ab_not[np.where(self.ab_not[:,0] <= x[0])[0][-1], 0] if np.any(self.ab_not[:,0] <= x[0]) else -np.inf b = self.ab_not[np.where(self.ab_not[:,1] >= x[-1])[0][0], 1] if np.any(self.ab_not[:,1] >= x[-1]) else np.inf p = lambda xx: np.sqrt(2 * np.abs(self.energy - self.potential_sys(xx))) int_pa = np.zeros_like(x) int_pb = np.zeros_like(x) psi = np.zeros_like(x) if a < -1e5: for i in range(len(x)): xx_ = np.linspace(x[i], b, 200) int_pb[i] = np.trapz(p(xx_), xx_) psi = 1.0 / (2 * p(x)) * np.exp(-int_pb) elif b > 1e5: for i in range(len(x)): xx_ = np.linspace(a, x[i], 200) int_pa[i] = np.trapz(p(xx_), xx_) psi = 1.0 / (2 * p(x)) * np.exp(-int_pa) else: for i in range(len(x)): xx1 = np.linspace(a, x[i], 200) xx2 = np.linspace(x[i], b, 200) int_pa[i] = np.trapz(p(xx1), xx1) int_pb[i] = np.trapz(p(xx2), xx2) psi = 1.0/(2*p(x))*np.exp(-int_pa) + 1.0/(2*p(x))*(np.exp(-int_pb)-np.exp(-int_pa))*Xi((x-a)/(b-a)) return psi
def WKB_airy(self, x, F, ab): F = float(F) pref = np.sqrt(np.pi) / (2 * np.abs(F))**(1/6) if F < 0: F = -F arg = (2*F)**(1/3) * (ab - x) else: arg = (2*F)**(1/3) * (x - ab) return pref * airy(arg)[0]
def approx_psi(self, delta, delta_tran): x = self.coordinate ab = self.root_E size_ab = ab.shape psi = np.zeros_like(x) for num_ab in range(size_ab[0]): a, b = ab[num_ab,0], ab[num_ab,1] F_a = (self.potential_sys(a + 1e-4) - self.potential_sys(a - 1e-4)) / 2e-4 F_b = (self.potential_sys(b + 1e-4) - self.potential_sys(b - 1e-4)) / 2e-4 i_a = np.argmax(x > a + delta + delta_tran) i_b = len(x) - np.argmax(x[::-1] < b - delta - delta_tran) - 1 psi[i_a:i_b+1] = self.WKB_psi_allow(x[i_a:i_b+1], a, b) if a > -1e5: i_a = np.argmax(x > a - delta) i_b = len(x) - np.argmax(x[::-1] < a + delta) - 1 psi[i_a:i_b+1] = self.WKB_airy(x[i_a:i_b+1], F_a, a) if b < 1e5: i_a = np.argmax(x > b - delta) i_b = len(x) - np.argmax(x[::-1] < b + delta) - 1 psi[i_a:i_b+1] = ((-1)**self.energy_leval) * self.WKB_airy(x[i_a:i_b+1], F_b, b) if a >= -1e5: i_a1 = np.argmax(x > a + delta) i_b1 = len(x) - np.argmax(x[::-1] < a + delta + delta_tran) - 1 i_a2 = np.argmax(x > a - delta - delta_tran) i_b2 = len(x) - np.argmax(x[::-1] < a - delta) - 1 xx1 = x[i_a1:i_b1+1] xx2 = x[i_a2:i_b2+1] if F_a > 0: psi[i_a1:i_b1+1] = self.WKB_airy(xx1, F_a, a) + (self.WKB_psi_forbidden(xx1) - self.WKB_airy(xx1, F_a, a))*Xi((xx1-a-delta)/delta_tran) psi[i_a2:i_b2+1] = self.WKB_psi_allow(xx2, a, b) + (self.WKB_airy(xx2, F_a, a) - self.WKB_psi_allow(xx2, a, b))*Xi((xx2-a+delta+delta_tran)/delta_tran) else: psi[i_a1:i_b1+1] = self.WKB_airy(xx1, F_a, a) + (self.WKB_psi_allow(xx1, a, b) - self.WKB_airy(xx1, F_a, a))*Xi((xx1-a-delta)/delta_tran) psi[i_a2:i_b2+1] = self.WKB_psi_forbidden(xx2) + (self.WKB_airy(xx2, F_a, a) - self.WKB_psi_forbidden(xx2))*Xi((xx2-a+delta+delta_tran)/delta_tran) if b <= 1e5: i_a1 = np.argmax(x > b + delta) i_b1 = len(x) - np.argmax(x[::-1] < b + delta + delta_tran) - 1 i_a2 = np.argmax(x > b - delta - delta_tran) i_b2 = len(x) - np.argmax(x[::-1] < b - delta) - 1 xx1 = x[i_a1:i_b1+1] xx2 = x[i_a2:i_b2+1] if F_b > 0: psi[i_a1:i_b1+1] = ((-1)**self.energy_leval)*(self.WKB_airy(xx1, F_b, b)+(self.WKB_psi_forbidden(xx1)-self.WKB_airy(xx1, F_b, b))*Xi((xx1-b-delta)/delta_tran)) psi[i_a2:i_b2+1] = self.WKB_psi_allow(xx2, a, b)+(((-1)**self.energy_leval)*self.WKB_airy(xx2, F_b, b)-self.WKB_psi_allow(xx2, a, b))*Xi((xx2-b+delta+delta_tran)/delta_tran) else: psi[i_a1:i_b1+1] = ((-1)**self.energy_leval)*self.WKB_airy(xx1,F_b,b)+(self.WKB_psi_allow(xx1,a,b)-((-1)**self.energy_leval)*self.WKB_airy(xx1,F_b,b))*Xi((xx1-b-delta)/delta_tran) psi[i_a2:i_b2+1] = ((-1)**self.energy_leval)*self.WKB_psi_forbidden(xx2)+(self.WKB_airy(xx2,F_b,b)-self.WKB_psi_forbidden(xx2))*Xi((xx2-b+delta+delta_tran)/delta_tran) num_a_before = np.where(self.ab_not[:,0] < a)[0] num_b_end = np.where(self.ab_not[:,1] > b)[0] if len(num_a_before) > 0: a_before = self.ab_not[num_a_before[-1],0] i_a = np.argmax(x > a_before + delta + delta_tran) i_b = len(x) - np.argmax(x[::-1] < a - delta - delta_tran) - 1 psi[i_a:i_b+1] = self.WKB_psi_forbidden(x[i_a:i_b+1]) if len(num_b_end) > 0: b_end = self.ab_not[num_b_end[0],1] i_a = np.argmax(x > b + delta + delta_tran) i_b = len(x) - np.argmax(x[::-1] < b_end - delta - delta_tran) - 1 psi[i_a:i_b+1] = ((-1)**self.energy_leval)*self.WKB_psi_forbidden(x[i_a:i_b+1]) norm = np.sqrt(np.sum((x[1]-x[0]) * psi**2)) psi = psi / norm rho = psi return rho, norm
if __name__ == "__main__": N_list = [0, 1, 20] x_grid = np.linspace(-15, 15, 1201) delta = 0.35 delta_tran = 0.25
plt.figure(figsize=(12, 8)) for idx, n in enumerate(N_list): wkb = WKB(potential, x_grid, n, ho_energy(n)) psi_wkb, _ = wkb.approx_psi(delta, delta_tran) psi_th = ho_psi(n, x_grid) sign = np.sign(np.trapz(psi_wkb * psi_th, x_grid)) psi_wkb *= sign norm_th = np.sqrt(np.trapz(psi_th ** 2, x_grid)) psi_th /= norm_th plt.subplot(len(N_list), 1, idx+1) plt.plot(x_grid, psi_wkb, label=f'$|\\psi_{{\\rm WKB}}|^2$ n={n}', color='blue') plt.plot(x_grid, psi_th, '--', label=f'Theory n={n}', color='red') for ab in wkb.root_E: plt.axvline(ab[0], color='k', linestyle=':', alpha=0.5) if ab[1] < 1e5: plt.axvline(ab[1], color='k', linestyle=':', alpha=0.5) plt.title(f"n={n}: E_wkb={wkb.energy:.4f}, E_exact={ho_energy(n):.4f}") plt.legend() plt.xlim(-15, 15) plt.tight_layout() plt.show()
plt.figure(figsize=(12, 8)) for idx, n in enumerate(N_list): wkb = WKB(potential, x_grid, n, ho_energy(n)) psi_wkb, norm = wkb.approx_psi(delta, delta_tran) psi_mod = psi_wkb**2 p = np.sqrt(2 * np.abs(wkb.energy - wkb.potential)) p[p==0] = np.nan invp = 1/norm**2 / p plt.subplot(len(N_list), 1, idx+1) plt.plot(x_grid, psi_mod, label=f'$|\\psi_{{\\rm WKB}}|^2$ n={n}', color='blue') plt.plot(x_grid, invp, '--', label='$1/p$ (归一化)', color='red') for ab in wkb.root_E: plt.axvline(ab[0], color='k', linestyle=':', alpha=0.5) if ab[1] < 1e5: plt.axvline(ab[1], color='k', linestyle=':', alpha=0.5) plt.title(f"n={n}: E_wkb={wkb.energy:.4f}, E_exact={ho_energy(n):.4f}") plt.legend() plt.xlim(-15, 15) plt.ylim(-0.05, 1.1*np.max(psi_mod)) plt.tight_layout() plt.show()
|